Bläddra i källkod

Merge pull request #367 from feneuilflo/master

Multiple protocols management for Websocket
bel2125 8 år sedan
förälder
incheckning
2770172bab
2 ändrade filer med 112 tillägg och 28 borttagningar
  1. 24 0
      include/civetweb.h
  2. 88 28
      src/civetweb.c

+ 24 - 0
include/civetweb.h

@@ -88,6 +88,8 @@ struct mg_request_info {
 	} http_headers[64];    /* Maximum 64 headers */
 	} http_headers[64];    /* Maximum 64 headers */
 
 
 	struct client_cert *client_cert; /* Client certificate information */
 	struct client_cert *client_cert; /* Client certificate information */
+
+	const char *acceptedSubprotocol; /* websocket, subprotocol accepted during handshake */
 };
 };
 
 
 
 
@@ -353,6 +355,14 @@ typedef int (*mg_websocket_data_handler)(struct mg_connection *,
 typedef void (*mg_websocket_close_handler)(const struct mg_connection *,
 typedef void (*mg_websocket_close_handler)(const struct mg_connection *,
                                            void *);
                                            void *);
 
 
+/* struct mg_websocket_subprotocols
+ *
+ * List of accepted subprotocols
+ */
+struct mg_websocket_subprotocols {
+	int nb_subprotocols;
+	char ** subprotocols;
+};
 
 
 /* mg_set_websocket_handler
 /* mg_set_websocket_handler
 
 
@@ -367,6 +377,20 @@ mg_set_websocket_handler(struct mg_context *ctx,
                          mg_websocket_close_handler close_handler,
                          mg_websocket_close_handler close_handler,
                          void *cbdata);
                          void *cbdata);
 
 
+/* mg_set_websocket_handler
+
+   Set or remove handler functions for websocket connections.
+   This function works similar to mg_set_request_handler - see there. */
+CIVETWEB_API void
+mg_set_websocket_handler_with_subprotocols(struct mg_context *ctx,
+                         const char *uri,
+						 struct mg_websocket_subprotocols *subprotocols,
+                         mg_websocket_connect_handler connect_handler,
+                         mg_websocket_ready_handler ready_handler,
+                         mg_websocket_data_handler data_handler,
+                         mg_websocket_close_handler close_handler,
+                         void *cbdata);
+
 
 
 /* mg_authorization_handler
 /* mg_authorization_handler
 
 

+ 88 - 28
src/civetweb.c

@@ -1515,6 +1515,9 @@ struct mg_handler_info {
 	mg_websocket_data_handler data_handler;
 	mg_websocket_data_handler data_handler;
 	mg_websocket_close_handler close_handler;
 	mg_websocket_close_handler close_handler;
 
 
+	/* accepted subprotocols for ws/wss requests. */
+	struct mg_websocket_subprotocols *subprotocols;
+
 	/* Handler for authorization requests */
 	/* Handler for authorization requests */
 	mg_authorization_handler auth_handler;
 	mg_authorization_handler auth_handler;
 
 
@@ -9367,7 +9370,6 @@ static int
 send_websocket_handshake(struct mg_connection *conn, const char *websock_key)
 send_websocket_handshake(struct mg_connection *conn, const char *websock_key)
 {
 {
 	static const char *magic = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
 	static const char *magic = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
-	const char *protocol = NULL;
 	char buf[100], sha[20], b64_sha[sizeof(sha) * 2];
 	char buf[100], sha[20], b64_sha[sizeof(sha) * 2];
 	SHA1_CTX sha_ctx;
 	SHA1_CTX sha_ctx;
 	int truncated;
 	int truncated;
@@ -9389,33 +9391,8 @@ send_websocket_handshake(struct mg_connection *conn, const char *websock_key)
 	          "Connection: Upgrade\r\n"
 	          "Connection: Upgrade\r\n"
 	          "Sec-WebSocket-Accept: %s\r\n",
 	          "Sec-WebSocket-Accept: %s\r\n",
 	          b64_sha);
 	          b64_sha);
-	protocol = mg_get_header(conn, "Sec-WebSocket-Protocol");
-	if (protocol) {
-		/* The protocol is a comma seperated list of names. */
-		/* The server must only return one value from this list. */
-		/* First check if it is a list or just a single value. */
-		const char *sep = strchr(protocol, ',');
-		if (sep == NULL) {
-			/* Just a single protocol -> accept it. */
-			mg_printf(conn, "Sec-WebSocket-Protocol: %s\r\n\r\n", protocol);
-		} else {
-			/* Multiple protocols -> accept the first one. */
-			/* This is just a quick fix if the client offers multiple
-			 * protocols. In order to get the behavior intended by
-			 * RFC 6455 (https://tools.ietf.org/rfc/rfc6455.txt), it is
-			 * required to have a list of websocket subprotocols accepted
-			 * by the server. Then the server must either select a subprotocol
-			 * supported by client and server, or the server has to abort the
-			 * handshake by not returning a Sec-Websocket-Protocol header if
-			 * no subprotocol is acceptable.
-			 */
-			mg_printf(conn,
-			          "Sec-WebSocket-Protocol: %.*s\r\n\r\n",
-			          (int)(sep - protocol),
-			          protocol);
-		}
-		/* TODO: Real subprotocol negotiation instead of just taking the first
-		 * websocket subprotocol suggested by the client. */
+	if (conn->request_info.acceptedSubprotocol) {
+		mg_printf(conn, "Sec-WebSocket-Protocol: %s\r\n\r\n", conn->request_info.acceptedSubprotocol);
 	} else {
 	} else {
 		mg_printf(conn, "%s", "\r\n");
 		mg_printf(conn, "%s", "\r\n");
 	}
 	}
@@ -9732,6 +9709,7 @@ static void
 handle_websocket_request(struct mg_connection *conn,
 handle_websocket_request(struct mg_connection *conn,
                          const char *path,
                          const char *path,
                          int is_callback_resource,
                          int is_callback_resource,
+						 struct mg_websocket_subprotocols * subprotocols,
                          mg_websocket_connect_handler ws_connect_handler,
                          mg_websocket_connect_handler ws_connect_handler,
                          mg_websocket_ready_handler ws_ready_handler,
                          mg_websocket_ready_handler ws_ready_handler,
                          mg_websocket_data_handler ws_data_handler,
                          mg_websocket_data_handler ws_data_handler,
@@ -9789,6 +9767,51 @@ handle_websocket_request(struct mg_connection *conn,
 
 
 	/* Step 2: If a callback is responsible, call it. */
 	/* Step 2: If a callback is responsible, call it. */
 	if (is_callback_resource) {
 	if (is_callback_resource) {
+		/* Step 2.1 check and select subprotocol */
+		const char *protocol = mg_get_header(conn, "Sec-WebSocket-Protocol");
+		if(protocol && subprotocols) {
+			int len, idx;
+			const char *sep, *curSubProtocol, *acceptedSubProtocol = NULL;
+
+
+			/* look for matching subprotocol */
+			do {
+				sep = strchr(protocol, ',');
+				curSubProtocol = protocol;
+				len = sep ? (int) (sep - protocol) : strlen(protocol);
+				protocol = sep ? sep + 1 : NULL;
+
+
+				for(idx = 0; idx < subprotocols->nb_subprotocols; idx++) {
+					if(strlen(subprotocols->subprotocols[idx])==len
+							&& strncmp(curSubProtocol, subprotocols->subprotocols[idx], len)==0) {
+						acceptedSubProtocol = subprotocols->subprotocols[idx];
+						break;
+					}
+				}
+			} while(sep && !acceptedSubProtocol);
+
+			conn->request_info.acceptedSubprotocol = acceptedSubProtocol;
+		} else if (protocol) {
+			/* keep legacy behavior */
+
+			/* The protocol is a comma seperated list of names. */
+			/* The server must only return one value from this list. */
+			/* First check if it is a list or just a single value. */
+			const char *sep = strrchr(protocol, ',');
+			if (sep == NULL) {
+				/* Just a single protocol -> accept it. */
+				conn->request_info.acceptedSubprotocol = protocol;
+			} else {
+				/* Multiple protocols -> accept the last one. */
+				/* This is just a quick fix if the client offers multiple
+				 * protocols. The handler should have a list of accepted protocols on his own
+				 * and use it to select one protocol among those the client has offered.
+				 */
+				conn->request_info.acceptedSubprotocol = (sep+1);
+			}
+		}
+
 		if (ws_connect_handler != NULL
 		if (ws_connect_handler != NULL
 		    && ws_connect_handler(conn, cbData) != 0) {
 		    && ws_connect_handler(conn, cbData) != 0) {
 			/* C callback has returned non-zero, do not proceed with
 			/* C callback has returned non-zero, do not proceed with
@@ -10150,6 +10173,7 @@ mg_set_handler_type(struct mg_context *ctx,
                     int handler_type,
                     int handler_type,
                     int is_delete_request,
                     int is_delete_request,
                     mg_request_handler handler,
                     mg_request_handler handler,
+					struct mg_websocket_subprotocols *subprotocols,
                     mg_websocket_connect_handler connect_handler,
                     mg_websocket_connect_handler connect_handler,
                     mg_websocket_ready_handler ready_handler,
                     mg_websocket_ready_handler ready_handler,
                     mg_websocket_data_handler data_handler,
                     mg_websocket_data_handler data_handler,
@@ -10230,6 +10254,7 @@ mg_set_handler_type(struct mg_context *ctx,
 					if (handler_type == REQUEST_HANDLER) {
 					if (handler_type == REQUEST_HANDLER) {
 						tmp_rh->handler = handler;
 						tmp_rh->handler = handler;
 					} else if (handler_type == WEBSOCKET_HANDLER) {
 					} else if (handler_type == WEBSOCKET_HANDLER) {
+						tmp_rh->subprotocols = subprotocols;
 						tmp_rh->connect_handler = connect_handler;
 						tmp_rh->connect_handler = connect_handler;
 						tmp_rh->ready_handler = ready_handler;
 						tmp_rh->ready_handler = ready_handler;
 						tmp_rh->data_handler = data_handler;
 						tmp_rh->data_handler = data_handler;
@@ -10276,6 +10301,7 @@ mg_set_handler_type(struct mg_context *ctx,
 	if (handler_type == REQUEST_HANDLER) {
 	if (handler_type == REQUEST_HANDLER) {
 		tmp_rh->handler = handler;
 		tmp_rh->handler = handler;
 	} else if (handler_type == WEBSOCKET_HANDLER) {
 	} else if (handler_type == WEBSOCKET_HANDLER) {
+		tmp_rh->subprotocols = subprotocols;
 		tmp_rh->connect_handler = connect_handler;
 		tmp_rh->connect_handler = connect_handler;
 		tmp_rh->ready_handler = ready_handler;
 		tmp_rh->ready_handler = ready_handler;
 		tmp_rh->data_handler = data_handler;
 		tmp_rh->data_handler = data_handler;
@@ -10303,6 +10329,7 @@ mg_set_request_handler(struct mg_context *ctx,
 	                    REQUEST_HANDLER,
 	                    REQUEST_HANDLER,
 	                    handler == NULL,
 	                    handler == NULL,
 	                    handler,
 	                    handler,
+						NULL,
 	                    NULL,
 	                    NULL,
 	                    NULL,
 	                    NULL,
 	                    NULL,
 	                    NULL,
@@ -10315,6 +10342,27 @@ mg_set_request_handler(struct mg_context *ctx,
 void
 void
 mg_set_websocket_handler(struct mg_context *ctx,
 mg_set_websocket_handler(struct mg_context *ctx,
                          const char *uri,
                          const char *uri,
+						 mg_websocket_connect_handler connect_handler,
+                         mg_websocket_ready_handler ready_handler,
+                         mg_websocket_data_handler data_handler,
+                         mg_websocket_close_handler close_handler,
+                         void *cbdata)
+{
+	mg_set_websocket_handler_with_subprotocols(ctx,
+			uri,
+			NULL,
+			connect_handler,
+			ready_handler,
+			data_handler,
+			close_handler,
+			cbdata);
+}
+
+
+void
+mg_set_websocket_handler_with_subprotocols(struct mg_context *ctx,
+                         const char *uri,
+						 struct mg_websocket_subprotocols* subprotocols,
                          mg_websocket_connect_handler connect_handler,
                          mg_websocket_connect_handler connect_handler,
                          mg_websocket_ready_handler ready_handler,
                          mg_websocket_ready_handler ready_handler,
                          mg_websocket_data_handler data_handler,
                          mg_websocket_data_handler data_handler,
@@ -10329,6 +10377,7 @@ mg_set_websocket_handler(struct mg_context *ctx,
 	                    WEBSOCKET_HANDLER,
 	                    WEBSOCKET_HANDLER,
 	                    is_delete_request,
 	                    is_delete_request,
 	                    NULL,
 	                    NULL,
+						subprotocols,
 	                    connect_handler,
 	                    connect_handler,
 	                    ready_handler,
 	                    ready_handler,
 	                    data_handler,
 	                    data_handler,
@@ -10349,6 +10398,7 @@ mg_set_auth_handler(struct mg_context *ctx,
 	                    AUTH_HANDLER,
 	                    AUTH_HANDLER,
 	                    handler == NULL,
 	                    handler == NULL,
 	                    NULL,
 	                    NULL,
+						NULL,
 	                    NULL,
 	                    NULL,
 	                    NULL,
 	                    NULL,
 	                    NULL,
 	                    NULL,
@@ -10362,6 +10412,7 @@ static int
 get_request_handler(struct mg_connection *conn,
 get_request_handler(struct mg_connection *conn,
                     int handler_type,
                     int handler_type,
                     mg_request_handler *handler,
                     mg_request_handler *handler,
+					struct mg_websocket_subprotocols **subprotocols,
                     mg_websocket_connect_handler *connect_handler,
                     mg_websocket_connect_handler *connect_handler,
                     mg_websocket_ready_handler *ready_handler,
                     mg_websocket_ready_handler *ready_handler,
                     mg_websocket_data_handler *data_handler,
                     mg_websocket_data_handler *data_handler,
@@ -10387,6 +10438,7 @@ get_request_handler(struct mg_connection *conn,
 			if (tmp_rh->handler_type == handler_type) {
 			if (tmp_rh->handler_type == handler_type) {
 				if (urilen == tmp_rh->uri_len && !strcmp(tmp_rh->uri, uri)) {
 				if (urilen == tmp_rh->uri_len && !strcmp(tmp_rh->uri, uri)) {
 					if (handler_type == WEBSOCKET_HANDLER) {
 					if (handler_type == WEBSOCKET_HANDLER) {
+						*subprotocols = tmp_rh->subprotocols;
 						*connect_handler = tmp_rh->connect_handler;
 						*connect_handler = tmp_rh->connect_handler;
 						*ready_handler = tmp_rh->ready_handler;
 						*ready_handler = tmp_rh->ready_handler;
 						*data_handler = tmp_rh->data_handler;
 						*data_handler = tmp_rh->data_handler;
@@ -10410,6 +10462,7 @@ get_request_handler(struct mg_connection *conn,
 				if (tmp_rh->uri_len < urilen && uri[tmp_rh->uri_len] == '/'
 				if (tmp_rh->uri_len < urilen && uri[tmp_rh->uri_len] == '/'
 				    && memcmp(tmp_rh->uri, uri, tmp_rh->uri_len) == 0) {
 				    && memcmp(tmp_rh->uri, uri, tmp_rh->uri_len) == 0) {
 					if (handler_type == WEBSOCKET_HANDLER) {
 					if (handler_type == WEBSOCKET_HANDLER) {
+						*subprotocols = tmp_rh->subprotocols;
 						*connect_handler = tmp_rh->connect_handler;
 						*connect_handler = tmp_rh->connect_handler;
 						*ready_handler = tmp_rh->ready_handler;
 						*ready_handler = tmp_rh->ready_handler;
 						*data_handler = tmp_rh->data_handler;
 						*data_handler = tmp_rh->data_handler;
@@ -10432,6 +10485,7 @@ get_request_handler(struct mg_connection *conn,
 			if (tmp_rh->handler_type == handler_type) {
 			if (tmp_rh->handler_type == handler_type) {
 				if (match_prefix(tmp_rh->uri, tmp_rh->uri_len, uri) > 0) {
 				if (match_prefix(tmp_rh->uri, tmp_rh->uri_len, uri) > 0) {
 					if (handler_type == WEBSOCKET_HANDLER) {
 					if (handler_type == WEBSOCKET_HANDLER) {
+						*subprotocols = tmp_rh->subprotocols;
 						*connect_handler = tmp_rh->connect_handler;
 						*connect_handler = tmp_rh->connect_handler;
 						*ready_handler = tmp_rh->ready_handler;
 						*ready_handler = tmp_rh->ready_handler;
 						*data_handler = tmp_rh->data_handler;
 						*data_handler = tmp_rh->data_handler;
@@ -10511,6 +10565,7 @@ handle_request(struct mg_connection *conn)
 		int i;
 		int i;
 		struct mg_file file = STRUCT_FILE_INITIALIZER;
 		struct mg_file file = STRUCT_FILE_INITIALIZER;
 		mg_request_handler callback_handler = NULL;
 		mg_request_handler callback_handler = NULL;
+		struct mg_websocket_subprotocols *subprotocols;
 		mg_websocket_connect_handler ws_connect_handler = NULL;
 		mg_websocket_connect_handler ws_connect_handler = NULL;
 		mg_websocket_ready_handler ws_ready_handler = NULL;
 		mg_websocket_ready_handler ws_ready_handler = NULL;
 		mg_websocket_data_handler ws_data_handler = NULL;
 		mg_websocket_data_handler ws_data_handler = NULL;
@@ -10608,6 +10663,7 @@ handle_request(struct mg_connection *conn)
 		                        is_websocket_request ? WEBSOCKET_HANDLER
 		                        is_websocket_request ? WEBSOCKET_HANDLER
 		                                             : REQUEST_HANDLER,
 		                                             : REQUEST_HANDLER,
 		                        &callback_handler,
 		                        &callback_handler,
+								&subprotocols,
 		                        &ws_connect_handler,
 		                        &ws_connect_handler,
 		                        &ws_ready_handler,
 		                        &ws_ready_handler,
 		                        &ws_data_handler,
 		                        &ws_data_handler,
@@ -10643,6 +10699,7 @@ handle_request(struct mg_connection *conn)
 		                        AUTH_HANDLER,
 		                        AUTH_HANDLER,
 		                        NULL,
 		                        NULL,
 		                        NULL,
 		                        NULL,
+								NULL,
 		                        NULL,
 		                        NULL,
 		                        NULL,
 		                        NULL,
 		                        NULL,
 		                        NULL,
@@ -10732,6 +10789,7 @@ handle_request(struct mg_connection *conn)
 				handle_websocket_request(conn,
 				handle_websocket_request(conn,
 				                         path,
 				                         path,
 				                         is_callback_resource,
 				                         is_callback_resource,
+										 subprotocols,
 				                         ws_connect_handler,
 				                         ws_connect_handler,
 				                         ws_ready_handler,
 				                         ws_ready_handler,
 				                         ws_data_handler,
 				                         ws_data_handler,
@@ -10750,6 +10808,7 @@ handle_request(struct mg_connection *conn)
 				handle_websocket_request(conn,
 				handle_websocket_request(conn,
 				                         path,
 				                         path,
 				                         0 /* Lua Script */,
 				                         0 /* Lua Script */,
+										 NULL,
 				                         NULL,
 				                         NULL,
 				                         NULL,
 				                         NULL,
 				                         NULL,
 				                         NULL,
@@ -10761,6 +10820,7 @@ handle_request(struct mg_connection *conn)
 				    conn,
 				    conn,
 				    path,
 				    path,
 				    !is_script_resource /* could be deprecated global callback */,
 				    !is_script_resource /* could be deprecated global callback */,
+					NULL,
 				    deprecated_websocket_connect_wrapper,
 				    deprecated_websocket_connect_wrapper,
 				    deprecated_websocket_ready_wrapper,
 				    deprecated_websocket_ready_wrapper,
 				    deprecated_websocket_data_wrapper,
 				    deprecated_websocket_data_wrapper,