]> granicus.if.org Git - apache/blob - modules/http2/h2_response.c
80d8b031e6ab779564c86a2784c9d1c94bfdd1ab
[apache] / modules / http2 / h2_response.c
1 /* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
2  *
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
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  
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.
14  */
15
16 #include <assert.h>
17 #include <stdio.h>
18
19 #include <apr_strings.h>
20
21 #include <httpd.h>
22 #include <http_core.h>
23 #include <http_log.h>
24 #include <util_time.h>
25
26 #include <nghttp2/nghttp2.h>
27
28 #include "h2_private.h"
29 #include "h2_filter.h"
30 #include "h2_h2.h"
31 #include "h2_util.h"
32 #include "h2_request.h"
33 #include "h2_response.h"
34
35
36 static apr_table_t *parse_headers(apr_array_header_t *hlines, apr_pool_t *pool)
37 {
38     if (hlines) {
39         apr_table_t *headers = apr_table_make(pool, hlines->nelts);        
40         int i;
41         
42         for (i = 0; i < hlines->nelts; ++i) {
43             char *hline = ((char **)hlines->elts)[i];
44             char *sep = ap_strchr(hline, ':');
45             if (!sep) {
46                 ap_log_perror(APLOG_MARK, APLOG_WARNING, APR_EINVAL, pool,
47                               APLOGNO(02955) "h2_response: invalid header[%d] '%s'",
48                               i, (char*)hline);
49                 /* not valid format, abort */
50                 return NULL;
51             }
52             (*sep++) = '\0';
53             while (*sep == ' ' || *sep == '\t') {
54                 ++sep;
55             }
56             
57             if (!h2_util_ignore_header(hline)) {
58                 apr_table_merge(headers, hline, sep);
59             }
60         }
61         return headers;
62     }
63     else {
64         return apr_table_make(pool, 0);        
65     }
66 }
67
68 static const char *get_sos_filter(apr_table_t *notes) 
69 {
70     return notes? apr_table_get(notes, H2_RESP_SOS_NOTE) : NULL;
71 }
72
73 static void check_clen(h2_response *response, request_rec *r, apr_pool_t *pool)
74 {
75     
76     if (r && r->header_only) {
77         response->content_length = 0;
78     }
79     else if (response->headers) {
80         const char *s = apr_table_get(response->headers, "Content-Length");
81         if (s) {
82             char *end;
83             response->content_length = apr_strtoi64(s, &end, 10);
84             if (s == end) {
85                 ap_log_perror(APLOG_MARK, APLOG_WARNING, APR_EINVAL, 
86                               pool, APLOGNO(02956) 
87                               "h2_response: content-length"
88                               " value not parsed: %s", s);
89                 response->content_length = -1;
90             }
91         }
92     }
93 }
94
95 static h2_response *h2_response_create_int(int stream_id,
96                                            int rst_error,
97                                            int http_status,
98                                            apr_table_t *headers,
99                                            apr_table_t *notes,
100                                            apr_pool_t *pool)
101 {
102     h2_response *response;
103
104     if (!headers) {
105         return NULL;
106     }
107     
108     response = apr_pcalloc(pool, sizeof(h2_response));
109     if (response == NULL) {
110         return NULL;
111     }
112     
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);
119     
120     check_clen(response, NULL, pool);
121     return response;
122 }
123
124
125 h2_response *h2_response_create(int stream_id,
126                                 int rst_error,
127                                 int http_status,
128                                 apr_array_header_t *hlines,
129                                 apr_table_t *notes,
130                                 apr_pool_t *pool)
131 {
132     return h2_response_create_int(stream_id, rst_error, http_status,
133                                   parse_headers(hlines, pool), notes, pool);
134 }
135
136 h2_response *h2_response_rcreate(int stream_id, request_rec *r, int status,
137                                  apr_table_t *header, apr_pool_t *pool)
138 {
139     h2_response *response = apr_pcalloc(pool, sizeof(h2_response));
140     if (response == NULL) {
141         return NULL;
142     }
143     
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);
149
150     check_clen(response, r, pool);
151     
152     if (response->http_status == HTTP_FORBIDDEN) {
153         const char *cause = apr_table_get(r->notes, "ssl-renegotiate-forbidden");
154         if (cause) {
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.
157              */
158             ap_log_rerror(APLOG_MARK, APLOG_DEBUG, response->http_status, r,
159                           APLOGNO(03061) 
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;
163         }
164     }
165     
166     return response;
167 }
168
169 h2_response *h2_response_die(int stream_id, apr_status_t type,
170                              const struct h2_request *req, apr_pool_t *pool)
171 {
172     apr_table_t *headers = apr_table_make(pool, 5);
173     char *date = NULL;
174     int status = (type >= 200 && type < 600)? type : 500;
175     
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());
180     
181     return h2_response_create_int(stream_id, 0, status, headers, NULL, pool);
182 }
183
184 h2_response *h2_response_clone(apr_pool_t *pool, h2_response *from)
185 {
186     h2_response *to = apr_pcalloc(pool, sizeof(h2_response));
187     
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;
192     if (from->headers) {
193         to->headers    = apr_table_clone(pool, from->headers);
194     }
195     if (from->trailers) {
196         to->trailers   = apr_table_clone(pool, from->trailers);
197     }
198     return to;
199 }
200
201 void h2_response_set_trailers(h2_response *response, apr_table_t *trailers)
202 {
203     response->trailers = trailers;
204 }
205
206 int h2_response_is_final(h2_response *response)
207 {
208     return response->http_status >= 200;
209 }
210
211 h2_response *h2_response_get_final(h2_response *response)
212 {
213     for (/**/; response; response = response->next) {
214         if (h2_response_is_final(response)) {
215             return response;
216         }
217     }
218     return NULL;
219 }