diff options
Diffstat (limited to 'src/core/transport/chttp2')
28 files changed, 4770 insertions, 0 deletions
diff --git a/src/core/transport/chttp2/frame.h b/src/core/transport/chttp2/frame.h new file mode 100644 index 0000000000..7c0bbe026b --- /dev/null +++ b/src/core/transport/chttp2/frame.h @@ -0,0 +1,74 @@ +/* + * + * Copyright 2014, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef __GRPC_INTERNAL_TRANSPORT_CHTTP2_FRAME_H__ +#define __GRPC_INTERNAL_TRANSPORT_CHTTP2_FRAME_H__ + +#include <grpc/support/port_platform.h> + +/* Common definitions for frame handling in the chttp2 transport */ + +typedef enum { + GRPC_CHTTP2_PARSE_OK, + GRPC_CHTTP2_STREAM_ERROR, + GRPC_CHTTP2_CONNECTION_ERROR +} grpc_chttp2_parse_error; + +typedef struct { + gpr_uint8 end_of_stream; + gpr_uint8 need_flush_reads; + gpr_uint8 metadata_boundary; + gpr_uint8 ack_settings; + gpr_uint8 send_ping_ack; + gpr_uint8 process_ping_reply; + + gpr_uint32 window_update; +} grpc_chttp2_parse_state; + +#define GRPC_CHTTP2_FRAME_DATA 0 +#define GRPC_CHTTP2_FRAME_HEADER 1 +#define GRPC_CHTTP2_FRAME_CONTINUATION 9 +#define GRPC_CHTTP2_FRAME_RST_STREAM 3 +#define GRPC_CHTTP2_FRAME_SETTINGS 4 +#define GRPC_CHTTP2_FRAME_PING 6 +#define GRPC_CHTTP2_FRAME_WINDOW_UPDATE 8 + +#define GRPC_CHTTP2_MAX_PAYLOAD_LENGTH ((1 << 14) - 1) + +#define GRPC_CHTTP2_DATA_FLAG_END_STREAM 1 +#define GRPC_CHTTP2_FLAG_ACK 1 +#define GRPC_CHTTP2_DATA_FLAG_END_HEADERS 4 +#define GRPC_CHTTP2_DATA_FLAG_PADDED 8 +#define GRPC_CHTTP2_FLAG_HAS_PRIORITY 0x20 + +#endif /* __GRPC_INTERNAL_TRANSPORT_CHTTP2_FRAME_H__ */ diff --git a/src/core/transport/chttp2/frame_data.c b/src/core/transport/chttp2/frame_data.c new file mode 100644 index 0000000000..fbd3b6cabf --- /dev/null +++ b/src/core/transport/chttp2/frame_data.c @@ -0,0 +1,164 @@ +/* + * + * Copyright 2014, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "src/core/transport/chttp2/frame_data.h" + +#include <string.h> + +#include <grpc/support/alloc.h> +#include <grpc/support/log.h> +#include <grpc/support/string.h> +#include <grpc/support/useful.h> +#include "src/core/transport/transport.h" + +grpc_chttp2_parse_error grpc_chttp2_data_parser_init( + grpc_chttp2_data_parser *parser) { + parser->state = GRPC_CHTTP2_DATA_FH_0; + grpc_sopb_init(&parser->incoming_sopb); + return GRPC_CHTTP2_PARSE_OK; +} + +void grpc_chttp2_data_parser_destroy(grpc_chttp2_data_parser *parser) { + grpc_sopb_destroy(&parser->incoming_sopb); +} + +grpc_chttp2_parse_error grpc_chttp2_data_parser_begin_frame( + grpc_chttp2_data_parser *parser, gpr_uint8 flags) { + if (flags & ~GRPC_CHTTP2_DATA_FLAG_END_STREAM) { + gpr_log(GPR_ERROR, "unsupported data flags: 0x%02x", flags); + return GRPC_CHTTP2_STREAM_ERROR; + } + + if (flags & GRPC_CHTTP2_DATA_FLAG_END_STREAM) { + parser->is_last_frame = 1; + } else { + parser->is_last_frame = 0; + } + + return GRPC_CHTTP2_PARSE_OK; +} + +grpc_chttp2_parse_error grpc_chttp2_data_parser_parse( + void *parser, grpc_chttp2_parse_state *state, gpr_slice slice, + int is_last) { + gpr_uint8 *const beg = GPR_SLICE_START_PTR(slice); + gpr_uint8 *const end = GPR_SLICE_END_PTR(slice); + gpr_uint8 *cur = beg; + grpc_chttp2_data_parser *p = parser; + + if (is_last && p->is_last_frame) { + state->end_of_stream = 1; + state->need_flush_reads = 1; + } + + if (cur == end) { + return GRPC_CHTTP2_PARSE_OK; + } + + switch (p->state) { + fh_0: + case GRPC_CHTTP2_DATA_FH_0: + p->frame_type = *cur; + if (++cur == end) { + p->state = GRPC_CHTTP2_DATA_FH_1; + return GRPC_CHTTP2_PARSE_OK; + } + switch (p->frame_type) { + case 0: + break; + case 1: + gpr_log(GPR_ERROR, "Compressed GRPC frames not yet supported"); + return GRPC_CHTTP2_STREAM_ERROR; + default: + gpr_log(GPR_ERROR, "Bad GRPC frame type 0x%02x", p->frame_type); + return GRPC_CHTTP2_STREAM_ERROR; + } + /* fallthrough */ + case GRPC_CHTTP2_DATA_FH_1: + p->frame_size = ((gpr_uint32)*cur) << 24; + if (++cur == end) { + p->state = GRPC_CHTTP2_DATA_FH_2; + return GRPC_CHTTP2_PARSE_OK; + } + /* fallthrough */ + case GRPC_CHTTP2_DATA_FH_2: + p->frame_size |= ((gpr_uint32)*cur) << 16; + if (++cur == end) { + p->state = GRPC_CHTTP2_DATA_FH_3; + return GRPC_CHTTP2_PARSE_OK; + } + /* fallthrough */ + case GRPC_CHTTP2_DATA_FH_3: + p->frame_size |= ((gpr_uint32)*cur) << 8; + if (++cur == end) { + p->state = GRPC_CHTTP2_DATA_FH_4; + return GRPC_CHTTP2_PARSE_OK; + } + /* fallthrough */ + case GRPC_CHTTP2_DATA_FH_4: + p->frame_size |= ((gpr_uint32)*cur); + p->state = GRPC_CHTTP2_DATA_FRAME; + ++cur; + state->need_flush_reads = 1; + grpc_sopb_add_begin_message(&p->incoming_sopb, p->frame_size, 0); + /* fallthrough */ + case GRPC_CHTTP2_DATA_FRAME: + if (cur == end) { + return GRPC_CHTTP2_PARSE_OK; + } else if (end - cur == p->frame_size) { + state->need_flush_reads = 1; + grpc_sopb_add_slice(&p->incoming_sopb, + gpr_slice_sub(slice, cur - beg, end - beg)); + p->state = GRPC_CHTTP2_DATA_FH_0; + return GRPC_CHTTP2_PARSE_OK; + } else if (end - cur > p->frame_size) { + state->need_flush_reads = 1; + grpc_sopb_add_slice( + &p->incoming_sopb, + gpr_slice_sub(slice, cur - beg, cur + p->frame_size - beg)); + cur += p->frame_size; + goto fh_0; /* loop */ + } else { + state->need_flush_reads = 1; + grpc_sopb_add_slice(&p->incoming_sopb, + gpr_slice_sub(slice, cur - beg, end - beg)); + p->frame_size -= (end - cur); + return GRPC_CHTTP2_PARSE_OK; + } + } + + gpr_log(GPR_ERROR, "should never reach here"); + abort(); + return GRPC_CHTTP2_CONNECTION_ERROR; +} + diff --git a/src/core/transport/chttp2/frame_data.h b/src/core/transport/chttp2/frame_data.h new file mode 100644 index 0000000000..abe26dab76 --- /dev/null +++ b/src/core/transport/chttp2/frame_data.h @@ -0,0 +1,80 @@ +/* + * + * Copyright 2014, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef __GRPC_INTERNAL_TRANSPORT_CHTTP2_FRAME_DATA_H__ +#define __GRPC_INTERNAL_TRANSPORT_CHTTP2_FRAME_DATA_H__ + +/* Parser for GRPC streams embedded in DATA frames */ + +#include <grpc/support/slice.h> +#include <grpc/support/slice_buffer.h> +#include "src/core/transport/stream_op.h" +#include "src/core/transport/chttp2/frame.h" + +typedef enum { + GRPC_CHTTP2_DATA_FH_0, + GRPC_CHTTP2_DATA_FH_1, + GRPC_CHTTP2_DATA_FH_2, + GRPC_CHTTP2_DATA_FH_3, + GRPC_CHTTP2_DATA_FH_4, + GRPC_CHTTP2_DATA_FRAME +} grpc_chttp2_stream_state; + +typedef struct { + grpc_chttp2_stream_state state; + gpr_uint8 is_last_frame; + gpr_uint8 frame_type; + gpr_uint32 frame_size; + + grpc_stream_op_buffer incoming_sopb; +} grpc_chttp2_data_parser; + +/* initialize per-stream state for data frame parsing */ +grpc_chttp2_parse_error grpc_chttp2_data_parser_init( + grpc_chttp2_data_parser *parser); + +void grpc_chttp2_data_parser_destroy(grpc_chttp2_data_parser *parser); + +/* start processing a new data frame */ +grpc_chttp2_parse_error grpc_chttp2_data_parser_begin_frame( + grpc_chttp2_data_parser *parser, gpr_uint8 flags); + +/* handle a slice of a data frame - is_last indicates the last slice of a + frame */ +grpc_chttp2_parse_error grpc_chttp2_data_parser_parse( + void *parser, grpc_chttp2_parse_state *state, gpr_slice slice, int is_last); + +/* create a slice with an empty data frame and is_last set */ +gpr_slice grpc_chttp2_data_frame_create_empty_close(gpr_uint32 id); + +#endif /* __GRPC_INTERNAL_TRANSPORT_CHTTP2_FRAME_DATA_H__ */ diff --git a/src/core/transport/chttp2/frame_ping.c b/src/core/transport/chttp2/frame_ping.c new file mode 100644 index 0000000000..9556c0cae8 --- /dev/null +++ b/src/core/transport/chttp2/frame_ping.c @@ -0,0 +1,93 @@ +/* + * + * Copyright 2014, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "src/core/transport/chttp2/frame_ping.h" + +#include <string.h> + +#include <grpc/support/log.h> + +gpr_slice grpc_chttp2_ping_create(gpr_uint8 ack, gpr_uint8 *opaque_8bytes) { + gpr_slice slice = gpr_slice_malloc(9 + 8); + gpr_uint8 *p = GPR_SLICE_START_PTR(slice); + + *p++ = 0; + *p++ = 0; + *p++ = 8; + *p++ = GRPC_CHTTP2_FRAME_PING; + *p++ = ack ? 1 : 0; + *p++ = 0; + *p++ = 0; + *p++ = 0; + *p++ = 0; + memcpy(p, opaque_8bytes, 8); + + return slice; +} + +grpc_chttp2_parse_error grpc_chttp2_ping_parser_begin_frame( + grpc_chttp2_ping_parser *parser, gpr_uint32 length, gpr_uint8 flags) { + if (flags & 0xfe || length != 8) { + gpr_log(GPR_ERROR, "invalid ping: length=%d, flags=%02x", length, flags); + return GRPC_CHTTP2_CONNECTION_ERROR; + } + parser->byte = 0; + parser->is_ack = flags; + return GRPC_CHTTP2_PARSE_OK; +} + +grpc_chttp2_parse_error grpc_chttp2_ping_parser_parse( + void *parser, grpc_chttp2_parse_state *state, gpr_slice slice, + int is_last) { + gpr_uint8 *const beg = GPR_SLICE_START_PTR(slice); + gpr_uint8 *const end = GPR_SLICE_END_PTR(slice); + gpr_uint8 *cur = beg; + grpc_chttp2_ping_parser *p = parser; + + while (p->byte != 8 && cur != end) { + p->opaque_8bytes[p->byte] = *cur; + cur++; + p->byte++; + } + + if (p->byte == 8) { + GPR_ASSERT(is_last); + if (p->is_ack) { + state->process_ping_reply = 1; + } else { + state->send_ping_ack = 1; + } + } + + return GRPC_CHTTP2_PARSE_OK; +} diff --git a/src/core/transport/chttp2/frame_ping.h b/src/core/transport/chttp2/frame_ping.h new file mode 100644 index 0000000000..a64d53644b --- /dev/null +++ b/src/core/transport/chttp2/frame_ping.h @@ -0,0 +1,53 @@ +/* + * + * Copyright 2014, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef __GRPC_INTERNAL_TRANSPORT_CHTTP2_FRAME_PING_H__ +#define __GRPC_INTERNAL_TRANSPORT_CHTTP2_FRAME_PING_H__ + +#include <grpc/support/slice.h> +#include "src/core/transport/chttp2/frame.h" + +typedef struct { + gpr_uint8 byte; + gpr_uint8 is_ack; + gpr_uint8 opaque_8bytes[8]; +} grpc_chttp2_ping_parser; + +gpr_slice grpc_chttp2_ping_create(gpr_uint8 ack, gpr_uint8 *opaque_8bytes); + +grpc_chttp2_parse_error grpc_chttp2_ping_parser_begin_frame( + grpc_chttp2_ping_parser *parser, gpr_uint32 length, gpr_uint8 flags); +grpc_chttp2_parse_error grpc_chttp2_ping_parser_parse( + void *parser, grpc_chttp2_parse_state *state, gpr_slice slice, int is_last); + +#endif /* __GRPC_INTERNAL_TRANSPORT_CHTTP2_FRAME_PING_H__ */ diff --git a/src/core/transport/chttp2/frame_rst_stream.c b/src/core/transport/chttp2/frame_rst_stream.c new file mode 100644 index 0000000000..825e156e46 --- /dev/null +++ b/src/core/transport/chttp2/frame_rst_stream.c @@ -0,0 +1,56 @@ +/* + * + * Copyright 2014, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "src/core/transport/chttp2/frame_rst_stream.h" +#include "src/core/transport/chttp2/frame.h" + +gpr_slice grpc_chttp2_rst_stream_create(gpr_uint32 id, gpr_uint32 code) { + gpr_slice slice = gpr_slice_malloc(13); + gpr_uint8 *p = GPR_SLICE_START_PTR(slice); + + *p++ = 0; + *p++ = 0; + *p++ = 4; + *p++ = GRPC_CHTTP2_FRAME_RST_STREAM; + *p++ = 0; + *p++ = id >> 24; + *p++ = id >> 16; + *p++ = id >> 8; + *p++ = id; + *p++ = code >> 24; + *p++ = code >> 16; + *p++ = code >> 8; + *p++ = code; + + return slice; +} diff --git a/src/core/transport/chttp2/frame_rst_stream.h b/src/core/transport/chttp2/frame_rst_stream.h new file mode 100644 index 0000000000..78aea0f26a --- /dev/null +++ b/src/core/transport/chttp2/frame_rst_stream.h @@ -0,0 +1,41 @@ +/* + * + * Copyright 2014, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef __GRPC_INTERNAL_TRANSPORT_CHTTP2_FRAME_RST_STREAM_H__ +#define __GRPC_INTERNAL_TRANSPORT_CHTTP2_FRAME_RST_STREAM_H__ + +#include <grpc/support/slice.h> + +gpr_slice grpc_chttp2_rst_stream_create(gpr_uint32 stream_id, gpr_uint32 code); + +#endif /* __GRPC_INTERNAL_TRANSPORT_CHTTP2_FRAME_RST_STREAM_H__ */ diff --git a/src/core/transport/chttp2/frame_settings.c b/src/core/transport/chttp2/frame_settings.c new file mode 100644 index 0000000000..488b96a728 --- /dev/null +++ b/src/core/transport/chttp2/frame_settings.c @@ -0,0 +1,227 @@ +/* + * + * Copyright 2014, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "src/core/transport/chttp2/frame_settings.h" + +#include <string.h> + +#include "src/core/transport/chttp2/frame.h" +#include <grpc/support/log.h> +#include <grpc/support/useful.h> + +/* HTTP/2 mandated initial connection settings */ +const grpc_chttp2_setting_parameters + grpc_chttp2_settings_parameters[GRPC_CHTTP2_NUM_SETTINGS] = { + {NULL, 0, 0, 0, GRPC_CHTTP2_DISCONNECT_ON_INVALID_VALUE}, + {"HEADER_TABLE_SIZE", 4096, 0, 0xffffffff, + GRPC_CHTTP2_CLAMP_INVALID_VALUE}, + {"ENABLE_PUSH", 1, 0, 1, GRPC_CHTTP2_DISCONNECT_ON_INVALID_VALUE}, + {"MAX_CONCURRENT_STREAMS", 0xffffffffu, 0, 0xffffffffu, + GRPC_CHTTP2_DISCONNECT_ON_INVALID_VALUE}, + {"INITIAL_WINDOW_SIZE", 65535, 0, 0xffffffffu, + GRPC_CHTTP2_DISCONNECT_ON_INVALID_VALUE}, + {"MAX_FRAME_SIZE", 16384, 16384, 16777215, + GRPC_CHTTP2_DISCONNECT_ON_INVALID_VALUE}, + {"MAX_HEADER_LIST_SIZE", 0xffffffffu, 0, 0xffffffffu, + GRPC_CHTTP2_CLAMP_INVALID_VALUE}, +}; + +static gpr_uint8 *fill_header(gpr_uint8 *out, gpr_uint32 length, + gpr_uint8 flags) { + *out++ = length >> 16; + *out++ = length >> 8; + *out++ = length; + *out++ = GRPC_CHTTP2_FRAME_SETTINGS; + *out++ = flags; + *out++ = 0; + *out++ = 0; + *out++ = 0; + *out++ = 0; + return out; +} + +gpr_slice grpc_chttp2_settings_create(gpr_uint32 *old, const gpr_uint32 *new, + size_t count) { + size_t i; + size_t n = 0; + gpr_slice output; + gpr_uint8 *p; + + for (i = 0; i < count; i++) { + n += (new[i] != old[i]); + } + + output = gpr_slice_malloc(9 + 6 * n); + p = fill_header(GPR_SLICE_START_PTR(output), 6 * n, 0); + + for (i = 0; i < count; i++) { + if (new[i] != old[i]) { + GPR_ASSERT(i); + *p++ = i >> 8; + *p++ = i; + *p++ = new[i] >> 24; + *p++ = new[i] >> 16; + *p++ = new[i] >> 8; + *p++ = new[i]; + old[i] = new[i]; + } + } + + GPR_ASSERT(p == GPR_SLICE_END_PTR(output)); + + return output; +} + +gpr_slice grpc_chttp2_settings_ack_create() { + gpr_slice output = gpr_slice_malloc(9); + fill_header(GPR_SLICE_START_PTR(output), 0, GRPC_CHTTP2_FLAG_ACK); + return output; +} + +grpc_chttp2_parse_error grpc_chttp2_settings_parser_begin_frame( + grpc_chttp2_settings_parser *parser, gpr_uint32 length, gpr_uint8 flags, + gpr_uint32 *settings) { + parser->target_settings = settings; + memcpy(parser->incoming_settings, settings, + GRPC_CHTTP2_NUM_SETTINGS * sizeof(gpr_uint32)); + parser->is_ack = 0; + parser->state = GRPC_CHTTP2_SPS_ID0; + if (flags == GRPC_CHTTP2_FLAG_ACK) { + parser->is_ack = 1; + if (length != 0) { + gpr_log(GPR_ERROR, "non-empty settings ack frame received"); + return GRPC_CHTTP2_CONNECTION_ERROR; + } + return GRPC_CHTTP2_PARSE_OK; + } else if (flags != 0) { + gpr_log(GPR_ERROR, "invalid flags on settings frame"); + return GRPC_CHTTP2_CONNECTION_ERROR; + } else if (length % 6 != 0) { + gpr_log(GPR_ERROR, "settings frames must be a multiple of six bytes"); + return GRPC_CHTTP2_CONNECTION_ERROR; + } else { + return GRPC_CHTTP2_PARSE_OK; + } +} + +grpc_chttp2_parse_error grpc_chttp2_settings_parser_parse( + void *p, grpc_chttp2_parse_state *state, gpr_slice slice, int is_last) { + grpc_chttp2_settings_parser *parser = p; + const gpr_uint8 *cur = GPR_SLICE_START_PTR(slice); + const gpr_uint8 *end = GPR_SLICE_END_PTR(slice); + + if (parser->is_ack) { + return GRPC_CHTTP2_PARSE_OK; + } + + for (;;) { + switch (parser->state) { + case GRPC_CHTTP2_SPS_ID0: + if (cur == end) { + parser->state = GRPC_CHTTP2_SPS_ID0; + if (is_last) { + memcpy(parser->target_settings, parser->incoming_settings, + GRPC_CHTTP2_NUM_SETTINGS * sizeof(gpr_uint32)); + state->ack_settings = 1; + } + return GRPC_CHTTP2_PARSE_OK; + } + parser->id = ((gpr_uint16)*cur) << 8; + cur++; + /* fallthrough */ + case GRPC_CHTTP2_SPS_ID1: + if (cur == end) { + parser->state = GRPC_CHTTP2_SPS_ID1; + return GRPC_CHTTP2_PARSE_OK; + } + parser->id |= (*cur); + cur++; + /* fallthrough */ + case GRPC_CHTTP2_SPS_VAL0: + if (cur == end) { + parser->state = GRPC_CHTTP2_SPS_VAL0; + return GRPC_CHTTP2_PARSE_OK; + } + parser->value = ((gpr_uint32)*cur) << 24; + cur++; + /* fallthrough */ + case GRPC_CHTTP2_SPS_VAL1: + if (cur == end) { + parser->state = GRPC_CHTTP2_SPS_VAL1; + return GRPC_CHTTP2_PARSE_OK; + } + parser->value |= ((gpr_uint32)*cur) << 16; + cur++; + /* fallthrough */ + case GRPC_CHTTP2_SPS_VAL2: + if (cur == end) { + parser->state = GRPC_CHTTP2_SPS_VAL2; + return GRPC_CHTTP2_PARSE_OK; + } + parser->value |= ((gpr_uint32)*cur) << 8; + cur++; + /* fallthrough */ + case GRPC_CHTTP2_SPS_VAL3: + if (cur == end) { + parser->state = GRPC_CHTTP2_SPS_VAL3; + return GRPC_CHTTP2_PARSE_OK; + } else { + parser->state = GRPC_CHTTP2_SPS_ID0; + } + parser->value |= *cur; + cur++; + + if (parser->id > 0 && parser->id < GRPC_CHTTP2_NUM_SETTINGS) { + const grpc_chttp2_setting_parameters *sp = + &grpc_chttp2_settings_parameters[parser->id]; + if (parser->value < sp->min_value || parser->value > sp->max_value) { + switch (sp->invalid_value_behavior) { + case GRPC_CHTTP2_CLAMP_INVALID_VALUE: + parser->value = + GPR_CLAMP(parser->value, sp->min_value, sp->max_value); + break; + case GRPC_CHTTP2_DISCONNECT_ON_INVALID_VALUE: + gpr_log(GPR_ERROR, "invalid value %u passed for %s", + parser->value, sp->name); + return GRPC_CHTTP2_CONNECTION_ERROR; + } + } + parser->incoming_settings[parser->id] = parser->value; + } else { + gpr_log(GPR_ERROR, "CHTTP2: Ignoring unknown setting %d (value %d)", + parser->id, parser->value); + } + break; + } + } +} diff --git a/src/core/transport/chttp2/frame_settings.h b/src/core/transport/chttp2/frame_settings.h new file mode 100644 index 0000000000..74e2b4fa22 --- /dev/null +++ b/src/core/transport/chttp2/frame_settings.h @@ -0,0 +1,99 @@ +/* + * + * Copyright 2014, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef __GRPC_INTERNAL_TRANSPORT_CHTTP2_FRAME_SETTINGS_H__ +#define __GRPC_INTERNAL_TRANSPORT_CHTTP2_FRAME_SETTINGS_H__ + +#include <grpc/support/port_platform.h> +#include <grpc/support/slice.h> +#include "src/core/transport/chttp2/frame.h" + +typedef enum { + GRPC_CHTTP2_SPS_ID0, + GRPC_CHTTP2_SPS_ID1, + GRPC_CHTTP2_SPS_VAL0, + GRPC_CHTTP2_SPS_VAL1, + GRPC_CHTTP2_SPS_VAL2, + GRPC_CHTTP2_SPS_VAL3 +} grpc_chttp2_settings_parse_state; + +/* The things HTTP/2 defines as connection level settings */ +typedef enum { + GRPC_CHTTP2_SETTINGS_HEADER_TABLE_SIZE = 1, + GRPC_CHTTP2_SETTINGS_ENABLE_PUSH = 2, + GRPC_CHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS = 3, + GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE = 4, + GRPC_CHTTP2_SETTINGS_MAX_FRAME_SIZE = 5, + GRPC_CHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE = 6, + GRPC_CHTTP2_NUM_SETTINGS +} grpc_chttp2_setting_id; + +typedef struct { + grpc_chttp2_settings_parse_state state; + gpr_uint32 *target_settings; + gpr_uint8 is_ack; + gpr_uint16 id; + gpr_uint32 value; + gpr_uint32 incoming_settings[GRPC_CHTTP2_NUM_SETTINGS]; +} grpc_chttp2_settings_parser; + +typedef enum { + GRPC_CHTTP2_CLAMP_INVALID_VALUE, + GRPC_CHTTP2_DISCONNECT_ON_INVALID_VALUE +} grpc_chttp2_invalid_value_behavior; + +typedef struct { + const char *name; + gpr_uint32 default_value; + gpr_uint32 min_value; + gpr_uint32 max_value; + grpc_chttp2_invalid_value_behavior invalid_value_behavior; +} grpc_chttp2_setting_parameters; + +/* HTTP/2 mandated connection setting parameters */ +extern const grpc_chttp2_setting_parameters + grpc_chttp2_settings_parameters[GRPC_CHTTP2_NUM_SETTINGS]; + +/* Create a settings frame by diffing old & new, and updating old to be new */ +gpr_slice grpc_chttp2_settings_create(gpr_uint32 *old, const gpr_uint32 *new, + size_t count); +/* Create an ack settings frame */ +gpr_slice grpc_chttp2_settings_ack_create(); + +grpc_chttp2_parse_error grpc_chttp2_settings_parser_begin_frame( + grpc_chttp2_settings_parser *parser, gpr_uint32 length, gpr_uint8 flags, + gpr_uint32 *settings); +grpc_chttp2_parse_error grpc_chttp2_settings_parser_parse( + void *parser, grpc_chttp2_parse_state *state, gpr_slice slice, int is_last); + +#endif /* __GRPC_INTERNAL_TRANSPORT_CHTTP2_FRAME_SETTINGS_H__ */ diff --git a/src/core/transport/chttp2/frame_window_update.c b/src/core/transport/chttp2/frame_window_update.c new file mode 100644 index 0000000000..f61714f52b --- /dev/null +++ b/src/core/transport/chttp2/frame_window_update.c @@ -0,0 +1,99 @@ +/* + * + * Copyright 2014, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "src/core/transport/chttp2/frame_window_update.h" + +#include <grpc/support/log.h> + +gpr_slice grpc_chttp2_window_update_create(gpr_uint32 id, + gpr_uint32 window_update) { + gpr_slice slice = gpr_slice_malloc(13); + gpr_uint8 *p = GPR_SLICE_START_PTR(slice); + + GPR_ASSERT(window_update); + + *p++ = 0; + *p++ = 0; + *p++ = 4; + *p++ = GRPC_CHTTP2_FRAME_WINDOW_UPDATE; + *p++ = 0; + *p++ = id >> 24; + *p++ = id >> 16; + *p++ = id >> 8; + *p++ = id; + *p++ = window_update >> 24; + *p++ = window_update >> 16; + *p++ = window_update >> 8; + *p++ = window_update; + + return slice; +} + +grpc_chttp2_parse_error grpc_chttp2_window_update_parser_begin_frame( + grpc_chttp2_window_update_parser *parser, gpr_uint32 length, + gpr_uint8 flags) { + if (flags || length != 4) { + gpr_log(GPR_ERROR, "invalid window update: length=%d, flags=%02x", length, + flags); + return GRPC_CHTTP2_CONNECTION_ERROR; + } + parser->byte = 0; + parser->amount = 0; + return GRPC_CHTTP2_PARSE_OK; +} + +grpc_chttp2_parse_error grpc_chttp2_window_update_parser_parse( + void *parser, grpc_chttp2_parse_state *state, gpr_slice slice, + int is_last) { + gpr_uint8 *const beg = GPR_SLICE_START_PTR(slice); + gpr_uint8 *const end = GPR_SLICE_END_PTR(slice); + gpr_uint8 *cur = beg; + grpc_chttp2_window_update_parser *p = parser; + + while (p->byte != 4 && cur != end) { + p->amount |= ((gpr_uint32)*cur) << (8 * (3 - p->byte)); + cur++; + p->byte++; + } + + if (p->byte == 4) { + if (p->amount == 0 || (p->amount & 0x80000000u)) { + gpr_log(GPR_ERROR, "invalid window update bytes: %d", p->amount); + return GRPC_CHTTP2_CONNECTION_ERROR; + } + GPR_ASSERT(is_last); + state->window_update = p->amount; + } + + return GRPC_CHTTP2_PARSE_OK; +} diff --git a/src/core/transport/chttp2/frame_window_update.h b/src/core/transport/chttp2/frame_window_update.h new file mode 100644 index 0000000000..4b789fcc4a --- /dev/null +++ b/src/core/transport/chttp2/frame_window_update.h @@ -0,0 +1,55 @@ +/* + * + * Copyright 2014, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef __GRPC_INTERNAL_TRANSPORT_CHTTP2_FRAME_WINDOW_UPDATE_H__ +#define __GRPC_INTERNAL_TRANSPORT_CHTTP2_FRAME_WINDOW_UPDATE_H__ + +#include <grpc/support/slice.h> +#include "src/core/transport/chttp2/frame.h" + +typedef struct { + gpr_uint8 byte; + gpr_uint8 is_connection_update; + gpr_uint32 amount; +} grpc_chttp2_window_update_parser; + +gpr_slice grpc_chttp2_window_update_create(gpr_uint32 id, + gpr_uint32 window_delta); + +grpc_chttp2_parse_error grpc_chttp2_window_update_parser_begin_frame( + grpc_chttp2_window_update_parser *parser, gpr_uint32 length, + gpr_uint8 flags); +grpc_chttp2_parse_error grpc_chttp2_window_update_parser_parse( + void *parser, grpc_chttp2_parse_state *state, gpr_slice slice, int is_last); + +#endif /* __GRPC_INTERNAL_TRANSPORT_CHTTP2_FRAME_WINDOW_UPDATE_H__ */ diff --git a/src/core/transport/chttp2/gen_hpack_tables.c b/src/core/transport/chttp2/gen_hpack_tables.c new file mode 100644 index 0000000000..cc94a737ca --- /dev/null +++ b/src/core/transport/chttp2/gen_hpack_tables.c @@ -0,0 +1,589 @@ +/* + * + * Copyright 2014, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +/* generates constant tables for hpack.c */ + +#include <stddef.h> +#include <stdio.h> +#include <string.h> +#include <assert.h> + +#include <grpc/support/log.h> + +/* + * first byte LUT generation + */ + +typedef struct { + const char *call; + /* bit prefix for the field type */ + unsigned char prefix; + /* length of the bit prefix for the field type */ + unsigned char prefix_length; + /* index value: 0 = all zeros, 2 = all ones, 1 otherwise */ + unsigned char index; +} spec; + +static const spec fields[] = { + {"INDEXED_FIELD", 0X80, 1, 1}, + {"INDEXED_FIELD_X", 0X80, 1, 2}, + {"LITHDR_INCIDX", 0X40, 2, 1}, + {"LITHDR_INCIDX_X", 0X40, 2, 2}, + {"LITHDR_INCIDX_V", 0X40, 2, 0}, + {"LITHDR_NOTIDX", 0X00, 4, 1}, + {"LITHDR_NOTIDX_X", 0X00, 4, 2}, + {"LITHDR_NOTIDX_V", 0X00, 4, 0}, + {"LITHDR_NVRIDX", 0X10, 4, 1}, + {"LITHDR_NVRIDX_X", 0X10, 4, 2}, + {"LITHDR_NVRIDX_V", 0X10, 4, 0}, + {"MAX_TBL_SIZE", 0X20, 3, 1}, + {"MAX_TBL_SIZE_X", 0X20, 3, 2}, +}; + +static const int num_fields = sizeof(fields) / sizeof(*fields); + +static unsigned char prefix_mask(unsigned char prefix_len) { + unsigned char i; + unsigned char out = 0; + for (i = 0; i < prefix_len; i++) { + out |= 1 << (7 - i); + } + return out; +} + +static unsigned char suffix_mask(unsigned char prefix_len) { + return ~prefix_mask(prefix_len); +} + +static void generate_first_byte_lut() { + int i, j, n; + const spec *chrspec; + unsigned char suffix; + + n = printf("static CALLTYPE first_byte[256] = {"); + /* for each potential first byte of a header */ + for (i = 0; i < 256; i++) { + /* find the field type that matches it */ + chrspec = NULL; + for (j = 0; j < num_fields; j++) { + if ((prefix_mask(fields[j].prefix_length) & i) == fields[j].prefix) { + suffix = suffix_mask(fields[j].prefix_length) & i; + if (suffix == suffix_mask(fields[j].prefix_length)) { + if (fields[j].index != 2) continue; + } else if (suffix == 0) { + if (fields[j].index != 0) continue; + } else { + if (fields[j].index != 1) continue; + } + GPR_ASSERT(chrspec == NULL); + chrspec = &fields[j]; + } + } + if (chrspec) { + n += printf("%s, ", chrspec->call); + } else { + n += printf("ILLEGAL, "); + } + /* make some small effort towards readable output */ + if (n > 70) { + printf("\n "); + n = 2; + } + } + printf("};\n"); +} + +/* + * Huffman decoder table generation + */ + +#define NSYMS 257 +#define MAXHUFFSTATES 1024 + +/* Constants pulled from the HPACK spec, and converted to C using the vim + command: + :%s/.* \([0-9a-f]\+\) \[ *\([0-9]\+\)\]/{0x\1, \2},/g */ +static const struct { + unsigned bits; + unsigned length; +} huffsyms[NSYMS] = { + {0x1ff8, 13}, + {0x7fffd8, 23}, + {0xfffffe2, 28}, + {0xfffffe3, 28}, + {0xfffffe4, 28}, + {0xfffffe5, 28}, + {0xfffffe6, 28}, + {0xfffffe7, 28}, + {0xfffffe8, 28}, + {0xffffea, 24}, + {0x3ffffffc, 30}, + {0xfffffe9, 28}, + {0xfffffea, 28}, + {0x3ffffffd, 30}, + {0xfffffeb, 28}, + {0xfffffec, 28}, + {0xfffffed, 28}, + {0xfffffee, 28}, + {0xfffffef, 28}, + {0xffffff0, 28}, + {0xffffff1, 28}, + {0xffffff2, 28}, + {0x3ffffffe, 30}, + {0xffffff3, 28}, + {0xffffff4, 28}, + {0xffffff5, 28}, + {0xffffff6, 28}, + {0xffffff7, 28}, + {0xffffff8, 28}, + {0xffffff9, 28}, + {0xffffffa, 28}, + {0xffffffb, 28}, + {0x14, 6}, + {0x3f8, 10}, + {0x3f9, 10}, + {0xffa, 12}, + {0x1ff9, 13}, + {0x15, 6}, + {0xf8, 8}, + {0x7fa, 11}, + {0x3fa, 10}, + {0x3fb, 10}, + {0xf9, 8}, + {0x7fb, 11}, + {0xfa, 8}, + {0x16, 6}, + {0x17, 6}, + {0x18, 6}, + {0x0, 5}, + {0x1, 5}, + {0x2, 5}, + {0x19, 6}, + {0x1a, 6}, + {0x1b, 6}, + {0x1c, 6}, + {0x1d, 6}, + {0x1e, 6}, + {0x1f, 6}, + {0x5c, 7}, + {0xfb, 8}, + {0x7ffc, 15}, + {0x20, 6}, + {0xffb, 12}, + {0x3fc, 10}, + {0x1ffa, 13}, + {0x21, 6}, + {0x5d, 7}, + {0x5e, 7}, + {0x5f, 7}, + {0x60, 7}, + {0x61, 7}, + {0x62, 7}, + {0x63, 7}, + {0x64, 7}, + {0x65, 7}, + {0x66, 7}, + {0x67, 7}, + {0x68, 7}, + {0x69, 7}, + {0x6a, 7}, + {0x6b, 7}, + {0x6c, 7}, + {0x6d, 7}, + {0x6e, 7}, + {0x6f, 7}, + {0x70, 7}, + {0x71, 7}, + {0x72, 7}, + {0xfc, 8}, + {0x73, 7}, + {0xfd, 8}, + {0x1ffb, 13}, + {0x7fff0, 19}, + {0x1ffc, 13}, + {0x3ffc, 14}, + {0x22, 6}, + {0x7ffd, 15}, + {0x3, 5}, + {0x23, 6}, + {0x4, 5}, + {0x24, 6}, + {0x5, 5}, + {0x25, 6}, + {0x26, 6}, + {0x27, 6}, + {0x6, 5}, + {0x74, 7}, + {0x75, 7}, + {0x28, 6}, + {0x29, 6}, + {0x2a, 6}, + {0x7, 5}, + {0x2b, 6}, + {0x76, 7}, + {0x2c, 6}, + {0x8, 5}, + {0x9, 5}, + {0x2d, 6}, + {0x77, 7}, + {0x78, 7}, + {0x79, 7}, + {0x7a, 7}, + {0x7b, 7}, + {0x7ffe, 15}, + {0x7fc, 11}, + {0x3ffd, 14}, + {0x1ffd, 13}, + {0xffffffc, 28}, + {0xfffe6, 20}, + {0x3fffd2, 22}, + {0xfffe7, 20}, + {0xfffe8, 20}, + {0x3fffd3, 22}, + {0x3fffd4, 22}, + {0x3fffd5, 22}, + {0x7fffd9, 23}, + {0x3fffd6, 22}, + {0x7fffda, 23}, + {0x7fffdb, 23}, + {0x7fffdc, 23}, + {0x7fffdd, 23}, + {0x7fffde, 23}, + {0xffffeb, 24}, + {0x7fffdf, 23}, + {0xffffec, 24}, + {0xffffed, 24}, + {0x3fffd7, 22}, + {0x7fffe0, 23}, + {0xffffee, 24}, + {0x7fffe1, 23}, + {0x7fffe2, 23}, + {0x7fffe3, 23}, + {0x7fffe4, 23}, + {0x1fffdc, 21}, + {0x3fffd8, 22}, + {0x7fffe5, 23}, + {0x3fffd9, 22}, + {0x7fffe6, 23}, + {0x7fffe7, 23}, + {0xffffef, 24}, + {0x3fffda, 22}, + {0x1fffdd, 21}, + {0xfffe9, 20}, + {0x3fffdb, 22}, + {0x3fffdc, 22}, + {0x7fffe8, 23}, + {0x7fffe9, 23}, + {0x1fffde, 21}, + {0x7fffea, 23}, + {0x3fffdd, 22}, + {0x3fffde, 22}, + {0xfffff0, 24}, + {0x1fffdf, 21}, + {0x3fffdf, 22}, + {0x7fffeb, 23}, + {0x7fffec, 23}, + {0x1fffe0, 21}, + {0x1fffe1, 21}, + {0x3fffe0, 22}, + {0x1fffe2, 21}, + {0x7fffed, 23}, + {0x3fffe1, 22}, + {0x7fffee, 23}, + {0x7fffef, 23}, + {0xfffea, 20}, + {0x3fffe2, 22}, + {0x3fffe3, 22}, + {0x3fffe4, 22}, + {0x7ffff0, 23}, + {0x3fffe5, 22}, + {0x3fffe6, 22}, + {0x7ffff1, 23}, + {0x3ffffe0, 26}, + {0x3ffffe1, 26}, + {0xfffeb, 20}, + {0x7fff1, 19}, + {0x3fffe7, 22}, + {0x7ffff2, 23}, + {0x3fffe8, 22}, + {0x1ffffec, 25}, + {0x3ffffe2, 26}, + {0x3ffffe3, 26}, + {0x3ffffe4, 26}, + {0x7ffffde, 27}, + {0x7ffffdf, 27}, + {0x3ffffe5, 26}, + {0xfffff1, 24}, + {0x1ffffed, 25}, + {0x7fff2, 19}, + {0x1fffe3, 21}, + {0x3ffffe6, 26}, + {0x7ffffe0, 27}, + {0x7ffffe1, 27}, + {0x3ffffe7, 26}, + {0x7ffffe2, 27}, + {0xfffff2, 24}, + {0x1fffe4, 21}, + {0x1fffe5, 21}, + {0x3ffffe8, 26}, + {0x3ffffe9, 26}, + {0xffffffd, 28}, + {0x7ffffe3, 27}, + {0x7ffffe4, 27}, + {0x7ffffe5, 27}, + {0xfffec, 20}, + {0xfffff3, 24}, + {0xfffed, 20}, + {0x1fffe6, 21}, + {0x3fffe9, 22}, + {0x1fffe7, 21}, + {0x1fffe8, 21}, + {0x7ffff3, 23}, + {0x3fffea, 22}, + {0x3fffeb, 22}, + {0x1ffffee, 25}, + {0x1ffffef, 25}, + {0xfffff4, 24}, + {0xfffff5, 24}, + {0x3ffffea, 26}, + {0x7ffff4, 23}, + {0x3ffffeb, 26}, + {0x7ffffe6, 27}, + {0x3ffffec, 26}, + {0x3ffffed, 26}, + {0x7ffffe7, 27}, + {0x7ffffe8, 27}, + {0x7ffffe9, 27}, + {0x7ffffea, 27}, + {0x7ffffeb, 27}, + {0xffffffe, 28}, + {0x7ffffec, 27}, + {0x7ffffed, 27}, + {0x7ffffee, 27}, + {0x7ffffef, 27}, + {0x7fffff0, 27}, + {0x3ffffee, 26}, + {0x3fffffff, 30}, +}; + +/* represents a set of symbols as an array of booleans indicating inclusion */ +typedef struct { char included[NSYMS]; } symset; +/* represents a lookup table indexed by a nibble */ +typedef struct { int values[16]; } nibblelut; + +/* returns a symset that includes all possible symbols */ +static symset symset_all() { + symset x; + memset(x.included, 1, sizeof(x.included)); + return x; +} + +/* returns a symset that includes no symbols */ +static symset symset_none() { + symset x; + memset(x.included, 0, sizeof(x.included)); + return x; +} + +/* returns an empty nibblelut */ +static nibblelut nibblelut_empty() { + nibblelut x; + int i; + for (i = 0; i < 16; i++) { + x.values[i] = -1; + } + return x; +} + +/* counts symbols in a symset - only used for debug builds */ +#ifndef NDEBUG +static int nsyms(symset s) { + int i; + int c = 0; + for (i = 0; i < NSYMS; i++) { + c += s.included[i] != 0; + } + return c; +} +#endif + +/* global table of discovered huffman decoding states */ +static struct { + /* the bit offset that this state starts at */ + int bitofs; + /* the set of symbols that this state started with */ + symset syms; + + /* lookup table for the next state */ + nibblelut next; + /* lookup table for what to emit */ + nibblelut emit; +} huffstates[MAXHUFFSTATES]; +static int nhuffstates = 0; + +/* given a number of decoded bits and a set of symbols that are live, + return the index into the decoder table for this state. + set isnew to 1 if this state was previously undiscovered */ +static int state_index(int bitofs, symset syms, int *isnew) { + int i; + for (i = 0; i < nhuffstates; i++) { + if (huffstates[i].bitofs != bitofs) continue; + if (0 != memcmp(huffstates[i].syms.included, syms.included, NSYMS)) + continue; + *isnew = 0; + return i; + } + GPR_ASSERT(nhuffstates != MAXHUFFSTATES); + i = nhuffstates++; + huffstates[i].bitofs = bitofs; + huffstates[i].syms = syms; + huffstates[i].next = nibblelut_empty(); + huffstates[i].emit = nibblelut_empty(); + *isnew = 1; + return i; +} + +/* recursively build a decoding table + + state - the huffman state that we are trying to fill in + nibble - the current nibble + nibbits - the number of bits in the nibble that have been filled in + bitofs - the number of bits of symbol that have been decoded + emit - the symbol to emit on this nibble (or -1 if no symbol has been + found) + syms - the set of symbols that could be matched */ +static void build_dec_tbl(int state, int nibble, int nibbits, int bitofs, + int emit, symset syms) { + int i; + int bit; + + /* If we have four bits in the nibble we're looking at, then we can fill in + a slot in the lookup tables. */ + if (nibbits == 4) { + int isnew; + /* Find the state that we are in: this may be a new state, in which case + we recurse to fill it in, or we may have already seen this state, in + which case the recursion terminates */ + int st = state_index(bitofs, syms, &isnew); + GPR_ASSERT(huffstates[state].next.values[nibble] == -1); + huffstates[state].next.values[nibble] = st; + huffstates[state].emit.values[nibble] = emit; + if (isnew) { + build_dec_tbl(st, 0, 0, bitofs, -1, syms); + } + return; + } + + assert(nsyms(syms)); + + /* A bit can be 0 or 1 */ + for (bit = 0; bit < 2; bit++) { + /* walk over active symbols and see if they have this bit set */ + symset nextsyms = symset_none(); + for (i = 0; i < NSYMS; i++) { + if (!syms.included[i]) continue; /* disregard inactive symbols */ + if (((huffsyms[i].bits >> (huffsyms[i].length - bitofs - 1)) & 1) == + bit) { + /* the bit is set, include it in the next recursive set */ + if (huffsyms[i].length == bitofs + 1) { + /* additionally, we've gotten to the end of a symbol - this is a + special recursion step: re-activate all the symbols, reset + bitofs to zero, and recurse */ + build_dec_tbl(state, (nibble << 1) | bit, nibbits + 1, 0, i, + symset_all()); + /* skip the remainder of this loop */ + goto next; + } + nextsyms.included[i] = 1; + } + } + /* recurse down for this bit */ + build_dec_tbl(state, (nibble << 1) | bit, nibbits + 1, bitofs + 1, emit, + nextsyms); + next: + ; + } +} + +static nibblelut ctbl[MAXHUFFSTATES]; +static int nctbl; + +static int ctbl_idx(nibblelut x) { + int i; + for (i = 0; i < nctbl; i++) { + if (0 == memcmp(&x, ctbl + i, sizeof(nibblelut))) return i; + } + ctbl[i] = x; + nctbl++; + return i; +} + +static void dump_ctbl(const char *name) { + int i, j; + printf("static const gpr_int16 %s[%d*16] = {\n", name, nctbl); + for (i = 0; i < nctbl; i++) { + for (j = 0; j < 16; j++) { + printf("%d,", ctbl[i].values[j]); + } + printf("\n"); + } + printf("};\n"); +} + +static void generate_huff_tables() { + int i; + build_dec_tbl(state_index(0, symset_all(), &i), 0, 0, 0, -1, symset_all()); + + nctbl = 0; + printf("static const gpr_uint8 next_tbl[%d] = {", nhuffstates); + for (i = 0; i < nhuffstates; i++) { + printf("%d,", ctbl_idx(huffstates[i].next)); + } + printf("};\n"); + dump_ctbl("next_sub_tbl"); + + nctbl = 0; + printf("static const gpr_uint16 emit_tbl[%d] = {", nhuffstates); + for (i = 0; i < nhuffstates; i++) { + printf("%d,", ctbl_idx(huffstates[i].emit)); + } + printf("};\n"); + dump_ctbl("emit_sub_tbl"); +} + +int main(void) { + generate_huff_tables(); + generate_first_byte_lut(); + + return 0; +} diff --git a/src/core/transport/chttp2/hpack_parser.c b/src/core/transport/chttp2/hpack_parser.c new file mode 100644 index 0000000000..33588a73d4 --- /dev/null +++ b/src/core/transport/chttp2/hpack_parser.c @@ -0,0 +1,1212 @@ +/* + * + * Copyright 2014, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "src/core/transport/chttp2/hpack_parser.h" + +#include <stddef.h> +#include <string.h> +#include <assert.h> + +#include "src/core/support/murmur_hash.h" +#include <grpc/support/alloc.h> +#include <grpc/support/log.h> +#include <grpc/support/port_platform.h> +#include <grpc/support/string.h> +#include <grpc/support/useful.h> + +/* How parsing works: + + The parser object keeps track of a function pointer which represents the + current parse state. + + Each time new bytes are presented, we call into the current state, which + recursively parses until all bytes in the given chunk are exhausted. + + The parse state that terminates then saves its function pointer to be the + current state so that it can resume when more bytes are available. + + It's expected that most optimizing compilers will turn this code into + a set of indirect jumps, and so not waste stack space. */ + +/* forward declarations for parsing states */ +static int parse_begin(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, + const gpr_uint8 *end); +static int parse_error(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, + const gpr_uint8 *end); + +static int parse_string_prefix(grpc_chttp2_hpack_parser *p, + const gpr_uint8 *cur, const gpr_uint8 *end); +static int parse_key_string(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, + const gpr_uint8 *end); +static int parse_value_string(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, + const gpr_uint8 *end); + +static int parse_value0(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, + const gpr_uint8 *end); +static int parse_value1(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, + const gpr_uint8 *end); +static int parse_value2(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, + const gpr_uint8 *end); +static int parse_value3(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, + const gpr_uint8 *end); +static int parse_value4(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, + const gpr_uint8 *end); +static int parse_value5up(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, + const gpr_uint8 *end); + +static int parse_indexed_field(grpc_chttp2_hpack_parser *p, + const gpr_uint8 *cur, const gpr_uint8 *end); +static int parse_indexed_field_x(grpc_chttp2_hpack_parser *p, + const gpr_uint8 *cur, const gpr_uint8 *end); +static int parse_lithdr_incidx(grpc_chttp2_hpack_parser *p, + const gpr_uint8 *cur, const gpr_uint8 *end); +static int parse_lithdr_incidx_x(grpc_chttp2_hpack_parser *p, + const gpr_uint8 *cur, const gpr_uint8 *end); +static int parse_lithdr_incidx_v(grpc_chttp2_hpack_parser *p, + const gpr_uint8 *cur, const gpr_uint8 *end); +static int parse_lithdr_notidx(grpc_chttp2_hpack_parser *p, + const gpr_uint8 *cur, const gpr_uint8 *end); +static int parse_lithdr_notidx_x(grpc_chttp2_hpack_parser *p, + const gpr_uint8 *cur, const gpr_uint8 *end); +static int parse_lithdr_notidx_v(grpc_chttp2_hpack_parser *p, + const gpr_uint8 *cur, const gpr_uint8 *end); +static int parse_lithdr_nvridx(grpc_chttp2_hpack_parser *p, + const gpr_uint8 *cur, const gpr_uint8 *end); +static int parse_lithdr_nvridx_x(grpc_chttp2_hpack_parser *p, + const gpr_uint8 *cur, const gpr_uint8 *end); +static int parse_lithdr_nvridx_v(grpc_chttp2_hpack_parser *p, + const gpr_uint8 *cur, const gpr_uint8 *end); +static int parse_max_tbl_size(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, + const gpr_uint8 *end); +static int parse_max_tbl_size_x(grpc_chttp2_hpack_parser *p, + const gpr_uint8 *cur, const gpr_uint8 *end); + +/* we translate the first byte of a hpack field into one of these decoding + cases, then use a lookup table to jump directly to the appropriate parser. + + _X => the integer index is all ones, meaning we need to do varint decoding + _V => the integer index is all zeros, meaning we need to decode an additional + string value */ +typedef enum { + INDEXED_FIELD, + INDEXED_FIELD_X, + LITHDR_INCIDX, + LITHDR_INCIDX_X, + LITHDR_INCIDX_V, + LITHDR_NOTIDX, + LITHDR_NOTIDX_X, + LITHDR_NOTIDX_V, + LITHDR_NVRIDX, + LITHDR_NVRIDX_X, + LITHDR_NVRIDX_V, + MAX_TBL_SIZE, + MAX_TBL_SIZE_X, + ILLEGAL +} first_byte_type; + +/* jump table of parse state functions -- order must match first_byte_type + above */ +static const grpc_chttp2_hpack_parser_state first_byte_action[] = { + parse_indexed_field, parse_indexed_field_x, parse_lithdr_incidx, + parse_lithdr_incidx_x, parse_lithdr_incidx_v, parse_lithdr_notidx, + parse_lithdr_notidx_x, parse_lithdr_notidx_v, parse_lithdr_nvridx, + parse_lithdr_nvridx_x, parse_lithdr_nvridx_v, parse_max_tbl_size, + parse_max_tbl_size_x, parse_error}; + +/* indexes the first byte to a parse state function - generated by + gen_hpack_tables.c */ +static const gpr_uint8 first_byte_lut[256] = { + LITHDR_NOTIDX_V, LITHDR_NOTIDX, LITHDR_NOTIDX, LITHDR_NOTIDX, + LITHDR_NOTIDX, LITHDR_NOTIDX, LITHDR_NOTIDX, LITHDR_NOTIDX, + LITHDR_NOTIDX, LITHDR_NOTIDX, LITHDR_NOTIDX, LITHDR_NOTIDX, + LITHDR_NOTIDX, LITHDR_NOTIDX, LITHDR_NOTIDX, LITHDR_NOTIDX_X, + LITHDR_NVRIDX_V, LITHDR_NVRIDX, LITHDR_NVRIDX, LITHDR_NVRIDX, + LITHDR_NVRIDX, LITHDR_NVRIDX, LITHDR_NVRIDX, LITHDR_NVRIDX, + LITHDR_NVRIDX, LITHDR_NVRIDX, LITHDR_NVRIDX, LITHDR_NVRIDX, + LITHDR_NVRIDX, LITHDR_NVRIDX, LITHDR_NVRIDX, LITHDR_NVRIDX_X, + ILLEGAL, MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE, + MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE, + MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE, + MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE, + MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE, + MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE, + MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE, + MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE_X, + LITHDR_INCIDX_V, LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, + LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, + LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, + LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, + LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, + LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, + LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, + LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, + LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, + LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, + LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, + LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, + LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, + LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, + LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, + LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX_X, + ILLEGAL, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, + INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, + INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, + INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, + INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, + INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, + INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, + INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, + INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, + INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, + INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, + INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, + INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, + INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, + INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, + INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, + INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, + INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, + INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, + INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, + INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, + INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, + INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, + INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, + INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, + INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, + INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, + INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, + INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, + INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, + INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, + INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD_X, +}; + +/* state table for huffman decoding: given a state, gives an index/16 into + next_sub_tbl. Taking that index and adding the value of the nibble being + considered returns the next state. + + generated by gen_hpack_tables.c */ +static const gpr_uint8 next_tbl[256] = { + 0, 1, 2, 3, 4, 1, 2, 5, 6, 1, 7, 8, 1, 3, 3, 9, 10, 11, 1, 1, + 1, 12, 1, 2, 13, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, + 14, 1, 15, 16, 1, 17, 1, 15, 2, 7, 3, 18, 19, 1, 1, 1, 1, 20, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 15, 2, 2, 7, 21, 1, 22, 1, 1, 1, 1, 1, + 1, 1, 1, 15, 2, 2, 2, 2, 2, 2, 23, 24, 25, 1, 1, 1, 1, 2, 2, 2, + 26, 3, 3, 27, 10, 28, 1, 1, 1, 1, 1, 1, 2, 3, 29, 10, 30, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 31, 1, 1, 1, 1, 1, 1, 1, 2, + 2, 2, 2, 2, 2, 2, 2, 32, 1, 1, 15, 33, 1, 34, 35, 9, 36, 1, 1, 1, + 1, 1, 1, 1, 37, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 26, 9, + 38, 1, 1, 1, 1, 1, 1, 1, 15, 2, 2, 2, 2, 26, 3, 3, 39, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 7, 3, 3, 3, 40, 2, + 41, 1, 1, 1, 42, 43, 1, 1, 44, 1, 1, 1, 1, 15, 2, 2, 2, 2, 2, 2, + 3, 3, 3, 45, 46, 1, 1, 2, 2, 2, 35, 3, 3, 18, 47, 2, +}; +/* next state, based upon current state and the current nibble: see above. + generated by gen_hpack_tables.c */ +static const gpr_int16 next_sub_tbl[48 * 16] = { + 1, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, + 218, 2, 6, 10, 13, 14, 15, 16, 17, 2, 6, 10, 13, 14, 15, + 16, 17, 3, 7, 11, 24, 3, 7, 11, 24, 3, 7, 11, 24, 3, + 7, 11, 24, 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, + 4, 8, 4, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, + 199, 200, 201, 202, 203, 4, 8, 4, 8, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 9, 133, 134, 135, 136, 137, 138, 139, 140, + 141, 142, 143, 144, 145, 146, 147, 3, 7, 11, 24, 3, 7, 11, 24, + 4, 8, 4, 8, 4, 8, 4, 8, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 12, 132, 4, 8, 4, 8, 4, 8, + 4, 8, 4, 8, 4, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 18, 19, 20, 21, 4, 8, 4, + 8, 4, 8, 4, 8, 4, 8, 0, 0, 0, 22, 23, 91, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 3, + 7, 11, 24, 3, 7, 11, 24, 0, 0, 0, 0, 0, 41, 42, 43, + 2, 6, 10, 13, 14, 15, 16, 17, 3, 7, 11, 24, 3, 7, 11, + 24, 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 0, 0, + 44, 45, 2, 6, 10, 13, 14, 15, 16, 17, 46, 47, 48, 49, 50, + 51, 52, 57, 4, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 53, 54, 55, 56, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, + 68, 69, 70, 71, 72, 74, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 73, 75, 76, 77, 78, 79, 80, 81, 82, + 83, 84, 85, 86, 87, 88, 89, 90, 3, 7, 11, 24, 3, 7, 11, + 24, 3, 7, 11, 24, 0, 0, 0, 0, 3, 7, 11, 24, 3, 7, + 11, 24, 4, 8, 4, 8, 0, 0, 0, 92, 0, 0, 0, 93, 94, + 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 3, 7, 11, 24, + 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 4, + 8, 4, 8, 4, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 4, + 8, 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 0, 0, + 0, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, + 131, 2, 6, 10, 13, 14, 15, 16, 17, 4, 8, 4, 8, 4, 8, + 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 148, + 149, 150, 151, 3, 7, 11, 24, 4, 8, 4, 8, 0, 0, 0, 0, + 0, 0, 152, 153, 3, 7, 11, 24, 3, 7, 11, 24, 3, 7, 11, + 24, 154, 155, 156, 164, 3, 7, 11, 24, 3, 7, 11, 24, 3, 7, + 11, 24, 4, 8, 4, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 157, 158, 159, 160, 161, 162, 163, 165, 166, 167, 168, 169, 170, 171, 172, + 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, + 188, 189, 190, 191, 192, 193, 194, 195, 196, 4, 8, 4, 8, 4, 8, + 4, 8, 4, 8, 4, 8, 4, 8, 197, 198, 4, 8, 4, 8, 4, + 8, 4, 8, 0, 0, 0, 0, 0, 0, 219, 220, 3, 7, 11, 24, + 4, 8, 4, 8, 4, 8, 0, 0, 221, 222, 223, 224, 3, 7, 11, + 24, 3, 7, 11, 24, 4, 8, 4, 8, 4, 8, 225, 228, 4, 8, + 4, 8, 4, 8, 0, 0, 0, 0, 0, 0, 0, 0, 226, 227, 229, + 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, + 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 245, 246, 247, 248, 249, 250, 251, 252, + 253, 254, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 255, +}; +/* emission table: indexed like next_tbl, ultimately gives the byte to be + emitted, or -1 for no byte, or 256 for end of stream + + generated by gen_hpack_tables.c */ +static const gpr_uint16 emit_tbl[256] = { + 0, 1, 2, 3, 4, 5, 6, 7, 0, 8, 9, 10, 11, 12, 13, + 14, 15, 16, 17, 18, 19, 20, 21, 22, 0, 23, 24, 25, 26, 27, + 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, + 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 0, 55, 56, + 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 0, + 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, + 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, + 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, + 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, + 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, + 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 0, + 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, + 0, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, + 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, + 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, + 219, 220, 221, 0, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, + 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, + 248, +}; +/* generated by gen_hpack_tables.c */ +static const gpr_int16 emit_sub_tbl[249 * 16] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 48, 48, 48, 48, 48, 48, 48, 48, 49, 49, 49, 49, 49, 49, + 49, 49, 48, 48, 48, 48, 49, 49, 49, 49, 50, 50, 50, 50, 97, + 97, 97, 97, 48, 48, 49, 49, 50, 50, 97, 97, 99, 99, 101, 101, + 105, 105, 111, 111, 48, 49, 50, 97, 99, 101, 105, 111, 115, 116, -1, + -1, -1, -1, -1, -1, 32, 32, 32, 32, 32, 32, 32, 32, 37, 37, + 37, 37, 37, 37, 37, 37, 99, 99, 99, 99, 101, 101, 101, 101, 105, + 105, 105, 105, 111, 111, 111, 111, 115, 115, 116, 116, 32, 37, 45, 46, + 47, 51, 52, 53, 54, 55, 56, 57, 61, 61, 61, 61, 61, 61, 61, + 61, 65, 65, 65, 65, 65, 65, 65, 65, 115, 115, 115, 115, 116, 116, + 116, 116, 32, 32, 37, 37, 45, 45, 46, 46, 61, 65, 95, 98, 100, + 102, 103, 104, 108, 109, 110, 112, 114, 117, -1, -1, 58, 58, 58, 58, + 58, 58, 58, 58, 66, 66, 66, 66, 66, 66, 66, 66, 47, 47, 51, + 51, 52, 52, 53, 53, 54, 54, 55, 55, 56, 56, 57, 57, 61, 61, + 65, 65, 95, 95, 98, 98, 100, 100, 102, 102, 103, 103, 104, 104, 108, + 108, 109, 109, 110, 110, 112, 112, 114, 114, 117, 117, 58, 66, 67, 68, + 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, + 84, 85, 86, 87, 89, 106, 107, 113, 118, 119, 120, 121, 122, -1, -1, + -1, -1, 38, 38, 38, 38, 38, 38, 38, 38, 42, 42, 42, 42, 42, + 42, 42, 42, 44, 44, 44, 44, 44, 44, 44, 44, 59, 59, 59, 59, + 59, 59, 59, 59, 88, 88, 88, 88, 88, 88, 88, 88, 90, 90, 90, + 90, 90, 90, 90, 90, 33, 33, 34, 34, 40, 40, 41, 41, 63, 63, + 39, 43, 124, -1, -1, -1, 35, 35, 35, 35, 35, 35, 35, 35, 62, + 62, 62, 62, 62, 62, 62, 62, 0, 0, 0, 0, 36, 36, 36, 36, + 64, 64, 64, 64, 91, 91, 91, 91, 69, 69, 69, 69, 69, 69, 69, + 69, 70, 70, 70, 70, 70, 70, 70, 70, 71, 71, 71, 71, 71, 71, + 71, 71, 72, 72, 72, 72, 72, 72, 72, 72, 73, 73, 73, 73, 73, + 73, 73, 73, 74, 74, 74, 74, 74, 74, 74, 74, 75, 75, 75, 75, + 75, 75, 75, 75, 76, 76, 76, 76, 76, 76, 76, 76, 77, 77, 77, + 77, 77, 77, 77, 77, 78, 78, 78, 78, 78, 78, 78, 78, 79, 79, + 79, 79, 79, 79, 79, 79, 80, 80, 80, 80, 80, 80, 80, 80, 81, + 81, 81, 81, 81, 81, 81, 81, 82, 82, 82, 82, 82, 82, 82, 82, + 83, 83, 83, 83, 83, 83, 83, 83, 84, 84, 84, 84, 84, 84, 84, + 84, 85, 85, 85, 85, 85, 85, 85, 85, 86, 86, 86, 86, 86, 86, + 86, 86, 87, 87, 87, 87, 87, 87, 87, 87, 89, 89, 89, 89, 89, + 89, 89, 89, 106, 106, 106, 106, 106, 106, 106, 106, 107, 107, 107, 107, + 107, 107, 107, 107, 113, 113, 113, 113, 113, 113, 113, 113, 118, 118, 118, + 118, 118, 118, 118, 118, 119, 119, 119, 119, 119, 119, 119, 119, 120, 120, + 120, 120, 120, 120, 120, 120, 121, 121, 121, 121, 121, 121, 121, 121, 122, + 122, 122, 122, 122, 122, 122, 122, 38, 38, 38, 38, 42, 42, 42, 42, + 44, 44, 44, 44, 59, 59, 59, 59, 88, 88, 88, 88, 90, 90, 90, + 90, 33, 34, 40, 41, 63, -1, -1, -1, 39, 39, 39, 39, 39, 39, + 39, 39, 43, 43, 43, 43, 43, 43, 43, 43, 124, 124, 124, 124, 124, + 124, 124, 124, 35, 35, 35, 35, 62, 62, 62, 62, 0, 0, 36, 36, + 64, 64, 91, 91, 93, 93, 126, 126, 94, 125, -1, -1, 60, 60, 60, + 60, 60, 60, 60, 60, 96, 96, 96, 96, 96, 96, 96, 96, 123, 123, + 123, 123, 123, 123, 123, 123, -1, -1, -1, -1, -1, -1, -1, -1, 92, + 92, 92, 92, 92, 92, 92, 92, 195, 195, 195, 195, 195, 195, 195, 195, + 208, 208, 208, 208, 208, 208, 208, 208, 128, 128, 128, 128, 130, 130, 130, + 130, 131, 131, 131, 131, 162, 162, 162, 162, 184, 184, 184, 184, 194, 194, + 194, 194, 224, 224, 224, 224, 226, 226, 226, 226, 153, 153, 161, 161, 167, + 167, 172, 172, 176, 176, 177, 177, 179, 179, 209, 209, 216, 216, 217, 217, + 227, 227, 229, 229, 230, 230, 129, 132, 133, 134, 136, 146, 154, 156, 160, + 163, 164, 169, 170, 173, 178, 181, 185, 186, 187, 189, 190, 196, 198, 228, + 232, 233, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 135, + 135, 135, 135, 135, 135, 135, 135, 137, 137, 137, 137, 137, 137, 137, 137, + 138, 138, 138, 138, 138, 138, 138, 138, 139, 139, 139, 139, 139, 139, 139, + 139, 140, 140, 140, 140, 140, 140, 140, 140, 141, 141, 141, 141, 141, 141, + 141, 141, 143, 143, 143, 143, 143, 143, 143, 143, 147, 147, 147, 147, 147, + 147, 147, 147, 149, 149, 149, 149, 149, 149, 149, 149, 150, 150, 150, 150, + 150, 150, 150, 150, 151, 151, 151, 151, 151, 151, 151, 151, 152, 152, 152, + 152, 152, 152, 152, 152, 155, 155, 155, 155, 155, 155, 155, 155, 157, 157, + 157, 157, 157, 157, 157, 157, 158, 158, 158, 158, 158, 158, 158, 158, 165, + 165, 165, 165, 165, 165, 165, 165, 166, 166, 166, 166, 166, 166, 166, 166, + 168, 168, 168, 168, 168, 168, 168, 168, 174, 174, 174, 174, 174, 174, 174, + 174, 175, 175, 175, 175, 175, 175, 175, 175, 180, 180, 180, 180, 180, 180, + 180, 180, 182, 182, 182, 182, 182, 182, 182, 182, 183, 183, 183, 183, 183, + 183, 183, 183, 188, 188, 188, 188, 188, 188, 188, 188, 191, 191, 191, 191, + 191, 191, 191, 191, 197, 197, 197, 197, 197, 197, 197, 197, 231, 231, 231, + 231, 231, 231, 231, 231, 239, 239, 239, 239, 239, 239, 239, 239, 9, 9, + 9, 9, 142, 142, 142, 142, 144, 144, 144, 144, 145, 145, 145, 145, 148, + 148, 148, 148, 159, 159, 159, 159, 171, 171, 171, 171, 206, 206, 206, 206, + 215, 215, 215, 215, 225, 225, 225, 225, 236, 236, 236, 236, 237, 237, 237, + 237, 199, 199, 207, 207, 234, 234, 235, 235, 192, 193, 200, 201, 202, 205, + 210, 213, 218, 219, 238, 240, 242, 243, 255, -1, 203, 203, 203, 203, 203, + 203, 203, 203, 204, 204, 204, 204, 204, 204, 204, 204, 211, 211, 211, 211, + 211, 211, 211, 211, 212, 212, 212, 212, 212, 212, 212, 212, 214, 214, 214, + 214, 214, 214, 214, 214, 221, 221, 221, 221, 221, 221, 221, 221, 222, 222, + 222, 222, 222, 222, 222, 222, 223, 223, 223, 223, 223, 223, 223, 223, 241, + 241, 241, 241, 241, 241, 241, 241, 244, 244, 244, 244, 244, 244, 244, 244, + 245, 245, 245, 245, 245, 245, 245, 245, 246, 246, 246, 246, 246, 246, 246, + 246, 247, 247, 247, 247, 247, 247, 247, 247, 248, 248, 248, 248, 248, 248, + 248, 248, 250, 250, 250, 250, 250, 250, 250, 250, 251, 251, 251, 251, 251, + 251, 251, 251, 252, 252, 252, 252, 252, 252, 252, 252, 253, 253, 253, 253, + 253, 253, 253, 253, 254, 254, 254, 254, 254, 254, 254, 254, 2, 2, 2, + 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, + 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 11, 11, 11, 11, 12, + 12, 12, 12, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, + 17, 17, 17, 17, 18, 18, 18, 18, 19, 19, 19, 19, 20, 20, 20, + 20, 21, 21, 21, 21, 23, 23, 23, 23, 24, 24, 24, 24, 25, 25, + 25, 25, 26, 26, 26, 26, 27, 27, 27, 27, 28, 28, 28, 28, 29, + 29, 29, 29, 30, 30, 30, 30, 31, 31, 31, 31, 127, 127, 127, 127, + 220, 220, 220, 220, 249, 249, 249, 249, 10, 13, 22, 256, 93, 93, 93, + 93, 126, 126, 126, 126, 94, 94, 125, 125, 60, 96, 123, -1, 92, 195, + 208, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 128, + 128, 128, 128, 128, 128, 128, 128, 130, 130, 130, 130, 130, 130, 130, 130, + 131, 131, 131, 131, 131, 131, 131, 131, 162, 162, 162, 162, 162, 162, 162, + 162, 184, 184, 184, 184, 184, 184, 184, 184, 194, 194, 194, 194, 194, 194, + 194, 194, 224, 224, 224, 224, 224, 224, 224, 224, 226, 226, 226, 226, 226, + 226, 226, 226, 153, 153, 153, 153, 161, 161, 161, 161, 167, 167, 167, 167, + 172, 172, 172, 172, 176, 176, 176, 176, 177, 177, 177, 177, 179, 179, 179, + 179, 209, 209, 209, 209, 216, 216, 216, 216, 217, 217, 217, 217, 227, 227, + 227, 227, 229, 229, 229, 229, 230, 230, 230, 230, 129, 129, 132, 132, 133, + 133, 134, 134, 136, 136, 146, 146, 154, 154, 156, 156, 160, 160, 163, 163, + 164, 164, 169, 169, 170, 170, 173, 173, 178, 178, 181, 181, 185, 185, 186, + 186, 187, 187, 189, 189, 190, 190, 196, 196, 198, 198, 228, 228, 232, 232, + 233, 233, 1, 135, 137, 138, 139, 140, 141, 143, 147, 149, 150, 151, 152, + 155, 157, 158, 165, 166, 168, 174, 175, 180, 182, 183, 188, 191, 197, 231, + 239, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 9, 9, 9, + 9, 9, 9, 9, 9, 142, 142, 142, 142, 142, 142, 142, 142, 144, 144, + 144, 144, 144, 144, 144, 144, 145, 145, 145, 145, 145, 145, 145, 145, 148, + 148, 148, 148, 148, 148, 148, 148, 159, 159, 159, 159, 159, 159, 159, 159, + 171, 171, 171, 171, 171, 171, 171, 171, 206, 206, 206, 206, 206, 206, 206, + 206, 215, 215, 215, 215, 215, 215, 215, 215, 225, 225, 225, 225, 225, 225, + 225, 225, 236, 236, 236, 236, 236, 236, 236, 236, 237, 237, 237, 237, 237, + 237, 237, 237, 199, 199, 199, 199, 207, 207, 207, 207, 234, 234, 234, 234, + 235, 235, 235, 235, 192, 192, 193, 193, 200, 200, 201, 201, 202, 202, 205, + 205, 210, 210, 213, 213, 218, 218, 219, 219, 238, 238, 240, 240, 242, 242, + 243, 243, 255, 255, 203, 204, 211, 212, 214, 221, 222, 223, 241, 244, 245, + 246, 247, 248, 250, 251, 252, 253, 254, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 2, 2, 2, 2, 2, 2, 2, + 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, + 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, + 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, + 8, 8, 8, 8, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, + 12, 12, 12, 12, 12, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, + 15, 15, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, 17, + 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, + 19, 19, 19, 19, 19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, + 20, 21, 21, 21, 21, 21, 21, 21, 21, 23, 23, 23, 23, 23, 23, + 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 25, 25, 25, 25, 25, + 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, + 27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, + 29, 29, 29, 29, 29, 30, 30, 30, 30, 30, 30, 30, 30, 31, 31, + 31, 31, 31, 31, 31, 31, 127, 127, 127, 127, 127, 127, 127, 127, 220, + 220, 220, 220, 220, 220, 220, 220, 249, 249, 249, 249, 249, 249, 249, 249, + 10, 10, 13, 13, 22, 22, 256, 256, 67, 67, 67, 67, 67, 67, 67, + 67, 68, 68, 68, 68, 68, 68, 68, 68, 95, 95, 95, 95, 95, 95, + 95, 95, 98, 98, 98, 98, 98, 98, 98, 98, 100, 100, 100, 100, 100, + 100, 100, 100, 102, 102, 102, 102, 102, 102, 102, 102, 103, 103, 103, 103, + 103, 103, 103, 103, 104, 104, 104, 104, 104, 104, 104, 104, 108, 108, 108, + 108, 108, 108, 108, 108, 109, 109, 109, 109, 109, 109, 109, 109, 110, 110, + 110, 110, 110, 110, 110, 110, 112, 112, 112, 112, 112, 112, 112, 112, 114, + 114, 114, 114, 114, 114, 114, 114, 117, 117, 117, 117, 117, 117, 117, 117, + 58, 58, 58, 58, 66, 66, 66, 66, 67, 67, 67, 67, 68, 68, 68, + 68, 69, 69, 69, 69, 70, 70, 70, 70, 71, 71, 71, 71, 72, 72, + 72, 72, 73, 73, 73, 73, 74, 74, 74, 74, 75, 75, 75, 75, 76, + 76, 76, 76, 77, 77, 77, 77, 78, 78, 78, 78, 79, 79, 79, 79, + 80, 80, 80, 80, 81, 81, 81, 81, 82, 82, 82, 82, 83, 83, 83, + 83, 84, 84, 84, 84, 85, 85, 85, 85, 86, 86, 86, 86, 87, 87, + 87, 87, 89, 89, 89, 89, 106, 106, 106, 106, 107, 107, 107, 107, 113, + 113, 113, 113, 118, 118, 118, 118, 119, 119, 119, 119, 120, 120, 120, 120, + 121, 121, 121, 121, 122, 122, 122, 122, 38, 38, 42, 42, 44, 44, 59, + 59, 88, 88, 90, 90, -1, -1, -1, -1, 33, 33, 33, 33, 33, 33, + 33, 33, 34, 34, 34, 34, 34, 34, 34, 34, 40, 40, 40, 40, 40, + 40, 40, 40, 41, 41, 41, 41, 41, 41, 41, 41, 63, 63, 63, 63, + 63, 63, 63, 63, 39, 39, 39, 39, 43, 43, 43, 43, 124, 124, 124, + 124, 35, 35, 62, 62, 0, 36, 64, 91, 93, 126, -1, -1, 94, 94, + 94, 94, 94, 94, 94, 94, 125, 125, 125, 125, 125, 125, 125, 125, 60, + 60, 60, 60, 96, 96, 96, 96, 123, 123, 123, 123, -1, -1, -1, -1, + 92, 92, 92, 92, 195, 195, 195, 195, 208, 208, 208, 208, 128, 128, 130, + 130, 131, 131, 162, 162, 184, 184, 194, 194, 224, 224, 226, 226, 153, 161, + 167, 172, 176, 177, 179, 209, 216, 217, 227, 229, 230, -1, -1, -1, -1, + -1, -1, -1, 129, 129, 129, 129, 129, 129, 129, 129, 132, 132, 132, 132, + 132, 132, 132, 132, 133, 133, 133, 133, 133, 133, 133, 133, 134, 134, 134, + 134, 134, 134, 134, 134, 136, 136, 136, 136, 136, 136, 136, 136, 146, 146, + 146, 146, 146, 146, 146, 146, 154, 154, 154, 154, 154, 154, 154, 154, 156, + 156, 156, 156, 156, 156, 156, 156, 160, 160, 160, 160, 160, 160, 160, 160, + 163, 163, 163, 163, 163, 163, 163, 163, 164, 164, 164, 164, 164, 164, 164, + 164, 169, 169, 169, 169, 169, 169, 169, 169, 170, 170, 170, 170, 170, 170, + 170, 170, 173, 173, 173, 173, 173, 173, 173, 173, 178, 178, 178, 178, 178, + 178, 178, 178, 181, 181, 181, 181, 181, 181, 181, 181, 185, 185, 185, 185, + 185, 185, 185, 185, 186, 186, 186, 186, 186, 186, 186, 186, 187, 187, 187, + 187, 187, 187, 187, 187, 189, 189, 189, 189, 189, 189, 189, 189, 190, 190, + 190, 190, 190, 190, 190, 190, 196, 196, 196, 196, 196, 196, 196, 196, 198, + 198, 198, 198, 198, 198, 198, 198, 228, 228, 228, 228, 228, 228, 228, 228, + 232, 232, 232, 232, 232, 232, 232, 232, 233, 233, 233, 233, 233, 233, 233, + 233, 1, 1, 1, 1, 135, 135, 135, 135, 137, 137, 137, 137, 138, 138, + 138, 138, 139, 139, 139, 139, 140, 140, 140, 140, 141, 141, 141, 141, 143, + 143, 143, 143, 147, 147, 147, 147, 149, 149, 149, 149, 150, 150, 150, 150, + 151, 151, 151, 151, 152, 152, 152, 152, 155, 155, 155, 155, 157, 157, 157, + 157, 158, 158, 158, 158, 165, 165, 165, 165, 166, 166, 166, 166, 168, 168, + 168, 168, 174, 174, 174, 174, 175, 175, 175, 175, 180, 180, 180, 180, 182, + 182, 182, 182, 183, 183, 183, 183, 188, 188, 188, 188, 191, 191, 191, 191, + 197, 197, 197, 197, 231, 231, 231, 231, 239, 239, 239, 239, 9, 9, 142, + 142, 144, 144, 145, 145, 148, 148, 159, 159, 171, 171, 206, 206, 215, 215, + 225, 225, 236, 236, 237, 237, 199, 207, 234, 235, 192, 192, 192, 192, 192, + 192, 192, 192, 193, 193, 193, 193, 193, 193, 193, 193, 200, 200, 200, 200, + 200, 200, 200, 200, 201, 201, 201, 201, 201, 201, 201, 201, 202, 202, 202, + 202, 202, 202, 202, 202, 205, 205, 205, 205, 205, 205, 205, 205, 210, 210, + 210, 210, 210, 210, 210, 210, 213, 213, 213, 213, 213, 213, 213, 213, 218, + 218, 218, 218, 218, 218, 218, 218, 219, 219, 219, 219, 219, 219, 219, 219, + 238, 238, 238, 238, 238, 238, 238, 238, 240, 240, 240, 240, 240, 240, 240, + 240, 242, 242, 242, 242, 242, 242, 242, 242, 243, 243, 243, 243, 243, 243, + 243, 243, 255, 255, 255, 255, 255, 255, 255, 255, 203, 203, 203, 203, 204, + 204, 204, 204, 211, 211, 211, 211, 212, 212, 212, 212, 214, 214, 214, 214, + 221, 221, 221, 221, 222, 222, 222, 222, 223, 223, 223, 223, 241, 241, 241, + 241, 244, 244, 244, 244, 245, 245, 245, 245, 246, 246, 246, 246, 247, 247, + 247, 247, 248, 248, 248, 248, 250, 250, 250, 250, 251, 251, 251, 251, 252, + 252, 252, 252, 253, 253, 253, 253, 254, 254, 254, 254, 2, 2, 3, 3, + 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 11, 11, 12, 12, 14, + 14, 15, 15, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, + 23, 23, 24, 24, 25, 25, 26, 26, 27, 27, 28, 28, 29, 29, 30, + 30, 31, 31, 127, 127, 220, 220, 249, 249, -1, -1, 10, 10, 10, 10, + 10, 10, 10, 10, 13, 13, 13, 13, 13, 13, 13, 13, 22, 22, 22, + 22, 22, 22, 22, 22, 256, 256, 256, 256, 256, 256, 256, 256, 45, 45, + 45, 45, 45, 45, 45, 45, 46, 46, 46, 46, 46, 46, 46, 46, 47, + 47, 47, 47, 47, 47, 47, 47, 51, 51, 51, 51, 51, 51, 51, 51, + 52, 52, 52, 52, 52, 52, 52, 52, 53, 53, 53, 53, 53, 53, 53, + 53, 54, 54, 54, 54, 54, 54, 54, 54, 55, 55, 55, 55, 55, 55, + 55, 55, 56, 56, 56, 56, 56, 56, 56, 56, 57, 57, 57, 57, 57, + 57, 57, 57, 50, 50, 50, 50, 50, 50, 50, 50, 97, 97, 97, 97, + 97, 97, 97, 97, 99, 99, 99, 99, 99, 99, 99, 99, 101, 101, 101, + 101, 101, 101, 101, 101, 105, 105, 105, 105, 105, 105, 105, 105, 111, 111, + 111, 111, 111, 111, 111, 111, 115, 115, 115, 115, 115, 115, 115, 115, 116, + 116, 116, 116, 116, 116, 116, 116, 32, 32, 32, 32, 37, 37, 37, 37, + 45, 45, 45, 45, 46, 46, 46, 46, 47, 47, 47, 47, 51, 51, 51, + 51, 52, 52, 52, 52, 53, 53, 53, 53, 54, 54, 54, 54, 55, 55, + 55, 55, 56, 56, 56, 56, 57, 57, 57, 57, 61, 61, 61, 61, 65, + 65, 65, 65, 95, 95, 95, 95, 98, 98, 98, 98, 100, 100, 100, 100, + 102, 102, 102, 102, 103, 103, 103, 103, 104, 104, 104, 104, 108, 108, 108, + 108, 109, 109, 109, 109, 110, 110, 110, 110, 112, 112, 112, 112, 114, 114, + 114, 114, 117, 117, 117, 117, 58, 58, 66, 66, 67, 67, 68, 68, 69, + 69, 70, 70, 71, 71, 72, 72, 73, 73, 74, 74, 75, 75, 76, 76, + 77, 77, 78, 78, 79, 79, 80, 80, 81, 81, 82, 82, 83, 83, 84, + 84, 85, 85, 86, 86, 87, 87, 89, 89, 106, 106, 107, 107, 113, 113, + 118, 118, 119, 119, 120, 120, 121, 121, 122, 122, 38, 42, 44, 59, 88, + 90, -1, -1, 33, 33, 33, 33, 34, 34, 34, 34, 40, 40, 40, 40, + 41, 41, 41, 41, 63, 63, 63, 63, 39, 39, 43, 43, 124, 124, 35, + 62, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 36, 36, + 36, 36, 36, 36, 36, 36, 64, 64, 64, 64, 64, 64, 64, 64, 91, + 91, 91, 91, 91, 91, 91, 91, 93, 93, 93, 93, 93, 93, 93, 93, + 126, 126, 126, 126, 126, 126, 126, 126, 94, 94, 94, 94, 125, 125, 125, + 125, 60, 60, 96, 96, 123, 123, -1, -1, 92, 92, 195, 195, 208, 208, + 128, 130, 131, 162, 184, 194, 224, 226, -1, -1, 153, 153, 153, 153, 153, + 153, 153, 153, 161, 161, 161, 161, 161, 161, 161, 161, 167, 167, 167, 167, + 167, 167, 167, 167, 172, 172, 172, 172, 172, 172, 172, 172, 176, 176, 176, + 176, 176, 176, 176, 176, 177, 177, 177, 177, 177, 177, 177, 177, 179, 179, + 179, 179, 179, 179, 179, 179, 209, 209, 209, 209, 209, 209, 209, 209, 216, + 216, 216, 216, 216, 216, 216, 216, 217, 217, 217, 217, 217, 217, 217, 217, + 227, 227, 227, 227, 227, 227, 227, 227, 229, 229, 229, 229, 229, 229, 229, + 229, 230, 230, 230, 230, 230, 230, 230, 230, 129, 129, 129, 129, 132, 132, + 132, 132, 133, 133, 133, 133, 134, 134, 134, 134, 136, 136, 136, 136, 146, + 146, 146, 146, 154, 154, 154, 154, 156, 156, 156, 156, 160, 160, 160, 160, + 163, 163, 163, 163, 164, 164, 164, 164, 169, 169, 169, 169, 170, 170, 170, + 170, 173, 173, 173, 173, 178, 178, 178, 178, 181, 181, 181, 181, 185, 185, + 185, 185, 186, 186, 186, 186, 187, 187, 187, 187, 189, 189, 189, 189, 190, + 190, 190, 190, 196, 196, 196, 196, 198, 198, 198, 198, 228, 228, 228, 228, + 232, 232, 232, 232, 233, 233, 233, 233, 1, 1, 135, 135, 137, 137, 138, + 138, 139, 139, 140, 140, 141, 141, 143, 143, 147, 147, 149, 149, 150, 150, + 151, 151, 152, 152, 155, 155, 157, 157, 158, 158, 165, 165, 166, 166, 168, + 168, 174, 174, 175, 175, 180, 180, 182, 182, 183, 183, 188, 188, 191, 191, + 197, 197, 231, 231, 239, 239, 9, 142, 144, 145, 148, 159, 171, 206, 215, + 225, 236, 237, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 199, 199, + 199, 199, 199, 199, 199, 199, 207, 207, 207, 207, 207, 207, 207, 207, 234, + 234, 234, 234, 234, 234, 234, 234, 235, 235, 235, 235, 235, 235, 235, 235, + 192, 192, 192, 192, 193, 193, 193, 193, 200, 200, 200, 200, 201, 201, 201, + 201, 202, 202, 202, 202, 205, 205, 205, 205, 210, 210, 210, 210, 213, 213, + 213, 213, 218, 218, 218, 218, 219, 219, 219, 219, 238, 238, 238, 238, 240, + 240, 240, 240, 242, 242, 242, 242, 243, 243, 243, 243, 255, 255, 255, 255, + 203, 203, 204, 204, 211, 211, 212, 212, 214, 214, 221, 221, 222, 222, 223, + 223, 241, 241, 244, 244, 245, 245, 246, 246, 247, 247, 248, 248, 250, 250, + 251, 251, 252, 252, 253, 253, 254, 254, 2, 3, 4, 5, 6, 7, 8, + 11, 12, 14, 15, 16, 17, 18, 19, 20, 21, 23, 24, 25, 26, 27, + 28, 29, 30, 31, 127, 220, 249, -1, 10, 10, 10, 10, 13, 13, 13, + 13, 22, 22, 22, 22, 256, 256, 256, 256, +}; + +/* emission helpers */ +static void on_hdr(grpc_chttp2_hpack_parser *p, grpc_mdelem *md, + int add_to_table) { + if (add_to_table) { + grpc_mdelem_ref(md); + grpc_chttp2_hptbl_add(&p->table, md); + } + p->on_header(p->on_header_user_data, md); +} + +static grpc_mdstr *take_string(grpc_chttp2_hpack_parser *p, + grpc_chttp2_hpack_parser_string *str) { + grpc_mdstr *s = grpc_mdstr_from_buffer(p->table.mdctx, (gpr_uint8 *)str->str, + str->length); + str->length = 0; + return s; +} + +/* jump to the next state */ +static int parse_next(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, + const gpr_uint8 *end) { + p->state = *p->next_state++; + return p->state(p, cur, end); +} + +/* begin parsing a header: all functionality is encoded into lookup tables + above */ +static int parse_begin(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, + const gpr_uint8 *end) { + if (cur == end) { + p->state = parse_begin; + return 1; + } + + return first_byte_action[first_byte_lut[*cur]](p, cur, end); +} + +/* stream dependency and prioritization data: we just skip it */ +static int parse_stream_weight(grpc_chttp2_hpack_parser *p, + const gpr_uint8 *cur, const gpr_uint8 *end) { + if (cur == end) { + p->state = parse_stream_weight; + return 1; + } + + return parse_begin(p, cur + 1, end); +} + +static int parse_stream_dep3(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, + const gpr_uint8 *end) { + if (cur == end) { + p->state = parse_stream_dep3; + return 1; + } + + return parse_stream_weight(p, cur + 1, end); +} + +static int parse_stream_dep2(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, + const gpr_uint8 *end) { + if (cur == end) { + p->state = parse_stream_dep2; + return 1; + } + + return parse_stream_dep3(p, cur + 1, end); +} + +static int parse_stream_dep1(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, + const gpr_uint8 *end) { + if (cur == end) { + p->state = parse_stream_dep1; + return 1; + } + + return parse_stream_dep2(p, cur + 1, end); +} + +static int parse_stream_dep0(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, + const gpr_uint8 *end) { + if (cur == end) { + p->state = parse_stream_dep0; + return 1; + } + + return parse_stream_dep1(p, cur + 1, end); +} + +/* emit an indexed field; for now just logs it to console; jumps to + begin the next field on completion */ +static int finish_indexed_field(grpc_chttp2_hpack_parser *p, + const gpr_uint8 *cur, const gpr_uint8 *end) { + grpc_mdelem *md = grpc_chttp2_hptbl_lookup(&p->table, p->index); + grpc_mdelem_ref(md); + on_hdr(p, md, 0); + return parse_begin(p, cur, end); +} + +/* parse an indexed field with index < 127 */ +static int parse_indexed_field(grpc_chttp2_hpack_parser *p, + const gpr_uint8 *cur, const gpr_uint8 *end) { + p->index = (*cur) & 0x7f; + return finish_indexed_field(p, cur + 1, end); +} + +/* parse an indexed field with index >= 127 */ +static int parse_indexed_field_x(grpc_chttp2_hpack_parser *p, + const gpr_uint8 *cur, const gpr_uint8 *end) { + static const grpc_chttp2_hpack_parser_state and_then[] = { + finish_indexed_field}; + p->next_state = and_then; + p->index = 0x7f; + p->parsing.value = &p->index; + return parse_value0(p, cur + 1, end); +} + +/* finish a literal header with incremental indexing: just log, and jump to ' + begin */ +static int finish_lithdr_incidx(grpc_chttp2_hpack_parser *p, + const gpr_uint8 *cur, const gpr_uint8 *end) { + grpc_mdelem *md = grpc_chttp2_hptbl_lookup(&p->table, p->index); + on_hdr(p, grpc_mdelem_from_metadata_strings(p->table.mdctx, + grpc_mdstr_ref(md->key), + take_string(p, &p->value)), + 1); + return parse_begin(p, cur, end); +} + +/* finish a literal header with incremental indexing with no index */ +static int finish_lithdr_incidx_v(grpc_chttp2_hpack_parser *p, + const gpr_uint8 *cur, const gpr_uint8 *end) { + on_hdr(p, grpc_mdelem_from_metadata_strings(p->table.mdctx, + take_string(p, &p->key), + take_string(p, &p->value)), + 1); + return parse_begin(p, cur, end); +} + +/* parse a literal header with incremental indexing; index < 63 */ +static int parse_lithdr_incidx(grpc_chttp2_hpack_parser *p, + const gpr_uint8 *cur, const gpr_uint8 *end) { + static const grpc_chttp2_hpack_parser_state and_then[] = { + parse_value_string, finish_lithdr_incidx}; + p->next_state = and_then; + p->index = (*cur) & 0x3f; + return parse_string_prefix(p, cur + 1, end); +} + +/* parse a literal header with incremental indexing; index >= 63 */ +static int parse_lithdr_incidx_x(grpc_chttp2_hpack_parser *p, + const gpr_uint8 *cur, const gpr_uint8 *end) { + static const grpc_chttp2_hpack_parser_state and_then[] = { + parse_string_prefix, parse_value_string, finish_lithdr_incidx}; + p->next_state = and_then; + p->index = 0x3f; + p->parsing.value = &p->index; + return parse_value0(p, cur + 1, end); +} + +/* parse a literal header with incremental indexing; index = 0 */ +static int parse_lithdr_incidx_v(grpc_chttp2_hpack_parser *p, + const gpr_uint8 *cur, const gpr_uint8 *end) { + static const grpc_chttp2_hpack_parser_state and_then[] = { + parse_key_string, parse_string_prefix, parse_value_string, + finish_lithdr_incidx_v}; + p->next_state = and_then; + return parse_string_prefix(p, cur + 1, end); +} + +/* finish a literal header without incremental indexing */ +static int finish_lithdr_notidx(grpc_chttp2_hpack_parser *p, + const gpr_uint8 *cur, const gpr_uint8 *end) { + grpc_mdelem *md = grpc_chttp2_hptbl_lookup(&p->table, p->index); + on_hdr(p, grpc_mdelem_from_metadata_strings(p->table.mdctx, + grpc_mdstr_ref(md->key), + take_string(p, &p->value)), + 0); + return parse_begin(p, cur, end); +} + +/* finish a literal header without incremental indexing with index = 0 */ +static int finish_lithdr_notidx_v(grpc_chttp2_hpack_parser *p, + const gpr_uint8 *cur, const gpr_uint8 *end) { + on_hdr(p, grpc_mdelem_from_metadata_strings(p->table.mdctx, + take_string(p, &p->key), + take_string(p, &p->value)), + 0); + return parse_begin(p, cur, end); +} + +/* parse a literal header without incremental indexing; index < 15 */ +static int parse_lithdr_notidx(grpc_chttp2_hpack_parser *p, + const gpr_uint8 *cur, const gpr_uint8 *end) { + static const grpc_chttp2_hpack_parser_state and_then[] = { + parse_value_string, finish_lithdr_notidx}; + p->next_state = and_then; + p->index = (*cur) & 0xf; + return parse_string_prefix(p, cur + 1, end); +} + +/* parse a literal header without incremental indexing; index >= 15 */ +static int parse_lithdr_notidx_x(grpc_chttp2_hpack_parser *p, + const gpr_uint8 *cur, const gpr_uint8 *end) { + static const grpc_chttp2_hpack_parser_state and_then[] = { + parse_string_prefix, parse_value_string, finish_lithdr_notidx}; + p->next_state = and_then; + p->index = 0xf; + p->parsing.value = &p->index; + return parse_value0(p, cur + 1, end); +} + +/* parse a literal header without incremental indexing; index == 0 */ +static int parse_lithdr_notidx_v(grpc_chttp2_hpack_parser *p, + const gpr_uint8 *cur, const gpr_uint8 *end) { + static const grpc_chttp2_hpack_parser_state and_then[] = { + parse_key_string, parse_string_prefix, parse_value_string, + finish_lithdr_notidx_v}; + p->next_state = and_then; + return parse_string_prefix(p, cur + 1, end); +} + +/* finish a literal header that is never indexed */ +static int finish_lithdr_nvridx(grpc_chttp2_hpack_parser *p, + const gpr_uint8 *cur, const gpr_uint8 *end) { + grpc_mdelem *md = grpc_chttp2_hptbl_lookup(&p->table, p->index); + on_hdr(p, grpc_mdelem_from_metadata_strings(p->table.mdctx, + grpc_mdstr_ref(md->key), + take_string(p, &p->value)), + 0); + return parse_begin(p, cur, end); +} + +/* finish a literal header that is never indexed with an extra value */ +static int finish_lithdr_nvridx_v(grpc_chttp2_hpack_parser *p, + const gpr_uint8 *cur, const gpr_uint8 *end) { + on_hdr(p, grpc_mdelem_from_metadata_strings(p->table.mdctx, + take_string(p, &p->key), + take_string(p, &p->value)), + 0); + return parse_begin(p, cur, end); +} + +/* parse a literal header that is never indexed; index < 15 */ +static int parse_lithdr_nvridx(grpc_chttp2_hpack_parser *p, + const gpr_uint8 *cur, const gpr_uint8 *end) { + static const grpc_chttp2_hpack_parser_state and_then[] = { + parse_value_string, finish_lithdr_nvridx}; + p->next_state = and_then; + p->index = (*cur) & 0xf; + return parse_string_prefix(p, cur + 1, end); +} + +/* parse a literal header that is never indexed; index >= 15 */ +static int parse_lithdr_nvridx_x(grpc_chttp2_hpack_parser *p, + const gpr_uint8 *cur, const gpr_uint8 *end) { + static const grpc_chttp2_hpack_parser_state and_then[] = { + parse_string_prefix, parse_value_string, finish_lithdr_nvridx}; + p->next_state = and_then; + p->index = 0xf; + p->parsing.value = &p->index; + return parse_value0(p, cur + 1, end); +} + +/* parse a literal header that is never indexed; index == 0 */ +static int parse_lithdr_nvridx_v(grpc_chttp2_hpack_parser *p, + const gpr_uint8 *cur, const gpr_uint8 *end) { + static const grpc_chttp2_hpack_parser_state and_then[] = { + parse_key_string, parse_string_prefix, parse_value_string, + finish_lithdr_nvridx_v}; + p->next_state = and_then; + return parse_string_prefix(p, cur + 1, end); +} + +/* finish parsing a max table size change */ +static int finish_max_tbl_size(grpc_chttp2_hpack_parser *p, + const gpr_uint8 *cur, const gpr_uint8 *end) { + gpr_log(GPR_INFO, "MAX TABLE SIZE: %d", p->index); + abort(); /* not implemented */ + return parse_begin(p, cur, end); +} + +/* parse a max table size change, max size < 15 */ +static int parse_max_tbl_size(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, + const gpr_uint8 *end) { + p->index = (*cur) & 0xf; + return finish_max_tbl_size(p, cur + 1, end); +} + +/* parse a max table size change, max size >= 15 */ +static int parse_max_tbl_size_x(grpc_chttp2_hpack_parser *p, + const gpr_uint8 *cur, const gpr_uint8 *end) { + static const grpc_chttp2_hpack_parser_state and_then[] = { + finish_max_tbl_size}; + p->next_state = and_then; + p->index = 0xf; + p->parsing.value = &p->index; + return parse_value0(p, cur + 1, end); +} + +/* a parse error: jam the parse state into parse_error, and return error */ +static int parse_error(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, + const gpr_uint8 *end) { + p->state = parse_error; + return 0; +} + +/* parse the 1st byte of a varint into p->parsing.value + no overflow is possible */ +static int parse_value0(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, + const gpr_uint8 *end) { + if (cur == end) { + p->state = parse_value0; + return 1; + } + + *p->parsing.value += (*cur) & 0x7f; + + if ((*cur) & 0x80) { + return parse_value1(p, cur + 1, end); + } else { + return parse_next(p, cur + 1, end); + } +} + +/* parse the 2nd byte of a varint into p->parsing.value + no overflow is possible */ +static int parse_value1(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, + const gpr_uint8 *end) { + if (cur == end) { + p->state = parse_value1; + return 1; + } + + *p->parsing.value += (((gpr_uint32)*cur) & 0x7f) << 7; + + if ((*cur) & 0x80) { + return parse_value2(p, cur + 1, end); + } else { + return parse_next(p, cur + 1, end); + } +} + +/* parse the 3rd byte of a varint into p->parsing.value + no overflow is possible */ +static int parse_value2(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, + const gpr_uint8 *end) { + if (cur == end) { + p->state = parse_value2; + return 1; + } + + *p->parsing.value += (((gpr_uint32)*cur) & 0x7f) << 14; + + if ((*cur) & 0x80) { + return parse_value3(p, cur + 1, end); + } else { + return parse_next(p, cur + 1, end); + } +} + +/* parse the 4th byte of a varint into p->parsing.value + no overflow is possible */ +static int parse_value3(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, + const gpr_uint8 *end) { + if (cur == end) { + p->state = parse_value3; + return 1; + } + + *p->parsing.value += (((gpr_uint32)*cur) & 0x7f) << 21; + + if ((*cur) & 0x80) { + return parse_value4(p, cur + 1, end); + } else { + return parse_next(p, cur + 1, end); + } +} + +/* parse the 5th byte of a varint into p->parsing.value + depending on the byte, we may overflow, and care must be taken */ +static int parse_value4(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, + const gpr_uint8 *end) { + gpr_uint8 c; + gpr_uint32 cur_value; + gpr_uint32 add_value; + + if (cur == end) { + p->state = parse_value4; + return 1; + } + + c = (*cur) & 0x7f; + if (c > 0xf) { + goto error; + } + + cur_value = *p->parsing.value; + add_value = ((gpr_uint32)c) << 28; + if (add_value > 0xffffffffu - cur_value) { + goto error; + } + + *p->parsing.value = cur_value + add_value; + + if ((*cur) & 0x80) { + return parse_value5up(p, cur + 1, end); + } else { + return parse_next(p, cur + 1, end); + } + +error: + gpr_log(GPR_ERROR, + "integer overflow in hpack integer decoding: have 0x%08x, " + "got byte 0x%02x", + *p->parsing.value, *cur); + return parse_error(p, cur, end); +} + +/* parse any trailing bytes in a varint: it's possible to append an arbitrary + number of 0x80's and not affect the value - a zero will terminate - and + anything else will overflow */ +static int parse_value5up(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, + const gpr_uint8 *end) { + while (cur != end && *cur == 0x80) { + ++cur; + } + + if (cur == end) { + p->state = parse_value5up; + return 1; + } + + if (*cur == 0) { + return parse_next(p, cur + 1, end); + } + + gpr_log(GPR_ERROR, + "integer overflow in hpack integer decoding: have 0x%08x, " + "got byte 0x%02x sometime after byte 4"); + return parse_error(p, cur, end); +} + +/* parse a string prefix */ +static int parse_string_prefix(grpc_chttp2_hpack_parser *p, + const gpr_uint8 *cur, const gpr_uint8 *end) { + if (cur == end) { + p->state = parse_string_prefix; + return 1; + } + + p->strlen = (*cur) & 0x7f; + p->huff = (*cur) >> 7; + if (p->strlen == 0x7f) { + p->parsing.value = &p->strlen; + return parse_value0(p, cur + 1, end); + } else { + return parse_next(p, cur + 1, end); + } +} + +/* append some bytes to a string */ +static void append_string(grpc_chttp2_hpack_parser_string *str, + const gpr_uint8 *data, size_t length) { + if (length + str->length > str->capacity) { + str->capacity = str->length + length; + str->str = gpr_realloc(str->str, str->capacity); + } + memcpy(str->str + str->length, data, length); + str->length += length; +} + +/* append a null terminator to a string */ +static void finish_str(grpc_chttp2_hpack_parser_string *str) { + gpr_uint8 terminator = 0; + append_string(str, &terminator, 1); + str->length--; /* don't actually count the null terminator */ +} + +/* decode a nibble from a huffman encoded stream */ +static void huff_nibble(grpc_chttp2_hpack_parser *p, gpr_uint8 nibble) { + gpr_int16 emit = emit_sub_tbl[16 * emit_tbl[p->huff_state] + nibble]; + gpr_int16 next = next_sub_tbl[16 * next_tbl[p->huff_state] + nibble]; + if (emit != -1) { + if (emit >= 0 && emit < 256) { + gpr_uint8 c = emit; + append_string(p->parsing.str, &c, 1); + } else { + assert(emit == 256); + } + } + p->huff_state = next; +} + +/* decode full bytes from a huffman encoded stream */ +static void add_huff_bytes(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, + const gpr_uint8 *end) { + for (; cur != end; ++cur) { + huff_nibble(p, *cur >> 4); + huff_nibble(p, *cur & 0xf); + } +} + +/* decode some string bytes based on the current decoding mode + (huffman or not) */ +static void add_str_bytes(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, + const gpr_uint8 *end) { + if (p->huff) { + add_huff_bytes(p, cur, end); + } else { + append_string(p->parsing.str, cur, end - cur); + } +} + +/* parse a string - tries to do large chunks at a time */ +static int parse_string(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, + const gpr_uint8 *end) { + size_t remaining = p->strlen - p->strgot; + size_t given = end - cur; + if (remaining <= given) { + add_str_bytes(p, cur, cur + remaining); + finish_str(p->parsing.str); + return parse_next(p, cur + remaining, end); + } else { + add_str_bytes(p, cur, cur + given); + p->strgot += given; + p->state = parse_string; + return 1; + } +} + +/* begin parsing a string - performs setup, calls parse_string */ +static int begin_parse_string(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, + const gpr_uint8 *end, + grpc_chttp2_hpack_parser_string *str) { + p->strgot = 0; + str->length = 0; + p->parsing.str = str; + p->huff_state = 0; + return parse_string(p, cur, end); +} + +/* parse the key string */ +static int parse_key_string(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, + const gpr_uint8 *end) { + return begin_parse_string(p, cur, end, &p->key); +} + +/* parse the value string */ +static int parse_value_string(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, + const gpr_uint8 *end) { + return begin_parse_string(p, cur, end, &p->value); +} + +/* PUBLIC INTERFACE */ + +static void on_header_not_set(void *user_data, grpc_mdelem *md) { + char *keyhex = + gpr_hexdump(grpc_mdstr_as_c_string(md->key), + GPR_SLICE_LENGTH(md->key->slice), GPR_HEXDUMP_PLAINTEXT); + char *valuehex = + gpr_hexdump(grpc_mdstr_as_c_string(md->value), + GPR_SLICE_LENGTH(md->value->slice), GPR_HEXDUMP_PLAINTEXT); + gpr_log(GPR_ERROR, "on_header callback not set; key=%s value=%s", keyhex, + valuehex); + gpr_free(keyhex); + gpr_free(valuehex); + grpc_mdelem_unref(md); + abort(); +} + +void grpc_chttp2_hpack_parser_init(grpc_chttp2_hpack_parser *p, + grpc_mdctx *mdctx) { + p->on_header = on_header_not_set; + p->on_header_user_data = NULL; + p->state = parse_begin; + p->key.str = NULL; + p->key.capacity = 0; + p->key.length = 0; + p->value.str = NULL; + p->value.capacity = 0; + p->value.length = 0; + grpc_chttp2_hptbl_init(&p->table, mdctx); +} + +void grpc_chttp2_hpack_parser_set_has_priority(grpc_chttp2_hpack_parser *p) { + GPR_ASSERT(p->state == parse_begin); + p->state = parse_stream_dep0; +} + +void grpc_chttp2_hpack_parser_destroy(grpc_chttp2_hpack_parser *p) { + grpc_chttp2_hptbl_destroy(&p->table); + gpr_free(p->key.str); + gpr_free(p->value.str); +} + +int grpc_chttp2_hpack_parser_parse(grpc_chttp2_hpack_parser *p, + const gpr_uint8 *beg, const gpr_uint8 *end) { + /* TODO(ctiller): limit the distance of end from beg, and perform multiple + steps in the event of a large chunk of data to limit + stack space usage when no tail call optimization is + available */ + return p->state(p, beg, end); +} + +grpc_chttp2_parse_error grpc_chttp2_header_parser_parse( + void *hpack_parser, grpc_chttp2_parse_state *state, gpr_slice slice, + int is_last) { + grpc_chttp2_hpack_parser *parser = hpack_parser; + if (!grpc_chttp2_hpack_parser_parse(parser, GPR_SLICE_START_PTR(slice), + GPR_SLICE_END_PTR(slice))) { + return GRPC_CHTTP2_CONNECTION_ERROR; + } + if (is_last) { + if (parser->is_boundary && parser->state != parse_begin) { + gpr_log(GPR_ERROR, + "end of header frame not aligned with a hpack record boundary"); + return GRPC_CHTTP2_CONNECTION_ERROR; + } + state->metadata_boundary = parser->is_boundary; + state->end_of_stream = parser->is_eof; + state->need_flush_reads = parser->is_eof; + parser->on_header = on_header_not_set; + parser->on_header_user_data = NULL; + parser->is_boundary = 0xde; + parser->is_eof = 0xde; + } + return GRPC_CHTTP2_PARSE_OK; +} diff --git a/src/core/transport/chttp2/hpack_parser.h b/src/core/transport/chttp2/hpack_parser.h new file mode 100644 index 0000000000..cf68042fbd --- /dev/null +++ b/src/core/transport/chttp2/hpack_parser.h @@ -0,0 +1,108 @@ +/* + * + * Copyright 2014, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef __GRPC_INTERNAL_TRANSPORT_CHTTP2_HPACK_PARSER_H__ +#define __GRPC_INTERNAL_TRANSPORT_CHTTP2_HPACK_PARSER_H__ + +#include <stddef.h> + +#include <grpc/support/port_platform.h> +#include "src/core/transport/chttp2/frame.h" +#include "src/core/transport/chttp2/hpack_table.h" +#include "src/core/transport/metadata.h" + +typedef struct grpc_chttp2_hpack_parser grpc_chttp2_hpack_parser; + +typedef int (*grpc_chttp2_hpack_parser_state)(grpc_chttp2_hpack_parser *p, + const gpr_uint8 *beg, + const gpr_uint8 *end); + +typedef struct { + char *str; + gpr_uint32 length; + gpr_uint32 capacity; +} grpc_chttp2_hpack_parser_string; + +struct grpc_chttp2_hpack_parser { + /* user specified callback for each header output */ + void (*on_header)(void *user_data, grpc_mdelem *md); + void *on_header_user_data; + + /* current parse state - or a function that implements it */ + grpc_chttp2_hpack_parser_state state; + /* future states dependent on the opening op code */ + const grpc_chttp2_hpack_parser_state *next_state; + /* the value we're currently parsing */ + union { + gpr_uint32 *value; + grpc_chttp2_hpack_parser_string *str; + } parsing; + /* string parameters for each chunk */ + grpc_chttp2_hpack_parser_string key; + grpc_chttp2_hpack_parser_string value; + /* parsed index */ + gpr_uint32 index; + /* length of source bytes for the currently parsing string */ + gpr_uint32 strlen; + /* number of source bytes read for the currently parsing string */ + gpr_uint32 strgot; + /* huffman decoding state */ + gpr_uint16 huff_state; + /* is the current string huffman encoded? */ + gpr_uint8 huff; + /* set by higher layers, used by grpc_chttp2_header_parser_parse to signal + it should append a metadata boundary at the end of frame */ + gpr_uint8 is_boundary; + gpr_uint8 is_eof; + + /* hpack table */ + grpc_chttp2_hptbl table; +}; + +void grpc_chttp2_hpack_parser_init(grpc_chttp2_hpack_parser *p, + grpc_mdctx *mdctx); +void grpc_chttp2_hpack_parser_destroy(grpc_chttp2_hpack_parser *p); + +void grpc_chttp2_hpack_parser_set_has_priority(grpc_chttp2_hpack_parser *p); + +/* returns 1 on success, 0 on error */ +int grpc_chttp2_hpack_parser_parse(grpc_chttp2_hpack_parser *p, + const gpr_uint8 *beg, const gpr_uint8 *end); + +/* wraps grpc_chttp2_hpack_parser_parse to provide a frame level parser for + the transport */ +grpc_chttp2_parse_error grpc_chttp2_header_parser_parse( + void *hpack_parser, grpc_chttp2_parse_state *state, gpr_slice slice, + int is_last); + +#endif /* __GRPC_INTERNAL_TRANSPORT_CHTTP2_HPACK_PARSER_H__ */ diff --git a/src/core/transport/chttp2/hpack_table.c b/src/core/transport/chttp2/hpack_table.c new file mode 100644 index 0000000000..8f2ebecfeb --- /dev/null +++ b/src/core/transport/chttp2/hpack_table.c @@ -0,0 +1,210 @@ +/* + * + * Copyright 2014, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "src/core/transport/chttp2/hpack_table.h" + +#include <assert.h> +#include <string.h> + +#include <grpc/support/log.h> +#include "src/core/support/murmur_hash.h" + +static struct { + const char *key; + const char *value; +} static_table[] = { + /* 0: */ {NULL, NULL}, + /* 1: */ {":authority", ""}, + /* 2: */ {":method", "GET"}, + /* 3: */ {":method", "POST"}, + /* 4: */ {":path", "/"}, + /* 5: */ {":path", "/index.html"}, + /* 6: */ {":scheme", "http"}, + /* 7: */ {":scheme", "https"}, + /* 8: */ {":status", "200"}, + /* 9: */ {":status", "204"}, + /* 10: */ {":status", "206"}, + /* 11: */ {":status", "304"}, + /* 12: */ {":status", "400"}, + /* 13: */ {":status", "404"}, + /* 14: */ {":status", "500"}, + /* 15: */ {"accept-charset", ""}, + /* 16: */ {"accept-encoding", "gzip, deflate"}, + /* 17: */ {"accept-language", ""}, + /* 18: */ {"accept-ranges", ""}, + /* 19: */ {"accept", ""}, + /* 20: */ {"access-control-allow-origin", ""}, + /* 21: */ {"age", ""}, + /* 22: */ {"allow", ""}, + /* 23: */ {"authorization", ""}, + /* 24: */ {"cache-control", ""}, + /* 25: */ {"content-disposition", ""}, + /* 26: */ {"content-encoding", ""}, + /* 27: */ {"content-language", ""}, + /* 28: */ {"content-length", ""}, + /* 29: */ {"content-location", ""}, + /* 30: */ {"content-range", ""}, + /* 31: */ {"content-type", ""}, + /* 32: */ {"cookie", ""}, + /* 33: */ {"date", ""}, + /* 34: */ {"etag", ""}, + /* 35: */ {"expect", ""}, + /* 36: */ {"expires", ""}, + /* 37: */ {"from", ""}, + /* 38: */ {"host", ""}, + /* 39: */ {"if-match", ""}, + /* 40: */ {"if-modified-since", ""}, + /* 41: */ {"if-none-match", ""}, + /* 42: */ {"if-range", ""}, + /* 43: */ {"if-unmodified-since", ""}, + /* 44: */ {"last-modified", ""}, + /* 45: */ {"link", ""}, + /* 46: */ {"location", ""}, + /* 47: */ {"max-forwards", ""}, + /* 48: */ {"proxy-authenticate", ""}, + /* 49: */ {"proxy-authorization", ""}, + /* 50: */ {"range", ""}, + /* 51: */ {"referer", ""}, + /* 52: */ {"refresh", ""}, + /* 53: */ {"retry-after", ""}, + /* 54: */ {"server", ""}, + /* 55: */ {"set-cookie", ""}, + /* 56: */ {"strict-transport-security", ""}, + /* 57: */ {"transfer-encoding", ""}, + /* 58: */ {"user-agent", ""}, + /* 59: */ {"vary", ""}, + /* 60: */ {"via", ""}, + /* 61: */ {"www-authenticate", ""}, +}; + +void grpc_chttp2_hptbl_init(grpc_chttp2_hptbl *tbl, grpc_mdctx *mdctx) { + size_t i; + + memset(tbl, 0, sizeof(*tbl)); + tbl->mdctx = mdctx; + tbl->max_bytes = GRPC_CHTTP2_INITIAL_HPACK_TABLE_SIZE; + for (i = 1; i <= GRPC_CHTTP2_LAST_STATIC_ENTRY; i++) { + tbl->static_ents[i - 1] = grpc_mdelem_from_strings( + mdctx, static_table[i].key, static_table[i].value); + } +} + +void grpc_chttp2_hptbl_destroy(grpc_chttp2_hptbl *tbl) { + size_t i; + for (i = 0; i < GRPC_CHTTP2_LAST_STATIC_ENTRY; i++) { + grpc_mdelem_unref(tbl->static_ents[i]); + } + for (i = 0; i < tbl->num_ents; i++) { + grpc_mdelem_unref( + tbl->ents[(tbl->first_ent + i) % GRPC_CHTTP2_MAX_TABLE_COUNT]); + } +} + +grpc_mdelem *grpc_chttp2_hptbl_lookup(const grpc_chttp2_hptbl *tbl, + gpr_uint32 index) { + /* Static table comes first, just return an entry from it */ + if (index <= GRPC_CHTTP2_LAST_STATIC_ENTRY) { + return tbl->static_ents[index - 1]; + } + /* Otherwise, find the value in the list of valid entries */ + index -= (GRPC_CHTTP2_LAST_STATIC_ENTRY + 1); + if (index < tbl->num_ents) { + gpr_uint32 offset = (tbl->num_ents - 1 - index + tbl->first_ent) % + GRPC_CHTTP2_MAX_TABLE_COUNT; + return tbl->ents[offset]; + } + /* Invalid entry: return error */ + return NULL; +} + +/* Evict one element from the table */ +static void evict1(grpc_chttp2_hptbl *tbl) { + grpc_mdelem *first_ent = tbl->ents[tbl->first_ent]; + tbl->mem_used -= GPR_SLICE_LENGTH(first_ent->key->slice) + + GPR_SLICE_LENGTH(first_ent->value->slice) + + GRPC_CHTTP2_HPACK_ENTRY_OVERHEAD; + tbl->first_ent = (tbl->first_ent + 1) % GRPC_CHTTP2_MAX_TABLE_COUNT; + tbl->num_ents--; + grpc_mdelem_unref(first_ent); +} + +void grpc_chttp2_hptbl_add(grpc_chttp2_hptbl *tbl, grpc_mdelem *md) { + /* determine how many bytes of buffer this entry represents */ + gpr_uint16 elem_bytes = GPR_SLICE_LENGTH(md->key->slice) + + GPR_SLICE_LENGTH(md->value->slice) + + GRPC_CHTTP2_HPACK_ENTRY_OVERHEAD; + + /* we can't add elements bigger than the max table size */ + assert(elem_bytes <= tbl->max_bytes); + + /* evict entries to ensure no overflow */ + while (elem_bytes > tbl->max_bytes - tbl->mem_used) { + evict1(tbl); + } + + /* copy the finalized entry in */ + tbl->ents[tbl->last_ent] = md; + + /* update accounting values */ + tbl->last_ent = (tbl->last_ent + 1) % GRPC_CHTTP2_MAX_TABLE_COUNT; + tbl->num_ents++; + tbl->mem_used += elem_bytes; +} + +grpc_chttp2_hptbl_find_result grpc_chttp2_hptbl_find( + const grpc_chttp2_hptbl *tbl, grpc_mdelem *md) { + grpc_chttp2_hptbl_find_result r = {0, 0}; + int i; + + /* See if the string is in the static table */ + for (i = 0; i < GRPC_CHTTP2_LAST_STATIC_ENTRY; i++) { + grpc_mdelem *ent = tbl->static_ents[i]; + if (md->key != ent->key) continue; + r.index = i + 1; + r.has_value = md->value == ent->value; + if (r.has_value) return r; + } + + /* Scan the dynamic table */ + for (i = 0; i < tbl->num_ents; i++) { + int idx = tbl->num_ents - i + GRPC_CHTTP2_LAST_STATIC_ENTRY; + grpc_mdelem *ent = + tbl->ents[(tbl->first_ent + i) % GRPC_CHTTP2_MAX_TABLE_COUNT]; + if (md->key != ent->key) continue; + r.index = idx; + r.has_value = md->value == ent->value; + if (r.has_value) return r; + } + + return r; +} diff --git a/src/core/transport/chttp2/hpack_table.h b/src/core/transport/chttp2/hpack_table.h new file mode 100644 index 0000000000..a3a07ad014 --- /dev/null +++ b/src/core/transport/chttp2/hpack_table.h @@ -0,0 +1,97 @@ +/* + * + * Copyright 2014, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef __GRPC_INTERNAL_TRANSPORT_CHTTP2_HPACK_TABLE_H__ +#define __GRPC_INTERNAL_TRANSPORT_CHTTP2_HPACK_TABLE_H__ + +#include "src/core/transport/metadata.h" +#include <grpc/support/port_platform.h> +#include <grpc/support/slice.h> + +/* HPACK header table */ + +/* last index in the static table */ +#define GRPC_CHTTP2_LAST_STATIC_ENTRY 61 + +/* Initial table size as per the spec */ +#define GRPC_CHTTP2_INITIAL_HPACK_TABLE_SIZE 4096 +/* Maximum table size that we'll use */ +#define GRPC_CHTTP2_MAX_HPACK_TABLE_SIZE GRPC_CHTTP2_INITIAL_HPACK_TABLE_SIZE +/* Per entry overhead bytes as per the spec */ +#define GRPC_CHTTP2_HPACK_ENTRY_OVERHEAD 32 +/* Maximum number of entries we could possibly fit in the table, given defined + overheads */ +#define GRPC_CHTTP2_MAX_TABLE_COUNT \ + ((GRPC_CHTTP2_MAX_HPACK_TABLE_SIZE + GRPC_CHTTP2_HPACK_ENTRY_OVERHEAD - 1) / \ + GRPC_CHTTP2_HPACK_ENTRY_OVERHEAD) + +/* hpack decoder table */ +typedef struct { + grpc_mdctx *mdctx; + /* the first used entry in ents */ + gpr_uint16 first_ent; + /* the last used entry in ents */ + gpr_uint16 last_ent; + /* how many entries are in the table */ + gpr_uint16 num_ents; + /* the amount of memory used by the table, according to the hpack algorithm */ + gpr_uint16 mem_used; + /* the max memory allowed to be used by the table, according to the hpack + algorithm */ + gpr_uint16 max_bytes; + /* a circular buffer of headers - this is stored in the opposite order to + what hpack specifies, in order to simplify table management a little... + meaning lookups need to SUBTRACT from the end position */ + grpc_mdelem *ents[GRPC_CHTTP2_MAX_TABLE_COUNT]; + grpc_mdelem *static_ents[GRPC_CHTTP2_LAST_STATIC_ENTRY]; +} grpc_chttp2_hptbl; + +/* initialize a hpack table */ +void grpc_chttp2_hptbl_init(grpc_chttp2_hptbl *tbl, grpc_mdctx *mdctx); +void grpc_chttp2_hptbl_destroy(grpc_chttp2_hptbl *tbl); + +/* lookup a table entry based on its hpack index */ +grpc_mdelem *grpc_chttp2_hptbl_lookup(const grpc_chttp2_hptbl *tbl, + gpr_uint32 index); +/* add a table entry to the index */ +void grpc_chttp2_hptbl_add(grpc_chttp2_hptbl *tbl, grpc_mdelem *md); +/* Find a key/value pair in the table... returns the index in the table of the + most similar entry, or 0 if the value was not found */ +typedef struct { + gpr_uint16 index; + gpr_uint8 has_value; +} grpc_chttp2_hptbl_find_result; +grpc_chttp2_hptbl_find_result grpc_chttp2_hptbl_find( + const grpc_chttp2_hptbl *tbl, grpc_mdelem *md); + +#endif /* __GRPC_INTERNAL_TRANSPORT_CHTTP2_HPACK_TABLE_H__ */ diff --git a/src/core/transport/chttp2/hpack_tables.txt b/src/core/transport/chttp2/hpack_tables.txt new file mode 100644 index 0000000000..08842a0267 --- /dev/null +++ b/src/core/transport/chttp2/hpack_tables.txt @@ -0,0 +1,66 @@ +Static table, from the spec: + +-------+-----------------------------+---------------+ + | Index | Header Name | Header Value | + +-------+-----------------------------+---------------+ + | 1 | :authority | | + | 2 | :method | GET | + | 3 | :method | POST | + | 4 | :path | / | + | 5 | :path | /index.html | + | 6 | :scheme | http | + | 7 | :scheme | https | + | 8 | :status | 200 | + | 9 | :status | 204 | + | 10 | :status | 206 | + | 11 | :status | 304 | + | 12 | :status | 400 | + | 13 | :status | 404 | + | 14 | :status | 500 | + | 15 | accept-charset | | + | 16 | accept-encoding | gzip, deflate | + | 17 | accept-language | | + | 18 | accept-ranges | | + | 19 | accept | | + | 20 | access-control-allow-origin | | + | 21 | age | | + | 22 | allow | | + | 23 | authorization | | + | 24 | cache-control | | + | 25 | content-disposition | | + | 26 | content-encoding | | + | 27 | content-language | | + | 28 | content-length | | + | 29 | content-location | | + | 30 | content-range | | + | 31 | content-type | | + | 32 | cookie | | + | 33 | date | | + | 34 | etag | | + | 35 | expect | | + | 36 | expires | | + | 37 | from | | + | 38 | host | | + | 39 | if-match | | + | 40 | if-modified-since | | + | 41 | if-none-match | | + | 42 | if-range | | + | 43 | if-unmodified-since | | + | 44 | last-modified | | + | 45 | link | | + | 46 | location | | + | 47 | max-forwards | | + | 48 | proxy-authenticate | | + | 49 | proxy-authorization | | + | 50 | range | | + | 51 | referer | | + | 52 | refresh | | + | 53 | retry-after | | + | 54 | server | | + | 55 | set-cookie | | + | 56 | strict-transport-security | | + | 57 | transfer-encoding | | + | 58 | user-agent | | + | 59 | vary | | + | 60 | via | | + | 61 | www-authenticate | | + +-------+-----------------------------+---------------+ diff --git a/src/core/transport/chttp2/http2_errors.h b/src/core/transport/chttp2/http2_errors.h new file mode 100644 index 0000000000..d065422c6f --- /dev/null +++ b/src/core/transport/chttp2/http2_errors.h @@ -0,0 +1,56 @@ +/* + * + * Copyright 2014, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef __GRPC_INTERNAL_TRANSPORT_CHTTP2_HTTP2_ERRORS_H__ +#define __GRPC_INTERNAL_TRANSPORT_CHTTP2_HTTP2_ERRORS_H__ + +/* error codes for RST_STREAM from http2 draft 14 section 7 */ +typedef enum { + GRPC_CHTTP2_NO_ERROR = 0x0, + GRPC_CHTTP2_PROTOCOL_ERROR = 0x1, + GRPC_CHTTP2_INTERNAL_ERROR = 0x2, + GRPC_CHTTP2_FLOW_CONTROL_ERROR = 0x3, + GRPC_CHTTP2_SETTINGS_TIMEOUT = 0x4, + GRPC_CHTTP2_STREAM_CLOSED = 0x5, + GRPC_CHTTP2_FRAME_SIZE_ERROR = 0x6, + GRPC_CHTTP2_REFUSED_STREAM = 0x7, + GRPC_CHTTP2_CANCEL = 0x8, + GRPC_CHTTP2_COMPRESSION_ERROR = 0x9, + GRPC_CHTTP2_CONNECT_ERROR = 0xa, + GRPC_CHTTP2_ENHANCE_YOUR_CALM = 0xb, + GRPC_CHTTP2_INADEQUATE_SECURITY = 0xc, + /* force use of a default clause */ + GRPC_CHTTP2__ERROR_DO_NOT_USE = -1 +} grpc_chttp2_error_code; + +#endif /* __GRPC_INTERNAL_TRANSPORT_CHTTP2_HTTP2_ERRORS_H__ */ diff --git a/src/core/transport/chttp2/status_conversion.c b/src/core/transport/chttp2/status_conversion.c new file mode 100644 index 0000000000..7bd85e8b29 --- /dev/null +++ b/src/core/transport/chttp2/status_conversion.c @@ -0,0 +1,109 @@ +/* + * + * Copyright 2014, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "src/core/transport/chttp2/status_conversion.h" + +int grpc_chttp2_grpc_status_to_http2_error(grpc_status_code status) { + switch (status) { + case GRPC_STATUS_OK: + return GRPC_CHTTP2_NO_ERROR; + case GRPC_STATUS_CANCELLED: + return GRPC_CHTTP2_CANCEL; + case GRPC_STATUS_RESOURCE_EXHAUSTED: + return GRPC_CHTTP2_ENHANCE_YOUR_CALM; + case GRPC_STATUS_PERMISSION_DENIED: + return GRPC_CHTTP2_INADEQUATE_SECURITY; + case GRPC_STATUS_UNAVAILABLE: + return GRPC_CHTTP2_REFUSED_STREAM; + default: + return GRPC_CHTTP2_INTERNAL_ERROR; + } +} + +grpc_status_code grpc_chttp2_http2_error_to_grpc_status( + grpc_chttp2_error_code error) { + switch (error) { + case GRPC_CHTTP2_NO_ERROR: + /* should never be received */ + return GRPC_STATUS_INTERNAL; + case GRPC_CHTTP2_CANCEL: + return GRPC_STATUS_CANCELLED; + case GRPC_CHTTP2_ENHANCE_YOUR_CALM: + return GRPC_STATUS_RESOURCE_EXHAUSTED; + case GRPC_CHTTP2_INADEQUATE_SECURITY: + return GRPC_STATUS_PERMISSION_DENIED; + case GRPC_CHTTP2_REFUSED_STREAM: + return GRPC_STATUS_UNAVAILABLE; + default: + return GRPC_STATUS_INTERNAL; + } +} + +grpc_status_code grpc_chttp2_http2_status_to_grpc_status(int status) { + switch (status) { + /* these HTTP2 status codes are called out explicitly in status.proto */ + case 200: + return GRPC_STATUS_OK; + case 400: + return GRPC_STATUS_INVALID_ARGUMENT; + case 401: + return GRPC_STATUS_UNAUTHENTICATED; + case 403: + return GRPC_STATUS_PERMISSION_DENIED; + case 404: + return GRPC_STATUS_NOT_FOUND; + case 409: + return GRPC_STATUS_ABORTED; + case 412: + return GRPC_STATUS_FAILED_PRECONDITION; + case 429: + return GRPC_STATUS_RESOURCE_EXHAUSTED; + case 499: + return GRPC_STATUS_CANCELLED; + case 500: + return GRPC_STATUS_UNKNOWN; + case 501: + return GRPC_STATUS_UNIMPLEMENTED; + case 503: + return GRPC_STATUS_UNAVAILABLE; + case 504: + return GRPC_STATUS_DEADLINE_EXCEEDED; + /* everything else is unknown */ + default: + return GRPC_STATUS_UNKNOWN; + } +} + +int grpc_chttp2_grpc_status_to_http2_status(grpc_status_code status) { + return 200; +} diff --git a/src/core/transport/chttp2/status_conversion.h b/src/core/transport/chttp2/status_conversion.h new file mode 100644 index 0000000000..ae9e7f2ca3 --- /dev/null +++ b/src/core/transport/chttp2/status_conversion.h @@ -0,0 +1,50 @@ +/* + * + * Copyright 2014, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef __GRPC_INTERNAL_TRANSPORT_CHTTP2_STATUS_CONVERSION_H__ +#define __GRPC_INTERNAL_TRANSPORT_CHTTP2_STATUS_CONVERSION_H__ + +#include <grpc/grpc.h> +#include "src/core/transport/chttp2/http2_errors.h" + +/* Conversion of grpc status codes to http2 error codes (for RST_STREAM) */ +grpc_chttp2_error_code grpc_chttp2_grpc_status_to_http2_error( + grpc_status_code status); +grpc_status_code grpc_chttp2_http2_error_to_grpc_status( + grpc_chttp2_error_code error); + +/* Conversion of HTTP status codes (:status) to grpc status codes */ +grpc_status_code grpc_chttp2_http2_status_to_grpc_status(int status); +int grpc_chttp2_grpc_status_to_http2_status(grpc_status_code status); + +#endif /* __GRPC_INTERNAL_TRANSPORT_CHTTP2_STATUS_CONVERSION_H__ */ diff --git a/src/core/transport/chttp2/stream_encoder.c b/src/core/transport/chttp2/stream_encoder.c new file mode 100644 index 0000000000..d46366d8b2 --- /dev/null +++ b/src/core/transport/chttp2/stream_encoder.c @@ -0,0 +1,553 @@ +/* + * + * Copyright 2014, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "src/core/transport/chttp2/stream_encoder.h" + +#include <assert.h> +#include <string.h> + +#include <grpc/support/log.h> +#include <grpc/support/useful.h> +#include "src/core/transport/chttp2/hpack_table.h" +#include "src/core/transport/chttp2/timeout_encoding.h" +#include "src/core/transport/chttp2/varint.h" + +#define HASH_FRAGMENT_1(x) ((x)&255) +#define HASH_FRAGMENT_2(x) ((x >> 8) & 255) +#define HASH_FRAGMENT_3(x) ((x >> 16) & 255) +#define HASH_FRAGMENT_4(x) ((x >> 24) & 255) + +/* if the probability of this item being seen again is < 1/x then don't add + it to the table */ +#define ONE_ON_ADD_PROBABILITY 128 +/* don't consider adding anything bigger than this to the hpack table */ +#define MAX_DECODER_SPACE_USAGE 512 + +/* what kind of frame our we encoding? */ +typedef enum { HEADER, DATA, NONE } frame_type; + +typedef struct { + frame_type cur_frame_type; + /* number of bytes in 'output' when we started the frame - used to calculate + frame length */ + size_t output_length_at_start_of_frame; + /* index (in output) of the header for the current frame */ + size_t header_idx; + /* was the last frame emitted a header? (if yes, we'll need a CONTINUATION */ + gpr_uint8 last_was_header; + /* output stream id */ + gpr_uint32 stream_id; + /* number of flow controlled bytes written */ + gpr_uint32 output_size; + gpr_slice_buffer *output; +} framer_state; + +/* fills p (which is expected to be 9 bytes long) with a data frame header */ +static void fill_header(gpr_uint8 *p, gpr_uint8 type, gpr_uint32 id, + gpr_uint32 len, gpr_uint8 flags) { + *p++ = len >> 16; + *p++ = len >> 8; + *p++ = len; + *p++ = type; + *p++ = flags; + *p++ = id >> 24; + *p++ = id >> 16; + *p++ = id >> 8; + *p++ = id; +} + +/* finish a frame - fill in the previously reserved header */ +static void finish_frame(framer_state *st, int is_header_boundary, + int is_last_in_stream) { + gpr_uint8 type = 0xff; + switch (st->cur_frame_type) { + case HEADER: + type = st->last_was_header ? GRPC_CHTTP2_FRAME_CONTINUATION + : GRPC_CHTTP2_FRAME_HEADER; + st->last_was_header = 1; + break; + case DATA: + type = GRPC_CHTTP2_FRAME_DATA; + st->last_was_header = 0; + is_header_boundary = 0; + break; + case NONE: + return; + } + fill_header(GPR_SLICE_START_PTR(st->output->slices[st->header_idx]), type, + st->stream_id, + st->output->length - st->output_length_at_start_of_frame, + (is_last_in_stream ? GRPC_CHTTP2_DATA_FLAG_END_STREAM : 0) | + (is_header_boundary ? GRPC_CHTTP2_DATA_FLAG_END_HEADERS : 0)); + st->cur_frame_type = NONE; +} + +/* begin a new frame: reserve off header space, remember how many bytes we'd + output before beginning */ +static void begin_frame(framer_state *st, frame_type type) { + GPR_ASSERT(type != NONE); + GPR_ASSERT(st->cur_frame_type == NONE); + st->cur_frame_type = type; + st->header_idx = + gpr_slice_buffer_add_indexed(st->output, gpr_slice_malloc(9)); + st->output_length_at_start_of_frame = st->output->length; +} + +/* make sure that the current frame is of the type desired, and has sufficient + space to add at least about_to_add bytes -- finishes the current frame if + needed */ +static void ensure_frame_type(framer_state *st, frame_type type, + int need_bytes) { + if (st->cur_frame_type == type && + st->output->length - st->output_length_at_start_of_frame + need_bytes <= + GRPC_CHTTP2_MAX_PAYLOAD_LENGTH) { + return; + } + finish_frame(st, type != HEADER, 0); + begin_frame(st, type); +} + +/* increment a filter count, halve all counts if one element reaches max */ +static void inc_filter(gpr_uint8 idx, gpr_uint32 *sum, gpr_uint8 *elems) { + elems[idx]++; + if (elems[idx] < 255) { + (*sum)++; + } else { + int i; + *sum = 0; + for (i = 0; i < GRPC_CHTTP2_HPACKC_NUM_FILTERS; i++) { + elems[i] /= 2; + (*sum) += elems[i]; + } + } +} + +static void add_header_data(framer_state *st, gpr_slice slice) { + size_t len = GPR_SLICE_LENGTH(slice); + size_t remaining; + if (len == 0) return; + ensure_frame_type(st, HEADER, 1); + remaining = GRPC_CHTTP2_MAX_PAYLOAD_LENGTH + + st->output_length_at_start_of_frame - st->output->length; + if (len <= remaining) { + gpr_slice_buffer_add(st->output, slice); + } else { + gpr_slice_buffer_add(st->output, gpr_slice_split_head(&slice, remaining)); + add_header_data(st, slice); + } +} + +static gpr_uint8 *add_tiny_header_data(framer_state *st, int len) { + ensure_frame_type(st, HEADER, len); + return gpr_slice_buffer_tiny_add(st->output, len); +} + +static void add_elem(grpc_chttp2_hpack_compressor *c, grpc_mdelem *elem) { + gpr_uint32 key_hash = elem->key->hash; + gpr_uint32 elem_hash = key_hash ^ elem->value->hash; + gpr_uint32 new_index = c->tail_remote_index + c->table_elems + 1; + gpr_uint32 elem_size = 32 + GPR_SLICE_LENGTH(elem->key->slice) + + GPR_SLICE_LENGTH(elem->value->slice); + int drop_ref; + + /* Reserve space for this element in the remote table: if this overflows + the current table, drop elements until it fits, matching the decompressor + algorithm */ + /* TODO(ctiller): constant */ + while (c->table_size + elem_size > 4096) { + c->tail_remote_index++; + GPR_ASSERT(c->tail_remote_index > 0); + GPR_ASSERT(c->table_size >= + c->table_elem_size[c->tail_remote_index % + GRPC_CHTTP2_HPACKC_MAX_TABLE_ELEMS]); + GPR_ASSERT(c->table_elems > 0); + c->table_size -= c->table_elem_size[c->tail_remote_index % + GRPC_CHTTP2_HPACKC_MAX_TABLE_ELEMS]; + c->table_elems--; + } + GPR_ASSERT(c->table_elems < GRPC_CHTTP2_HPACKC_MAX_TABLE_ELEMS); + c->table_elem_size[new_index % GRPC_CHTTP2_HPACKC_MAX_TABLE_ELEMS] = + elem_size; + c->table_size += elem_size; + c->table_elems++; + + /* Store this element into {entries,indices}_elem */ + if (c->entries_elems[HASH_FRAGMENT_2(elem_hash)] == elem) { + /* already there: update with new index */ + c->indices_elems[HASH_FRAGMENT_2(elem_hash)] = new_index; + drop_ref = 1; + } else if (c->entries_elems[HASH_FRAGMENT_3(elem_hash)] == elem) { + /* already there (cuckoo): update with new index */ + c->indices_elems[HASH_FRAGMENT_3(elem_hash)] = new_index; + drop_ref = 1; + } else if (c->entries_elems[HASH_FRAGMENT_2(elem_hash)] == NULL) { + /* not there, but a free element: add */ + c->entries_elems[HASH_FRAGMENT_2(elem_hash)] = elem; + c->indices_elems[HASH_FRAGMENT_2(elem_hash)] = new_index; + drop_ref = 0; + } else if (c->entries_elems[HASH_FRAGMENT_3(elem_hash)] == NULL) { + /* not there (cuckoo), but a free element: add */ + c->entries_elems[HASH_FRAGMENT_3(elem_hash)] = elem; + c->indices_elems[HASH_FRAGMENT_3(elem_hash)] = new_index; + drop_ref = 0; + } else if (c->indices_elems[HASH_FRAGMENT_2(elem_hash)] < + c->indices_elems[HASH_FRAGMENT_3(elem_hash)]) { + /* not there: replace oldest */ + grpc_mdelem_unref(c->entries_elems[HASH_FRAGMENT_2(elem_hash)]); + c->entries_elems[HASH_FRAGMENT_2(elem_hash)] = elem; + c->indices_elems[HASH_FRAGMENT_2(elem_hash)] = new_index; + drop_ref = 0; + } else { + /* not there: replace oldest */ + grpc_mdelem_unref(c->entries_elems[HASH_FRAGMENT_3(elem_hash)]); + c->entries_elems[HASH_FRAGMENT_3(elem_hash)] = elem; + c->indices_elems[HASH_FRAGMENT_3(elem_hash)] = new_index; + drop_ref = 0; + } + + /* do exactly the same for the key (so we can find by that again too) */ + + if (c->entries_keys[HASH_FRAGMENT_2(key_hash)] == elem->key) { + c->indices_keys[HASH_FRAGMENT_2(key_hash)] = new_index; + } else if (c->entries_keys[HASH_FRAGMENT_3(key_hash)] == elem->key) { + c->indices_keys[HASH_FRAGMENT_3(key_hash)] = new_index; + } else if (c->entries_keys[HASH_FRAGMENT_2(key_hash)] == NULL) { + c->entries_keys[HASH_FRAGMENT_2(key_hash)] = grpc_mdstr_ref(elem->key); + c->indices_keys[HASH_FRAGMENT_2(key_hash)] = new_index; + } else if (c->entries_keys[HASH_FRAGMENT_3(key_hash)] == NULL) { + c->entries_keys[HASH_FRAGMENT_3(key_hash)] = grpc_mdstr_ref(elem->key); + c->indices_keys[HASH_FRAGMENT_3(key_hash)] = new_index; + } else if (c->indices_keys[HASH_FRAGMENT_2(key_hash)] < + c->indices_keys[HASH_FRAGMENT_3(key_hash)]) { + grpc_mdstr_unref(c->entries_keys[HASH_FRAGMENT_2(key_hash)]); + c->entries_keys[HASH_FRAGMENT_2(key_hash)] = grpc_mdstr_ref(elem->key); + c->indices_keys[HASH_FRAGMENT_2(key_hash)] = new_index; + } else { + grpc_mdstr_unref(c->entries_keys[HASH_FRAGMENT_3(key_hash)]); + c->entries_keys[HASH_FRAGMENT_3(key_hash)] = grpc_mdstr_ref(elem->key); + c->indices_keys[HASH_FRAGMENT_3(key_hash)] = new_index; + } + + if (drop_ref) { + grpc_mdelem_unref(elem); + } +} + +static void emit_indexed(grpc_chttp2_hpack_compressor *c, gpr_uint32 index, + framer_state *st) { + int len = GRPC_CHTTP2_VARINT_LENGTH(index, 1); + GRPC_CHTTP2_WRITE_VARINT(index, 1, 0x80, add_tiny_header_data(st, len), len); +} + +static void emit_lithdr_incidx(grpc_chttp2_hpack_compressor *c, + gpr_uint32 key_index, grpc_mdstr *value, + framer_state *st) { + int len_pfx = GRPC_CHTTP2_VARINT_LENGTH(key_index, 2); + int len_val = GPR_SLICE_LENGTH(value->slice); + int len_val_len = GRPC_CHTTP2_VARINT_LENGTH(len_val, 1); + GRPC_CHTTP2_WRITE_VARINT(key_index, 2, 0x40, + add_tiny_header_data(st, len_pfx), len_pfx); + GRPC_CHTTP2_WRITE_VARINT(len_val, 1, 0x00, + add_tiny_header_data(st, len_val_len), len_val_len); + add_header_data(st, gpr_slice_ref(value->slice)); +} + +static void emit_lithdr_noidx(grpc_chttp2_hpack_compressor *c, + gpr_uint32 key_index, grpc_mdstr *value, + framer_state *st) { + int len_pfx = GRPC_CHTTP2_VARINT_LENGTH(key_index, 4); + int len_val = GPR_SLICE_LENGTH(value->slice); + int len_val_len = GRPC_CHTTP2_VARINT_LENGTH(len_val, 1); + GRPC_CHTTP2_WRITE_VARINT(key_index, 4, 0x00, + add_tiny_header_data(st, len_pfx), len_pfx); + GRPC_CHTTP2_WRITE_VARINT(len_val, 1, 0x00, + add_tiny_header_data(st, len_val_len), len_val_len); + add_header_data(st, gpr_slice_ref(value->slice)); +} + +static void emit_lithdr_incidx_v(grpc_chttp2_hpack_compressor *c, + grpc_mdstr *key, grpc_mdstr *value, + framer_state *st) { + int len_key = GPR_SLICE_LENGTH(key->slice); + int len_val = GPR_SLICE_LENGTH(value->slice); + int len_key_len = GRPC_CHTTP2_VARINT_LENGTH(len_key, 1); + int len_val_len = GRPC_CHTTP2_VARINT_LENGTH(len_val, 1); + *add_tiny_header_data(st, 1) = 0x40; + GRPC_CHTTP2_WRITE_VARINT(len_key, 1, 0x00, + add_tiny_header_data(st, len_key_len), len_key_len); + add_header_data(st, gpr_slice_ref(key->slice)); + GRPC_CHTTP2_WRITE_VARINT(len_val, 1, 0x00, + add_tiny_header_data(st, len_val_len), len_val_len); + add_header_data(st, gpr_slice_ref(value->slice)); +} + +static void emit_lithdr_noidx_v(grpc_chttp2_hpack_compressor *c, + grpc_mdstr *key, grpc_mdstr *value, + framer_state *st) { + int len_key = GPR_SLICE_LENGTH(key->slice); + int len_val = GPR_SLICE_LENGTH(value->slice); + int len_key_len = GRPC_CHTTP2_VARINT_LENGTH(len_key, 1); + int len_val_len = GRPC_CHTTP2_VARINT_LENGTH(len_val, 1); + *add_tiny_header_data(st, 1) = 0x00; + GRPC_CHTTP2_WRITE_VARINT(len_key, 1, 0x00, + add_tiny_header_data(st, len_key_len), len_key_len); + add_header_data(st, gpr_slice_ref(key->slice)); + GRPC_CHTTP2_WRITE_VARINT(len_val, 1, 0x00, + add_tiny_header_data(st, len_val_len), len_val_len); + add_header_data(st, gpr_slice_ref(value->slice)); +} + +static gpr_uint32 dynidx(grpc_chttp2_hpack_compressor *c, gpr_uint32 index) { + return 1 + GRPC_CHTTP2_LAST_STATIC_ENTRY + c->tail_remote_index + + c->table_elems - index; +} + +/* encode an mdelem, taking ownership of it */ +static void hpack_enc(grpc_chttp2_hpack_compressor *c, grpc_mdelem *elem, + framer_state *st) { + gpr_uint32 key_hash = elem->key->hash; + gpr_uint32 elem_hash = key_hash ^ elem->value->hash; + size_t decoder_space_usage; + int should_add_elem; + + inc_filter(HASH_FRAGMENT_1(elem_hash), &c->filter_elems_sum, c->filter_elems); + + /* is this elem currently in the decoders table? */ + + if (c->entries_elems[HASH_FRAGMENT_2(elem_hash)] == elem && + c->indices_elems[HASH_FRAGMENT_2(elem_hash)] > c->tail_remote_index) { + /* HIT: complete element (first cuckoo hash) */ + emit_indexed(c, dynidx(c, c->indices_elems[HASH_FRAGMENT_2(elem_hash)]), + st); + grpc_mdelem_unref(elem); + return; + } + + if (c->entries_elems[HASH_FRAGMENT_3(elem_hash)] == elem && + c->indices_elems[HASH_FRAGMENT_3(elem_hash)] > c->tail_remote_index) { + /* HIT: complete element (second cuckoo hash) */ + emit_indexed(c, dynidx(c, c->indices_elems[HASH_FRAGMENT_3(elem_hash)]), + st); + grpc_mdelem_unref(elem); + return; + } + + /* should this elem be in the table? */ + decoder_space_usage = 32 + GPR_SLICE_LENGTH(elem->key->slice) + + GPR_SLICE_LENGTH(elem->value->slice); + should_add_elem = decoder_space_usage < MAX_DECODER_SPACE_USAGE && + c->filter_elems[HASH_FRAGMENT_1(elem_hash)] >= + c->filter_elems_sum / ONE_ON_ADD_PROBABILITY; + + /* no hits for the elem... maybe there's a key? */ + + if (c->entries_keys[HASH_FRAGMENT_2(key_hash)] == elem->key && + c->indices_keys[HASH_FRAGMENT_2(key_hash)] > c->tail_remote_index) { + /* HIT: key (first cuckoo hash) */ + if (should_add_elem) { + emit_lithdr_incidx(c, + dynidx(c, c->indices_keys[HASH_FRAGMENT_2(key_hash)]), + elem->value, st); + add_elem(c, elem); + } else { + emit_lithdr_noidx(c, + dynidx(c, c->indices_keys[HASH_FRAGMENT_2(key_hash)]), + elem->value, st); + grpc_mdelem_unref(elem); + } + return; + } + + if (c->entries_keys[HASH_FRAGMENT_3(key_hash)] == elem->key && + c->indices_keys[HASH_FRAGMENT_3(key_hash)] > c->tail_remote_index) { + /* HIT: key (first cuckoo hash) */ + if (should_add_elem) { + emit_lithdr_incidx(c, + dynidx(c, c->indices_keys[HASH_FRAGMENT_3(key_hash)]), + elem->value, st); + add_elem(c, elem); + } else { + emit_lithdr_noidx(c, + dynidx(c, c->indices_keys[HASH_FRAGMENT_3(key_hash)]), + elem->value, st); + grpc_mdelem_unref(elem); + } + return; + } + + /* no elem, key in the table... fall back to literal emission */ + + if (should_add_elem) { + emit_lithdr_incidx_v(c, elem->key, elem->value, st); + add_elem(c, elem); + } else { + emit_lithdr_noidx_v(c, elem->key, elem->value, st); + grpc_mdelem_unref(elem); + } +} + +#define STRLEN_LIT(x) (sizeof(x) - 1) +#define TIMEOUT_KEY "grpc-timeout" + +static void deadline_enc(grpc_chttp2_hpack_compressor *c, gpr_timespec deadline, + framer_state *st) { + char timeout_str[32]; + grpc_chttp2_encode_timeout(gpr_time_sub(deadline, gpr_now()), timeout_str); + hpack_enc(c, grpc_mdelem_from_metadata_strings( + c->mdctx, grpc_mdstr_ref(c->timeout_key_str), + grpc_mdstr_from_string(c->mdctx, timeout_str)), + st); +} + +gpr_slice grpc_chttp2_data_frame_create_empty_close(gpr_uint32 id) { + gpr_slice slice = gpr_slice_malloc(9); + fill_header(GPR_SLICE_START_PTR(slice), GRPC_CHTTP2_FRAME_DATA, id, 0, 1); + return slice; +} + +void grpc_chttp2_hpack_compressor_init(grpc_chttp2_hpack_compressor *c, + grpc_mdctx *ctx) { + memset(c, 0, sizeof(*c)); + c->mdctx = ctx; + c->timeout_key_str = grpc_mdstr_from_string(ctx, "grpc-timeout"); +} + +void grpc_chttp2_hpack_compressor_destroy(grpc_chttp2_hpack_compressor *c) { + int i; + for (i = 0; i < GRPC_CHTTP2_HPACKC_NUM_VALUES; i++) { + if (c->entries_keys[i]) grpc_mdstr_unref(c->entries_keys[i]); + if (c->entries_elems[i]) grpc_mdelem_unref(c->entries_elems[i]); + } + grpc_mdstr_unref(c->timeout_key_str); +} + +gpr_uint32 grpc_chttp2_encode_some(grpc_stream_op *ops, size_t *ops_count, + int eof, gpr_slice_buffer *output, + gpr_uint32 max_bytes, gpr_uint32 stream_id, + grpc_chttp2_hpack_compressor *compressor) { + framer_state st; + gpr_slice slice; + grpc_stream_op *op; + gpr_uint32 max_take_size; + gpr_uint32 curop = 0; + gpr_uint32 nops = *ops_count; + gpr_uint8 *p; + + GPR_ASSERT(stream_id != 0); + + st.cur_frame_type = NONE; + st.last_was_header = 0; + st.stream_id = stream_id; + st.output = output; + st.output_size = 0; + + while (curop < nops) { + GPR_ASSERT(st.output_size <= max_bytes); + op = &ops[curop]; + switch (op->type) { + case GRPC_NO_OP: + curop++; + break; + case GRPC_OP_FLOW_CTL_CB: + op->data.flow_ctl_cb.cb(op->data.flow_ctl_cb.arg, GRPC_OP_OK); + curop++; + break; + case GRPC_OP_METADATA: + hpack_enc(compressor, op->data.metadata, &st); + curop++; + break; + case GRPC_OP_DEADLINE: + deadline_enc(compressor, op->data.deadline, &st); + curop++; + break; + case GRPC_OP_METADATA_BOUNDARY: + ensure_frame_type(&st, HEADER, 0); + finish_frame(&st, 1, 0); + st.last_was_header = 0; /* force a new header frame */ + curop++; + break; + case GRPC_OP_BEGIN_MESSAGE: + /* begin op: for now we just convert the op to a slice and fall + through - this lets us reuse the slice framing code below */ + slice = gpr_slice_malloc(5); + p = GPR_SLICE_START_PTR(slice); + p[0] = 0; + p[1] = op->data.begin_message.length >> 24; + p[2] = op->data.begin_message.length >> 16; + p[3] = op->data.begin_message.length >> 8; + p[4] = op->data.begin_message.length; + op->type = GRPC_OP_SLICE; + op->data.slice = slice; + /* fallthrough */ + case GRPC_OP_SLICE: + slice = op->data.slice; + if (!GPR_SLICE_LENGTH(slice)) { + curop++; + break; + } + if (st.output_size == max_bytes) { + goto exit_loop; + } + if (st.cur_frame_type == DATA && + st.output->length - st.output_length_at_start_of_frame == + GRPC_CHTTP2_MAX_PAYLOAD_LENGTH) { + finish_frame(&st, 0, 0); + } + ensure_frame_type(&st, DATA, 1); + max_take_size = + GPR_MIN(max_bytes - st.output_size, + GRPC_CHTTP2_MAX_PAYLOAD_LENGTH + + st.output_length_at_start_of_frame - st.output->length); + if (GPR_SLICE_LENGTH(slice) > max_take_size) { + slice = gpr_slice_split_head(&op->data.slice, max_take_size); + } else { + /* consume this op immediately */ + curop++; + } + st.output_size += GPR_SLICE_LENGTH(slice); + gpr_slice_buffer_add(output, slice); + break; + } + } +exit_loop: + if (eof && st.cur_frame_type == NONE) { + begin_frame(&st, DATA); + } + finish_frame(&st, 1, eof && curop == nops); + + nops -= curop; + *ops_count = nops; + memmove(ops, ops + curop, nops * sizeof(grpc_stream_op)); + + return st.output_size; +} diff --git a/src/core/transport/chttp2/stream_encoder.h b/src/core/transport/chttp2/stream_encoder.h new file mode 100644 index 0000000000..dad64697a5 --- /dev/null +++ b/src/core/transport/chttp2/stream_encoder.h @@ -0,0 +1,86 @@ +/* + * + * Copyright 2014, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef __GRPC_INTERNAL_TRANSPORT_CHTTP2_STREAM_ENCODER_H__ +#define __GRPC_INTERNAL_TRANSPORT_CHTTP2_STREAM_ENCODER_H__ + +#include "src/core/transport/chttp2/frame.h" +#include "src/core/transport/metadata.h" +#include "src/core/transport/stream_op.h" +#include <grpc/support/port_platform.h> +#include <grpc/support/slice.h> +#include <grpc/support/slice_buffer.h> + +#define GRPC_CHTTP2_HPACKC_NUM_FILTERS 256 +#define GRPC_CHTTP2_HPACKC_NUM_VALUES 256 +#define GRPC_CHTTP2_HPACKC_MAX_TABLE_ELEMS (4096 / 32) + +typedef struct { + gpr_uint32 filter_elems_sum; + /* one before the lowest usable table index */ + gpr_uint32 tail_remote_index; + gpr_uint16 table_size; + gpr_uint16 table_elems; + + /* filter tables for elems: this tables provides an approximate + popularity count for particular hashes, and are used to determine whether + a new literal should be added to the compression table or not. + They track a single integer that counts how often a particular value has + been seen. When that count reaches max (255), all values are halved. */ + gpr_uint8 filter_elems[GRPC_CHTTP2_HPACKC_NUM_FILTERS]; + + /* metadata context */ + grpc_mdctx *mdctx; + /* the string 'grpc-timeout' */ + grpc_mdstr *timeout_key_str; + + /* entry tables for keys & elems: these tables track values that have been + seen and *may* be in the decompressor table */ + grpc_mdstr *entries_keys[GRPC_CHTTP2_HPACKC_NUM_VALUES]; + grpc_mdelem *entries_elems[GRPC_CHTTP2_HPACKC_NUM_VALUES]; + gpr_uint32 indices_keys[GRPC_CHTTP2_HPACKC_NUM_VALUES]; + gpr_uint32 indices_elems[GRPC_CHTTP2_HPACKC_NUM_VALUES]; + + gpr_uint16 table_elem_size[GRPC_CHTTP2_HPACKC_MAX_TABLE_ELEMS]; +} grpc_chttp2_hpack_compressor; + +void grpc_chttp2_hpack_compressor_init(grpc_chttp2_hpack_compressor *c, + grpc_mdctx *mdctx); +void grpc_chttp2_hpack_compressor_destroy(grpc_chttp2_hpack_compressor *c); + +gpr_uint32 grpc_chttp2_encode_some(grpc_stream_op *ops, size_t *ops_count, + int eof, gpr_slice_buffer *output, + gpr_uint32 max_bytes, gpr_uint32 stream_id, + grpc_chttp2_hpack_compressor *compressor); + +#endif /* __GRPC_INTERNAL_TRANSPORT_CHTTP2_STREAM_ENCODER_H__ */ diff --git a/src/core/transport/chttp2/stream_map.c b/src/core/transport/chttp2/stream_map.c new file mode 100644 index 0000000000..9ac3a4750d --- /dev/null +++ b/src/core/transport/chttp2/stream_map.c @@ -0,0 +1,154 @@ +/* + * + * Copyright 2014, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "src/core/transport/chttp2/stream_map.h" +#include <grpc/support/alloc.h> +#include <grpc/support/log.h> + +void grpc_chttp2_stream_map_init(grpc_chttp2_stream_map *map, + size_t initial_capacity) { + GPR_ASSERT(initial_capacity > 1); + map->keys = gpr_malloc(sizeof(gpr_uint32) * initial_capacity); + map->values = gpr_malloc(sizeof(void *) * initial_capacity); + map->count = 0; + map->free = 0; + map->capacity = initial_capacity; +} + +void grpc_chttp2_stream_map_destroy(grpc_chttp2_stream_map *map) { + gpr_free(map->keys); + gpr_free(map->values); +} + +static size_t compact(gpr_uint32 *keys, void **values, size_t count) { + size_t i, out; + + for (i = 0, out = 0; i < count; i++) { + if (values[i]) { + keys[out] = keys[i]; + values[out] = values[i]; + out++; + } + } + + return out; +} + +void grpc_chttp2_stream_map_add(grpc_chttp2_stream_map *map, gpr_uint32 key, + void *value) { + size_t count = map->count; + size_t capacity = map->capacity; + gpr_uint32 *keys = map->keys; + void **values = map->values; + + GPR_ASSERT(count == 0 || keys[count - 1] < key); + GPR_ASSERT(value); + + if (count == capacity) { + if (map->free > capacity / 4) { + count = compact(keys, values, count); + map->free = 0; + } else { + /* resize when less than 25% of the table is free, because compaction + won't help much */ + map->capacity = capacity = 3 * capacity / 2; + map->keys = keys = gpr_realloc(keys, capacity * sizeof(gpr_uint32)); + map->values = values = gpr_realloc(values, capacity * sizeof(void *)); + } + } + + keys[count] = key; + values[count] = value; + map->count = count + 1; +} + +static void **find(grpc_chttp2_stream_map *map, gpr_uint32 key) { + size_t min_idx = 0; + size_t max_idx = map->count; + size_t mid_idx; + gpr_uint32 *keys = map->keys; + void **values = map->values; + gpr_uint32 mid_key; + + if (max_idx == 0) return NULL; + + while (min_idx < max_idx) { + /* find the midpoint, avoiding overflow */ + mid_idx = min_idx + ((max_idx - min_idx) / 2); + mid_key = keys[mid_idx]; + + if (mid_key < key) { + min_idx = mid_idx + 1; + } else if (mid_key > key) { + max_idx = mid_idx; + } else /* mid_key == key */ { + return &values[mid_idx]; + } + } + + return NULL; +} + +void *grpc_chttp2_stream_map_delete(grpc_chttp2_stream_map *map, + gpr_uint32 key) { + void **pvalue = find(map, key); + void *out = NULL; + if (pvalue != NULL) { + out = *pvalue; + *pvalue = NULL; + map->free += (out != NULL); + } + return out; +} + +void *grpc_chttp2_stream_map_find(grpc_chttp2_stream_map *map, gpr_uint32 key) { + void **pvalue = find(map, key); + return pvalue != NULL ? *pvalue : NULL; +} + +size_t grpc_chttp2_stream_map_size(grpc_chttp2_stream_map *map) { + return map->count - map->free; +} + +void grpc_chttp2_stream_map_for_each(grpc_chttp2_stream_map *map, + void (*f)(void *user_data, gpr_uint32 key, + void *value), + void *user_data) { + size_t i; + + for (i = 0; i < map->count; i++) { + if (map->values[i]) { + f(user_data, map->keys[i], map->values[i]); + } + } +} diff --git a/src/core/transport/chttp2/stream_map.h b/src/core/transport/chttp2/stream_map.h new file mode 100644 index 0000000000..caaee30676 --- /dev/null +++ b/src/core/transport/chttp2/stream_map.h @@ -0,0 +1,81 @@ +/* + * + * Copyright 2014, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef __GRPC_INTERNAL_TRANSPORT_CHTTP2_STREAM_MAP_H__ +#define __GRPC_INTERNAL_TRANSPORT_CHTTP2_STREAM_MAP_H__ + +#include <grpc/support/port_platform.h> + +#include <stddef.h> + +/* Data structure to map a gpr_uint32 to a data object (represented by a void*) + + Represented as a sorted array of keys, and a corresponding array of values. + Lookups are performed with binary search. + Adds are restricted to strictly higher keys than previously seen (this is + guaranteed by http2). */ +typedef struct { + gpr_uint32 *keys; + void **values; + size_t count; + size_t free; + size_t capacity; +} grpc_chttp2_stream_map; + +void grpc_chttp2_stream_map_init(grpc_chttp2_stream_map *map, + size_t initial_capacity); +void grpc_chttp2_stream_map_destroy(grpc_chttp2_stream_map *map); + +/* Add a new key: given http2 semantics, new keys must always be greater than + existing keys - this is asserted */ +void grpc_chttp2_stream_map_add(grpc_chttp2_stream_map *map, gpr_uint32 key, + void *value); + +/* Delete an existing key - returns the previous value of the key if it existed, + or NULL otherwise */ +void *grpc_chttp2_stream_map_delete(grpc_chttp2_stream_map *map, + gpr_uint32 key); + +/* Return an existing key, or NULL if it does not exist */ +void *grpc_chttp2_stream_map_find(grpc_chttp2_stream_map *map, gpr_uint32 key); + +/* How many (populated) entries are in the stream map? */ +size_t grpc_chttp2_stream_map_size(grpc_chttp2_stream_map *map); + +/* Callback on each stream */ +void grpc_chttp2_stream_map_for_each(grpc_chttp2_stream_map *map, + void (*f)(void *user_data, gpr_uint32 key, + void *value), + void *user_data); + +#endif /* __GRPC_INTERNAL_TRANSPORT_CHTTP2_STREAM_MAP_H__ */ diff --git a/src/core/transport/chttp2/timeout_encoding.c b/src/core/transport/chttp2/timeout_encoding.c new file mode 100644 index 0000000000..2706c369a6 --- /dev/null +++ b/src/core/transport/chttp2/timeout_encoding.c @@ -0,0 +1,176 @@ +/* + * + * Copyright 2014, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "src/core/transport/chttp2/timeout_encoding.h" + +#include <stdio.h> +#include <string.h> + +static int round_up(int x, int divisor) { + return (x / divisor + (x % divisor != 0)) * divisor; +} + +/* round an integer up to the next value with three significant figures */ +static int round_up_to_three_sig_figs(int x) { + if (x < 1000) return x; + if (x < 10000) return round_up(x, 10); + if (x < 100000) return round_up(x, 100); + if (x < 1000000) return round_up(x, 1000); + if (x < 10000000) return round_up(x, 10000); + if (x < 100000000) return round_up(x, 100000); + if (x < 1000000000) return round_up(x, 1000000); + return round_up(x, 10000000); +} + +/* encode our minimum viable timeout value */ +static void enc_tiny(char *buffer) { strcpy(buffer, "1n"); } + +static void enc_seconds(char *buffer, long sec) { + if (sec % 3600 == 0) { + sprintf(buffer, "%ldH", sec / 3600); + } else if (sec % 60 == 0) { + sprintf(buffer, "%ldM", sec / 60); + } else { + sprintf(buffer, "%ldS", sec); + } +} + +static void enc_nanos(char *buffer, int x) { + x = round_up_to_three_sig_figs(x); + if (x < 100000) { + if (x % 1000 == 0) { + sprintf(buffer, "%du", x / 1000); + } else { + sprintf(buffer, "%dn", x); + } + } else if (x < 100000000) { + if (x % 1000000 == 0) { + sprintf(buffer, "%dm", x / 1000000); + } else { + sprintf(buffer, "%du", x / 1000); + } + } else if (x < 1000000000) { + sprintf(buffer, "%dm", x / 1000000); + } else { + /* note that this is only ever called with times of less than one second, + so if we reach here the time must have been rounded up to a whole second + (and no more) */ + strcpy(buffer, "1S"); + } +} + +static void enc_micros(char *buffer, int x) { + x = round_up_to_three_sig_figs(x); + if (x < 100000) { + if (x % 1000 == 0) { + sprintf(buffer, "%dm", x / 1000); + } else { + sprintf(buffer, "%du", x); + } + } else if (x < 100000000) { + if (x % 1000000 == 0) { + sprintf(buffer, "%dS", x / 1000000); + } else { + sprintf(buffer, "%dm", x / 1000); + } + } else { + sprintf(buffer, "%dS", x / 1000000); + } +} + +void grpc_chttp2_encode_timeout(gpr_timespec timeout, char *buffer) { + if (timeout.tv_sec < 0) { + enc_tiny(buffer); + } else if (timeout.tv_sec == 0) { + enc_nanos(buffer, timeout.tv_nsec); + } else if (timeout.tv_sec < 1000 && timeout.tv_nsec != 0) { + enc_micros(buffer, + timeout.tv_sec * 1000000 + + (timeout.tv_nsec / 1000 + (timeout.tv_nsec % 1000 != 0))); + } else { + enc_seconds(buffer, timeout.tv_sec + (timeout.tv_nsec != 0)); + } +} + +static int is_all_whitespace(const char *p) { + while (*p == ' ') p++; + return *p == 0; +} + +int grpc_chttp2_decode_timeout(const char *buffer, gpr_timespec *timeout) { + gpr_uint32 x = 0; + const char *p = buffer; + int have_digit = 0; + /* skip whitespace */ + for (; *p == ' '; p++) + ; + /* decode numeric part */ + for (; *p >= '0' && *p <= '9'; p++) { + gpr_uint32 xp = x * 10 + *p - '0'; + have_digit = 1; + if (xp < x) { + *timeout = gpr_inf_future; + return 1; + } + x = xp; + } + if (!have_digit) return 0; + /* skip whitespace */ + for (; *p == ' '; p++) + ; + /* decode unit specifier */ + switch (*p) { + case 'n': + *timeout = gpr_time_from_nanos(x); + break; + case 'u': + *timeout = gpr_time_from_micros(x); + break; + case 'm': + *timeout = gpr_time_from_millis(x); + break; + case 'S': + *timeout = gpr_time_from_seconds(x); + break; + case 'M': + *timeout = gpr_time_from_minutes(x); + break; + case 'H': + *timeout = gpr_time_from_hours(x); + break; + default: + return 0; + } + p++; + return is_all_whitespace(p); +} diff --git a/src/core/transport/chttp2/timeout_encoding.h b/src/core/transport/chttp2/timeout_encoding.h new file mode 100644 index 0000000000..a4582566ad --- /dev/null +++ b/src/core/transport/chttp2/timeout_encoding.h @@ -0,0 +1,44 @@ +/* + * + * Copyright 2014, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef __GRPC_INTERNAL_TRANSPORT_CHTTP2_TIMEOUT_ENCODING_H_ +#define __GRPC_INTERNAL_TRANSPORT_CHTTP2_TIMEOUT_ENCODING_H_ + +#include <grpc/support/time.h> + +/* Encode/decode timeouts to the GRPC over HTTP2 format; + encoding may round up arbitrarily */ +void grpc_chttp2_encode_timeout(gpr_timespec timeout, char *buffer); +int grpc_chttp2_decode_timeout(const char *buffer, gpr_timespec *timeout); + +#endif /* __GRPC_INTERNAL_TRANSPORT_CHTTP2_TIMEOUT_ENCODING_H_ */ diff --git a/src/core/transport/chttp2/varint.c b/src/core/transport/chttp2/varint.c new file mode 100644 index 0000000000..5d551be642 --- /dev/null +++ b/src/core/transport/chttp2/varint.c @@ -0,0 +1,65 @@ +/* + * + * Copyright 2014, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "src/core/transport/chttp2/varint.h" + +int grpc_chttp2_hpack_varint_length(gpr_uint32 tail_value) { + if (tail_value < (1 << 7)) { + return 2; + } else if (tail_value < (1 << 14)) { + return 3; + } else if (tail_value < (1 << 21)) { + return 4; + } else if (tail_value < (1 << 28)) { + return 5; + } else { + return 6; + } +} + +void grpc_chttp2_hpack_write_varint_tail(gpr_uint32 tail_value, + gpr_uint8* target, int tail_length) { + switch (tail_length) { + case 5: + target[4] = (gpr_uint8)((tail_value >> 28) | 0x80); + case 4: + target[3] = (gpr_uint8)((tail_value >> 21) | 0x80); + case 3: + target[2] = (gpr_uint8)((tail_value >> 14) | 0x80); + case 2: + target[1] = (gpr_uint8)((tail_value >> 7) | 0x80); + case 1: + target[0] = (gpr_uint8)((tail_value) | 0x80); + } + target[tail_length - 1] &= 0x7f; +} diff --git a/src/core/transport/chttp2/varint.h b/src/core/transport/chttp2/varint.h new file mode 100644 index 0000000000..780390238f --- /dev/null +++ b/src/core/transport/chttp2/varint.h @@ -0,0 +1,73 @@ +/* + * + * Copyright 2014, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef __GRPC_INTERNAL_TRANSPORT_CHTTP2_VARINT_H__ +#define __GRPC_INTERNAL_TRANSPORT_CHTTP2_VARINT_H__ + +#include <grpc/support/port_platform.h> + +/* Helpers for hpack varint encoding */ + +/* length of a value that needs varint tail encoding (it's bigger than can be + bitpacked into the opcode byte) - returned value includes the length of the + opcode byte */ +int grpc_chttp2_hpack_varint_length(gpr_uint32 tail_value); + +void grpc_chttp2_hpack_write_varint_tail(gpr_uint32 tail_value, + gpr_uint8* target, int tail_length); + +/* maximum value that can be bitpacked with the opcode if the opcode has a + prefix + of length prefix_bits */ +#define GRPC_CHTTP2_MAX_IN_PREFIX(prefix_bits) ((1 << (8 - (prefix_bits))) - 1) + +/* length required to bitpack a value */ +#define GRPC_CHTTP2_VARINT_LENGTH(n, prefix_bits) \ + ((n) < GRPC_CHTTP2_MAX_IN_PREFIX(prefix_bits) \ + ? 1 \ + : grpc_chttp2_hpack_varint_length( \ + (n)-GRPC_CHTTP2_MAX_IN_PREFIX(prefix_bits))) + +#define GRPC_CHTTP2_WRITE_VARINT(n, prefix_bits, prefix_or, target, length) \ + do { \ + gpr_uint8* tgt = target; \ + if ((length) == 1) { \ + (tgt)[0] = (prefix_or) | (n); \ + } else { \ + (tgt)[0] = (prefix_or) | GRPC_CHTTP2_MAX_IN_PREFIX(prefix_bits); \ + grpc_chttp2_hpack_write_varint_tail( \ + (n)-GRPC_CHTTP2_MAX_IN_PREFIX(prefix_bits), (tgt) + 1, (length)-1); \ + } \ + } while (0) + +#endif /* __GRPC_INTERNAL_TRANSPORT_CHTTP2_VARINT_H__ */ |