This is the backend part of the WebSocket Demo application, built with Spring Boot.
- WebSocket server implementation using Spring's WebSocket support
- Structured message handling with a ChatMessage model
- Support for different message types (CHAT, JOIN, LEAVE, etc.)
- Username management and tracking
- User count broadcasting
- Message broadcasting to all connected clients
- Ping/pong communication mechanism
- Support for standalone, integrated, and Docker deployment modes
- Integrated static resource serving for the Vue.js frontend
- Java 21
- Spring Boot 3.4
- Spring WebSocket
- Lombok for code simplification
- Jackson for JSON serialization/deserialization
model/ChatMessage.java
- Structured message model with message types (using Lombok)ChatMessageHandler.java
- WebSocket message handling and broadcastingWebSocketConfig.java
- WebSocket configurationWebsocketSpringBackApplication.java
- Main application classFrontendController.java
- Controller for serving the frontendconfig/CorsConfig.java
- CORS configuration for development mode
# Run with API context path for standalone mode
API_CONTEXT_PATH=/api ./mvnw spring-boot:run
In development mode, the backend runs on http://localhost:8080/api
and expects the frontend to run separately.
First, build the frontend:
cd ../websocket-vue-front
npm run dist
Then run the backend without context path:
cd ../websocket-spring-back
API_CONTEXT_PATH= ./mvnw spring-boot:run
In this mode, the application serves both the API and the frontend from the same server.
From the project root directory:
# Default (no context path)
./build-docker.sh
docker-compose up
# Or with a context path
./build-docker.sh latest "/api"
docker-compose up
This builds and runs the entire application in a Docker container with Java 21. The application can run either with or without a context path based on the build parameters.
The application is configured to serve the Vue.js frontend from the src/main/resources/static
directory. This directory is:
- Excluded from git tracking (via
.gitignore
) except for a.gitkeep
file - Automatically cleaned before each frontend build by build scripts
- Configured to serve content at the root path (
/
)
The cleaning process:
# From build scripts
find "$STATIC_DIR" -type f -not -name ".gitkeep" -delete
find "$STATIC_DIR" -type d -empty -delete
This ensures that no stale files remain between builds and prevents potential conflicts or outdated resources being served.
./mvnw clean package
This produces a standalone JAR file in the target
directory.
./mvnw test
The application includes various tests:
-
Unit Tests: Verify the behavior of individual components
ChatMessageTest
: Tests theChatMessage
model class and its factory methodsChatMessageHandlerTest
: Tests the WebSocket handler using mocked sessionsWebSocketConfigTest
: Tests the WebSocket configuration
-
Integration Tests: Test the application as a whole
WebSocketIntegrationTest
: Starts a server and tests connecting to the WebSocket endpoint
Code coverage can be generated with:
./mvnw test jacoco:report
The coverage report will be available in target/site/jacoco/index.html
.
The system implements security measures to protect against common attacks:
- Username sanitization: Removes HTML tags, scripts, and potentially harmful content from usernames.
private String sanitizeUsername(String username) {
// Remove any HTML tags, scripts, and potentially harmful content
String sanitized = username.replaceAll("<[^>]*>", "") // Remove HTML tags
.replaceAll("(?i)script|alert|eval|function|\\(|\\)|'|\\\"|\\\\|XSS", "") // Remove JavaScript keywords and XSS
.trim();
// Limit length to 28 characters
if (sanitized.length() > 28) {
sanitized = sanitized.substring(0, 28);
}
// If empty after sanitizing, generate a default one
if (sanitized.isEmpty()) {
sanitized = "User-" + System.currentTimeMillis() % 10000;
}
return sanitized;
}
- Secure JSON handling: Uses Jackson to parse and validate JSON messages, catching and handling errors.
- WebSocket error handling: Properly detects and handles transport errors in WebSocket sessions.
Configuration is in:
src/main/resources/application.properties
- Standard configurationsrc/main/resources/application-docker.properties
- Docker-specific configuration
Key configuration properties:
server.port
- HTTP server port (default: 8080)server.servlet.context-path
- API base path (configurable viaAPI_CONTEXT_PATH
environment variable)websocket.endpoint
- WebSocket endpoint path (default: /chat)websocket.allowed-origins
- Allowed origins for CORSapp.name
- Application name used in welcome messagesspring.web.resources.static-locations
- Location of static resourcesspring.mvc.static-path-pattern
- URL pattern for static resourceswebsocket.container.max-text-message-buffer-size
- WebSocket buffer size for text messageswebsocket.container.max-binary-message-buffer-size
- WebSocket buffer size for binary messageswebsocket.container.max-session-idle-timeout
- WebSocket session timeout
The application uses a structured ChatMessage model with Lombok:
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ChatMessage {
private String name; // Sender name
private String message; // Message content
private String timestamp; // ISO timestamp
private MessageType type; // Message type enum
public enum MessageType {
CHAT, JOIN, LEAVE, ERROR, USER_COUNT, PING, PONG
}
// Static factory methods using Builder pattern...
}
This model provides:
- Type safety with the MessageType enum
- Builder pattern for creating messages
- Automatic getters/setters via Lombok
- Factory methods for creating different message types
- Timestamp tracking
- Standard JSON serialization/deserialization
The application uses Spring's WebSocket support:
-
WebSocketConfig
configures the WebSocket endpoints at both paths:/chat
- For standalone mode/api/chat
- For integrated and Docker modes
-
ChatMessageHandler
provides:- Connection tracking
- Username management
- Message broadcasting
- Ping/pong handling
- User join/leave notifications
- User count broadcasting
The application includes Docker support via the Dockerfile in the project root:
- A single build stage installs both Maven and Node.js
- The Vue.js frontend is built directly into the Spring Boot's static resources directory
- The Spring Boot application is built with the integrated frontend
- A final lightweight runtime image with Java 21 is created for deployment
This approach simplifies the build process by:
- Avoiding complex file copying between build stages
- Ensuring reliable builds with direct integration of frontend into backend
- Maintaining a clean static directory structure
- Providing flexibility for different context path configurations
To run the application in Docker:
# From the project root, with no context path
./build-docker.sh
docker-compose up
# Or with a context path
./build-docker.sh latest "/api"
docker-compose up
Docker-specific configuration is activated via the Spring profile docker
in application-docker.properties
.
The project uses JUnit 5 and Mockito for unit and integration testing. The testing structure includes:
- Mocks to simulate WebSocket sessions and verify interactions
- Argument captors to inspect sent messages
- Testing of truncation and sanitization behavior
- Verification of error scenarios and exceptions
- Specific test configurations, such as lenient mode for Mockito
The tests cover all major aspects of the application, including:
- Connection establishment and closure
- Handling of different message types
- Message broadcasting
- Username sanitization
- Transport errors and exception handling
- Fixed username truncation issue
- Enhanced sanitization for improved security
- Optimized unit tests with Mockito
- Fixed issues in build configurations
- Added detailed documentation