/*
* libwebsockets - small server side websockets and web server implementation
*
- * Copyright (C) 2010-2013 Andy Green <andy@warmcat.com>
+ * Copyright (C) 2010-2015 Andy Green <andy@warmcat.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* 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
* argument lengths. So, we parse using a single-character at a time state
* machine that is completely independent of packet size.
+ *
+ * Returns <0 for error or length of chars consumed from buf (up to len)
*/
LWS_VISIBLE int
-libwebsocket_read(struct libwebsocket_context *context,
- struct libwebsocket *wsi, unsigned char *buf, size_t len)
+lws_read(struct lws *wsi, unsigned char *buf, lws_filepos_t len)
{
+ unsigned char *last_char, *oldbuf = buf;
+ lws_filepos_t body_chunk_len;
size_t n;
- switch (wsi->state) {
-
- case WSI_STATE_HTTP_BODY:
-http_postbody:
- while (len--) {
+ lwsl_debug("%s: incoming len %d state %d\n", __func__, (int)len, wsi->state);
- if (wsi->u.http.content_length_seen >= wsi->u.http.content_length)
- break;
+ switch (wsi->state) {
+#ifdef LWS_USE_HTTP2
+ case LWSS_HTTP2_AWAIT_CLIENT_PREFACE:
+ case LWSS_HTTP2_ESTABLISHED_PRE_SETTINGS:
+ case LWSS_HTTP2_ESTABLISHED:
+ n = 0;
+ while (n < len) {
+ /*
+ * we were accepting input but now we stopped doing so
+ */
+ if (!(wsi->rxflow_change_to & LWS_RXFLOW_ALLOW)) {
+ lws_rxflow_cache(wsi, buf, n, len);
- 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;
+ return 1;
}
- 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;
+ /* account for what we're using in rxflow buffer */
+ if (wsi->rxflow_buffer)
+ wsi->rxflow_pos++;
+ if (lws_http2_parser(wsi, buf[n++])) {
+ lwsl_debug("%s: http2_parser bailed\n", __func__);
+ goto bail;
}
-
}
+ break;
+#endif
- /*
- * 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;
- }
+ case LWSS_HTTP_ISSUING_FILE:
+ return 0;
+
+ case LWSS_CLIENT_HTTP_ESTABLISHED:
break;
- case WSI_STATE_HTTP_ISSUING_FILE:
- case WSI_STATE_HTTP:
- wsi->state = WSI_STATE_HTTP_HEADERS;
- wsi->u.hdr.parser_state = WSI_TOKEN_NAME_PART;
- wsi->u.hdr.lextable_pos = 0;
+ case LWSS_HTTP:
+ wsi->hdr_parsing_completed = 0;
/* fallthru */
- case WSI_STATE_HTTP_HEADERS:
+ case LWSS_HTTP_HEADERS:
+ if (!wsi->u.hdr.ah) {
+ lwsl_err("%s: LWSS_HTTP_HEADERS: NULL ah\n", __func__);
+ assert(0);
+ }
lwsl_parser("issuing %d bytes to parser\n", (int)len);
- if (lws_handshake_client(wsi, &buf, len))
+ if (lws_handshake_client(wsi, &buf, (size_t)len))
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);
- goto http_postbody;
+ last_char = buf;
+ if (lws_handshake_server(wsi, &buf, (size_t)len))
+ /* Handshake indicates this session is done. */
+ goto bail;
+
+ /* we might have transitioned to RAW */
+ if (wsi->mode == LWSCM_RAW)
+ /* we gave the read buffer to RAW handler already */
+ goto read_ok;
+
+ /*
+ * It's possible that we've exhausted our data already, or
+ * rx flow control has stopped us dealing with this early,
+ * but lws_handshake_server doesn't update len for us.
+ * Figure out how much was read, so that we can proceed
+ * appropriately:
+ */
+ len -= (buf - last_char);
+ lwsl_debug("%s: thinks we have used %ld\n", __func__, (long)len);
+
+ if (!wsi->hdr_parsing_completed)
+ /* More header content on the way */
+ goto read_ok;
+
+ switch (wsi->state) {
+ case LWSS_HTTP:
+ case LWSS_HTTP_HEADERS:
+ goto read_ok;
+ case LWSS_HTTP_ISSUING_FILE:
+ goto read_ok;
+ case LWSS_HTTP_BODY:
+ wsi->u.http.content_remain =
+ wsi->u.http.content_length;
+ if (wsi->u.http.content_remain)
+ goto http_postbody;
+
+ /* there is no POST content */
+ goto postbody_completion;
+ default:
+ break;
+ }
+ break;
+
+ case LWSS_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;
+#ifdef LWS_WITH_CGI
+ if (wsi->cgi) {
+ struct lws_cgi_args args;
+
+ args.ch = LWS_STDIN;
+ args.stdwsi = &wsi->cgi->stdwsi[0];
+ args.data = buf;
+ args.len = body_chunk_len;
+
+ /* returns how much used */
+ n = user_callback_handle_rxflow(
+ wsi->protocol->callback,
+ wsi, LWS_CALLBACK_CGI_STDIN_DATA,
+ wsi->user_space,
+ (void *)&args, 0);
+ if ((int)n < 0)
+ goto bail;
+ } else {
+#endif
+ n = wsi->protocol->callback(wsi,
+ LWS_CALLBACK_HTTP_BODY, wsi->user_space,
+ buf, (size_t)body_chunk_len);
+ if (n)
+ goto bail;
+ n = (size_t)body_chunk_len;
+#ifdef LWS_WITH_CGI
+ }
+#endif
+ buf += n;
+
+ if (wsi->u.http.content_remain) {
+ lws_set_timeout(wsi, PENDING_TIMEOUT_HTTP_CONTENT,
+ wsi->context->timeout_secs);
+ break;
}
+ /* he sent all the content in time */
+postbody_completion:
+#ifdef LWS_WITH_CGI
+ /* if we're running a cgi, we can't let him off the hook just because he sent his POST data */
+ if (wsi->cgi)
+ lws_set_timeout(wsi, PENDING_TIMEOUT_CGI, wsi->context->timeout_secs);
+ else
+#endif
+ lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
+#ifdef LWS_WITH_CGI
+ if (!wsi->cgi)
+#endif
+ {
+ n = wsi->protocol->callback(wsi,
+ LWS_CALLBACK_HTTP_BODY_COMPLETION,
+ wsi->user_space, NULL, 0);
+ if (n)
+ goto bail;
+ }
+
+ break;
}
break;
- case WSI_STATE_AWAITING_CLOSE_ACK:
- case WSI_STATE_ESTABLISHED:
- if (lws_handshake_client(wsi, &buf, len))
+ case LWSS_ESTABLISHED:
+ case LWSS_AWAITING_CLOSE_ACK:
+ case LWSS_WAITING_TO_SEND_CLOSE_NOTIFICATION:
+ case LWSS_SHUTDOWN:
+ if (lws_handshake_client(wsi, &buf, (size_t)len))
goto bail;
switch (wsi->mode) {
- case LWS_CONNMODE_WS_SERVING:
+ case LWSCM_WS_SERVING:
- if (libwebsocket_interpret_incoming_packet(wsi, buf, len) < 0) {
+ if (lws_interpret_incoming_packet(wsi, &buf, (size_t)len) < 0) {
lwsl_info("interpret_incoming_packet has bailed\n");
goto bail;
}
}
break;
default:
- lwsl_err("libwebsocket_read: Unhandled state\n");
+ lwsl_err("%s: Unhandled state %d\n", __func__, wsi->state);
break;
}
- return 0;
+read_ok:
+ /* Nothing more to do for now */
+ lwsl_info("%s: read_ok, used %ld\n", __func__, (long)(buf - oldbuf));
-bail:
- lwsl_debug("closing connection at libwebsocket_read bail:\n");
+ return buf - oldbuf;
- libwebsocket_close_and_free_session(context, wsi,
- LWS_CLOSE_STATUS_NOSTATUS);
+bail:
+ //lwsl_notice("closing connection at lws_read bail:\n");
+ lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS);
return -1;
}