int nghttp2_http_on_header(nghttp2_session *session, nghttp2_stream *stream, nghttp2_frame *frame, nghttp2_nv *nv, int token, int trailer) { int rv; /* We are strict for pseudo header field. One bad character should lead to fail. OTOH, we should be a bit forgiving for regular headers, since existing public internet has so much illegal headers floating around and if we kill the stream because of this, we may disrupt many web sites and/or libraries. So we become conservative here, and just ignore those illegal regular headers. */ if (!nghttp2_check_header_name(nv->name, nv->namelen)) { size_t i; if (nv->namelen > 0 && nv->name[0] == ':') { return NGHTTP2_ERR_HTTP_HEADER; } /* header field name must be lower-cased without exception */ for (i = 0; i < nv->namelen; ++i) { uint8_t c = nv->name[i]; if ('A' <= c && c <= 'Z') { return NGHTTP2_ERR_HTTP_HEADER; } } /* When ignoring regular headers, we set this flag so that we still enforce header field ordering rule for pseudo header fields. */ stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED; return NGHTTP2_ERR_IGN_HTTP_HEADER; } if (token == NGHTTP2_TOKEN__AUTHORITY || token == NGHTTP2_TOKEN_HOST) { rv = check_authority(nv->value, nv->valuelen); } else if (token == NGHTTP2_TOKEN__SCHEME) { rv = check_scheme(nv->value, nv->valuelen); } else { rv = nghttp2_check_header_value(nv->value, nv->valuelen); } if (rv == 0) { assert(nv->namelen > 0); if (nv->name[0] == ':') { return NGHTTP2_ERR_HTTP_HEADER; } /* When ignoring regular headers, we set this flag so that we still enforce header field ordering rule for pseudo header fields. */ stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED; return NGHTTP2_ERR_IGN_HTTP_HEADER; } if (session->server || frame->hd.type == NGHTTP2_PUSH_PROMISE) { return http_request_on_header(stream, nv, token, trailer); } return http_response_on_header(stream, nv, token, trailer); }
/* frame->hd.type is either NGHTTP2_HEADERS or NGHTTP2_PUSH_PROMISE */ static int on_header(nghttp2_session *session, const nghttp2_frame *frame, const uint8_t *name, size_t namelen, const uint8_t *value, size_t valuelen, uint8_t flags, void *userp) { struct connectdata *conn = (struct connectdata *)userp; struct http_conn *c = &conn->proto.httpc; int rv; (void)session; (void)frame; (void)flags; if(frame->hd.stream_id != c->stream_id) { return 0; } if(c->bodystarted) { /* Ignore trailer or HEADERS not mapped to HTTP semantics. The consequence is handled in on_frame_recv(). */ return 0; } if(!nghttp2_check_header_name(name, namelen) || !nghttp2_check_header_value(value, valuelen)) { rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, frame->hd.stream_id, NGHTTP2_PROTOCOL_ERROR); if(nghttp2_is_fatal(rv)) { return NGHTTP2_ERR_CALLBACK_FAILURE; } return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; } if(namelen == sizeof(":status") - 1 && memcmp(STATUS, name, namelen) == 0) { /* :status must appear exactly once. */ if(c->status_code != -1 || (c->status_code = decode_status_code(value, valuelen)) == -1) { rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, frame->hd.stream_id, NGHTTP2_PROTOCOL_ERROR); if(nghttp2_is_fatal(rv)) { return NGHTTP2_ERR_CALLBACK_FAILURE; } return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; } Curl_add_buffer(c->header_recvbuf, "HTTP/2.0 ", 9); Curl_add_buffer(c->header_recvbuf, value, valuelen); Curl_add_buffer(c->header_recvbuf, "\r\n", 2); return 0; } else { /* Here we are sure that namelen > 0 because of nghttp2_check_header_name(). Pseudo header other than :status is illegal. */ if(c->status_code == -1 || name[0] == ':') { rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, frame->hd.stream_id, NGHTTP2_PROTOCOL_ERROR); if(nghttp2_is_fatal(rv)) { return NGHTTP2_ERR_CALLBACK_FAILURE; } return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; } /* convert to a HTTP1-style header */ infof(conn->data, "got header\n"); Curl_add_buffer(c->header_recvbuf, name, namelen); Curl_add_buffer(c->header_recvbuf, ":", 1); Curl_add_buffer(c->header_recvbuf, value, valuelen); Curl_add_buffer(c->header_recvbuf, "\r\n", 2); } return 0; /* 0 is successful */ }