Thanks to visit codestin.com
Credit goes to github.com

Skip to content
80 changes: 80 additions & 0 deletions examples/h2o/wildcard.crt
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 1 (0x1)
Signature Algorithm: sha256WithRSAEncryption
Issuer: CN=H2O Test CA
Validity
Not Before: Jun 9 21:40:42 2018 GMT
Not After : Jun 6 21:40:42 2028 GMT
Subject: CN=*.127.0.0.1.xip.io
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (2048 bit)
Modulus:
00:bc:e7:3c:bb:6d:2b:6b:ac:60:a6:3b:cb:f4:0d:
0a:fa:c7:ad:55:5e:40:a7:b3:f2:bb:de:ab:57:30:
b5:df:ed:a9:c3:fc:46:29:c7:f1:4e:7c:c8:49:d0:
18:2c:ba:d1:9a:e0:71:e6:1d:de:23:fd:2d:3e:a4:
1a:44:c8:c1:b8:08:c6:78:5b:b7:e1:30:14:48:b4:
45:81:29:1a:e5:70:f0:3e:ee:4a:25:2f:0e:2e:d1:
7a:61:77:6f:f0:55:04:b6:f1:e9:eb:5b:eb:0b:d7:
fc:7d:00:4b:56:5e:af:04:c0:02:17:0c:7a:e2:ab:
87:40:87:a6:5e:0b:4c:bb:53:77:d4:c6:70:ff:b8:
7e:fe:a8:06:fe:0e:f3:92:07:3f:6c:86:71:f3:45:
42:5b:ad:45:ec:65:2c:6c:f2:67:e7:1d:1e:b7:13:
8b:cf:8b:36:cf:30:af:32:cc:91:b0:d7:10:b1:7d:
cf:4d:4a:fb:04:ad:d8:34:68:35:5c:33:f8:09:11:
6f:08:e0:42:4e:f7:7e:40:bc:58:00:2a:f7:76:b3:
2f:68:1d:04:81:96:f2:d5:a8:92:8f:64:d8:9e:d1:
c0:ac:8d:51:56:78:60:f3:fb:31:16:c7:cb:39:fb:
d6:3d:fd:d2:80:3e:37:4b:5b:f1:bf:f1:b0:68:8c:
cd:31
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Basic Constraints:
CA:FALSE
Netscape Comment:
OpenSSL Generated Certificate
X509v3 Subject Key Identifier:
0C:42:CB:56:F5:6E:DC:A8:D3:39:A6:84:15:10:F4:81:74:61:FF:35
X509v3 Authority Key Identifier:
DirName:/CN=H2O Test CA
serial:9A:A4:B5:57:B0:EF:CD:B6

