diff --git a/include/libwebsockets/lws-callbacks.h b/include/libwebsockets/lws-callbacks.h index aa232bd07..8b49218e9 100644 --- a/include/libwebsockets/lws-callbacks.h +++ b/include/libwebsockets/lws-callbacks.h @@ -311,6 +311,20 @@ enum lws_callback_reasons { * do something different now. Any protocol allocation related * to the http transaction processing should be destroyed. */ + LWS_CALLBACK_HTTP_CONFIRM_UPGRADE = 86, + /**< This is your chance to reject an HTTP upgrade action. The + * name of the protocol being upgraded to is in 'in', and the ah + * is still bound to the wsi, so you can look at the headers. + * + * The default of returning 0 (ie, also if not handled) means the + * upgrade may proceed. Return <0 to just hang up the connection, + * or >0 if you have rejected the connection by returning http headers + * and response code yourself. + * + * There is no need for you to call transaction_completed() as the + * caller will take care of it when it sees you returned >0. + */ + /* --------------------------------------------------------------------- * ----- Callbacks related to HTTP Client ----- */ diff --git a/lib/roles/http/client/client.c b/lib/roles/http/client/client.c index b11cb615d..80c73a9dc 100644 --- a/lib/roles/http/client/client.c +++ b/lib/roles/http/client/client.c @@ -916,8 +916,7 @@ lws_client_interpret_server_handshake(struct lws *wsi) wsi->http.rx_content_length; } else /* can't do 1.1 without a content length or chunked */ if (!wsi->chunked) - wsi->http.conn_type = - HTTP_CONNECTION_CLOSE; + wsi->http.conn_type = HTTP_CONNECTION_CLOSE; /* * we seem to be good to go, give client last chance to check diff --git a/lib/roles/http/server/server.c b/lib/roles/http/server/server.c index 5e66e7537..046aa8e8a 100644 --- a/lib/roles/http/server/server.c +++ b/lib/roles/http/server/server.c @@ -1699,12 +1699,44 @@ raw_transition: lwsi_set_state(wsi, LRS_PRE_WS_SERVING_ACCEPT); lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0); - /* is this websocket protocol or normal http 1.0? */ - if (lws_hdr_total_length(wsi, WSI_TOKEN_UPGRADE)) { - if (!strcasecmp(lws_hdr_simple_ptr(wsi, - WSI_TOKEN_UPGRADE), - "websocket")) { + + const char *up = lws_hdr_simple_ptr(wsi, + WSI_TOKEN_UPGRADE); + + if (strcasecmp(up, "websocket") && + strcasecmp(up, "h2c")) { + lwsl_info("Unknown upgrade '%s'\n", up); + + if (lws_return_http_status(wsi, + HTTP_STATUS_FORBIDDEN, NULL) || + lws_http_transaction_completed(wsi)) + goto bail_nuke_ah; + } + + n = user_callback_handle_rxflow(wsi->protocol->callback, + wsi, LWS_CALLBACK_HTTP_CONFIRM_UPGRADE, + wsi->user_space, (char *)up, 0); + + /* just hang up? */ + + if (n < 0) + goto bail_nuke_ah; + + /* callback returned headers already, do t_c? */ + + if (n > 0) { + if (lws_http_transaction_completed(wsi)) + goto bail_nuke_ah; + + /* continue on */ + + return 0; + } + + /* callback said 0, it was allowed */ + + if (!strcasecmp(up, "websocket")) { #if defined(LWS_ROLE_WS) wsi->vhost->conn_stats.ws_upg++; lwsl_info("Upgrade to ws\n"); @@ -1712,17 +1744,12 @@ raw_transition: #endif } #if defined(LWS_WITH_HTTP2) - if (!strcasecmp(lws_hdr_simple_ptr(wsi, - WSI_TOKEN_UPGRADE), - "h2c")) { + if (!strcasecmp(up, "h2c")) { wsi->vhost->conn_stats.h2_upg++; lwsl_info("Upgrade to h2c\n"); goto upgrade_h2c; } #endif - lwsl_info("Unknown upgrade\n"); - /* dunno what he wanted to upgrade to */ - goto bail_nuke_ah; } /* no upgrade ack... he remained as HTTP */