فهرست منبع

Unit test: Add test for websocket server and client

bel 10 سال پیش
والد
کامیت
bcf7f23742
2فایلهای تغییر یافته به همراه434 افزوده شده و 104 حذف شده
  1. 3 2
      test/private.c
  2. 431 102
      test/public_server.c

+ 3 - 2
test/private.c

@@ -209,8 +209,9 @@ END_TEST
 START_TEST(test_is_valid_uri)
 START_TEST(test_is_valid_uri)
 {
 {
 	ck_assert_int_eq(1, is_valid_uri("/api"));
 	ck_assert_int_eq(1, is_valid_uri("/api"));
-    ck_assert_int_eq(1, is_valid_uri("/api/"));
-    ck_assert_int_eq(1, is_valid_uri("/some/long/path%20with%20space/file.xyz"));
+	ck_assert_int_eq(1, is_valid_uri("/api/"));
+	ck_assert_int_eq(1,
+	                 is_valid_uri("/some/long/path%20with%20space/file.xyz"));
 	ck_assert_int_eq(0, is_valid_uri("api"));
 	ck_assert_int_eq(0, is_valid_uri("api"));
 	ck_assert_int_eq(1, is_valid_uri("*"));
 	ck_assert_int_eq(1, is_valid_uri("*"));
 	ck_assert_int_eq(0, is_valid_uri("*xy"));
 	ck_assert_int_eq(0, is_valid_uri("*xy"));

+ 431 - 102
test/public_server.c

@@ -55,13 +55,13 @@ const char *locate_ssl_cert(void)
 #else
 #else
 	    /* Appveyor */
 	    /* Appveyor */
 	    "..\\..\\..\\resources\\ssl_cert.pem"; /* TODO: the different paths
 	    "..\\..\\..\\resources\\ssl_cert.pem"; /* TODO: the different paths
-                                                * used in the different test 
-                                                * system is an unsolved 
-                                                * problem */
+	                                            * used in the different test
+	                                            * system is an unsolved
+	                                            * problem */
 #endif
 #endif
 #else
 #else
 #ifdef LOCAL_TEST
 #ifdef LOCAL_TEST
-        "../resources/ssl_cert.pem";
+	    "../resources/ssl_cert.pem";
 #else
 #else
 	    /* Travis */
 	    /* Travis */
 	    "../../resources/ssl_cert.pem"; // TODO: fix path in CI test environment
 	    "../../resources/ssl_cert.pem"; // TODO: fix path in CI test environment
@@ -95,12 +95,12 @@ START_TEST(test_the_test_environment)
 /* Check the pem file */
 /* Check the pem file */
 #ifdef _WIN32
 #ifdef _WIN32
 	strcpy(buf, wd);
 	strcpy(buf, wd);
-    strcat(buf, "\\");
+	strcat(buf, "\\");
 	strcat(buf, ssl_cert);
 	strcat(buf, ssl_cert);
 	f = fopen(buf, "rb");
 	f = fopen(buf, "rb");
 #else
 #else
 	strcpy(buf, wd);
 	strcpy(buf, wd);
-    strcat(buf, "/");
+	strcat(buf, "/");
 	strcat(buf, ssl_cert);
 	strcat(buf, ssl_cert);
 	f = fopen(buf, "r");
 	f = fopen(buf, "r");
 #endif
 #endif
@@ -269,9 +269,9 @@ static int request_test_handler(struct mg_connection *conn, void *cbdata)
 
 
 	mg_set_user_connection_data(conn, (void *)6543);
 	mg_set_user_connection_data(conn, (void *)6543);
 	cud = mg_get_user_connection_data(conn);
 	cud = mg_get_user_connection_data(conn);
-	ck_assert(cud == (void *)6543);
+	ck_assert_int_eq((int)cud, (int)6543);
 
 
-	ck_assert(cbdata == (void *)7);
+	ck_assert_int_eq((int)cbdata, (int)7);
 	strcpy(chunk_data, "123456789A123456789B123456789C");
 	strcpy(chunk_data, "123456789A123456789B123456789C");
 
 
 	mg_printf(conn,
 	mg_printf(conn,
@@ -291,11 +291,130 @@ static int request_test_handler(struct mg_connection *conn, void *cbdata)
 }
 }
 
 
 
 
+#ifdef USE_WEBSOCKET
+/****************************************************************************/
+/* WEBSOCKET SERVER                                                         */
+/****************************************************************************/
+const char *websocket_welcome_msg = "websocket welcome\n";
+const size_t websocket_welcome_msg_len = 18 /* strlen(websocket_welcome_msg) */;
+const char *websocket_acknowledge_msg = "websocket msg ok\n";
+const size_t websocket_acknowledge_msg_len =
+    17 /* strlen(websocket_acknowledge_msg) */;
+const char *websocket_goodbye_msg = "websocket bye\n";
+const size_t websocket_goodbye_msg_len = 14 /* strlen(websocket_goodbye_msg) */;
+
+
+int websock_server_connect(const struct mg_connection *conn, void *udata)
+{
+	ck_assert_int_eq((int)udata, 7531);
+	printf("Server: Websocket connected\n");
+	return 0; /* return 0 to accept every connection */
+}
+
+void websock_server_ready(struct mg_connection *conn, void *udata)
+{
+	ck_assert_int_eq((int)udata, 7531);
+	printf("Server: Websocket ready\n");
+
+	/* Send websocket welcome message */
+	mg_lock_connection(conn);
+	mg_websocket_write(conn,
+	                   WEBSOCKET_OPCODE_TEXT,
+	                   websocket_welcome_msg,
+	                   websocket_welcome_msg_len);
+	mg_unlock_connection(conn);
+}
+
+int websock_server_data(struct mg_connection *conn,
+                        int bits,
+                        char *data,
+                        size_t data_len,
+                        void *udata)
+{
+	ck_assert_int_eq((int)udata, 7531);
+	printf("Server: Got %u bytes from the client\n", data_len);
+
+	if (data_len < 3 || 0 != memcmp(data, "bye", 3)) {
+		/* Send websocket acknowledge message */
+		mg_lock_connection(conn);
+		mg_websocket_write(conn,
+		                   WEBSOCKET_OPCODE_TEXT,
+		                   websocket_acknowledge_msg,
+		                   websocket_acknowledge_msg_len);
+		mg_unlock_connection(conn);
+	} else {
+		/* Send websocket acknowledge message */
+		mg_lock_connection(conn);
+		mg_websocket_write(conn,
+		                   WEBSOCKET_OPCODE_TEXT,
+		                   websocket_goodbye_msg,
+		                   websocket_goodbye_msg_len);
+		mg_unlock_connection(conn);
+	}
+
+	return 1; /* return 1 to keep the connetion open */
+}
+
+void websock_server_close(const struct mg_connection *conn, void *udata)
+{
+	ck_assert_int_eq((int)udata, 7531);
+	printf("Server: Close connection\n");
+
+	/* Can not send a websocket goodbye message here - the connection is already
+	 * closed */
+}
+
+/****************************************************************************/
+/* WEBSOCKET CLIENT                                                         */
+/****************************************************************************/
+struct tclient_data {
+	void *data;
+	size_t len;
+	int closed;
+};
+
+static int websocket_client_data_handler(struct mg_connection *conn,
+                                         int flags,
+                                         char *data,
+                                         size_t data_len,
+                                         void *user_data)
+{
+	struct mg_context *ctx = mg_get_context(conn);
+	struct tclient_data *pclient_data =
+	    (struct tclient_data *)mg_get_user_data(ctx);
+	ck_assert(pclient_data != NULL);
+
+	printf("Client received data from server: ");
+	fwrite(data, 1, data_len, stdout);
+	printf("\n");
+
+	pclient_data->data = malloc(data_len);
+	ck_assert(pclient_data->data != NULL);
+	memcpy(pclient_data->data, data, data_len);
+	pclient_data->len = data_len;
+
+	return 1;
+}
+
+static void websocket_client_close_handler(const struct mg_connection *conn,
+                                           void *user_data)
+{
+	struct mg_context *ctx = mg_get_context(conn);
+	struct tclient_data *pclient_data =
+	    (struct tclient_data *)mg_get_user_data(ctx);
+	ck_assert(pclient_data != NULL);
+
+	printf("Client: Close handler\n");
+	pclient_data->closed++;
+}
+#endif
+
+
 START_TEST(test_request_handlers)
 START_TEST(test_request_handlers)
 {
 {
 	char ebuf[100];
 	char ebuf[100];
 	struct mg_context *ctx;
 	struct mg_context *ctx;
-	struct mg_connection *conn;
+	struct mg_connection *client_conn;
 	const struct mg_request_info *ri;
 	const struct mg_request_info *ri;
 	char uri[64];
 	char uri[64];
 	char buf[1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 8];
 	char buf[1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 8];
@@ -331,6 +450,16 @@ START_TEST(test_request_handlers)
 	int opt_idx = 0;
 	int opt_idx = 0;
 	const char *ssl_cert = locate_ssl_cert();
 	const char *ssl_cert = locate_ssl_cert();
 
 
+#ifdef USE_WEBSOCKET
+	struct tclient_data ws_client1_data = {NULL, 0, 0};
+	struct tclient_data ws_client2_data = {NULL, 0, 0};
+	struct tclient_data ws_client3_data = {NULL, 0, 0};
+	struct mg_connection *ws_client1_conn = NULL;
+	struct mg_connection *ws_client2_conn = NULL;
+	struct mg_connection *ws_client3_conn = NULL;
+#endif
+
+
 	memset((void *)OPTIONS, 0, sizeof(OPTIONS));
 	memset((void *)OPTIONS, 0, sizeof(OPTIONS));
 	OPTIONS[opt_idx++] = "listening_ports";
 	OPTIONS[opt_idx++] = "listening_ports";
 	OPTIONS[opt_idx++] = HTTP_PORT;
 	OPTIONS[opt_idx++] = HTTP_PORT;
@@ -381,124 +510,135 @@ START_TEST(test_request_handlers)
 	}
 	}
 
 
 
 
+#ifdef USE_WEBSOCKET
+	mg_set_websocket_handler(ctx,
+	                         "/websocket",
+	                         websock_server_connect,
+	                         websock_server_ready,
+	                         websock_server_data,
+	                         websock_server_close,
+	                         (void *)7531);
+#endif
+
+
 	/* Try to load non existing file */
 	/* Try to load non existing file */
-	conn = mg_download("localhost",
-	                   ipv4_port,
-	                   0,
-	                   ebuf,
-	                   sizeof(ebuf),
-	                   "%s",
-	                   "GET /file/not/found HTTP/1.0\r\n\r\n");
-	ck_assert(conn != NULL);
-	ri = mg_get_request_info(conn);
+	client_conn = mg_download("localhost",
+	                          ipv4_port,
+	                          0,
+	                          ebuf,
+	                          sizeof(ebuf),
+	                          "%s",
+	                          "GET /file/not/found HTTP/1.0\r\n\r\n");
+	ck_assert(client_conn != NULL);
+	ri = mg_get_request_info(client_conn);
 
 
 	ck_assert(ri != NULL);
 	ck_assert(ri != NULL);
 	ck_assert_str_eq(ri->uri, "404");
 	ck_assert_str_eq(ri->uri, "404");
-	mg_close_connection(conn);
+	mg_close_connection(client_conn);
 
 
 
 
 	/* Get data from callback */
 	/* Get data from callback */
-	conn = mg_download(
+	client_conn = mg_download(
 	    "localhost", ipv4_port, 0, ebuf, sizeof(ebuf), "%s", request);
 	    "localhost", ipv4_port, 0, ebuf, sizeof(ebuf), "%s", request);
-	ck_assert(conn != NULL);
-	ri = mg_get_request_info(conn);
+	ck_assert(client_conn != NULL);
+	ri = mg_get_request_info(client_conn);
 
 
 	ck_assert(ri != NULL);
 	ck_assert(ri != NULL);
 	ck_assert_str_eq(ri->uri, "200");
 	ck_assert_str_eq(ri->uri, "200");
-	i = mg_read(conn, buf, sizeof(buf));
+	i = mg_read(client_conn, buf, sizeof(buf));
 	ck_assert_int_eq(i, (int)strlen(expected));
 	ck_assert_int_eq(i, (int)strlen(expected));
 	buf[i] = 0;
 	buf[i] = 0;
 	ck_assert_str_eq(buf, expected);
 	ck_assert_str_eq(buf, expected);
-	mg_close_connection(conn);
+	mg_close_connection(client_conn);
 
 
 
 
 	/* Get data from callback using http://127.0.0.1 */
 	/* Get data from callback using http://127.0.0.1 */
-	conn = mg_download(
+	client_conn = mg_download(
 	    "127.0.0.1", ipv4_port, 0, ebuf, sizeof(ebuf), "%s", request);
 	    "127.0.0.1", ipv4_port, 0, ebuf, sizeof(ebuf), "%s", request);
-	ck_assert(conn != NULL);
-	ri = mg_get_request_info(conn);
+	ck_assert(client_conn != NULL);
+	ri = mg_get_request_info(client_conn);
 
 
 	ck_assert(ri != NULL);
 	ck_assert(ri != NULL);
 	ck_assert_str_eq(ri->uri, "200");
 	ck_assert_str_eq(ri->uri, "200");
-	i = mg_read(conn, buf, sizeof(buf));
+	i = mg_read(client_conn, buf, sizeof(buf));
 	ck_assert_int_eq(i, (int)strlen(expected));
 	ck_assert_int_eq(i, (int)strlen(expected));
 	buf[i] = 0;
 	buf[i] = 0;
 	ck_assert_str_eq(buf, expected);
 	ck_assert_str_eq(buf, expected);
-	mg_close_connection(conn);
+	mg_close_connection(client_conn);
 
 
 
 
 #if defined(USE_IPV6)
 #if defined(USE_IPV6)
 	/* Get data from callback using http://[::1] */
 	/* Get data from callback using http://[::1] */
-	conn =
+	client_conn =
 	    mg_download("[::1]", ipv6_port, 0, ebuf, sizeof(ebuf), "%s", request);
 	    mg_download("[::1]", ipv6_port, 0, ebuf, sizeof(ebuf), "%s", request);
-	ck_assert(conn != NULL);
-	ri = mg_get_request_info(conn);
+	ck_assert(client_conn != NULL);
+	ri = mg_get_request_info(client_conn);
 
 
 	ck_assert(ri != NULL);
 	ck_assert(ri != NULL);
 	ck_assert_str_eq(ri->uri, "200");
 	ck_assert_str_eq(ri->uri, "200");
-	i = mg_read(conn, buf, sizeof(buf));
+	i = mg_read(client_conn, buf, sizeof(buf));
 	ck_assert_int_eq(i, (int)strlen(expected));
 	ck_assert_int_eq(i, (int)strlen(expected));
 	buf[i] = 0;
 	buf[i] = 0;
 	ck_assert_str_eq(buf, expected);
 	ck_assert_str_eq(buf, expected);
-	mg_close_connection(conn);
+	mg_close_connection(client_conn);
 #endif
 #endif
 
 
 
 
 #if !defined(NO_SSL)
 #if !defined(NO_SSL)
 	/* Get data from callback using https://127.0.0.1 */
 	/* Get data from callback using https://127.0.0.1 */
-	conn = mg_download(
+	client_conn = mg_download(
 	    "127.0.0.1", ipv4s_port, 1, ebuf, sizeof(ebuf), "%s", request);
 	    "127.0.0.1", ipv4s_port, 1, ebuf, sizeof(ebuf), "%s", request);
-	ck_assert(conn != NULL);
-	ri = mg_get_request_info(conn);
+	ck_assert(client_conn != NULL);
+	ri = mg_get_request_info(client_conn);
 
 
 	ck_assert(ri != NULL);
 	ck_assert(ri != NULL);
 	ck_assert_str_eq(ri->uri, "200");
 	ck_assert_str_eq(ri->uri, "200");
-	i = mg_read(conn, buf, sizeof(buf));
+	i = mg_read(client_conn, buf, sizeof(buf));
 	ck_assert_int_eq(i, (int)strlen(expected));
 	ck_assert_int_eq(i, (int)strlen(expected));
 	buf[i] = 0;
 	buf[i] = 0;
 	ck_assert_str_eq(buf, expected);
 	ck_assert_str_eq(buf, expected);
-	mg_close_connection(conn);
+	mg_close_connection(client_conn);
 
 
 	/* Get redirect from callback using http://127.0.0.1 */
 	/* Get redirect from callback using http://127.0.0.1 */
-	conn = mg_download(
+	client_conn = mg_download(
 	    "127.0.0.1", ipv4r_port, 0, ebuf, sizeof(ebuf), "%s", request);
 	    "127.0.0.1", ipv4r_port, 0, ebuf, sizeof(ebuf), "%s", request);
-	ck_assert(conn != NULL);
-	ri = mg_get_request_info(conn);
+	ck_assert(client_conn != NULL);
+	ri = mg_get_request_info(client_conn);
 
 
 	ck_assert(ri != NULL);
 	ck_assert(ri != NULL);
 	ck_assert_str_eq(ri->uri, "302");
 	ck_assert_str_eq(ri->uri, "302");
-	i = mg_read(conn, buf, sizeof(buf));
+	i = mg_read(client_conn, buf, sizeof(buf));
 	ck_assert_int_eq(i, -1);
 	ck_assert_int_eq(i, -1);
-	mg_close_connection(conn);
+	mg_close_connection(client_conn);
 #endif
 #endif
 
 
 
 
 #if defined(USE_IPV6) && !defined(NO_SSL)
 #if defined(USE_IPV6) && !defined(NO_SSL)
 	/* Get data from callback using https://[::1] */
 	/* Get data from callback using https://[::1] */
-	conn =
+	client_conn =
 	    mg_download("[::1]", ipv6s_port, 1, ebuf, sizeof(ebuf), "%s", request);
 	    mg_download("[::1]", ipv6s_port, 1, ebuf, sizeof(ebuf), "%s", request);
-	ck_assert(conn != NULL);
-	ri = mg_get_request_info(conn);
+	ck_assert(client_conn != NULL);
+	ri = mg_get_request_info(client_conn);
 
 
 	ck_assert(ri != NULL);
 	ck_assert(ri != NULL);
 	ck_assert_str_eq(ri->uri, "200");
 	ck_assert_str_eq(ri->uri, "200");
-	i = mg_read(conn, buf, sizeof(buf));
+	i = mg_read(client_conn, buf, sizeof(buf));
 	ck_assert_int_eq(i, (int)strlen(expected));
 	ck_assert_int_eq(i, (int)strlen(expected));
 	buf[i] = 0;
 	buf[i] = 0;
 	ck_assert_str_eq(buf, expected);
 	ck_assert_str_eq(buf, expected);
-	mg_close_connection(conn);
+	mg_close_connection(client_conn);
 
 
 	/* Get redirect from callback using http://127.0.0.1 */
 	/* Get redirect from callback using http://127.0.0.1 */
-	conn =
+	client_conn =
 	    mg_download("[::1]", ipv6r_port, 0, ebuf, sizeof(ebuf), "%s", request);
 	    mg_download("[::1]", ipv6r_port, 0, ebuf, sizeof(ebuf), "%s", request);
-	ck_assert(conn != NULL);
-	ri = mg_get_request_info(conn);
+	ck_assert(client_conn != NULL);
+	ri = mg_get_request_info(client_conn);
 
 
 	ck_assert(ri != NULL);
 	ck_assert(ri != NULL);
 	ck_assert_str_eq(ri->uri, "302");
 	ck_assert_str_eq(ri->uri, "302");
-	i = mg_read(conn, buf, sizeof(buf));
+	i = mg_read(client_conn, buf, sizeof(buf));
 	ck_assert_int_eq(i, -1);
 	ck_assert_int_eq(i, -1);
-	mg_close_connection(conn);
+	mg_close_connection(client_conn);
 #endif
 #endif
 
 
 /* It seems to be impossible to find out what the actual working
 /* It seems to be impossible to find out what the actual working
@@ -515,15 +655,15 @@ START_TEST(test_request_handlers)
 
 
 
 
 	/* Get static data */
 	/* Get static data */
-	conn = mg_download("localhost",
-	                   ipv4_port,
-	                   0,
-	                   ebuf,
-	                   sizeof(ebuf),
-	                   "%s",
-	                   "GET /test.txt HTTP/1.0\r\n\r\n");
-	ck_assert(conn != NULL);
-	ri = mg_get_request_info(conn);
+	client_conn = mg_download("localhost",
+	                          ipv4_port,
+	                          0,
+	                          ebuf,
+	                          sizeof(ebuf),
+	                          "%s",
+	                          "GET /test.txt HTTP/1.0\r\n\r\n");
+	ck_assert(client_conn != NULL);
+	ri = mg_get_request_info(client_conn);
 
 
 	ck_assert(ri != NULL);
 	ck_assert(ri != NULL);
 
 
@@ -531,74 +671,74 @@ START_TEST(test_request_handlers)
 	ck_assert_str_eq(ri->uri, "404");
 	ck_assert_str_eq(ri->uri, "404");
 #else
 #else
 	ck_assert_str_eq(ri->uri, "200");
 	ck_assert_str_eq(ri->uri, "200");
-	i = mg_read(conn, buf, sizeof(buf));
+	i = mg_read(client_conn, buf, sizeof(buf));
 	ck_assert_int_eq(i, 17);
 	ck_assert_int_eq(i, 17);
 	if ((i >= 0) && (i < (int)sizeof(buf))) {
 	if ((i >= 0) && (i < (int)sizeof(buf))) {
 		buf[i] = 0;
 		buf[i] = 0;
 	}
 	}
 	ck_assert_str_eq(buf, "simple text file\n");
 	ck_assert_str_eq(buf, "simple text file\n");
 #endif
 #endif
-	mg_close_connection(conn);
+	mg_close_connection(client_conn);
 
 
 
 
 	/* Get directory listing */
 	/* Get directory listing */
-	conn = mg_download("localhost",
-	                   ipv4_port,
-	                   0,
-	                   ebuf,
-	                   sizeof(ebuf),
-	                   "%s",
-	                   "GET / HTTP/1.0\r\n\r\n");
-	ck_assert(conn != NULL);
-	ri = mg_get_request_info(conn);
+	client_conn = mg_download("localhost",
+	                          ipv4_port,
+	                          0,
+	                          ebuf,
+	                          sizeof(ebuf),
+	                          "%s",
+	                          "GET / HTTP/1.0\r\n\r\n");
+	ck_assert(client_conn != NULL);
+	ri = mg_get_request_info(client_conn);
 
 
 	ck_assert(ri != NULL);
 	ck_assert(ri != NULL);
 #if defined(NO_FILES)
 #if defined(NO_FILES)
 	ck_assert_str_eq(ri->uri, "404");
 	ck_assert_str_eq(ri->uri, "404");
 #else
 #else
 	ck_assert_str_eq(ri->uri, "200");
 	ck_assert_str_eq(ri->uri, "200");
-	i = mg_read(conn, buf, sizeof(buf));
+	i = mg_read(client_conn, buf, sizeof(buf));
 	ck_assert(i > 6);
 	ck_assert(i > 6);
 	buf[6] = 0;
 	buf[6] = 0;
 	ck_assert_str_eq(buf, "<html>");
 	ck_assert_str_eq(buf, "<html>");
 #endif
 #endif
-	mg_close_connection(conn);
+	mg_close_connection(client_conn);
 
 
 
 
 	/* POST to static file (will not work) */
 	/* POST to static file (will not work) */
-	conn = mg_download("localhost",
-	                   ipv4_port,
-	                   0,
-	                   ebuf,
-	                   sizeof(ebuf),
-	                   "%s",
-	                   "POST /test.txt HTTP/1.0\r\n\r\n");
-	ck_assert(conn != NULL);
-	ri = mg_get_request_info(conn);
+	client_conn = mg_download("localhost",
+	                          ipv4_port,
+	                          0,
+	                          ebuf,
+	                          sizeof(ebuf),
+	                          "%s",
+	                          "POST /test.txt HTTP/1.0\r\n\r\n");
+	ck_assert(client_conn != NULL);
+	ri = mg_get_request_info(client_conn);
 
 
 	ck_assert(ri != NULL);
 	ck_assert(ri != NULL);
 #if defined(NO_FILES)
 #if defined(NO_FILES)
 	ck_assert_str_eq(ri->uri, "404");
 	ck_assert_str_eq(ri->uri, "404");
 #else
 #else
 	ck_assert_str_eq(ri->uri, "405");
 	ck_assert_str_eq(ri->uri, "405");
-	i = mg_read(conn, buf, sizeof(buf));
+	i = mg_read(client_conn, buf, sizeof(buf));
 	ck_assert(i >= 29);
 	ck_assert(i >= 29);
 	buf[29] = 0;
 	buf[29] = 0;
 	ck_assert_str_eq(buf, "Error 405: Method Not Allowed");
 	ck_assert_str_eq(buf, "Error 405: Method Not Allowed");
 #endif
 #endif
-	mg_close_connection(conn);
+	mg_close_connection(client_conn);
 
 
 
 
 	/* PUT to static file (will not work) */
 	/* PUT to static file (will not work) */
-	conn = mg_download("localhost",
-	                   ipv4_port,
-	                   0,
-	                   ebuf,
-	                   sizeof(ebuf),
-	                   "%s",
-	                   "PUT /test.txt HTTP/1.0\r\n\r\n");
-	ck_assert(conn != NULL);
-	ri = mg_get_request_info(conn);
+	client_conn = mg_download("localhost",
+	                          ipv4_port,
+	                          0,
+	                          ebuf,
+	                          sizeof(ebuf),
+	                          "%s",
+	                          "PUT /test.txt HTTP/1.0\r\n\r\n");
+	ck_assert(client_conn != NULL);
+	ri = mg_get_request_info(client_conn);
 
 
 	ck_assert(ri != NULL);
 	ck_assert(ri != NULL);
 #if defined(NO_FILES)
 #if defined(NO_FILES)
@@ -606,16 +746,204 @@ START_TEST(test_request_handlers)
 #else
 #else
 	ck_assert_str_eq(ri->uri, "401"); /* not authorized */
 	ck_assert_str_eq(ri->uri, "401"); /* not authorized */
 #endif
 #endif
-	mg_close_connection(conn);
-
-
-	/* TODO: Test websockets */
-
+	mg_close_connection(client_conn);
+
+
+/* Websocket test */
+#ifdef USE_WEBSOCKET
+	/* Then connect a first client */
+	ws_client1_conn =
+	    mg_connect_websocket_client("localhost",
+	                                ipv4_port,
+	                                0,
+	                                ebuf,
+	                                sizeof(ebuf),
+	                                "/websocket",
+	                                NULL,
+	                                websocket_client_data_handler,
+	                                websocket_client_close_handler,
+	                                &ws_client1_data);
+
+	ck_assert(ws_client1_conn != NULL);
+
+	mg_Sleep(3); /* Should get the websocket welcome message */
+	ck_assert_int_eq(ws_client1_data.closed, 0);
+	ck_assert_int_eq(ws_client2_data.closed, 0);
+	ck_assert_int_eq(ws_client3_data.closed, 0);
+	ck_assert(ws_client2_data.data == NULL);
+	ck_assert_uint_eq(ws_client2_data.len, 0);
+	ck_assert(ws_client1_data.data != NULL);
+	ck_assert_uint_eq(ws_client1_data.len, websocket_welcome_msg_len);
+	ck_assert(!memcmp(ws_client1_data.data,
+	                  websocket_welcome_msg,
+	                  websocket_welcome_msg_len));
+	free(ws_client1_data.data);
+	ws_client1_data.data = NULL;
+	ws_client1_data.len = 0;
+
+	mg_websocket_write(ws_client1_conn, WEBSOCKET_OPCODE_TEXT, "data1", 5);
+
+	mg_Sleep(3); /* Should get the acknowledge message */
+	ck_assert_int_eq(ws_client1_data.closed, 0);
+	ck_assert_int_eq(ws_client2_data.closed, 0);
+	ck_assert(ws_client2_data.data == NULL);
+	ck_assert_uint_eq(ws_client2_data.len, 0);
+	ck_assert(ws_client1_data.data != NULL);
+	ck_assert_uint_eq(ws_client1_data.len, websocket_acknowledge_msg_len);
+	ck_assert(!memcmp(ws_client1_data.data,
+	                  websocket_acknowledge_msg,
+	                  websocket_acknowledge_msg_len));
+	free(ws_client1_data.data);
+	ws_client1_data.data = NULL;
+	ws_client1_data.len = 0;
+
+/* Now connect a second client */
+#ifdef USE_IPV6
+	ws_client2_conn =
+	    mg_connect_websocket_client("[::1]",
+	                                ipv6_port,
+	                                0,
+	                                ebuf,
+	                                sizeof(ebuf),
+	                                "/websocket",
+	                                NULL,
+	                                websocket_client_data_handler,
+	                                websocket_client_close_handler,
+	                                &ws_client2_data);
+#else
+	ws_client2_conn =
+	    mg_connect_websocket_client("127.0.0.1",
+	                                ipv4_port,
+	                                0,
+	                                ebuf,
+	                                sizeof(ebuf),
+	                                "/websocket",
+	                                NULL,
+	                                websocket_client_data_handler,
+	                                websocket_client_close_handler,
+	                                &ws_client2_data);
+#endif
+	ck_assert(ws_client2_conn != NULL);
+
+	mg_Sleep(3); /* Client 2 should get the websocket welcome message */
+	ck_assert(ws_client1_data.closed == 0);
+	ck_assert(ws_client2_data.closed == 0);
+	ck_assert(ws_client1_data.data == NULL);
+	ck_assert(ws_client1_data.len == 0);
+	ck_assert(ws_client2_data.data != NULL);
+	ck_assert(ws_client2_data.len == websocket_welcome_msg_len);
+	ck_assert(!memcmp(ws_client2_data.data,
+	                  websocket_welcome_msg,
+	                  websocket_welcome_msg_len));
+	free(ws_client2_data.data);
+	ws_client2_data.data = NULL;
+	ws_client2_data.len = 0;
+
+	mg_websocket_write(ws_client1_conn, WEBSOCKET_OPCODE_TEXT, "data2", 5);
+
+	mg_Sleep(3); /* Should get the acknowledge message */
+	ck_assert(ws_client1_data.closed == 0);
+	ck_assert(ws_client2_data.closed == 0);
+	ck_assert(ws_client2_data.data == NULL);
+	ck_assert(ws_client2_data.len == 0);
+	ck_assert(ws_client1_data.data != NULL);
+	ck_assert(ws_client1_data.len == websocket_acknowledge_msg_len);
+	ck_assert(!memcmp(ws_client1_data.data,
+	                  websocket_acknowledge_msg,
+	                  websocket_acknowledge_msg_len));
+	free(ws_client1_data.data);
+	ws_client1_data.data = NULL;
+	ws_client1_data.len = 0;
+
+	mg_websocket_write(ws_client1_conn, WEBSOCKET_OPCODE_TEXT, "bye", 3);
+
+	mg_Sleep(3); /* Should get the goodbye message */
+	ck_assert(ws_client1_data.closed == 0);
+	ck_assert(ws_client2_data.closed == 0);
+	ck_assert(ws_client2_data.data == NULL);
+	ck_assert(ws_client2_data.len == 0);
+	ck_assert(ws_client1_data.data != NULL);
+	ck_assert(ws_client1_data.len == websocket_goodbye_msg_len);
+	ck_assert(!memcmp(ws_client1_data.data,
+	                  websocket_goodbye_msg,
+	                  websocket_goodbye_msg_len));
+	free(ws_client1_data.data);
+	ws_client1_data.data = NULL;
+	ws_client1_data.len = 0;
+
+	mg_close_connection(ws_client1_conn);
+
+	mg_Sleep(3); /* Won't get any message */
+	ck_assert(ws_client1_data.closed == 1);
+	ck_assert(ws_client2_data.closed == 0);
+	ck_assert(ws_client1_data.data == NULL);
+	ck_assert(ws_client1_data.len == 0);
+	ck_assert(ws_client2_data.data == NULL);
+	ck_assert(ws_client2_data.len == 0);
+
+	mg_websocket_write(ws_client2_conn, WEBSOCKET_OPCODE_TEXT, "bye", 3);
+
+	mg_Sleep(3); /* Should get the goodbye message */
+	ck_assert(ws_client1_data.closed == 1);
+	ck_assert(ws_client2_data.closed == 0);
+	ck_assert(ws_client1_data.data == NULL);
+	ck_assert(ws_client1_data.len == 0);
+	ck_assert(ws_client2_data.data != NULL);
+	ck_assert(ws_client2_data.len == websocket_goodbye_msg_len);
+	ck_assert(!memcmp(ws_client2_data.data,
+	                  websocket_goodbye_msg,
+	                  websocket_goodbye_msg_len));
+	free(ws_client2_data.data);
+	ws_client2_data.data = NULL;
+	ws_client2_data.len = 0;
+
+	mg_close_connection(ws_client2_conn);
+
+	mg_Sleep(3); /* Won't get any message */
+	ck_assert(ws_client1_data.closed == 1);
+	ck_assert(ws_client2_data.closed == 1);
+	ck_assert(ws_client1_data.data == NULL);
+	ck_assert(ws_client1_data.len == 0);
+	ck_assert(ws_client2_data.data == NULL);
+	ck_assert(ws_client2_data.len == 0);
+
+	/* Connect client 3 */
+	ws_client3_conn =
+	    mg_connect_websocket_client("localhost",
+	                                ipv4_port,
+	                                0,
+	                                ebuf,
+	                                sizeof(ebuf),
+	                                "/websocket",
+	                                NULL,
+	                                websocket_client_data_handler,
+	                                websocket_client_close_handler,
+	                                &ws_client3_data);
+
+	mg_Sleep(3); /* Client 3 should get the websocket welcome message */
+	ck_assert(ws_client1_data.closed == 1);
+	ck_assert(ws_client2_data.closed == 1);
+	ck_assert(ws_client3_data.closed == 0);
+	ck_assert(ws_client1_data.data == NULL);
+	ck_assert(ws_client1_data.len == 0);
+	ck_assert(ws_client2_data.data == NULL);
+	ck_assert(ws_client2_data.len == 0);
+	ck_assert(ws_client3_data.data != NULL);
+	ck_assert(ws_client3_data.len == websocket_welcome_msg_len);
+	ck_assert(!memcmp(ws_client3_data.data,
+	                  websocket_welcome_msg,
+	                  websocket_welcome_msg_len));
+	free(ws_client3_data.data);
+	ws_client3_data.data = NULL;
+	ws_client3_data.len = 0;
+#endif
 
 
 	/* Close the server */
 	/* Close the server */
 	g_ctx = NULL;
 	g_ctx = NULL;
 	mg_stop(ctx);
 	mg_stop(ctx);
-	mg_Sleep(1);
+	mg_Sleep(30);
+
+	ck_assert_int_eq(ws_client3_data.closed, 1);
 }
 }
 END_TEST
 END_TEST
 
 
@@ -668,6 +996,7 @@ void _ck_assert_failed(const char *file, int line, const char *expr, ...)
 	va_start(va, expr);
 	va_start(va, expr);
 	fprintf(stderr, "Error: %s, line %i\n", file, line); /* breakpoint here ! */
 	fprintf(stderr, "Error: %s, line %i\n", file, line); /* breakpoint here ! */
 	vfprintf(stderr, expr, va);
 	vfprintf(stderr, expr, va);
+	fprintf(stderr, "\n\n");
 	va_end(va);
 	va_end(va);
 	chk_failed++;
 	chk_failed++;
 }
 }