Signature Algorithm: sha256WithRSAEncryption
ab:8a:a8:c8:09:f9:f3:87:5b:a6:3c:25:59:85:a1:b5:37:68:
59:03:ce:30:9d:df:53:b2:7b:a6:4f:8b:57:09:ce:20:78:b7:
f9:b9:ce:c4:16:19:38:b6:e1:82:13:b1:33:84:84:e6:1f:b4:
62:39:ec:41:df:50:e8:28:16:ce:14:93:54:65:e6:26:32:c8:
19:a2:4e:cb:28:d1:8c:ce:f6:ef:22:be:9e:f5:07:fe:69:fa:
09:cc:cb:81:dd:e7:de:73:52:d4:9d:2b:23:de:1d:62:9d:10:
f2:ca:7c:c0:72:29:69:47:db:ae:a7:24:60:81:62:b9:1b:b1:
f7:f1:71:b2:05:20:65:bc:86:c3:9d:d3:19:3e:74:87:0c:e7:
96:10:c2:fa:31:cf:31:c7:bf:91:83:80:8d:3b:9c:5a:95:2a:
81:aa:45:1e:53:5b:69:f7:c0:71:51:63:fd:93:67:f8:54:8c:
1e:96:62:d7:72:df:0e:af:65:72:fc:65:53:8c:25:dc:3c:23:
c9:a5:95:64:bd:d4:04:1d:b8:68:20:a8:dc:23:1a:2c:e9:cc:
03:5a:d4:1a:0e:6b:08:79:53:bc:ba:e7:d4:68:d2:70:7d:ca:
e3:d8:fc:ba:bc:8a:5f:94:39:d7:68:8a:28:b1:36:1c:f1:55:
27:73:23:88
-----BEGIN CERTIFICATE-----
MIIDPDCCAiSgAwIBAgIBATANBgkqhkiG9w0BAQsFADAWMRQwEgYDVQQDEwtIMk8g
VGVzdCBDQTAeFw0xODA2MDkyMTQwNDJaFw0yODA2MDYyMTQwNDJaMB0xGzAZBgNV
BAMMEiouMTI3LjAuMC4xLnhpcC5pbzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
AQoCggEBALznPLttK2usYKY7y/QNCvrHrVVeQKez8rveq1cwtd/tqcP8RinH8U58
yEnQGCy60ZrgceYd3iP9LT6kGkTIwbgIxnhbt+EwFEi0RYEpGuVw8D7uSiUvDi7R
emF3b/BVBLbx6etb6wvX/H0AS1ZerwTAAhcMeuKrh0CHpl4LTLtTd9TGcP+4fv6o
Bv4O85IHP2yGcfNFQlutRexlLGzyZ+cdHrcTi8+LNs8wrzLMkbDXELF9z01K+wSt
2DRoNVwz+AkRbwjgQk73fkC8WAAq93azL2gdBIGW8tWoko9k2J7RwKyNUVZ4YPP7
MRbHyzn71j390oA+N0tb8b/xsGiMzTECAwEAAaOBjTCBijAJBgNVHRMEAjAAMCwG
CWCGSAGG+EIBDQQfFh1PcGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNV
HQ4EFgQUDELLVvVu3KjTOaaEFRD0gXRh/zUwMAYDVR0jBCkwJ6EapBgwFjEUMBIG
A1UEAxMLSDJPIFRlc3QgQ0GCCQCapLVXsO/NtjANBgkqhkiG9w0BAQsFAAOCAQEA
q4qoyAn584dbpjwlWYWhtTdoWQPOMJ3fU7J7pk+LVwnOIHi3+bnOxBYZOLbhghOx
M4SE5h+0YjnsQd9Q6CgWzhSTVGXmJjLIGaJOyyjRjM727yK+nvUH/mn6CczLgd3n
3nNS1J0rI94dYp0Q8sp8wHIpaUfbrqckYIFiuRux9/FxsgUgZbyGw53TGT50hwzn
lhDC+jHPMce/kYOAjTucWpUqgapFHlNbaffAcVFj/ZNn+FSMHpZi13LfDq9lcvxl
U4wl3DwjyaWVZL3UBB24aCCo3CMaLOnMA1rUGg5rCHlTvLrn1GjScH3K49j8uryK
X5Q512iKKLE2HPFVJ3MjiA==
-----END CERTIFICATE-----
27 changes: 27 additions & 0 deletions examples/h2o/wildcard.key
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEAvOc8u20ra6xgpjvL9A0K+setVV5Ap7Pyu96rVzC13+2pw/xG
KcfxTnzISdAYLLrRmuBx5h3eI/0tPqQaRMjBuAjGeFu34TAUSLRFgSka5XDwPu5K
JS8OLtF6YXdv8FUEtvHp61vrC9f8fQBLVl6vBMACFwx64quHQIemXgtMu1N31MZw
/7h+/qgG/g7zkgc/bIZx80VCW61F7GUsbPJn5x0etxOLz4s2zzCvMsyRsNcQsX3P
TUr7BK3YNGg1XDP4CRFvCOBCTvd+QLxYACr3drMvaB0EgZby1aiSj2TYntHArI1R
Vnhg8/sxFsfLOfvWPf3SgD43S1vxv/GwaIzNMQIDAQABAoIBADSmVnz+rm1yO/XZ
EL1SrU68PIlgnbCgLPVD2ImSn/5rpTdkpsGaYp03ljNMdpBw+V6rU9OJW5K4S9X7
sv6c3bFHcNm25ocy+VRSOlUHeeGdGrjPfYbjmJZf+DYWLfLUH15GCqZ4lghVCKBm
ZpkKBsnSIkL+TXnDyQypQitQBvDmlduLXpg70LZ9WaRQRSbmfT29ousJkhaz9tx1
WVZP21LAmFCHhpmhG+1QyrLiunfXRsMhhPQYB8AukhjYPzIJrmzSeRufHJZ/19Pd
UzttoxQKfLKCXd51YSUhKHoURDyZWPvmzpzfg5JcxqkFodzo1Cfx7aHGvSXEoPw3
pe3FWykCgYEA6f+eXZ9P38zYr0OJ3cQ24lSoyWaSWGROLi/UKci/vARf3U8RScNI
Jh7yW/palM1tw+YV5m6teXMFkwWjUTg2bqlJ8TMNRPZO2Zv5uoYYChgFdbCiii23
zCZ+RGJ4YhlNawe7rhNl1ETHZu3El7bKkC0/cr+xVTkzefKO+jl0YD8CgYEAzqor
ROlbzHy0ryGsHFi8mbD7jrJApZ/xx+26f3b37NnpMSIMMWG67bqTl8JHI4GIi5OY
AiFXZ5wqBKpSTZYW0Do79UVMGvOhvj1BqSenJNidvOXwCAJCGnS3zrRpTh4s4tey
WUtzo9bN24OEmrZPVc67NmlmMD13yG7xYihSdo8CgYBkP5PsTi1dFxiZ9Zhh/wb4
bgalyiDWZ5qq5OjDQfMKrOEZIh+o91W4pzdzuk0GRQBKlg852p7NUj7IHvk5+Zgw
TqyT/igoLFHZnp4dkAdKg8ILCrKH+lAs0Sz1RS6H8IkfWn/Icx0EFLdM3H6F9NTR
ceEl2wQxG8F29DjW8Lhv5wKBgFA6m6Wa+jo+R16dsdUnVcBMhrv000f4+cHltjr5
knHufqrGTvl0uT7E6btGfpt4E+wrlh1tmCLu6xj4jg70r1KS6OfkyRDFeAsEeNMU
Q7z4IvRMR4y0Y7klip3CSAc9i6tclS45LmFyaGRJgFcXY2Eqal9LN5KOqkuzVh4H
DUxdAoGANy2ZQPAyiLQEjC0KjWWOySaaG5a9ZiKmfkaZCXUoy4Q9oNYfwFZZu/q1
+y/s8xAJY3r/hNa+eXJVNZInDcON+5WnOyHwh5Thbqck7gsVfiuUeJIIwCoZ1lHB
Y0c6e3U/lGEjsTQnoH0Z9ssizX23fsSUkrQ9RyVSRf6IWMf0MaM=
-----END RSA PRIVATE KEY-----
3 changes: 3 additions & 0 deletions include/h2o.h
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,8 @@ struct st_h2o_globalconf_t {
* list of callbacks
*/
h2o_protocol_callbacks_t callbacks;
/* */
h2o_iovec_t origin_frame;
} http2;

