*/
#include <ctype.h>
+
+#include <import/sha1.h>
+
#include <haproxy/api.h>
+#include <haproxy/base64.h>
#include <haproxy/h1.h>
#include <haproxy/http-hdr.h>
}
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);
+}
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 */
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);
/* 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
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;
}
struct buffer tmp;
size_t total = 0;
int last_data = 0;
+ int ws_key_found = 0;
if (!count)
goto end;
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 &&
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);