Tatsuhiro Tsujikawa před 9 roky
rodič
revize
fb380d4016

+ 14 - 0
deps/wslay/.gitignore

@@ -27,3 +27,17 @@ stamp-h1
 INSTALL
 .DS_STORE
 tests/main
+tests/main.log
+tests/main.trs
+tests/test-suite.log
+/compile
+/test-driver
+/build.*
+doc/man
+doc/sphinx/_build
+/CMakeFiles/
+/cmake_install.cmake
+/lib/cmake_install.cmake
+/lib/libwslay.a
+/wslay-config.cmake
+/wslay.cmake

+ 3 - 2
deps/wslay/README.rst

@@ -1,7 +1,7 @@
 Wslay - The WebSocket library
 =============================
 
-Project Web: http://wslay.sourceforge.net/
+Project Web: https://github.com/tatsuhiro-t/wslay
 
 Wslay is a WebSocket library written in C.
 It implements the protocol version 13 described in
@@ -34,7 +34,8 @@ and
 Requirements
 ------------
 
-`Sphinx <http://sphinx.pocoo.org/>`_ is used to generate man pages.
+`Sphinx <http://www.sphinx-doc.org/en/stable/>`_ is used to generate
+man pages.
 
 To build and run the unit test programs, the following packages are
 needed:

+ 32 - 4
deps/wslay/lib/includes/wslay/wslay.h