struct {
Expand Down Expand Up @@ -1177,6 +1179,7 @@ typedef struct st_h2o_accept_ctx_t {
h2o_context_t *ctx;
h2o_hostconf_t **hosts;
SSL_CTX *ssl_ctx;
h2o_iovec_t *http2_origin_frame;
int expect_proxy_line;
h2o_multithread_receiver_t *libmemcached_receiver;
} h2o_accept_ctx_t;
Expand Down
3 changes: 3 additions & 0 deletions include/h2o/http2_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ static h2o_hpack_header_table_entry_t *h2o_hpack_header_table_get(h2o_hpack_head
#define H2O_HTTP2_FRAME_TYPE_GOAWAY 7
#define H2O_HTTP2_FRAME_TYPE_WINDOW_UPDATE 8
#define H2O_HTTP2_FRAME_TYPE_CONTINUATION 9
#define H2O_HTTP2_FRAME_TYPE_ORIGIN 12

#define H2O_HTTP2_FRAME_FLAG_END_STREAM 0x1
#define H2O_HTTP2_FRAME_FLAG_ACK 0x1
Expand Down Expand Up @@ -273,6 +274,7 @@ struct st_h2o_http2_conn_t {
struct {
h2o_linklist_t blocked_streams;
} early_data;
h2o_iovec_t *http2_origin_frame;
};

int h2o_http2_update_peer_settings(h2o_http2_settings_t *settings, const uint8_t *src, size_t len, const char **err_desc);
Expand All @@ -287,6 +289,7 @@ void h2o_http2__encode_rst_stream_frame(h2o_buffer_t **buf, uint32_t stream_id,
void h2o_http2_encode_ping_frame(h2o_buffer_t **buf, int is_ack, const uint8_t *data);
void h2o_http2_encode_goaway_frame(h2o_buffer_t **buf, uint32_t last_stream_id, int errnum, h2o_iovec_t additional_data);
void h2o_http2_encode_window_update_frame(h2o_buffer_t **buf, uint32_t stream_id, int32_t window_size_increment);
void h2o_http2_encode_origin_frame(h2o_buffer_t **buf, h2o_iovec_t payload);
ssize_t h2o_http2_decode_frame(h2o_http2_frame_t *frame, const uint8_t *src, size_t len, const char **err_desc);
int h2o_http2_decode_data_payload(h2o_http2_data_payload_t *payload, const h2o_http2_frame_t *frame, const char **err_desc);
int h2o_http2_decode_headers_payload(h2o_http2_headers_payload_t *payload, const h2o_http2_frame_t *frame, const char **err_desc);
Expand Down
5 changes: 5 additions & 0 deletions lib/http2/connection.c
Original file line number Diff line number Diff line change
Expand Up @@ -1004,6 +1004,10 @@ static ssize_t expect_preface(h2o_http2_conn_t *conn, const uint8_t *src, size_t
h2o_iovec_t vec = h2o_buffer_reserve(&conn->_write.buf, SERVER_PREFACE.len);
memcpy(vec.base, SERVER_PREFACE.base, SERVER_PREFACE.len);
conn->_write.buf->size += SERVER_PREFACE.len;
if (conn->http2_origin_frame) {
/* write origin frame */
h2o_http2_encode_origin_frame(&conn->_write.buf, *conn->http2_origin_frame);
}
h2o_http2_conn_request_write(conn);
}

Expand Down Expand Up @@ -1529,6 +1533,7 @@ static int foreach_request(h2o_context_t *ctx, int (*cb)(h2o_req_t *req, void *c
void h2o_http2_accept(h2o_accept_ctx_t *ctx, h2o_socket_t *sock, struct timeval connected_at)
{
h2o_http2_conn_t *conn = create_conn(ctx->ctx, ctx->hosts, sock, connected_at);
conn->http2_origin_frame = ctx->http2_origin_frame;
sock->data = conn;
h2o_socket_read_start(conn->sock, on_read);
update_idle_timeout(conn);
Expand Down
6 changes: 6 additions & 0 deletions lib/http2/frame.c
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,12 @@ void h2o_http2_encode_window_update_frame(h2o_buffer_t **buf, uint32_t stream_id
dst = h2o_http2_encode32u(dst, window_size_increment);
}

void h2o_http2_encode_origin_frame(h2o_buffer_t **buf, h2o_iovec_t payload)
{
uint8_t *dst = allocate_frame(buf, payload.len, H2O_HTTP2_FRAME_TYPE_ORIGIN, 0, 0);
memcpy(dst, payload.base, payload.len);
}

ssize_t h2o_http2_decode_frame(h2o_http2_frame_t *frame, const uint8_t *src, size_t len, const char **err_desc)
{
if (len < H2O_HTTP2_FRAME_HEADER_SIZE)
Expand Down
66 changes: 58 additions & 8 deletions src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ struct listener_ssl_config_t {
H2O_VECTOR(h2o_iovec_t) hostnames;
char *certificate_file;
SSL_CTX *ctx;
h2o_iovec_t *http2_origin_frame;
#if H2O_USE_OCSP
struct {
uint64_t interval;
Expand Down Expand Up @@ -525,12 +526,39 @@ static void listener_setup_ssl_add_host(struct listener_ssl_config_t *ssl_config
ssl_config->hostnames.entries[ssl_config->hostnames.size++] = h2o_iovec_init(host.base, host_end - host.base);
}

static h2o_iovec_t *build_http2_origin_frame(h2o_configurator_command_t *cmd, yoml_t **origins, size_t nr_origins)
{
size_t i;
h2o_iovec_t *http2_origin_frame = h2o_mem_alloc(sizeof(*http2_origin_frame));
uint16_t lengths[nr_origins];
h2o_iovec_t elems[nr_origins * 2];
for (i = 0; i < nr_origins; i++) {
yoml_t *origin = origins[i];
if (origin->type != YOML_TYPE_SCALAR) {
h2o_configurator_errprintf(cmd, origin, "element of a sequence passed to http2-origin-frame must be a scalar");
free(http2_origin_frame);
return NULL;
}
size_t origin_len = strlen(origins[i]->data.scalar);
lengths[i] = htons(origin_len);
elems[i * 2].base = (char *)&lengths[i];
elems[i * 2].len = 2;
elems[i * 2 + 1].base = origins[i]->data.scalar;
elems[i * 2 + 1].len = origin_len;
h2o_strtolower(elems[i * 2 + 1].base, origin_len);
}
*http2_origin_frame = h2o_concat_list(NULL, elems, nr_origins * 2);
return http2_origin_frame;
}

static int listener_setup_ssl(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *listen_node,
yoml_t **ssl_node, struct listener_config_t *listener, int listener_is_new)
{
SSL_CTX *ssl_ctx = NULL;
yoml_t **certificate_file, **key_file, **dh_file, **min_version, **max_version, **cipher_suite, **ocsp_update_cmd,
**ocsp_update_interval_node, **ocsp_max_failures_node, **cipher_preference_node, **neverbleed_node;
**ocsp_update_interval_node, **ocsp_max_failures_node, **cipher_preference_node, **neverbleed_node,
**http2_origin_frame_node;
h2o_iovec_t *http2_origin_frame = NULL;
long ssl_options = SSL_OP_ALL;
uint64_t ocsp_update_interval = 4 * 60 * 60; /* defaults to 4 hours */
unsigned ocsp_max_failures = 3; /* defaults to 3; permit 3 failures before temporary disabling OCSP stapling */
Expand All @@ -551,12 +579,14 @@ static int listener_setup_ssl(h2o_configurator_command_t *cmd, h2o_configurator_
return 0;

/* parse */
if (h2o_configurator_parse_mapping(
cmd, *ssl_node, "certificate-file:s,key-file:s", "min-version:s,minimum-version:s,max-version:s,maximum-version:s,"
"cipher-suite:s,ocsp-update-cmd:s,ocsp-update-interval:*,"
"ocsp-max-failures:*,dh-file:s,cipher-preference:*,neverbleed:*",
&certificate_file, &key_file, &min_version, &min_version, &max_version, &max_version, &cipher_suite, &ocsp_update_cmd,
&ocsp_update_interval_node, &ocsp_max_failures_node, &dh_file, &cipher_preference_node, &neverbleed_node) != 0)
if (h2o_configurator_parse_mapping(cmd, *ssl_node, "certificate-file:s,key-file:s",
"min-version:s,minimum-version:s,max-version:s,maximum-version:s,"
"cipher-suite:s,ocsp-update-cmd:s,ocsp-update-interval:*,"
"ocsp-max-failures:*,dh-file:s,cipher-preference:*,neverbleed:*,"
"http2-origin-frame:*",
&certificate_file, &key_file, &min_version, &min_version, &max_version, &max_version,
&cipher_suite, &ocsp_update_cmd, &ocsp_update_interval_node, &ocsp_max_failures_node,
&dh_file, &cipher_preference_node, &neverbleed_node, &http2_origin_frame_node) != 0)
return -1;
if (cipher_preference_node != NULL) {
switch (h2o_configurator_get_one_of(cmd, *cipher_preference_node, "client,server")) {
Expand All @@ -572,6 +602,23 @@ static int listener_setup_ssl(h2o_configurator_command_t *cmd, h2o_configurator_
}
if (neverbleed_node != NULL && (use_neverbleed = (int)h2o_configurator_get_one_of(cmd, *neverbleed_node, "off,on")) == -1)
return -1;
if (http2_origin_frame_node != NULL) {
switch ((*http2_origin_frame_node)->type) {
case YOML_TYPE_SCALAR:
if ((http2_origin_frame = build_http2_origin_frame(cmd, http2_origin_frame_node, 1)) == NULL)
return -1;
break;
case YOML_TYPE_SEQUENCE:
if ((http2_origin_frame = build_http2_origin_frame(cmd, (*http2_origin_frame_node)->data.sequence.elements,
(*http2_origin_frame_node)->data.sequence.size)) == NULL)
return -1;
break;
default:
h2o_configurator_errprintf(cmd, *http2_origin_frame_node,
"argument to `http2-origin-frame` must be either a scalar or a sequence");
return -1;
}
}
if (min_version != NULL) {
#define MAP(tok, op) \
if (strcasecmp((*min_version)->data.scalar, tok) == 0) { \
Expand Down Expand Up @@ -715,6 +762,7 @@ static int listener_setup_ssl(h2o_configurator_command_t *cmd, h2o_configurator_
}
ssl_config->ctx = ssl_ctx;
ssl_config->certificate_file = h2o_strdup(NULL, (*certificate_file)->data.scalar, SIZE_MAX).base;
ssl_config->http2_origin_frame = http2_origin_frame;

#if !H2O_USE_OCSP
if (ocsp_update_interval != 0)
Expand Down Expand Up @@ -1568,8 +1616,10 @@ H2O_NORETURN static void *run_loop(void *_thread_index)
memset(listeners + i, 0, sizeof(listeners[i]));
listeners[i].accept_ctx.ctx = &conf.threads[thread_index].ctx;
listeners[i].accept_ctx.hosts = listener_config->hosts;
if (listener_config->ssl.size != 0)
if (listener_config->ssl.size != 0) {
listeners[i].accept_ctx.ssl_ctx = listener_config->ssl.entries[0]->ctx;
listeners[i].accept_ctx.http2_origin_frame = listener_config->ssl.entries[0]->http2_origin_frame;
}
listeners[i].accept_ctx.expect_proxy_line = listener_config->proxy_protocol;
listeners[i].accept_ctx.libmemcached_receiver = &conf.threads[thread_index].memcached;
listeners[i].sock = h2o_evloop_socket_create(conf.threads[thread_index].ctx.loop, fd, H2O_SOCKET_FLAG_DONT_READ);
Expand Down
58 changes: 58 additions & 0 deletions t/50origin-frame.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
use strict;
use warnings;
use Net::EmptyPort qw(check_port empty_port);
use Test::More;
use Time::HiRes;
use t::Util;

sub test_origin_frame {
my ($origin_conf,$expected) = @_;
my $server = spawn_h2o(sub {
my ($port, $tls_port) = @_;
return << "EOT";
listen:
port: $tls_port
ssl:
key-file: examples/h2o/wildcard.key
certificate-file: examples/h2o/wildcard.crt
$origin_conf
hosts:
"*.127.0.0.1.xip.io:$tls_port":
paths:
/:
file.dir: examples/doc_root
EOT
});

my $output = run_with_h2get($server, <<"EOR");
h2g = H2.new
host = ARGV[0]
h2g.connect(host)
h2g.send_prefix()
h2g.send_settings()
i = 0
while i < 3 do
f = h2g.read(-1)
if f.type_num == 12 then
puts f.len
puts f.payload.dump
end

if f.type == "SETTINGS" and (f.flags == ACK) then
# ignore
elsif f.type == "SETTINGS" then
h2g.send_settings_ack()
end
i += 1
end
EOR

chomp $output;
is $output, $expected;
}

test_origin_frame('', '');
test_origin_frame('http2-origin-frame: [ ]', "0\n\"\"");
test_origin_frame('http2-origin-frame: [ "https://a.127.0.0.1.xip.io" ]', "28\n\"\\000\\032https://a.127.0.0.1.xip.io\"");
test_origin_frame('http2-origin-frame: [ "https://a.127.0.0.1.xip.io", "https://b.127.0.0.1.xip.io" ]', "56\n\"\\000\\032https://a.127.0.0.1.xip.io\\000\\032https://b.127.0.0.1.xip.io\"");
done_testing();