/src/civetweb/src/response.inl
Line | Count | Source |
1 | | /* response.inl |
2 | | * |
3 | | * Bufferring for HTTP headers for HTTP response. |
4 | | * This function are only intended to be used at the server side. |
5 | | * Optional for HTTP/1.0 and HTTP/1.1, mandatory for HTTP/2. |
6 | | * |
7 | | * This file is part of the CivetWeb project. |
8 | | */ |
9 | | |
10 | | #if defined(NO_RESPONSE_BUFFERING) && defined(USE_HTTP2) |
11 | | #error "HTTP2 works only if NO_RESPONSE_BUFFERING is not set" |
12 | | #endif |
13 | | |
14 | | |
15 | | /* Internal function to free header list */ |
16 | | static void |
17 | | free_buffered_response_header_list(struct mg_connection *conn) |
18 | 0 | { |
19 | 0 | #if !defined(NO_RESPONSE_BUFFERING) |
20 | 0 | while (conn->response_info.num_headers > 0) { |
21 | 0 | conn->response_info.num_headers--; |
22 | 0 | mg_free((void *)conn->response_info |
23 | 0 | .http_headers[conn->response_info.num_headers] |
24 | 0 | .name); |
25 | 0 | conn->response_info.http_headers[conn->response_info.num_headers].name = |
26 | 0 | 0; |
27 | 0 | mg_free((void *)conn->response_info |
28 | 0 | .http_headers[conn->response_info.num_headers] |
29 | 0 | .value); |
30 | 0 | conn->response_info.http_headers[conn->response_info.num_headers] |
31 | 0 | .value = 0; |
32 | 0 | } |
33 | | #else |
34 | | (void)conn; /* Nothing to do */ |
35 | | #endif |
36 | 0 | } |
37 | | |
38 | | |
39 | | /* Send first line of HTTP/1.x response */ |
40 | | static int |
41 | | send_http1_response_status_line(struct mg_connection *conn) |
42 | 0 | { |
43 | 0 | const char *status_txt; |
44 | 0 | const char *http_version = conn->request_info.http_version; |
45 | 0 | int status_code = conn->status_code; |
46 | |
|
47 | 0 | if ((status_code < 100) || (status_code > 999)) { |
48 | | /* Set invalid status code to "500 Internal Server Error" */ |
49 | 0 | status_code = 500; |
50 | 0 | } |
51 | 0 | if (!http_version) { |
52 | 0 | http_version = "1.0"; |
53 | 0 | } |
54 | | |
55 | | /* mg_get_response_code_text will never return NULL */ |
56 | 0 | status_txt = mg_get_response_code_text(conn, conn->status_code); |
57 | |
|
58 | 0 | if (mg_printf( |
59 | 0 | conn, "HTTP/%s %i %s\r\n", http_version, status_code, status_txt) |
60 | 0 | < 10) { |
61 | | /* Network sending failed */ |
62 | 0 | return 0; |
63 | 0 | } |
64 | 0 | return 1; |
65 | 0 | } |
66 | | |
67 | | |
68 | | /* Initialize a new HTTP response |
69 | | * Parameters: |
70 | | * conn: Current connection handle. |
71 | | * status: HTTP status code (e.g., 200 for "OK"). |
72 | | * Return: |
73 | | * 0: ok |
74 | | * -1: parameter error |
75 | | * -2: invalid connection type |
76 | | * -3: invalid connection status |
77 | | * -4: network error (only if built with NO_RESPONSE_BUFFERING) |
78 | | */ |
79 | | int |
80 | | mg_response_header_start(struct mg_connection *conn, int status) |
81 | 0 | { |
82 | 0 | int ret = 0; |
83 | 0 | if ((conn == NULL) || (status < 100) || (status > 999)) { |
84 | | /* Parameter error */ |
85 | 0 | return -1; |
86 | 0 | } |
87 | 0 | if ((conn->connection_type != CONNECTION_TYPE_REQUEST) |
88 | 0 | || (conn->protocol_type == PROTOCOL_TYPE_WEBSOCKET)) { |
89 | | /* Only allowed in server context */ |
90 | 0 | return -2; |
91 | 0 | } |
92 | 0 | if (conn->request_state != 0) { |
93 | | /* only allowed if nothing was sent up to now */ |
94 | 0 | return -3; |
95 | 0 | } |
96 | 0 | conn->status_code = status; |
97 | 0 | conn->request_state = 1; |
98 | | |
99 | | /* Buffered response is stored, unbuffered response will be sent directly, |
100 | | * but we can only send HTTP/1.x response here */ |
101 | 0 | #if !defined(NO_RESPONSE_BUFFERING) |
102 | 0 | free_buffered_response_header_list(conn); |
103 | | #else |
104 | | if (!send_http1_response_status_line(conn)) { |
105 | | ret = -4; |
106 | | }; |
107 | | conn->request_state = 1; /* Reset from 10 to 1 */ |
108 | | #endif |
109 | |
|
110 | 0 | return ret; |
111 | 0 | } |
112 | | |
113 | | |
114 | | /* Add a new HTTP response header line |
115 | | * Parameters: |
116 | | * conn: Current connection handle. |
117 | | * header: Header name. |
118 | | * value: Header value. |
119 | | * value_len: Length of header value, excluding the terminating zero. |
120 | | * Use -1 for "strlen(value)". |
121 | | * Return: |
122 | | * 0: ok |
123 | | * -1: parameter error |
124 | | * -2: invalid connection type |
125 | | * -3: invalid connection status |
126 | | * -4: too many headers |
127 | | * -5: out of memory |
128 | | */ |
129 | | int |
130 | | mg_response_header_add(struct mg_connection *conn, |
131 | | const char *header, |
132 | | const char *value, |
133 | | int value_len) |
134 | 0 | { |
135 | 0 | #if !defined(NO_RESPONSE_BUFFERING) |
136 | 0 | int hidx; |
137 | 0 | #endif |
138 | |
|
139 | 0 | if ((conn == NULL) || (header == NULL) || (value == NULL)) { |
140 | | /* Parameter error */ |
141 | 0 | return -1; |
142 | 0 | } |
143 | 0 | if ((conn->connection_type != CONNECTION_TYPE_REQUEST) |
144 | 0 | || (conn->protocol_type == PROTOCOL_TYPE_WEBSOCKET)) { |
145 | | /* Only allowed in server context */ |
146 | 0 | return -2; |
147 | 0 | } |
148 | 0 | if (conn->request_state != 1) { |
149 | | /* only allowed if mg_response_header_start has been called before */ |
150 | 0 | return -3; |
151 | 0 | } |
152 | | |
153 | 0 | #if !defined(NO_RESPONSE_BUFFERING) |
154 | 0 | hidx = conn->response_info.num_headers; |
155 | 0 | if (hidx >= MG_MAX_HEADERS) { |
156 | | /* Too many headers */ |
157 | 0 | return -4; |
158 | 0 | } |
159 | | |
160 | | /* Alloc new element */ |
161 | 0 | conn->response_info.http_headers[hidx].name = |
162 | 0 | mg_strdup_ctx(header, conn->phys_ctx); |
163 | 0 | if (value_len >= 0) { |
164 | 0 | char *hbuf = |
165 | 0 | (char *)mg_malloc_ctx((unsigned)value_len + 1, conn->phys_ctx); |
166 | 0 | if (hbuf) { |
167 | 0 | memcpy(hbuf, value, (unsigned)value_len); |
168 | 0 | hbuf[value_len] = 0; |
169 | 0 | } |
170 | 0 | conn->response_info.http_headers[hidx].value = hbuf; |
171 | 0 | } else { |
172 | 0 | conn->response_info.http_headers[hidx].value = |
173 | 0 | mg_strdup_ctx(value, conn->phys_ctx); |
174 | 0 | } |
175 | |
|
176 | 0 | if ((conn->response_info.http_headers[hidx].name == 0) |
177 | 0 | || (conn->response_info.http_headers[hidx].value == 0)) { |
178 | | /* Out of memory */ |
179 | 0 | mg_free((void *)conn->response_info.http_headers[hidx].name); |
180 | 0 | conn->response_info.http_headers[hidx].name = 0; |
181 | 0 | mg_free((void *)conn->response_info.http_headers[hidx].value); |
182 | 0 | conn->response_info.http_headers[hidx].value = 0; |
183 | 0 | return -5; |
184 | 0 | } |
185 | | |
186 | | /* OK, header stored */ |
187 | 0 | conn->response_info.num_headers++; |
188 | |
|
189 | | #else |
190 | | if (value_len >= 0) { |
191 | | mg_printf(conn, "%s: %.*s\r\n", header, (int)value_len, value); |
192 | | } else { |
193 | | mg_printf(conn, "%s: %s\r\n", header, value); |
194 | | } |
195 | | conn->request_state = 1; /* Reset from 10 to 1 */ |
196 | | #endif |
197 | |
|
198 | 0 | return 0; |
199 | 0 | } |
200 | | |
201 | | |
202 | | /* forward */ |
203 | | static int parse_http_headers(char **buf, struct mg_header hdr[MG_MAX_HEADERS]); |
204 | | |
205 | | |
206 | | /* Add a complete header string (key + value). |
207 | | * Parameters: |
208 | | * conn: Current connection handle. |
209 | | * http1_headers: Header line(s) in the form "name: value". |
210 | | * Return: |
211 | | * >=0: no error, number of header lines added |
212 | | * -1: parameter error |
213 | | * -2: invalid connection type |
214 | | * -3: invalid connection status |
215 | | * -4: too many headers |
216 | | * -5: out of memory |
217 | | */ |
218 | | int |
219 | | mg_response_header_add_lines(struct mg_connection *conn, |
220 | | const char *http1_headers) |
221 | 0 | { |
222 | 0 | struct mg_header add_hdr[MG_MAX_HEADERS]; |
223 | 0 | int num_hdr, i, ret; |
224 | 0 | char *workbuffer, *parse; |
225 | | |
226 | | /* We need to work on a copy of the work buffer, sice parse_http_headers |
227 | | * will modify */ |
228 | 0 | workbuffer = mg_strdup_ctx(http1_headers, conn->phys_ctx); |
229 | 0 | if (!workbuffer) { |
230 | | /* Out of memory */ |
231 | 0 | return -5; |
232 | 0 | } |
233 | | |
234 | | /* Call existing method to split header buffer */ |
235 | 0 | parse = workbuffer; |
236 | 0 | num_hdr = parse_http_headers(&parse, add_hdr); |
237 | 0 | ret = num_hdr; |
238 | |
|
239 | 0 | for (i = 0; i < num_hdr; i++) { |
240 | 0 | int lret = |
241 | 0 | mg_response_header_add(conn, add_hdr[i].name, add_hdr[i].value, -1); |
242 | 0 | if ((ret > 0) && (lret < 0)) { |
243 | | /* Store error return value */ |
244 | 0 | ret = lret; |
245 | 0 | } |
246 | 0 | } |
247 | | |
248 | | /* mg_response_header_add created a copy, so we can free the original */ |
249 | 0 | mg_free(workbuffer); |
250 | 0 | return ret; |
251 | 0 | } |
252 | | |
253 | | |
254 | | #if defined(USE_HTTP2) |
255 | | static int http2_send_response_headers(struct mg_connection *conn); |
256 | | #endif |
257 | | |
258 | | |
259 | | /* Send http response |
260 | | * Parameters: |
261 | | * conn: Current connection handle. |
262 | | * Return: |
263 | | * 0: ok |
264 | | * -1: parameter error |
265 | | * -2: invalid connection type |
266 | | * -3: invalid connection status |
267 | | * -4: network send failed |
268 | | */ |
269 | | int |
270 | | mg_response_header_send(struct mg_connection *conn) |
271 | 0 | { |
272 | 0 | #if !defined(NO_RESPONSE_BUFFERING) |
273 | 0 | int i; |
274 | 0 | int has_date = 0; |
275 | 0 | int has_connection = 0; |
276 | 0 | #endif |
277 | |
|
278 | 0 | if (conn == NULL) { |
279 | | /* Parameter error */ |
280 | 0 | return -1; |
281 | 0 | } |
282 | 0 | if ((conn->connection_type != CONNECTION_TYPE_REQUEST) |
283 | 0 | || (conn->protocol_type == PROTOCOL_TYPE_WEBSOCKET)) { |
284 | | /* Only allowed in server context */ |
285 | 0 | return -2; |
286 | 0 | } |
287 | 0 | if (conn->request_state != 1) { |
288 | | /* only allowed if mg_response_header_start has been called before */ |
289 | 0 | return -3; |
290 | 0 | } |
291 | | |
292 | | /* State: 2 */ |
293 | 0 | conn->request_state = 2; |
294 | |
|
295 | 0 | #if !defined(NO_RESPONSE_BUFFERING) |
296 | | #if defined(USE_HTTP2) |
297 | | if (conn->protocol_type == PROTOCOL_TYPE_HTTP2) { |
298 | | int ret = http2_send_response_headers(conn); |
299 | | free_buffered_response_header_list(conn); |
300 | | return (ret ? 0 : -4); |
301 | | } |
302 | | #endif |
303 | | |
304 | | /* Send */ |
305 | 0 | if (!send_http1_response_status_line(conn)) { |
306 | 0 | free_buffered_response_header_list(conn); |
307 | 0 | return -4; |
308 | 0 | }; |
309 | 0 | for (i = 0; i < conn->response_info.num_headers; i++) { |
310 | 0 | mg_printf(conn, |
311 | 0 | "%s: %s\r\n", |
312 | 0 | conn->response_info.http_headers[i].name, |
313 | 0 | conn->response_info.http_headers[i].value); |
314 | | |
315 | | /* Check for some special headers */ |
316 | 0 | if (!mg_strcasecmp("Date", conn->response_info.http_headers[i].name)) { |
317 | 0 | has_date = 1; |
318 | 0 | } |
319 | 0 | if (!mg_strcasecmp("Connection", |
320 | 0 | conn->response_info.http_headers[i].name)) { |
321 | 0 | has_connection = 1; |
322 | 0 | } |
323 | 0 | } |
324 | |
|
325 | 0 | if (!has_date) { |
326 | 0 | time_t curtime = time(NULL); |
327 | 0 | char date[64]; |
328 | 0 | gmt_time_string(date, sizeof(date), &curtime); |
329 | 0 | mg_printf(conn, "Date: %s\r\n", date); |
330 | 0 | } |
331 | 0 | if (!has_connection) { |
332 | 0 | mg_printf(conn, "Connection: %s\r\n", suggest_connection_header(conn)); |
333 | 0 | } |
334 | 0 | #endif |
335 | |
|
336 | 0 | mg_write(conn, "\r\n", 2); |
337 | 0 | conn->request_state = 3; |
338 | | |
339 | | /* ok */ |
340 | 0 | free_buffered_response_header_list(conn); |
341 | 0 | return 0; |
342 | 0 | } |