* Sec-WebSocket-Protocol: chat
*/
+#ifndef min
+#define min(a, b) ((a) < (b) ? (a) : (b))
+#endif
+
/*
* We have to take care about parsing because the headers may be split
* into multiple fragments. They may contain unknown headers with arbitrary
struct libwebsocket *wsi, unsigned char *buf, size_t len)
{
size_t n;
+ int body_chunk_len;
+ int content_remain = 0;
+ unsigned char *last_char;
switch (wsi->state) {
-
- case WSI_STATE_HTTP_BODY:
-http_postbody:
- while (len--) {
-
- if (wsi->u.http.content_length_seen >= wsi->u.http.content_length)
- break;
-
- wsi->u.http.post_buffer[wsi->u.http.body_index++] = *buf++;
- wsi->u.http.content_length_seen++;
- n = wsi->protocol->rx_buffer_size;
- if (!n)
- n = LWS_MAX_SOCKET_IO_BUF;
-
- if (wsi->u.http.body_index != n &&
- wsi->u.http.content_length_seen != wsi->u.http.content_length)
- continue;
-
- if (wsi->protocol->callback) {
- n = wsi->protocol->callback(
- wsi->protocol->owning_server, wsi,
- LWS_CALLBACK_HTTP_BODY,
- wsi->user_space, wsi->u.http.post_buffer,
- wsi->u.http.body_index);
- wsi->u.http.body_index = 0;
- if (n)
- goto bail;
- }
-
- if (wsi->u.http.content_length_seen == wsi->u.http.content_length) {
- /* he sent the content in time */
- libwebsocket_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
- n = wsi->protocol->callback(
- wsi->protocol->owning_server, wsi,
- LWS_CALLBACK_HTTP_BODY_COMPLETION,
- wsi->user_space, NULL, 0);
- wsi->u.http.body_index = 0;
- if (n)
- goto bail;
- }
-
- }
-
- /*
- * we need to spill here so everything is seen in the case
- * there is no content-length
- */
- if (wsi->u.http.body_index && wsi->protocol->callback) {
- n = wsi->protocol->callback(
- wsi->protocol->owning_server, wsi,
- LWS_CALLBACK_HTTP_BODY,
- wsi->user_space, wsi->u.http.post_buffer,
- wsi->u.http.body_index);
- wsi->u.http.body_index = 0;
- if (n)
- goto bail;
- }
- break;
-
- case WSI_STATE_HTTP_ISSUING_FILE:
+http_new:
case WSI_STATE_HTTP:
+ case WSI_STATE_HTTP_ISSUING_FILE:
wsi->state = WSI_STATE_HTTP_HEADERS;
wsi->u.hdr.parser_state = WSI_TOKEN_NAME_PART;
wsi->u.hdr.lextable_pos = 0;
/* fallthru */
case WSI_STATE_HTTP_HEADERS:
-
lwsl_parser("issuing %d bytes to parser\n", (int)len);
if (lws_handshake_client(wsi, &buf, len))
goto bail;
+ last_char = buf;
+ if (lws_handshake_server(context, wsi, &buf, len))
+ /* Handshake indicates this session is done. */
+ goto bail;
+
/* It's possible that we've exhausted our data already, but
* lws_handshake_server doesn't update len for us. Figure out how
* much was read, so that we can proceed appropriately: */
- {
- unsigned char *last_char = buf;
- switch (lws_handshake_server(context, wsi, &buf, len)) {
- case 1:
- goto bail;
- case 2:
- len -= (buf - last_char);
+ len -= (buf - last_char);
+ switch (wsi->state) {
+ case WSI_STATE_HTTP:
+ case WSI_STATE_HTTP_HEADERS:
+ goto http_complete;
+ case WSI_STATE_HTTP_ISSUING_FILE:
+ goto read_ok;
+ case WSI_STATE_HTTP_BODY:
+ wsi->u.http.content_remain = wsi->u.http.content_length;
goto http_postbody;
+ default:
+ break;
+ }
+ break;
+
+ case WSI_STATE_HTTP_BODY:
+http_postbody:
+ while (len && wsi->u.http.content_remain) {
+ /* Copy as much as possible, up to the limit of:
+ * what we have in the read buffer (len)
+ * remaining portion of the POST body (content_remain)
+ */
+ body_chunk_len = min(wsi->u.http.content_remain,len);
+ wsi->u.http.content_remain -= body_chunk_len;
+ len -= body_chunk_len;
+
+ if (wsi->protocol->callback) {
+ n = wsi->protocol->callback(
+ wsi->protocol->owning_server, wsi,
+ LWS_CALLBACK_HTTP_BODY, wsi->user_space,
+ buf, body_chunk_len);
+ if (n)
+ goto bail;
+ }
+ buf += body_chunk_len;
+
+ if (!wsi->u.http.content_remain) {
+ /* he sent the content in time */
+ libwebsocket_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
+ if (wsi->protocol->callback) {
+ n = wsi->protocol->callback(
+ wsi->protocol->owning_server, wsi,
+ LWS_CALLBACK_HTTP_BODY_COMPLETION,
+ wsi->user_space, NULL, 0);
+ if (n)
+ goto bail;
+ }
+ goto http_complete;
}
}
break;
- case WSI_STATE_AWAITING_CLOSE_ACK:
case WSI_STATE_ESTABLISHED:
+ case WSI_STATE_AWAITING_CLOSE_ACK:
if (lws_handshake_client(wsi, &buf, len))
goto bail;
switch (wsi->mode) {
break;
}
+read_ok:
+ /* Nothing more to do for now. */
+ lwsl_debug("libwebsocket_read: read_ok\n");
+
return 0;
+http_complete:
+ lwsl_debug("libwebsocket_read: http_complete\n");
+ /* Handle keep-alives, by preparing for a new request: */
+ if (wsi->u.http.connection_type == HTTP_CONNECTION_KEEP_ALIVE) {
+ wsi->state = WSI_STATE_HTTP;
+ wsi->mode = LWS_CONNMODE_HTTP_SERVING;
+ lwsl_debug("libwebsocket_read: keep-alive\n");
+
+ if (lws_allocate_header_table(wsi))
+ goto bail;
+
+ /* If we have more data, loop back around: */
+ if (len)
+ goto http_new;
+
+ return 0;
+ }
+
bail:
lwsl_debug("closing connection at libwebsocket_read bail:\n");
struct allocated_headers *ah;
char *uri_ptr = NULL;
int uri_len = 0;
+ enum http_version request_version;
+ enum http_connection_type connection_type;
+ int http_version_len;
char content_length_str[32];
int n;
wsi->u.http.content_length = atoi(content_length_str);
}
- if (wsi->u.http.content_length > 0) {
- wsi->u.http.body_index = 0;
- n = wsi->protocol->rx_buffer_size;
- if (!n)
- n = LWS_MAX_SOCKET_IO_BUF;
- wsi->u.http.post_buffer = malloc(n);
- if (!wsi->u.http.post_buffer) {
- lwsl_err("Unable to allocate post buffer\n");
- n = -1;
- goto cleanup;
+ /* http_version? Default to 1.0, override with token: */
+ request_version = HTTP_VERSION_1_0;
+
+ /* Works for single digit HTTP versions. : */
+ http_version_len = lws_hdr_total_length(wsi, WSI_TOKEN_HTTP);
+ if (http_version_len > 7) {
+ char http_version_str[10];
+ lws_hdr_copy(wsi, http_version_str,
+ sizeof(http_version_str) - 1, WSI_TOKEN_HTTP);
+ if (http_version_str[5] == '1' &&
+ http_version_str[7] == '1') {
+ request_version = HTTP_VERSION_1_1;
}
}
+ wsi->u.http.request_version = request_version;
+
+ /* HTTP/1.1 defaults to "keep-alive", 1.0 to "close" */
+ if (request_version == HTTP_VERSION_1_1)
+ connection_type = HTTP_CONNECTION_KEEP_ALIVE;
+
+ else
+ connection_type = HTTP_CONNECTION_CLOSE;
+
+
+ /* Override default if http "Connection:" header: */
+ if (lws_hdr_total_length(wsi, WSI_TOKEN_CONNECTION)) {
+ char http_conn_str[20];
+ lws_hdr_copy(wsi, http_conn_str,
+ sizeof(http_conn_str)-1, WSI_TOKEN_CONNECTION);
+ http_conn_str[sizeof(http_conn_str)-1] = '\0';
+ if (strcasecmp(http_conn_str,"keep-alive") == 0)
+ connection_type = HTTP_CONNECTION_KEEP_ALIVE;
+
+ else if (strcasecmp(http_conn_str,"close") == 0)
+ connection_type = HTTP_CONNECTION_CLOSE;
+
+ }
+ wsi->u.http.connection_type = connection_type;
n = 0;
if (wsi->protocol->callback)
return 1; /* struct ah ptr already nuked */
}
- /*
- * (if callback didn't start sending a file)
- * deal with anything else as body, whether
- * there was a content-length or not
- */
-
+ /* If we're not issuing a file, check for content_length or
+ * HTTP keep-alive. No keep-alive header allocation for
+ * ISSUING_FILE, as this uses HTTP/1.0.
+ * In any case, return 0 and let libwebsocket_read decide how to
+ * proceed based on state. */
if (wsi->state != WSI_STATE_HTTP_ISSUING_FILE)
- wsi->state = WSI_STATE_HTTP_BODY;
- return 2; /* goto http_postbody; */
+
+ /* Prepare to read body if we have a content length: */
+ if (wsi->u.http.content_length > 0)
+ wsi->state = WSI_STATE_HTTP_BODY;
+
+
+ return 0; /* don't bail out of libwebsocket_read, just yet */
}
if (!wsi->protocol)
if (wsi->state != WSI_STATE_FLUSHING_STORED_SEND_BEFORE_CLOSE) {
/* hm this may want to send (via HTTP callback for example) */
-
n = libwebsocket_read(context, wsi,
context->service_buffer, len);
if (n < 0)