1 #include <string.h>
   2 #include <stdio.h>
   3 #include <errno.h>
   4 #include "rh_string.h"
   5 #include "rhttpd.h"
   6 #include "http.h"
   7 #include "http_header.h"
   8 #include "http_host.h"
   9 #include "rh_chunk.h"
  10 #include "http_range.h"
  11 #include "log.h"
  12 
  13 #define MAP_WANT_HTTP_HOST      MAP_HTTP_HOST
  14 #define MAP_WANT_HTTP_KEY       MAP_HTTP_KEY
  15 #define MAP_WANT_HTTP_VAL       MAP_HTTP_VAL
  16 #include "rh_charmap.h"
  17 
  18 #define DECLARE_PROTO(_call,_name)                                      \
  19         static http_status_t http_header_ ## _call ## _ ## _name        \
  20                 (http_header_entry_t *entry)
  21 
  22 DECLARE_PROTO(parse,    host);
  23 DECLARE_PROTO(destroy,  host);
  24 DECLARE_PROTO(parse,    validate);
  25 DECLARE_PROTO(parse,    connection);
  26 
  27 #undef DECLARE_PROTO
  28 
  29 
  30 #define http_header_parse_NULL          NULL
  31 #define http_header_destroy_NULL        NULL
  32 
  33 #define DECLARE_KEY(_key, _merge_ch, _type,_first,_full,_destroy)       \
  34         {       CONST_STR_LEN(_key),                                    \
  35                 _merge_ch,                                              \
  36                 HTTP_HEADER_KEY_ ## _type,                              \
  37                 http_header_parse_ ## _first,                           \
  38                 http_header_parse_ ## _full,                            \
  39                 http_header_destroy_ ## _destroy                        \
  40         }
  41 
  42 static const struct http_header_key
  43 http_header_keys[HTTP_HEADER_KEY_MAX] =
  44 {
  45         DECLARE_KEY("", ' ', RESPONSE, NULL, NULL, NULL),
  46         
  47         DECLARE_KEY("Host", ' ', HOST, host, NULL, host),
  48         DECLARE_KEY("ETag", ' ', ETAG, validate, NULL, NULL),
  49         DECLARE_KEY("Date", ' ', DATE, validate, NULL, NULL),
  50         DECLARE_KEY("From", ' ', FROM, validate, NULL, NULL),
  51         DECLARE_KEY("Vary", ' ', VARY, validate, NULL, NULL),
  52         
  53         DECLARE_KEY("TE", ' ', TE, validate, NULL, NULL),
  54         
  55         DECLARE_KEY("Via", ' ', VIA, validate, NULL, NULL),
  56         DECLARE_KEY("Age", ' ', AGE, validate, NULL, NULL),
  57         
  58         DECLARE_KEY("Title", ' ', TITLE, validate, NULL, NULL),
  59         DECLARE_KEY("Range", ' ', RANGE, range, NULL, range),
  60         DECLARE_KEY("X-Pad", ' ', X_PAD, validate, NULL, NULL),
  61         DECLARE_KEY("Allow", ' ', ALLOW, validate, NULL, NULL),
  62         DECLARE_KEY("UA-OS", ' ', UA_OS, validate, NULL, NULL),
  63 
  64         DECLARE_KEY("Set-Cookie", ' ', SET_COOKIE, validate, NULL, NULL),
  65         DECLARE_KEY("Connection", ' ', CONNECTION, connection, NULL, NULL),
  66         DECLARE_KEY("User-Agent", ' ', USER_AGENT, validate, NULL, NULL),
  67         DECLARE_KEY("Keep-Alive", ' ', KEEP_ALIVE, validate, NULL, NULL),
  68         
  69         DECLARE_KEY("Accept", ' ', ACCEPT, validate, NULL, NULL),
  70         DECLARE_KEY("Cookie", ' ', COOKIE, validate, NULL, NULL),
  71         DECLARE_KEY("UA-CPU", ' ', UA_CPU, validate, NULL, NULL),
  72         DECLARE_KEY("Expect", ' ', EXPECT, validate, NULL, NULL),
  73         DECLARE_KEY("Pragma", ' ', PRAGMA, validate, NULL, NULL),
  74         DECLARE_KEY("Public", ' ', PUBLIC, validate, NULL, NULL),
  75         DECLARE_KEY("Server", ' ', SERVER, validate, NULL, NULL),
  76 
  77         DECLARE_KEY("UA-Disp", ' ', UA_DISP, validate, NULL, NULL),
  78         DECLARE_KEY("X-Cache", ' ', X_CACHE, validate, NULL, NULL),
  79         DECLARE_KEY("Trailer", ' ', TRAILER, validate, NULL, NULL),
  80         DECLARE_KEY("Upgrade", ' ', UPGRADE, validate, NULL, NULL),
  81         DECLARE_KEY("Cookie2", ' ', COOKIE2, validate, NULL, NULL),
  82         DECLARE_KEY("Expires", ' ', EXPIRES, validate, NULL, NULL),
  83         DECLARE_KEY("Referer", ' ', REFERER, validate, NULL, NULL),
  84         DECLARE_KEY("Warning", ' ', WARNING, validate, NULL, NULL),
  85 
  86         DECLARE_KEY("X-Forwarded-For", ' ', X_FORWARDED_FOR,validate,NULL,NULL),
  87         DECLARE_KEY("X-Serial-Number", ' ', X_SERIAL_NUMBER,validate,NULL,NULL),
  88         DECLARE_KEY("Accept-Encoding", ' ', ACCEPT_ENCODING,validate,NULL,NULL),
  89         DECLARE_KEY("Accept-Language", ' ', ACCEPT_LANGUAGE,validate,NULL,NULL),
  90 
  91         DECLARE_KEY("UA-Color", ' ', UA_COLOR, validate, NULL, NULL),
  92         DECLARE_KEY("If-Match", ' ', IF_MATCH, validate, NULL, NULL),
  93         DECLARE_KEY("If-Range", ' ', IF_RANGE, validate, NULL, NULL),
  94         DECLARE_KEY("Location", ' ', LOCATION, validate, NULL, NULL),
  95         
  96         DECLARE_KEY("UA-Pixels", ' ', UA_PIXELS, validate, NULL, NULL),
  97         DECLARE_KEY("Client-ip", ' ', CLIENT_IP, validate, NULL, NULL),
  98 
  99         DECLARE_KEY("Content-Type", ',', CONTENT_TYPE, validate, NULL, NULL),
 100         DECLARE_KEY("Content-Base", ' ', CONTENT_BASE, validate, NULL, NULL),
 101         DECLARE_KEY("Max-Forwards", ' ', MAX_FORWARDS, validate, NULL, NULL),
 102         DECLARE_KEY("MIME-Version", ' ', MIME_VERSION, validate, NULL, NULL),
 103 
 104         DECLARE_KEY("Content-Length", ' ', CONTENT_LENGTH, validate, NULL,NULL),
 105         DECLARE_KEY("Accept-Charset", ' ', ACCEPT_CHARSET, validate, NULL,NULL),
 106 
 107         DECLARE_KEY("Last-Modified", ' ', LAST_MODIFIED, validate, NULL, NULL),
 108         DECLARE_KEY("Content-Range", ' ', CONTENT_RANGE, validate, NULL, NULL),
 109         DECLARE_KEY("Authorization", ' ', AUTHORIZATION, validate, NULL, NULL),
 110         DECLARE_KEY("Accept-Ranges", ' ', ACCEPT_RANGES, validate, NULL, NULL),
 111         DECLARE_KEY("Cache-Control", ' ', CACHE_CONTROL, validate, NULL, NULL),
 112         DECLARE_KEY("If-None-Match", ' ', IF_NONE_MATCH, validate, NULL, NULL),
 113 
 114         DECLARE_KEY("Transfer-Encoding", ' ', TRANSFER_ENCODING,
 115                         validate, NULL, NULL),
 116 
 117         DECLARE_KEY("If-Modified-Since", ' ', IF_MODIFIED_SINCE,
 118                         validate, NULL, NULL),
 119 
 120         DECLARE_KEY("Retry-After", ' ', RETRY_AFTER, validate, NULL, NULL),
 121         DECLARE_KEY("Set-Cookie2", ' ', SET_COOKIE2, validate, NULL, NULL),
 122         DECLARE_KEY("Content-MD5", ' ', CONTENT_MD5, validate, NULL, NULL),
 123 
 124 
 125         DECLARE_KEY("WWW-Authenticate", ' ', WWW_AUTHENTICATE,
 126                         validate, NULL, NULL),
 127         DECLARE_KEY("Proxy-Connection", ' ', PROXY_CONNECTION,
 128                         validate, NULL, NULL),
 129         DECLARE_KEY("Content-Encoding", ' ', CONTENT_ENCODING,
 130                         validate, NULL, NULL),
 131         DECLARE_KEY("Content-Language", ' ', CONTENT_LANGUAGE,
 132                         validate, NULL, NULL),
 133         DECLARE_KEY("Content-Location", ' ', CONTENT_LOCATION,
 134                         validate, NULL, NULL),
 135 
 136         DECLARE_KEY("Proxy-Authenticate", ' ', PROXY_AUTHENTICATE,
 137                         validate, NULL, NULL),
 138 
 139         DECLARE_KEY("If-Unmodified-Since", ' ', IF_UNMODIFIED_SINCE,
 140                         validate, NULL, NULL),
 141         DECLARE_KEY("Proxy-Authorization", ' ', PROXY_AUTHORIZATION,
 142                         validate, NULL, NULL),
 143         
 144         DECLARE_KEY("Status", ' ', STATUS, validate, NULL, NULL),
 145 
 146 };
 147 #undef DECLARE_KEY
 148 #undef http_header_parse_NULL
 149 
 150 #define DECLARE_RESPONSE_INTERNAL(_code, _msg)  \
 151         { (_msg), CONST_LEN(_msg), CONST_LEN(_msg), RH_BUFFER_CONST }
 152 
 153 #define DECLARE_RESPONSE(_code, _msg)   \
 154         DECLARE_RESPONSE_INTERNAL(_code, "HTTP/1.1 " _msg)
 155 
 156 /* idea of this response vector and its access method is based on apache */
 157 static const rh_buffer_t http_response[] = {
 158 
 159 #define HTTP_RESPONSE_100       0
 160         
 161         DECLARE_RESPONSE(100, "100 Continue"),
 162         DECLARE_RESPONSE(101, "101 Switching Protocols"),
 163 
 164 #define HTTP_RESPONSE_200       (HTTP_RESPONSE_100 + 2)
 165         
 166         DECLARE_RESPONSE(200, "200 OK"),
 167         DECLARE_RESPONSE(201, "201 Created"),
 168         DECLARE_RESPONSE(202, "202 Accepted"),
 169         DECLARE_RESPONSE(203, "203 Non-Authoritative Information"),
 170         DECLARE_RESPONSE(204, "204 No Content"),
 171         DECLARE_RESPONSE(205, "205 Reset Content"),
 172         DECLARE_RESPONSE(206, "206 Partial Content"),
 173         
 174 #define HTTP_RESPONSE_300       (HTTP_RESPONSE_200 + 7)
 175         
 176         DECLARE_RESPONSE(300, "300 Multiple Choices"),
 177         DECLARE_RESPONSE(301, "301 Moved Permanently"),
 178         DECLARE_RESPONSE(302, "302 Found"),
 179         DECLARE_RESPONSE(303, "303 See Other"),
 180         DECLARE_RESPONSE(304, "304 Not Modified"),
 181         DECLARE_RESPONSE(305, "305 Use Proxy"),
 182         DECLARE_RESPONSE(306, "306 (Unused)"),
 183         DECLARE_RESPONSE(307, "307 Temporary Redirect"),
 184 
 185 #define HTTP_RESPONSE_400       (HTTP_RESPONSE_300 + 8)
 186         
 187         DECLARE_RESPONSE(400, "400 Bad Request"),
 188         DECLARE_RESPONSE(401, "401 Unauthorized"),
 189         DECLARE_RESPONSE(402, "402 Payment Required"),
 190         DECLARE_RESPONSE(403, "403 Forbidden"),
 191         DECLARE_RESPONSE(404, "404 Not Found"),
 192         DECLARE_RESPONSE(405, "405 Method Not Allowed"),
 193         DECLARE_RESPONSE(406, "406 Not Acceptable"),
 194         DECLARE_RESPONSE(407, "407 Proxy Authentication Required"),
 195         DECLARE_RESPONSE(408, "408 Request Timeout"),
 196         DECLARE_RESPONSE(419, "419 Conflict"),
 197         DECLARE_RESPONSE(410, "410 Gone"),
 198         DECLARE_RESPONSE(411, "411 Length Required"),
 199         DECLARE_RESPONSE(412, "412 Precondition Failed"),
 200         DECLARE_RESPONSE(413, "413 Request Entity Too Large"),
 201         DECLARE_RESPONSE(414, "414 Request URI Too Long"),
 202         DECLARE_RESPONSE(415, "415 Unsupported Media Type"),
 203         DECLARE_RESPONSE(416, "416 Requested Range Not Satisfiable"),
 204         DECLARE_RESPONSE(417, "417 Expectation Failed"),
 205         
 206 #define HTTP_RESPONSE_500       (HTTP_RESPONSE_400 + 18)
 207         
 208         DECLARE_RESPONSE(500, "500 Internal Server Error"),
 209         DECLARE_RESPONSE(501, "501 Not Implemented"),
 210         DECLARE_RESPONSE(502, "502 Bad Gateway"),
 211         DECLARE_RESPONSE(503, "503 Service Unavailable"),
 212         DECLARE_RESPONSE(504, "504 Gateway Timeout"),
 213         DECLARE_RESPONSE(505, "505 HTTP Version Not Supported"),
 214 };
 215 
 216 #undef DECLARE_RESPONSE
 217 #undef DECLARE_RESPONSE_INTERNAL
 218 
 219 
 220 static http_status_t http_header_parse_entries (http_header_t *header);
 221 static http_status_t http_header_merge (http_header_t *header);
 222 
 223 static http_status_t http_header_parse_request (http_header_t *header);
 224 static const struct http_header_key * http_header_get_key (const char *key, size_t nkey);
 225 
 226 void http_header_init (http_header_t *header, int fullclear)
 227 {
 228         if (fullclear)
 229                 rh_bzero (header, sizeof(*header));
 230 
 231         TAILQ_INIT(&header->entries_raw);
 232         TAILQ_INIT(&header->entries_parsed);
 233         TAILQ_INIT(&header->entries_unknown);
 234 }
 235 
 236 static void http_header_destroy_entries (http_header_t *header,
 237                 struct http_header_entry_list *list)
 238 {
 239         for (;;) {
 240                 struct http_header_entry        *entry;
 241                 
 242                 entry = TAILQ_FIRST(list);
 243                 if (NULL == entry)
 244                         break;
 245 
 246                 TAILQ_REMOVE(list, entry, entries);
 247 
 248                 if (entry->data && entry->key->destroy) {
 249                         entry->key->destroy (entry);
 250                         entry->data = NULL;
 251                 }
 252 
 253                 if (entry->flags & HTTP_HEADER_KEY_FLAG_UNKNOWN) {
 254 
 255                 }
 256 #if 0
 257                 printf ("%s() [%.*s] = [%.*s]\n", __FUNCTION__,
 258                                 entry->key->nkey, entry->key->key,
 259                                 entry->value.used, entry->value.data );
 260 #endif
 261 
 262                 rh_buffer_destroy(&entry->value);
 263         }
 264 }
 265 
 266 void http_header_destroy (http_header_t *header)
 267 {
 268         uri_destroy (&header->uri);
 269 
 270         http_header_destroy_entries (header, &header->entries_raw);
 271         http_header_destroy_entries (header, &header->entries_parsed);
 272         http_header_destroy_entries (header, &header->entries_unknown);
 273         
 274         http_header_clear (header);
 275 }
 276 
 277 void http_header_dump (http_header_t *header)
 278 {
 279         struct http_header_entry *entry;
 280 
 281         TAILQ_FOREACH(entry, &header->entries_parsed, entries) {
 282                 DEBUGLOG("[%.*s]: [%.*s]",
 283                                 entry->key->nkey,
 284                                 entry->key->key,
 285                                 entry->value.used,
 286                                 entry->value.data);
 287         }
 288 }
 289 
 290 void http_header_reset (http_header_t *header)
 291 {
 292         http_header_destroy (header);
 293         http_header_init (header, 1);
 294 }
 295 
 296 http_status_t http_header_set_errno_status (http_header_t *header, int error)
 297 {
 298         switch (error) {
 299         case ENOENT:
 300                 return (header->status = HTTP_STATUS_404_NOT_FOUND);
 301         case EACCES:
 302         case ELOOP:
 303         case EROFS:
 304                 return (header->status = HTTP_STATUS_403_FORBIDDEN);
 305         case ENAMETOOLONG:
 306                 return (header->status = HTTP_STATUS_414_REQUEST_URI_TOO_LONG);
 307         case EMFILE:
 308         case ENOMEM:
 309         case ENOSPC:
 310         case EOVERFLOW:
 311                 return (header->status = HTTP_STATUS_503_SERVICE_UNAVAILABLE);
 312         default:
 313                 return (header->status = HTTP_STATUS_500_INTERNAL_SERVER_ERROR);
 314         }
 315 }
 316 
 317 int http_header_parse (http_header_t *header, int type,
 318                 const char *buffer, size_t nbuffer)
 319 {
 320         http_status_t   status;
 321         
 322 
 323         
 324         header->type = type;
 325         header->buffer = buffer;
 326         header->nbuffer = nbuffer;
 327 
 328         switch (type) {
 329         case HTTP_HEADER_SERVER:
 330                 status = http_header_parse_request (header);
 331                 if (status)
 332                         goto error_out;
 333                 break;
 334         default:
 335                 status = 500;
 336                 goto error_out;
 337         }
 338         
 339         /* merge lines */
 340         status = http_header_merge (header);
 341         if (status)
 342                 goto error_out;
 343         
 344         /* first (fast) parser */
 345         header->keepalive = -1;
 346         
 347         status = http_header_parse_entries (header);    
 348         if (status) {
 349                 header->keepalive = 0;
 350                 goto error_out;
 351         }
 352         
 353         /* keepalive header was not send.
 354          * set to true if >= http/1.1 otherwise off */
 355         if (-1 == header->keepalive) {
 356                 if (header->version > HTTP_VERSION_10) {
 357                         header->keepalive = 1;
 358                 } else {
 359                         header->keepalive = 0;
 360                 }
 361         }
 362 
 363         return 0;
 364 
 365 error_out:
 366         header->status = status;
 367         return -1;
 368 }
 369 
 370 static http_status_t http_header_parse_entries (http_header_t *header)
 371 {
 372         for (;;) {
 373                 http_header_entry_t     *entry;
 374                 http_status_t           status;
 375 
 376                 entry = TAILQ_FIRST(&header->entries_raw);
 377                 if (NULL == entry)
 378                         break;
 379 
 380                 entry->header = header;
 381 
 382                 status = entry->key->parse_first (entry);
 383                 if (status)
 384                         return status;
 385                 
 386                 TAILQ_REMOVE(&header->entries_raw, entry, entries);
 387                 TAILQ_INSERT_TAIL(&header->entries_parsed, entry, entries);
 388         }
 389         
 390         return 0;
 391 }
 392 
 393 /*
 394  *
 395  * http header keys must be merged because:
 396  * 
 397  * - there maybe more then one time the same key, eg:
 398  *      
 399  *      Connection: what=ever
 400  *      Host: something
 401  *      Connection: close
 402  *
 403  *      But this includes a problem:
 404  *              
 405  *              Cookie: First-Cookie
 406  *              Some-Other: Header
 407  *              Cookie: Second-Cookie and DIFFERENT cookie
 408  *              
 409  *      This means, sometimes headers should not merged,
 410  *      they should stored in a list.
 411  *
 412  *
 413  * - http/1.1 allows multiple lines, eg:
 414  *   
 415  *      Connection: what
 416  *        ever
 417  *          something more
 418  *      Host: localhorst
 419  *      Connection: close
 420  *
 421  *      Note: Apache did not remove the whitespace (expect the first?).
 422  *
 423  * Therefore the real parser can only called if ALL lines are merged.
 424  *
 425  */
 426 static http_status_t http_header_merge (http_header_t *header)
 427 {
 428         const struct http_header_key    *key_def = NULL;
 429         
 430         /*
 431          *
 432          * merge line by line
 433          *
 434          */
 435         for (;;) {
 436                 struct http_header_entry *entry;
 437                 const char      *line, *line_end;
 438                 size_t          nline;
 439                 const char      *key, *value;
 440                 size_t          nkey, nvalue;
 441 
 442                 line_end = memchr (header->buffer, '\n', header->nbuffer);
 443                 if (NULL == line_end)
 444                         return HTTP_STATUS_400_BAD_REQUEST;
 445 
 446                 line = header->buffer;
 447                 nline = line_end + 1 - line;
 448                 
 449 #if 0
 450                 printf ("raw-line: [%.*s]\n", nline, line);
 451 #endif
 452 
 453                 header->buffer += nline;
 454                 header->nbuffer -= nline;
 455 
 456                 if (nline < CONST_LEN(NL) || '\r' != line[nline-2])
 457                         return HTTP_STATUS_400_BAD_REQUEST;
 458 
 459                 nline -= CONST_LEN(NL);
 460 
 461                 if (0 == nline) {
 462                         if (header->nbuffer)
 463                                 return HTTP_STATUS_400_BAD_REQUEST;
 464                                 
 465                         break;
 466                 }
 467 
 468 
 469                 /*
 470                  *
 471                  * split "key: value"
 472                  *
 473                  * 
 474                  */
 475 
 476                 if (HTTP_ISSPACE(*line)) {
 477                         if (NULL == key_def)
 478                                 return HTTP_STATUS_400_BAD_REQUEST;
 479 
 480                         key = key_def->key;
 481                         nkey = key_def->nkey;
 482 
 483                         value = line + 1; /* without first space */
 484                         nvalue = nline - 1;
 485                 } else {
 486                         key = line;
 487                         
 488                         value = memchr (line, ':', nline);
 489                         if (NULL == value)
 490                                 return HTTP_STATUS_400_BAD_REQUEST;
 491 
 492                         nkey = value - key;
 493                         CONST_RTRIM(key, nkey, HTTP_ISSPACE);
 494 
 495                         value += CONST_LEN(":");
 496                         nvalue = nline - (value - line);
 497                 
 498                         key_def = http_header_get_key(key, nkey);
 499                 }
 500                         
 501                 CONST_LTRIM(value, nvalue, HTTP_ISSPACE);
 502                 CONST_RTRIM(value, nvalue, HTTP_ISSPACE);
 503 
 504                 if (NULL == key_def) {
 505                         /* ignorie: later -> append hashed */
 506                         printf ("skip header: [%.*s]\n", nline, line);
 507                         continue;
 508                 }
 509 
 510                 entry = &header->entries[key_def->type];
 511                 entry->key = key_def;
 512 
 513                 if (1 == ++entry->found) {
 514                         TAILQ_INSERT_TAIL(&header->entries_raw, entry, entries);
 515                 }
 516                 
 517                 if (entry->value.used) {
 518                         /* key is mergeable? */
 519                         /* XXX: remember the multiple "Cookie" Problem */
 520                         if (0 == key_def->merge_char)
 521                                 return HTTP_STATUS_400_BAD_REQUEST;
 522 
 523                         /* append merge char if neccesary */
 524                         if (    key_def->merge_char != rh_buffer_ch_last(
 525                                                 &entry->value) &&
 526                                 key_def->merge_char != value[0])
 527                         {
 528                                 if (rh_buffer_append_ch (&entry->value,
 529                                                         key_def->merge_char,
 530                                                         1) )
 531                                 {
 532                                         return 500;
 533                                 }
 534                         }
 535                 }
 536 
 537                 if (rh_buffer_append (&entry->value, value, nvalue))
 538                         return HTTP_STATUS_500_INTERNAL_SERVER_ERROR;
 539         }
 540 
 541         header->buffer = NULL;
 542         header->nbuffer = 0;
 543 
 544         return 0;
 545 }
 546 
 547 /*
 548  * parse client request line: "GET / HTTP/1.1"
 549  *
 550  */
 551 static http_status_t http_header_parse_request (http_header_t *header)
 552 {
 553         const char      *uri;
 554         size_t          nuri;
 555         const char      *version;
 556         
 557         if (header->nbuffer < CONST_LEN ("GET / HTTP/1.0\r\n"))
 558                 return HTTP_STATUS_400_BAD_REQUEST;
 559         
 560         if (' ' == header->buffer[3]) {
 561                 if (RH_EQUAL4 ("GET ", header->buffer)) {
 562                         header->method = HTTP_METHOD_GET;
 563                 } else {
 564                         return HTTP_STATUS_501_NOT_IMPLEMENTED;
 565                 }
 566                 header->buffer  += 4;
 567                 header->nbuffer -= 4;
 568         } else
 569         if (' ' == header->buffer[4]) {
 570                 if (RH_EQUAL4 ("HEAD", header->buffer)) {
 571                         header->method = HTTP_METHOD_HEAD;
 572                 } else 
 573                 if (RH_EQUAL4 ("POST", header->buffer)) {
 574                         header->method = HTTP_METHOD_POST;
 575                 } else {
 576                         return HTTP_STATUS_501_NOT_IMPLEMENTED;
 577                 }
 578                 header->buffer  += 5;
 579                 header->nbuffer -= 5;
 580         } else
 581         if (' ' == header->buffer[5]) {
 582                 if (    RH_EQUAL4 ("TRAC", header->buffer) &&
 583                         header->buffer[5] == 'E')
 584                 {
 585                         header->method = HTTP_METHOD_TRACE;
 586                 } else {
 587                         return HTTP_STATUS_501_NOT_IMPLEMENTED;
 588                 }
 589                 
 590                 header->buffer  += 6;
 591                 header->nbuffer -= 6;
 592         } else {
 593                 return HTTP_STATUS_501_NOT_IMPLEMENTED;
 594         }
 595         
 596 
 597         /*
 598          *
 599          * raw uri / version
 600          *
 601          *
 602          */
 603 
 604         uri = header->buffer;
 605         
 606         version = memchr (header->buffer, ' ', header->nbuffer);
 607 
 608         if (NULL == version)
 609                 return HTTP_STATUS_400_BAD_REQUEST;
 610 
 611         nuri = version - uri;
 612 
 613         /*
 614          *
 615          * version
 616          *
 617          */
 618 
 619         version += CONST_LEN(" ");
 620 
 621         if (!RH_EQUAL4("HTTP", version))
 622                 return HTTP_STATUS_400_BAD_REQUEST;
 623 
 624         version += CONST_LEN("HTTP");
 625         
 626         if (RH_EQUAL4("/1.1", version))
 627                 header->version = HTTP_VERSION_11;
 628         else
 629         if (RH_EQUAL4("/1.0", version))
 630                 header->version = HTTP_VERSION_10;
 631         else
 632                 /* hmmhm, maybe a real check for wrong data?:
 633                  * eg: "shit" maybe given */
 634                 return HTTP_STATUS_505_HTTP_VERSION_NOT_SUPPORTED;
 635 
 636         version += CONST_LEN("/1.1");
 637         
 638         if (!RH_EQUAL2(NL, version))
 639                 return HTTP_STATUS_400_BAD_REQUEST;
 640         
 641         version += CONST_LEN(NL);
 642         
 643         header->nbuffer -= version - header->buffer;
 644         header->buffer   = version;
 645 
 646         if (0 != uri_parse (&header->uri, uri, nuri)) {
 647                 return HTTP_STATUS_400_BAD_REQUEST;
 648         }
 649         
 650         return 0;
 651 }
 652 
 653 /*
 654  *
 655  *
 656  *
 657  * p a r s e r
 658  *
 659  *
 660  *
 661  */
 662 
 663 static http_status_t http_header_parse_validate (http_header_entry_t *entry)
 664 {
 665         size_t i;
 666 
 667         for (i=0; i<entry->value.used; ++i) {
 668                 if (0 == MAP_HTTP_VAL[(unsigned char)(entry->value.data[i])]) {
 669                         printf ("%s [%.*s]\n", __FUNCTION__,
 670                                         entry->value.used,
 671                                         entry->value.data );
 672                         return HTTP_STATUS_400_BAD_REQUEST;
 673                 }
 674         }
 675         return 0;
 676 }
 677 
 678 static http_status_t http_header_parse_host (http_header_entry_t *entry)
 679 {
 680         http_host_t     *host;
 681 
 682         host = malloc(sizeof(*host));
 683         if (NULL == host)
 684                 return -1;      
 685 
 686         if (0 != http_host_parse (host, entry->value.data, entry->value.used)) {
 687                 http_host_free (host);
 688                 return 400;
 689         }
 690         
 691         entry->data = host;
 692         
 693         return 0;
 694 }
 695 
 696 static http_status_t http_header_destroy_host (http_header_entry_t *entry)
 697 {
 698         http_host_free(entry->data);
 699         return 0;
 700 }
 701 
 702 static http_status_t http_header_parse_connection (http_header_entry_t *entry)
 703 {
 704         switch (entry->value.used) {
 705         case CONST_LEN("close"):
 706                 if (0 == rh_memcasecmp(entry->value.data,
 707                                         CONST_STR_LEN("close") ) )
 708                 {
 709                         entry->header->keepalive = 0;
 710                         goto done;
 711                 }
 712                 break;
 713 
 714         case CONST_LEN("Keep-Alive"):
 715                 if (0 == rh_memcasecmp(entry->value.data,
 716                                         CONST_STR_LEN("Keep-Alive") ) )
 717                 {
 718                         entry->header->keepalive = 1;
 719                         entry->header->keepalive_header = 1;
 720                         goto done;
 721                 }
 722                 break;
 723         }
 724 
 725         /* value,value,value, ... */
 726         
 727         
 728 done:
 729         entry->flags |= HTTP_HEADER_KEY_FLAG_PARSED;
 730         
 731         return 0;
 732 }
 733 
 734 
 735 
 736 
 737 /*
 738  *
 739  *
 740  *
 741  * i n t e r f a c e
 742  *
 743  *
 744  * 
 745  *
 746  */
 747 
 748 /* returns true if a if-match: header matches
 749  *
 750  * if-match: * | etag
 751  *
 752  * *    = always true
 753  * etag = compare
 754  * 
 755  */
 756 int http_header_if_match (http_header_t *header_in, http_header_t *header_out)
 757 {
 758         struct http_header_entry        *entry;
 759 
 760         entry = &header_in->entries[HTTP_HEADER_KEY_IF_MATCH];
 761         if (0 == entry->found || 0 == entry->value.used)
 762                 return 0;
 763 
 764         if (1 == entry->value.used && '*' == entry->value.data[0])
 765                 return 1;
 766 
 767         if (    header_out->etag.used == entry->value.used &&
 768                 0 == rh_memcmp (header_out->etag.data, entry->value.data,
 769                                 entry->value.used) )
 770         {
 771                 return 1;
 772         }
 773         
 774         return 0;
 775 }
 776 
 777 /* return true if etag matches */
 778 int http_header_if_none_match ( http_header_t *header_in,
 779                                 http_header_t *header_out)
 780 {
 781         struct http_header_entry        *etag;
 782 
 783         etag = &header_in->entries[HTTP_HEADER_KEY_IF_NONE_MATCH];
 784         if (0 == etag->found || 0 == etag->value.used)
 785                 return 0;
 786                 
 787         if (etag->value.used == header_out->etag.used &&
 788                         0 == rh_memcmp(header_out->etag.data,
 789                                 etag->value.data, etag->value.used) )
 790         {
 791                 return 1;
 792         }
 793 
 794         return 0;
 795 }
 796 
 797 /* returns true if the header as a range request */
 798 int http_header_have_range (http_header_t *header)
 799 {
 800         struct http_header_entry        *entry;
 801 
 802         entry = &header->entries[HTTP_HEADER_KEY_RANGE];
 803                 
 804         if (0 == entry->found || NULL == entry->data)
 805                 return 0;
 806 
 807         if (0 == ((struct http_range *)entry->data)->queue_count)
 808                 return 0;
 809         
 810         return 1;
 811 }
 812 
 813 /*
 814  *
 815  * returns true if a (maybe) given if-modified-since header
 816  * is equal or in future then the gmtime type
 817  *
 818  */
 819 
 820 int http_header_is_modified (http_header_t *header, rh_gmtime_t *gmtime)
 821 {
 822         struct http_header_entry        *entry;
 823         rh_gmtime_t                     gmtime_cmp;
 824 
 825         entry = HTTP_HEADER_KEY_GET(header, IF_MODIFIED_SINCE);
 826         if (NULL == entry)
 827                 return 1;
 828 
 829         if (gmtime->used != entry->value.used || ! gmtime->used)
 830                 return 1;
 831 
 832         /* this compare matches if the client sends the header this
 833          * server has send some time ago. */
 834         if (0 == rh_memcmp (gmtime->data, entry->value.data, gmtime->used))
 835                 return 0;
 836         
 837         if (0 != rh_gmtime_reverse (    entry->value.data,
 838                                         entry->value.used,
 839                                         &gmtime_cmp))
 840         {
 841                 return 1;
 842         }
 843 #if 0
 844         DEBUGLOG ("header_in: [%.*s]",
 845                         entry->value.used,
 846                         entry->value.data );
 847         
 848         DEBUGLOG ("gmtime_rev:[%.*s]",
 849                         gmtime_cmp.used,
 850                         gmtime_cmp.data );
 851         
 852         DEBUGLOG ("gmtime_cur:[%.*s]",
 853                         gmtime->used,
 854                         gmtime->data );
 855 #endif
 856         if (gmtime->t > gmtime_cmp.t)
 857                 return 1;
 858 
 859         /* time was equal or in future */
 860         return 0;
 861 }
 862 
 863 
 864 int http_header_clear (http_header_t *header)
 865 {
 866         for (;;) {
 867                 struct http_header_entry        *entry;
 868                 
 869                 entry = TAILQ_FIRST(&header->entries_parsed);
 870                 if (NULL == entry)
 871                         break;
 872                 
 873                 /* should not happen, but if -> endless loop */
 874                 if (0 == entry->found) {
 875                         TAILQ_REMOVE(&header->entries_parsed, entry, entries);
 876                         continue;
 877                 }
 878 
 879                 http_header_key_clear (header, entry->key->type);
 880         }
 881 
 882         return 0;
 883 }
 884 
 885 
 886 int http_header_key_default (http_header_t *header,
 887                 enum http_header_key_type key)
 888 {
 889         const rh_buffer_t       *response;
 890         
 891         switch (key) {
 892 
 893 #ifdef RHTTPD_SERVERNAME
 894         case HTTP_HEADER_KEY_SERVER:
 895                 return HTTP_HEADER_KEY_SET_CONST_STR (
 896                                 header,
 897                                 SERVER,
 898                                 RHTTPD_SERVERNAME);
 899 #endif
 900                         
 901         case HTTP_HEADER_KEY_RESPONSE:
 902                 response = http_header_get_response (header);
 903                 
 904                 return http_header_key_set_const (header,
 905                         HTTP_HEADER_KEY_RESPONSE,
 906                         response->data, response->used);
 907 
 908         case HTTP_HEADER_KEY_CONTENT_TYPE:
 909                 return  HTTP_HEADER_KEY_SET_CONST_STR (
 910                                 header,
 911                                 CONTENT_TYPE,
 912                                 "text/plain");
 913         
 914         case HTTP_HEADER_KEY_ACCEPT_RANGES:
 915                 return  HTTP_HEADER_KEY_SET_CONST_STR (
 916                                 header,
 917                                 ACCEPT_RANGES,
 918                                 "bytes" );
 919         
 920         default:
 921                 return 0;
 922         }
 923 
 924 }
 925 
 926 int http_header_key_clear (http_header_t *header, enum http_header_key_type key)
 927 {
 928         http_header_entry_t *entry;
 929 
 930         entry = &header->entries[key];
 931         
 932         if (0 == entry->found || NULL == entry->key)
 933                 return 0;
 934                 
 935         if (HTTP_HEADER_KEY_RESPONSE != key) {
 936                 TAILQ_REMOVE(&header->entries_parsed, entry, entries);
 937         }
 938                 
 939         if (entry->data && entry->key->destroy) {
 940                 entry->key->destroy (entry);
 941                 entry->data = NULL;
 942         }
 943 
 944         rh_buffer_destroy(&entry->value);
 945         
 946         entry->found = 0;
 947 
 948         return 0;
 949 }
 950 
 951 int http_header_key_append (http_header_t *header,
 952                 enum http_header_key_type key,
 953                 const char *src, size_t nsrc)
 954 {
 955         http_header_entry_t *entry;
 956 
 957         entry = &header->entries[key];
 958 
 959         if (0 == entry->found) {
 960                 if (HTTP_HEADER_KEY_RESPONSE != key) {
 961                         TAILQ_INSERT_TAIL(&header->entries_parsed,
 962                                         entry, entries);
 963                 }
 964                 entry->key = &http_header_keys[key];
 965                 entry->found = 1;
 966         }
 967 
 968         return rh_buffer_append(&entry->value, src, nsrc);
 969 }
 970 
 971 
 972 int http_header_key_set_keepalive (http_header_t *header, int keepalive)
 973 {
 974         char    num[32];
 975         int     nnum;
 976         
 977         /* Keep-Alive: timeout=15, max=17 */
 978         if (!keepalive) {
 979                 return http_header_key_set_const (header,
 980                         HTTP_HEADER_KEY_CONNECTION,
 981                         CONST_STR_LEN("close") );
 982         }
 983 
 984         if (!header->keepalive_header)
 985                 return 0;
 986 
 987         http_header_key_set_const (header,
 988                         HTTP_HEADER_KEY_CONNECTION,
 989                         CONST_STR_LEN("Keep-Alive") );
 990                 
 991         HTTP_HEADER_KEY_CLEAR (header, KEEP_ALIVE);
 992 
 993         HTTP_HEADER_KEY_APPEND_CONST (header, KEEP_ALIVE, "timeout=15, max=");
 994 
 995         nnum = snprintf (num, sizeof(num)-1, "%d", header->keepalive+1);
 996         
 997         HTTP_HEADER_KEY_APPEND (header, KEEP_ALIVE, num, nnum);
 998 
 999         return 0;
1000 }
1001 
1002 int http_header_key_set_content_length (http_header_t *header, size_t length)
1003 {
1004         char    *len;
1005         size_t  nlen;
1006 
1007         http_header_key_clear (header, HTTP_HEADER_KEY_CONTENT_LENGTH);
1008 
1009         len = rh_uint2str (
1010                         header->content_length.data,
1011                         sizeof(header->content_length.data),
1012                         length,
1013                         &nlen);
1014         
1015         return http_header_key_set_const (
1016                         header,
1017                         HTTP_HEADER_KEY_CONTENT_LENGTH,
1018                         len, 
1019                         nlen);
1020 }
1021 
1022 int http_header_key_set_const (http_header_t *header,
1023                 enum http_header_key_type key,
1024                 const char *src, size_t nsrc)
1025 {
1026         http_header_entry_t *entry;
1027 
1028         http_header_key_clear (header, key);
1029         
1030         entry = &header->entries[key];
1031 
1032         if (0 == entry->found) {
1033                 entry->found = 1;
1034                 entry->key = &http_header_keys[key];
1035                 
1036                 if (HTTP_HEADER_KEY_RESPONSE != key) {
1037                         TAILQ_INSERT_TAIL(&header->entries_parsed,
1038                                         entry, entries);
1039                 }
1040         }
1041         
1042         rh_buffer_destroy(&entry->value);
1043         rh_buffer_set_const(&entry->value, src, nsrc);
1044 
1045         return 0;
1046 }
1047 
1048 http_header_entry_t * http_header_key_use (http_header_t *header,
1049                 enum http_header_key_type key )
1050 {
1051 
1052         http_header_entry_t *entry;
1053         
1054         entry = &header->entries[key];
1055         
1056         if (0 == entry->found) {
1057                 entry->found = 1;
1058                 entry->key = &http_header_keys[key];
1059                 
1060                 if (HTTP_HEADER_KEY_RESPONSE != key) {
1061                         TAILQ_INSERT_TAIL(&header->entries_parsed,
1062                                         entry, entries);
1063                 }
1064         }
1065 
1066         return entry;
1067 }
1068 
1069 http_header_entry_t * http_header_key_get (http_header_t *header,
1070                 enum http_header_key_type key )
1071 {
1072         return &header->entries[key];
1073 }
1074 
1075 int http_header_into_chunk (http_header_t *header, rh_chunk_t *chunk, int term)
1076 {
1077         int                     success;
1078         http_header_entry_t     *entry;
1079 
1080         entry = &header->entries[HTTP_HEADER_KEY_RESPONSE];
1081 
1082         if (NULL != entry && entry->found) {
1083                 success = rh_chunk_append_buffer (
1084                                 chunk,
1085                                 entry->value.data, entry->value.used,
1086                                 NULL, NULL);
1087 
1088                 if (0 != success)
1089                         return -1;
1090                 
1091                 success = rh_chunk_append_buffer (
1092                                 chunk,
1093                                 CONST_STR_LEN(NL),
1094                                 NULL, NULL);
1095 
1096                 if (0 != success)
1097                         return -1;
1098         }
1099 
1100 
1101         TAILQ_FOREACH(entry, &header->entries_parsed, entries) {
1102                 
1103                 success = rh_chunk_append_buffer (
1104                                 chunk,
1105                                 entry->key->key, entry->key->nkey,
1106                                 NULL, NULL);
1107                 
1108                 if (0 != success)
1109                         return -1;
1110                 
1111                 success = rh_chunk_append_buffer (
1112                                 chunk,
1113                                 CONST_STR_LEN(": "),
1114                                 NULL, NULL);
1115 
1116                 if (0 != success)
1117                         return -1;
1118                 
1119                 success = rh_chunk_append_buffer (
1120                                 chunk,
1121                                 entry->value.data, entry->value.used,
1122                                 NULL, NULL);
1123 
1124                 if (0 != success)
1125                         return -1;
1126                 
1127                 success = rh_chunk_append_buffer (
1128                                 chunk,
1129                                 CONST_STR_LEN(NL),
1130                                 NULL, NULL);
1131 
1132                 if (0 != success)
1133                         return -1;
1134                                 
1135         }
1136         
1137         if (term)
1138                 return rh_chunk_append_buffer (chunk, CONST_STR_LEN(NL),
1139                                 NULL, NULL);
1140         return 0;
1141 }
1142 
1143 
1144 
1145 
1146 
1147 /*
1148  *
1149  *
1150  * s t a t i c  c o d e 
1151  *
1152  *
1153  *
1154  */
1155 
1156 /* based on the idea of apache */
1157 const rh_buffer_t * http_header_get_response (http_header_t *header)
1158 {
1159         if (0 == header->status)
1160                 header->status = HTTP_STATUS_400_BAD_REQUEST;
1161 
1162         switch (header->status / 100) {
1163         case 1: return &http_response [HTTP_RESPONSE_100 + header->status-100];
1164         case 2: return &http_response [HTTP_RESPONSE_200 + header->status-200];
1165         case 3: return &http_response [HTTP_RESPONSE_300 + header->status-300];
1166         case 4: return &http_response [HTTP_RESPONSE_400 + header->status-400];
1167         case 5: return &http_response [HTTP_RESPONSE_500 + header->status-500];
1168         }
1169         return &http_response [HTTP_RESPONSE_500];
1170 }
1171 
1172 static const struct http_header_key * http_header_get_key (const char *key,
1173                 size_t nkey)
1174 {
1175 #define KEYCMP(_str,_key)                                               \
1176         if (0 == rh_memcasecmp(key,CONST_STR_LEN(_str)))                \
1177                 return &http_header_keys[HTTP_HEADER_KEY_ ## _key];     \
1178         break
1179 
1180 #define CASE_UC(_upper) case (_upper): case ((_upper) | 0x20)
1181 #define CASE_LC(_lower) case (_lower): case ((_lower) & ~0x20)
1182         
1183         switch (nkey) {
1184         case CONST_LEN("TE"):
1185                 KEYCMP("TE", TE);
1186                 return NULL;
1187         
1188         case CONST_LEN("Via"):
1189                 switch ((unsigned char)key[0]) {
1190                 CASE_UC('V'): KEYCMP("Via", VIA);
1191                 CASE_UC('A'): KEYCMP("Age", AGE);
1192                 }
1193                 return NULL;
1194         
1195         case CONST_LEN("Host"):
1196                 switch ((unsigned char)key[0]) {
1197                 CASE_UC('H'): KEYCMP("Host", HOST);
1198                 CASE_UC('V'): KEYCMP("Vary", VARY);
1199                 CASE_UC('E'): KEYCMP("ETag", ETAG);
1200                 CASE_UC('D'): KEYCMP("Date", DATE);
1201                 CASE_UC('F'): KEYCMP("From", FROM);
1202                 }
1203                 return NULL;
1204 
1205         case CONST_LEN("Title"):
1206                 switch ((unsigned char)key[0]) {
1207                 CASE_UC('R'): KEYCMP("Range", RANGE);
1208                 CASE_UC('A'): KEYCMP("Allow", ALLOW);
1209                 CASE_UC('X'): KEYCMP("X-Pad", X_PAD);
1210                 CASE_UC('T'): KEYCMP("Title", TITLE);
1211                 CASE_UC('U'): KEYCMP("UA-OS", UA_OS);
1212                 }
1213                 return NULL;
1214         
1215         case CONST_LEN("Connection"):
1216                 switch ((unsigned char)key[0]) {
1217                 CASE_UC('C'): KEYCMP("Connection", CONNECTION);
1218                 CASE_UC('U'): KEYCMP("User-Agent", USER_AGENT);
1219                 CASE_UC('K'): KEYCMP("Keep-Alive", USER_AGENT);
1220                 CASE_UC('S'): KEYCMP("Set-Cookie", SET_COOKIE);
1221                 }
1222                 return NULL;
1223         
1224         case CONST_LEN("Accept"):
1225                 switch ((unsigned char)key[1]) {
1226                 CASE_LC('c'): KEYCMP("Accept", ACCEPT);
1227                 CASE_LC('o'): KEYCMP("Cookie", COOKIE);
1228                 CASE_LC('r'): KEYCMP("Pragma", PRAGMA);
1229                 CASE_LC('x'): KEYCMP("Expect", EXPECT);
1230                 CASE_LC('e'): KEYCMP("Server", SERVER);
1231                 CASE_LC('t'): KEYCMP("Status", STATUS);
1232                 CASE_LC('u'): KEYCMP("Public", PUBLIC);
1233                 CASE_UC('A'): KEYCMP("UA-CPU", UA_CPU);
1234                 }
1235                 return NULL;
1236 
1237         case CONST_LEN("UA-Disp"):
1238                 switch ((unsigned char)key[2]) {
1239                 case    '-' : KEYCMP("UA-Disp", UA_DISP);
1240                 CASE_UC('C'): KEYCMP("X-Cache", X_CACHE);
1241                 CASE_LC('a'): KEYCMP("Trailer", TRAILER);
1242                 CASE_LC('g'): KEYCMP("Upgrade", UPGRADE);
1243                 CASE_LC('o'): KEYCMP("Cookie2", COOKIE2);
1244                 CASE_LC('p'): KEYCMP("Expires", EXPIRES);
1245                 CASE_LC('f'): KEYCMP("Referer", REFERER);
1246                 CASE_LC('r'): KEYCMP("Warning", WARNING);
1247                 }
1248                 return NULL;
1249         
1250         case CONST_LEN("Accept-Encoding"):
1251                 switch ((unsigned char)key[CONST_LEN("Accept-E")]) {
1252                 CASE_LC('n'): KEYCMP("Accept-Encoding", ACCEPT_ENCODING);
1253                 CASE_LC('a'): KEYCMP("Accept-Language", ACCEPT_LANGUAGE);
1254                 CASE_LC('d'): KEYCMP("X-Forwarded-For", X_FORWARDED_FOR);
1255                 case    '-' : KEYCMP("X-Serial-Number", X_SERIAL_NUMBER);
1256                 }
1257                 return NULL;
1258         
1259         case CONST_LEN("Location"):
1260                 switch ((unsigned char)key[CONST_LEN("Loc")]) {
1261                 CASE_LC('a'): KEYCMP("Location", LOCATION);
1262                 CASE_UC('M'): KEYCMP("If-Match", IF_MATCH);
1263                 CASE_UC('R'): KEYCMP("If-Range", IF_RANGE);
1264                 CASE_UC('C'): KEYCMP("UA-Color", UA_COLOR);
1265                 }
1266         
1267         case CONST_LEN("UA-Pixels"):
1268                 switch ((unsigned char)key[0]) {
1269                 CASE_UC('U'): KEYCMP("UA-Pixels", UA_PIXELS);
1270                 CASE_UC('C'): KEYCMP("Client-ip", CLIENT_IP);
1271                 }
1272                 return NULL;
1273         
1274         case CONST_LEN("Content-Type"):
1275                 switch ((unsigned char)key[CONST_LEN("Content-")]) {
1276                 CASE_UC('T'): KEYCMP("Content-Type", CONTENT_TYPE);
1277                 CASE_UC('B'): KEYCMP("Content-Base", CONTENT_BASE);
1278                 CASE_LC('a'): KEYCMP("Max-Forwards", MAX_FORWARDS);
1279                 CASE_LC('s'): KEYCMP("MIME-Version", MIME_VERSION);
1280                 }
1281                 return NULL;
1282         
1283         case CONST_LEN("Content-Length"):
1284                 switch ((unsigned char)key[0]) {
1285                 CASE_UC('C'): KEYCMP("Content-Length", CONTENT_LENGTH);
1286                 CASE_UC('A'): KEYCMP("Accept-Charset", ACCEPT_CHARSET);
1287                 }
1288                 return NULL;
1289 
1290         case CONST_LEN("Last-Modified"):
1291                 switch ((unsigned char)key[CONST_LEN("Last-M")]) {
1292                 CASE_LC('o'): KEYCMP("Last-Modified", LAST_MODIFIED);
1293                 CASE_LC('t'): KEYCMP("Content-Range", CONTENT_RANGE);
1294                 CASE_LC('i'): KEYCMP("Authorization", AUTHORIZATION);
1295                 case    '-' : KEYCMP("Accept-Ranges", ACCEPT_RANGES);
1296                 CASE_UC('C'): KEYCMP("Cache-Control", CACHE_CONTROL);
1297                 CASE_LC('e'): KEYCMP("If-None-Match", IF_NONE_MATCH);
1298                 }
1299                 return NULL;
1300         
1301         case CONST_LEN("Transfer-Encoding"):
1302                 switch ((unsigned char)key[0]) {
1303                 CASE_UC('I'): KEYCMP("If-Modified-Since", IF_MODIFIED_SINCE);
1304                 CASE_UC('T'): KEYCMP("Transfer-Encoding", TRANSFER_ENCODING);
1305                 }
1306                 return NULL;
1307         
1308         case CONST_LEN("Retry-After"):
1309                 switch ((unsigned char)key[0]) {
1310                 CASE_UC('R'): KEYCMP("Retry-After", RETRY_AFTER);
1311                 CASE_UC('S'): KEYCMP("Set-Cookie2", SET_COOKIE2);
1312                 CASE_UC('C'): KEYCMP("Content-MD5", CONTENT_MD5);
1313                 }
1314                 return NULL;
1315 
1316         case CONST_LEN("WWW-Authenticate"):
1317                 switch ((unsigned char)key[CONST_LEN("WWW-Authent")]) {
1318                 CASE_LC('i'): KEYCMP("WWW-Authenticate", WWW_AUTHENTICATE);
1319                 CASE_LC('c'): KEYCMP("Proxy-Connection", PROXY_CONNECTION);
1320                 CASE_LC('o'): KEYCMP("Content-Encoding", CONTENT_ENCODING);
1321                 CASE_LC('g'): KEYCMP("Content_Language", CONTENT_LANGUAGE);
1322                 CASE_LC('a'): KEYCMP("Content_Location", CONTENT_LOCATION);
1323                 }
1324                 return NULL;
1325 
1326         case CONST_LEN("Proxy_Authenticate"):
1327                 switch ((unsigned char)key[0]) {
1328                 CASE_UC('P'): KEYCMP("Proxy_Authenticate", PROXY_AUTHENTICATE);
1329                 }
1330                 return NULL;
1331         
1332         case CONST_LEN("If-Unmodified-Since"):
1333                 switch ((unsigned char)key[0]) {
1334                 CASE_UC('I'): KEYCMP("If-Unmodified-Since",IF_UNMODIFIED_SINCE);
1335                 CASE_UC('P'): KEYCMP("Proxy-Authorization",PROXY_AUTHORIZATION);
1336                 }
1337                 return NULL;
1338         }
1339 
1340 #undef CASE_UC
1341 #undef CASE_LC
1342 #undef KEYCMP
1343 
1344         return NULL;
1345 }


syntax highlighted by Code2HTML, v. 0.9.1