o.apache.coyote.http11.Http11Processor: Ошибка синтаксического анализа заголовка HTTP-запроса - WebSocket - STOMP + SockJS - PullRequest
0 голосов
/ 13 марта 2019

Проблема

У меня есть приложение Spring Boot с веб-сокетом (STOMP). Я отправляю JSON-строки через esp8260. Все нормально, если Spring Security выключен, но если я включаю Security, я получаю сообщение об ошибке:

2019-03-12 19:42:46.729  INFO 6568 --- [nio-8080-exec-2] o.apache.coyote.http11.Http11Processor   : Error parsing HTTP request header
 Note: further occurrences of HTTP request parsing errors will be logged at DEBUG level.

java.lang.IllegalArgumentException: Invalid character found in the HTTP protocol
    at org.apache.coyote.http11.Http11InputBuffer.parseRequestLine(Http11InputBuffer.java:531) ~[tomcat-embed-core-9.0.13.jar:9.0.13]
    at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:294) ~[tomcat-embed-core-9.0.13.jar:9.0.13]
    at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) [tomcat-embed-core-9.0.13.jar:9.0.13]
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:791) [tomcat-embed-core-9.0.13.jar:9.0.13]
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1417) [tomcat-embed-core-9.0.13.jar:9.0.13]
    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-9.0.13.jar:9.0.13]
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [na:1.8.0_201]
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [na:1.8.0_201]
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-9.0.13.jar:9.0.13]
    at java.lang.Thread.run(Thread.java:748) [na:1.8.0_201]

Как исправить

Кто-нибудь знает, в чем проблема? Я могу поставить больше кода, если хотите.

WebSecurityConfig

Если я закомментирую одну строку в методе настройки -. AnyRequest (). Authenticated (), все в порядке

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private UserDetailsService userDetailsService;

    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
                .authorizeRequests()
                    .antMatchers("/resources/**", "/registration","/gs-guide-websocket").permitAll()
                    //If I comment out the line below, everything is fine
                    .anyRequest().authenticated()
                    .and()
                .formLogin()
                    .loginPage("/login")
                    .permitAll()
                    .and()
                .logout()
                    .permitAll();

    }
    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
}

WebSocketConfig

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {
        config.enableSimpleBroker("/commands", "/readings");
        config.setApplicationDestinationPrefixes("/esp");
    }

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/gs-guide-websocket").withSockJS();
    }
}

AuthChannelInterceptorAdapter

Я получаю сообщения в методе preSend, только если Spring Security выключен.

@Slf4j
@Service
public class AuthChannelInterceptorAdapter implements ChannelInterceptor {

    private static final String USERNAME_HEADER = "login";
    private static final String PASSWORD_HEADER = "passcode";

    @Autowired
    private UserDetailsService userDetailsService;
    @Autowired
    private AuthenticationManager authenticationManager;

    @Override
    public Message<?> preSend(final Message<?> message, final MessageChannel channel) throws AuthenticationException {
        final StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);

        log.info("Trying to authentication...");
        log.info(accessor.toString());
        if (StompCommand.CONNECT == accessor.getCommand()) {
            final String username = accessor.getFirstNativeHeader(USERNAME_HEADER);
            final String password = accessor.getFirstNativeHeader(PASSWORD_HEADER);

            UserDetails userDetails = userDetailsService.loadUserByUsername(username);
            UsernamePasswordAuthenticationToken user = new UsernamePasswordAuthenticationToken(userDetails, password, userDetails.getAuthorities());

            accessor.setUser(user);
            log.info("StompCommand.CONNECT ");
        }
        return message;



    }
    /*...*/
}

Зависимости

buildscript {
    ext {
        springBootVersion = '2.1.1.RELEASE'
    }
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
    }
}

apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'

group = 'com.smartpi'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8

repositories {
    mavenCentral()
}


dependencies {
    implementation('org.springframework.boot:spring-boot-starter-data-jpa')
    implementation('org.springframework.boot:spring-boot-starter-web')
    implementation('org.springframework.security:spring-security-config')
    implementation('org.springframework.security:spring-security-core')
    implementation('org.springframework.security:spring-security-crypto')
    implementation('org.springframework.security:spring-security-web')
    compile group: 'org.springframework.boot', name: 'spring-boot-starter-websocket', version: '2.0.4.RELEASE'
    compile group: 'org.springframework', name: 'spring-messaging', version: '4.0.0.RELEASE'
    compile group: 'org.springframework.security', name: 'spring-security-messaging', version: '4.0.1.RELEASE'

    compile group: 'org.springframework.security.oauth', name: 'spring-security-oauth2', version: '1.0.0.RELEASE'
    compile group: 'org.springframework.boot', name: 'spring-boot-starter-security', version: '2.1.1.RELEASE'
    compile group: 'javax.servlet', name: 'jstl'
    compile group: 'org.apache.tomcat.embed', name: 'tomcat-embed-jasper'
    runtimeOnly('com.h2database:h2')
    compileOnly('org.projectlombok:lombok')
    testImplementation('org.springframework.boot:spring-boot-starter-test')
}

Мой клиент - ESP8260 ESP-01, как я упоминал ранее.

Я использую эту библиотеку для esp: https://github.com/ukmaker/StompClient

Пример

Как я отправляю сообщения в приложение Spring Boot:

#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <WebSocketsClient.h>
#include "StompClient.h"


/**
* WiFi settings
**/
const char* wlan_ssid             = "dlink";
const char* wlan_password         = "";

/**
* Stomp server settings
**/
bool useWSS                       = false;
const char* ws_host               = "192.168.0.101";
const int ws_port                 = 8080;
const char* ws_baseurl            = "/gs-guide-websocket/"; // don't forget leading and trailing "/" !!!

