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

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 6 additions & 21 deletions include/h2o/header.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@
extern "C" {
#endif

typedef struct st_h2o_header_flags_t {
unsigned char dont_compress : 1;
} h2o_header_flags_t;

/**
* represents a HTTP header
*/
Expand All @@ -46,8 +50,8 @@ typedef struct st_h2o_header_t {
*/
h2o_iovec_t value;
/**
* flags of the header
*/
* flags of the header
*/
h2o_header_flags_t flags;
} h2o_header_t;

Expand Down Expand Up @@ -103,14 +107,6 @@ ssize_t h2o_set_header_token(h2o_mem_pool_t *pool, h2o_headers_t *headers, const
* deletes a header from list
*/
ssize_t h2o_delete_header(h2o_headers_t *headers, ssize_t cursor);
/**
* validates the structure
*/
static void h2o_header_validate(const h2o_header_t *header);
/**
* returns an boolean value if given header's name is a token, with validation
*/
static int h2o_header_is_token(const h2o_header_t *header);

/* inline definitions */

Expand All @@ -123,17 +119,6 @@ inline int h2o_header_name_is_equal(const h2o_header_t *x, const h2o_header_t *y
}
}

inline void h2o_header_validate(const h2o_header_t *header)
{
assert(header->flags.token_index_plus1 == 0 || header->name == &h2o__tokens[header->flags.token_index_plus1 - 1].buf);
}

inline int h2o_header_is_token(const h2o_header_t *header)
{
h2o_header_validate(header);
return header->flags.token_index_plus1 != 0;
}

#ifdef __cplusplus
}
#endif
Expand Down
9 changes: 4 additions & 5 deletions include/h2o/token.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,23 +29,22 @@
extern "C" {
#endif

typedef struct st_h2o_header_flags_t {
unsigned char token_index_plus1; /* 1-origin, 0 means not token */
typedef struct st_h2o_token_flags_t {
char http2_static_table_name_index; /* non-zero if any */
unsigned char proxy_should_drop_for_req : 1;
unsigned char proxy_should_drop_for_res : 1;
unsigned char is_init_header_special : 1;
unsigned char http2_should_reject : 1;
unsigned char copy_for_push_request : 1;
unsigned char dont_compress : 1;
} h2o_header_flags_t;
unsigned char dont_compress : 1; /* consult `h2o_header_t:dont_compress` as well */
} h2o_token_flags_t;

/**
* a predefined, read-only, fast variant of h2o_iovec_t, defined in h2o/token.h
*/
typedef struct st_h2o_token_t {
h2o_iovec_t buf;
h2o_header_flags_t flags;
h2o_token_flags_t flags;
} h2o_token_t;

#ifndef H2O_MAX_TOKENS
Expand Down
3 changes: 1 addition & 2 deletions lib/common/http1client.c
Original file line number Diff line number Diff line change
Expand Up @@ -267,14 +267,13 @@ static void on_head(h2o_socket_t *sock, const char *err)
token = h2o_lookup_token(src_headers[i].name, src_headers[i].name_len);
if (token != NULL) {
headers[i].name = (h2o_iovec_t *)&token->buf;
headers[i].flags = token->flags;
} else {
header_names[i] = h2o_iovec_init(src_headers[i].name, src_headers[i].name_len);
headers[i].name = &header_names[i];
headers[i].flags = (h2o_header_flags_t){0};
}
headers[i].value = h2o_iovec_init(src_headers[i].value, src_headers[i].value_len);
headers[i].orig_name = orig_name;
headers[i].flags = (h2o_header_flags_t){0};
}

if (!(100 <= http_status && http_status <= 199 && http_status != 101))
Expand Down
126 changes: 63 additions & 63 deletions lib/common/token_table.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,69 +21,69 @@
*/

