/* Called either: - in response to an API call (or similar) from above, to send something - a network event (or similar) from below, to receive something op contains type and call direction information, in addition to the data that is being sent or received. */ static void call_op(grpc_call_element *elem, grpc_call_element *from_elem, grpc_call_op *op) { /* grab pointers to our data from the call element */ call_data *calld = elem->call_data; channel_data *channeld = elem->channel_data; GRPC_CALL_LOG_OP(GPR_INFO, elem, op); switch (op->type) { case GRPC_RECV_METADATA: grpc_metadata_batch_filter(&op->data.metadata, server_filter, elem); if (!calld->got_initial_metadata) { calld->got_initial_metadata = 1; /* Have we seen the required http2 transport headers? (:method, :scheme, content-type, with :path and :authority covered at the channel level right now) */ if (calld->seen_post && calld->seen_scheme && calld->seen_te_trailers && calld->seen_path) { grpc_call_next_op(elem, op); } else { if (!calld->seen_path) { gpr_log(GPR_ERROR, "Missing :path header"); } if (!calld->seen_post) { gpr_log(GPR_ERROR, "Missing :method header"); } if (!calld->seen_scheme) { gpr_log(GPR_ERROR, "Missing :scheme header"); } if (!calld->seen_te_trailers) { gpr_log(GPR_ERROR, "Missing te trailers header"); } /* Error this call out */ grpc_metadata_batch_destroy(&op->data.metadata); op->done_cb(op->user_data, GRPC_OP_OK); grpc_call_element_send_cancel(elem); } } else { grpc_call_next_op(elem, op); } break; case GRPC_SEND_METADATA: /* If we haven't sent status 200 yet, we need to so so because it needs to come before any non : prefixed metadata. */ if (!calld->sent_status) { calld->sent_status = 1; grpc_metadata_batch_add_head(&op->data.metadata, &calld->status, grpc_mdelem_ref(channeld->status_ok)); } grpc_call_next_op(elem, op); break; default: /* pass control up or down the stack depending on op->dir */ grpc_call_next_op(elem, op); break; } }
static grpc_mdelem *client_filter(void *user_data, grpc_mdelem *md) { grpc_call_element *elem = user_data; channel_data *channeld = elem->channel_data; if (md == channeld->status) { return NULL; } else if (md->key == channeld->status->key) { grpc_call_element_send_cancel(elem); return NULL; } return md; }
static grpc_mdelem *client_recv_filter(void *user_data, grpc_mdelem *md) { client_recv_filter_args *a = user_data; if (md == GRPC_MDELEM_STATUS_200) { return NULL; } else if (md->key == GRPC_MDSTR_STATUS) { grpc_call_element_send_cancel(a->exec_ctx, a->elem); return NULL; } else if (md->key == GRPC_MDSTR_CONTENT_TYPE) { return NULL; } return md; }
static void hs_on_recv(grpc_exec_ctx *exec_ctx, void *user_data, grpc_error *err) { grpc_call_element *elem = user_data; call_data *calld = elem->call_data; if (err == GRPC_ERROR_NONE) { server_filter_args a; a.elem = elem; a.exec_ctx = exec_ctx; grpc_metadata_batch_filter(calld->recv_initial_metadata, server_filter, &a); /* Have we seen the required http2 transport headers? (:method, :scheme, content-type, with :path and :authority covered at the channel level right now) */ if (calld->seen_method && calld->seen_scheme && calld->seen_te_trailers && calld->seen_path && calld->seen_authority) { /* do nothing */ } else { err = GRPC_ERROR_CREATE("Bad incoming HTTP headers"); if (!calld->seen_path) { err = grpc_error_add_child(err, GRPC_ERROR_CREATE("Missing :path header")); } if (!calld->seen_authority) { err = grpc_error_add_child( err, GRPC_ERROR_CREATE("Missing :authority header")); } if (!calld->seen_method) { err = grpc_error_add_child(err, GRPC_ERROR_CREATE("Missing :method header")); } if (!calld->seen_scheme) { err = grpc_error_add_child(err, GRPC_ERROR_CREATE("Missing :scheme header")); } if (!calld->seen_te_trailers) { err = grpc_error_add_child( err, GRPC_ERROR_CREATE("Missing te: trailers header")); } /* Error this call out */ if (grpc_http_trace) { const char *error_str = grpc_error_string(err); gpr_log(GPR_ERROR, "Invalid http2 headers: %s", error_str); grpc_error_free_string(error_str); } grpc_call_element_send_cancel(exec_ctx, elem); } } else { GRPC_ERROR_REF(err); } calld->on_done_recv->cb(exec_ctx, calld->on_done_recv->cb_arg, err); GRPC_ERROR_UNREF(err); }
static void hs_on_recv(grpc_exec_ctx *exec_ctx, void *user_data, int success) { grpc_call_element *elem = user_data; call_data *calld = elem->call_data; if (success) { size_t i; size_t nops = calld->recv_ops->nops; grpc_stream_op *ops = calld->recv_ops->ops; for (i = 0; i < nops; i++) { grpc_stream_op *op = &ops[i]; server_filter_args a; if (op->type != GRPC_OP_METADATA) continue; calld->got_initial_metadata = 1; a.elem = elem; a.exec_ctx = exec_ctx; grpc_metadata_batch_filter(&op->data.metadata, server_filter, &a); /* Have we seen the required http2 transport headers? (:method, :scheme, content-type, with :path and :authority covered at the channel level right now) */ if (calld->seen_post && calld->seen_scheme && calld->seen_te_trailers && calld->seen_path && calld->seen_authority) { /* do nothing */ } else { if (!calld->seen_path) { gpr_log(GPR_ERROR, "Missing :path header"); } if (!calld->seen_authority) { gpr_log(GPR_ERROR, "Missing :authority header"); } if (!calld->seen_post) { gpr_log(GPR_ERROR, "Missing :method header"); } if (!calld->seen_scheme) { gpr_log(GPR_ERROR, "Missing :scheme header"); } if (!calld->seen_te_trailers) { gpr_log(GPR_ERROR, "Missing te trailers header"); } /* Error this call out */ success = 0; grpc_call_element_send_cancel(exec_ctx, elem); } } } calld->on_done_recv->cb(exec_ctx, calld->on_done_recv->cb_arg, success); }
static void hs_on_recv(grpc_exec_ctx *exec_ctx, void *user_data, bool success) { grpc_call_element *elem = user_data; call_data *calld = elem->call_data; if (success) { server_filter_args a; a.elem = elem; a.exec_ctx = exec_ctx; grpc_metadata_batch_filter(calld->recv_initial_metadata, server_filter, &a); /* Have we seen the required http2 transport headers? (:method, :scheme, content-type, with :path and :authority covered at the channel level right now) */ if (calld->seen_method && calld->seen_scheme && calld->seen_te_trailers && calld->seen_path && calld->seen_authority) { /* do nothing */ } else { if (!calld->seen_path) { gpr_log(GPR_ERROR, "Missing :path header"); } if (!calld->seen_authority) { gpr_log(GPR_ERROR, "Missing :authority header"); } if (!calld->seen_method) { gpr_log(GPR_ERROR, "Missing :method header"); } if (!calld->seen_scheme) { gpr_log(GPR_ERROR, "Missing :scheme header"); } if (!calld->seen_te_trailers) { gpr_log(GPR_ERROR, "Missing te trailers header"); } /* Error this call out */ success = 0; grpc_call_element_send_cancel(exec_ctx, elem); } } calld->on_done_recv->cb(exec_ctx, calld->on_done_recv->cb_arg, success); }
static grpc_mdelem *server_filter(void *user_data, grpc_mdelem *md) { server_filter_args *a = user_data; grpc_call_element *elem = a->elem; call_data *calld = elem->call_data; /* Check if it is one of the headers we care about. */ if (md == GRPC_MDELEM_TE_TRAILERS || md == GRPC_MDELEM_METHOD_POST || md == GRPC_MDELEM_METHOD_PUT || md == GRPC_MDELEM_SCHEME_HTTP || md == GRPC_MDELEM_SCHEME_HTTPS || md == GRPC_MDELEM_CONTENT_TYPE_APPLICATION_SLASH_GRPC) { /* swallow it */ if (md == GRPC_MDELEM_METHOD_POST) { calld->seen_method = 1; *calld->recv_idempotent_request = false; } else if (md == GRPC_MDELEM_METHOD_PUT) { calld->seen_method = 1; *calld->recv_idempotent_request = true; } else if (md->key == GRPC_MDSTR_SCHEME) { calld->seen_scheme = 1; } else if (md == GRPC_MDELEM_TE_TRAILERS) { calld->seen_te_trailers = 1; } /* TODO(klempner): Track that we've seen all the headers we should require */ return NULL; } else if (md->key == GRPC_MDSTR_CONTENT_TYPE) { const char *value_str = grpc_mdstr_as_c_string(md->value); if (strncmp(value_str, EXPECTED_CONTENT_TYPE, EXPECTED_CONTENT_TYPE_LENGTH) == 0 && (value_str[EXPECTED_CONTENT_TYPE_LENGTH] == '+' || value_str[EXPECTED_CONTENT_TYPE_LENGTH] == ';')) { /* Although the C implementation doesn't (currently) generate them, any custom +-suffix is explicitly valid. */ /* TODO(klempner): We should consider preallocating common values such as +proto or +json, or at least stashing them if we see them. */ /* TODO(klempner): Should we be surfacing this to application code? */ } else { /* TODO(klempner): We're currently allowing this, but we shouldn't see it without a proxy so log for now. */ gpr_log(GPR_INFO, "Unexpected content-type '%s'", value_str); } return NULL; } else if (md->key == GRPC_MDSTR_TE || md->key == GRPC_MDSTR_METHOD || md->key == GRPC_MDSTR_SCHEME) { gpr_log(GPR_ERROR, "Invalid %s: header: '%s'", grpc_mdstr_as_c_string(md->key), grpc_mdstr_as_c_string(md->value)); /* swallow it and error everything out. */ /* TODO(klempner): We ought to generate more descriptive error messages on the wire here. */ grpc_call_element_send_cancel(a->exec_ctx, elem); return NULL; } else if (md->key == GRPC_MDSTR_PATH) { if (calld->seen_path) { gpr_log(GPR_ERROR, "Received :path twice"); return NULL; } calld->seen_path = 1; return md; } else if (md->key == GRPC_MDSTR_AUTHORITY) { calld->seen_authority = 1; return md; } else if (md->key == GRPC_MDSTR_HOST) { /* translate host to :authority since :authority may be omitted */ grpc_mdelem *authority = grpc_mdelem_from_metadata_strings( GRPC_MDSTR_AUTHORITY, GRPC_MDSTR_REF(md->value)); calld->seen_authority = 1; return authority; } else { return md; } }
static grpc_mdelem *server_filter(void *user_data, grpc_mdelem *md) { grpc_call_element *elem = user_data; channel_data *channeld = elem->channel_data; call_data *calld = elem->call_data; /* Check if it is one of the headers we care about. */ if (md == channeld->te_trailers || md == channeld->method_post || md == channeld->http_scheme || md == channeld->https_scheme || md == channeld->grpc_scheme || md == channeld->content_type) { /* swallow it */ if (md == channeld->method_post) { calld->seen_post = 1; } else if (md->key == channeld->http_scheme->key) { calld->seen_scheme = 1; } else if (md == channeld->te_trailers) { calld->seen_te_trailers = 1; } /* TODO(klempner): Track that we've seen all the headers we should require */ return NULL; } else if (md->key == channeld->content_type->key) { if (strncmp(grpc_mdstr_as_c_string(md->value), "application/grpc+", 17) == 0) { /* Although the C implementation doesn't (currently) generate them, any custom +-suffix is explicitly valid. */ /* TODO(klempner): We should consider preallocating common values such as +proto or +json, or at least stashing them if we see them. */ /* TODO(klempner): Should we be surfacing this to application code? */ } else { /* TODO(klempner): We're currently allowing this, but we shouldn't see it without a proxy so log for now. */ gpr_log(GPR_INFO, "Unexpected content-type %s", channeld->content_type->key); } return NULL; } else if (md->key == channeld->te_trailers->key || md->key == channeld->method_post->key || md->key == channeld->http_scheme->key || md->key == channeld->content_type->key) { gpr_log(GPR_ERROR, "Invalid %s: header: '%s'", grpc_mdstr_as_c_string(md->key), grpc_mdstr_as_c_string(md->value)); /* swallow it and error everything out. */ /* TODO(klempner): We ought to generate more descriptive error messages on the wire here. */ grpc_call_element_send_cancel(elem); return NULL; } else if (md->key == channeld->path_key) { if (calld->seen_path) { gpr_log(GPR_ERROR, "Received :path twice"); return NULL; } calld->seen_path = 1; return md; } else if (md->key == channeld->authority_key) { calld->seen_authority = 1; return md; } else if (md->key == channeld->host_key) { /* translate host to :authority since :authority may be omitted */ grpc_mdelem *authority = grpc_mdelem_from_metadata_strings( channeld->mdctx, GRPC_MDSTR_REF(channeld->authority_key), GRPC_MDSTR_REF(md->value)); GRPC_MDELEM_UNREF(md); calld->seen_authority = 1; return authority; } else { return md; } }