dummy handler: LWS_CALLBACK_HTTP_FILE_COMPLETION
[platform/upstream/libwebsockets.git] / lib / handshake.c
index dffd2d2..5e897e2 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)
  */
 
 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;
                        }
@@ -162,17 +244,19 @@ http_postbody:
                }
                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;
 }