diff --git a/lib/http2.c b/lib/http2.c index 61d9d076..08466518 100644 --- a/lib/http2.c +++ b/lib/http2.c @@ -212,6 +212,7 @@ lws_http2_parser(struct libwebsocket_context *context, return 1; break; case LWS_HTTP2_FRAME_TYPE_HEADERS: + lwsl_info(" %02X\n", c); if (lws_hpack_interpret(context, wsi->u.http2.stream_wsi, c)) return 1; break; @@ -276,11 +277,16 @@ lws_http2_parser(struct libwebsocket_context *context, if (wsi->u.http2.stream_id) return 1; - if (wsi->u.http2.flags & 1) { // ack - } else { + if (wsi->u.http2.flags & LWS_HTTP2_FLAG_SETTINGS_ACK) { // ack + } else + /* non-ACK coming in means we must ACK it */ lws_set_protocol_write_pending(context, wsi, LWS_PPS_HTTP2_ACK_SETTINGS); - } break; + case LWS_HTTP2_FRAME_TYPE_CONTINUATION: + if (wsi->u.http2.END_HEADERS) + return 1; + goto update_end_headers; + case LWS_HTTP2_FRAME_TYPE_HEADERS: lwsl_info("LWS_HTTP2_FRAME_TYPE_HEADERS: stream_id = %d\n", wsi->u.http2.stream_id); if (!wsi->u.http2.stream_id) @@ -291,6 +297,13 @@ lws_http2_parser(struct libwebsocket_context *context, if (!wsi->u.http2.stream_wsi) return 1; + + /* END_STREAM means after servicing this, close the stream */ + wsi->u.http2.END_STREAM = !!(wsi->u.http2.flags & LWS_HTTP2_FLAG_END_STREAM); +update_end_headers: + /* no END_HEADERS means CONTINUATION must come */ + wsi->u.http2.END_HEADERS = !!(wsi->u.http2.flags & LWS_HTTP2_FLAG_END_HEADERS); + break; } if (wsi->u.http2.length == 0) wsi->u.http2.frame_state = 0; diff --git a/lib/libwebsockets.h b/lib/libwebsockets.h index 816b1df6..444d7160 100644 --- a/lib/libwebsockets.h +++ b/lib/libwebsockets.h @@ -1089,6 +1089,8 @@ lws_add_http_header_status(struct libwebsocket_context *context, unsigned char **p, unsigned char *end); +LWS_EXTERN int lws_http_transaction_completed(struct libwebsocket *wsi); + #ifdef LWS_USE_LIBEV LWS_VISIBLE LWS_EXTERN int libwebsocket_initloop( diff --git a/lib/output.c b/lib/output.c index b2ab45bf..7c5cb537 100644 --- a/lib/output.c +++ b/lib/output.c @@ -441,7 +441,7 @@ send_raw: n = LWS_HTTP2_FRAME_TYPE_DATA; if (protocol == LWS_WRITE_HTTP_HEADERS) { n = LWS_HTTP2_FRAME_TYPE_HEADERS; - flags = LWS_HTTP2_FLAGS__HEADER__END_HEADER; + flags = LWS_HTTP2_FLAG_END_HEADERS; } return lws_http2_frame_write(wsi, n, flags, wsi->u.http2.my_stream_id, len, buf); } diff --git a/lib/private-libwebsockets.h b/lib/private-libwebsockets.h index 7e01fe7d..e0a66800 100755 --- a/lib/private-libwebsockets.h +++ b/lib/private-libwebsockets.h @@ -608,12 +608,19 @@ enum lws_http2_wellknown_frame_types { LWS_HTTP2_FRAME_TYPE_COUNT /* always last */ }; +enum lws_http2_flags { + LWS_HTTP2_FLAG_END_STREAM = 1, + LWS_HTTP2_FLAG_END_HEADERS = 4, + LWS_HTTP2_FLAG_PADDED = 8, + LWS_HTTP2_FLAG_PRIORITY = 0x20, + + LWS_HTTP2_FLAG_SETTINGS_ACK = 1, +}; + #define LWS_HTTP2_STREAM_ID_MASTER 0 #define LWS_HTTP2_FRAME_HEADER_LENGTH 9 #define LWS_HTTP2_SETTINGS_LENGTH 6 -#define LWS_HTTP2_FLAGS__HEADER__END_HEADER 4 - struct http2_settings { unsigned int setting[LWS_HTTP2_SETTINGS__COUNT]; }; @@ -661,6 +668,9 @@ struct _lws_http2_related { unsigned char type; unsigned char flags; unsigned char frame_state; + + unsigned int END_STREAM:1; + unsigned int END_HEADERS:1; /* hpack */ enum http2_hpack_state hpack; @@ -862,6 +872,7 @@ libwebsockets_generate_client_handshake(struct libwebsocket_context *context, LWS_EXTERN int lws_handle_POLLOUT_event(struct libwebsocket_context *context, struct libwebsocket *wsi, struct libwebsocket_pollfd *pollfd); + /* * EXTENSIONS */ diff --git a/lib/server.c b/lib/server.c index b05b3ef7..6b9b84a0 100644 --- a/lib/server.c +++ b/lib/server.c @@ -640,6 +640,32 @@ libwebsocket_create_new_server_wsi(struct libwebsocket_context *context) return new_wsi; } +/** + * lws_http_transaction_completed() - wait for new http transaction or close + * @wsi: websocket connection + * + * Returns 1 if the HTTP connection must close now + * Returns 0 and resets connection to wait for new HTTP header / + * transaction if possible + */ + +LWS_VISIBLE +int lws_http_transaction_completed(struct libwebsocket *wsi) +{ + /* if we can't go back to accept new headers, drop the connection */ + if (wsi->u.http.connection_type != HTTP_CONNECTION_KEEP_ALIVE) { + lwsl_info("%s: close connection\n", __func__); + return 1; + } + + /* otherwise set ourselves up ready to go again */ + wsi->state = WSI_STATE_HTTP; + + lwsl_info("%s: await new transaction\n", __func__); + + return 0; +} + int lws_server_socket_service(struct libwebsocket_context *context, struct libwebsocket *wsi, struct libwebsocket_pollfd *pollfd) { @@ -739,8 +765,9 @@ try_pollout: break; } - /* nonzero for completion or error */ - if (libwebsockets_serve_http_file_fragment(context, wsi)) + /* >0 == completion, <0 == error */ + n = libwebsockets_serve_http_file_fragment(context, wsi); + if (n < 0 || (n > 0 && lws_http_transaction_completed(wsi))) libwebsocket_close_and_free_session(context, wsi, LWS_CLOSE_STATUS_NOSTATUS); break; @@ -1026,7 +1053,8 @@ LWS_VISIBLE int libwebsockets_return_http_status( * local files down the http link in a single step. * * Returning <0 indicates error and the wsi should be closed. Returning - * >0 indicates the file was completely sent and the wsi should be closed. + * >0 indicates the file was completely sent and + * lws_http_transaction_completed() called on the wsi (and close if != 0) * ==0 indicates the file transfer is started and needs more service later, * the wsi should be left alone. */ @@ -1055,14 +1083,14 @@ LWS_VISIBLE int libwebsockets_serve_http_file( } if (lws_add_http_header_status(context, wsi, 200, &p, end)) - return 1; + return -1; if (lws_add_http_header_by_token(context, wsi, WSI_TOKEN_HTTP_SERVER, (unsigned char *)"libwebsockets", 13, &p, end)) - return 1; + return -1; if (lws_add_http_header_by_token(context, wsi, WSI_TOKEN_HTTP_CONTENT_TYPE, (unsigned char *)content_type, strlen(content_type), &p, end)) - return 1; + return -1; n = sprintf((char *)clen, "%lu", (unsigned long)wsi->u.http.filelen); if (lws_add_http_header_by_token(context, wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH, clen, n, &p, end)) - return 1; + return -1; if (other_headers) { if ((end - p) < other_headers_len) @@ -1072,7 +1100,7 @@ LWS_VISIBLE int libwebsockets_serve_http_file( } if (lws_finalize_http_header(context, wsi, &p, end)) - return 1; + return -1; ret = libwebsocket_write(wsi, response, p - response, LWS_WRITE_HTTP_HEADERS); diff --git a/test-server/test-server.c b/test-server/test-server.c index d33d2f98..421eca4e 100644 --- a/test-server/test-server.c +++ b/test-server/test-server.c @@ -182,14 +182,14 @@ static int callback_http(struct libwebsocket_context *context, if (len < 1) { libwebsockets_return_http_status(context, wsi, HTTP_STATUS_BAD_REQUEST, NULL); - return -1; + goto try_to_reuse; } /* this example server has no concept of directories */ if (strchr((const char *)in + 1, '/')) { libwebsockets_return_http_status(context, wsi, HTTP_STATUS_FORBIDDEN, NULL); - return -1; + goto try_to_reuse; } /* if a legal POST URL, let it continue and accept data */ @@ -306,9 +306,10 @@ static int callback_http(struct libwebsocket_context *context, other_headers = leaf_path; } - if (libwebsockets_serve_http_file(context, wsi, buf, - mimetype, other_headers, n)) - return -1; /* through completion or error, close the socket */ + n = libwebsockets_serve_http_file(context, wsi, buf, + mimetype, other_headers, n); + if (n < 0 || ((n > 0) && lws_http_transaction_completed(wsi))) + return -1; /* error or can't reuse connection: close the socket */ /* * notice that the sending of the file completes asynchronously, @@ -331,16 +332,15 @@ static int callback_http(struct libwebsocket_context *context, case LWS_CALLBACK_HTTP_BODY_COMPLETION: lwsl_notice("LWS_CALLBACK_HTTP_BODY_COMPLETION\n"); - /* the whole of the sent body arried, close the connection */ + /* the whole of the sent body arrived, close or reuse the connection */ libwebsockets_return_http_status(context, wsi, HTTP_STATUS_OK, NULL); - - return -1; + goto try_to_reuse; case LWS_CALLBACK_HTTP_FILE_COMPLETION: // lwsl_info("LWS_CALLBACK_HTTP_FILE_COMPLETION seen\n"); /* kill the connection after we sent one file */ - return -1; + goto try_to_reuse; case LWS_CALLBACK_HTTP_WRITEABLE: /* @@ -386,6 +386,8 @@ flush_bail: libwebsocket_callback_on_writable(context, wsi); break; } + close(pss->fd); + goto try_to_reuse; bail: close(pss->fd); @@ -475,6 +477,12 @@ bail: } return 0; + +try_to_reuse: + if (lws_http_transaction_completed(wsi)) + return -1; + + return 0; }