-
Notifications
You must be signed in to change notification settings - Fork 8
feat: STOMP Config 추가 #400
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
e8383b1
a874ae8
97dd884
b6af18f
772435e
2b59f9c
11be0a6
f6f9672
6ebfec7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| package com.example.solidconnection.chat.config; | ||
|
|
||
| import java.util.Set; | ||
| import java.util.concurrent.ConcurrentHashMap; | ||
| import org.springframework.context.event.EventListener; | ||
| import org.springframework.messaging.simp.stomp.StompHeaderAccessor; | ||
| import org.springframework.stereotype.Component; | ||
| import org.springframework.web.socket.messaging.SessionConnectEvent; | ||
| import org.springframework.web.socket.messaging.SessionDisconnectEvent; | ||
|
|
||
| @Component | ||
| public class StompEventListener { | ||
|
|
||
| private final Set<String> sessions = ConcurrentHashMap.newKeySet(); | ||
|
|
||
| @EventListener | ||
| public void connectHandle(SessionConnectEvent event) { | ||
| StompHeaderAccessor accessor = StompHeaderAccessor.wrap(event.getMessage()); | ||
| sessions.add(accessor.getSessionId()); | ||
| } | ||
|
|
||
| @EventListener | ||
| public void disconnectHandle(SessionDisconnectEvent event) { | ||
| StompHeaderAccessor accessor = StompHeaderAccessor.wrap(event.getMessage()); | ||
| sessions.remove(accessor.getSessionId()); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,64 @@ | ||
| package com.example.solidconnection.chat.config; | ||
|
|
||
| import static com.example.solidconnection.common.exception.ErrorCode.AUTHENTICATION_FAILED; | ||
|
|
||
| import com.example.solidconnection.auth.token.JwtTokenProvider; | ||
| import com.example.solidconnection.common.exception.CustomException; | ||
| import com.example.solidconnection.common.exception.ErrorCode; | ||
| import io.jsonwebtoken.Claims; | ||
| import lombok.RequiredArgsConstructor; | ||
| import org.springframework.messaging.Message; | ||
| import org.springframework.messaging.MessageChannel; | ||
| import org.springframework.messaging.simp.stomp.StompCommand; | ||
| import org.springframework.messaging.simp.stomp.StompHeaderAccessor; | ||
| import org.springframework.messaging.support.ChannelInterceptor; | ||
| import org.springframework.stereotype.Component; | ||
|
|
||
| @Component | ||
| @RequiredArgsConstructor | ||
| public class StompHandler implements ChannelInterceptor { | ||
|
|
||
| private final JwtTokenProvider jwtTokenProvider; | ||
|
|
||
| @Override | ||
| public Message<?> preSend(Message<?> message, MessageChannel channel) { | ||
| final StompHeaderAccessor accessor = StompHeaderAccessor.wrap(message); | ||
|
|
||
| if (StompCommand.CONNECT.equals(accessor.getCommand())) { | ||
| Claims claims = validateAndExtractClaims(accessor, AUTHENTICATION_FAILED); | ||
| } | ||
|
|
||
| if (StompCommand.SUBSCRIBE.equals(accessor.getCommand())) { | ||
| Claims claims = validateAndExtractClaims(accessor, AUTHENTICATION_FAILED); | ||
|
|
||
| String email = claims.getSubject(); | ||
| String destination = accessor.getDestination(); | ||
|
|
||
| String roomId = extractRoomId(destination); | ||
|
|
||
| // todo: roomId 기반 실제 구독 권한 검사 로직 추가 | ||
| } | ||
|
|
||
| return message; | ||
| } | ||
|
|
||
| private Claims validateAndExtractClaims(StompHeaderAccessor accessor, ErrorCode errorCode) { | ||
| String bearerToken = accessor.getFirstNativeHeader("Authorization"); | ||
| if (bearerToken == null || !bearerToken.startsWith("Bearer ")) { | ||
| throw new CustomException(errorCode); | ||
| } | ||
| String token = bearerToken.substring(7); | ||
| return jwtTokenProvider.parseClaims(token); | ||
| } | ||
|
|
||
| private String extractRoomId(String destination) { | ||
| if (destination == null) { | ||
| throw new CustomException(ErrorCode.INVALID_ROOM_ID); | ||
| } | ||
| String[] parts = destination.split("/"); | ||
| if (parts.length < 3 || !parts[1].equals("topic")) { | ||
| throw new CustomException(ErrorCode.INVALID_ROOM_ID); | ||
| } | ||
| return parts[2]; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| package com.example.solidconnection.chat.config; | ||
|
|
||
| import org.springframework.boot.context.properties.ConfigurationProperties; | ||
|
|
||
| @ConfigurationProperties(prefix = "websocket") | ||
| public record StompProperties(ThreadPool threadPool, HeartbeatProperties heartbeat) { | ||
|
|
||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 파일 끝 개행이 두줄 되어있는 것 같습니다 😲
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 수정 완료했습니다! |
||
| public record ThreadPool(InboundProperties inbound, OutboundProperties outbound) { | ||
|
|
||
| } | ||
|
|
||
| public record InboundProperties(int corePoolSize, int maxPoolSize, int queueCapacity) { | ||
|
|
||
| } | ||
|
|
||
| public record OutboundProperties(int corePoolSize, int maxPoolSize) { | ||
|
|
||
| } | ||
|
|
||
| public record HeartbeatProperties(long serverInterval, long clientInterval) { | ||
|
|
||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,55 @@ | ||
| package com.example.solidconnection.chat.config; | ||
|
|
||
| import com.example.solidconnection.chat.config.StompProperties.HeartbeatProperties; | ||
| import com.example.solidconnection.chat.config.StompProperties.InboundProperties; | ||
| import com.example.solidconnection.chat.config.StompProperties.OutboundProperties; | ||
| import com.example.solidconnection.security.config.CorsProperties; | ||
| import java.util.List; | ||
| import lombok.RequiredArgsConstructor; | ||
| import org.springframework.context.annotation.Configuration; | ||
| import org.springframework.messaging.simp.config.ChannelRegistration; | ||
| import org.springframework.messaging.simp.config.MessageBrokerRegistry; | ||
| import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; | ||
| import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker; | ||
| import org.springframework.web.socket.config.annotation.StompEndpointRegistry; | ||
| import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer; | ||
|
|
||
| @Configuration | ||
| @EnableWebSocketMessageBroker | ||
| @RequiredArgsConstructor | ||
| public class StompWebSocketConfig implements WebSocketMessageBrokerConfigurer { | ||
|
|
||
| private final StompHandler stompHandler; | ||
| private final StompProperties stompProperties; | ||
| private final CorsProperties corsProperties; | ||
|
|
||
| @Override | ||
| public void registerStompEndpoints(StompEndpointRegistry registry) { | ||
| List<String> strings = corsProperties.allowedOrigins(); | ||
| String[] allowedOrigins = strings.toArray(String[]::new); | ||
| registry.addEndpoint("/connect").setAllowedOrigins(allowedOrigins).withSockJS(); | ||
| } | ||
|
|
||
| @Override | ||
| public void configureClientInboundChannel(ChannelRegistration registration) { | ||
| InboundProperties inboundProperties = stompProperties.threadPool().inbound(); | ||
| registration.interceptors(stompHandler).taskExecutor().corePoolSize(inboundProperties.corePoolSize()).maxPoolSize(inboundProperties.maxPoolSize()).queueCapacity(inboundProperties.queueCapacity()); | ||
| } | ||
|
|
||
| @Override | ||
| public void configureClientOutboundChannel(ChannelRegistration registration) { | ||
| OutboundProperties outboundProperties = stompProperties.threadPool().outbound(); | ||
| registration.taskExecutor().corePoolSize(outboundProperties.corePoolSize()).maxPoolSize(outboundProperties.maxPoolSize()); | ||
| } | ||
|
|
||
| @Override | ||
| public void configureMessageBroker(MessageBrokerRegistry registry) { | ||
| ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler(); | ||
| scheduler.setPoolSize(1); | ||
| scheduler.setThreadNamePrefix("wss-heartbeat-"); | ||
| scheduler.initialize(); | ||
| HeartbeatProperties heartbeatProperties = stompProperties.heartbeat(); | ||
| registry.setApplicationDestinationPrefixes("/publish"); | ||
| registry.enableSimpleBroker("/topic").setHeartbeatValue(new long[]{heartbeatProperties.serverInterval(), heartbeatProperties.clientInterval()}).setTaskScheduler(scheduler); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -42,6 +42,18 @@ view: | |
| count: | ||
| scheduling: | ||
| delay: 3000 | ||
| websocket: | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 현재 WebSocket 관련 설정값이
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 아니 분명 테스트랑 main 둘 다 올렸는데 merge 과정에서 날아간 것 같네요............. ㅜㅜㅜ 다시 올릴게용
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 수정 완료했습니다! |
||
| thread-pool: | ||
| inbound: | ||
| core-pool-size: 8 | ||
| max-pool-size: 16 | ||
| queue-capacity: 1000 | ||
| outbound: | ||
| core-pool-size: 8 | ||
| max-pool-size: 16 | ||
| heartbeat: | ||
| server-interval: 15000 | ||
| client-interval: 15000 | ||
| oauth: | ||
| apple: | ||
| token-url: "https://appleid.apple.com/auth/token" | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이 파일 전체에도 포매팅이 필요해보입니다 🏃♀️
만약 다른 개발자가 main.java 패키지 전체를 포매팅했을 때, 이 파일까지 함께 변경된다면
그 변경이 본인의 관심사와는 무관한 부분임에도 불구하고 PR에 포함될 것입니다.
이런 경우엔 불필요한 diff가 발생해서 리뷰나 히스토리 추적에 혼란을 줄 수 있다고 생각합니다~
그래서 미리 포매팅을 맞춰두는 게 좋을 것 같아요!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
분명 모든 파일에 포매팅을 했는데........ 빼먹은 것 같습니다 담부터 잘 확인 할게용 ㅜㅜ