@@ -145,10 +145,15 @@ enum wslay_opcode {
 #define wslay_is_ctrl_frame(opcode) ((opcode >> 3) & 1)
 
 /*
- * Macros that returns reserved bits: RSV1, RSV2, RSV3.  These macros
- * assumes that rsv is constructed by ((RSV1 << 2) | (RSV2 << 1) |
- * RSV3)
+ * Macros that represent and return reserved bits: RSV1, RSV2, RSV3.
+ * These macros assume that rsv is constructed by ((RSV1 << 2) |
+ * (RSV2 << 1) | RSV3)
  */
+#define WSLAY_RSV_NONE ((uint8_t) 0)
+#define WSLAY_RSV1_BIT (((uint8_t) 1) << 2)
+#define WSLAY_RSV2_BIT (((uint8_t) 1) << 1)
+#define WSLAY_RSV3_BIT (((uint8_t) 1) << 0)
+
 #define wslay_get_rsv1(rsv) ((rsv >> 2) & 1)
 #define wslay_get_rsv2(rsv) ((rsv >> 1) & 1)
 #define wslay_get_rsv3(rsv) (rsv & 1)
@@ -201,7 +206,7 @@ void wslay_frame_context_free(wslay_frame_context_ptr ctx);
  * 1 if this is masked frame, otherwise 0.  iocb->payload_length is
  * the payload_length of this frame.  iocb->data must point to the
  * payload data to be sent. iocb->data_length must be the length of
- * the data.  This function calls recv_callback function if it needs
+ * the data.  This function calls send_callback function if it needs
  * to send bytes.  This function calls gen_mask_callback function if
  * it needs new mask key.  This function returns the number of payload
  * bytes sent. Please note that it does not include any number of
@@ -407,6 +412,16 @@ int wslay_event_context_client_init
  */
 void wslay_event_context_free(wslay_event_context_ptr ctx);
 
+/*
+ * Sets a bit mask of allowed reserved bits.
+ * Currently only permitted values are WSLAY_RSV1_BIT to allow PMCE
+ * extension (see RFC-7692) or WSLAY_RSV_NONE to disable.
+ *
+ * Default: WSLAY_RSV_NONE
+ */
+void wslay_event_config_set_allowed_rsv_bits(wslay_event_context_ptr ctx,
+                                             uint8_t rsv);
+
 /*
  * Enables or disables buffering of an entire message for non-control
  * frames. If val is 0, buffering is enabled. Otherwise, buffering is
@@ -550,6 +565,12 @@ struct wslay_event_msg {
 int wslay_event_queue_msg(wslay_event_context_ptr ctx,
                           const struct wslay_event_msg *arg);
 
+/*
+ * Extended version of wslay_event_queue_msg which allows to set reserved bits.
+ */
+int wslay_event_queue_msg_ex(wslay_event_context_ptr ctx,
+                             const struct wslay_event_msg *arg, uint8_t rsv);
+
 /*
  * Specify "source" to generate message.
  */
@@ -607,6 +628,13 @@ struct wslay_event_fragmented_msg {
 int wslay_event_queue_fragmented_msg
 (wslay_event_context_ptr ctx, const struct wslay_event_fragmented_msg *arg);
 
+/*
+ * Extended version of wslay_event_queue_fragmented_msg which allows to set
+ * reserved bits.
+ */
+int wslay_event_queue_fragmented_msg_ex(wslay_event_context_ptr ctx,
+    const struct wslay_event_fragmented_msg *arg, uint8_t rsv);
+
 /*
  * Queues close control frame. This function is provided just for
  * convenience. wslay_event_queue_msg() can queue a close control

+ 52 - 12
deps/wslay/lib/wslay_event.c

@@ -206,7 +206,7 @@ static int wslay_event_imsg_append_chunk(struct wslay_event_imsg *m, size_t len)
 }
 
 static int wslay_event_omsg_non_fragmented_init
-(struct wslay_event_omsg **m, uint8_t opcode,
+(struct wslay_event_omsg **m, uint8_t opcode, uint8_t rsv,
  const uint8_t *msg, size_t msg_length)
 {
   *m = (struct wslay_event_omsg*)malloc(sizeof(struct wslay_event_omsg));
@@ -216,6 +216,7 @@ static int wslay_event_omsg_non_fragmented_init
   memset(*m, 0, sizeof(struct wslay_event_omsg));
   (*m)->fin = 1;
   (*m)->opcode = opcode;
+  (*m)->rsv = rsv;
   (*m)->type = WSLAY_NON_FRAGMENTED;
   if(msg_length) {
     (*m)->data = (uint8_t*)malloc(msg_length);
@@ -230,7 +231,7 @@ static int wslay_event_omsg_non_fragmented_init
 }
 
 static int wslay_event_omsg_fragmented_init
-(struct wslay_event_omsg **m, uint8_t opcode,
+(struct wslay_event_omsg **m, uint8_t opcode, uint8_t rsv,
  const union wslay_event_msg_source source,
  wslay_event_fragmented_msg_callback read_callback)
 {
@@ -240,6 +241,7 @@ static int wslay_event_omsg_fragmented_init
   }
   memset(*m, 0, sizeof(struct wslay_event_omsg));
   (*m)->opcode = opcode;
+  (*m)->rsv = rsv;
   (*m)->type = WSLAY_FRAGMENTED;
   (*m)->source = source;
   (*m)->read_callback = read_callback;
@@ -301,7 +303,9 @@ int wslay_event_queue_close(wslay_event_context_ptr ctx, uint16_t status_code,
     } else {
       ncode = htons(status_code);
       memcpy(msg, &ncode, 2);
-      memcpy(msg+2, reason, reason_length);
+      if(reason_length) {
+        memcpy(msg+2, reason, reason_length);
+      }
       msg_length = reason_length+2;
     }
     arg.opcode = WSLAY_CONNECTION_CLOSE;
@@ -328,19 +332,33 @@ static int wslay_event_queue_close_wrapper
   return 0;
 }
 
+static int wslay_event_verify_rsv_bits(wslay_event_context_ptr ctx, uint8_t rsv)
+{
+  return ((rsv & ~ctx->allowed_rsv_bits) == 0);
+}
+
 int wslay_event_queue_msg(wslay_event_context_ptr ctx,
                           const struct wslay_event_msg *arg)
+{
+  return wslay_event_queue_msg_ex(ctx, arg, WSLAY_RSV_NONE);
+}
+
+int wslay_event_queue_msg_ex(wslay_event_context_ptr ctx,
+                              const struct wslay_event_msg *arg, uint8_t rsv)
 {
   int r;
   struct wslay_event_omsg *omsg;
   if(!wslay_event_is_msg_queueable(ctx)) {
     return WSLAY_ERR_NO_MORE_MSG;
   }
-  if(wslay_is_ctrl_frame(arg->opcode) && arg->msg_length > 125) {
+  /* RSV1 is not allowed for control frames */
+  if((wslay_is_ctrl_frame(arg->opcode) &&
+      (arg->msg_length > 125 || wslay_get_rsv1(rsv)))
+        || !wslay_event_verify_rsv_bits(ctx, rsv)) {
     return WSLAY_ERR_INVALID_ARGUMENT;
   }
   if((r = wslay_event_omsg_non_fragmented_init
-      (&omsg, arg->opcode, arg->msg, arg->msg_length)) != 0) {
+      (&omsg, arg->opcode, rsv, arg->msg, arg->msg_length)) != 0) {
     return r;
   }
   if(wslay_is_ctrl_frame(arg->opcode)) {
@@ -359,17 +377,24 @@ int wslay_event_queue_msg(wslay_event_context_ptr ctx,
 
 int wslay_event_queue_fragmented_msg
 (wslay_event_context_ptr ctx, const struct wslay_event_fragmented_msg *arg)
+{
+  return wslay_event_queue_fragmented_msg_ex(ctx, arg, WSLAY_RSV_NONE);
+}
+
+int wslay_event_queue_fragmented_msg_ex(wslay_event_context_ptr ctx,
+    const struct wslay_event_fragmented_msg *arg, uint8_t rsv)
 {
   int r;
   struct wslay_event_omsg *omsg;
   if(!wslay_event_is_msg_queueable(ctx)) {
     return WSLAY_ERR_NO_MORE_MSG;
   }
-  if(wslay_is_ctrl_frame(arg->opcode)) {
+  if(wslay_is_ctrl_frame(arg->opcode) ||
+     !wslay_event_verify_rsv_bits(ctx, rsv)) {
     return WSLAY_ERR_INVALID_ARGUMENT;
   }
   if((r = wslay_event_omsg_fragmented_init
-      (&omsg, arg->opcode, arg->source, arg->read_callback)) != 0) {
+      (&omsg, arg->opcode, rsv, arg->source, arg->read_callback)) != 0) {
     return r;
   }
   if((r = wslay_queue_push(ctx->send_queue, omsg)) != 0) {
@@ -547,9 +572,11 @@ int wslay_event_recv(wslay_event_context_ptr ctx)
     r = wslay_frame_recv(ctx->frame_ctx, &iocb);
     if(r >= 0) {
       int new_frame = 0;
-      /* We only allow rsv == 0 ATM. */
-      if(iocb.rsv != 0 ||
-         ((ctx->server && !iocb.mask) || (!ctx->server && iocb.mask))) {
+      /* RSV1 is not allowed on control and continuation frames */
+      if((!wslay_event_verify_rsv_bits(ctx, iocb.rsv)) ||
+          (wslay_get_rsv1(iocb.rsv) && (wslay_is_ctrl_frame(iocb.opcode) ||
+             iocb.opcode == WSLAY_CONTINUATION_FRAME)) ||
+               (ctx->server && !iocb.mask) || (!ctx->server && iocb.mask)) {
         if((r = wslay_event_queue_close_wrapper
             (ctx, WSLAY_CODE_PROTOCOL_ERROR, NULL, 0)) != 0) {
           return r;
@@ -608,8 +635,10 @@ int wslay_event_recv(wslay_event_context_ptr ctx)
           }
         }
       }
-      if(ctx->imsg->opcode == WSLAY_TEXT_FRAME ||
-         ctx->imsg->opcode == WSLAY_CONNECTION_CLOSE) {
+      /* If RSV1 bit is set then it is too early for utf-8 validation */
+      if((!wslay_get_rsv1(ctx->imsg->rsv) &&
+          ctx->imsg->opcode == WSLAY_TEXT_FRAME) ||
+            ctx->imsg->opcode == WSLAY_CONNECTION_CLOSE) {
         size_t i;
         if(ctx->imsg->opcode == WSLAY_CONNECTION_CLOSE) {
           i = 2;
@@ -815,6 +844,7 @@ int wslay_event_send(wslay_event_context_ptr ctx)
       memset(&iocb, 0, sizeof(iocb));
       iocb.fin = 1;
       iocb.opcode = ctx->omsg->opcode;
+      iocb.rsv = ctx->omsg->rsv;
       iocb.mask = ctx->server^1;
       iocb.data = ctx->omsg->data+ctx->opayloadoff;
       iocb.data_length = ctx->opayloadlen-ctx->opayloadoff;
@@ -871,6 +901,7 @@ int wslay_event_send(wslay_event_context_ptr ctx)
       memset(&iocb, 0, sizeof(iocb));
       iocb.fin = ctx->omsg->fin;
       iocb.opcode = ctx->omsg->opcode;
+      iocb.rsv = ctx->omsg->rsv;
       iocb.mask = ctx->server ? 0 : 1;
       iocb.data = ctx->obufmark;
       iocb.data_length = ctx->obuflimit-ctx->obufmark;
@@ -886,6 +917,8 @@ int wslay_event_send(wslay_event_context_ptr ctx)
             ctx->omsg = NULL;
           } else {
             ctx->omsg->opcode = WSLAY_CONTINUATION_FRAME;
+            /* RSV1 is not set on continuation frames */
+            ctx->omsg->rsv = ctx->omsg->rsv & ~WSLAY_RSV1_BIT;
           }
         } else {
           break;
@@ -951,6 +984,13 @@ int wslay_event_get_close_sent(wslay_event_context_ptr ctx)
   return (ctx->close_status & WSLAY_CLOSE_SENT) > 0;
 }
 
+void wslay_event_config_set_allowed_rsv_bits(wslay_event_context_ptr ctx,
+                                             uint8_t rsv)
+{
+  /* We currently only allow WSLAY_RSV1_BIT or WSLAY_RSV_NONE */
+  ctx->allowed_rsv_bits = rsv & WSLAY_RSV1_BIT;
+}
+
 void wslay_event_config_set_no_buffering(wslay_event_context_ptr ctx, int val)
 {
   if(val) {

+ 2 - 0
deps/wslay/lib/wslay_event.h

@@ -56,6 +56,7 @@ enum wslay_event_msg_type {
 struct wslay_event_omsg {
   uint8_t fin;
   uint8_t opcode;
+  uint8_t rsv;
   enum wslay_event_msg_type type;
 
   uint8_t *data;
@@ -135,6 +136,7 @@ struct wslay_event_context {
   struct wslay_event_callbacks callbacks;
   struct wslay_event_frame_user_data frame_user_data;
   void *user_data;
+  uint8_t allowed_rsv_bits;
 };
 
 #endif /* WSLAY_EVENT_H */

+ 18 - 1
deps/wslay/tests/main.c

@@ -44,6 +44,7 @@ static int clean_suite1(void)
 int main(void)
 {
    CU_pSuite pSuite = NULL;
+   unsigned int num_tests_failed;
 
    /* initialize the CUnit test registry */
    if (CUE_SUCCESS != CU_initialize_registry())
@@ -93,8 +94,14 @@ int main(void)
                    test_wslay_event_send_fragmented_msg) ||
       !CU_add_test(pSuite, "wslay_event_send_fragmented_msg_with_ctrl",
                    test_wslay_event_send_fragmented_msg_with_ctrl) ||
+      !CU_add_test(pSuite, "wslay_event_send_fragmented_msg_with_rsv1",
+                   test_wslay_event_send_fragmented_msg_with_rsv1) ||
+      !CU_add_test(pSuite, "wslay_event_send_msg_with_rsv1",
+                   test_wslay_event_send_msg_with_rsv1) ||
       !CU_add_test(pSuite, "wslay_event_send_ctrl_msg_first",
                    test_wslay_event_send_ctrl_msg_first) ||
+      !CU_add_test(pSuite, "wslay_event_send_ctrl_msg_with_rsv1",
+                   test_wslay_event_send_ctrl_msg_with_rsv1) ||
       !CU_add_test(pSuite, "wslay_event_queue_close",
                    test_wslay_event_queue_close) ||
       !CU_add_test(pSuite, "wslay_event_queue_close_without_code",
@@ -109,10 +116,14 @@ int main(void)
                    test_wslay_event_callback_failure) ||
       !CU_add_test(pSuite, "wslay_event_no_buffering",
                    test_wslay_event_no_buffering) ||
+      !CU_add_test(pSuite, "wslay_event_recv_text_frame_with_rsv1",
+                   test_wslay_event_recv_text_frame_with_rsv1) ||
       !CU_add_test(pSuite, "wslay_event_frame_too_big",
                    test_wslay_event_frame_too_big) ||
       !CU_add_test(pSuite, "wslay_event_message_too_big",
                    test_wslay_event_message_too_big) ||
+      !CU_add_test(pSuite, "wslay_event_config_set_allowed_rsv_bits",
+                   test_wslay_event_config_set_allowed_rsv_bits) ||
       !CU_add_test(pSuite, "wslay_queue", test_wslay_queue)) {
      CU_cleanup_registry();
      return CU_get_error();
@@ -121,6 +132,12 @@ int main(void)
    /* Run all tests using the CUnit Basic interface */
    CU_basic_set_mode(CU_BRM_VERBOSE);
    CU_basic_run_tests();
+   num_tests_failed = CU_get_number_of_tests_failed();
    CU_cleanup_registry();
-   return CU_get_error();
+   if (CU_get_error() == CUE_SUCCESS) {
+     return (int)num_tests_failed;
+   } else {
+     printf("CUnit Error: %s\n", CU_get_error_msg());
+     return CU_get_error();
+   }
 }

+ 194 - 0
deps/wslay/tests/wslay_event_test.c

@@ -214,6 +214,71 @@ void test_wslay_event_send_fragmented_msg_with_ctrl(void)
   wslay_event_context_free(ctx);
 }
 
+void test_wslay_event_send_fragmented_msg_with_rsv1(void)
+{
+  wslay_event_context_ptr ctx;
+  struct wslay_event_callbacks callbacks;
+  struct my_user_data ud;
+  struct accumulator acc;
+  const char msg[] = "Hello";
+  struct scripted_data_feed df;
+  struct wslay_event_fragmented_msg arg;
+  const uint8_t ans[] = {
+    0x41, 0x03, 0x48, 0x65, 0x6c,
+    0x80, 0x02, 0x6c, 0x6f
+  };
+  scripted_data_feed_init(&df, (const uint8_t*)msg, sizeof(msg)-1);
+  df.feedseq[0] = 3;
+  df.feedseq[1] = 2;
+  memset(&callbacks, 0, sizeof(callbacks));
+  callbacks.send_callback = accumulator_send_callback;
+  memset(&acc, 0, sizeof(acc));
+  ud.acc = &acc;
+  wslay_event_context_server_init(&ctx, &callbacks, &ud);
+  wslay_event_config_set_allowed_rsv_bits(ctx, WSLAY_RSV1_BIT);
+
+  memset(&arg, 0, sizeof(arg));
+  arg.opcode = WSLAY_TEXT_FRAME;
+  arg.source.data = &df;
+  arg.read_callback = scripted_read_callback;
+  CU_ASSERT(0 ==
+            wslay_event_queue_fragmented_msg_ex(ctx, &arg, WSLAY_RSV1_BIT));
+  CU_ASSERT(0 == wslay_event_send(ctx));
+  CU_ASSERT_EQUAL(9, acc.length);
+  CU_ASSERT(0 == memcmp(ans, acc.buf, acc.length));
+  wslay_event_context_free(ctx);
+}
+
+void test_wslay_event_send_msg_with_rsv1(void)
+{
+  wslay_event_context_ptr ctx;
+  struct wslay_event_callbacks callbacks;
+  struct my_user_data ud;
+  struct accumulator acc;
+  const char msg[] = "Hello";
+  struct wslay_event_msg arg;
+  const uint8_t ans[] = {
+    0xc1, 0x05, 0x48, 0x65, 0x6c, 0x6c, 0x6f /* "Hello" */
+  };
+
+  memset(&callbacks, 0, sizeof(callbacks));
+  callbacks.send_callback = accumulator_send_callback;
+  memset(&acc, 0, sizeof(acc));
+  ud.acc = &acc;
+  wslay_event_context_server_init(&ctx, &callbacks, &ud);
+  wslay_event_config_set_allowed_rsv_bits(ctx, WSLAY_RSV1_BIT);
+
+  memset(&arg, 0, sizeof(arg));
+  arg.opcode = WSLAY_TEXT_FRAME;
+  arg.msg = (const uint8_t*)msg;
+  arg.msg_length = 5;
+  CU_ASSERT(0 == wslay_event_queue_msg_ex(ctx, &arg, WSLAY_RSV1_BIT));
+  CU_ASSERT(0 == wslay_event_send(ctx));
+  CU_ASSERT(7 == acc.length);
+  CU_ASSERT(0 == memcmp(ans, acc.buf, acc.length));
+  wslay_event_context_free(ctx);
+}
+
 void test_wslay_event_send_ctrl_msg_first(void)
 {
   wslay_event_context_ptr ctx;
@@ -246,6 +311,26 @@ void test_wslay_event_send_ctrl_msg_first(void)
   wslay_event_context_free(ctx);
 }
 
+void test_wslay_event_send_ctrl_msg_with_rsv1(void)
+{
+  wslay_event_context_ptr ctx;
+  struct wslay_event_callbacks callbacks;
+  struct wslay_event_msg arg;
+
+  memset(&callbacks, 0, sizeof(callbacks));
+
+  wslay_event_context_server_init(&ctx, &callbacks, NULL);
+  wslay_event_config_set_allowed_rsv_bits(ctx, WSLAY_RSV1_BIT);
+
+  memset(&arg, 0, sizeof(arg));
+  arg.opcode = WSLAY_PING;
+  arg.msg_length = 0;
+  CU_ASSERT(WSLAY_ERR_INVALID_ARGUMENT ==
+            wslay_event_queue_msg_ex(ctx, &arg, WSLAY_RSV1_BIT));
+
+  wslay_event_context_free(ctx);
+}
+
 void test_wslay_event_queue_close(void)
 {
   wslay_event_context_ptr ctx;
@@ -422,6 +507,89 @@ void test_wslay_event_no_buffering(void)
   wslay_event_context_free(ctx);
 }
 
+static void
+text_rsv1_on_msg_recv_callback(wslay_event_context_ptr ctx,
+                               const struct wslay_event_on_msg_recv_arg *arg,
+                               void *user_data)
+{
+  CU_ASSERT(WSLAY_TEXT_FRAME == arg->opcode);
+  CU_ASSERT(WSLAY_RSV1_BIT == arg->rsv);
+}
+
+void test_wslay_event_recv_text_frame_with_rsv1(void)
+{
+  wslay_event_context_ptr ctx;
+  struct wslay_event_callbacks callbacks;
+  struct my_user_data ud;
+  const uint8_t msg[] = {
+    0xc1, 0x07, 0xf2, 0x48, 0xcd, 0xc9, 0xc9, 0x07, 0x00 // "Hello" pm-deflate
+  };
+  const uint8_t fragmented[] = {
+    0x41, 0x03, 0xf2, 0x48, 0xcd, // First fragment
+    0x80, 0x04, 0xc9, 0xc9, 0x07, 0x00 // Second fragment, RSV1 bit off
+  };
+  const uint8_t bad_fragment[] = {
+    0x41, 0x03, 0xf2, 0x48, 0xcd,
+    0xc0, 0x04, 0xc9, 0xc9, 0x07, 0x00 // RSV1 bit on
+  };
+  const uint8_t pingmsg[] = {
+    0xc9, 0x03, 0x46, 0x6f, 0x6f /* ping with "Foo" */
+  };
+  struct scripted_data_feed df;
+
+  /* Message marked with RSV1 should skip UTF-8 validation */
+  scripted_data_feed_init(&df, (const uint8_t*)msg, sizeof(msg));
+  memset(&callbacks, 0, sizeof(callbacks));
+  ud.df = &df;
+  callbacks.recv_callback = scripted_recv_callback;
+  callbacks.on_msg_recv_callback = text_rsv1_on_msg_recv_callback;
+  wslay_event_context_client_init(&ctx, &callbacks, &ud);
+  wslay_event_config_set_allowed_rsv_bits(ctx, WSLAY_RSV1_BIT);
+  CU_ASSERT(0 == wslay_event_recv(ctx));
+  CU_ASSERT(0 == wslay_event_want_write(ctx));
+  wslay_event_context_free(ctx);
+
+  /* UTF-8 validation is skipped for continuation frames if the
+   * initial frame was marked with RSV1 bit */
+  scripted_data_feed_init(&df, (const uint8_t*)fragmented, sizeof(fragmented));
+  memset(&callbacks, 0, sizeof(callbacks));
+  ud.df = &df;
+  callbacks.recv_callback = scripted_recv_callback;
+  callbacks.on_msg_recv_callback = text_rsv1_on_msg_recv_callback;
+  wslay_event_context_client_init(&ctx, &callbacks, &ud);
+  wslay_event_config_set_allowed_rsv_bits(ctx, WSLAY_RSV1_BIT);
+  CU_ASSERT(0 == wslay_event_recv(ctx));
+  CU_ASSERT(0 == wslay_event_want_write(ctx));
+  wslay_event_context_free(ctx);
+
+  /* disallow RSV1 */
+  scripted_data_feed_init(&df, (const uint8_t*)msg, sizeof(msg));
+  wslay_event_context_client_init(&ctx, &callbacks, &ud);
+  CU_ASSERT(0 == wslay_event_recv(ctx));
+  /* Close frame must be queued */
+  CU_ASSERT(wslay_event_want_write(ctx));
+  wslay_event_context_free(ctx);
+
+  /* RSV1 is not allowed in continuation frame */
+  scripted_data_feed_init(&df, (const uint8_t*)bad_fragment,
+                                sizeof(bad_fragment));
+  wslay_event_context_client_init(&ctx, &callbacks, &ud);
+  wslay_event_config_set_allowed_rsv_bits(ctx, WSLAY_RSV1_BIT);
+  CU_ASSERT(0 == wslay_event_recv(ctx));
+  /* Close frame must be queued */
+  CU_ASSERT(wslay_event_want_write(ctx));
+  wslay_event_context_free(ctx);
+
+  /* RSV1 is not allowed in ping frame */
+  scripted_data_feed_init(&df, (const uint8_t*)pingmsg, sizeof(pingmsg));
+  wslay_event_context_client_init(&ctx, &callbacks, &ud);
+  wslay_event_config_set_allowed_rsv_bits(ctx, WSLAY_RSV1_BIT);
+  CU_ASSERT(0 == wslay_event_recv(ctx));
+  /* Close frame must be queued */
+  CU_ASSERT(wslay_event_want_write(ctx));
+  wslay_event_context_free(ctx);
+}
+
 void test_wslay_event_frame_too_big(void)
 {
   wslay_event_context_ptr ctx;
@@ -489,3 +657,29 @@ void test_wslay_event_message_too_big(void)
             wslay_event_get_status_code_sent(ctx));
   wslay_event_context_free(ctx);
 }
+
+void test_wslay_event_config_set_allowed_rsv_bits(void)
+{
+  wslay_event_context_ptr ctx;
+  struct wslay_event_callbacks callbacks;
+
+  memset(&callbacks, 0, sizeof(callbacks));
+
+  CU_ASSERT(0 == wslay_event_context_server_init(&ctx, &callbacks, NULL));
+  CU_ASSERT(WSLAY_RSV_NONE == ctx->allowed_rsv_bits);
+
+  wslay_event_config_set_allowed_rsv_bits(ctx, WSLAY_RSV1_BIT);
+
+  CU_ASSERT(WSLAY_RSV1_BIT == ctx->allowed_rsv_bits);
+
+  wslay_event_config_set_allowed_rsv_bits(ctx, WSLAY_RSV1_BIT | WSLAY_RSV2_BIT |
+                                                   WSLAY_RSV3_BIT);
+
+  CU_ASSERT(WSLAY_RSV1_BIT == ctx->allowed_rsv_bits);
+
+  wslay_event_config_set_allowed_rsv_bits(ctx, WSLAY_RSV2_BIT | WSLAY_RSV3_BIT);
+
+  CU_ASSERT(WSLAY_RSV_NONE == ctx->allowed_rsv_bits);
+
+  wslay_event_context_free(ctx);
+}

+ 5 - 0
deps/wslay/tests/wslay_event_test.h

@@ -27,7 +27,10 @@
 
 void test_wslay_event_send_fragmented_msg(void);
 void test_wslay_event_send_fragmented_msg_with_ctrl(void);
+void test_wslay_event_send_fragmented_msg_with_rsv1(void);
+void test_wslay_event_send_msg_with_rsv1(void);
 void test_wslay_event_send_ctrl_msg_first(void);
+void test_wslay_event_send_ctrl_msg_with_rsv1(void);
 void test_wslay_event_queue_close(void);
 void test_wslay_event_queue_close_without_code(void);
 void test_wslay_event_recv_close_without_code(void);
@@ -35,7 +38,9 @@ void test_wslay_event_reply_close(void);
 void test_wslay_event_no_more_msg(void);
 void test_wslay_event_callback_failure(void);
 void test_wslay_event_no_buffering(void);
+void test_wslay_event_recv_text_frame_with_rsv1(void);
 void test_wslay_event_frame_too_big(void);
 void test_wslay_event_message_too_big(void);
+void test_wslay_event_config_set_allowed_rsv_bits(void);
 
 #endif /* WSLAY_EVENT_TEST_H */