// Adapted from // "Illustrated Guide to HTTP" // by Paul S. Hethmon // Manning, 1997 // https://www.manning.com/books/illustrated-guide-to-http // Here is a very "high level" grammar for HTTP/1.1. generic-message = (Request-Line | Status-Line) *message-header CRLF [ message-body ] Request-Line = Method SP Request-URI SP HTTP-Version CRLF Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF message-header = field-name ":" [ field-value ] CRLF field-name = token field-value = *( field-content | LWS ) field-content = message-body = entity-body entity-body = *OCTET OCTET = // Here is a more detailed grammar for HTTP/1.1. HTTP-message = Request | Response Request = Request-Line *( general-header | request-header | entity-header ) CRLF ; sentinel value [ message-body ] Request-Line = Method SP Request-URI SP HTTP-Version CRLF Method = "GET" | "HEAD" | "POST" | "PUT" | "PATCH" | "DELETE" | "OPTIONS" | "TRACE" | "CONNECT" HTTP-Version = "HTTP" "/" 1*DIGIT "." 1*DIGIT Response = Status-Line *( general-header | response-header | entity-header ) CRLF ; sentinel value [ message-body ] message-body = entity-body | chunked-body ; entity-body encoded as per Transfer-Encoding entity-body = *OCTET OCTET = ; a byte chunked-body = *chunk "0" CRLF ; sentinel value footer CRLF ; sentinel value chunk = chunk-size [ chunk-ext ] CRLF chunk-data CRLF chunk-size = hex-no-zero *HEX chunk-ext = *( ";" chunk-ext-name [ "=" chunk-ext-value ] ) chunk-ext-name = token chunk-ext-val = token | quoted-string chunk-data = chunk-size(OCTET) footer = *entity-header Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF Status-Code = "100" ; Continue | "101" ; Switching Protocols | "200" ; OK | "201" ; Created | "202" ; Accepted | "203" ; Non-Authoritative Information | "204" ; No Content | "205" ; Reset Content | "206" ; Partial Content | "300" ; Multiple Choices | "301" ; Moved Permanently | "302" ; Moved Temporarily | "303" ; See Other | "304" ; Not Modified | "305" ; Use Proxy | "400" ; Bad Request | "401" ; Unauthorized | "402" ; Payment Required | "403" ; Forbidden | "404" ; Not Found | "405" ; Method Not Allowed | "406" ; Not Acceptable | "407" ; Proxy Authentication Required | "408" ; Request Time-out | "409" ; Conflict | "410" ; Gone | "411" ; Length Required | "412" ; Precondition Failed | "413" ; Request Entity Too Large | "414" ; Request-URI Too Large | "415" ; Unsupported Media Type | "500" ; Internal Server Error | "501" ; Not Implemented | "502" ; Bad Gateway | "503" ; Service Unavailable | "504" ; Gateway Time-out | "505" ; HTTP Version not supported | extension-code extension-code = 3DIGIT Reason-Phrase = * request-header = Accept | Accept-Charset | Accept-Encoding | Accept-Language | Authorization | Host | If-Modified-Since | If-Match | If-None-Match | If-Range | If-Unmodified-Since | Max-Forwards | Range | Referer | User-Agent response-header = Age | Location | Public | Retry-After | Server | Vary | Warning | WWW-Authenticate general-header = Cache-Control | Connection | Date | Pragma | Transfer-Encoding | Upgrade | Via entity-header = Allow | Content-Base | Content-Encoding | Content-Language | Content-Length | Content-Location | Content-MD5 | Content-Range | Content-Type | ETag | Expires | Last-Modified | extension-header extension-header = message-header Accept = "Accept" ":" #( media-range [ accept-params ] ) media-range = ( "*/*" | ( type "/" "*" ) | ( type "/" subtype ) ) *( ";" parameter ) type = token subtype = token parameter = attribute "=" value attribute = token value = token | quoted-string accept-params = ";" "q" "=" qvalue *( accept-extension ) qvalue = ( "0" [ "." 0*3DIGIT ] ) | ( "1" [ "." 0*3("0") ] ) Accept-Charset = "Accept-Charset" ":" 1#( charset [ ";" "q" "=" qvalue ] ) charset = token Accept-Encoding = "Accept-Encoding" ":" #( content-coding ) Accept-Language = "Accept-Language" ":" 1#( language-range [ ";" "q" "=" qvalue ] ) language-range = ( ( 1*8ALPHA *( "-" 1*8ALPHA ) ) | "*" ) Accept-Ranges = "Accept-Ranges" ":" acceptable-ranges acceptable-ranges = 1#range-unit | "none" range-unit = bytes-unit | other-range-unit bytes-unit = "bytes" other-range-unit = token Age = "Age" ":" age-value age-value = delta-seconds delta-seconds = 1*DIGIT Allow = "Allow" ":" 1#method Authorization = "Authorization" ":" credentials Connection-header = "Connection" ":" 1#(connection-token) connection-token = token Content-Base = "Content-Base" ":" absoluteURI Content-Encoding = "Content-Encoding" ":" 1#content-coding content-coding = token Content-Language = "Content-Language" ":" 1#language-tag language-tag = primary-tag *( "-" subtag ) primary-tag = 1*8ALPHA subtag = 1*8ALPHA Content-Length = "Content-Length" ":" 1*DIGIT Content-Location = "Content-Location" ":" ( absoluteURI | relativeURI ) Content-MD5 = "Content-MD5" ":" md5-digest md5-digest = Content-Range = "Content-Range" ":" content-range-spec content-range-spec = byte-content-range-spec byte-content-range-spec = bytes-unit SP first-byte-pos "-" last-byte-pos "/" entity-length first-byte-pos = 1*DIGIT last-byte-pos = 1*DIGIT entity-length = 1*DIGIT Content-Type = "Content-Type" ":" media-type media-type = type "/" subtype *( ";" parameter ) Date = "Date" ":" HTTP-date HTTP-date = rfc1123-date | rfc850-date | asctime-date rfc1123-date = wkday "," SP date1 SP time SP "GMT" rfc850-date = weekday "," SP date2 SP time SP "GMT" asctime-date = wkday SP date3 SP time SP 4DIGIT time = 2DIGIT ":" 2DIGIT ":" 2DIGIT ; 00:00:00 - 23:59:59 weekday = "Monday" | "Tuesday" | "Wednesday" | "Thursday" | "Friday" | "Saturday" | "Sunday" wkday = "Mon" | "Tue" | "Wed" | "Thu" | "Fri" | "Sat" | "Sun" month = "Jan" | "Feb" | "Mar" | "Apr" | "May" | "Jun" | "Jul" | "Aug" | "Sep" | "Oct" | "Nov" | "Dec" date = rfc1123-date date1 = 2DIGIT SP month SP 4DIGIT ; day month year (e.g., 02 Jun 1982) date2 = 2DIGIT "-" month "-" 2DIGIT ; day-month-year (e.g., 02-Jun-82) date3 = month SP ( 2DIGIT | ( SP 1DIGIT )) ; month day (e.g., Jun 2) ETag = "ETag" ":" entity-tag entity-tag = [ weak ] opaque-tag weak = "W/" opaque-tag = quoted-string Expires = "Expires" ":" HTTP-date From = "From" ":" mailbox Host = "Host" ":" host [ ":" port ] host = port = *DIGIT If-Match = "If-Match" ":" ( "*" | 1#entity-tag ) If-Modified-Since = "If-Modified-Since" ":" HTTP-date If-None-Match = "If-None-Match" ":" ( "*" | 1#entity-tag ) If-Range = "If-Range" ":" ( entity-tag | HTTP-date ) If-Unmodified-Since = "If-Unmodified-Since" ":" HTTP-date Last-Modified = "Last-Modified" ":" HTTP-date Location = "Location" ":" absoluteURI Max-Forwards = "Max-Forwards" ":" 1*DIGIT ; used by TRACE Pragma = "Pragma" ":" 1#pragma-directive pragma-directive = "no-cache" | extension-pragma Public = "Public" ":" 1#method Range = "Range" ":" ranges-specifier ranges-specifier = byte-ranges-specifier byte-ranges-specifier = bytes-unit "=" byte-range-set byte-range-set = 1#( byte-range-spec | suffix-byte-range-spec ) byte-range-spec = first-byte-pos "-" [last-byte-pos] suffix-byte-range-spec = "-" suffix-length suffix-length = 1*DIGIT Referer = "Referer" ":" ( absoluteURI | relativeURI ) Retry-After = "Retry-After" ":" ( HTTP-date | delta-seconds ) Server = "Server" ":" 1*( product | comment ) Transfer-Encoding = "Transfer-Encoding" ":" 1#transfer-coding transfer-coding = "chunked" | transfer-extension transfer-extension = token Upgrade = "Upgrade" ":" 1#product User-Agent = "User-Agent" ":" 1*( product | comment ) product = token ["/" product-version] product-version = token comment = "(" *( ctext | comment ) ")" Vary = "Vary" ":" ( "*" | 1#field-name ) field-name = token Via = "Via" ":" 1#( received-protocol received-by [ comment ] ) received-protocol = [ protocol-name "/" ] protocol-version protocol-name = token protocol-version = token received-by = ( host [ ":" port ] ) | pseudonym pseudonym = token ctext = Warning = "Warning" ":" 1#warning-value warning-value = warn-code SP warn-agent SP warn-text warn-code = 2DIGIT warn-agent = ( host [ ":" port ] ) | pseudonym ; the name or pseudonym of the server adding ; the Warning header, for use in debugging warn-text = quoted-string // Grammar for URIs and URLs. URI = ( absoluteURI | relativeURI ) [ "#" fragment ] absoluteURI = scheme ":" *( uchar | reserved ) scheme = 1*( ALPHA | DIGIT | "+" | "-" | "." ) relativeURI = abs_path | rel_path | net_path abs_path = "/" rel_path rel_path = [ path ] [ ";" params ] [ "?" query ] path = fsegment *( "/" segment ) fsegment = 1*pchar segment = *pchar params = param *( ";" param ) param = *( pchar | "/" ) query = *( uchar | reserved ) net_path = "//" net_loc [ abs_path ] net_loc = *( pchar | ";" | "?" ) fragment = *( uchar | reserved ) http_URL = "http:" "//" host [ ":" port ] [ abs_path ] host = port = *DIGIT // Here are the "lexical" definitions. CHAR = ALPHA = LOALPHA | UPALPHA LOALPHA = UPALPHA = DIGIT = TEXT = CTL = LWS = [CRLF] 1*( SP | HT ) ; linear white space qdtext = > quoted-string = ( <"> *(qdtext) <"> ) token = 1* tspecials = "(" | ")" | "<" | ">" | "@" | "," | ";" | ":" | "\" | <"> | "/" | "[" | "]" | "?" | "=" | "{" | "}" | SP | HT SP = HT = CRLF = CR LF CR = LF = quoted-pair = "\" CHAR reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" unreserved = ALPHA | DIGIT | extra | safe | national national = extra = "!" | "*" | "'" | "(" | ")" | "," safe = "$" | "-" | "_" | "." unsafe = CTL | SP | <"> | "#" | "%" | "<" | ">" pchar = uchar | ":" | "@" | "&" | "=" | "+" uchar = unreserved | escape escape = "%" HEX HEX HEX = "A" | "B" | "C" | "D" | "E" | "F" | "a" | "b" | "c" | "d" | "e" | "f" | DIGIT hex-no-zero = LHEX = "a" | "b" | "c" | "d" | "e" | "f" | DIGIT