mirror of
https://git.haproxy.org/git/haproxy.git/
synced 2025-08-10 09:07: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_connection_header(struct h1m *h1m, struct ist *value);
|
||||||
void h1_parse_upgrade_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 */
|
/* for debugging, reports the HTTP/1 message state name */
|
||||||
static inline const char *h1m_state_str(enum h1m_state msg_state)
|
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 <ctype.h>
|
||||||
|
|
||||||
|
#include <import/sha1.h>
|
||||||
|
|
||||||
#include <haproxy/api.h>
|
#include <haproxy/api.h>
|
||||||
|
#include <haproxy/base64.h>
|
||||||
#include <haproxy/h1.h>
|
#include <haproxy/h1.h>
|
||||||
#include <haproxy/http-hdr.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;
|
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 */
|
enum http_meth_t meth; /* HTTP request method */
|
||||||
uint16_t status; /* HTTP response status */
|
uint16_t status; /* HTTP response status */
|
||||||
|
|
||||||
|
char ws_key[25]; /* websocket handshake key */
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Map of headers used to convert outgoing headers */
|
/* 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->flags = H1S_F_WANT_KAL;
|
||||||
h1s->subs = NULL;
|
h1s->subs = NULL;
|
||||||
h1s->rxbuf = BUF_NULL;
|
h1s->rxbuf = BUF_NULL;
|
||||||
|
memset(h1s->ws_key, 0, sizeof(h1s->ws_key));
|
||||||
|
|
||||||
h1m_init_req(&h1s->req);
|
h1m_init_req(&h1s->req);
|
||||||
h1s->req.flags |= (H1_MF_NO_PHDR|H1_MF_CLEAN_CONN_HDR);
|
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
|
/* Search for a websocket key header. The message should have been identified
|
||||||
* as a valid websocket handshake.
|
* 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
|
* Returns 0 if no key found
|
||||||
*/
|
*/
|
||||||
static int h1_search_websocket_key(struct h1s *h1s, struct h1m *h1m, struct htx *htx)
|
static int h1_search_websocket_key(struct h1s *h1s, struct h1m *h1m, struct htx *htx)
|
||||||
{
|
{
|
||||||
struct htx_blk *blk;
|
struct htx_blk *blk;
|
||||||
enum htx_blk_type type;
|
enum htx_blk_type type;
|
||||||
struct ist n;
|
struct ist n, v;
|
||||||
int ws_key_found = 0, idx;
|
int ws_key_found = 0, idx;
|
||||||
|
|
||||||
idx = htx_get_head(htx); // returns the SL that we skip
|
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;
|
break;
|
||||||
|
|
||||||
n = htx_get_blk_name(htx, blk);
|
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)) {
|
!(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;
|
ws_key_found = 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -1702,6 +1716,7 @@ static size_t h1_process_output(struct h1c *h1c, struct buffer *buf, size_t coun
|
|||||||
struct buffer tmp;
|
struct buffer tmp;
|
||||||
size_t total = 0;
|
size_t total = 0;
|
||||||
int last_data = 0;
|
int last_data = 0;
|
||||||
|
int ws_key_found = 0;
|
||||||
|
|
||||||
if (!count)
|
if (!count)
|
||||||
goto end;
|
goto end;
|
||||||
@ -1905,6 +1920,13 @@ static size_t h1_process_output(struct h1c *h1c, struct buffer *buf, size_t coun
|
|||||||
if (!v.len)
|
if (!v.len)
|
||||||
goto skip_hdr;
|
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 */
|
/* Skip header if same name is used to add the server name */
|
||||||
if (!(h1m->flags & H1_MF_RESP) && h1c->px->server_id_hdr_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;
|
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"),
|
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);
|
H1_EV_TX_DATA|H1_EV_TX_HDRS, h1c->conn, h1s);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user