diff options
Diffstat (limited to 'nginx/src/http/modules/ngx_http_grpc_module.c')
-rw-r--r-- | nginx/src/http/modules/ngx_http_grpc_module.c | 4707 |
1 files changed, 0 insertions, 4707 deletions
diff --git a/nginx/src/http/modules/ngx_http_grpc_module.c b/nginx/src/http/modules/ngx_http_grpc_module.c deleted file mode 100644 index 62b33fd..0000000 --- a/nginx/src/http/modules/ngx_http_grpc_module.c +++ /dev/null @@ -1,4707 +0,0 @@ - -/* - * Copyright (C) Maxim Dounin - * Copyright (C) Nginx, Inc. - */ - - -#include <ngx_config.h> -#include <ngx_core.h> -#include <ngx_http.h> - - -typedef struct { - ngx_array_t *flushes; - ngx_array_t *lengths; - ngx_array_t *values; - ngx_hash_t hash; -} ngx_http_grpc_headers_t; - - -typedef struct { - ngx_http_upstream_conf_t upstream; - - ngx_http_grpc_headers_t headers; - ngx_array_t *headers_source; - - ngx_str_t host; - ngx_uint_t host_set; - -#if (NGX_HTTP_SSL) - ngx_uint_t ssl; - ngx_uint_t ssl_protocols; - ngx_str_t ssl_ciphers; - ngx_uint_t ssl_verify_depth; - ngx_str_t ssl_trusted_certificate; - ngx_str_t ssl_crl; - ngx_str_t ssl_certificate; - ngx_str_t ssl_certificate_key; - ngx_array_t *ssl_passwords; -#endif -} ngx_http_grpc_loc_conf_t; - - -typedef enum { - ngx_http_grpc_st_start = 0, - ngx_http_grpc_st_length_2, - ngx_http_grpc_st_length_3, - ngx_http_grpc_st_type, - ngx_http_grpc_st_flags, - ngx_http_grpc_st_stream_id, - ngx_http_grpc_st_stream_id_2, - ngx_http_grpc_st_stream_id_3, - ngx_http_grpc_st_stream_id_4, - ngx_http_grpc_st_payload, - ngx_http_grpc_st_padding -} ngx_http_grpc_state_e; - - -typedef struct { - size_t init_window; - size_t send_window; - size_t recv_window; - ngx_uint_t last_stream_id; -} ngx_http_grpc_conn_t; - - -typedef struct { - ngx_http_grpc_state_e state; - ngx_uint_t frame_state; - ngx_uint_t fragment_state; - - ngx_chain_t *in; - ngx_chain_t *out; - ngx_chain_t *free; - ngx_chain_t *busy; - - ngx_http_grpc_conn_t *connection; - - ngx_uint_t id; - - ngx_uint_t pings; - ngx_uint_t settings; - - ssize_t send_window; - size_t recv_window; - - size_t rest; - ngx_uint_t stream_id; - u_char type; - u_char flags; - u_char padding; - - ngx_uint_t error; - ngx_uint_t window_update; - - ngx_uint_t setting_id; - ngx_uint_t setting_value; - - u_char ping_data[8]; - - ngx_uint_t index; - ngx_str_t name; - ngx_str_t value; - - u_char *field_end; - size_t field_length; - size_t field_rest; - u_char field_state; - - unsigned literal:1; - unsigned field_huffman:1; - - unsigned header_sent:1; - unsigned output_closed:1; - unsigned output_blocked:1; - unsigned parsing_headers:1; - unsigned end_stream:1; - unsigned done:1; - unsigned status:1; - - ngx_http_request_t *request; -} ngx_http_grpc_ctx_t; - - -typedef struct { - u_char length_0; - u_char length_1; - u_char length_2; - u_char type; - u_char flags; - u_char stream_id_0; - u_char stream_id_1; - u_char stream_id_2; - u_char stream_id_3; -} ngx_http_grpc_frame_t; - - -static ngx_int_t ngx_http_grpc_create_request(ngx_http_request_t *r); -static ngx_int_t ngx_http_grpc_reinit_request(ngx_http_request_t *r); -static ngx_int_t ngx_http_grpc_body_output_filter(void *data, ngx_chain_t *in); -static ngx_int_t ngx_http_grpc_process_header(ngx_http_request_t *r); -static ngx_int_t ngx_http_grpc_filter_init(void *data); -static ngx_int_t ngx_http_grpc_filter(void *data, ssize_t bytes); - -static ngx_int_t ngx_http_grpc_parse_frame(ngx_http_request_t *r, - ngx_http_grpc_ctx_t *ctx, ngx_buf_t *b); -static ngx_int_t ngx_http_grpc_parse_header(ngx_http_request_t *r, - ngx_http_grpc_ctx_t *ctx, ngx_buf_t *b); -static ngx_int_t ngx_http_grpc_parse_fragment(ngx_http_request_t *r, - ngx_http_grpc_ctx_t *ctx, ngx_buf_t *b); -static ngx_int_t ngx_http_grpc_validate_header_name(ngx_http_request_t *r, - ngx_str_t *s); -static ngx_int_t ngx_http_grpc_validate_header_value(ngx_http_request_t *r, - ngx_str_t *s); -static ngx_int_t ngx_http_grpc_parse_rst_stream(ngx_http_request_t *r, - ngx_http_grpc_ctx_t *ctx, ngx_buf_t *b); -static ngx_int_t ngx_http_grpc_parse_goaway(ngx_http_request_t *r, - ngx_http_grpc_ctx_t *ctx, ngx_buf_t *b); -static ngx_int_t ngx_http_grpc_parse_window_update(ngx_http_request_t *r, - ngx_http_grpc_ctx_t *ctx, ngx_buf_t *b); -static ngx_int_t ngx_http_grpc_parse_settings(ngx_http_request_t *r, - ngx_http_grpc_ctx_t *ctx, ngx_buf_t *b); -static ngx_int_t ngx_http_grpc_parse_ping(ngx_http_request_t *r, - ngx_http_grpc_ctx_t *ctx, ngx_buf_t *b); - -static ngx_int_t ngx_http_grpc_send_settings_ack(ngx_http_request_t *r, - ngx_http_grpc_ctx_t *ctx); -static ngx_int_t ngx_http_grpc_send_ping_ack(ngx_http_request_t *r, - ngx_http_grpc_ctx_t *ctx); -static ngx_int_t ngx_http_grpc_send_window_update(ngx_http_request_t *r, - ngx_http_grpc_ctx_t *ctx); - -static ngx_chain_t *ngx_http_grpc_get_buf(ngx_http_request_t *r, - ngx_http_grpc_ctx_t *ctx); -static ngx_http_grpc_ctx_t *ngx_http_grpc_get_ctx(ngx_http_request_t *r); -static ngx_int_t ngx_http_grpc_get_connection_data(ngx_http_request_t *r, - ngx_http_grpc_ctx_t *ctx, ngx_peer_connection_t *pc); -static void ngx_http_grpc_cleanup(void *data); - -static void ngx_http_grpc_abort_request(ngx_http_request_t *r); -static void ngx_http_grpc_finalize_request(ngx_http_request_t *r, - ngx_int_t rc); - -static ngx_int_t ngx_http_grpc_internal_trailers_variable( - ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); - -static ngx_int_t ngx_http_grpc_add_variables(ngx_conf_t *cf); -static void *ngx_http_grpc_create_loc_conf(ngx_conf_t *cf); -static char *ngx_http_grpc_merge_loc_conf(ngx_conf_t *cf, - void *parent, void *child); -static ngx_int_t ngx_http_grpc_init_headers(ngx_conf_t *cf, - ngx_http_grpc_loc_conf_t *conf, ngx_http_grpc_headers_t *headers, - ngx_keyval_t *default_headers); - -static char *ngx_http_grpc_pass(ngx_conf_t *cf, ngx_command_t *cmd, - void *conf); - -#if (NGX_HTTP_SSL) -static char *ngx_http_grpc_ssl_password_file(ngx_conf_t *cf, - ngx_command_t *cmd, void *conf); -static ngx_int_t ngx_http_grpc_set_ssl(ngx_conf_t *cf, - ngx_http_grpc_loc_conf_t *glcf); -#endif - - -static ngx_conf_bitmask_t ngx_http_grpc_next_upstream_masks[] = { - { ngx_string("error"), NGX_HTTP_UPSTREAM_FT_ERROR }, - { ngx_string("timeout"), NGX_HTTP_UPSTREAM_FT_TIMEOUT }, - { ngx_string("invalid_header"), NGX_HTTP_UPSTREAM_FT_INVALID_HEADER }, - { ngx_string("non_idempotent"), NGX_HTTP_UPSTREAM_FT_NON_IDEMPOTENT }, - { ngx_string("http_500"), NGX_HTTP_UPSTREAM_FT_HTTP_500 }, - { ngx_string("http_502"), NGX_HTTP_UPSTREAM_FT_HTTP_502 }, - { ngx_string("http_503"), NGX_HTTP_UPSTREAM_FT_HTTP_503 }, - { ngx_string("http_504"), NGX_HTTP_UPSTREAM_FT_HTTP_504 }, - { ngx_string("http_403"), NGX_HTTP_UPSTREAM_FT_HTTP_403 }, - { ngx_string("http_404"), NGX_HTTP_UPSTREAM_FT_HTTP_404 }, - { ngx_string("http_429"), NGX_HTTP_UPSTREAM_FT_HTTP_429 }, - { ngx_string("off"), NGX_HTTP_UPSTREAM_FT_OFF }, - { ngx_null_string, 0 } -}; - - -#if (NGX_HTTP_SSL) - -static ngx_conf_bitmask_t ngx_http_grpc_ssl_protocols[] = { - { ngx_string("SSLv2"), NGX_SSL_SSLv2 }, - { ngx_string("SSLv3"), NGX_SSL_SSLv3 }, - { ngx_string("TLSv1"), NGX_SSL_TLSv1 }, - { ngx_string("TLSv1.1"), NGX_SSL_TLSv1_1 }, - { ngx_string("TLSv1.2"), NGX_SSL_TLSv1_2 }, - { ngx_string("TLSv1.3"), NGX_SSL_TLSv1_3 }, - { ngx_null_string, 0 } -}; - -#endif - - -static ngx_command_t ngx_http_grpc_commands[] = { - - { ngx_string("grpc_pass"), - NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1, - ngx_http_grpc_pass, - NGX_HTTP_LOC_CONF_OFFSET, - 0, - NULL }, - - { ngx_string("grpc_bind"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE12, - ngx_http_upstream_bind_set_slot, - NGX_HTTP_LOC_CONF_OFFSET, - offsetof(ngx_http_grpc_loc_conf_t, upstream.local), - NULL }, - - { ngx_string("grpc_connect_timeout"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, - ngx_conf_set_msec_slot, - NGX_HTTP_LOC_CONF_OFFSET, - offsetof(ngx_http_grpc_loc_conf_t, upstream.connect_timeout), - NULL }, - - { ngx_string("grpc_send_timeout"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, - ngx_conf_set_msec_slot, - NGX_HTTP_LOC_CONF_OFFSET, - offsetof(ngx_http_grpc_loc_conf_t, upstream.send_timeout), - NULL }, - - { ngx_string("grpc_intercept_errors"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, - ngx_conf_set_flag_slot, - NGX_HTTP_LOC_CONF_OFFSET, - offsetof(ngx_http_grpc_loc_conf_t, upstream.intercept_errors), - NULL }, - - { ngx_string("grpc_buffer_size"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, - ngx_conf_set_size_slot, - NGX_HTTP_LOC_CONF_OFFSET, - offsetof(ngx_http_grpc_loc_conf_t, upstream.buffer_size), - NULL }, - - { ngx_string("grpc_read_timeout"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, - ngx_conf_set_msec_slot, - NGX_HTTP_LOC_CONF_OFFSET, - offsetof(ngx_http_grpc_loc_conf_t, upstream.read_timeout), - NULL }, - - { ngx_string("grpc_next_upstream"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE, - ngx_conf_set_bitmask_slot, - NGX_HTTP_LOC_CONF_OFFSET, - offsetof(ngx_http_grpc_loc_conf_t, upstream.next_upstream), - &ngx_http_grpc_next_upstream_masks }, - - { ngx_string("grpc_next_upstream_tries"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, - ngx_conf_set_num_slot, - NGX_HTTP_LOC_CONF_OFFSET, - offsetof(ngx_http_grpc_loc_conf_t, upstream.next_upstream_tries), - NULL }, - - { ngx_string("grpc_next_upstream_timeout"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, - ngx_conf_set_msec_slot, - NGX_HTTP_LOC_CONF_OFFSET, - offsetof(ngx_http_grpc_loc_conf_t, upstream.next_upstream_timeout), - NULL }, - - { ngx_string("grpc_set_header"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2, - ngx_conf_set_keyval_slot, - NGX_HTTP_LOC_CONF_OFFSET, - offsetof(ngx_http_grpc_loc_conf_t, headers_source), - NULL }, - - { ngx_string("grpc_pass_header"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, - ngx_conf_set_str_array_slot, - NGX_HTTP_LOC_CONF_OFFSET, - offsetof(ngx_http_grpc_loc_conf_t, upstream.pass_headers), - NULL }, - - { ngx_string("grpc_hide_header"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, - ngx_conf_set_str_array_slot, - NGX_HTTP_LOC_CONF_OFFSET, - offsetof(ngx_http_grpc_loc_conf_t, upstream.hide_headers), - NULL }, - - { ngx_string("grpc_ignore_headers"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE, - ngx_conf_set_bitmask_slot, - NGX_HTTP_LOC_CONF_OFFSET, - offsetof(ngx_http_grpc_loc_conf_t, upstream.ignore_headers), - &ngx_http_upstream_ignore_headers_masks }, - -#if (NGX_HTTP_SSL) - - { ngx_string("grpc_ssl_session_reuse"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, - ngx_conf_set_flag_slot, - NGX_HTTP_LOC_CONF_OFFSET, - offsetof(ngx_http_grpc_loc_conf_t, upstream.ssl_session_reuse), - NULL }, - - { ngx_string("grpc_ssl_protocols"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE, - ngx_conf_set_bitmask_slot, - NGX_HTTP_LOC_CONF_OFFSET, - offsetof(ngx_http_grpc_loc_conf_t, ssl_protocols), - &ngx_http_grpc_ssl_protocols }, - - { ngx_string("grpc_ssl_ciphers"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, - ngx_conf_set_str_slot, - NGX_HTTP_LOC_CONF_OFFSET, - offsetof(ngx_http_grpc_loc_conf_t, ssl_ciphers), - NULL }, - - { ngx_string("grpc_ssl_name"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, - ngx_http_set_complex_value_slot, - NGX_HTTP_LOC_CONF_OFFSET, - offsetof(ngx_http_grpc_loc_conf_t, upstream.ssl_name), - NULL }, - - { ngx_string("grpc_ssl_server_name"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, - ngx_conf_set_flag_slot, - NGX_HTTP_LOC_CONF_OFFSET, - offsetof(ngx_http_grpc_loc_conf_t, upstream.ssl_server_name), - NULL }, - - { ngx_string("grpc_ssl_verify"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, - ngx_conf_set_flag_slot, - NGX_HTTP_LOC_CONF_OFFSET, - offsetof(ngx_http_grpc_loc_conf_t, upstream.ssl_verify), - NULL }, - - { ngx_string("grpc_ssl_verify_depth"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, - ngx_conf_set_num_slot, - NGX_HTTP_LOC_CONF_OFFSET, - offsetof(ngx_http_grpc_loc_conf_t, ssl_verify_depth), - NULL }, - - { ngx_string("grpc_ssl_trusted_certificate"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, - ngx_conf_set_str_slot, - NGX_HTTP_LOC_CONF_OFFSET, - offsetof(ngx_http_grpc_loc_conf_t, ssl_trusted_certificate), - NULL }, - - { ngx_string("grpc_ssl_crl"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, - ngx_conf_set_str_slot, - NGX_HTTP_LOC_CONF_OFFSET, - offsetof(ngx_http_grpc_loc_conf_t, ssl_crl), - NULL }, - - { ngx_string("grpc_ssl_certificate"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, - ngx_conf_set_str_slot, - NGX_HTTP_LOC_CONF_OFFSET, - offsetof(ngx_http_grpc_loc_conf_t, ssl_certificate), - NULL }, - - { ngx_string("grpc_ssl_certificate_key"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, - ngx_conf_set_str_slot, - NGX_HTTP_LOC_CONF_OFFSET, - offsetof(ngx_http_grpc_loc_conf_t, ssl_certificate_key), - NULL }, - - { ngx_string("grpc_ssl_password_file"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, - ngx_http_grpc_ssl_password_file, - NGX_HTTP_LOC_CONF_OFFSET, - 0, - NULL }, - -#endif - - ngx_null_command -}; - - -static ngx_http_module_t ngx_http_grpc_module_ctx = { - ngx_http_grpc_add_variables, /* preconfiguration */ - NULL, /* postconfiguration */ - - NULL, /* create main configuration */ - NULL, /* init main configuration */ - - NULL, /* create server configuration */ - NULL, /* merge server configuration */ - - ngx_http_grpc_create_loc_conf, /* create location configuration */ - ngx_http_grpc_merge_loc_conf /* merge location configuration */ -}; - - -ngx_module_t ngx_http_grpc_module = { - NGX_MODULE_V1, - &ngx_http_grpc_module_ctx, /* module context */ - ngx_http_grpc_commands, /* module directives */ - NGX_HTTP_MODULE, /* module type */ - NULL, /* init master */ - NULL, /* init module */ - NULL, /* init process */ - NULL, /* init thread */ - NULL, /* exit thread */ - NULL, /* exit process */ - NULL, /* exit master */ - NGX_MODULE_V1_PADDING -}; - - -static u_char ngx_http_grpc_connection_start[] = - "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n" /* connection preface */ - - "\x00\x00\x12\x04\x00\x00\x00\x00\x00" /* settings frame */ - "\x00\x01\x00\x00\x00\x00" /* header table size */ - "\x00\x02\x00\x00\x00\x00" /* disable push */ - "\x00\x04\x7f\xff\xff\xff" /* initial window */ - - "\x00\x00\x04\x08\x00\x00\x00\x00\x00" /* window update frame */ - "\x7f\xff\x00\x00"; - - -static ngx_keyval_t ngx_http_grpc_headers[] = { - { ngx_string("Content-Length"), ngx_string("$content_length") }, - { ngx_string("TE"), ngx_string("$grpc_internal_trailers") }, - { ngx_string("Host"), ngx_string("") }, - { ngx_string("Connection"), ngx_string("") }, - { ngx_string("Transfer-Encoding"), ngx_string("") }, - { ngx_string("Keep-Alive"), ngx_string("") }, - { ngx_string("Expect"), ngx_string("") }, - { ngx_string("Upgrade"), ngx_string("") }, - { ngx_null_string, ngx_null_string } -}; - - -static ngx_str_t ngx_http_grpc_hide_headers[] = { - ngx_string("Date"), - ngx_string("Server"), - ngx_string("X-Accel-Expires"), - ngx_string("X-Accel-Redirect"), - ngx_string("X-Accel-Limit-Rate"), - ngx_string("X-Accel-Buffering"), - ngx_string("X-Accel-Charset"), - ngx_null_string -}; - - -static ngx_http_variable_t ngx_http_grpc_vars[] = { - - { ngx_string("grpc_internal_trailers"), NULL, - ngx_http_grpc_internal_trailers_variable, 0, - NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 }, - - ngx_http_null_variable -}; - - -static ngx_int_t -ngx_http_grpc_handler(ngx_http_request_t *r) -{ - ngx_int_t rc; - ngx_http_upstream_t *u; - ngx_http_grpc_ctx_t *ctx; - ngx_http_grpc_loc_conf_t *glcf; - - if (ngx_http_upstream_create(r) != NGX_OK) { - return NGX_HTTP_INTERNAL_SERVER_ERROR; - } - - glcf = ngx_http_get_module_loc_conf(r, ngx_http_grpc_module); - - u = r->upstream; - -#if (NGX_HTTP_SSL) - u->ssl = (glcf->upstream.ssl != NULL); - - if (u->ssl) { - ngx_str_set(&u->schema, "grpcs://"); - - } else { - ngx_str_set(&u->schema, "grpc://"); - } -#else - ngx_str_set(&u->schema, "grpc://"); -#endif - - u->output.tag = (ngx_buf_tag_t) &ngx_http_grpc_module; - - u->conf = &glcf->upstream; - - u->create_request = ngx_http_grpc_create_request; - u->reinit_request = ngx_http_grpc_reinit_request; - u->process_header = ngx_http_grpc_process_header; - u->abort_request = ngx_http_grpc_abort_request; - u->finalize_request = ngx_http_grpc_finalize_request; - - ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_grpc_ctx_t)); - if (ctx == NULL) { - return NGX_HTTP_INTERNAL_SERVER_ERROR; - } - - ctx->request = r; - - ngx_http_set_ctx(r, ctx, ngx_http_grpc_module); - - u->input_filter_init = ngx_http_grpc_filter_init; - u->input_filter = ngx_http_grpc_filter; - u->input_filter_ctx = ctx; - - r->request_body_no_buffering = 1; - - rc = ngx_http_read_client_request_body(r, ngx_http_upstream_init); - - if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { - return rc; - } - - return NGX_DONE; -} - - -static ngx_int_t -ngx_http_grpc_create_request(ngx_http_request_t *r) -{ - u_char *p, *tmp, *key_tmp, *val_tmp, *headers_frame; - size_t len, tmp_len, key_len, val_len, uri_len; - uintptr_t escape; - ngx_buf_t *b; - ngx_uint_t i, next; - ngx_chain_t *cl, *body; - ngx_list_part_t *part; - ngx_table_elt_t *header; - ngx_http_upstream_t *u; - ngx_http_grpc_frame_t *f; - ngx_http_script_code_pt code; - ngx_http_grpc_loc_conf_t *glcf; - ngx_http_script_engine_t e, le; - ngx_http_script_len_code_pt lcode; - - u = r->upstream; - - glcf = ngx_http_get_module_loc_conf(r, ngx_http_grpc_module); - - len = sizeof(ngx_http_grpc_connection_start) - 1 - + sizeof(ngx_http_grpc_frame_t); /* headers frame */ - - /* :method header */ - - if (r->method == NGX_HTTP_GET || r->method == NGX_HTTP_POST) { - len += 1; - tmp_len = 0; - - } else { - len += 1 + NGX_HTTP_V2_INT_OCTETS + r->method_name.len; - tmp_len = r->method_name.len; - } - - /* :scheme header */ - - len += 1; - - /* :path header */ - - if (r->valid_unparsed_uri) { - escape = 0; - uri_len = r->unparsed_uri.len; - - } else { - escape = 2 * ngx_escape_uri(NULL, r->uri.data, r->uri.len, - NGX_ESCAPE_URI); - uri_len = r->uri.len + escape + sizeof("?") - 1 + r->args.len; - } - - len += 1 + NGX_HTTP_V2_INT_OCTETS + uri_len; - - if (tmp_len < uri_len) { - tmp_len = uri_len; - } - - /* :authority header */ - - if (!glcf->host_set) { - len += 1 + NGX_HTTP_V2_INT_OCTETS + glcf->host.len; - - if (tmp_len < glcf->host.len) { - tmp_len = glcf->host.len; - } - } - - /* other headers */ - - ngx_http_script_flush_no_cacheable_variables(r, glcf->headers.flushes); - ngx_memzero(&le, sizeof(ngx_http_script_engine_t)); - - le.ip = glcf->headers.lengths->elts; - le.request = r; - le.flushed = 1; - - while (*(uintptr_t *) le.ip) { - - lcode = *(ngx_http_script_len_code_pt *) le.ip; - key_len = lcode(&le); - - for (val_len = 0; *(uintptr_t *) le.ip; val_len += lcode(&le)) { - lcode = *(ngx_http_script_len_code_pt *) le.ip; - } - le.ip += sizeof(uintptr_t); - - if (val_len == 0) { - continue; - } - - len += 1 + NGX_HTTP_V2_INT_OCTETS + key_len - + NGX_HTTP_V2_INT_OCTETS + val_len; - - if (tmp_len < key_len) { - tmp_len = key_len; - } - - if (tmp_len < val_len) { - tmp_len = val_len; - } - } - - if (glcf->upstream.pass_request_headers) { - part = &r->headers_in.headers.part; - header = part->elts; - - for (i = 0; /* void */; i++) { - - if (i >= part->nelts) { - if (part->next == NULL) { - break; - } - - part = part->next; - header = part->elts; - i = 0; - } - - if (ngx_hash_find(&glcf->headers.hash, header[i].hash, - header[i].lowcase_key, header[i].key.len)) - { - continue; - } - - len += 1 + NGX_HTTP_V2_INT_OCTETS + header[i].key.len - + NGX_HTTP_V2_INT_OCTETS + header[i].value.len; - - if (tmp_len < header[i].key.len) { - tmp_len = header[i].key.len; - } - - if (tmp_len < header[i].value.len) { - tmp_len = header[i].value.len; - } - } - } - - /* continuation frames */ - - len += sizeof(ngx_http_grpc_frame_t) - * (len / NGX_HTTP_V2_DEFAULT_FRAME_SIZE); - - - b = ngx_create_temp_buf(r->pool, len); - if (b == NULL) { - return NGX_ERROR; - } - - cl = ngx_alloc_chain_link(r->pool); - if (cl == NULL) { - return NGX_ERROR; - } - - cl->buf = b; - cl->next = NULL; - - tmp = ngx_palloc(r->pool, tmp_len * 3); - if (tmp == NULL) { - return NGX_ERROR; - } - - key_tmp = tmp + tmp_len; - val_tmp = tmp + 2 * tmp_len; - - /* connection preface */ - - b->last = ngx_copy(b->last, ngx_http_grpc_connection_start, - sizeof(ngx_http_grpc_connection_start) - 1); - - /* headers frame */ - - headers_frame = b->last; - - f = (ngx_http_grpc_frame_t *) b->last; - b->last += sizeof(ngx_http_grpc_frame_t); - - f->length_0 = 0; - f->length_1 = 0; - f->length_2 = 0; - f->type = NGX_HTTP_V2_HEADERS_FRAME; - f->flags = 0; - f->stream_id_0 = 0; - f->stream_id_1 = 0; - f->stream_id_2 = 0; - f->stream_id_3 = 1; - - if (r->method == NGX_HTTP_GET) { - *b->last++ = ngx_http_v2_indexed(NGX_HTTP_V2_METHOD_GET_INDEX); - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "grpc header: \":method: GET\""); - - } else if (r->method == NGX_HTTP_POST) { - *b->last++ = ngx_http_v2_indexed(NGX_HTTP_V2_METHOD_POST_INDEX); - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "grpc header: \":method: POST\""); - - } else { - *b->last++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_METHOD_INDEX); - b->last = ngx_http_v2_write_value(b->last, r->method_name.data, - r->method_name.len, tmp); - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "grpc header: \":method: %V\"", &r->method_name); - } - -#if (NGX_HTTP_SSL) - if (glcf->ssl) { - *b->last++ = ngx_http_v2_indexed(NGX_HTTP_V2_SCHEME_HTTPS_INDEX); - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "grpc header: \":scheme: https\""); - } else -#endif - { - *b->last++ = ngx_http_v2_indexed(NGX_HTTP_V2_SCHEME_HTTP_INDEX); - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "grpc header: \":scheme: http\""); - } - - if (r->valid_unparsed_uri) { - - if (r->unparsed_uri.len == 1 && r->unparsed_uri.data[0] == '/') { - *b->last++ = ngx_http_v2_indexed(NGX_HTTP_V2_PATH_ROOT_INDEX); - - } else { - *b->last++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_PATH_INDEX); - b->last = ngx_http_v2_write_value(b->last, r->unparsed_uri.data, - r->unparsed_uri.len, tmp); - } - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "grpc header: \":path: %V\"", &r->unparsed_uri); - - } else if (escape || r->args.len > 0) { - p = val_tmp; - - if (escape) { - p = (u_char *) ngx_escape_uri(p, r->uri.data, r->uri.len, - NGX_ESCAPE_URI); - - } else { - p = ngx_copy(p, r->uri.data, r->uri.len); - } - - if (r->args.len > 0) { - *p++ = '?'; - p = ngx_copy(p, r->args.data, r->args.len); - } - - *b->last++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_PATH_INDEX); - b->last = ngx_http_v2_write_value(b->last, val_tmp, p - val_tmp, tmp); - - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "grpc header: \":path: %*s\"", p - val_tmp, val_tmp); - - } else { - *b->last++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_PATH_INDEX); - b->last = ngx_http_v2_write_value(b->last, r->uri.data, - r->uri.len, tmp); - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "grpc header: \":path: %V\"", &r->uri); - } - - if (!glcf->host_set) { - *b->last++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_AUTHORITY_INDEX); - b->last = ngx_http_v2_write_value(b->last, glcf->host.data, - glcf->host.len, tmp); - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "grpc header: \":authority: %V\"", &glcf->host); - } - - ngx_memzero(&e, sizeof(ngx_http_script_engine_t)); - - e.ip = glcf->headers.values->elts; - e.request = r; - e.flushed = 1; - - le.ip = glcf->headers.lengths->elts; - - while (*(uintptr_t *) le.ip) { - - lcode = *(ngx_http_script_len_code_pt *) le.ip; - key_len = lcode(&le); - - for (val_len = 0; *(uintptr_t *) le.ip; val_len += lcode(&le)) { - lcode = *(ngx_http_script_len_code_pt *) le.ip; - } - le.ip += sizeof(uintptr_t); - - if (val_len == 0) { - e.skip = 1; - - while (*(uintptr_t *) e.ip) { - code = *(ngx_http_script_code_pt *) e.ip; - code((ngx_http_script_engine_t *) &e); - } - e.ip += sizeof(uintptr_t); - - e.skip = 0; - - continue; - } - - *b->last++ = 0; - - e.pos = key_tmp; - - code = *(ngx_http_script_code_pt *) e.ip; - code((ngx_http_script_engine_t *) &e); - - b->last = ngx_http_v2_write_name(b->last, key_tmp, key_len, tmp); - - e.pos = val_tmp; - - while (*(uintptr_t *) e.ip) { - code = *(ngx_http_script_code_pt *) e.ip; - code((ngx_http_script_engine_t *) &e); - } - e.ip += sizeof(uintptr_t); - - b->last = ngx_http_v2_write_value(b->last, val_tmp, val_len, tmp); - -#if (NGX_DEBUG) - if (r->connection->log->log_level & NGX_LOG_DEBUG_HTTP) { - ngx_strlow(key_tmp, key_tmp, key_len); - - ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "grpc header: \"%*s: %*s\"", - key_len, key_tmp, val_len, val_tmp); - } -#endif - } - - if (glcf->upstream.pass_request_headers) { - part = &r->headers_in.headers.part; - header = part->elts; - - for (i = 0; /* void */; i++) { - - if (i >= part->nelts) { - if (part->next == NULL) { - break; - } - - part = part->next; - header = part->elts; - i = 0; - } - - if (ngx_hash_find(&glcf->headers.hash, header[i].hash, - header[i].lowcase_key, header[i].key.len)) - { - continue; - } - - *b->last++ = 0; - - b->last = ngx_http_v2_write_name(b->last, header[i].key.data, - header[i].key.len, tmp); - - b->last = ngx_http_v2_write_value(b->last, header[i].value.data, - header[i].value.len, tmp); - -#if (NGX_DEBUG) - if (r->connection->log->log_level & NGX_LOG_DEBUG_HTTP) { - ngx_strlow(tmp, header[i].key.data, header[i].key.len); - - ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "grpc header: \"%*s: %V\"", - header[i].key.len, tmp, &header[i].value); - } -#endif - } - } - - /* update headers frame length */ - - len = b->last - headers_frame - sizeof(ngx_http_grpc_frame_t); - - if (len > NGX_HTTP_V2_DEFAULT_FRAME_SIZE) { - len = NGX_HTTP_V2_DEFAULT_FRAME_SIZE; - next = 1; - - } else { - next = 0; - } - - f = (ngx_http_grpc_frame_t *) headers_frame; - - f->length_0 = (u_char) ((len >> 16) & 0xff); - f->length_1 = (u_char) ((len >> 8) & 0xff); - f->length_2 = (u_char) (len & 0xff); - - /* create additional continuation frames */ - - p = headers_frame; - - while (next) { - p += sizeof(ngx_http_grpc_frame_t) + NGX_HTTP_V2_DEFAULT_FRAME_SIZE; - len = b->last - p; - - ngx_memmove(p + sizeof(ngx_http_grpc_frame_t), p, len); - b->last += sizeof(ngx_http_grpc_frame_t); - - if (len > NGX_HTTP_V2_DEFAULT_FRAME_SIZE) { - len = NGX_HTTP_V2_DEFAULT_FRAME_SIZE; - next = 1; - - } else { - next = 0; - } - - f = (ngx_http_grpc_frame_t *) p; - - f->length_0 = (u_char) ((len >> 16) & 0xff); - f->length_1 = (u_char) ((len >> 8) & 0xff); - f->length_2 = (u_char) (len & 0xff); - f->type = NGX_HTTP_V2_CONTINUATION_FRAME; - f->flags = 0; - f->stream_id_0 = 0; - f->stream_id_1 = 0; - f->stream_id_2 = 0; - f->stream_id_3 = 1; - } - - f->flags |= NGX_HTTP_V2_END_HEADERS_FLAG; - -#if (NGX_DEBUG) - if (r->connection->log->log_level & NGX_LOG_DEBUG_HTTP) { - u_char buf[512]; - size_t n, m; - - n = ngx_min(b->last - b->pos, 256); - m = ngx_hex_dump(buf, b->pos, n) - buf; - - ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "grpc header: %*s%s, len: %uz", - m, buf, b->last - b->pos > 256 ? "..." : "", - b->last - b->pos); - } -#endif - - if (r->request_body_no_buffering) { - - u->request_bufs = cl; - - } else { - - body = u->request_bufs; - u->request_bufs = cl; - - if (body == NULL) { - f = (ngx_http_grpc_frame_t *) headers_frame; - f->flags |= NGX_HTTP_V2_END_STREAM_FLAG; - } - - while (body) { - b = ngx_alloc_buf(r->pool); - if (b == NULL) { - return NGX_ERROR; - } - - ngx_memcpy(b, body->buf, sizeof(ngx_buf_t)); - - cl->next = ngx_alloc_chain_link(r->pool); - if (cl->next == NULL) { - return NGX_ERROR; - } - - cl = cl->next; - cl->buf = b; - - body = body->next; - } - - b->last_buf = 1; - } - - u->output.output_filter = ngx_http_grpc_body_output_filter; - u->output.filter_ctx = r; - - b->flush = 1; - cl->next = NULL; - - return NGX_OK; -} - - -static ngx_int_t -ngx_http_grpc_reinit_request(ngx_http_request_t *r) -{ - ngx_http_grpc_ctx_t *ctx; - - ctx = ngx_http_get_module_ctx(r, ngx_http_grpc_module); - - if (ctx == NULL) { - return NGX_OK; - } - - ctx->state = 0; - ctx->header_sent = 0; - ctx->output_closed = 0; - ctx->output_blocked = 0; - ctx->parsing_headers = 0; - ctx->end_stream = 0; - ctx->done = 0; - ctx->status = 0; - ctx->connection = NULL; - - return NGX_OK; -} - - -static ngx_int_t -ngx_http_grpc_body_output_filter(void *data, ngx_chain_t *in) -{ - ngx_http_request_t *r = data; - - off_t file_pos; - u_char *p, *pos, *start; - size_t len, limit; - ngx_buf_t *b; - ngx_int_t rc; - ngx_uint_t next, last; - ngx_chain_t *cl, *out, **ll; - ngx_http_upstream_t *u; - ngx_http_grpc_ctx_t *ctx; - ngx_http_grpc_frame_t *f; - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "grpc output filter"); - - ctx = ngx_http_grpc_get_ctx(r); - - if (ctx == NULL) { - return NGX_ERROR; - } - - if (in) { - if (ngx_chain_add_copy(r->pool, &ctx->in, in) != NGX_OK) { - return NGX_ERROR; - } - } - - out = NULL; - ll = &out; - - if (!ctx->header_sent) { - /* first buffer contains headers */ - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "grpc output header"); - - ctx->header_sent = 1; - - if (ctx->id != 1) { - /* - * keepalive connection: skip connection preface, - * update stream identifiers - */ - - b = ctx->in->buf; - b->pos += sizeof(ngx_http_grpc_connection_start) - 1; - - p = b->pos; - - while (p < b->last) { - f = (ngx_http_grpc_frame_t *) p; - p += sizeof(ngx_http_grpc_frame_t); - - f->stream_id_0 = (u_char) ((ctx->id >> 24) & 0xff); - f->stream_id_1 = (u_char) ((ctx->id >> 16) & 0xff); - f->stream_id_2 = (u_char) ((ctx->id >> 8) & 0xff); - f->stream_id_3 = (u_char) (ctx->id & 0xff); - - p += (f->length_0 << 16) + (f->length_1 << 8) + f->length_2; - } - } - - if (ctx->in->buf->last_buf) { - ctx->output_closed = 1; - } - - *ll = ctx->in; - ll = &ctx->in->next; - - ctx->in = ctx->in->next; - } - - if (ctx->out) { - /* queued control frames */ - - *ll = ctx->out; - - for (cl = ctx->out, ll = &cl->next; cl; cl = cl->next) { - ll = &cl->next; - } - - ctx->out = NULL; - } - - f = NULL; - last = 0; - - limit = ngx_max(0, ctx->send_window); - - if (limit > ctx->connection->send_window) { - limit = ctx->connection->send_window; - } - - ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "grpc output limit: %uz w:%z:%uz", - limit, ctx->send_window, ctx->connection->send_window); - -#if (NGX_SUPPRESS_WARN) - file_pos = 0; - pos = NULL; - cl = NULL; -#endif - - in = ctx->in; - - while (in && limit > 0) { - - ngx_log_debug7(NGX_LOG_DEBUG_EVENT, r->connection->log, 0, - "grpc output in l:%d f:%d %p, pos %p, size: %z " - "file: %O, size: %O", - in->buf->last_buf, - in->buf->in_file, - in->buf->start, in->buf->pos, - in->buf->last - in->buf->pos, - in->buf->file_pos, - in->buf->file_last - in->buf->file_pos); - - if (ngx_buf_special(in->buf)) { - goto next; - } - - if (in->buf->in_file) { - file_pos = in->buf->file_pos; - - } else { - pos = in->buf->pos; - } - - next = 0; - - do { - - cl = ngx_http_grpc_get_buf(r, ctx); - if (cl == NULL) { - return NGX_ERROR; - } - - b = cl->buf; - - f = (ngx_http_grpc_frame_t *) b->last; - b->last += sizeof(ngx_http_grpc_frame_t); - - *ll = cl; - ll = &cl->next; - - cl = ngx_chain_get_free_buf(r->pool, &ctx->free); - if (cl == NULL) { - return NGX_ERROR; - } - - b = cl->buf; - start = b->start; - - ngx_memcpy(b, in->buf, sizeof(ngx_buf_t)); - - /* - * restore b->start to preserve memory allocated in the buffer, - * to reuse it later for headers and control frames - */ - - b->start = start; - - if (in->buf->in_file) { - b->file_pos = file_pos; - file_pos += ngx_min(NGX_HTTP_V2_DEFAULT_FRAME_SIZE, limit); - - if (file_pos >= in->buf->file_last) { - file_pos = in->buf->file_last; - next = 1; - } - - b->file_last = file_pos; - len = (ngx_uint_t) (file_pos - b->file_pos); - - } else { - b->pos = pos; - pos += ngx_min(NGX_HTTP_V2_DEFAULT_FRAME_SIZE, limit); - - if (pos >= in->buf->last) { - pos = in->buf->last; - next = 1; - } - - b->last = pos; - len = (ngx_uint_t) (pos - b->pos); - } - - b->tag = (ngx_buf_tag_t) &ngx_http_grpc_body_output_filter; - b->shadow = in->buf; - b->last_shadow = next; - - b->last_buf = 0; - b->last_in_chain = 0; - - *ll = cl; - ll = &cl->next; - - f->length_0 = (u_char) ((len >> 16) & 0xff); - f->length_1 = (u_char) ((len >> 8) & 0xff); - f->length_2 = (u_char) (len & 0xff); - f->type = NGX_HTTP_V2_DATA_FRAME; - f->flags = 0; - f->stream_id_0 = (u_char) ((ctx->id >> 24) & 0xff); - f->stream_id_1 = (u_char) ((ctx->id >> 16) & 0xff); - f->stream_id_2 = (u_char) ((ctx->id >> 8) & 0xff); - f->stream_id_3 = (u_char) (ctx->id & 0xff); - - limit -= len; - ctx->send_window -= len; - ctx->connection->send_window -= len; - - } while (!next && limit > 0); - - if (!next) { - /* - * if the buffer wasn't fully sent due to flow control limits, - * preserve position for future use - */ - - if (in->buf->in_file) { - in->buf->file_pos = file_pos; - - } else { - in->buf->pos = pos; - } - - break; - } - - next: - - if (in->buf->last_buf) { - last = 1; - } - - in = in->next; - } - - ctx->in = in; - - if (last) { - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "grpc output last"); - - ctx->output_closed = 1; - - if (f) { - f->flags |= NGX_HTTP_V2_END_STREAM_FLAG; - - } else { - cl = ngx_http_grpc_get_buf(r, ctx); - if (cl == NULL) { - return NGX_ERROR; - } - - b = cl->buf; - - f = (ngx_http_grpc_frame_t *) b->last; - b->last += sizeof(ngx_http_grpc_frame_t); - - f->length_0 = 0; - f->length_1 = 0; - f->length_2 = 0; - f->type = NGX_HTTP_V2_DATA_FRAME; - f->flags = NGX_HTTP_V2_END_STREAM_FLAG; - f->stream_id_0 = (u_char) ((ctx->id >> 24) & 0xff); - f->stream_id_1 = (u_char) ((ctx->id >> 16) & 0xff); - f->stream_id_2 = (u_char) ((ctx->id >> 8) & 0xff); - f->stream_id_3 = (u_char) (ctx->id & 0xff); - - *ll = cl; - ll = &cl->next; - } - - cl->buf->last_buf = 1; - } - - *ll = NULL; - -#if (NGX_DEBUG) - - for (cl = out; cl; cl = cl->next) { - ngx_log_debug7(NGX_LOG_DEBUG_EVENT, r->connection->log, 0, - "grpc output out l:%d f:%d %p, pos %p, size: %z " - "file: %O, size: %O", - cl->buf->last_buf, - cl->buf->in_file, - cl->buf->start, cl->buf->pos, - cl->buf->last - cl->buf->pos, - cl->buf->file_pos, - cl->buf->file_last - cl->buf->file_pos); - } - - ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "grpc output limit: %uz w:%z:%uz", - limit, ctx->send_window, ctx->connection->send_window); - -#endif - - rc = ngx_chain_writer(&r->upstream->writer, out); - - ngx_chain_update_chains(r->pool, &ctx->free, &ctx->busy, &out, - (ngx_buf_tag_t) &ngx_http_grpc_body_output_filter); - - for (cl = ctx->free; cl; cl = cl->next) { - - /* mark original buffers as sent */ - - if (cl->buf->shadow) { - if (cl->buf->last_shadow) { - b = cl->buf->shadow; - b->pos = b->last; - } - - cl->buf->shadow = NULL; - } - } - - if (rc == NGX_OK && ctx->in) { - rc = NGX_AGAIN; - } - - if (rc == NGX_AGAIN) { - ctx->output_blocked = 1; - - } else { - ctx->output_blocked = 0; - } - - if (ctx->done) { - - /* - * We have already got the response and were sending some additional - * control frames. Even if there is still something unsent, stop - * here anyway. - */ - - u = r->upstream; - u->length = 0; - - if (ctx->in == NULL - && ctx->out == NULL - && ctx->output_closed - && !ctx->output_blocked - && ctx->state == ngx_http_grpc_st_start) - { - u->keepalive = 1; - } - - ngx_post_event(u->peer.connection->read, &ngx_posted_events); - } - - return rc; -} - - -static ngx_int_t -ngx_http_grpc_process_header(ngx_http_request_t *r) -{ - ngx_str_t *status_line; - ngx_int_t rc, status; - ngx_buf_t *b; - ngx_table_elt_t *h; - ngx_http_upstream_t *u; - ngx_http_grpc_ctx_t *ctx; - ngx_http_upstream_header_t *hh; - ngx_http_upstream_main_conf_t *umcf; - - u = r->upstream; - b = &u->buffer; - -#if (NGX_DEBUG) - if (r->connection->log->log_level & NGX_LOG_DEBUG_HTTP) { - u_char buf[512]; - size_t n, m; - - n = ngx_min(b->last - b->pos, 256); - m = ngx_hex_dump(buf, b->pos, n) - buf; - - ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "grpc response: %*s%s, len: %uz", - m, buf, b->last - b->pos > 256 ? "..." : "", - b->last - b->pos); - } -#endif - - ctx = ngx_http_grpc_get_ctx(r); - - if (ctx == NULL) { - return NGX_ERROR; - } - - umcf = ngx_http_get_module_main_conf(r, ngx_http_upstream_module); - - for ( ;; ) { - - if (ctx->state < ngx_http_grpc_st_payload) { - - rc = ngx_http_grpc_parse_frame(r, ctx, b); - - if (rc == NGX_AGAIN) { - - /* - * there can be a lot of window update frames, - * so we reset buffer if it is empty and we haven't - * started parsing headers yet - */ - - if (!ctx->parsing_headers) { - b->pos = b->start; - b->last = b->pos; - } - - return NGX_AGAIN; - } - - if (rc == NGX_ERROR) { - return NGX_HTTP_UPSTREAM_INVALID_HEADER; - } - - /* - * RFC 7540 says that implementations MUST discard frames - * that have unknown or unsupported types. However, extension - * frames that appear in the middle of a header block are - * not permitted. Also, for obvious reasons CONTINUATION frames - * cannot appear before headers, and DATA frames are not expected - * to appear before all headers are parsed. - */ - - if (ctx->type == NGX_HTTP_V2_DATA_FRAME - || (ctx->type == NGX_HTTP_V2_CONTINUATION_FRAME - && !ctx->parsing_headers) - || (ctx->type != NGX_HTTP_V2_CONTINUATION_FRAME - && ctx->parsing_headers)) - { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "upstream sent unexpected http2 frame: %d", - ctx->type); - return NGX_HTTP_UPSTREAM_INVALID_HEADER; - } - - if (ctx->stream_id && ctx->stream_id != ctx->id) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "upstream sent frame for unknown stream %ui", - ctx->stream_id); - return NGX_HTTP_UPSTREAM_INVALID_HEADER; - } - } - - /* frame payload */ - - if (ctx->type == NGX_HTTP_V2_RST_STREAM_FRAME) { - - rc = ngx_http_grpc_parse_rst_stream(r, ctx, b); - - if (rc == NGX_AGAIN) { - return NGX_AGAIN; - } - - if (rc == NGX_ERROR) { - return NGX_HTTP_UPSTREAM_INVALID_HEADER; - } - - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "upstream rejected request with error %ui", - ctx->error); - - return NGX_HTTP_UPSTREAM_INVALID_HEADER; - } - - if (ctx->type == NGX_HTTP_V2_GOAWAY_FRAME) { - - rc = ngx_http_grpc_parse_goaway(r, ctx, b); - - if (rc == NGX_AGAIN) { - return NGX_AGAIN; - } - - if (rc == NGX_ERROR) { - return NGX_HTTP_UPSTREAM_INVALID_HEADER; - } - - /* - * If stream_id is lower than one we use, our - * request won't be processed and needs to be retried. - * If stream_id is greater or equal to the one we use, - * we can continue normally (except we can't use this - * connection for additional requests). If there is - * a real error, the connection will be closed. - */ - - if (ctx->stream_id < ctx->id) { - - /* TODO: we can retry non-idempotent requests */ - - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "upstream sent goaway with error %ui", - ctx->error); - - return NGX_HTTP_UPSTREAM_INVALID_HEADER; - } - - continue; - } - - if (ctx->type == NGX_HTTP_V2_WINDOW_UPDATE_FRAME) { - - rc = ngx_http_grpc_parse_window_update(r, ctx, b); - - if (rc == NGX_AGAIN) { - return NGX_AGAIN; - } - - if (rc == NGX_ERROR) { - return NGX_HTTP_UPSTREAM_INVALID_HEADER; - } - - if (ctx->in) { - ngx_post_event(u->peer.connection->write, &ngx_posted_events); - } - - continue; - } - - if (ctx->type == NGX_HTTP_V2_SETTINGS_FRAME) { - - rc = ngx_http_grpc_parse_settings(r, ctx, b); - - if (rc == NGX_AGAIN) { - return NGX_AGAIN; - } - - if (rc == NGX_ERROR) { - return NGX_HTTP_UPSTREAM_INVALID_HEADER; - } - - if (ctx->in) { - ngx_post_event(u->peer.connection->write, &ngx_posted_events); - } - - continue; - } - - if (ctx->type == NGX_HTTP_V2_PING_FRAME) { - - rc = ngx_http_grpc_parse_ping(r, ctx, b); - - if (rc == NGX_AGAIN) { - return NGX_AGAIN; - } - - if (rc == NGX_ERROR) { - return NGX_HTTP_UPSTREAM_INVALID_HEADER; - } - - ngx_post_event(u->peer.connection->write, &ngx_posted_events); - continue; - } - - if (ctx->type == NGX_HTTP_V2_PUSH_PROMISE_FRAME) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "upstream sent unexpected push promise frame"); - return NGX_HTTP_UPSTREAM_INVALID_HEADER; - } - - if (ctx->type != NGX_HTTP_V2_HEADERS_FRAME - && ctx->type != NGX_HTTP_V2_CONTINUATION_FRAME) - { - /* priority, unknown frames */ - - if (b->last - b->pos < (ssize_t) ctx->rest) { - ctx->rest -= b->last - b->pos; - b->pos = b->last; - return NGX_AGAIN; - } - - b->pos += ctx->rest; - ctx->rest = 0; - ctx->state = ngx_http_grpc_st_start; - - continue; - } - - /* headers */ - - for ( ;; ) { - - rc = ngx_http_grpc_parse_header(r, ctx, b); - - if (rc == NGX_AGAIN) { - break; - } - - if (rc == NGX_OK) { - - /* a header line has been parsed successfully */ - - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "grpc header: \"%V: %V\"", - &ctx->name, &ctx->value); - - if (ctx->name.len && ctx->name.data[0] == ':') { - - if (ctx->name.len != sizeof(":status") - 1 - || ngx_strncmp(ctx->name.data, ":status", - sizeof(":status") - 1) - != 0) - { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "upstream sent invalid header \"%V: %V\"", - &ctx->name, &ctx->value); - return NGX_HTTP_UPSTREAM_INVALID_HEADER; - } - - if (ctx->status) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "upstream sent duplicate :status header"); - return NGX_HTTP_UPSTREAM_INVALID_HEADER; - } - - status_line = &ctx->value; - - if (status_line->len != 3) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "upstream sent invalid :status \"%V\"", - status_line); - return NGX_HTTP_UPSTREAM_INVALID_HEADER; - } - - status = ngx_atoi(status_line->data, 3); - - if (status == NGX_ERROR) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "upstream sent invalid :status \"%V\"", - status_line); - return NGX_HTTP_UPSTREAM_INVALID_HEADER; - } - - if (status < NGX_HTTP_OK) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "upstream sent unexpected :status \"%V\"", - status_line); - return NGX_HTTP_UPSTREAM_INVALID_HEADER; - } - - u->headers_in.status_n = status; - - if (u->state && u->state->status == 0) { - u->state->status = status; - } - - ctx->status = 1; - - continue; - - } else if (!ctx->status) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "upstream sent no :status header"); - return NGX_HTTP_UPSTREAM_INVALID_HEADER; - } - - h = ngx_list_push(&u->headers_in.headers); - if (h == NULL) { - return NGX_ERROR; - } - - h->key = ctx->name; - h->value = ctx->value; - h->lowcase_key = h->key.data; - h->hash = ngx_hash_key(h->key.data, h->key.len); - - hh = ngx_hash_find(&umcf->headers_in_hash, h->hash, - h->lowcase_key, h->key.len); - - if (hh && hh->handler(r, h, hh->offset) != NGX_OK) { - return NGX_ERROR; - } - - continue; - } - - if (rc == NGX_HTTP_PARSE_HEADER_DONE) { - - /* a whole header has been parsed successfully */ - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "grpc header done"); - - if (ctx->end_stream) { - u->headers_in.content_length_n = 0; - - if (ctx->in == NULL - && ctx->out == NULL - && ctx->output_closed - && !ctx->output_blocked - && b->last == b->pos) - { - u->keepalive = 1; - } - } - - return NGX_OK; - } - - /* there was error while a header line parsing */ - - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "upstream sent invalid header"); - - return NGX_HTTP_UPSTREAM_INVALID_HEADER; - } - - /* rc == NGX_AGAIN */ - - if (ctx->rest == 0) { - ctx->state = ngx_http_grpc_st_start; - continue; - } - - return NGX_AGAIN; - } -} - - -static ngx_int_t -ngx_http_grpc_filter_init(void *data) -{ - ngx_http_grpc_ctx_t *ctx = data; - - ngx_http_request_t *r; - ngx_http_upstream_t *u; - - r = ctx->request; - u = r->upstream; - - u->length = 1; - - if (ctx->end_stream) { - u->length = 0; - } - - return NGX_OK; -} - - -static ngx_int_t -ngx_http_grpc_filter(void *data, ssize_t bytes) -{ - ngx_http_grpc_ctx_t *ctx = data; - - ngx_int_t rc; - ngx_buf_t *b, *buf; - ngx_chain_t *cl, **ll; - ngx_table_elt_t *h; - ngx_http_request_t *r; - ngx_http_upstream_t *u; - - r = ctx->request; - u = r->upstream; - b = &u->buffer; - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "grpc filter bytes:%z", bytes); - - b->pos = b->last; - b->last += bytes; - - for (cl = u->out_bufs, ll = &u->out_bufs; cl; cl = cl->next) { - ll = &cl->next; - } - - for ( ;; ) { - - if (ctx->state < ngx_http_grpc_st_payload) { - - rc = ngx_http_grpc_parse_frame(r, ctx, b); - - if (rc == NGX_AGAIN) { - - if (ctx->done) { - - /* - * We have finished parsing the response and the - * remaining control frames. If there are unsent - * control frames, post a write event to send them. - */ - - if (ctx->out) { - ngx_post_event(u->peer.connection->write, - &ngx_posted_events); - return NGX_AGAIN; - } - - u->length = 0; - - if (ctx->in == NULL - && ctx->output_closed - && !ctx->output_blocked - && ctx->state == ngx_http_grpc_st_start) - { - u->keepalive = 1; - } - - break; - } - - return NGX_AGAIN; - } - - if (rc == NGX_ERROR) { - return NGX_ERROR; - } - - if ((ctx->type == NGX_HTTP_V2_CONTINUATION_FRAME - && !ctx->parsing_headers) - || (ctx->type != NGX_HTTP_V2_CONTINUATION_FRAME - && ctx->parsing_headers)) - { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "upstream sent unexpected http2 frame: %d", - ctx->type); - return NGX_ERROR; - } - - if (ctx->type == NGX_HTTP_V2_DATA_FRAME) { - - if (ctx->stream_id != ctx->id) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "upstream sent data frame " - "for unknown stream %ui", - ctx->stream_id); - return NGX_ERROR; - } - - if (ctx->rest > ctx->recv_window) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "upstream violated stream flow control, " - "received %uz data frame with window %uz", - ctx->rest, ctx->recv_window); - return NGX_ERROR; - } - - if (ctx->rest > ctx->connection->recv_window) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "upstream violated connection flow control, " - "received %uz data frame with window %uz", - ctx->rest, ctx->connection->recv_window); - return NGX_ERROR; - } - - ctx->recv_window -= ctx->rest; - ctx->connection->recv_window -= ctx->rest; - - if (ctx->connection->recv_window < NGX_HTTP_V2_MAX_WINDOW / 4 - || ctx->recv_window < NGX_HTTP_V2_MAX_WINDOW / 4) - { - if (ngx_http_grpc_send_window_update(r, ctx) != NGX_OK) { - return NGX_ERROR; - } - - ngx_post_event(u->peer.connection->write, - &ngx_posted_events); - } - } - - if (ctx->stream_id && ctx->stream_id != ctx->id) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "upstream sent frame for unknown stream %ui", - ctx->stream_id); - return NGX_ERROR; - } - - if (ctx->stream_id && ctx->done) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "upstream sent frame for closed stream %ui", - ctx->stream_id); - return NGX_ERROR; - } - - ctx->padding = 0; - } - - if (ctx->state == ngx_http_grpc_st_padding) { - - if (b->last - b->pos < (ssize_t) ctx->rest) { - ctx->rest -= b->last - b->pos; - b->pos = b->last; - return NGX_AGAIN; - } - - b->pos += ctx->rest; - ctx->rest = 0; - ctx->state = ngx_http_grpc_st_start; - - if (ctx->flags & NGX_HTTP_V2_END_STREAM_FLAG) { - ctx->done = 1; - } - - continue; - } - - /* frame payload */ - - if (ctx->type == NGX_HTTP_V2_RST_STREAM_FRAME) { - - rc = ngx_http_grpc_parse_rst_stream(r, ctx, b); - - if (rc == NGX_AGAIN) { - return NGX_AGAIN; - } - - if (rc == NGX_ERROR) { - return NGX_ERROR; - } - - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "upstream rejected request with error %ui", - ctx->error); - - return NGX_ERROR; - } - - if (ctx->type == NGX_HTTP_V2_GOAWAY_FRAME) { - - rc = ngx_http_grpc_parse_goaway(r, ctx, b); - - if (rc == NGX_AGAIN) { - return NGX_AGAIN; - } - - if (rc == NGX_ERROR) { - return NGX_ERROR; - } - - /* - * If stream_id is lower than one we use, our - * request won't be processed and needs to be retried. - * If stream_id is greater or equal to the one we use, - * we can continue normally (except we can't use this - * connection for additional requests). If there is - * a real error, the connection will be closed. - */ - - if (ctx->stream_id < ctx->id) { - - /* TODO: we can retry non-idempotent requests */ - - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "upstream sent goaway with error %ui", - ctx->error); - - return NGX_ERROR; - } - - continue; - } - - if (ctx->type == NGX_HTTP_V2_WINDOW_UPDATE_FRAME) { - - rc = ngx_http_grpc_parse_window_update(r, ctx, b); - - if (rc == NGX_AGAIN) { - return NGX_AGAIN; - } - - if (rc == NGX_ERROR) { - return NGX_ERROR; - } - - if (ctx->in) { - ngx_post_event(u->peer.connection->write, &ngx_posted_events); - } - - continue; - } - - if (ctx->type == NGX_HTTP_V2_SETTINGS_FRAME) { - - rc = ngx_http_grpc_parse_settings(r, ctx, b); - - if (rc == NGX_AGAIN) { - return NGX_AGAIN; - } - - if (rc == NGX_ERROR) { - return NGX_ERROR; - } - - if (ctx->in) { - ngx_post_event(u->peer.connection->write, &ngx_posted_events); - } - - continue; - } - - if (ctx->type == NGX_HTTP_V2_PING_FRAME) { - - rc = ngx_http_grpc_parse_ping(r, ctx, b); - - if (rc == NGX_AGAIN) { - return NGX_AGAIN; - } - - if (rc == NGX_ERROR) { - return NGX_ERROR; - } - - ngx_post_event(u->peer.connection->write, &ngx_posted_events); - continue; - } - - if (ctx->type == NGX_HTTP_V2_PUSH_PROMISE_FRAME) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "upstream sent unexpected push promise frame"); - return NGX_ERROR; - } - - if (ctx->type == NGX_HTTP_V2_HEADERS_FRAME - || ctx->type == NGX_HTTP_V2_CONTINUATION_FRAME) - { - for ( ;; ) { - - rc = ngx_http_grpc_parse_header(r, ctx, b); - - if (rc == NGX_AGAIN) { - break; - } - - if (rc == NGX_OK) { - - /* a header line has been parsed successfully */ - - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "grpc trailer: \"%V: %V\"", - &ctx->name, &ctx->value); - - if (ctx->name.len && ctx->name.data[0] == ':') { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "upstream sent invalid " - "trailer \"%V: %V\"", - &ctx->name, &ctx->value); - return NGX_ERROR; - } - - h = ngx_list_push(&u->headers_in.trailers); - if (h == NULL) { - return NGX_ERROR; - } - - h->key = ctx->name; - h->value = ctx->value; - h->lowcase_key = h->key.data; - h->hash = ngx_hash_key(h->key.data, h->key.len); - - continue; - } - - if (rc == NGX_HTTP_PARSE_HEADER_DONE) { - - /* a whole header has been parsed successfully */ - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "grpc trailer done"); - - if (ctx->end_stream) { - ctx->done = 1; - break; - } - - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "upstream sent trailer without " - "end stream flag"); - return NGX_ERROR; - } - - /* there was error while a header line parsing */ - - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "upstream sent invalid trailer"); - - return NGX_ERROR; - } - - if (rc == NGX_HTTP_PARSE_HEADER_DONE) { - continue; - } - - /* rc == NGX_AGAIN */ - - if (ctx->rest == 0) { - ctx->state = ngx_http_grpc_st_start; - continue; - } - - return NGX_AGAIN; - } - - if (ctx->type != NGX_HTTP_V2_DATA_FRAME) { - - /* priority, unknown frames */ - - if (b->last - b->pos < (ssize_t) ctx->rest) { - ctx->rest -= b->last - b->pos; - b->pos = b->last; - return NGX_AGAIN; - } - - b->pos += ctx->rest; - ctx->rest = 0; - ctx->state = ngx_http_grpc_st_start; - - continue; - } - - /* - * data frame: - * - * +---------------+ - * |Pad Length? (8)| - * +---------------+-----------------------------------------------+ - * | Data (*) ... - * +---------------------------------------------------------------+ - * | Padding (*) ... - * +---------------------------------------------------------------+ - */ - - if (ctx->flags & NGX_HTTP_V2_PADDED_FLAG) { - - if (ctx->rest == 0) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "upstream sent too short http2 frame"); - return NGX_ERROR; - } - - if (b->pos == b->last) { - return NGX_AGAIN; - } - - ctx->flags &= ~NGX_HTTP_V2_PADDED_FLAG; - ctx->padding = *b->pos++; - ctx->rest -= 1; - - if (ctx->padding > ctx->rest) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "upstream sent http2 frame with too long " - "padding: %d in frame %uz", - ctx->padding, ctx->rest); - return NGX_ERROR; - } - - continue; - } - - if (ctx->rest == ctx->padding) { - goto done; - } - - if (b->pos == b->last) { - return NGX_AGAIN; - } - - cl = ngx_chain_get_free_buf(r->pool, &u->free_bufs); - if (cl == NULL) { - return NGX_ERROR; - } - - *ll = cl; - ll = &cl->next; - - buf = cl->buf; - - buf->flush = 1; - buf->memory = 1; - - buf->pos = b->pos; - buf->tag = u->output.tag; - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "grpc output buf %p", buf->pos); - - if (b->last - b->pos < (ssize_t) ctx->rest - ctx->padding) { - - ctx->rest -= b->last - b->pos; - b->pos = b->last; - buf->last = b->pos; - - return NGX_AGAIN; - } - - b->pos += ctx->rest - ctx->padding; - buf->last = b->pos; - ctx->rest = ctx->padding; - - done: - - if (ctx->padding) { - ctx->state = ngx_http_grpc_st_padding; - continue; - } - - ctx->state = ngx_http_grpc_st_start; - - if (ctx->flags & NGX_HTTP_V2_END_STREAM_FLAG) { - ctx->done = 1; - } - } - - return NGX_OK; -} - - -static ngx_int_t -ngx_http_grpc_parse_frame(ngx_http_request_t *r, ngx_http_grpc_ctx_t *ctx, - ngx_buf_t *b) -{ - u_char ch, *p; - ngx_http_grpc_state_e state; - - state = ctx->state; - - for (p = b->pos; p < b->last; p++) { - ch = *p; - -#if 0 - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "grpc frame byte: %02Xd, s:%d", ch, state); -#endif - - switch (state) { - - case ngx_http_grpc_st_start: - ctx->rest = ch << 16; - state = ngx_http_grpc_st_length_2; - break; - - case ngx_http_grpc_st_length_2: - ctx->rest |= ch << 8; - state = ngx_http_grpc_st_length_3; - break; - - case ngx_http_grpc_st_length_3: - ctx->rest |= ch; - - if (ctx->rest > NGX_HTTP_V2_DEFAULT_FRAME_SIZE) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "upstream sent too large http2 frame: %uz", - ctx->rest); - return NGX_ERROR; - } - - state = ngx_http_grpc_st_type; - break; - - case ngx_http_grpc_st_type: - ctx->type = ch; - state = ngx_http_grpc_st_flags; - break; - - case ngx_http_grpc_st_flags: - ctx->flags = ch; - state = ngx_http_grpc_st_stream_id; - break; - - case ngx_http_grpc_st_stream_id: - ctx->stream_id = (ch & 0x7f) << 24; - state = ngx_http_grpc_st_stream_id_2; - break; - - case ngx_http_grpc_st_stream_id_2: - ctx->stream_id |= ch << 16; - state = ngx_http_grpc_st_stream_id_3; - break; - - case ngx_http_grpc_st_stream_id_3: - ctx->stream_id |= ch << 8; - state = ngx_http_grpc_st_stream_id_4; - break; - - case ngx_http_grpc_st_stream_id_4: - ctx->stream_id |= ch; - - ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "grpc frame: %d, len: %uz, f:%d, i:%ui", - ctx->type, ctx->rest, ctx->flags, ctx->stream_id); - - b->pos = p + 1; - - ctx->state = ngx_http_grpc_st_payload; - ctx->frame_state = 0; - - return NGX_OK; - - /* suppress warning */ - case ngx_http_grpc_st_payload: - case ngx_http_grpc_st_padding: - break; - } - } - - b->pos = p; - ctx->state = state; - - return NGX_AGAIN; -} - - -static ngx_int_t -ngx_http_grpc_parse_header(ngx_http_request_t *r, ngx_http_grpc_ctx_t *ctx, - ngx_buf_t *b) -{ - u_char ch, *p, *last; - size_t min; - ngx_int_t rc; - enum { - sw_start = 0, - sw_padding_length, - sw_dependency, - sw_dependency_2, - sw_dependency_3, - sw_dependency_4, - sw_weight, - sw_fragment, - sw_padding - } state; - - state = ctx->frame_state; - - if (state == sw_start) { - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "grpc parse header: start"); - - if (ctx->type == NGX_HTTP_V2_HEADERS_FRAME) { - ctx->parsing_headers = 1; - ctx->fragment_state = 0; - - min = (ctx->flags & NGX_HTTP_V2_PADDED_FLAG ? 1 : 0) - + (ctx->flags & NGX_HTTP_V2_PRIORITY_FLAG ? 5 : 0); - - if (ctx->rest < min) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "upstream sent headers frame " - "with invalid length: %uz", - ctx->rest); - return NGX_ERROR; - } - - if (ctx->flags & NGX_HTTP_V2_END_STREAM_FLAG) { - ctx->end_stream = 1; - } - - if (ctx->flags & NGX_HTTP_V2_PADDED_FLAG) { - state = sw_padding_length; - - } else if (ctx->flags & NGX_HTTP_V2_PRIORITY_FLAG) { - state = sw_dependency; - - } else { - state = sw_fragment; - } - - } else if (ctx->type == NGX_HTTP_V2_CONTINUATION_FRAME) { - state = sw_fragment; - } - - ctx->padding = 0; - ctx->frame_state = state; - } - - if (state < sw_fragment) { - - if (b->last - b->pos < (ssize_t) ctx->rest) { - last = b->last; - - } else { - last = b->pos + ctx->rest; - } - - for (p = b->pos; p < last; p++) { - ch = *p; - -#if 0 - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "grpc header byte: %02Xd s:%d", ch, state); -#endif - - /* - * headers frame: - * - * +---------------+ - * |Pad Length? (8)| - * +-+-------------+----------------------------------------------+ - * |E| Stream Dependency? (31) | - * +-+-------------+----------------------------------------------+ - * | Weight? (8) | - * +-+-------------+----------------------------------------------+ - * | Header Block Fragment (*) ... - * +--------------------------------------------------------------+ - * | Padding (*) ... - * +--------------------------------------------------------------+ - */ - - switch (state) { - - case sw_padding_length: - - ctx->padding = ch; - - if (ctx->flags & NGX_HTTP_V2_PRIORITY_FLAG) { - state = sw_dependency; - break; - } - - goto fragment; - - case sw_dependency: - state = sw_dependency_2; - break; - - case sw_dependency_2: - state = sw_dependency_3; - break; - - case sw_dependency_3: - state = sw_dependency_4; - break; - - case sw_dependency_4: - state = sw_weight; - break; - - case sw_weight: - goto fragment; - - /* suppress warning */ - case sw_start: - case sw_fragment: - case sw_padding: - break; - } - } - - ctx->rest -= p - b->pos; - b->pos = p; - - ctx->frame_state = state; - return NGX_AGAIN; - - fragment: - - p++; - ctx->rest -= p - b->pos; - b->pos = p; - - if (ctx->padding > ctx->rest) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "upstream sent http2 frame with too long " - "padding: %d in frame %uz", - ctx->padding, ctx->rest); - return NGX_ERROR; - } - - state = sw_fragment; - ctx->frame_state = state; - } - - if (state == sw_fragment) { - - rc = ngx_http_grpc_parse_fragment(r, ctx, b); - - if (rc == NGX_AGAIN) { - return NGX_AGAIN; - } - - if (rc == NGX_ERROR) { - return NGX_ERROR; - } - - if (rc == NGX_OK) { - return NGX_OK; - } - - /* rc == NGX_DONE */ - - state = sw_padding; - ctx->frame_state = state; - } - - if (state == sw_padding) { - - if (b->last - b->pos < (ssize_t) ctx->rest) { - - ctx->rest -= b->last - b->pos; - b->pos = b->last; - - return NGX_AGAIN; - } - - b->pos += ctx->rest; - ctx->rest = 0; - - ctx->state = ngx_http_grpc_st_start; - - if (ctx->flags & NGX_HTTP_V2_END_HEADERS_FLAG) { - - if (ctx->fragment_state) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "upstream sent truncated http2 header"); - return NGX_ERROR; - } - - ctx->parsing_headers = 0; - - return NGX_HTTP_PARSE_HEADER_DONE; - } - - return NGX_AGAIN; - } - - /* unreachable */ - - return NGX_ERROR; -} - - -static ngx_int_t -ngx_http_grpc_parse_fragment(ngx_http_request_t *r, ngx_http_grpc_ctx_t *ctx, - ngx_buf_t *b) -{ - u_char ch, *p, *last; - size_t size; - ngx_uint_t index, size_update; - enum { - sw_start = 0, - sw_index, - sw_name_length, - sw_name_length_2, - sw_name_length_3, - sw_name_length_4, - sw_name, - sw_name_bytes, - sw_value_length, - sw_value_length_2, - sw_value_length_3, - sw_value_length_4, - sw_value, - sw_value_bytes - } state; - - /* header block fragment */ - -#if 0 - ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "grpc header fragment %p:%p rest:%uz", - b->pos, b->last, ctx->rest); -#endif - - if (b->last - b->pos < (ssize_t) ctx->rest - ctx->padding) { - last = b->last; - - } else { - last = b->pos + ctx->rest - ctx->padding; - } - - state = ctx->fragment_state; - - for (p = b->pos; p < last; p++) { - ch = *p; - -#if 0 - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "grpc header byte: %02Xd s:%d", ch, state); -#endif - - switch (state) { - - case sw_start: - ctx->index = 0; - - if ((ch & 0x80) == 0x80) { - /* - * indexed header: - * - * 0 1 2 3 4 5 6 7 - * +---+---+---+---+---+---+---+---+ - * | 1 | Index (7+) | - * +---+---------------------------+ - */ - - index = ch & ~0x80; - - if (index == 0 || index > 61) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "upstream sent invalid http2 " - "table index: %ui", index); - return NGX_ERROR; - } - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "grpc indexed header: %ui", index); - - ctx->index = index; - ctx->literal = 0; - - goto done; - - } else if ((ch & 0xc0) == 0x40) { - /* - * literal header with incremental indexing: - * - * 0 1 2 3 4 5 6 7 - * +---+---+---+---+---+---+---+---+ - * | 0 | 1 | Index (6+) | - * +---+---+-----------------------+ - * | H | Value Length (7+) | - * +---+---------------------------+ - * | Value String (Length octets) | - * +-------------------------------+ - * - * 0 1 2 3 4 5 6 7 - * +---+---+---+---+---+---+---+---+ - * | 0 | 1 | 0 | - * +---+---+-----------------------+ - * | H | Name Length (7+) | - * +---+---------------------------+ - * | Name String (Length octets) | - * +---+---------------------------+ - * | H | Value Length (7+) | - * +---+---------------------------+ - * | Value String (Length octets) | - * +-------------------------------+ - */ - - index = ch & ~0xc0; - - if (index > 61) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "upstream sent invalid http2 " - "table index: %ui", index); - return NGX_ERROR; - } - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "grpc literal header: %ui", index); - - if (index == 0) { - state = sw_name_length; - break; - } - - ctx->index = index; - ctx->literal = 1; - - state = sw_value_length; - break; - - } else if ((ch & 0xe0) == 0x20) { - /* - * dynamic table size update: - * - * 0 1 2 3 4 5 6 7 - * +---+---+---+---+---+---+---+---+ - * | 0 | 0 | 1 | Max size (5+) | - * +---+---------------------------+ - */ - - size_update = ch & ~0xe0; - - if (size_update > 0) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "upstream sent invalid http2 " - "dynamic table size update: %ui", - size_update); - return NGX_ERROR; - } - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "grpc table size update: %ui", size_update); - - break; - - } else if ((ch & 0xf0) == 0x10) { - /* - * literal header field never indexed: - * - * 0 1 2 3 4 5 6 7 - * +---+---+---+---+---+---+---+---+ - * | 0 | 0 | 0 | 1 | Index (4+) | - * +---+---+-----------------------+ - * | H | Value Length (7+) | - * +---+---------------------------+ - * | Value String (Length octets) | - * +-------------------------------+ - * - * 0 1 2 3 4 5 6 7 - * +---+---+---+---+---+---+---+---+ - * | 0 | 0 | 0 | 1 | 0 | - * +---+---+-----------------------+ - * | H | Name Length (7+) | - * +---+---------------------------+ - * | Name String (Length octets) | - * +---+---------------------------+ - * | H | Value Length (7+) | - * +---+---------------------------+ - * | Value String (Length octets) | - * +-------------------------------+ - */ - - index = ch & ~0xf0; - - if (index == 0x0f) { - ctx->index = index; - ctx->literal = 1; - state = sw_index; - break; - } - - if (index == 0) { - state = sw_name_length; - break; - } - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "grpc literal header never indexed: %ui", - index); - - ctx->index = index; - ctx->literal = 1; - - state = sw_value_length; - break; - - } else if ((ch & 0xf0) == 0x00) { - /* - * literal header field without indexing: - * - * 0 1 2 3 4 5 6 7 - * +---+---+---+---+---+---+---+---+ - * | 0 | 0 | 0 | 0 | Index (4+) | - * +---+---+-----------------------+ - * | H | Value Length (7+) | - * +---+---------------------------+ - * | Value String (Length octets) | - * +-------------------------------+ - * - * 0 1 2 3 4 5 6 7 - * +---+---+---+---+---+---+---+---+ - * | 0 | 0 | 0 | 0 | 0 | - * +---+---+-----------------------+ - * | H | Name Length (7+) | - * +---+---------------------------+ - * | Name String (Length octets) | - * +---+---------------------------+ - * | H | Value Length (7+) | - * +---+---------------------------+ - * | Value String (Length octets) | - * +-------------------------------+ - */ - - index = ch & ~0xf0; - - if (index == 0x0f) { - ctx->index = index; - ctx->literal = 1; - state = sw_index; - break; - } - - if (index == 0) { - state = sw_name_length; - break; - } - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "grpc literal header without indexing: %ui", - index); - - ctx->index = index; - ctx->literal = 1; - - state = sw_value_length; - break; - } - - /* not reached */ - - return NGX_ERROR; - - case sw_index: - ctx->index = ctx->index + (ch & ~0x80); - - if (ch & 0x80) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "upstream sent http2 table index " - "with continuation flag"); - return NGX_ERROR; - } - - if (ctx->index > 61) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "upstream sent invalid http2 " - "table index: %ui", ctx->index); - return NGX_ERROR; - } - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "grpc header index: %ui", ctx->index); - - state = sw_value_length; - break; - - case sw_name_length: - ctx->field_huffman = ch & 0x80 ? 1 : 0; - ctx->field_length = ch & ~0x80; - - if (ctx->field_length == 0x7f) { - state = sw_name_length_2; - break; - } - - if (ctx->field_length == 0) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "upstream sent zero http2 " - "header name length"); - return NGX_ERROR; - } - - state = sw_name; - break; - - case sw_name_length_2: - ctx->field_length += ch & ~0x80; - - if (ch & 0x80) { - state = sw_name_length_3; - break; - } - - state = sw_name; - break; - - case sw_name_length_3: - ctx->field_length += (ch & ~0x80) << 7; - - if (ch & 0x80) { - state = sw_name_length_4; - break; - } - - state = sw_name; - break; - - case sw_name_length_4: - ctx->field_length += (ch & ~0x80) << 14; - - if (ch & 0x80) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "upstream sent too large http2 " - "header name length"); - return NGX_ERROR; - } - - state = sw_name; - break; - - case sw_name: - ctx->name.len = ctx->field_huffman ? - ctx->field_length * 8 / 5 : ctx->field_length; - - ctx->name.data = ngx_pnalloc(r->pool, ctx->name.len + 1); - if (ctx->name.data == NULL) { - return NGX_ERROR; - } - - ctx->field_end = ctx->name.data; - ctx->field_rest = ctx->field_length; - ctx->field_state = 0; - - state = sw_name_bytes; - - /* fall through */ - - case sw_name_bytes: - - ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "grpc name: len:%uz h:%d last:%uz, rest:%uz", - ctx->field_length, - ctx->field_huffman, - last - p, - ctx->rest - (p - b->pos)); - - size = ngx_min(last - p, (ssize_t) ctx->field_rest); - ctx->field_rest -= size; - - if (ctx->field_huffman) { - if (ngx_http_v2_huff_decode(&ctx->field_state, p, size, - &ctx->field_end, - ctx->field_rest == 0, - r->connection->log) - != NGX_OK) - { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "upstream sent invalid encoded header"); - return NGX_ERROR; - } - - ctx->name.len = ctx->field_end - ctx->name.data; - ctx->name.data[ctx->name.len] = '\0'; - - } else { - ctx->field_end = ngx_cpymem(ctx->field_end, p, size); - ctx->name.data[ctx->name.len] = '\0'; - } - - p += size - 1; - - if (ctx->field_rest == 0) { - state = sw_value_length; - } - - break; - - case sw_value_length: - ctx->field_huffman = ch & 0x80 ? 1 : 0; - ctx->field_length = ch & ~0x80; - - if (ctx->field_length == 0x7f) { - state = sw_value_length_2; - break; - } - - if (ctx->field_length == 0) { - ngx_str_set(&ctx->value, ""); - goto done; - } - - state = sw_value; - break; - - case sw_value_length_2: - ctx->field_length += ch & ~0x80; - - if (ch & 0x80) { - state = sw_value_length_3; - break; - } - - state = sw_value; - break; - - case sw_value_length_3: - ctx->field_length += (ch & ~0x80) << 7; - - if (ch & 0x80) { - state = sw_value_length_4; - break; - } - - state = sw_value; - break; - - case sw_value_length_4: - ctx->field_length += (ch & ~0x80) << 14; - - if (ch & 0x80) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "upstream sent too large http2 " - "header value length"); - return NGX_ERROR; - } - - state = sw_value; - break; - - case sw_value: - ctx->value.len = ctx->field_huffman ? - ctx->field_length * 8 / 5 : ctx->field_length; - - ctx->value.data = ngx_pnalloc(r->pool, ctx->value.len + 1); - if (ctx->value.data == NULL) { - return NGX_ERROR; - } - - ctx->field_end = ctx->value.data; - ctx->field_rest = ctx->field_length; - ctx->field_state = 0; - - state = sw_value_bytes; - - /* fall through */ - - case sw_value_bytes: - - ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "grpc value: len:%uz h:%d last:%uz, rest:%uz", - ctx->field_length, - ctx->field_huffman, - last - p, - ctx->rest - (p - b->pos)); - - size = ngx_min(last - p, (ssize_t) ctx->field_rest); - ctx->field_rest -= size; - - if (ctx->field_huffman) { - if (ngx_http_v2_huff_decode(&ctx->field_state, p, size, - &ctx->field_end, - ctx->field_rest == 0, - r->connection->log) - != NGX_OK) - { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "upstream sent invalid encoded header"); - return NGX_ERROR; - } - - ctx->value.len = ctx->field_end - ctx->value.data; - ctx->value.data[ctx->value.len] = '\0'; - - } else { - ctx->field_end = ngx_cpymem(ctx->field_end, p, size); - ctx->value.data[ctx->value.len] = '\0'; - } - - p += size - 1; - - if (ctx->field_rest == 0) { - goto done; - } - - break; - } - - continue; - - done: - - p++; - ctx->rest -= p - b->pos; - ctx->fragment_state = sw_start; - b->pos = p; - - if (ctx->index) { - ctx->name = *ngx_http_v2_get_static_name(ctx->index); - } - - if (ctx->index && !ctx->literal) { - ctx->value = *ngx_http_v2_get_static_value(ctx->index); - } - - if (!ctx->index) { - if (ngx_http_grpc_validate_header_name(r, &ctx->name) != NGX_OK) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "upstream sent invalid header: \"%V: %V\"", - &ctx->name, &ctx->value); - return NGX_ERROR; - } - } - - if (!ctx->index || ctx->literal) { - if (ngx_http_grpc_validate_header_value(r, &ctx->value) != NGX_OK) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "upstream sent invalid header: \"%V: %V\"", - &ctx->name, &ctx->value); - return NGX_ERROR; - } - } - - return NGX_OK; - } - - ctx->rest -= p - b->pos; - ctx->fragment_state = state; - b->pos = p; - - if (ctx->rest > ctx->padding) { - return NGX_AGAIN; - } - - return NGX_DONE; -} - - -static ngx_int_t -ngx_http_grpc_validate_header_name(ngx_http_request_t *r, ngx_str_t *s) -{ - u_char ch; - ngx_uint_t i; - - for (i = 0; i < s->len; i++) { - ch = s->data[i]; - - if (ch == ':' && i > 0) { - return NGX_ERROR; - } - - if (ch >= 'A' && ch <= 'Z') { - return NGX_ERROR; - } - - if (ch == '\0' || ch == CR || ch == LF) { - return NGX_ERROR; - } - } - - return NGX_OK; -} - - -static ngx_int_t -ngx_http_grpc_validate_header_value(ngx_http_request_t *r, ngx_str_t *s) -{ - u_char ch; - ngx_uint_t i; - - for (i = 0; i < s->len; i++) { - ch = s->data[i]; - - if (ch == '\0' || ch == CR || ch == LF) { - return NGX_ERROR; - } - } - - return NGX_OK; -} - - -static ngx_int_t -ngx_http_grpc_parse_rst_stream(ngx_http_request_t *r, ngx_http_grpc_ctx_t *ctx, - ngx_buf_t *b) -{ - u_char ch, *p, *last; - enum { - sw_start = 0, - sw_error_2, - sw_error_3, - sw_error_4 - } state; - - if (b->last - b->pos < (ssize_t) ctx->rest) { - last = b->last; - - } else { - last = b->pos + ctx->rest; - } - - state = ctx->frame_state; - - if (state == sw_start) { - if (ctx->rest != 4) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "upstream sent rst stream frame " - "with invalid length: %uz", - ctx->rest); - return NGX_ERROR; - } - } - - for (p = b->pos; p < last; p++) { - ch = *p; - -#if 0 - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "grpc rst byte: %02Xd s:%d", ch, state); -#endif - - switch (state) { - - case sw_start: - ctx->error = (ngx_uint_t) ch << 24; - state = sw_error_2; - break; - - case sw_error_2: - ctx->error |= ch << 16; - state = sw_error_3; - break; - - case sw_error_3: - ctx->error |= ch << 8; - state = sw_error_4; - break; - - case sw_error_4: - ctx->error |= ch; - state = sw_start; - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "grpc error: %ui", ctx->error); - - break; - } - } - - ctx->rest -= p - b->pos; - ctx->frame_state = state; - b->pos = p; - - if (ctx->rest > 0) { - return NGX_AGAIN; - } - - return NGX_OK; -} - - -static ngx_int_t -ngx_http_grpc_parse_goaway(ngx_http_request_t *r, ngx_http_grpc_ctx_t *ctx, - ngx_buf_t *b) -{ - u_char ch, *p, *last; - enum { - sw_start = 0, - sw_last_stream_id_2, - sw_last_stream_id_3, - sw_last_stream_id_4, - sw_error, - sw_error_2, - sw_error_3, - sw_error_4, - sw_debug - } state; - - if (b->last - b->pos < (ssize_t) ctx->rest) { - last = b->last; - - } else { - last = b->pos + ctx->rest; - } - - state = ctx->frame_state; - - if (state == sw_start) { - - if (ctx->stream_id) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "upstream sent goaway frame " - "with non-zero stream id: %ui", - ctx->stream_id); - return NGX_ERROR; - } - - if (ctx->rest < 8) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "upstream sent goaway frame " - "with invalid length: %uz", - ctx->rest); - return NGX_ERROR; - } - } - - for (p = b->pos; p < last; p++) { - ch = *p; - -#if 0 - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "grpc goaway byte: %02Xd s:%d", ch, state); -#endif - - switch (state) { - - case sw_start: - ctx->stream_id = (ch & 0x7f) << 24; - state = sw_last_stream_id_2; - break; - - case sw_last_stream_id_2: - ctx->stream_id |= ch << 16; - state = sw_last_stream_id_3; - break; - - case sw_last_stream_id_3: - ctx->stream_id |= ch << 8; - state = sw_last_stream_id_4; - break; - - case sw_last_stream_id_4: - ctx->stream_id |= ch; - state = sw_error; - break; - - case sw_error: - ctx->error = (ngx_uint_t) ch << 24; - state = sw_error_2; - break; - - case sw_error_2: - ctx->error |= ch << 16; - state = sw_error_3; - break; - - case sw_error_3: - ctx->error |= ch << 8; - state = sw_error_4; - break; - - case sw_error_4: - ctx->error |= ch; - state = sw_debug; - break; - - case sw_debug: - break; - } - } - - ctx->rest -= p - b->pos; - ctx->frame_state = state; - b->pos = p; - - if (ctx->rest > 0) { - return NGX_AGAIN; - } - - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "grpc goaway: %ui, stream %ui", - ctx->error, ctx->stream_id); - - ctx->state = ngx_http_grpc_st_start; - - return NGX_OK; -} - - -static ngx_int_t -ngx_http_grpc_parse_window_update(ngx_http_request_t *r, - ngx_http_grpc_ctx_t *ctx, ngx_buf_t *b) -{ - u_char ch, *p, *last; - enum { - sw_start = 0, - sw_size_2, - sw_size_3, - sw_size_4 - } state; - - if (b->last - b->pos < (ssize_t) ctx->rest) { - last = b->last; - - } else { - last = b->pos + ctx->rest; - } - - state = ctx->frame_state; - - if (state == sw_start) { - if (ctx->rest != 4) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "upstream sent window update frame " - "with invalid length: %uz", - ctx->rest); - return NGX_ERROR; - } - } - - for (p = b->pos; p < last; p++) { - ch = *p; - -#if 0 - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "grpc window update byte: %02Xd s:%d", ch, state); -#endif - - switch (state) { - - case sw_start: - ctx->window_update = (ch & 0x7f) << 24; - state = sw_size_2; - break; - - case sw_size_2: - ctx->window_update |= ch << 16; - state = sw_size_3; - break; - - case sw_size_3: - ctx->window_update |= ch << 8; - state = sw_size_4; - break; - - case sw_size_4: - ctx->window_update |= ch; - state = sw_start; - break; - } - } - - ctx->rest -= p - b->pos; - ctx->frame_state = state; - b->pos = p; - - if (ctx->rest > 0) { - return NGX_AGAIN; - } - - ctx->state = ngx_http_grpc_st_start; - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "grpc window update: %ui", ctx->window_update); - - if (ctx->stream_id) { - - if (ctx->window_update > (size_t) NGX_HTTP_V2_MAX_WINDOW - - ctx->send_window) - { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "upstream sent too large window update"); - return NGX_ERROR; - } - - ctx->send_window += ctx->window_update; - - } else { - - if (ctx->window_update > NGX_HTTP_V2_MAX_WINDOW - - ctx->connection->send_window) - { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "upstream sent too large window update"); - return NGX_ERROR; - } - - ctx->connection->send_window += ctx->window_update; - } - - return NGX_OK; -} - - -static ngx_int_t -ngx_http_grpc_parse_settings(ngx_http_request_t *r, ngx_http_grpc_ctx_t *ctx, - ngx_buf_t *b) -{ - u_char ch, *p, *last; - ssize_t window_update; - enum { - sw_start = 0, - sw_id, - sw_id_2, - sw_value, - sw_value_2, - sw_value_3, - sw_value_4 - } state; - - if (b->last - b->pos < (ssize_t) ctx->rest) { - last = b->last; - - } else { - last = b->pos + ctx->rest; - } - - state = ctx->frame_state; - - if (state == sw_start) { - - if (ctx->stream_id) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "upstream sent settings frame " - "with non-zero stream id: %ui", - ctx->stream_id); - return NGX_ERROR; - } - - if (ctx->flags & NGX_HTTP_V2_ACK_FLAG) { - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "grpc settings ack"); - - if (ctx->rest != 0) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "upstream sent settings frame " - "with ack flag and non-zero length: %uz", - ctx->rest); - return NGX_ERROR; - } - - ctx->state = ngx_http_grpc_st_start; - - return NGX_OK; - } - - if (ctx->rest % 6 != 0) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "upstream sent settings frame " - "with invalid length: %uz", - ctx->rest); - return NGX_ERROR; - } - - if (ctx->free == NULL && ctx->settings++ > 1000) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "upstream sent too many settings frames"); - return NGX_ERROR; - } - } - - for (p = b->pos; p < last; p++) { - ch = *p; - -#if 0 - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "grpc settings byte: %02Xd s:%d", ch, state); -#endif - - switch (state) { - - case sw_start: - case sw_id: - ctx->setting_id = ch << 8; - state = sw_id_2; - break; - - case sw_id_2: - ctx->setting_id |= ch; - state = sw_value; - break; - - case sw_value: - ctx->setting_value = (ngx_uint_t) ch << 24; - state = sw_value_2; - break; - - case sw_value_2: - ctx->setting_value |= ch << 16; - state = sw_value_3; - break; - - case sw_value_3: - ctx->setting_value |= ch << 8; - state = sw_value_4; - break; - - case sw_value_4: - ctx->setting_value |= ch; - state = sw_id; - - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "grpc setting: %ui %ui", - ctx->setting_id, ctx->setting_value); - - /* - * The following settings are defined by the protocol: - * - * SETTINGS_HEADER_TABLE_SIZE, SETTINGS_ENABLE_PUSH, - * SETTINGS_MAX_CONCURRENT_STREAMS, SETTINGS_INITIAL_WINDOW_SIZE, - * SETTINGS_MAX_FRAME_SIZE, SETTINGS_MAX_HEADER_LIST_SIZE - * - * Only SETTINGS_INITIAL_WINDOW_SIZE seems to be needed in - * a simple client. - */ - - if (ctx->setting_id == 0x04) { - /* SETTINGS_INITIAL_WINDOW_SIZE */ - - if (ctx->setting_value > NGX_HTTP_V2_MAX_WINDOW) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "upstream sent settings frame " - "with too large initial window size: %ui", - ctx->setting_value); - return NGX_ERROR; - } - - window_update = ctx->setting_value - - ctx->connection->init_window; - ctx->connection->init_window = ctx->setting_value; - - if (ctx->send_window > 0 - && window_update > (ssize_t) NGX_HTTP_V2_MAX_WINDOW - - ctx->send_window) - { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "upstream sent settings frame " - "with too large initial window size: %ui", - ctx->setting_value); - return NGX_ERROR; - } - - ctx->send_window += window_update; - } - - break; - } - } - - ctx->rest -= p - b->pos; - ctx->frame_state = state; - b->pos = p; - - if (ctx->rest > 0) { - return NGX_AGAIN; - } - - ctx->state = ngx_http_grpc_st_start; - - return ngx_http_grpc_send_settings_ack(r, ctx); -} - - -static ngx_int_t -ngx_http_grpc_parse_ping(ngx_http_request_t *r, - ngx_http_grpc_ctx_t *ctx, ngx_buf_t *b) -{ - u_char ch, *p, *last; - enum { - sw_start = 0, - sw_data_2, - sw_data_3, - sw_data_4, - sw_data_5, - sw_data_6, - sw_data_7, - sw_data_8 - } state; - - if (b->last - b->pos < (ssize_t) ctx->rest) { - last = b->last; - - } else { - last = b->pos + ctx->rest; - } - - state = ctx->frame_state; - - if (state == sw_start) { - - if (ctx->stream_id) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "upstream sent ping frame " - "with non-zero stream id: %ui", - ctx->stream_id); - return NGX_ERROR; - } - - if (ctx->rest != 8) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "upstream sent ping frame " - "with invalid length: %uz", - ctx->rest); - return NGX_ERROR; - } - - if (ctx->flags & NGX_HTTP_V2_ACK_FLAG) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "upstream sent ping frame with ack flag"); - return NGX_ERROR; - } - - if (ctx->free == NULL && ctx->pings++ > 1000) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "upstream sent too many ping frames"); - return NGX_ERROR; - } - } - - for (p = b->pos; p < last; p++) { - ch = *p; - -#if 0 - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "grpc ping byte: %02Xd s:%d", ch, state); -#endif - - if (state < sw_data_8) { - ctx->ping_data[state] = ch; - state++; - - } else { - ctx->ping_data[7] = ch; - state = sw_start; - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "grpc ping"); - } - } - - ctx->rest -= p - b->pos; - ctx->frame_state = state; - b->pos = p; - - if (ctx->rest > 0) { - return NGX_AGAIN; - } - - ctx->state = ngx_http_grpc_st_start; - - return ngx_http_grpc_send_ping_ack(r, ctx); -} - - -static ngx_int_t -ngx_http_grpc_send_settings_ack(ngx_http_request_t *r, ngx_http_grpc_ctx_t *ctx) -{ - ngx_chain_t *cl, **ll; - ngx_http_grpc_frame_t *f; - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "grpc send settings ack"); - - for (cl = ctx->out, ll = &ctx->out; cl; cl = cl->next) { - ll = &cl->next; - } - - cl = ngx_http_grpc_get_buf(r, ctx); - if (cl == NULL) { - return NGX_ERROR; - } - - f = (ngx_http_grpc_frame_t *) cl->buf->last; - cl->buf->last += sizeof(ngx_http_grpc_frame_t); - - f->length_0 = 0; - f->length_1 = 0; - f->length_2 = 0; - f->type = NGX_HTTP_V2_SETTINGS_FRAME; - f->flags = NGX_HTTP_V2_ACK_FLAG; - f->stream_id_0 = 0; - f->stream_id_1 = 0; - f->stream_id_2 = 0; - f->stream_id_3 = 0; - - *ll = cl; - - return NGX_OK; -} - - -static ngx_int_t -ngx_http_grpc_send_ping_ack(ngx_http_request_t *r, ngx_http_grpc_ctx_t *ctx) -{ - ngx_chain_t *cl, **ll; - ngx_http_grpc_frame_t *f; - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "grpc send ping ack"); - - for (cl = ctx->out, ll = &ctx->out; cl; cl = cl->next) { - ll = &cl->next; - } - - cl = ngx_http_grpc_get_buf(r, ctx); - if (cl == NULL) { - return NGX_ERROR; - } - - f = (ngx_http_grpc_frame_t *) cl->buf->last; - cl->buf->last += sizeof(ngx_http_grpc_frame_t); - - f->length_0 = 0; - f->length_1 = 0; - f->length_2 = 8; - f->type = NGX_HTTP_V2_PING_FRAME; - f->flags = NGX_HTTP_V2_ACK_FLAG; - f->stream_id_0 = 0; - f->stream_id_1 = 0; - f->stream_id_2 = 0; - f->stream_id_3 = 0; - - cl->buf->last = ngx_copy(cl->buf->last, ctx->ping_data, 8); - - *ll = cl; - - return NGX_OK; -} - - -static ngx_int_t -ngx_http_grpc_send_window_update(ngx_http_request_t *r, - ngx_http_grpc_ctx_t *ctx) -{ - size_t n; - ngx_chain_t *cl, **ll; - ngx_http_grpc_frame_t *f; - - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "grpc send window update: %uz %uz", - ctx->connection->recv_window, ctx->recv_window); - - for (cl = ctx->out, ll = &ctx->out; cl; cl = cl->next) { - ll = &cl->next; - } - - cl = ngx_http_grpc_get_buf(r, ctx); - if (cl == NULL) { - return NGX_ERROR; - } - - f = (ngx_http_grpc_frame_t *) cl->buf->last; - cl->buf->last += sizeof(ngx_http_grpc_frame_t); - - f->length_0 = 0; - f->length_1 = 0; - f->length_2 = 4; - f->type = NGX_HTTP_V2_WINDOW_UPDATE_FRAME; - f->flags = 0; - f->stream_id_0 = 0; - f->stream_id_1 = 0; - f->stream_id_2 = 0; - f->stream_id_3 = 0; - - n = NGX_HTTP_V2_MAX_WINDOW - ctx->connection->recv_window; - ctx->connection->recv_window = NGX_HTTP_V2_MAX_WINDOW; - - *cl->buf->last++ = (u_char) ((n >> 24) & 0xff); - *cl->buf->last++ = (u_char) ((n >> 16) & 0xff); - *cl->buf->last++ = (u_char) ((n >> 8) & 0xff); - *cl->buf->last++ = (u_char) (n & 0xff); - - f = (ngx_http_grpc_frame_t *) cl->buf->last; - cl->buf->last += sizeof(ngx_http_grpc_frame_t); - - f->length_0 = 0; - f->length_1 = 0; - f->length_2 = 4; - f->type = NGX_HTTP_V2_WINDOW_UPDATE_FRAME; - f->flags = 0; - f->stream_id_0 = (u_char) ((ctx->id >> 24) & 0xff); - f->stream_id_1 = (u_char) ((ctx->id >> 16) & 0xff); - f->stream_id_2 = (u_char) ((ctx->id >> 8) & 0xff); - f->stream_id_3 = (u_char) (ctx->id & 0xff); - - n = NGX_HTTP_V2_MAX_WINDOW - ctx->recv_window; - ctx->recv_window = NGX_HTTP_V2_MAX_WINDOW; - - *cl->buf->last++ = (u_char) ((n >> 24) & 0xff); - *cl->buf->last++ = (u_char) ((n >> 16) & 0xff); - *cl->buf->last++ = (u_char) ((n >> 8) & 0xff); - *cl->buf->last++ = (u_char) (n & 0xff); - - *ll = cl; - - return NGX_OK; -} - - -static ngx_chain_t * -ngx_http_grpc_get_buf(ngx_http_request_t *r, ngx_http_grpc_ctx_t *ctx) -{ - u_char *start; - ngx_buf_t *b; - ngx_chain_t *cl; - - cl = ngx_chain_get_free_buf(r->pool, &ctx->free); - if (cl == NULL) { - return NULL; - } - - b = cl->buf; - start = b->start; - - if (start == NULL) { - - /* - * each buffer is large enough to hold two window update - * frames in a row - */ - - start = ngx_palloc(r->pool, 2 * sizeof(ngx_http_grpc_frame_t) + 8); - if (start == NULL) { - return NULL; - } - - } - - ngx_memzero(b, sizeof(ngx_buf_t)); - - b->start = start; - b->pos = start; - b->last = start; - b->end = start + 2 * sizeof(ngx_http_grpc_frame_t) + 8; - - b->tag = (ngx_buf_tag_t) &ngx_http_grpc_body_output_filter; - b->temporary = 1; - b->flush = 1; - - return cl; -} - - -static ngx_http_grpc_ctx_t * -ngx_http_grpc_get_ctx(ngx_http_request_t *r) -{ - ngx_http_grpc_ctx_t *ctx; - ngx_http_upstream_t *u; - - ctx = ngx_http_get_module_ctx(r, ngx_http_grpc_module); - - if (ctx->connection == NULL) { - u = r->upstream; - - if (ngx_http_grpc_get_connection_data(r, ctx, &u->peer) != NGX_OK) { - return NULL; - } - } - - return ctx; -} - - -static ngx_int_t -ngx_http_grpc_get_connection_data(ngx_http_request_t *r, - ngx_http_grpc_ctx_t *ctx, ngx_peer_connection_t *pc) -{ - ngx_connection_t *c; - ngx_pool_cleanup_t *cln; - - c = pc->connection; - - if (pc->cached) { - - /* - * for cached connections, connection data can be found - * in the cleanup handler - */ - - for (cln = c->pool->cleanup; cln; cln = cln->next) { - if (cln->handler == ngx_http_grpc_cleanup) { - ctx->connection = cln->data; - break; - } - } - - if (ctx->connection == NULL) { - ngx_log_error(NGX_LOG_ERR, c->log, 0, - "no connection data found for " - "keepalive http2 connection"); - return NGX_ERROR; - } - - ctx->send_window = ctx->connection->init_window; - ctx->recv_window = NGX_HTTP_V2_MAX_WINDOW; - - ctx->connection->last_stream_id += 2; - ctx->id = ctx->connection->last_stream_id; - - return NGX_OK; - } - - cln = ngx_pool_cleanup_add(c->pool, sizeof(ngx_http_grpc_conn_t)); - if (cln == NULL) { - return NGX_ERROR; - } - - cln->handler = ngx_http_grpc_cleanup; - ctx->connection = cln->data; - - ctx->connection->init_window = NGX_HTTP_V2_DEFAULT_WINDOW; - ctx->connection->send_window = NGX_HTTP_V2_DEFAULT_WINDOW; - ctx->connection->recv_window = NGX_HTTP_V2_MAX_WINDOW; - - ctx->send_window = NGX_HTTP_V2_DEFAULT_WINDOW; - ctx->recv_window = NGX_HTTP_V2_MAX_WINDOW; - - ctx->id = 1; - ctx->connection->last_stream_id = 1; - - return NGX_OK; -} - - -static void -ngx_http_grpc_cleanup(void *data) -{ -#if 0 - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, - "grpc cleanup"); -#endif - return; -} - - -static void -ngx_http_grpc_abort_request(ngx_http_request_t *r) -{ - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "abort grpc request"); - return; -} - - -static void -ngx_http_grpc_finalize_request(ngx_http_request_t *r, ngx_int_t rc) -{ - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "finalize grpc request"); - return; -} - - -static ngx_int_t -ngx_http_grpc_internal_trailers_variable(ngx_http_request_t *r, - ngx_http_variable_value_t *v, uintptr_t data) -{ - ngx_table_elt_t *te; - - te = r->headers_in.te; - - if (te == NULL) { - v->not_found = 1; - return NGX_OK; - } - - if (ngx_strlcasestrn(te->value.data, te->value.data + te->value.len, - (u_char *) "trailers", 8 - 1) - == NULL) - { - v->not_found = 1; - return NGX_OK; - } - - v->valid = 1; - v->no_cacheable = 0; - v->not_found = 0; - - v->data = (u_char *) "trailers"; - v->len = sizeof("trailers") - 1; - - return NGX_OK; -} - - -static ngx_int_t -ngx_http_grpc_add_variables(ngx_conf_t *cf) -{ - ngx_http_variable_t *var, *v; - - for (v = ngx_http_grpc_vars; v->name.len; v++) { - var = ngx_http_add_variable(cf, &v->name, v->flags); - if (var == NULL) { - return NGX_ERROR; - } - - var->get_handler = v->get_handler; - var->data = v->data; - } - - return NGX_OK; -} - - -static void * -ngx_http_grpc_create_loc_conf(ngx_conf_t *cf) -{ - ngx_http_grpc_loc_conf_t *conf; - - conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_grpc_loc_conf_t)); - if (conf == NULL) { - return NULL; - } - - /* - * set by ngx_pcalloc(): - * - * conf->upstream.ignore_headers = 0; - * conf->upstream.next_upstream = 0; - * conf->upstream.hide_headers_hash = { NULL, 0 }; - * conf->upstream.ssl_name = NULL; - * - * conf->headers_source = NULL; - * conf->headers.lengths = NULL; - * conf->headers.values = NULL; - * conf->headers.hash = { NULL, 0 }; - * conf->host = { 0, NULL }; - * conf->host_set = 0; - * conf->ssl = 0; - * conf->ssl_protocols = 0; - * conf->ssl_ciphers = { 0, NULL }; - * conf->ssl_trusted_certificate = { 0, NULL }; - * conf->ssl_crl = { 0, NULL }; - * conf->ssl_certificate = { 0, NULL }; - * conf->ssl_certificate_key = { 0, NULL }; - */ - - conf->upstream.local = NGX_CONF_UNSET_PTR; - conf->upstream.next_upstream_tries = NGX_CONF_UNSET_UINT; - conf->upstream.connect_timeout = NGX_CONF_UNSET_MSEC; - conf->upstream.send_timeout = NGX_CONF_UNSET_MSEC; - conf->upstream.read_timeout = NGX_CONF_UNSET_MSEC; - conf->upstream.next_upstream_timeout = NGX_CONF_UNSET_MSEC; - - conf->upstream.buffer_size = NGX_CONF_UNSET_SIZE; - - conf->upstream.hide_headers = NGX_CONF_UNSET_PTR; - conf->upstream.pass_headers = NGX_CONF_UNSET_PTR; - - conf->upstream.intercept_errors = NGX_CONF_UNSET; - -#if (NGX_HTTP_SSL) - conf->upstream.ssl_session_reuse = NGX_CONF_UNSET; - conf->upstream.ssl_server_name = NGX_CONF_UNSET; - conf->upstream.ssl_verify = NGX_CONF_UNSET; - conf->ssl_verify_depth = NGX_CONF_UNSET_UINT; - conf->ssl_passwords = NGX_CONF_UNSET_PTR; -#endif - - /* the hardcoded values */ - conf->upstream.cyclic_temp_file = 0; - conf->upstream.buffering = 0; - conf->upstream.ignore_client_abort = 0; - conf->upstream.send_lowat = 0; - conf->upstream.bufs.num = 0; - conf->upstream.busy_buffers_size = 0; - conf->upstream.max_temp_file_size = 0; - conf->upstream.temp_file_write_size = 0; - conf->upstream.pass_request_headers = 1; - conf->upstream.pass_request_body = 1; - conf->upstream.force_ranges = 0; - conf->upstream.pass_trailers = 1; - conf->upstream.preserve_output = 1; - - ngx_str_set(&conf->upstream.module, "grpc"); - - return conf; -} - - -static char * -ngx_http_grpc_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) -{ - ngx_http_grpc_loc_conf_t *prev = parent; - ngx_http_grpc_loc_conf_t *conf = child; - - ngx_int_t rc; - ngx_hash_init_t hash; - ngx_http_core_loc_conf_t *clcf; - - ngx_conf_merge_ptr_value(conf->upstream.local, - prev->upstream.local, NULL); - - ngx_conf_merge_uint_value(conf->upstream.next_upstream_tries, - prev->upstream.next_upstream_tries, 0); - - ngx_conf_merge_msec_value(conf->upstream.connect_timeout, - prev->upstream.connect_timeout, 60000); - - ngx_conf_merge_msec_value(conf->upstream.send_timeout, - prev->upstream.send_timeout, 60000); - - ngx_conf_merge_msec_value(conf->upstream.read_timeout, - prev->upstream.read_timeout, 60000); - - ngx_conf_merge_msec_value(conf->upstream.next_upstream_timeout, - prev->upstream.next_upstream_timeout, 0); - - ngx_conf_merge_size_value(conf->upstream.buffer_size, - prev->upstream.buffer_size, - (size_t) ngx_pagesize); - - ngx_conf_merge_bitmask_value(conf->upstream.ignore_headers, - prev->upstream.ignore_headers, - NGX_CONF_BITMASK_SET); - - ngx_conf_merge_bitmask_value(conf->upstream.next_upstream, - prev->upstream.next_upstream, - (NGX_CONF_BITMASK_SET - |NGX_HTTP_UPSTREAM_FT_ERROR - |NGX_HTTP_UPSTREAM_FT_TIMEOUT)); - - if (conf->upstream.next_upstream & NGX_HTTP_UPSTREAM_FT_OFF) { - conf->upstream.next_upstream = NGX_CONF_BITMASK_SET - |NGX_HTTP_UPSTREAM_FT_OFF; - } - - ngx_conf_merge_value(conf->upstream.intercept_errors, - prev->upstream.intercept_errors, 0); - -#if (NGX_HTTP_SSL) - - ngx_conf_merge_value(conf->upstream.ssl_session_reuse, - prev->upstream.ssl_session_reuse, 1); - - ngx_conf_merge_bitmask_value(conf->ssl_protocols, prev->ssl_protocols, - (NGX_CONF_BITMASK_SET|NGX_SSL_TLSv1 - |NGX_SSL_TLSv1_1|NGX_SSL_TLSv1_2)); - - ngx_conf_merge_str_value(conf->ssl_ciphers, prev->ssl_ciphers, - "DEFAULT"); - - if (conf->upstream.ssl_name == NULL) { - conf->upstream.ssl_name = prev->upstream.ssl_name; - } - - ngx_conf_merge_value(conf->upstream.ssl_server_name, - prev->upstream.ssl_server_name, 0); - ngx_conf_merge_value(conf->upstream.ssl_verify, - prev->upstream.ssl_verify, 0); - ngx_conf_merge_uint_value(conf->ssl_verify_depth, - prev->ssl_verify_depth, 1); - ngx_conf_merge_str_value(conf->ssl_trusted_certificate, - prev->ssl_trusted_certificate, ""); - ngx_conf_merge_str_value(conf->ssl_crl, prev->ssl_crl, ""); - - ngx_conf_merge_str_value(conf->ssl_certificate, - prev->ssl_certificate, ""); - ngx_conf_merge_str_value(conf->ssl_certificate_key, - prev->ssl_certificate_key, ""); - ngx_conf_merge_ptr_value(conf->ssl_passwords, prev->ssl_passwords, NULL); - - if (conf->ssl && ngx_http_grpc_set_ssl(cf, conf) != NGX_OK) { - return NGX_CONF_ERROR; - } - -#endif - - hash.max_size = 512; - hash.bucket_size = ngx_align(64, ngx_cacheline_size); - hash.name = "grpc_headers_hash"; - - if (ngx_http_upstream_hide_headers_hash(cf, &conf->upstream, - &prev->upstream, ngx_http_grpc_hide_headers, &hash) - != NGX_OK) - { - return NGX_CONF_ERROR; - } - - clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module); - - if (clcf->noname && conf->upstream.upstream == NULL) { - conf->upstream.upstream = prev->upstream.upstream; - conf->host = prev->host; -#if (NGX_HTTP_SSL) - conf->upstream.ssl = prev->upstream.ssl; -#endif - } - - if (clcf->lmt_excpt && clcf->handler == NULL && conf->upstream.upstream) { - clcf->handler = ngx_http_grpc_handler; - } - - if (conf->headers_source == NULL) { - conf->headers = prev->headers; - conf->headers_source = prev->headers_source; - conf->host_set = prev->host_set; - } - - rc = ngx_http_grpc_init_headers(cf, conf, &conf->headers, - ngx_http_grpc_headers); - if (rc != NGX_OK) { - return NGX_CONF_ERROR; - } - - /* - * special handling to preserve conf->headers in the "http" section - * to inherit it to all servers - */ - - if (prev->headers.hash.buckets == NULL - && conf->headers_source == prev->headers_source) - { - prev->headers = conf->headers; - prev->host_set = conf->host_set; - } - - return NGX_CONF_OK; -} - - -static ngx_int_t -ngx_http_grpc_init_headers(ngx_conf_t *cf, ngx_http_grpc_loc_conf_t *conf, - ngx_http_grpc_headers_t *headers, ngx_keyval_t *default_headers) -{ - u_char *p; - size_t size; - uintptr_t *code; - ngx_uint_t i; - ngx_array_t headers_names, headers_merged; - ngx_keyval_t *src, *s, *h; - ngx_hash_key_t *hk; - ngx_hash_init_t hash; - ngx_http_script_compile_t sc; - ngx_http_script_copy_code_t *copy; - - if (headers->hash.buckets) { - return NGX_OK; - } - - if (ngx_array_init(&headers_names, cf->temp_pool, 4, sizeof(ngx_hash_key_t)) - != NGX_OK) - { - return NGX_ERROR; - } - - if (ngx_array_init(&headers_merged, cf->temp_pool, 4, sizeof(ngx_keyval_t)) - != NGX_OK) - { - return NGX_ERROR; - } - - headers->lengths = ngx_array_create(cf->pool, 64, 1); - if (headers->lengths == NULL) { - return NGX_ERROR; - } - - headers->values = ngx_array_create(cf->pool, 512, 1); - if (headers->values == NULL) { - return NGX_ERROR; - } - - if (conf->headers_source) { - - src = conf->headers_source->elts; - for (i = 0; i < conf->headers_source->nelts; i++) { - - if (src[i].key.len == 4 - && ngx_strncasecmp(src[i].key.data, (u_char *) "Host", 4) == 0) - { - conf->host_set = 1; - } - - s = ngx_array_push(&headers_merged); - if (s == NULL) { - return NGX_ERROR; - } - - *s = src[i]; - } - } - - h = default_headers; - - while (h->key.len) { - - src = headers_merged.elts; - for (i = 0; i < headers_merged.nelts; i++) { - if (ngx_strcasecmp(h->key.data, src[i].key.data) == 0) { - goto next; - } - } - - s = ngx_array_push(&headers_merged); - if (s == NULL) { - return NGX_ERROR; - } - - *s = *h; - - next: - - h++; - } - - - src = headers_merged.elts; - for (i = 0; i < headers_merged.nelts; i++) { - - hk = ngx_array_push(&headers_names); - if (hk == NULL) { - return NGX_ERROR; - } - - hk->key = src[i].key; - hk->key_hash = ngx_hash_key_lc(src[i].key.data, src[i].key.len); - hk->value = (void *) 1; - - if (src[i].value.len == 0) { - continue; - } - - copy = ngx_array_push_n(headers->lengths, - sizeof(ngx_http_script_copy_code_t)); - if (copy == NULL) { - return NGX_ERROR; - } - - copy->code = (ngx_http_script_code_pt) (void *) - ngx_http_script_copy_len_code; - copy->len = src[i].key.len; - - size = (sizeof(ngx_http_script_copy_code_t) - + src[i].key.len + sizeof(uintptr_t) - 1) - & ~(sizeof(uintptr_t) - 1); - - copy = ngx_array_push_n(headers->values, size); - if (copy == NULL) { - return NGX_ERROR; - } - - copy->code = ngx_http_script_copy_code; - copy->len = src[i].key.len; - - p = (u_char *) copy + sizeof(ngx_http_script_copy_code_t); - ngx_memcpy(p, src[i].key.data, src[i].key.len); - - ngx_memzero(&sc, sizeof(ngx_http_script_compile_t)); - - sc.cf = cf; - sc.source = &src[i].value; - sc.flushes = &headers->flushes; - sc.lengths = &headers->lengths; - sc.values = &headers->values; - - if (ngx_http_script_compile(&sc) != NGX_OK) { - return NGX_ERROR; - } - - code = ngx_array_push_n(headers->lengths, sizeof(uintptr_t)); - if (code == NULL) { - return NGX_ERROR; - } - - *code = (uintptr_t) NULL; - - code = ngx_array_push_n(headers->values, sizeof(uintptr_t)); - if (code == NULL) { - return NGX_ERROR; - } - - *code = (uintptr_t) NULL; - } - - code = ngx_array_push_n(headers->lengths, sizeof(uintptr_t)); - if (code == NULL) { - return NGX_ERROR; - } - - *code = (uintptr_t) NULL; - - - hash.hash = &headers->hash; - hash.key = ngx_hash_key_lc; - hash.max_size = 512; - hash.bucket_size = 64; - hash.name = "grpc_headers_hash"; - hash.pool = cf->pool; - hash.temp_pool = NULL; - - return ngx_hash_init(&hash, headers_names.elts, headers_names.nelts); -} - - -static char * -ngx_http_grpc_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) -{ - ngx_http_grpc_loc_conf_t *glcf = conf; - - size_t add; - ngx_str_t *value, *url; - ngx_url_t u; - ngx_http_core_loc_conf_t *clcf; - - if (glcf->upstream.upstream) { - return "is duplicate"; - } - - value = cf->args->elts; - url = &value[1]; - - if (ngx_strncasecmp(url->data, (u_char *) "grpc://", 7) == 0) { - add = 7; - - } else if (ngx_strncasecmp(url->data, (u_char *) "grpcs://", 8) == 0) { - -#if (NGX_HTTP_SSL) - glcf->ssl = 1; - - add = 8; -#else - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "grpcs protocol requires SSL support"); - return NGX_CONF_ERROR; -#endif - - } else { - add = 0; - } - - ngx_memzero(&u, sizeof(ngx_url_t)); - - u.url.len = url->len - add; - u.url.data = url->data + add; - u.no_resolve = 1; - - glcf->upstream.upstream = ngx_http_upstream_add(cf, &u, 0); - if (glcf->upstream.upstream == NULL) { - return NGX_CONF_ERROR; - } - - if (u.family != AF_UNIX) { - - if (u.no_port) { - glcf->host = u.host; - - } else { - glcf->host.len = u.host.len + 1 + u.port_text.len; - glcf->host.data = u.host.data; - } - - } else { - ngx_str_set(&glcf->host, "localhost"); - } - - clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module); - - clcf->handler = ngx_http_grpc_handler; - - if (clcf->name.data[clcf->name.len - 1] == '/') { - clcf->auto_redirect = 1; - } - - return NGX_CONF_OK; -} - - -#if (NGX_HTTP_SSL) - -static char * -ngx_http_grpc_ssl_password_file(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) -{ - ngx_http_grpc_loc_conf_t *glcf = conf; - - ngx_str_t *value; - - if (glcf->ssl_passwords != NGX_CONF_UNSET_PTR) { - return "is duplicate"; - } - - value = cf->args->elts; - - glcf->ssl_passwords = ngx_ssl_read_password_file(cf, &value[1]); - - if (glcf->ssl_passwords == NULL) { - return NGX_CONF_ERROR; - } - - return NGX_CONF_OK; -} - - -static ngx_int_t -ngx_http_grpc_set_ssl(ngx_conf_t *cf, ngx_http_grpc_loc_conf_t *glcf) -{ - ngx_pool_cleanup_t *cln; - - glcf->upstream.ssl = ngx_pcalloc(cf->pool, sizeof(ngx_ssl_t)); - if (glcf->upstream.ssl == NULL) { - return NGX_ERROR; - } - - glcf->upstream.ssl->log = cf->log; - - if (ngx_ssl_create(glcf->upstream.ssl, glcf->ssl_protocols, NULL) - != NGX_OK) - { - return NGX_ERROR; - } - - cln = ngx_pool_cleanup_add(cf->pool, 0); - if (cln == NULL) { - return NGX_ERROR; - } - - cln->handler = ngx_ssl_cleanup_ctx; - cln->data = glcf->upstream.ssl; - - if (glcf->ssl_certificate.len) { - - if (glcf->ssl_certificate_key.len == 0) { - ngx_log_error(NGX_LOG_EMERG, cf->log, 0, - "no \"grpc_ssl_certificate_key\" is defined " - "for certificate \"%V\"", &glcf->ssl_certificate); - return NGX_ERROR; - } - - if (ngx_ssl_certificate(cf, glcf->upstream.ssl, &glcf->ssl_certificate, - &glcf->ssl_certificate_key, glcf->ssl_passwords) - != NGX_OK) - { - return NGX_ERROR; - } - } - - if (ngx_ssl_ciphers(cf, glcf->upstream.ssl, &glcf->ssl_ciphers, 0) - != NGX_OK) - { - return NGX_ERROR; - } - - if (glcf->upstream.ssl_verify) { - if (glcf->ssl_trusted_certificate.len == 0) { - ngx_log_error(NGX_LOG_EMERG, cf->log, 0, - "no grpc_ssl_trusted_certificate for grpc_ssl_verify"); - return NGX_ERROR; - } - - if (ngx_ssl_trusted_certificate(cf, glcf->upstream.ssl, - &glcf->ssl_trusted_certificate, - glcf->ssl_verify_depth) - != NGX_OK) - { - return NGX_ERROR; - } - - if (ngx_ssl_crl(cf, glcf->upstream.ssl, &glcf->ssl_crl) != NGX_OK) { - return NGX_ERROR; - } - } - -#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation - - if (SSL_CTX_set_alpn_protos(glcf->upstream.ssl->ctx, - (u_char *) "\x02h2", 3) - != 0) - { - ngx_ssl_error(NGX_LOG_EMERG, cf->log, 0, - "SSL_CTX_set_alpn_protos() failed"); - return NGX_ERROR; - } - -#endif - - return NGX_OK; -} - -#endif |