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;
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)
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;
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(
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);
}
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];
};
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;
LWS_EXTERN int
lws_handle_POLLOUT_event(struct libwebsocket_context *context,
struct libwebsocket *wsi, struct libwebsocket_pollfd *pollfd);
+
/*
* EXTENSIONS
*/
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)
{
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;
* 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.
*/
}
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)
}
if (lws_finalize_http_header(context, wsi, &p, end))
- return 1;
+ return -1;
ret = libwebsocket_write(wsi, response,
p - response, LWS_WRITE_HTTP_HEADERS);
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 */
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,
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:
/*
libwebsocket_callback_on_writable(context, wsi);
break;
}
+ close(pss->fd);
+ goto try_to_reuse;
bail:
close(pss->fd);
}
return 0;
+
+try_to_reuse:
+ if (lws_http_transaction_completed(wsi))
+ return -1;
+
+ return 0;
}