/* DO NOT EDIT! generated by tokens.pl */
h2o_token_t h2o__tokens[] = {{{H2O_STRLIT(":authority")}, {1, 1, 0, 0, 0, 0, 0, 0}},
{{H2O_STRLIT(":method")}, {2, 2, 0, 0, 0, 0, 0, 0}},
{{H2O_STRLIT(":path")}, {3, 4, 0, 0, 0, 0, 0, 0}},
{{H2O_STRLIT(":scheme")}, {4, 6, 0, 0, 0, 0, 0, 0}},
{{H2O_STRLIT(":status")}, {5, 8, 0, 0, 0, 0, 0, 0}},
{{H2O_STRLIT("accept")}, {6, 19, 0, 0, 0, 0, 1, 0}},
{{H2O_STRLIT("accept-charset")}, {7, 15, 0, 0, 0, 0, 1, 0}},
{{H2O_STRLIT("accept-encoding")}, {8, 16, 0, 0, 0, 0, 1, 0}},
{{H2O_STRLIT("accept-language")}, {9, 17, 0, 0, 0, 0, 1, 0}},
{{H2O_STRLIT("accept-ranges")}, {10, 18, 0, 0, 0, 0, 0, 0}},
{{H2O_STRLIT("access-control-allow-origin")}, {11, 20, 0, 0, 0, 0, 0, 0}},
{{H2O_STRLIT("age")}, {12, 21, 0, 0, 0, 0, 0, 0}},
{{H2O_STRLIT("allow")}, {13, 22, 0, 0, 0, 0, 0, 0}},
{{H2O_STRLIT("authorization")}, {14, 23, 0, 0, 0, 0, 0, 0}},
{{H2O_STRLIT("cache-control")}, {15, 24, 0, 0, 0, 0, 0, 0}},
{{H2O_STRLIT("cache-digest")}, {16, 0, 0, 0, 0, 0, 0, 0}},
{{H2O_STRLIT("connection")}, {17, 0, 1, 1, 0, 1, 0, 0}},
{{H2O_STRLIT("content-disposition")}, {18, 25, 0, 0, 0, 0, 0, 0}},
{{H2O_STRLIT("content-encoding")}, {19, 26, 0, 0, 0, 0, 0, 0}},
{{H2O_STRLIT("content-language")}, {20, 27, 0, 0, 0, 0, 0, 0}},
{{H2O_STRLIT("content-length")}, {21, 28, 0, 0, 1, 0, 0, 0}},
{{H2O_STRLIT("content-location")}, {22, 29, 0, 0, 0, 0, 0, 0}},
{{H2O_STRLIT("content-range")}, {23, 30, 0, 0, 0, 0, 0, 0}},
{{H2O_STRLIT("content-type")}, {24, 31, 0, 0, 0, 0, 0, 0}},
{{H2O_STRLIT("cookie")}, {25, 32, 0, 0, 0, 0, 0, 1}},
{{H2O_STRLIT("date")}, {26, 33, 0, 0, 0, 0, 0, 0}},
{{H2O_STRLIT("early-data")}, {27, 0, 0, 0, 0, 0, 0, 0}},
{{H2O_STRLIT("etag")}, {28, 34, 0, 0, 0, 0, 0, 0}},
{{H2O_STRLIT("expect")}, {29, 35, 0, 0, 1, 0, 0, 0}},
{{H2O_STRLIT("expires")}, {30, 36, 0, 0, 0, 0, 0, 0}},
{{H2O_STRLIT("from")}, {31, 37, 0, 0, 0, 0, 0, 0}},
{{H2O_STRLIT("host")}, {32, 38, 0, 0, 1, 1, 0, 0}},
{{H2O_STRLIT("http2-settings")}, {33, 0, 1, 0, 0, 1, 0, 0}},
{{H2O_STRLIT("if-match")}, {34, 39, 0, 0, 0, 0, 0, 0}},
{{H2O_STRLIT("if-modified-since")}, {35, 40, 0, 0, 0, 0, 0, 0}},
{{H2O_STRLIT("if-none-match")}, {36, 41, 0, 0, 0, 0, 0, 0}},
{{H2O_STRLIT("if-range")}, {37, 42, 0, 0, 0, 0, 0, 0}},
{{H2O_STRLIT("if-unmodified-since")}, {38, 43, 0, 0, 0, 0, 0, 0}},
{{H2O_STRLIT("keep-alive")}, {39, 0, 1, 1, 0, 0, 0, 0}},
{{H2O_STRLIT("last-modified")}, {40, 44, 0, 0, 0, 0, 0, 0}},
{{H2O_STRLIT("link")}, {41, 45, 0, 0, 0, 0, 0, 0}},
{{H2O_STRLIT("location")}, {42, 46, 0, 0, 0, 0, 0, 0}},
{{H2O_STRLIT("max-forwards")}, {43, 47, 0, 0, 0, 0, 0, 0}},
{{H2O_STRLIT("proxy-authenticate")}, {44, 48, 1, 0, 0, 0, 0, 0}},
{{H2O_STRLIT("proxy-authorization")}, {45, 49, 1, 0, 0, 0, 0, 0}},
{{H2O_STRLIT("range")}, {46, 50, 0, 0, 0, 0, 0, 0}},
{{H2O_STRLIT("referer")}, {47, 51, 0, 0, 0, 0, 0, 0}},
{{H2O_STRLIT("refresh")}, {48, 52, 0, 0, 0, 0, 0, 0}},
{{H2O_STRLIT("retry-after")}, {49, 53, 0, 0, 0, 0, 0, 0}},
{{H2O_STRLIT("server")}, {50, 54, 0, 0, 0, 0, 0, 0}},
{{H2O_STRLIT("set-cookie")}, {51, 55, 0, 0, 0, 0, 0, 1}},
{{H2O_STRLIT("strict-transport-security")}, {52, 56, 0, 0, 0, 0, 0, 0}},
{{H2O_STRLIT("te")}, {53, 0, 1, 0, 0, 1, 0, 0}},
{{H2O_STRLIT("transfer-encoding")}, {54, 57, 1, 1, 1, 1, 0, 0}},
{{H2O_STRLIT("upgrade")}, {55, 0, 1, 1, 1, 1, 0, 0}},
{{H2O_STRLIT("user-agent")}, {56, 58, 0, 0, 0, 0, 1, 0}},
{{H2O_STRLIT("vary")}, {57, 59, 0, 0, 0, 0, 0, 0}},
{{H2O_STRLIT("via")}, {58, 60, 0, 0, 0, 0, 0, 0}},
{{H2O_STRLIT("www-authenticate")}, {59, 61, 0, 0, 0, 0, 0, 0}},
{{H2O_STRLIT("x-compress-hint")}, {60, 0, 0, 0, 0, 0, 0, 0}},
{{H2O_STRLIT("x-forwarded-for")}, {61, 0, 0, 0, 0, 0, 0, 0}},
{{H2O_STRLIT("x-reproxy-url")}, {62, 0, 0, 0, 0, 0, 0, 0}},
{{H2O_STRLIT("x-traffic")}, {63, 0, 0, 0, 0, 0, 0, 0}}};
h2o_token_t h2o__tokens[] = {{{H2O_STRLIT(":authority")}, {1, 0, 0, 0, 0, 0, 0}},
{{H2O_STRLIT(":method")}, {2, 0, 0, 0, 0, 0, 0}},
{{H2O_STRLIT(":path")}, {4, 0, 0, 0, 0, 0, 0}},
{{H2O_STRLIT(":scheme")}, {6, 0, 0, 0, 0, 0, 0}},
{{H2O_STRLIT(":status")}, {8, 0, 0, 0, 0, 0, 0}},
{{H2O_STRLIT("accept")}, {19, 0, 0, 0, 0, 1, 0}},
{{H2O_STRLIT("accept-charset")}, {15, 0, 0, 0, 0, 1, 0}},
{{H2O_STRLIT("accept-encoding")}, {16, 0, 0, 0, 0, 1, 0}},
{{H2O_STRLIT("accept-language")}, {17, 0, 0, 0, 0, 1, 0}},
{{H2O_STRLIT("accept-ranges")}, {18, 0, 0, 0, 0, 0, 0}},
{{H2O_STRLIT("access-control-allow-origin")}, {20, 0, 0, 0, 0, 0, 0}},
{{H2O_STRLIT("age")}, {21, 0, 0, 0, 0, 0, 0}},
{{H2O_STRLIT("allow")}, {22, 0, 0, 0, 0, 0, 0}},
{{H2O_STRLIT("authorization")}, {23, 0, 0, 0, 0, 0, 0}},
{{H2O_STRLIT("cache-control")}, {24, 0, 0, 0, 0, 0, 0}},
{{H2O_STRLIT("cache-digest")}, {0, 0, 0, 0, 0, 0, 0}},
{{H2O_STRLIT("connection")}, {0, 1, 1, 0, 1, 0, 0}},
{{H2O_STRLIT("content-disposition")}, {25, 0, 0, 0, 0, 0, 0}},
{{H2O_STRLIT("content-encoding")}, {26, 0, 0, 0, 0, 0, 0}},
{{H2O_STRLIT("content-language")}, {27, 0, 0, 0, 0, 0, 0}},
{{H2O_STRLIT("content-length")}, {28, 0, 0, 1, 0, 0, 0}},
{{H2O_STRLIT("content-location")}, {29, 0, 0, 0, 0, 0, 0}},
{{H2O_STRLIT("content-range")}, {30, 0, 0, 0, 0, 0, 0}},
{{H2O_STRLIT("content-type")}, {31, 0, 0, 0, 0, 0, 0}},
{{H2O_STRLIT("cookie")}, {32, 0, 0, 0, 0, 0, 1}},
{{H2O_STRLIT("date")}, {33, 0, 0, 0, 0, 0, 0}},
{{H2O_STRLIT("early-data")}, {0, 0, 0, 0, 0, 0, 0}},
{{H2O_STRLIT("etag")}, {34, 0, 0, 0, 0, 0, 0}},
{{H2O_STRLIT("expect")}, {35, 0, 0, 1, 0, 0, 0}},
{{H2O_STRLIT("expires")}, {36, 0, 0, 0, 0, 0, 0}},
{{H2O_STRLIT("from")}, {37, 0, 0, 0, 0, 0, 0}},
{{H2O_STRLIT("host")}, {38, 0, 0, 1, 1, 0, 0}},
{{H2O_STRLIT("http2-settings")}, {0, 1, 0, 0, 1, 0, 0}},
{{H2O_STRLIT("if-match")}, {39, 0, 0, 0, 0, 0, 0}},
{{H2O_STRLIT("if-modified-since")}, {40, 0, 0, 0, 0, 0, 0}},
{{H2O_STRLIT("if-none-match")}, {41, 0, 0, 0, 0, 0, 0}},
{{H2O_STRLIT("if-range")}, {42, 0, 0, 0, 0, 0, 0}},
{{H2O_STRLIT("if-unmodified-since")}, {43, 0, 0, 0, 0, 0, 0}},
{{H2O_STRLIT("keep-alive")}, {0, 1, 1, 0, 0, 0, 0}},
{{H2O_STRLIT("last-modified")}, {44, 0, 0, 0, 0, 0, 0}},
{{H2O_STRLIT("link")}, {45, 0, 0, 0, 0, 0, 0}},
{{H2O_STRLIT("location")}, {46, 0, 0, 0, 0, 0, 0}},
{{H2O_STRLIT("max-forwards")}, {47, 0, 0, 0, 0, 0, 0}},
{{H2O_STRLIT("proxy-authenticate")}, {48, 1, 0, 0, 0, 0, 0}},
{{H2O_STRLIT("proxy-authorization")}, {49, 1, 0, 0, 0, 0, 0}},
{{H2O_STRLIT("range")}, {50, 0, 0, 0, 0, 0, 0}},
{{H2O_STRLIT("referer")}, {51, 0, 0, 0, 0, 0, 0}},
{{H2O_STRLIT("refresh")}, {52, 0, 0, 0, 0, 0, 0}},
{{H2O_STRLIT("retry-after")}, {53, 0, 0, 0, 0, 0, 0}},
{{H2O_STRLIT("server")}, {54, 0, 0, 0, 0, 0, 0}},
{{H2O_STRLIT("set-cookie")}, {55, 0, 0, 0, 0, 0, 1}},
{{H2O_STRLIT("strict-transport-security")}, {56, 0, 0, 0, 0, 0, 0}},
{{H2O_STRLIT("te")}, {0, 1, 0, 0, 1, 0, 0}},
{{H2O_STRLIT("transfer-encoding")}, {57, 1, 1, 1, 1, 0, 0}},
{{H2O_STRLIT("upgrade")}, {0, 1, 1, 1, 1, 0, 0}},
{{H2O_STRLIT("user-agent")}, {58, 0, 0, 0, 0, 1, 0}},
{{H2O_STRLIT("vary")}, {59, 0, 0, 0, 0, 0, 0}},
{{H2O_STRLIT("via")}, {60, 0, 0, 0, 0, 0, 0}},
{{H2O_STRLIT("www-authenticate")}, {61, 0, 0, 0, 0, 0, 0}},
{{H2O_STRLIT("x-compress-hint")}, {0, 0, 0, 0, 0, 0, 0}},
{{H2O_STRLIT("x-forwarded-for")}, {0, 0, 0, 0, 0, 0, 0}},
{{H2O_STRLIT("x-reproxy-url")}, {0, 0, 0, 0, 0, 0, 0}},
{{H2O_STRLIT("x-traffic")}, {0, 0, 0, 0, 0, 0, 0}}};
size_t h2o__num_tokens = 63;

