blob: 202f7a468b53877f7edba22e98122150b5357ce6 [file] [log] [blame]
Willy Tarreau35b51c62018-09-10 15:38:55 +02001/*
2 * HTTP semantics
3 *
4 * Copyright 2000-2018 Willy Tarreau <w@1wt.eu>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version.
10 *
11 */
12
13#include <ctype.h>
14#include <common/config.h>
15#include <common/http.h>
Willy Tarreau04f1e2d2018-09-10 18:04:24 +020016#include <common/standard.h>
Willy Tarreau35b51c62018-09-10 15:38:55 +020017
18/* It is about twice as fast on recent architectures to lookup a byte in a
19 * table than to perform a boolean AND or OR between two tests. Refer to
20 * RFC2616/RFC5234/RFC7230 for those chars. A token is any ASCII char that is
21 * neither a separator nor a CTL char. An http ver_token is any ASCII which can
22 * be found in an HTTP version, which includes 'H', 'T', 'P', '/', '.' and any
23 * digit. Note: please do not overwrite values in assignment since gcc-2.95
24 * will not handle them correctly. It's worth noting that chars 128..255 are
25 * nothing, not even control chars.
26 */
27const unsigned char http_char_classes[256] = {
28 [ 0] = HTTP_FLG_CTL,
29 [ 1] = HTTP_FLG_CTL,
30 [ 2] = HTTP_FLG_CTL,
31 [ 3] = HTTP_FLG_CTL,
32 [ 4] = HTTP_FLG_CTL,
33 [ 5] = HTTP_FLG_CTL,
34 [ 6] = HTTP_FLG_CTL,
35 [ 7] = HTTP_FLG_CTL,
36 [ 8] = HTTP_FLG_CTL,
37 [ 9] = HTTP_FLG_SPHT | HTTP_FLG_LWS | HTTP_FLG_SEP | HTTP_FLG_CTL,
38 [ 10] = HTTP_FLG_CRLF | HTTP_FLG_LWS | HTTP_FLG_CTL,
39 [ 11] = HTTP_FLG_CTL,
40 [ 12] = HTTP_FLG_CTL,
41 [ 13] = HTTP_FLG_CRLF | HTTP_FLG_LWS | HTTP_FLG_CTL,
42 [ 14] = HTTP_FLG_CTL,
43 [ 15] = HTTP_FLG_CTL,
44 [ 16] = HTTP_FLG_CTL,
45 [ 17] = HTTP_FLG_CTL,
46 [ 18] = HTTP_FLG_CTL,
47 [ 19] = HTTP_FLG_CTL,
48 [ 20] = HTTP_FLG_CTL,
49 [ 21] = HTTP_FLG_CTL,
50 [ 22] = HTTP_FLG_CTL,
51 [ 23] = HTTP_FLG_CTL,
52 [ 24] = HTTP_FLG_CTL,
53 [ 25] = HTTP_FLG_CTL,
54 [ 26] = HTTP_FLG_CTL,
55 [ 27] = HTTP_FLG_CTL,
56 [ 28] = HTTP_FLG_CTL,
57 [ 29] = HTTP_FLG_CTL,
58 [ 30] = HTTP_FLG_CTL,
59 [ 31] = HTTP_FLG_CTL,
60 [' '] = HTTP_FLG_SPHT | HTTP_FLG_LWS | HTTP_FLG_SEP,
61 ['!'] = HTTP_FLG_TOK,
62 ['"'] = HTTP_FLG_SEP,
63 ['#'] = HTTP_FLG_TOK,
64 ['$'] = HTTP_FLG_TOK,
65 ['%'] = HTTP_FLG_TOK,
66 ['&'] = HTTP_FLG_TOK,
67 [ 39] = HTTP_FLG_TOK,
68 ['('] = HTTP_FLG_SEP,
69 [')'] = HTTP_FLG_SEP,
70 ['*'] = HTTP_FLG_TOK,
71 ['+'] = HTTP_FLG_TOK,
72 [','] = HTTP_FLG_SEP,
73 ['-'] = HTTP_FLG_TOK,
74 ['.'] = HTTP_FLG_TOK | HTTP_FLG_VER,
75 ['/'] = HTTP_FLG_SEP | HTTP_FLG_VER,
76 ['0'] = HTTP_FLG_TOK | HTTP_FLG_VER | HTTP_FLG_DIG,
77 ['1'] = HTTP_FLG_TOK | HTTP_FLG_VER | HTTP_FLG_DIG,
78 ['2'] = HTTP_FLG_TOK | HTTP_FLG_VER | HTTP_FLG_DIG,
79 ['3'] = HTTP_FLG_TOK | HTTP_FLG_VER | HTTP_FLG_DIG,
80 ['4'] = HTTP_FLG_TOK | HTTP_FLG_VER | HTTP_FLG_DIG,
81 ['5'] = HTTP_FLG_TOK | HTTP_FLG_VER | HTTP_FLG_DIG,
82 ['6'] = HTTP_FLG_TOK | HTTP_FLG_VER | HTTP_FLG_DIG,
83 ['7'] = HTTP_FLG_TOK | HTTP_FLG_VER | HTTP_FLG_DIG,
84 ['8'] = HTTP_FLG_TOK | HTTP_FLG_VER | HTTP_FLG_DIG,
85 ['9'] = HTTP_FLG_TOK | HTTP_FLG_VER | HTTP_FLG_DIG,
86 [':'] = HTTP_FLG_SEP,
87 [';'] = HTTP_FLG_SEP,
88 ['<'] = HTTP_FLG_SEP,
89 ['='] = HTTP_FLG_SEP,
90 ['>'] = HTTP_FLG_SEP,
91 ['?'] = HTTP_FLG_SEP,
92 ['@'] = HTTP_FLG_SEP,
93 ['A'] = HTTP_FLG_TOK,
94 ['B'] = HTTP_FLG_TOK,
95 ['C'] = HTTP_FLG_TOK,
96 ['D'] = HTTP_FLG_TOK,
97 ['E'] = HTTP_FLG_TOK,
98 ['F'] = HTTP_FLG_TOK,
99 ['G'] = HTTP_FLG_TOK,
100 ['H'] = HTTP_FLG_TOK | HTTP_FLG_VER,
101 ['I'] = HTTP_FLG_TOK,
102 ['J'] = HTTP_FLG_TOK,
103 ['K'] = HTTP_FLG_TOK,
104 ['L'] = HTTP_FLG_TOK,
105 ['M'] = HTTP_FLG_TOK,
106 ['N'] = HTTP_FLG_TOK,
107 ['O'] = HTTP_FLG_TOK,
108 ['P'] = HTTP_FLG_TOK | HTTP_FLG_VER,
109 ['Q'] = HTTP_FLG_TOK,
110 ['R'] = HTTP_FLG_TOK | HTTP_FLG_VER,
111 ['S'] = HTTP_FLG_TOK | HTTP_FLG_VER,
112 ['T'] = HTTP_FLG_TOK | HTTP_FLG_VER,
113 ['U'] = HTTP_FLG_TOK,
114 ['V'] = HTTP_FLG_TOK,
115 ['W'] = HTTP_FLG_TOK,
116 ['X'] = HTTP_FLG_TOK,
117 ['Y'] = HTTP_FLG_TOK,
118 ['Z'] = HTTP_FLG_TOK,
119 ['['] = HTTP_FLG_SEP,
120 [ 92] = HTTP_FLG_SEP,
121 [']'] = HTTP_FLG_SEP,
122 ['^'] = HTTP_FLG_TOK,
123 ['_'] = HTTP_FLG_TOK,
124 ['`'] = HTTP_FLG_TOK,
125 ['a'] = HTTP_FLG_TOK,
126 ['b'] = HTTP_FLG_TOK,
127 ['c'] = HTTP_FLG_TOK,
128 ['d'] = HTTP_FLG_TOK,
129 ['e'] = HTTP_FLG_TOK,
130 ['f'] = HTTP_FLG_TOK,
131 ['g'] = HTTP_FLG_TOK,
132 ['h'] = HTTP_FLG_TOK,
133 ['i'] = HTTP_FLG_TOK,
134 ['j'] = HTTP_FLG_TOK,
135 ['k'] = HTTP_FLG_TOK,
136 ['l'] = HTTP_FLG_TOK,
137 ['m'] = HTTP_FLG_TOK,
138 ['n'] = HTTP_FLG_TOK,
139 ['o'] = HTTP_FLG_TOK,
140 ['p'] = HTTP_FLG_TOK,
141 ['q'] = HTTP_FLG_TOK,
142 ['r'] = HTTP_FLG_TOK,
143 ['s'] = HTTP_FLG_TOK,
144 ['t'] = HTTP_FLG_TOK,
145 ['u'] = HTTP_FLG_TOK,
146 ['v'] = HTTP_FLG_TOK,
147 ['w'] = HTTP_FLG_TOK,
148 ['x'] = HTTP_FLG_TOK,
149 ['y'] = HTTP_FLG_TOK,
150 ['z'] = HTTP_FLG_TOK,
151 ['{'] = HTTP_FLG_SEP,
152 ['|'] = HTTP_FLG_TOK,
153 ['}'] = HTTP_FLG_SEP,
154 ['~'] = HTTP_FLG_TOK,
155 [127] = HTTP_FLG_CTL,
156};
157
Willy Tarreau04f1e2d2018-09-10 18:04:24 +0200158/* We must put the messages here since GCC cannot initialize consts depending
159 * on strlen().
160 */
161struct buffer http_err_chunks[HTTP_ERR_SIZE];
162
163const struct ist HTTP_100 = IST("HTTP/1.1 100 Continue\r\n\r\n");
164
165/* Warning: no "connection" header is provided with the 3xx messages below */
166const char *HTTP_301 =
167 "HTTP/1.1 301 Moved Permanently\r\n"
168 "Content-length: 0\r\n"
169 "Location: "; /* not terminated since it will be concatenated with the URL */
170
171const char *HTTP_302 =
172 "HTTP/1.1 302 Found\r\n"
173 "Cache-Control: no-cache\r\n"
174 "Content-length: 0\r\n"
175 "Location: "; /* not terminated since it will be concatenated with the URL */
176
177/* same as 302 except that the browser MUST retry with the GET method */
178const char *HTTP_303 =
179 "HTTP/1.1 303 See Other\r\n"
180 "Cache-Control: no-cache\r\n"
181 "Content-length: 0\r\n"
182 "Location: "; /* not terminated since it will be concatenated with the URL */
183
184/* same as 302 except that the browser MUST retry with the same method */
185const char *HTTP_307 =
186 "HTTP/1.1 307 Temporary Redirect\r\n"
187 "Cache-Control: no-cache\r\n"
188 "Content-length: 0\r\n"
189 "Location: "; /* not terminated since it will be concatenated with the URL */
190
191/* same as 301 except that the browser MUST retry with the same method */
192const char *HTTP_308 =
193 "HTTP/1.1 308 Permanent Redirect\r\n"
194 "Content-length: 0\r\n"
195 "Location: "; /* not terminated since it will be concatenated with the URL */
196
197/* Warning: this one is an sprintf() fmt string, with <realm> as its only argument */
198const char *HTTP_401_fmt =
199 "HTTP/1.0 401 Unauthorized\r\n"
200 "Cache-Control: no-cache\r\n"
201 "Connection: close\r\n"
202 "Content-Type: text/html\r\n"
203 "WWW-Authenticate: Basic realm=\"%s\"\r\n"
204 "\r\n"
205 "<html><body><h1>401 Unauthorized</h1>\nYou need a valid user and password to access this content.\n</body></html>\n";
206
207const char *HTTP_407_fmt =
208 "HTTP/1.0 407 Unauthorized\r\n"
209 "Cache-Control: no-cache\r\n"
210 "Connection: close\r\n"
211 "Content-Type: text/html\r\n"
212 "Proxy-Authenticate: Basic realm=\"%s\"\r\n"
213 "\r\n"
214 "<html><body><h1>407 Unauthorized</h1>\nYou need a valid user and password to access this content.\n</body></html>\n";
215
216const int http_err_codes[HTTP_ERR_SIZE] = {
217 [HTTP_ERR_200] = 200, /* used by "monitor-uri" */
218 [HTTP_ERR_400] = 400,
219 [HTTP_ERR_403] = 403,
220 [HTTP_ERR_405] = 405,
221 [HTTP_ERR_408] = 408,
222 [HTTP_ERR_421] = 421,
223 [HTTP_ERR_425] = 425,
224 [HTTP_ERR_429] = 429,
225 [HTTP_ERR_500] = 500,
226 [HTTP_ERR_502] = 502,
227 [HTTP_ERR_503] = 503,
228 [HTTP_ERR_504] = 504,
229};
230
231static const char *http_err_msgs[HTTP_ERR_SIZE] = {
232 [HTTP_ERR_200] =
233 "HTTP/1.0 200 OK\r\n"
234 "Cache-Control: no-cache\r\n"
235 "Connection: close\r\n"
236 "Content-Type: text/html\r\n"
237 "\r\n"
238 "<html><body><h1>200 OK</h1>\nService ready.\n</body></html>\n",
239
240 [HTTP_ERR_400] =
241 "HTTP/1.0 400 Bad request\r\n"
242 "Cache-Control: no-cache\r\n"
243 "Connection: close\r\n"
244 "Content-Type: text/html\r\n"
245 "\r\n"
246 "<html><body><h1>400 Bad request</h1>\nYour browser sent an invalid request.\n</body></html>\n",
247
248 [HTTP_ERR_403] =
249 "HTTP/1.0 403 Forbidden\r\n"
250 "Cache-Control: no-cache\r\n"
251 "Connection: close\r\n"
252 "Content-Type: text/html\r\n"
253 "\r\n"
254 "<html><body><h1>403 Forbidden</h1>\nRequest forbidden by administrative rules.\n</body></html>\n",
255
256 [HTTP_ERR_405] =
257 "HTTP/1.0 405 Method Not Allowed\r\n"
258 "Cache-Control: no-cache\r\n"
259 "Connection: close\r\n"
260 "Content-Type: text/html\r\n"
261 "\r\n"
262 "<html><body><h1>405 Method Not Allowed</h1>\nA request was made of a resource using a request method not supported by that resource\n</body></html>\n",
263
264 [HTTP_ERR_408] =
265 "HTTP/1.0 408 Request Time-out\r\n"
266 "Cache-Control: no-cache\r\n"
267 "Connection: close\r\n"
268 "Content-Type: text/html\r\n"
269 "\r\n"
270 "<html><body><h1>408 Request Time-out</h1>\nYour browser didn't send a complete request in time.\n</body></html>\n",
271
272 [HTTP_ERR_421] =
273 "HTTP/1.0 421 Misdirected Request\r\n"
274 "Cache-Control: no-cache\r\n"
275 "Connection: close\r\n"
276 "Content-Type: text/html\r\n"
277 "\r\n"
278 "<html><body><h1>421 Misdirected Request</h1>\nRequest sent to a non-authoritative server.\n</body></html>\n",
279
280 [HTTP_ERR_425] =
281 "HTTP/1.0 425 Too Early\r\n"
282 "Cache-Control: no-cache\r\n"
283 "Connection: close\r\n"
284 "Content-Type: text/html\r\n"
285 "\r\n"
286 "<html><body><h1>425 Too Early</h1>\nYour browser sent early data.\n</body></html>\n",
287
288 [HTTP_ERR_429] =
289 "HTTP/1.0 429 Too Many Requests\r\n"
290 "Cache-Control: no-cache\r\n"
291 "Connection: close\r\n"
292 "Content-Type: text/html\r\n"
293 "\r\n"
294 "<html><body><h1>429 Too Many Requests</h1>\nYou have sent too many requests in a given amount of time.\n</body></html>\n",
295
296 [HTTP_ERR_500] =
297 "HTTP/1.0 500 Internal Server Error\r\n"
298 "Cache-Control: no-cache\r\n"
299 "Connection: close\r\n"
300 "Content-Type: text/html\r\n"
301 "\r\n"
302 "<html><body><h1>500 Internal Server Error</h1>\nAn internal server error occured.\n</body></html>\n",
303
304 [HTTP_ERR_502] =
305 "HTTP/1.0 502 Bad Gateway\r\n"
306 "Cache-Control: no-cache\r\n"
307 "Connection: close\r\n"
308 "Content-Type: text/html\r\n"
309 "\r\n"
310 "<html><body><h1>502 Bad Gateway</h1>\nThe server returned an invalid or incomplete response.\n</body></html>\n",
311
312 [HTTP_ERR_503] =
313 "HTTP/1.0 503 Service Unavailable\r\n"
314 "Cache-Control: no-cache\r\n"
315 "Connection: close\r\n"
316 "Content-Type: text/html\r\n"
317 "\r\n"
318 "<html><body><h1>503 Service Unavailable</h1>\nNo server is available to handle this request.\n</body></html>\n",
319
320 [HTTP_ERR_504] =
321 "HTTP/1.0 504 Gateway Time-out\r\n"
322 "Cache-Control: no-cache\r\n"
323 "Connection: close\r\n"
324 "Content-Type: text/html\r\n"
325 "\r\n"
326 "<html><body><h1>504 Gateway Time-out</h1>\nThe server didn't respond in time.\n</body></html>\n",
327
328};
329
Willy Tarreau35b51c62018-09-10 15:38:55 +0200330const struct ist http_known_methods[HTTP_METH_OTHER] = {
331 [HTTP_METH_OPTIONS] = IST("OPTIONS"),
332 [HTTP_METH_GET] = IST("GET"),
333 [HTTP_METH_HEAD] = IST("HEAD"),
334 [HTTP_METH_POST] = IST("POST"),
335 [HTTP_METH_PUT] = IST("PUT"),
336 [HTTP_METH_DELETE] = IST("DELETE"),
337 [HTTP_METH_TRACE] = IST("TRACE"),
338 [HTTP_METH_CONNECT] = IST("CONNECT"),
339};
340
341/*
342 * returns a known method among HTTP_METH_* or HTTP_METH_OTHER for all unknown
343 * ones.
344 */
345enum http_meth_t find_http_meth(const char *str, const int len)
346{
347 const struct ist m = ist2(str, len);
348
349 if (isteq(m, ist("GET"))) return HTTP_METH_GET;
350 else if (isteq(m, ist("HEAD"))) return HTTP_METH_HEAD;
351 else if (isteq(m, ist("POST"))) return HTTP_METH_POST;
352 else if (isteq(m, ist("CONNECT"))) return HTTP_METH_CONNECT;
353 else if (isteq(m, ist("PUT"))) return HTTP_METH_PUT;
354 else if (isteq(m, ist("OPTIONS"))) return HTTP_METH_OPTIONS;
355 else if (isteq(m, ist("DELETE"))) return HTTP_METH_DELETE;
356 else if (isteq(m, ist("TRACE"))) return HTTP_METH_TRACE;
357 else return HTTP_METH_OTHER;
358}
Willy Tarreau6b952c82018-09-10 17:45:34 +0200359
Willy Tarreau04f1e2d2018-09-10 18:04:24 +0200360/* This function returns HTTP_ERR_<num> (enum) matching http status code.
361 * Returned value should match codes from http_err_codes.
362 */
363const int http_get_status_idx(unsigned int status)
364{
365 switch (status) {
366 case 200: return HTTP_ERR_200;
367 case 400: return HTTP_ERR_400;
368 case 403: return HTTP_ERR_403;
369 case 405: return HTTP_ERR_405;
370 case 408: return HTTP_ERR_408;
371 case 421: return HTTP_ERR_421;
372 case 425: return HTTP_ERR_425;
373 case 429: return HTTP_ERR_429;
374 case 500: return HTTP_ERR_500;
375 case 502: return HTTP_ERR_502;
376 case 503: return HTTP_ERR_503;
377 case 504: return HTTP_ERR_504;
378 default: return HTTP_ERR_500;
379 }
380}
381
382/* This function returns a reason associated with the HTTP status.
383 * This function never fails, a message is always returned.
384 */
385const char *http_get_reason(unsigned int status)
386{
387 switch (status) {
388 case 100: return "Continue";
389 case 101: return "Switching Protocols";
390 case 102: return "Processing";
391 case 200: return "OK";
392 case 201: return "Created";
393 case 202: return "Accepted";
394 case 203: return "Non-Authoritative Information";
395 case 204: return "No Content";
396 case 205: return "Reset Content";
397 case 206: return "Partial Content";
398 case 207: return "Multi-Status";
399 case 210: return "Content Different";
400 case 226: return "IM Used";
401 case 300: return "Multiple Choices";
402 case 301: return "Moved Permanently";
403 case 302: return "Moved Temporarily";
404 case 303: return "See Other";
405 case 304: return "Not Modified";
406 case 305: return "Use Proxy";
407 case 307: return "Temporary Redirect";
408 case 308: return "Permanent Redirect";
409 case 310: return "Too many Redirects";
410 case 400: return "Bad Request";
411 case 401: return "Unauthorized";
412 case 402: return "Payment Required";
413 case 403: return "Forbidden";
414 case 404: return "Not Found";
415 case 405: return "Method Not Allowed";
416 case 406: return "Not Acceptable";
417 case 407: return "Proxy Authentication Required";
418 case 408: return "Request Time-out";
419 case 409: return "Conflict";
420 case 410: return "Gone";
421 case 411: return "Length Required";
422 case 412: return "Precondition Failed";
423 case 413: return "Request Entity Too Large";
424 case 414: return "Request-URI Too Long";
425 case 415: return "Unsupported Media Type";
426 case 416: return "Requested range unsatisfiable";
427 case 417: return "Expectation failed";
428 case 418: return "I'm a teapot";
429 case 421: return "Misdirected Request";
430 case 422: return "Unprocessable entity";
431 case 423: return "Locked";
432 case 424: return "Method failure";
433 case 425: return "Too Early";
434 case 426: return "Upgrade Required";
435 case 428: return "Precondition Required";
436 case 429: return "Too Many Requests";
437 case 431: return "Request Header Fields Too Large";
438 case 449: return "Retry With";
439 case 450: return "Blocked by Windows Parental Controls";
440 case 451: return "Unavailable For Legal Reasons";
441 case 456: return "Unrecoverable Error";
442 case 499: return "client has closed connection";
443 case 500: return "Internal Server Error";
444 case 501: return "Not Implemented";
445 case 502: return "Bad Gateway or Proxy Error";
446 case 503: return "Service Unavailable";
447 case 504: return "Gateway Time-out";
448 case 505: return "HTTP Version not supported";
449 case 506: return "Variant also negociate";
450 case 507: return "Insufficient storage";
451 case 508: return "Loop detected";
452 case 509: return "Bandwidth Limit Exceeded";
453 case 510: return "Not extended";
454 case 511: return "Network authentication required";
455 case 520: return "Web server is returning an unknown error";
456 default:
457 switch (status) {
458 case 100 ... 199: return "Informational";
459 case 200 ... 299: return "Success";
460 case 300 ... 399: return "Redirection";
461 case 400 ... 499: return "Client Error";
462 case 500 ... 599: return "Server Error";
463 default: return "Other";
464 }
465 }
466}
467
Willy Tarreau6b952c82018-09-10 17:45:34 +0200468/* Parse the URI from the given transaction (which is assumed to be in request
469 * phase) and look for the "/" beginning the PATH. If not found, ist2(0,0) is
470 * returned. Otherwise the pointer and length are returned.
471 */
472struct ist http_get_path(const struct ist uri)
473{
474 const char *ptr, *end;
475
476 if (!uri.len)
477 goto not_found;
478
479 ptr = uri.ptr;
480 end = ptr + uri.len;
481
482 /* RFC7230, par. 2.7 :
483 * Request-URI = "*" | absuri | abspath | authority
484 */
485
486 if (*ptr == '*')
487 goto not_found;
488
489 if (isalpha((unsigned char)*ptr)) {
490 /* this is a scheme as described by RFC3986, par. 3.1 */
491 ptr++;
492 while (ptr < end &&
493 (isalnum((unsigned char)*ptr) || *ptr == '+' || *ptr == '-' || *ptr == '.'))
494 ptr++;
495 /* skip '://' */
496 if (ptr == end || *ptr++ != ':')
497 goto not_found;
498 if (ptr == end || *ptr++ != '/')
499 goto not_found;
500 if (ptr == end || *ptr++ != '/')
501 goto not_found;
502 }
503 /* skip [user[:passwd]@]host[:[port]] */
504
505 while (ptr < end && *ptr != '/')
506 ptr++;
507
508 if (ptr == end)
509 goto not_found;
510
511 /* OK, we got the '/' ! */
512 return ist2(ptr, end - ptr);
513
514 not_found:
515 return ist2(NULL, 0);
516}
Willy Tarreau04f1e2d2018-09-10 18:04:24 +0200517
518/* post-initializes the HTTP parts. Returns non-zero on error, with <err>
519 * pointing to the error message.
520 */
521int init_http(char **err)
522{
523 int msg;
524
525 for (msg = 0; msg < HTTP_ERR_SIZE; msg++) {
526 if (!http_err_msgs[msg]) {
527 memprintf(err, "Internal error: no message defined for HTTP return code %d", msg);
528 return 0;
529 }
530
531 http_err_chunks[msg].area = (char *)http_err_msgs[msg];
532 http_err_chunks[msg].data = strlen(http_err_msgs[msg]);
533 }
534 return 1;
535}