win32: enable 64-bit file lengths
[platform/upstream/libwebsockets.git] / lib / handshake.c
index 8b5d9ea..98e75b3 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * 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)
  */
 
-#ifndef LWS_NO_SERVER
-extern int handshake_0405(struct libwebsocket_context *context, struct libwebsocket *wsi);
-#endif
-
-int
-libwebsocket_read(struct libwebsocket_context *context,
-                    struct libwebsocket *wsi, unsigned char * buf, size_t len)
+LWS_VISIBLE int
+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_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;
-               /* fallthru */
-       case WSI_STATE_HTTP_HEADERS:
-
-               lwsl_parser("issuing %d bytes to parser\n", (int)len);
-#ifdef _DEBUG
-               //fwrite(buf, 1, len, stderr);
-#endif
-
-#ifndef LWS_NO_CLIENT
-
-//             lwsl_info("mode=%d\n", wsi->mode);
+       lwsl_debug("%s: incoming len %d  state %d\n", __func__, (int)len, wsi->state);
 
-               switch (wsi->mode) {
-               case LWS_CONNMODE_WS_CLIENT_WAITING_PROXY_REPLY:
-               case LWS_CONNMODE_WS_CLIENT_ISSUE_HANDSHAKE:
-               case LWS_CONNMODE_WS_CLIENT_WAITING_SERVER_REPLY:
-               case LWS_CONNMODE_WS_CLIENT_WAITING_EXTENSION_CONNECT:
-               case LWS_CONNMODE_WS_CLIENT:
-                       for (n = 0; n < len; n++)
-                               if (libwebsocket_client_rx_sm(wsi, *buf++)) {
-                                       lwsl_info("libwebsocket_client_rx_sm failed\n");
-                                       goto bail;
-                               }
-                       return 0;
-               default:
-                       break;
-               }
-#endif
-#ifndef LWS_NO_SERVER
-               /* LWS_CONNMODE_WS_SERVING */
-
-               for (n = 0; n < len; n++)
-                       if (libwebsocket_parse(wsi, *buf++)) {
-                               lwsl_info("libwebsocket_parse failed\n");
-                               goto bail;
+       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);
+
+                               return 1;
                        }
 