const h2o_token_t *h2o_lookup_token(const char *name, size_t len)
Expand Down
4 changes: 2 additions & 2 deletions lib/core/headers.c
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ ssize_t h2o_find_header_by_str(const h2o_headers_t *headers, const char *name, s
ssize_t h2o_add_header(h2o_mem_pool_t *pool, h2o_headers_t *headers, const h2o_token_t *token, const char *orig_name,
const char *value, size_t value_len)
{
return add_header(pool, headers, (h2o_iovec_t *)&token->buf, orig_name, value, value_len, token->flags);
return add_header(pool, headers, (h2o_iovec_t *)&token->buf, orig_name, value, value_len, (h2o_header_flags_t){0});
}

ssize_t h2o_add_header_by_str(h2o_mem_pool_t *pool, h2o_headers_t *headers, const char *name, size_t name_len, int maybe_token,
Expand All @@ -74,7 +74,7 @@ ssize_t h2o_add_header_by_str(h2o_mem_pool_t *pool, h2o_headers_t *headers, cons
if (maybe_token) {
const h2o_token_t *token = h2o_lookup_token(name, name_len);
if (token != NULL) {
return add_header(pool, headers, (h2o_iovec_t *)token, orig_name, value, value_len, token->flags);
return add_header(pool, headers, (h2o_iovec_t *)token, orig_name, value, value_len, (h2o_header_flags_t){0});
}
}
name_buf = h2o_mem_alloc_pool(pool, *name_buf, 1);
Expand Down
12 changes: 6 additions & 6 deletions lib/core/proxy.c
Original file line number Diff line number Diff line change
Expand Up @@ -212,10 +212,10 @@ static void build_request(h2o_req_t *req, h2o_iovec_t *method, h2o_url_t *url, h
const h2o_header_t *h, *h_end;
int found_early_data = 0;
for (h = req_headers.entries, h_end = h + req_headers.size; h != h_end; ++h) {
if (h->flags.proxy_should_drop_for_req)
continue;
if (h2o_header_is_token(h)) {
if (h2o_iovec_is_token(h->name)) {
const h2o_token_t *token = (void *)h->name;
if (token->flags.proxy_should_drop_for_req)
continue;
if (token == H2O_TOKEN_COOKIE) {
/* merge the cookie headers; see HTTP/2 8.1.2.5 and HTTP/1 (RFC6265 5.4) */
/* FIXME current algorithm is O(n^2) against the number of cookie headers */
Expand Down Expand Up @@ -429,10 +429,10 @@ static h2o_httpclient_body_cb on_head(h2o_httpclient_t *client, const char *errs
req->res.reason = h2o_strdup(&req->pool, msg.base, msg.len).base;
for (i = 0; i != num_headers; ++i) {
h2o_iovec_t value = headers[i].value;
if (headers[i].flags.proxy_should_drop_for_res)
continue;
if (h2o_header_is_token(&headers[i])) {
if (h2o_iovec_is_token(headers[i].name)) {
const h2o_token_t *token = H2O_STRUCT_FROM_MEMBER(h2o_token_t, buf, headers[i].name);
if (token->flags.proxy_should_drop_for_res)
continue;
if (token == H2O_TOKEN_CONTENT_LENGTH) {
if (req->res.content_length != SIZE_MAX ||
(req->res.content_length = h2o_strtosize(headers[i].value.base, headers[i].value.len)) == SIZE_MAX) {
Expand Down
2 changes: 1 addition & 1 deletion lib/core/request.c
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,7 @@ void h2o_init_request(h2o_req_t *req, h2o_conn_t *conn, h2o_req_t *src)
req->headers.size = src->headers.size;
for (i = 0; i != src->headers.size; ++i) {
h2o_header_t *dst_header = req->headers.entries + i, *src_header = src->headers.entries + i;
if (h2o_header_is_token(src_header)) {
if (h2o_iovec_is_token(src_header->name)) {
dst_header->name = src_header->name;
} else {
dst_header->name = h2o_mem_alloc_pool(&req->pool, *dst_header->name, 1);
Expand Down
2 changes: 1 addition & 1 deletion lib/handler/mruby.c
Original file line number Diff line number Diff line change
Expand Up @@ -689,7 +689,7 @@ static int iterate_headers_callback(h2o_mruby_shared_context_t *shared_ctx, h2o_
{
mrb_value env = mrb_obj_value(cb_data);
mrb_value n;
if (h2o_header_is_token(header)) {
if (h2o_iovec_is_token(header->name)) {
const h2o_token_t *token = H2O_STRUCT_FROM_MEMBER(h2o_token_t, buf, header->name);
n = h2o_mruby_token_env_key(shared_ctx, token);
} else {
Expand Down
2 changes: 1 addition & 1 deletion lib/handler/mruby/middleware.c
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ static int iterate_headers_callback(h2o_mruby_shared_context_t *shared_ctx, h2o_
{
mrb_value result_hash = mrb_obj_value(cb_data);
mrb_value n;
if (h2o_header_is_token(header)) {
if (h2o_iovec_is_token(header->name)) {
const h2o_token_t *token = H2O_STRUCT_FROM_MEMBER(h2o_token_t, buf, header->name);
n = h2o_mruby_token_string(shared_ctx, token);
} else {
Expand Down
2 changes: 1 addition & 1 deletion lib/http1.c
Original file line number Diff line number Diff line change
Expand Up @@ -360,7 +360,7 @@ static ssize_t fixup_request(struct st_h2o_http1_conn_t *conn, struct phr_header
conn->req.input.path = h2o_strdup(&conn->req.pool, conn->req.input.path.base, conn->req.input.path.len);
for (i = 0; i != conn->req.headers.size; ++i) {
h2o_header_t *header = conn->req.headers.entries + i;
if (!h2o_header_is_token(header)) {
if (!h2o_iovec_is_token(header->name)) {
*header->name = h2o_strdup(&conn->req.pool, header->name->base, header->name->len);
}
header->value = h2o_strdup(&conn->req.pool, header->value.base, header->value.len);
Expand Down
11 changes: 6 additions & 5 deletions lib/http2/connection.c
Original file line number Diff line number Diff line change
Expand Up @@ -1495,12 +1495,13 @@ static void push_path(h2o_req_t *src_req, const char *abspath, size_t abspath_le
size_t i;
for (i = 0; i != src_stream->req.headers.size; ++i) {
h2o_header_t *src_header = src_stream->req.headers.entries + i;
if (src_header->flags.copy_for_push_request) {
assert(h2o_header_is_token(src_header)); /* currently only predefined headers are copiable*/
/* currently only predefined headers are copiable */
if (h2o_iovec_is_token(src_header->name)) {
h2o_token_t *token = H2O_STRUCT_FROM_MEMBER(h2o_token_t, buf, src_header->name);
h2o_add_header(&stream->req.pool, &stream->req.headers, token, NULL,
h2o_strdup(&stream->req.pool, src_header->value.base, src_header->value.len).base,
src_header->value.len);
if (token->flags.copy_for_push_request)
h2o_add_header(&stream->req.pool, &stream->req.headers, token, NULL,
h2o_strdup(&stream->req.pool, src_header->value.base, src_header->value.len).base,
src_header->value.len);
}
}
}
Expand Down
Loading