http2 handle flags
authorAndy Green <andy.green@linaro.org>
Fri, 17 Oct 2014 00:38:44 +0000 (08:38 +0800)
committerAndy Green <andy.green@linaro.org>
Fri, 17 Oct 2014 01:33:16 +0000 (09:33 +0800)
Signed-off-by: Andy Green <andy.green@linaro.org>
lib/http2.c
lib/libwebsockets.h
lib/output.c
lib/private-libwebsockets.h
lib/server.c
test-server/test-server.c

index 61d9d07..0846651 100644 (file)
@@ -212,6 +212,7 @@ lws_http2_parser(struct libwebsocket_context *context,
                                                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;
@@ -276,11 +277,16 @@ lws_http2_parser(struct libwebsocket_context *context,
                                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)
@@ -291,6 +297,13 @@ lws_http2_parser(struct libwebsocket_context *context,
 
                                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;
index 816b1df..444d716 100644 (file)
@@ -1089,6 +1089,8 @@ lws_add_http_header_status(struct libwebsocket_context *context,
                            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(
index b2ab45b..7c5cb53 100644 (file)
@@ -441,7 +441,7 @@ send_raw:
                        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);
                }
index 7e01fe7..e0a6680 100755 (executable)
@@ -608,12 +608,19 @@ enum lws_http2_wellknown_frame_types {
        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];
 };
@@ -661,6 +668,9 @@ struct _lws_http2_related {
        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;
@@ -862,6 +872,7 @@ libwebsockets_generate_client_handshake(struct libwebsocket_context *context,
 LWS_EXTERN int
 lws_handle_POLLOUT_event(struct libwebsocket_context *context,
                              struct libwebsocket *wsi, struct libwebsocket_pollfd *pollfd);
+
 /*
  * EXTENSIONS
  */
index b05b3ef..6b9b84a 100644 (file)
@@ -640,6 +640,32 @@ libwebsocket_create_new_server_wsi(struct libwebsocket_context *context)
        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)
 {
@@ -739,8 +765,9 @@ try_pollout:
                        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;
@@ -1026,7 +1053,8 @@ LWS_VISIBLE int libwebsockets_return_http_status(
  *     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.
  */
@@ -1055,14 +1083,14 @@ LWS_VISIBLE int libwebsockets_serve_http_file(
        }
 
        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)
@@ -1072,7 +1100,7 @@ LWS_VISIBLE int libwebsockets_serve_http_file(
        }
 
        if (lws_finalize_http_header(context, wsi, &p, end))
-               return 1;
+               return -1;
        
        ret = libwebsocket_write(wsi, response,
                                   p - response, LWS_WRITE_HTTP_HEADERS);
index d33d2f9..421eca4 100644 (file)
@@ -182,14 +182,14 @@ static int callback_http(struct libwebsocket_context *context,
                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 */
@@ -306,9 +306,10 @@ static int callback_http(struct libwebsocket_context *context,
                        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,
@@ -331,16 +332,15 @@ static int callback_http(struct libwebsocket_context *context,
 
        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:
                /*
@@ -386,6 +386,8 @@ flush_bail:
                        libwebsocket_callback_on_writable(context, wsi);
                        break;
                }
+               close(pss->fd);
+               goto try_to_reuse;
 
 bail:
                close(pss->fd);
@@ -475,6 +477,12 @@ bail:
        }
 
        return 0;
+       
+try_to_reuse:
+       if (lws_http_transaction_completed(wsi))
+               return -1;
+
+       return 0;
 }