-               if (wsi->u.hdr.parser_state != WSI_PARSING_COMPLETE)
-                       break;
-
-               lwsl_parser("libwebsocket_parse sees parsing complete\n");
-
-               /* is this websocket protocol or normal http 1.0? */
-
-               if (!wsi->u.hdr.hdrs[WSI_TOKEN_UPGRADE].token_len ||
-                            !wsi->u.hdr.hdrs[WSI_TOKEN_CONNECTION].token_len) {
-                       wsi->state = WSI_STATE_HTTP;
-                       if (wsi->protocol->callback)
-                               if (wsi->protocol->callback(context, wsi,
-                                               LWS_CALLBACK_HTTP,
-                                               wsi->user_space,
-                                               wsi->u.hdr.hdrs[WSI_TOKEN_GET_URI].token,
-                                               wsi->u.hdr.hdrs[WSI_TOKEN_GET_URI].token_len)) {
-                                       lwsl_info("LWS_CALLBACK_HTTP wanted to close\n");
-                                       goto bail;
-                               }
-                       return 0;
-               }
-
-               if (!wsi->protocol)
-                       lwsl_err("NULL protocol at libwebsocket_read\n");
-
-               /*
-                * It's websocket
-                *
-                * Make sure user side is happy about protocol
-                */
-
-               while (wsi->protocol->callback) {
-
-                       if (wsi->u.hdr.hdrs[WSI_TOKEN_PROTOCOL].token == NULL) {
-                               if (wsi->protocol->name == NULL)
-                                       break;
-                       } else
-                               if (wsi->protocol->name && strcmp(
-                                    wsi->u.hdr.hdrs[WSI_TOKEN_PROTOCOL].token,
-                                                     wsi->protocol->name) == 0)
-                                       break;
-
-                       wsi->protocol++;
-               }
-
-               /* we didn't find a protocol he wanted? */
-
-               if (wsi->protocol->callback == NULL) {
-                       if (wsi->u.hdr.hdrs[WSI_TOKEN_PROTOCOL].token == NULL) {
-                               lwsl_info("[no protocol] "
-                                       "mapped to protocol 0 handler\n");
-                               wsi->protocol = &context->protocols[0];
-                       } else {
-                               lwsl_err("Requested protocol %s "
-                                               "not supported\n",
-                                    wsi->u.hdr.hdrs[WSI_TOKEN_PROTOCOL].token);
+                       /* 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
 
-               /*
-                * Give the user code a chance to study the request and
-                * have the opportunity to deny it
-                */
+       case LWSS_CLIENT_HTTP_ESTABLISHED:
+               break;
 
-               if ((wsi->protocol->callback)(wsi->protocol->owning_server, wsi,
-                               LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION,
-                                               &wsi->u.hdr.hdrs[0], NULL, 0)) {
-                       lwsl_warn("User code denied connection\n");
-                       goto bail;
+       case LWSS_HTTP:
+               wsi->hdr_parsing_completed = 0;
+               /* fallthru */
+       case LWSS_HTTP_ISSUING_FILE:
+               wsi->state = LWSS_HTTP_HEADERS;
+               wsi->u.hdr.parser_state = WSI_TOKEN_NAME_PART;
+               wsi->u.hdr.lextable_pos = 0;
+               /* fallthru */
+       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);
 
-
-               /*
-                * Perform the handshake according to the protocol version the
-                * client announced
-                */
-
-               switch (wsi->ietf_spec_revision) {
-               case 13:
-                       lwsl_parser("libwebsocket_parse calling handshake_04\n");
-                       if (handshake_0405(context, wsi)) {
-                               lwsl_info("handshake_0405 xor 05 has failed the connection\n");
-                               goto bail;
-                       }
-                       break;
-
-               default:
-                       lwsl_warn("Unknown client spec version %d\n",
-                                                      wsi->ietf_spec_revision);
+               if (lws_handshake_client(wsi, &buf, (size_t)len))
                        goto bail;
-               }
-
-               /* drop the header info */
 
-               /* free up his parsing allocations... these are gone... */
-
-               for (n = 0; n < WSI_TOKEN_COUNT; n++)
-                       if (wsi->u.hdr.hdrs[n].token)
-                               free(wsi->u.hdr.hdrs[n].token);
-
-               wsi->mode = LWS_CONNMODE_WS_SERVING;
+               last_char = buf;
+               if (lws_handshake_server(wsi, &buf, (size_t)len))
+                       /* Handshake indicates this session is done. */
+                       goto bail;
 
-               /* union transition */
-               memset(&wsi->u, 0, sizeof wsi->u);
+               /* we might have transitioned to RAW */
+               if (wsi->mode == LWSCM_RAW)
+                        /* we gave the read buffer to RAW handler already */
+                       goto read_ok;
 
                /*
-                * create the frame buffer for this connection according to the
-                * size mentioned in the protocol definition.  If 0 there, use
-                * a big default for compatibility
+                * 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:
                 */
-
-               n = wsi->protocol->rx_buffer_size;
-               if (!n)
-                       n = LWS_MAX_SOCKET_IO_BUF;
-               n += LWS_SEND_BUFFER_PRE_PADDING + LWS_SEND_BUFFER_POST_PADDING;
-               wsi->u.ws.rx_user_buffer = malloc(n);
-               if (!wsi->u.ws.rx_user_buffer) {
-                       lwsl_err("Out of Mem allocating rx buffer %d\n", n);
-                       goto bail3;
+               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;
                }
-               lwsl_info("Allocating client RX buffer %d\n", n);
+               break;
 
-               lwsl_parser("accepted v%02d connection\n",
-                                                      wsi->ietf_spec_revision);
+       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
-               break;
+                               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;
 
-       case WSI_STATE_AWAITING_CLOSE_ACK:
-       case WSI_STATE_ESTABLISHED:
-#ifndef LWS_NO_CLIENT
-               switch (wsi->mode) {
-               case LWS_CONNMODE_WS_CLIENT:
-                       for (n = 0; n < len; n++)
-                               if (libwebsocket_client_rx_sm(wsi, *buf++) < 0) {
-                                       lwsl_info("client rx has bailed\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;
-                               }
+                       }
 
-                       return 0;
-               default:
                        break;
                }
-#endif
-#ifndef LWS_NO_SERVER
-               /* LWS_CONNMODE_WS_SERVING */
+               break;
 
-               if (libwebsocket_interpret_incoming_packet(wsi, buf, len) < 0) {
-                       lwsl_info("interpret_incoming_packet has bailed\n");
+       case LWSS_ESTABLISHED:
+       case LWSS_AWAITING_CLOSE_ACK:
+       case LWSS_SHUTDOWN:
+               if (lws_handshake_client(wsi, &buf, (size_t)len))
                        goto bail;
+               switch (wsi->mode) {
+               case LWSCM_WS_SERVING:
+
+                       if (lws_interpret_incoming_packet(wsi, &buf, (size_t)len) < 0) {
+                               lwsl_info("interpret_incoming_packet has bailed\n");
+                               goto bail;
+                       }
+                       break;
                }
-#endif
                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_info("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;
 }