WebSocket с бэкэндом Spring теряет соединение через некоторое время, onclose не вызывается
В нашем весеннем приложении большинство контроллеров защищено. Веб-розетки за основной. Перед доступом к websocket зарегистрированный пользователь запрашивает имя пользователя и хешированный пароль для подключения к websocket. Оба будут сгенерированы, но сейчас для целей тестирования он всегда возвращает одни и те же кредитные данные.
URL для информации выглядит следующим образом:
https://user:debaee4affbeaba909a184066981d55a@localhost:8000/project-name/chat/info
WebSocket открыт правильно. Мы можем отправлять несколько сообщений, и они идут через брокера и отображаются для пользователей. Вот информация запроса от инструментов Chrome:
Remote Address:127.0.0.1:8000
Request URL:https://benny:debaee4affbeaba909a184066981d55a@localhost:8000/project-name/chat/033/7szz8k_f/xhr_send
Request Method:POST
Status Code:204 No Content
Response Headers:
HTTP/1.1 204 No Content
server: Apache-Coyote/1.1
x-content-type-options: nosniff
x-xss-protection: 1; mode=block
cache-control: no-cache, no-store, max-age=0, must-revalidate
pragma: no-cache
expires: 0
strict-transport-security: max-age=31536000 ; includeSubDomains
x-frame-options: DENY
access-control-allow-origin: https://localhost:8000
access-control-allow-credentials: true
vary: Origin
content-type: text/plain;charset=UTF-8
date: Mon, 15 Jun 2015 08:22:43 GMT
Connection: keep-alive
Request Headers:
POST /project-name/chat/033/7szz8k_f/xhr_send HTTP/1.1
Host: localhost:8000
Connection: keep-alive
Content-Length: 143
Origin: https://localhost:8000
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.89 Safari/537.36
Content-Type: text/plain;charset=UTF-8
Accept: */*
Referer: https://localhost:8000/
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.8,pl;q=0.6
Cookie: JSESSIONID=FF967D3DD1247C1D572C15CF8A3D5E8E; i18next=en; language=pl; tmhDynamicLocale.locale=%22pl-pl%22
["SEND\npriority:9\ndestination:/random/chat/1/FUNNY\ncontent-length:49\n\n{\"message\":\"sfsdf\",\"display\":\"The great wizard.\"}\u0000"]
Но через минуту или около того при отправке другого запроса мы получаем 404 ответа. Не имеет значения, были ли какие-либо SEND-запросы выданы ранее. Мы можем написать более 50 сообщений за этот промежуток времени, а затем мы получим 404.
Пример 404 запроса данных выглядит следующим образом:
Remote Address:127.0.0.1:8000
Request URL:https://hill:debaee4affbeaba909a184066981d55a@localhost:8000/project-name/chat/033/7szz8k_f/xhr_send
Request Method:POST
Status Code:404 Not Found
Response Headers:
HTTP/1.1 404 Not Found
server: Apache-Coyote/1.1
x-content-type-options: nosniff
x-xss-protection: 1; mode=block
cache-control: no-cache, no-store, max-age=0, must-revalidate
pragma: no-cache
expires: 0
strict-transport-security: max-age=31536000 ; includeSubDomains
x-frame-options: DENY
content-length: 0
date: Mon, 15 Jun 2015 08:24:17 GMT
Connection: keep-alive
Request Headers:
POST /project-name/chat/033/7szz8k_f/xhr_send HTTP/1.1
Host: localhost:8000
Connection: keep-alive
Content-Length: 143
Origin: https://localhost:8000
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.89 Safari/537.36
Content-Type: text/plain;charset=UTF-8
Accept: */*
Referer: https://localhost:8000/
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.8,pl;q=0.6
Cookie: JSESSIONID=FF967D3DD1247C1D572C15CF8A3D5E8E; i18next=en; language=pl; tmhDynamicLocale.locale=%22pl-pl%22
Request Payload:
["SEND\npriority:9\ndestination:/random/chat/1/FUNNY\ncontent-length:49\n\n{\"message\":\"yhgfh\",\"username\":\"The great wizard.\"}\u0000"]
При настройке Stomp мы настраиваем функцию реагирования на закрытие:
socket.client = new SockJS(targetUrl);
socket.stomp = Stomp.over(socket.client);
socket.stomp.connect({}, startListener);
socket.stomp.onclose = reconnect;
С функцией повторного подключения, выглядящей так (это в AngularJS):
var reconnect = function() {
$log.debug('Reconnect called');
$timeout(function() {
initialize();
}, this.RECONNECT_TIMEOUT);
};
Но функция никогда не вызывается.
Контроллер для чата довольно прост:
@Controller
public class StageChatController {
@Inject
private SimpMessagingTemplate template;
@Inject
private ChatMessageRepository chatMessageRepository;
@MessageMapping("/chat/{channel}/{type}")
public void sendMessage(@DestinationVariable Long channel, @DestinationVariable ChatType type, ChatMessageDto message) {
ChatMessage chatMessage = new ChatMessage();
chatMessage.setDatestamp(LocalDateTime.now());
chatMessage.setMessage(message.getMessage());
chatMessage.setChannelId(channel);
chatMessage.setChatType(type);
chatMessage.setDisplayName(message.getDisplay());
chatMessage = this.chatMessageRepository.save(chatMessage);
this.template.convertAndSend("/channel/" + project + "/" + type, chatMessage);
}
Безопасность для чата переопределяет oauth безопасность для чата:
@Configuration
@EnableWebSecurity
@Order(2)
static class BasicAccessConfig extends WebSecurityConfigurerAdapter {
@Inject
private OAuth2ClientContextFilter oauth2ClientContextFilter;
@Value("${project.name.chat.token}")
private String chat_token;
@Override
protected void configure(HttpSecurity http) throws Exception {
//@formatter:off
http
.requestMatcher(new AntPathRequestMatcher("/chat/**/*"))
.authorizeRequests().anyRequest().authenticated()
.and()
.httpBasic()
.and()
.anonymous().disable()
.csrf().disable()
.addFilterBefore(this.oauth2ClientContextFilter, SecurityContextPersistenceFilter.class);
;
//@formatter:on
}
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/assets/**");
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("hill").password(this.chat_token).authorities("read_chat");
}
}