웹소켓 프로젝트 중
웹소켓 헤더를 수정할 일이 발생했다.
검색 중에 한국어도 정리된 내용이없어서 이에 대한 내용을 정리하려고 한다.
먼저 현재 구현된 웹소켓 코드부터 보자..!
1. configuration
Gradle에 의존성을 추가한다. (매우매우매우매우 중요!!!!!!!!!!!!!!!!)
// https://mvnrepository.com/artifact/javax.websocket/javax.websocket-api
compileOnly group: 'javax.websocket', name: 'javax.websocket-api'
@Configuration
public class ServerConfigurator extends ServerEndpointConfig.Configurator implements ApplicationContextAware {
private static volatile BeanFactory context;
@Override
public <T> T getEndpointInstance(Class<T> clazz) throws InstantiationException {
Object serverConfigurator = context.getBean("serverConfigurator");
return context.getBean(clazz);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
ServerConfigurator.context = applicationContext;
}
@Override
public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) {
String protocolName = "ocpp1.6";
List<String> headerValue = new ArrayList<>();
headerValue.add(protocolName);
response.getHeaders().put(HandshakeRequest.SEC_WEBSOCKET_PROTOCOL, headerValue);
}
}
2. endpoint
아래 코드를 보면
@ServerEndpoint에서 configurator = ServerConfigurator.class로 설정함을 알 수 있다.
@Component
@RequiredArgsConstructor
@ServerEndpoint(value ="/endpoint/{id}", configurator = ServerConfigurator.class)
public class Socket {
@OnOpen
public void onOpen(Session session, @PathParam("id") String cpid) {
}
@OnClose
public void onClose(Session session, @PathParam("id") String cpid) {
}
@OnError
public void onError(Session session, Throwable throwable, @PathParam("id") String cpid) {
}
@OnMessage
public void onMessage(Session session, String rawMessage, @PathParam("id") String cpid) {
}
}
클라이언트에 맞게 응답을 줌을 알 수 있다.
Handshake Details
Request URL: http://localhost:8081/CSMS/OCPPJ/0019001Request Method: GETStatus Code: 101
▶Request
HeadersSec-WebSocket-Version: 13
Sec-WebSocket-Key: cfkx7NnsDNdwb0KzQzJb5A==
Connection: UpgradeUpgrade: websocket
Sec-WebSocket-Protocol: ocpp1.6, ocpp1.5
Sec-WebSocket-Extensions: permessage-deflate;
client_max_window_bitsHost: localhost:8081
▶Response
HeadersUpgrade: websocket
Connection: upgrade
Sec-WebSocket-Accept: u3wcncSRaesx0QFHF590zeOel8w=
Sec-WebSocket-Extensions: permessage-deflate;
client_max_window_bits=15
Sec-WebSocket-Protocol: ocpp1.6
Date: Wed, 18 May 2022 05:47:32 GMT
이거 구현하려고 반나절이나 걸렸다.. ㅠ0ㅠ
막힌 부분은 바로 이부분이다...
@Override
public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) {
String protocolName = "ocpp1.6";
List<String> headerValue = new ArrayList<>();
headerValue.add(protocolName);
response.getHeaders().put(HandshakeRequest.SEC_WEBSOCKET_PROTOCOL, headerValue);
}
처음에 modifyHandshake가 override가 안되서 많이 삽질했다..
아니 왜 안되는거야!!!!
꾸역꾸역 찾은 결과 gardle에 의존성을 추가하니 해결되었다... 너무 행복했다.. 😀😀
// https://mvnrepository.com/artifact/javax.websocket/javax.websocket-api
compileOnly group: 'javax.websocket', name: 'javax.websocket-api'
javax.websocket 동작 원리
https://stackoverflow.com/questions/25992111/how-does-serverendpointconfig-configurator-work
HTTP 업그레이드(websocket이라고도 함) 요청이 도착하면 어떻게 됩니까?
- 컨테이너는 웹 소켓에 대한 업그레이드 요청임을 나타내는 다양한 헤더가 있음을 확인합니다.
- 요청 방법은 GET무엇입니까?
- Upgrade헤더 값 입니까 WebSocket?
- Sec-WebSocket-Key헤더 값이 존재하고 유효합니까 ?
- Sec-WebSocket-Version헤더 값이 존재하고 유효합니까 ?
- 컨테이너는 들어오는 요청에 무언가가 매핑되어 있는지 찾으려고 시도합니다(서블릿 경로 매핑 규칙과 javax.websocket uri 템플릿 경로 매핑 규칙의 조합 사용).
- 등록된 끝점이 위의 규칙에서 검색되면 열린 것으로 간주되기 전에 초기화 및 구성해야 합니다.
- 해당 ServerEndpointConfig끝점에 대해 사용됩니다(모든 끝점에는 이 클래스가 등록되어 있고, 일부는 기본값이고, 일부는 주석에서 계산되고, 일부는 ServerContainer.addEndpoint(ServerEndpointConfig)메서드를 통해 제공됩니다.
- 다음 순서는 웹 소켓 끝점을 초기화하는 데 사용됩니다.
- 컨테이너 는 들어오는 원시 업그레이드 요청 HandshakeRequest을 나타내는 개체를 설정합니다.HandshakeResponse
- Configurator컨테이너는 다음 에서 얻습니다.ServerEndpointConfig.getConfigurator()
- 컨테이너 호출Configurator.modifyHandshake(ServerEndpointConfig config, HandshakeRequest request, HandshakeResponse response)
- 컨테이너 호출 Configurator.checkOrigin(String origin)- 이것이 실패하면 즉시 오류 403 Forbidden이 반환됩니다.
- 컨테이너 호출 Configurator.getNegotiatedSubprotocol(List<String> supported, List<String> requested)- 하위 프로토콜이 제공되면 WebSocket 업그레이드 응답 헤더에 사용됩니다.Sec-WebSocket-Protocol
- 컨테이너 호출 Configurator.getNegotiatedExtensions(List<Extension> installed, List<Extension> requested)- 반환된 값은 WebSocket 업그레이드 응답 헤더에 사용됩니다.Sec-WebSocket-Extensions
- Configurator.getEndpointInstance(Class<T> endpointClass)생성 된 엔드포인트 클래스 매개변수를 사용한 컨테이너 호출 ServerEndpointConfig.
- 이 시점에서 Endpoint+ Session가 생성되고 websocket이 열립니다.
언제든지 이 요청을 업그레이드하지 않으려면 getEndpointInstance클래스에서 예외를 던지십시오. 추천합니다 java.lang.InstantiationException. 이로 인해 Jetty가 업그레이드를 수행하지 않고 서블릿 처리 체인으로 요청을 보냅니다.
그러나 Configurator에서 http 응답이 어떻게 보이는지에 대한 옵션은 매우 제한적입니다(JSR-356/javax.websocket 사양에 따라 정의되지 않음).
'0+ 스프링 > 0 + SpringBoot(스프링부트)' 카테고리의 다른 글
[스프링] HTTP Only와 Secure Cookie (0) | 2023.01.15 |
---|---|
[JPA 에러 해결] Named parameter not bound: ~ (0) | 2022.07.29 |
[JPA 에러 해결] Caused by: org.h2.jdbc.JdbcSQLSyntaxErrorException: Syntax error in SQL statement "\000d\000a ~ " 오류 해결 (0) | 2022.07.27 |
JPA 복합키 사용방법(@IdClass, @EmbeddedId) (0) | 2022.06.02 |
JPA 다중 DataSource 설정 (0) | 2022.01.08 |