1 /* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
7 * http://www.apache.org/licenses/LICENSE-2.0
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
19 #include <apr_strings.h>
22 #include <http_core.h>
24 #include <util_time.h>
26 #include <nghttp2/nghttp2.h>
28 #include "h2_private.h"
29 #include "h2_filter.h"
32 #include "h2_request.h"
33 #include "h2_response.h"
36 static apr_table_t *parse_headers(apr_array_header_t *hlines, apr_pool_t *pool)
39 apr_table_t *headers = apr_table_make(pool, hlines->nelts);
42 for (i = 0; i < hlines->nelts; ++i) {
43 char *hline = ((char **)hlines->elts)[i];
44 char *sep = ap_strchr(hline, ':');
46 ap_log_perror(APLOG_MARK, APLOG_WARNING, APR_EINVAL, pool,
47 APLOGNO(02955) "h2_response: invalid header[%d] '%s'",
49 /* not valid format, abort */
53 while (*sep == ' ' || *sep == '\t') {
57 if (!h2_util_ignore_header(hline)) {
58 apr_table_merge(headers, hline, sep);
64 return apr_table_make(pool, 0);
68 static const char *get_sos_filter(apr_table_t *notes)
70 return notes? apr_table_get(notes, H2_RESP_SOS_NOTE) : NULL;
73 static void check_clen(h2_response *response, request_rec *r, apr_pool_t *pool)
76 if (r && r->header_only) {
77 response->content_length = 0;
79 else if (response->headers) {
80 const char *s = apr_table_get(response->headers, "Content-Length");
83 response->content_length = apr_strtoi64(s, &end, 10);
85 ap_log_perror(APLOG_MARK, APLOG_WARNING, APR_EINVAL,
87 "h2_response: content-length"
88 " value not parsed: %s", s);
89 response->content_length = -1;
95 static h2_response *h2_response_create_int(int stream_id,
102 h2_response *response;
108 response = apr_pcalloc(pool, sizeof(h2_response));
109 if (response == NULL) {
113 response->stream_id = stream_id;
114 response->rst_error = rst_error;
115 response->http_status = http_status? http_status : 500;
116 response->content_length = -1;
117 response->headers = headers;
118 response->sos_filter = get_sos_filter(notes);
120 check_clen(response, NULL, pool);
125 h2_response *h2_response_create(int stream_id,
128 apr_array_header_t *hlines,
132 return h2_response_create_int(stream_id, rst_error, http_status,
133 parse_headers(hlines, pool), notes, pool);
136 h2_response *h2_response_rcreate(int stream_id, request_rec *r, int status,
137 apr_table_t *header, apr_pool_t *pool)
139 h2_response *response = apr_pcalloc(pool, sizeof(h2_response));
140 if (response == NULL) {
144 response->stream_id = stream_id;
145 response->http_status = status;
146 response->content_length = -1;
147 response->headers = header;
148 response->sos_filter = get_sos_filter(r->notes);
150 check_clen(response, r, pool);
152 if (response->http_status == HTTP_FORBIDDEN) {
153 const char *cause = apr_table_get(r->notes, "ssl-renegotiate-forbidden");
155 /* This request triggered a TLS renegotiation that is now allowed
156 * in HTTP/2. Tell the client that it should use HTTP/1.1 for this.
158 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, response->http_status, r,
160 "h2_response(%ld-%d): renegotiate forbidden, cause: %s",
161 (long)r->connection->id, stream_id, cause);
162 response->rst_error = H2_ERR_HTTP_1_1_REQUIRED;
169 h2_response *h2_response_die(int stream_id, apr_status_t type,
170 const struct h2_request *req, apr_pool_t *pool)
172 apr_table_t *headers = apr_table_make(pool, 5);
174 int status = (type >= 200 && type < 600)? type : 500;
176 date = apr_palloc(pool, APR_RFC822_DATE_LEN);
177 ap_recent_rfc822_date(date, req->request_time);
178 apr_table_setn(headers, "Date", date);
179 apr_table_setn(headers, "Server", ap_get_server_banner());
181 return h2_response_create_int(stream_id, 0, status, headers, NULL, pool);
184 h2_response *h2_response_clone(apr_pool_t *pool, h2_response *from)
186 h2_response *to = apr_pcalloc(pool, sizeof(h2_response));
188 to->stream_id = from->stream_id;
189 to->http_status = from->http_status;
190 to->content_length = from->content_length;
191 to->sos_filter = from->sos_filter;
193 to->headers = apr_table_clone(pool, from->headers);
195 if (from->trailers) {
196 to->trailers = apr_table_clone(pool, from->trailers);
201 void h2_response_set_trailers(h2_response *response, apr_table_t *trailers)
203 response->trailers = trailers;
206 int h2_response_is_final(h2_response *response)
208 return response->http_status >= 200;
211 h2_response *h2_response_get_final(h2_response *response)
213 for (/**/; response; response = response->next) {
214 if (h2_response_is_final(response)) {