mirror of
https://git.haproxy.org/git/haproxy.git/
synced 2025-08-07 07:37:02 +02:00
MEDIUM: h1: generate WebSocket key on response if needed
Add the Sec-Websocket-Accept header on a websocket handshake response. This header may be missing if a h2 server is used with a h1 client. The response key is calculated following the rfc6455. For this, the handshake request key must be stored in the h1 session, as a new field name ws_key. Note that this is only done if the message has been prealably identified as a Websocket handshake request.
This commit is contained in:
parent
18ee5c3eb0
commit
c193823343
@ -150,6 +150,8 @@ void h1_parse_xfer_enc_header(struct h1m *h1m, struct ist value);
|
||||
void h1_parse_connection_header(struct h1m *h1m, struct ist *value);
|
||||
void h1_parse_upgrade_header(struct h1m *h1m, struct ist value);
|
||||
|
||||
void h1_calculate_ws_output_key(const char *key, char *result);
|
||||
|
||||
/* for debugging, reports the HTTP/1 message state name */
|
||||
static inline const char *h1m_state_str(enum h1m_state msg_state)
|
||||
{
|
||||
|
29
src/h1.c
29
src/h1.c
@ -11,7 +11,11 @@
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
|
||||
#include <import/sha1.h>
|
||||
|
||||
#include <haproxy/api.h>
|
||||
#include <haproxy/base64.h>
|
||||
#include <haproxy/h1.h>
|
||||
#include <haproxy/http-hdr.h>
|
||||
|
||||
@ -1051,3 +1055,28 @@ int h1_measure_trailers(const struct buffer *buf, unsigned int ofs, unsigned int
|
||||
}
|
||||
return count - ofs;
|
||||
}
|
||||
|
||||
#define H1_WS_KEY_SUFFIX_GUID "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
|
||||
|
||||
/*
|
||||
* Calculate the WebSocket handshake response key from <key_in>. Following the
|
||||
* rfc6455, <key_in> must be 24 bytes longs. The result is stored in <key_out>
|
||||
* as a 29 bytes long string.
|
||||
*/
|
||||
void h1_calculate_ws_output_key(const char *key, char *result)
|
||||
{
|
||||
blk_SHA_CTX sha1_ctx;
|
||||
char hash_in[60], hash_out[20];
|
||||
|
||||
/* concatenate the key with a fixed suffix */
|
||||
memcpy(hash_in, key, 24);
|
||||
memcpy(&hash_in[24], H1_WS_KEY_SUFFIX_GUID, 36);
|
||||
|
||||
/* sha1 the result */
|
||||
blk_SHA1_Init(&sha1_ctx);
|
||||
blk_SHA1_Update(&sha1_ctx, hash_in, 60);
|
||||
blk_SHA1_Final((unsigned char *)hash_out, &sha1_ctx);
|
||||
|
||||
/* encode in base64 the hash */
|
||||
a2base64(hash_out, 20, result, 29);
|
||||
}
|
||||
|
41
src/mux_h1.c
41
src/mux_h1.c
@ -124,6 +124,8 @@ struct h1s {
|
||||
|
||||
enum http_meth_t meth; /* HTTP request method */
|
||||
uint16_t status; /* HTTP response status */
|
||||
|
||||
char ws_key[25]; /* websocket handshake key */
|
||||
};
|
||||
|
||||
/* Map of headers used to convert outgoing headers */
|
||||
@ -647,6 +649,7 @@ static struct h1s *h1s_new(struct h1c *h1c)
|
||||
h1s->flags = H1S_F_WANT_KAL;
|
||||
h1s->subs = NULL;
|
||||
h1s->rxbuf = BUF_NULL;
|
||||
memset(h1s->ws_key, 0, sizeof(h1s->ws_key));
|
||||
|
||||
h1m_init_req(&h1s->req);
|
||||
h1s->req.flags |= (H1_MF_NO_PHDR|H1_MF_CLEAN_CONN_HDR);
|
||||
@ -1300,13 +1303,17 @@ static void h1_set_tunnel_mode(struct h1s *h1s)
|
||||
|
||||
/* Search for a websocket key header. The message should have been identified
|
||||
* as a valid websocket handshake.
|
||||
*
|
||||
* On the request side, if found the key is stored in the session. It might be
|
||||
* needed to calculate response key if the server side is using http/2.
|
||||
*
|
||||
* Returns 0 if no key found
|
||||
*/
|
||||
static int h1_search_websocket_key(struct h1s *h1s, struct h1m *h1m, struct htx *htx)
|
||||
{
|
||||
struct htx_blk *blk;
|
||||
enum htx_blk_type type;
|
||||
struct ist n;
|
||||
struct ist n, v;
|
||||
int ws_key_found = 0, idx;
|
||||
|
||||
idx = htx_get_head(htx); // returns the SL that we skip
|
||||
@ -1321,9 +1328,16 @@ static int h1_search_websocket_key(struct h1s *h1s, struct h1m *h1m, struct htx
|
||||
break;
|
||||
|
||||
n = htx_get_blk_name(htx, blk);
|
||||
v = htx_get_blk_value(htx, blk);
|
||||
|
||||
if (isteqi(n, ist("sec-websocket-key")) &&
|
||||
/* Websocket key is base64 encoded of 16 bytes */
|
||||
if (isteqi(n, ist("sec-websocket-key")) && v.len == 24 &&
|
||||
!(h1m->flags & H1_MF_RESP)) {
|
||||
/* Copy the key on request side
|
||||
* we might need it if the server is using h2 and does
|
||||
* not provide the response
|
||||
*/
|
||||
memcpy(h1s->ws_key, v.ptr, 24);
|
||||
ws_key_found = 1;
|
||||
break;
|
||||
}
|
||||
@ -1702,6 +1716,7 @@ static size_t h1_process_output(struct h1c *h1c, struct buffer *buf, size_t coun
|
||||
struct buffer tmp;
|
||||
size_t total = 0;
|
||||
int last_data = 0;
|
||||
int ws_key_found = 0;
|
||||
|
||||
if (!count)
|
||||
goto end;
|
||||
@ -1905,6 +1920,13 @@ static size_t h1_process_output(struct h1c *h1c, struct buffer *buf, size_t coun
|
||||
if (!v.len)
|
||||
goto skip_hdr;
|
||||
}
|
||||
else if (isteq(n, ist("upgrade"))) {
|
||||
h1_parse_upgrade_header(h1m, v);
|
||||
}
|
||||
else if (isteq(n, ist("sec-websocket-accept")) &&
|
||||
h1m->flags & H1_MF_RESP) {
|
||||
ws_key_found = 1;
|
||||
}
|
||||
|
||||
/* Skip header if same name is used to add the server name */
|
||||
if (!(h1m->flags & H1_MF_RESP) && h1c->px->server_id_hdr_name &&
|
||||
@ -1984,6 +2006,21 @@ static size_t h1_process_output(struct h1c *h1c, struct buffer *buf, size_t coun
|
||||
h1s->flags |= H1S_F_HAVE_SRV_NAME;
|
||||
}
|
||||
|
||||
/* Add websocket handshake key if needed */
|
||||
if ((h1m->flags & (H1_MF_CONN_UPG|H1_MF_UPG_WEBSOCKET)) == (H1_MF_CONN_UPG|H1_MF_UPG_WEBSOCKET) &&
|
||||
!ws_key_found) {
|
||||
if (h1m->flags & H1_MF_RESP) {
|
||||
/* add the response header key */
|
||||
char key[29];
|
||||
h1_calculate_ws_output_key(h1s->ws_key, key);
|
||||
if (!h1_format_htx_hdr(ist("Sec-Websocket-Accept"),
|
||||
ist(key),
|
||||
&tmp)) {
|
||||
goto full;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TRACE_PROTO((!(h1m->flags & H1_MF_RESP) ? "H1 request headers xferred" : "H1 response headers xferred"),
|
||||
H1_EV_TX_DATA|H1_EV_TX_HDRS, h1c->conn, h1s);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user