diff --git a/lib/handshake.c b/lib/handshake.c index c3e9ed58..51cd5584 100644 --- a/lib/handshake.c +++ b/lib/handshake.c @@ -239,6 +239,11 @@ handshake_0405(struct libwebsocket *wsi) char *m = mask_summing_buf; int nonce_len = 0; int accept_len; + char *c; + char ext_name[128]; + struct libwebsocket_extension * ext; + int ext_count = 0; + int more = 1; if (!wsi->utf8_token[WSI_TOKEN_HOST].token_len || !wsi->utf8_token[WSI_TOKEN_KEY].token_len) { @@ -352,6 +357,103 @@ handshake_0405(struct libwebsocket *wsi) p += wsi->utf8_token[WSI_TOKEN_PROTOCOL].token_len; } + /* + * Figure out which extensions the client has that we want to + * enable on this connection, and give him back the list + */ + + if (wsi->utf8_token[WSI_TOKEN_EXTENSIONS].token_len) { + strcpy(p, "\x0d\x0aSec-WebSocket-Extensions: "); + p += strlen("\x0d\x0aSec-WebSocket-Extensions: "); + + /* + * break down the list of client extensions + * and go through them + */ + + c = wsi->utf8_token[WSI_TOKEN_EXTENSIONS].token; + n = 0; + while (more) { + + if (*c && *c != ',') { + ext_name[n] = *c++; + if (n < sizeof(ext_name) - 1) + n++; + continue; + } + ext_name[n] = '\0'; + if (!*c) + more = 0; + + /* check a client's extension against our support */ + + ext = wsi->protocol->owning_server->extensions; + + while (ext && ext->callback) { + + if (strcmp(ext_name, ext->name)) { + ext++; + continue; + } + + /* + * oh, we do support this one he + * asked for... but let's ask user + * code if it's OK to apply it on this + * particular connection + protocol + */ + + n = wsi->protocol->owning_server-> + protocols[0].callback( + wsi->protocol->owning_server, + wsi, + LWS_CALLBACK_CONFIRM_EXTENSION_OKAY, + wsi->user_space, ext_name, 0); + + /* + * zero return from callback means + * go ahead and allow the extension, + * it's what we get if the callback is + * unhandled + */ + + if (n) { + ext++; + continue; + } + + /* apply it */ + + if (ext_count) + *p++ = ','; + p += sprintf(p, ext_name); + ext_count++; + + /* instantiate the extension on this conn */ + + wsi->active_extensions_user[ + wsi->count_active_extensions] = + malloc(ext->per_session_data_size); + wsi->active_extensions[ + wsi->count_active_extensions] = ext; + + /* allow him to construct his context */ + + ext->callback(wsi->protocol->owning_server, + wsi, LWS_EXT_CALLBACK_CONSTRUCT, + wsi->active_extensions_user[ + wsi->count_active_extensions], NULL, 0); + + wsi->count_active_extensions++; + + ext++; + } + + n = 0; + } + } + + /* end of response packet */ strcpy(p, "\x0d\x0a\x0d\x0a"); diff --git a/lib/libwebsockets.c b/lib/libwebsockets.c index c59bbdd9..39c537fc 100644 --- a/lib/libwebsockets.c +++ b/lib/libwebsockets.c @@ -1733,6 +1733,8 @@ OpenSSL_verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx) * specific callback for each one. The list is ended with an * entry that has a NULL callback pointer. * It's not const because we write the owning_server member + * @extensions: NULL or array of libwebsocket_extension structs listing the + * extensions this context supports * @ssl_cert_filepath: If libwebsockets was compiled to use ssl, and you want * to listen using SSL, set to the filepath to fetch the * server cert from, otherwise NULL for unencrypted diff --git a/lib/libwebsockets.h b/lib/libwebsockets.h index 5402ee1e..eded0259 100644 --- a/lib/libwebsockets.h +++ b/lib/libwebsockets.h @@ -63,6 +63,7 @@ enum libwebsocket_callback_reasons { LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS, LWS_CALLBACK_OPENSSL_PERFORM_CLIENT_CERT_VERIFICATION, LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER, + LWS_CALLBACK_CONFIRM_EXTENSION_OKAY, /* external poll() management support */ LWS_CALLBACK_ADD_POLL_FD, LWS_CALLBACK_DEL_POLL_FD, @@ -70,6 +71,11 @@ enum libwebsocket_callback_reasons { LWS_CALLBACK_CLEAR_MODE_POLL_FD, }; +enum libwebsocket_extension_callback_reasons { + LWS_EXT_CALLBACK_CONSTRUCT, + LWS_EXT_CALLBACK_DESTROY, +}; + enum libwebsocket_write_protocol { LWS_WRITE_TEXT, LWS_WRITE_BINARY, @@ -315,6 +321,17 @@ struct libwebsocket_context; * Notice the callback is coming to protocols[0] all the time, * because there is no specific protocol handshook yet. * + * LWS_CALLBACK_CONFIRM_EXTENSION_OKAY: When the server handshake code + * sees that it does support a requested extension, before + * accepting the extension by additing to the list sent back to + * the client it gives this callback just to check that it's okay + * to use that extension. It calls back to the requested protocol + * and with @in being the extension name, @len is 0 and @user is + * valid. Note though at this time the ESTABLISHED callback hasn't + * happened yet so if you initialize @user content there, @user + * content during this callback might not be useful for anything. + * Notice this callback comes to protocols[0]. + * * The next four reasons are optional and only need taking care of if you * will be integrating libwebsockets sockets into an external polling * array. @@ -415,8 +432,8 @@ struct libwebsocket_extension { const char *name; int (*callback)(struct libwebsocket_context *context, struct libwebsocket *wsi, - enum libwebsocket_callback_reasons reason, void *user, - void *in, size_t len); + enum libwebsocket_extension_callback_reasons reason, + void *user, void *in, size_t len); size_t per_session_data_size; }; diff --git a/libwebsockets-api-doc.html b/libwebsockets-api-doc.html index 239f3934..0a039e9e 100644 --- a/libwebsockets-api-doc.html +++ b/libwebsockets-api-doc.html @@ -241,6 +241,9 @@ interface name, eg, "eth2" specific callback for each one. The list is ended with an entry that has a NULL callback pointer. It's not const because we write the owning_server member +
Notice the callback is coming to protocols[0] all the time, because there is no specific protocol handshook yet. + +
+When the server handshake code +sees that it does support a requested extension, before +accepting the extension by additing to the list sent back to +the client it gives this callback just to check that it's okay +to use that extension. It calls back to the requested protocol +and with in being the extension name, len is 0 and user is +valid. Note though at this time the ESTABLISHED callback hasn't +happened yet so if you initialize user content there, user +content during this callback might not be useful for anything. +Notice this callback comes to protocols[0].The next four reasons are optional and only need taking care of if you will be integrating libwebsockets sockets into an external polling