refactored websockets
[src/app-framework-binder.git] / src / websock.c
index dc0b5f4..41e063f 100644 (file)
@@ -170,12 +170,12 @@ void websock_pong(struct websock *ws)
 
 void websock_text(struct websock *ws, const char *text, size_t length)
 {
-       websock_send(ws, OPCODE_TEXT, NULL, 0);
+       websock_send(ws, OPCODE_TEXT, text, length);
 }
 
 void websock_binary(struct websock *ws, const void *data, size_t length)
 {
-       websock_send(ws, OPCODE_BINARY, NULL, 0);
+       websock_send(ws, OPCODE_BINARY, data, length);
 }
 
 static int read_header(struct websock *ws)
@@ -190,9 +190,25 @@ static int read_header(struct websock *ws)
        return 0;
 }
 
+static int check_control_header(struct websock *ws)
+{
+       /* sanity checks */
+       if (FRAME_GET_RSV1(ws->header[0]) != 0)
+               return 0;
+       if (FRAME_GET_RSV2(ws->header[0]) != 0)
+               return 0;
+       if (FRAME_GET_RSV3(ws->header[0]) != 0)
+               return 0;
+       if (FRAME_GET_MASK(ws->header[1]))
+               return 0;
+       if (FRAME_GET_OPCODE(ws->header[0]) == OPCODE_CLOSE)
+               return FRAME_GET_PAYLOAD_LEN(ws->header[1]) != 1;
+       return FRAME_GET_PAYLOAD_LEN(ws->header[1]) == 0;
+}
+
 int websock_dispatch(struct websock *ws)
 {
- loop:
+loop:
        switch (ws->state) {
        case STATE_INIT:
                ws->lenhead = 0;
@@ -201,17 +217,10 @@ int websock_dispatch(struct websock *ws)
 
        case STATE_START:
                /* read the header */
-               if (!read_header(ws))
+               if (read_header(ws))
                        return -1;
                else if (ws->lenhead < ws->szhead)
                        return 0;
-               /* sanity checks */
-               if (FRAME_GET_RSV1(ws->header[0]) != 0)
-                       goto protocol_error;
-               if (FRAME_GET_RSV2(ws->header[0]) != 0)
-                       goto protocol_error;
-               if (FRAME_GET_RSV3(ws->header[0]) != 0)
-                       goto protocol_error;
                /* fast track */
                switch (FRAME_GET_OPCODE(ws->header[0])) {
                case OPCODE_CONTINUATION:
@@ -219,17 +228,13 @@ int websock_dispatch(struct websock *ws)
                case OPCODE_BINARY:
                        break;
                case OPCODE_CLOSE:
-                       if (FRAME_GET_MASK(ws->header[1]))
-                               goto protocol_error;
-                       if (FRAME_GET_PAYLOAD_LEN(ws->header[1]) == 1)
+                       if (!check_control_header(ws))
                                goto protocol_error;
                        if (FRAME_GET_PAYLOAD_LEN(ws->header[1]))
                                ws->szhead += 2;
                        break;
                case OPCODE_PING:
-                       if (FRAME_GET_MASK(ws->header[1]))
-                               goto protocol_error;
-                       if (FRAME_GET_PAYLOAD_LEN(ws->header[1]) != 0)
+                       if (!check_control_header(ws))
                                goto protocol_error;
                        if (ws->itf->on_ping)
                                ws->itf->on_ping(ws->closure);
@@ -238,16 +243,14 @@ int websock_dispatch(struct websock *ws)
                        ws->state = STATE_INIT;
                        goto loop;
                case OPCODE_PONG:
-                       if (FRAME_GET_MASK(ws->header[1]))
-                               goto protocol_error;
-                       if (FRAME_GET_PAYLOAD_LEN(ws->header[1]) != 0)
+                       if (!check_control_header(ws))
                                goto protocol_error;
                        if (ws->itf->on_pong)
                                ws->itf->on_pong(ws->closure);
                        ws->state = STATE_INIT;
                        goto loop;
                default:
-                       goto protocol_error;
+                       break;
                }
                /* update heading size */
                switch (FRAME_GET_PAYLOAD_LEN(ws->header[1])) {
@@ -262,7 +265,7 @@ int websock_dispatch(struct websock *ws)
 
        case STATE_LENGTH:
                /* continue to read the header */
-               if (!read_header(ws))
+               if (read_header(ws))
                        return -1;
                else if (ws->lenhead < ws->szhead)
                        return 0;
@@ -295,7 +298,30 @@ int websock_dispatch(struct websock *ws)
                        ((unsigned char *)&ws->mask)[3] = ws->header[ws->szhead - 1];
                } else
                        ws->mask = 0;
+
+               /* all heading fields are known, process */
                ws->state = STATE_DATA;
+               if (ws->itf->on_extension != NULL) {
+                       if (ws->itf->on_extension(ws->closure,
+                                       FRAME_GET_FIN(ws->header[0]),
+                                       FRAME_GET_RSV1(ws->header[0]),
+                                       FRAME_GET_RSV2(ws->header[0]),
+                                       FRAME_GET_RSV3(ws->header[0]),
+                                       FRAME_GET_OPCODE(ws->header[0]),
+                                       (size_t) ws->length)) {
+                               return 0;
+                       }
+               }
+
+               /* not an extension case */
+               if (FRAME_GET_RSV1(ws->header[0]) != 0)
+                       goto protocol_error;
+               if (FRAME_GET_RSV2(ws->header[0]) != 0)
+                       goto protocol_error;
+               if (FRAME_GET_RSV3(ws->header[0]) != 0)
+                       goto protocol_error;
+
+               /* handle */
                switch (FRAME_GET_OPCODE(ws->header[0])) {
                case OPCODE_CONTINUATION:
                        ws->itf->on_continue(ws->closure,
@@ -320,9 +346,11 @@ int websock_dispatch(struct websock *ws)
                                                  (size_t) ws->length);
                        else
                                ws->itf->on_close(ws->closure,
-                                                 STATUS_CODE_UNSET, 0);
+                                                 WEBSOCKET_CODE_NOT_SET, 0);
                        ws->itf->disconnect(ws->closure);
                        return 0;
+               default:
+                       goto protocol_error;
                }
                break;
 
@@ -338,11 +366,11 @@ int websock_dispatch(struct websock *ws)
        goto loop;
 
  too_long_error:
-       websock_close_code(ws, STATUS_CODE_MESSAGE_TOO_LARGE);
+       websock_close_code(ws, WEBSOCKET_CODE_MESSAGE_TOO_LARGE);
        return 0;
 
  protocol_error:
-       websock_close_code(ws, STATUS_CODE_PROTOCOL_ERROR);
+       websock_close_code(ws, WEBSOCKET_CODE_PROTOCOL_ERROR);
        return 0;
 }
 
@@ -377,7 +405,7 @@ ssize_t websock_read(struct websock * ws, void *buffer, size_t size)
                        }
                        b32 = (uint32_t *) b8;
                        while (size >= sizeof(uint32_t)) {
-                               *b32++ ^= m;
+                               *b32++ ^= mask;
                                size -= sizeof(uint32_t);
                        }
                        b8 = (uint8_t *) b32;
@@ -398,17 +426,18 @@ ssize_t websock_read(struct websock * ws, void *buffer, size_t size)
 
 void websock_drop(struct websock *ws)
 {
-       char buffer[4096];
+       char buffer[8000];
 
        while (ws->length && ws_read(ws, buffer, sizeof buffer) >= 0) ;
 }
 
-struct websock *websock_create(const struct websock_itf *itf, void *closure)
+struct websock *websock_create_v13(const struct websock_itf *itf, void *closure)
 {
        struct websock *result = calloc(1, sizeof *result);
        if (result) {
                result->itf = itf;
                result->closure = closure;
+               result->maxlength = 65000;
        }
        return result;
 }