PageRenderTime 283ms CodeModel.GetById 111ms app.highlight 7ms RepoModel.GetById 98ms app.codeStats 1ms

/mordor/http/http_parser.rl

http://github.com/mozy/mordor
Unknown | 1008 lines | 850 code | 158 blank | 0 comment | 0 complexity | 16277134f083b4339dd0fe411492db71 MD5 | raw file
   1// Copyright (c) 2009 - Mozy, Inc.
   2
   3#include "mordor/pch.h"
   4
   5#include "mordor/http/parser.h"
   6
   7#include <locale>
   8#include <sstream>
   9#include <string>
  10
  11#include "mordor/version.h"
  12
  13namespace Mordor {
  14
  15// From uri.rl
  16std::string unescape(const std::string& str, bool spaceAsPlus = false);
  17
  18namespace HTTP {
  19
  20static
  21Version
  22parseVersion(const char *str)
  23{
  24    Version ver;
  25    ver.major = atoi(str + 5);
  26    ver.minor = atoi(strchr(str + 5, '.') + 1);
  27    return ver;
  28}
  29
  30static boost::posix_time::time_input_facet rfc1123Facet_in("%a, %d %b %Y %H:%M:%S GMT",
  31        1 /* starting refcount, so this never gets deleted */);
  32static boost::posix_time::time_input_facet rfc850Facet_in("%A, %d-%b-%y %H:%M:%S GMT",
  33        1 /* starting refcount, so this never gets deleted */);
  34static boost::posix_time::time_input_facet ansiFacet_in("%a %b %e %H:%M:%S %Y",
  35        1 /* starting refcount, so this never gets deleted */);
  36
  37boost::posix_time::ptime
  38parseHttpDate(const char *str, size_t size)
  39{
  40    boost::posix_time::ptime result;
  41    std::string val(str, size);
  42
  43    #define ATTEMPT_WITH_FACET(facet)                              \
  44    {                                                              \
  45        std::istringstream is(val);                                \
  46        is.imbue(std::locale(is.getloc(), facet));                 \
  47        is >> result;                                              \
  48        if (!result.is_not_a_date_time())                          \
  49            return result;                                         \
  50    }
  51
  52    ATTEMPT_WITH_FACET(&rfc1123Facet_in);
  53    ATTEMPT_WITH_FACET(&rfc850Facet_in);
  54    ATTEMPT_WITH_FACET(&ansiFacet_in);
  55    return result;
  56}
  57
  58static
  59std::string
  60unfold(char *p, char *pe)
  61{
  62    char *start = p;
  63    char *pw = p;
  64
  65    while (p < pe) {
  66        // Skip leading whitespace
  67        if (pw == start) {
  68            if (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
  69                ++p; ++pw; ++start;
  70                continue;
  71            }
  72        }
  73        // Only copy if necessary
  74        if (pw != p) {
  75            *pw = *p;
  76        }
  77        ++p; ++pw;
  78    }
  79    // Remove trailing whitespace
  80    do {
  81        --pw;
  82    } while ((*pw == ' ' || *pw == '\t' || *pw == '\r' || *pw == '\n') && pw >= start);
  83    ++pw;
  84    return std::string(start, pw - start);
  85}
  86
  87std::string
  88unquote(const char *str, size_t size)
  89{
  90    if (size == 0 || (str[0] != '"' && str[0] != '('))
  91        return std::string(str, size);
  92    MORDOR_ASSERT((str[size - 1] == '"' && str[0] == '"') ||
  93           (str[size - 1] == ')' && str[0] == '('));
  94    std::string result(str + 1, size - 2);
  95    char *p = const_cast<char *>(result.c_str());
  96    char *pe = p + result.size();
  97    char *pw = p;
  98
  99    bool escaping = false;
 100    while (p < pe) {
 101        if (escaping) {
 102            escaping = false;
 103        } else if (*p == '\\') {
 104            escaping = true;
 105            ++p;
 106            continue;
 107        }
 108        // Only copy if necessary
 109        if (pw != p)
 110            *pw = *p;
 111        ++p; ++pw;
 112    }
 113    result.resize(pw - result.c_str());
 114    return result;
 115}
 116
 117std::string
 118unquote(const std::string &str)
 119{
 120    if (str.empty() || (str[0] != '"' && str[0] != '('))
 121        return str;
 122    return unquote(str.c_str(), str.size());
 123}
 124
 125%%{
 126    machine http_parser;
 127
 128    # See RFC 2616: http://www.w3.org/Protocols/rfc2616/rfc2616.html
 129
 130    action mark { mark = fpc; }
 131    action mark2 { mark2 = fpc; }
 132    action clearmark2 { mark2 = NULL; }
 133    action done { fbreak; }
 134    action bad_header { if (m_strict) m_isBadValue = true; }
 135    prepush { prepush(); }
 136    postpop { postpop(); }
 137
 138    # basic character types
 139    OCTET = any;
 140    CHAR = ascii;
 141    UPALPHA = "A".."Z";
 142    LOALPHA = "a".."z";
 143    ALPHA = alpha;
 144    DIGIT = digit;
 145    CTL = cntrl | 127;
 146    CR = "\r";
 147    LF = "\n";
 148    SP = " ";
 149    HT = "\t";
 150
 151    # almost-basic character types
 152    # note that we allow a single LF for a CR LF
 153    CRLF = CR LF | LF;
 154    LWS = CRLF? ( SP | HT )+;
 155    TEXT = LWS | (OCTET -- CTL);
 156    HEX = xdigit;
 157
 158    # some basic tokens
 159    separators = "(" | ")" | "<" | ">" | "@" | "," | ";" | ":" | "\\" | "\"" | "/" | "[" | "]" | "?" | "=" | "{" | "}" | SP | HT;
 160    token = (CHAR -- (separators | CTL))+;
 161    # token68 is from http://tools.ietf.org/html/draft-ietf-httpbis-p7-auth-26
 162    # token68 represents a set of 68 chars, we add ":" to support AWS and "@", "'" to support email address
 163    token68 = (ALPHA | DIGIT | "-" | "." | "_" | "~" | "+" | "/" | ":" | "@" | "'")+ "="*;
 164    quoted_pair = "\\" CHAR;
 165    ctext = TEXT -- ("(" | ")");
 166    comment = "(" @{fcall parse_comment;};
 167    parse_comment := (ctext | quoted_pair | '(' @{fcall parse_comment;} )* ")" @{fret;};
 168    qdtext = TEXT -- ("\"" | "\\");
 169    quoted_string = "\"" ( qdtext | quoted_pair )* "\"";
 170
 171    base64char = ALPHA | DIGIT | '+' | '/';
 172    base64 = (base64char{4})+ ( (base64char{3} '=') | (base64char{2} '==') )?;
 173
 174    action parse_HTTP_Version {
 175        MORDOR_ASSERT(m_ver);
 176        *m_ver = parseVersion(mark);
 177        mark = NULL;
 178    }
 179
 180    HTTP_Version = ("HTTP/" DIGIT+ "." DIGIT+) >mark %parse_HTTP_Version;
 181
 182    action save_date {
 183        MORDOR_ASSERT(m_date);
 184        *m_date = parseHttpDate(mark, fpc - mark);
 185        mark = NULL;
 186    }
 187
 188    wkday = "Mon" | "Tue" | "Wed" | "Thu" | "Fri" | "Sat" | "Sun";
 189    weekday = "Monday" | "Tuesday" | "Wednesday" | "Thursday" | "Friday" | "Saturday" | "Sunday";
 190    month = "Jan" | "Feb" | "Mar" | "Apr" | "May" | "Jun" | "Jul" | "Aug" | "Sep" | "Oct" | "Nov" | "Dec";
 191    date1 = DIGIT{2} SP month SP DIGIT{4};
 192    date2 = DIGIT{2} "-" month "-" DIGIT{2};
 193    date3 = month SP ( DIGIT{2} | (SP DIGIT));
 194    time = DIGIT{2} ":" DIGIT{2} ":" DIGIT{2};
 195    rfc1123_date = wkday "," SP date1 SP time SP "GMT";
 196    rfc850_date = weekday "," SP date2 SP time SP "GMT";
 197    asctime_date = wkday SP date3 SP time SP DIGIT{4};
 198    HTTP_date = (rfc1123_date | rfc850_date | asctime_date) >mark %save_date;
 199
 200    delta_seconds = DIGIT+;
 201
 202    action save_product_name {
 203        m_product.product = std::string(mark, fpc - mark);
 204        mark = NULL;
 205    }
 206    action save_product_version {
 207        m_product.version = std::string(mark, fpc - mark);
 208        mark = NULL;
 209    }
 210    action save_product {
 211        MORDOR_ASSERT(m_productAndCommentList);
 212        m_productAndCommentList->push_back(m_product);
 213        m_product = Product();
 214    }
 215    action save_comment {
 216        MORDOR_ASSERT(m_productAndCommentList);
 217        m_productAndCommentList->push_back(unquote(mark, fpc - mark));
 218        mark = NULL;
 219    }
 220
 221    product_version = token;
 222    product = token >mark %save_product_name ("/" product_version >mark %save_product_version)?;
 223    product_or_comment = (product %save_product) | (comment >mark %save_comment);
 224    product_and_comment_list = LWS* product_or_comment ( LWS+ product_or_comment)* LWS*;
 225
 226    qvalue = ('0' ('.' DIGIT{0,3})?) | ('1' ('.' '0'{0,3})?);
 227
 228    subtag = ALPHA{1,8};
 229    primary_tag = ALPHA{1,8};
 230    language_tag = primary_tag ("-" subtag);
 231
 232    action start_etag {
 233        MORDOR_ASSERT(m_eTag);
 234        m_eTag->weak = false;
 235    }
 236    action save_weak {
 237        MORDOR_ASSERT(m_eTag);
 238        m_eTag->unspecified = false;
 239        m_eTag->weak = true;
 240    }
 241    action save_etag {
 242        MORDOR_ASSERT(m_eTag);
 243        m_eTag->unspecified = false;
 244        m_eTag->value = unquote(mark, fpc - mark);
 245    }
 246    action set_etag_list {
 247        m_eTag = &m_tempETag;
 248    }
 249    action save_unspecified {
 250        MORDOR_ASSERT(m_eTagSet);
 251        m_eTagSet->insert(ETag());
 252    }
 253    action save_etag_element {
 254        MORDOR_ASSERT(m_eTagSet);
 255        MORDOR_ASSERT(m_eTag == & m_tempETag);
 256        m_eTagSet->insert(m_tempETag);
 257    }
 258
 259    weak = "W" "/" >save_weak;
 260    opaque_tag = quoted_string >mark %save_etag;
 261    entity_tag = ((weak)? opaque_tag) >start_etag;
 262
 263    etag_list = LWS* ("*" % save_unspecified | (LWS* entity_tag %save_etag_element ( LWS* ',' LWS* entity_tag %save_etag_element)* LWS*) LWS* ) > set_etag_list;
 264
 265    bytes_unit = "bytes";
 266    other_range_unit = token;
 267    range_unit = bytes_unit | other_range_unit;
 268
 269    action save_field_name {
 270        m_genericHeaderName = std::string(mark2, fpc - mark2);
 271        mark2 = NULL;
 272    }
 273    action save_field_value {
 274        if (!mark2) break;
 275
 276        std::string fieldValue = unfold((char*)mark2, (char*)fpc);
 277
 278        if (m_isBadValue) {
 279            MORDOR_THROW_EXCEPTION(BadFieldValueException(m_genericHeaderName, fieldValue));
 280            m_isBadValue = false;
 281            fbreak;
 282        }
 283
 284        StringMap::iterator it = m_entity->extension.find(m_genericHeaderName);
 285        if (it == m_entity->extension.end()) {
 286            m_entity->extension[m_genericHeaderName] = fieldValue;
 287        } else {
 288            it->second.append(",");
 289            it->second.append(fieldValue);
 290        }
 291        mark2 = NULL;
 292    }
 293
 294    field_chars = OCTET -- (CTL | CR LF SP HT);
 295    field_name = token >mark2 %save_field_name;
 296    field_value = TEXT* >mark2 %save_field_value;
 297    message_header = field_name ":" field_value;
 298
 299    action save_string {
 300        *m_string = std::string(mark, fpc - mark);
 301        mark = NULL;
 302    }
 303    action save_ulong {
 304        *m_ulong = strtoull(mark, NULL, 10);
 305        mark = NULL;
 306    }
 307
 308    action save_element {
 309        MORDOR_ASSERT(m_list || m_set);
 310        if (m_list)
 311            m_list->push_back(std::string(mark, fpc - mark));
 312        else
 313            m_set->insert(std::string(mark, fpc - mark));
 314        mark = NULL;
 315    }
 316    element = token >mark %save_element;
 317    list = (LWS* element ( LWS* ',' LWS* element)* LWS*);
 318
 319    action save_parameterized_list_element {
 320        ValueWithParameters vp;
 321        vp.value = std::string(mark, fpc - mark);
 322        m_parameterizedList->push_back(vp);
 323        m_parameters = &m_parameterizedList->back().parameters;
 324        mark = NULL;
 325    }
 326
 327    action save_parameter_attribute {
 328        m_temp1 = std::string(mark, fpc - mark);
 329        // Don't NULL out here; could be base64 later
 330    }
 331
 332    action save_parameter_attribute_unquote {
 333        m_temp1 = unquote(mark, fpc - mark);
 334        // Don't NULL out here; could be base64 later
 335    }
 336
 337    action save_parameter_value {
 338        (*m_parameters)[m_temp1] = unquote(mark, fpc - mark);
 339        mark = NULL;
 340    }
 341
 342    attribute = (token - 'q'i) >mark %save_parameter_attribute; # q separates params from accept-params
 343    value = (token | quoted_string) >mark %save_parameter_value;
 344    parameter = attribute '=' value;
 345    parameterizedListElement = token >mark %save_parameterized_list_element (';' parameter)*;
 346    parameterizedList = LWS* parameterizedListElement ( LWS* ',' LWS* parameterizedListElement)* LWS*;
 347
 348    action save_auth_scheme {
 349        if (m_challengeList && ((!m_challengeList->empty() && m_auth == &m_challengeList->back())
 350            || m_challengeList->empty())) {
 351            AuthParams ap;
 352            m_challengeList->push_back(ap);
 353            m_auth = &m_challengeList->back();
 354        }
 355        m_auth->scheme = std::string(mark, fpc - mark);
 356        m_parameters = &m_auth->parameters;
 357        mark = NULL;
 358    }
 359
 360    action save_token_param {
 361        m_auth->param = std::string(mark, fpc - mark);
 362        mark = NULL;
 363    }
 364
 365    auth_param = attribute '=' value;
 366    auth_scheme = token;
 367    auth_params = auth_param (LWS* ',' LWS* auth_param)*;
 368    auth_token68 = token68 >mark %save_token_param;
 369    credentials = auth_scheme >mark %save_auth_scheme (SP+ (auth_token68 | auth_params))?;
 370    challenge = credentials;
 371    challengeList = LWS* challenge ( LWS* ',' LWS* challenge)* LWS*;
 372
 373    action set_connection {
 374        m_set = &m_general->connection;
 375        m_list = NULL;
 376    }
 377
 378    action set_date {
 379        m_date = &m_general->date;
 380    }
 381
 382    action set_proxy_connection {
 383        m_set = &m_general->proxyConnection;
 384        m_list = NULL;
 385    }
 386
 387    action set_trailer {
 388        m_set = &m_general->trailer;
 389        m_list = NULL;
 390    }
 391
 392    action set_transfer_encoding {
 393        m_parameterizedList = &m_general->transferEncoding;
 394    }
 395
 396    action save_upgrade_product {
 397        m_general->upgrade.push_back(m_product);
 398        m_product = Product();
 399    }
 400
 401    Connection = 'Connection:'i @set_connection list;
 402    Date = 'Date:'i @set_date LWS* HTTP_date $lerr(bad_header) LWS*;
 403    # NON-STANDARD!!!
 404    Proxy_Connection = 'Proxy-Connection:'i @set_proxy_connection list;
 405    Trailer = 'Trailer:'i @set_trailer list;
 406    Transfer_Encoding = 'Transfer-Encoding:'i @set_transfer_encoding parameterizedList;
 407    Upgrade = 'Upgrade:'i LWS* product %save_upgrade_product ( LWS* ',' LWS* product %save_upgrade_product)* LWS*;
 408
 409    general_header = Connection | Date | Proxy_Connection | Trailer | Transfer_Encoding | Upgrade;
 410
 411    action set_allow {
 412        m_list = &m_entity->allow;
 413        m_set = NULL;
 414    }
 415
 416    action set_content_encoding {
 417        m_list = &m_entity->contentEncoding;
 418        m_set = NULL;
 419    }
 420
 421    action set_content_length {
 422        m_ulong = &m_entity->contentLength;
 423    }
 424
 425    action set_content_md5 {
 426        m_string = &m_entity->contentMD5;
 427    }
 428
 429    action save_cr_first_byte_pos {
 430        m_entity->contentRange.first = strtoull(mark, NULL, 10);
 431        mark = NULL;
 432    }
 433
 434    action save_cr_last_byte_pos {
 435        m_entity->contentRange.last = strtoull(mark, NULL, 10);
 436        mark = NULL;
 437    }
 438
 439    action save_blank_cr {
 440        m_entity->contentRange.last = 0;
 441    }
 442
 443    action save_instance_length {
 444        m_entity->contentRange.instance = strtoull(mark, NULL, 10);
 445        mark = NULL;
 446    }
 447
 448    action set_content_type
 449    {
 450        m_parameters = &m_entity->contentType.parameters;
 451    }
 452    action save_type
 453    {
 454        m_entity->contentType.type = std::string(mark, fpc - mark);
 455        mark = NULL;
 456    }
 457    action save_subtype
 458    {
 459        m_entity->contentType.subtype = std::string(mark, fpc - mark);
 460        mark = NULL;
 461    }
 462
 463    action set_expires
 464    {
 465        m_date = &m_entity->expires;
 466    }
 467    action set_last_modified
 468    {
 469        m_date = &m_entity->lastModified;
 470    }
 471
 472    Allow = 'Allow:'i @set_allow list;
 473    Content_Encoding = 'Content-Encoding:'i @set_content_encoding list;
 474    Content_Length = 'Content-Length:'i @set_content_length LWS* DIGIT+ >mark $lerr(bad_header) %save_ulong $lerr(bad_header) LWS*;
 475    Content_MD5 = 'Content-MD5:'i @set_content_md5 LWS* base64 >mark $lerr(bad_header) %save_string LWS*;
 476
 477    byte_range_resp_spec = (DIGIT+ >mark %save_cr_first_byte_pos '-' DIGIT+ >mark %save_cr_last_byte_pos) | '*' %save_blank_cr;
 478    content_range_spec = bytes_unit SP byte_range_resp_spec '/' ( DIGIT+ >mark %save_instance_length | '*');
 479    Content_Range = 'Content-Range:'i LWS* content_range_spec $lerr(bad_header) LWS*;
 480
 481    type = token >mark %save_type;
 482    subtype = token >mark %save_subtype;
 483    media_type = type '/' subtype (';' LWS* parameter)*;
 484    Content_Type = 'Content-Type:'i @set_content_type LWS* media_type LWS*;
 485
 486    Expires = 'Expires:'i @set_expires LWS* HTTP_date $lerr(bad_header) LWS*;
 487    Last_Modified = 'Last-Modified:'i @set_last_modified LWS* HTTP_date $lerr(bad_header) LWS*;
 488
 489    entity_header = Allow | Content_Encoding | Content_Length | Content_MD5 | Content_Range | Content_Type | Expires | Last_Modified; # | message_header;
 490
 491}%%
 492
 493%%{
 494    machine http_request_parser;
 495    include http_parser;
 496    include uri_parser "../uri.rl";
 497
 498    action save_Method {
 499        m_request->requestLine.method = std::string(mark, fpc - mark);
 500        mark = NULL;
 501    }
 502
 503    action set_request_uri {
 504        m_uri = &m_request->requestLine.uri;
 505        m_segments = &m_uri->path.segments;
 506        m_authority = &m_uri->authority;
 507    }
 508
 509    action save_accept_list_element {
 510        if (m_acceptList) {
 511            AcceptValue av;
 512            if (fpc - mark != 1 || *mark != '*')
 513                av.value = std::string(mark, fpc - mark);
 514            m_acceptList->push_back(av);
 515            mark = NULL;
 516        } else {
 517            MORDOR_ASSERT(m_acceptListWithParams);
 518            AcceptValueWithParameters avp;
 519            avp.value = std::string(mark, fpc - mark);
 520            m_acceptListWithParams->push_back(avp);
 521            m_parameters = &m_acceptListWithParams->back().parameters;
 522            mark = NULL;
 523        }
 524    }
 525
 526    action save_qvalue {
 527        unsigned int *qvalue = NULL;
 528        if (m_acceptList) {
 529            qvalue = &m_acceptList->back().qvalue;
 530        } else {
 531            MORDOR_ASSERT(m_acceptListWithParams);
 532            qvalue = &m_acceptListWithParams->back().qvalue;
 533        }
 534        *qvalue = 0;
 535        size_t i = 0;
 536        unsigned int curPlace = 1000;
 537        for (; i < 5 && mark < fpc; ++i, ++mark) {
 538            if (i == 1)
 539                continue;
 540            unsigned int cur = *mark - '0';
 541            *qvalue += cur * curPlace;
 542            curPlace /= 10;
 543        }
 544        mark = NULL;
 545    }
 546
 547    action set_accept_charset {
 548        m_acceptList = &m_request->request.acceptCharset;
 549        m_acceptListWithParams = NULL;
 550    }
 551
 552    action set_accept_encoding {
 553        m_acceptList = &m_request->request.acceptEncoding;
 554        m_acceptListWithParams = NULL;
 555    }
 556
 557    action set_authorization {
 558        m_challengeList = NULL;
 559        m_auth = &m_request->request.authorization;
 560    }
 561
 562    action save_expectation {
 563        KeyValueWithParameters kvp;
 564        kvp.key = std::string(mark, fpc - mark);
 565        m_request->request.expect.push_back(kvp);
 566        mark = NULL;
 567    }
 568    action save_expectation_value {
 569        m_request->request.expect.back().value = unquote(mark, fpc - mark);
 570        mark = NULL;
 571    }
 572    action save_expectation_param {
 573        m_temp1 = std::string(mark, fpc - mark);
 574        m_request->request.expect.back().parameters[m_temp1] = "";
 575        mark = NULL;
 576    }
 577    action save_expectation_param_value {
 578        m_request->request.expect.back().parameters[m_temp1] = unquote(mark, fpc - mark);
 579        mark = NULL;
 580    }
 581
 582    action set_host {
 583        m_string = &m_request->request.host;
 584    }
 585
 586    action set_if_match {
 587        m_eTagSet = &m_request->request.ifMatch;
 588    }
 589
 590    action set_if_modified_since {
 591        m_date = &m_request->request.ifModifiedSince;
 592    }
 593
 594    action set_if_none_match {
 595        m_eTagSet = &m_request->request.ifNoneMatch;
 596    }
 597
 598    action clear_if_range_entity_tag {
 599        m_eTag = NULL;
 600    }
 601
 602    action set_if_range_entity_tag {
 603        if (!m_eTag) {
 604            m_request->request.ifRange = ETag();
 605            m_eTag = boost::get<ETag>(&m_request->request.ifRange);
 606        }
 607    }
 608
 609    action set_if_range_http_date {
 610        m_request->request.ifRange = boost::posix_time::ptime();
 611        m_date = boost::get<boost::posix_time::ptime>(&m_request->request.ifRange);
 612    }
 613
 614    action set_if_unmodified_since {
 615        m_date = &m_request->request.ifModifiedSince;
 616    }
 617
 618    action set_proxy_authorization {
 619        m_challengeList = NULL;
 620        m_auth = &m_request->request.proxyAuthorization;
 621    }
 622
 623    action save_first_byte_pos {
 624        m_request->request.range.push_back(RangeSet::value_type(
 625            strtoull(mark, NULL, 10), ~0ull));
 626        mark = NULL;
 627    }
 628    action save_last_byte_pos {
 629        if (mark != NULL) {
 630            m_request->request.range.back().second = strtoull(mark, NULL, 10);
 631        }
 632        mark = NULL;
 633    }
 634    action save_suffix_byte_pos {
 635        m_request->request.range.push_back(RangeSet::value_type(
 636            ~0ull, strtoull(mark, NULL, 10)));
 637        mark = NULL;
 638    }
 639
 640    action set_referer {
 641        m_uri = &m_request->request.referer;
 642        m_segments = &m_uri->path.segments;
 643        m_authority = &m_uri->authority;
 644    }
 645
 646    action save_accept_attribute {
 647        m_temp1 = std::string(mark, fpc - mark);
 648        m_acceptListWithParams->back().acceptParams[m_temp1] = "";
 649        mark = NULL;
 650    }
 651
 652    action save_accept_value {
 653        m_acceptListWithParams->back().acceptParams[m_temp1] = unquote(mark, fpc - mark);
 654        mark = NULL;
 655    }
 656
 657    action set_te {
 658        m_acceptList = NULL;
 659        m_acceptListWithParams = &m_request->request.te;
 660    }
 661
 662    action set_user_agent {
 663        m_productAndCommentList = &m_request->request.userAgent;
 664    }
 665
 666    acceptListElement = ( token | '*' ) >mark %save_accept_list_element (';q='i qvalue >mark %save_qvalue)?;
 667    acceptList = LWS* acceptListElement ( LWS* ',' LWS* acceptListElement)* LWS*;
 668    Accept_Charset = 'Accept-Charset:'i @set_accept_charset acceptList;
 669
 670    Accept_Encoding = 'Accept-Encoding:'i @set_accept_encoding acceptList;
 671
 672    Authorization = 'Authorization:'i @set_authorization LWS* credentials;
 673
 674    expect_params = ';' token >mark %save_expectation_param ( '=' (token | quoted_string) >mark %save_expectation_param_value )?;
 675    expectation = token >mark %save_expectation ( '=' (token | quoted_string) >mark %save_expectation_value expect_params* )?;
 676    Expect = 'Expect:'i LWS* expectation ( LWS* ',' LWS* expectation )* LWS*;
 677
 678    Host = 'Host:'i @set_host LWS* (host (':' port)?) >mark %save_string LWS*;
 679
 680    If_Match = 'If-Match:'i @set_if_match etag_list;
 681    If_Modified_Since = 'If-Modified-Since:'i @set_if_modified_since LWS* HTTP_date LWS*;
 682    If_None_Match = 'If-None-Match:'i @set_if_none_match etag_list;
 683
 684    weak_for_if_range = "W" "/" >set_if_range_entity_tag >save_weak;
 685    entity_tag_for_if_range = ((weak_for_if_range)? opaque_tag >set_if_range_entity_tag) >clear_if_range_entity_tag;
 686
 687    If_Range = 'If-Range:'i LWS* (entity_tag_for_if_range | HTTP_date >set_if_range_http_date) LWS*;
 688    If_Unmodified_Since = 'If-Unmodified-Since:'i @set_if_unmodified_since LWS* HTTP_date LWS*;
 689
 690    Proxy_Authorization = 'Proxy-Authorization:'i @set_proxy_authorization LWS* credentials;
 691
 692    byte_range_spec = DIGIT+ >mark %save_first_byte_pos '-' (DIGIT+ >mark %save_last_byte_pos)?;
 693    suffix_byte_range_spec = '-' DIGIT+ > mark %save_suffix_byte_pos;
 694    byte_range_set = LWS* (byte_range_spec | suffix_byte_range_spec) ( LWS* ',' LWS* (byte_range_spec | suffix_byte_range_spec))* LWS*;
 695    ranges_specifier = bytes_unit '=' byte_range_set;
 696    Range = 'Range:'i LWS* ranges_specifier $lerr(bad_header);
 697
 698    Referer = 'Referer:'i @set_referer LWS* (absolute_URI | relative_URI);
 699
 700    accept_extension = ';' token >mark %save_accept_attribute ('=' (token | quoted_string) >mark %save_accept_value)?;
 701    accept_params = ';q='i qvalue >mark %save_qvalue (accept_extension)*;
 702    acceptListWithParamsElement = token >mark %save_accept_list_element (';' parameter)* (accept_params)?;
 703    acceptListWithParams = LWS* acceptListWithParamsElement ( LWS* ',' LWS* acceptListWithParamsElement)* LWS*;
 704    TE = 'TE:'i @set_te acceptListWithParams;
 705
 706    User_Agent = 'User-Agent:'i @set_user_agent product_and_comment_list;
 707
 708    request_header = Accept_Charset | Accept_Encoding | Authorization | Expect | Host | If_Match | If_Modified_Since | If_None_Match | If_Range | If_Unmodified_Since | Proxy_Authorization | Range | Referer | TE | User_Agent;
 709
 710    Method = token >mark %save_Method;
 711
 712    # we explicitly add query to path_absolute, because the URI spec changed from RFC 2396 to RFC 3986
 713    # with the query not being part of hier_part
 714    Request_URI = ( "*" | absolute_URI | (path_absolute ( "?" query )?));
 715    # HTTP specifies that a Request_URI may be an authority, but only for the
 716    # CONNECT method; enforce that, and by so doing remove the ambiguity that
 717    # an authority might be a scheme
 718    Connect_Line = 'CONNECT' %save_Method SP authority >set_request_uri SP HTTP_Version CRLF;
 719    Request_Line = (Method - 'CONNECT') SP Request_URI >set_request_uri SP HTTP_Version CRLF;
 720    Request = (Request_Line | Connect_Line) (((general_header | request_header | entity_header) %clearmark2 | message_header) CRLF)* CRLF @done;
 721
 722    main := Request;
 723    write data;
 724}%%
 725
 726void
 727Parser::init()
 728{
 729    m_string = NULL;
 730    m_set = NULL;
 731    m_list = NULL;
 732    m_parameterizedList = NULL;
 733    m_acceptList = NULL;
 734    m_acceptListWithParams = NULL;
 735    m_parameters = NULL;
 736    m_auth = NULL;
 737    m_challengeList = NULL;
 738    m_ulong = NULL;
 739    m_eTag = NULL;
 740    m_eTagSet = NULL;
 741    m_productAndCommentList = NULL;
 742    mark2 = NULL;
 743    m_isBadValue = false;
 744    RagelParser::init();
 745}
 746
 747const char *
 748Parser::earliestPointer() const
 749{
 750    const char *parent = RagelParser::earliestPointer();
 751    if (mark2 && parent)
 752        return (std::min)(mark2, parent);
 753    if (mark2)
 754        return mark2;
 755    return parent;
 756}
 757
 758void
 759Parser::adjustPointers(ptrdiff_t offset)
 760{
 761    if (mark2)
 762        mark2 += offset;
 763    RagelParser::adjustPointers(offset);
 764}
 765
 766RequestParser::RequestParser(Request& request, bool strict)
 767: Parser(strict),
 768  m_request(&request),
 769  m_ver(&request.requestLine.ver),
 770  m_segments(&request.requestLine.uri.path.segments),
 771  m_authority(&request.requestLine.uri.authority),
 772  m_general(&request.general),
 773  m_entity(&request.entity)
 774{}
 775
 776void
 777RequestParser::init()
 778{
 779    Parser::init();
 780    %% write init;
 781}
 782
 783bool
 784RequestParser::final() const
 785{
 786    return cs >= http_request_parser_first_final;
 787}
 788
 789bool
 790RequestParser::error() const
 791{
 792    return cs == http_request_parser_error;
 793}
 794
 795void
 796RequestParser::exec()
 797{
 798#ifdef MSVC
 799#pragma warning(push)
 800#pragma warning(disable : 4244)
 801#endif
 802    %% write exec;
 803#ifdef MSVC
 804#pragma warning(pop)
 805#endif
 806}
 807
 808%%{
 809    machine http_response_parser;
 810    include http_parser;
 811    include uri_parser "../uri.rl";
 812
 813    action parse_Status_Code {
 814        m_response->status.status = (Status)atoi(mark);
 815        mark = NULL;
 816    }
 817
 818    action parse_Reason_Phrase {
 819        m_response->status.reason = std::string(mark, fpc - mark);
 820        mark = NULL;
 821    }
 822
 823    action set_accept_ranges
 824    {
 825        m_set = &m_response->response.acceptRanges;
 826        m_list = NULL;
 827    }
 828    action set_etag
 829    {
 830        m_eTag = &m_response->response.eTag;
 831    }
 832    action set_proxy_authenticate {
 833        m_challengeList = &m_response->response.proxyAuthenticate;
 834    }
 835    action set_retry_after_http_date {
 836        m_response->response.retryAfter = boost::posix_time::ptime();
 837        m_date = boost::get<boost::posix_time::ptime>(&m_response->response.retryAfter);
 838    }
 839    action set_retry_after_delta_seconds {
 840        m_response->response.retryAfter = ~0ull;
 841        m_ulong = boost::get<unsigned long long>(&m_response->response.retryAfter);
 842    }
 843    action set_server {
 844        m_productAndCommentList = &m_response->response.server;
 845    }
 846    action set_www_authenticate {
 847        m_challengeList = &m_response->response.wwwAuthenticate;
 848    }
 849
 850    Accept_Ranges = 'Accept-Ranges:'i @set_accept_ranges list;
 851    ETag = 'ETag:'i @set_etag LWS* entity_tag;
 852    # This *should* be absolute_URI, but we're generous
 853    Location = 'Location:'i LWS* URI_reference LWS*;
 854    Proxy_Authenticate = 'Proxy-Authenticate:'i @set_proxy_authenticate challengeList;
 855    Retry_After = 'Retry-After:'i LWS* (HTTP_date %set_retry_after_http_date | delta_seconds >mark %set_retry_after_delta_seconds %save_ulong) LWS*;
 856    Server = 'Server:'i @set_server product_and_comment_list;
 857    WWW_Authenticate = 'WWW-Authenticate:'i @set_www_authenticate challengeList;
 858
 859    response_header = Accept_Ranges | ETag | Location | Proxy_Authenticate | Retry_After | Server | WWW_Authenticate;
 860
 861    Status_Code = DIGIT{3} > mark %parse_Status_Code;
 862    Reason_Phrase = (TEXT -- (CR | LF))* >mark %parse_Reason_Phrase;
 863    Status_Line = HTTP_Version SP Status_Code SP Reason_Phrase CRLF;
 864    Response = Status_Line (((general_header | response_header | entity_header) %clearmark2 | message_header) CRLF)* CRLF @done;
 865
 866    main := Response;
 867
 868    write data;
 869}%%
 870
 871ResponseParser::ResponseParser(Response& response, bool strict)
 872: Parser(strict),
 873  m_response(&response),
 874  m_ver(&response.status.ver),
 875  m_uri(&response.response.location),
 876  m_segments(&response.response.location.path.segments),
 877  m_authority(&response.response.location.authority),
 878  m_general(&response.general),
 879  m_entity(&response.entity)
 880{}
 881
 882void
 883ResponseParser::init()
 884{
 885    Parser::init();
 886    %% write init;
 887}
 888
 889bool
 890ResponseParser::final() const
 891{
 892    return cs >= http_response_parser_first_final;
 893}
 894
 895bool
 896ResponseParser::error() const
 897{
 898    return cs == http_response_parser_error;
 899}
 900
 901void
 902ResponseParser::exec()
 903{
 904#ifdef MSVC
 905#pragma warning(push)
 906#pragma warning(disable : 4244)
 907#endif
 908    %% write exec;
 909#ifdef MSVC
 910#pragma warning(pop)
 911#endif
 912}
 913
 914%%{
 915    machine http_trailer_parser;
 916    include http_parser;
 917
 918    trailer = ((entity_header %clearmark2 | message_header) CRLF)*;
 919
 920    main := trailer CRLF @done;
 921
 922    write data;
 923}%%
 924
 925TrailerParser::TrailerParser(EntityHeaders& entity, bool strict)
 926: Parser(strict), m_entity(&entity)
 927{}
 928
 929void
 930TrailerParser::init()
 931{
 932    Parser::init();
 933    %% write init;
 934}
 935
 936bool
 937TrailerParser::final() const
 938{
 939    return cs >= http_trailer_parser_first_final;
 940}
 941
 942bool
 943TrailerParser::error() const
 944{
 945    return cs == http_trailer_parser_error;
 946}
 947
 948void
 949TrailerParser::exec()
 950{
 951#ifdef MSVC
 952#pragma warning(push)
 953#pragma warning(disable : 4244)
 954#endif
 955    %% write exec;
 956#ifdef MSVC
 957#pragma warning(pop)
 958#endif
 959}
 960
 961
 962%%{
 963    machine http_list_parser;
 964    include http_parser;
 965
 966    main := list;
 967
 968    write data;
 969}%%
 970
 971ListParser::ListParser(StringSet& stringSet)
 972: m_set(&stringSet),
 973  m_list(NULL)
 974{}
 975
 976void
 977ListParser::init()
 978{
 979    RagelParser::init();
 980    %% write init;
 981}
 982
 983bool
 984ListParser::final() const
 985{
 986    return cs >= http_list_parser_first_final;
 987}
 988
 989bool
 990ListParser::error() const
 991{
 992    return cs == http_list_parser_error;
 993}
 994
 995void
 996ListParser::exec()
 997{
 998#ifdef MSVC
 999#pragma warning(push)
1000#pragma warning(disable : 4244)
1001#endif
1002    %% write exec;
1003#ifdef MSVC
1004#pragma warning(pop)
1005#endif
1006}
1007
1008}}