bool sample = false;


// VARIABLES

WebSocketsClient webSocket;

Stomp::StompClient stomper(webSocket, ws_host, ws_port, ws_baseurl, true);

void setup() {



  // setup serial
  Serial.begin(115200);
  // flush it - ESP Serial seems to start with rubbish
  Serial.println();

  // connect to WiFi
  Serial.println("Logging into WLAN: " + String(wlan_ssid));
  Serial.print(" ...");
  WiFi.begin(wlan_ssid, wlan_password);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println(" success.");
  Serial.print("IP: "); Serial.println(WiFi.localIP());

  // Start the StompClient
  stomper.onConnect(subscribe);
  stomper.onError(error);

  if (useWSS) {
    stomper.beginSSL();
  } else {
    stomper.begin();
  }
}

// Once the Stomp connection has been made, subscribe to a topic
void subscribe(Stomp::StompCommand cmd) {
  Serial.println("Connected to STOMP broker");
  stomper.subscribe("/commands/blink", Stomp::CLIENT, handleBlinkMessage);
  stomper.subscribe("/commands/sample", Stomp::CLIENT, handleSampleMessage);
}

void error(const Stomp::StompCommand cmd) {
  Serial.println("ERROR: " + cmd.body);
}

Stomp::Stomp_Ack_t handleBlinkMessage(const Stomp::StompCommand cmd) {
  Serial.println("Got a message!");
  Serial.println(cmd.body);

  return Stomp::CONTINUE;
}

Stomp::Stomp_Ack_t handleSampleMessage(const Stomp::StompCommand cmd) {
  Serial.println("Got a message!");
  Serial.println(cmd.body);

  sample = true;
  return Stomp::CONTINUE;
}

void takeSample() {
  Serial.println("Take sample...");
  stomper.sendMessage("/esp/sensors", "{\\\"id\\\":\\\"ESP8266_1\\\",\\\"label\\\":\\\"ESP8266 ESP-01\\\",\\\"internalIP\\\":null,\\\"modules\\\":[{\\\"type\\\":\\\"switch\\\",\\\"internalId\\\":1,\\\"label\\\":\\\"Pokój Marcina: światło\\\",\\\"isOn\\\":false},{\\\"type\\\":\\\"temperatureSensor\\\",\\\"internalId\\\":1,\\\"label\\\":\\\"Termometr: pokój\\\",\\\"temperature\\\":34.22}]}");
}


void loop() {
  webSocket.loop();
  takeSample();
}

UPDATE

Найдено что-то в хранилище apahe (531 строка): https://ci.apache.org/projects/tomcat/tomcat9/coverage/org.apache.coyote.http11.Http11InputBuffer.html

Метод HttpParser.isHttpProtocol (255 и 86): https://ci.apache.org/projects/tomcat/tomcat9/coverage/org.apache.tomcat.util.http.parser.HttpParser.html

Возможно, клиент отправляет неверные данные, но я не знаю, что мне искать.

ОБНОВЛЕНИЕ .v2

Я включил дебаг на ESP

Отправлено сообщение от ESP:

[WS-Client] connect ws...
[WS-Client] connected to 192.168.0.100:80.
[WS-Client][sendHeader] sending header...
[WS-Client][sendHeader] handshake GET /socket/243/726111/websocket HTTP/1.1
Host: 192.168.0.100:80
Connection: Upgrade
Upgrade: websocket
Sec-WebSocket-Version: 13
Sec-WebSocket-Key: xQN1skwd8HNgHQg9taqSpQ==
Sec-WebSocket-Protocol: arduino
User-Agent: arduino-WebSocket-Client

Поступило:

[WS-Client][sendHeader] sending header... Done (21608us).
[WS-Client][handleHeader] RX: HTTP/1.1 302
[WS-Client][handleHeader] RX: Set-Cookie: JSESSIONID=43B94478F616F01BD3B8BDBB34ADDEDE; Path=/; HttpOnly
[WS-Client][handleHeader] RX: X-Content-Type-Options: nosniff
[WS-Client][handleHeader] RX: X-XSS-Protection: 1; mode=block
[WS-Client][handleHeader] RX: Cache-Control: no-cache, no-store, max-age=0, must-revalidate
[WS-Client][handleHeader] RX: Pragma: no-cache
[WS-Client][handleHeader] RX: Expires: 0
[WS-Client][handleHeader] RX: X-Frame-Options: SAMEORIGIN
[WS-Client][handleHeader] RX: Location: http://192.168.0.100/login
[WS-Client][handleHeader] RX: Content-Length: 0
[WS-Client][handleHeader] RX: Date: Sat, 16 Mar 2019 11:35:26 GMT
[WS-Client][handleHeader] Header read fin.
[WS-Client][handleHeader] Client settings:
[WS-Client][handleHeader]  - cURL: /socket/243/726111/websocket
[WS-Client][handleHeader]  - cKey: xQN1skwd8HNgHQg9taqSpQ==
[WS-Client][handleHeader] Server header:
[WS-Client][handleHeader]  - cCode: 302
[WS-Client][handleHeader]  - cIsUpgrade: 0
[WS-Client][handleHeader]  - cIsWebsocket: 0
[WS-Client][handleHeader]  - cAccept: 
[WS-Client][handleHeader]  - cProtocol: arduino
[WS-Client][handleHeader]  - cExtensions: 
[WS-Client][handleHeader]  - cVersion: 0
[WS-Client][handleHeader]  - cSessionId: 43B94478F616F01BD3B8BDBB34ADDEDE
[WS-Client][handleHeader] no Websocket connection close.
[WS-Client] client disconnected.
...