
// 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 = <the OCTETs making up the field-value and
                 consisting of either *TEXT or combinations
                 of token, tspecials, and quoted-string>

message-body = entity-body

entity-body = *OCTET

OCTET = <any 8-bit sequence of data>





// 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 = <any 8-bit sequence of data>   ; 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 = *<TEXT, excluding CR, LF>


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 = <base64 of 128 bit MD5 digest as per RFC 1864>

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 = <A legal Internet host domain name
        or IP address (in dotted-decimal form),
        as defined by Section 2.1 of RFC 1123>

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 = <any TEXT excluding "(" and ")">

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 = <A legal Internet host domain name or
        IP address (in dotted-decimal form),
        as defined by Section 2.1 of RFC 1123>

port = *DIGIT






// Here are the "lexical" definitions.

CHAR = <any US-ASCII character (octets 0 - 127)>

ALPHA = LOALPHA | UPALPHA

LOALPHA = <any US-ASCII lowercase letter "a".."z">

UPALPHA = <any US-ASCII uppercase letter "A".."Z">

DIGIT = <any US-ASCII digit "0".."9">

TEXT = <any OCTET except CTLs, but including LWS>

CTL = <any US-ASCII control character
      (octets 0 - 31) and DEL (127)>

LWS = [CRLF] 1*( SP | HT )    ; linear white space

qdtext = <any TEXT except <">>

quoted-string = ( <"> *(qdtext) <"> )

token = 1*<any CHAR except CTLs or tspecials>

tspecials = "(" | ")" | "<" | ">" | "@"
            | "," | ";" | ":" | "\" | <">
            | "/" | "[" | "]" | "?" | "="
            | "{" | "}" | SP | HT

SP = <US-ASCII SP, space (32)>

HT = <US-ASCII HT, horizontal-tab (9)>

CRLF = CR LF

CR = <US-ASCII CR, carriage return (13)>

LF = <US-ASCII LF, linefeed (10)>

quoted-pair = "\" CHAR

reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+"

unreserved = ALPHA | DIGIT | extra | safe | national

national = <any OCTET excluding ALPHA, DIGIT,
            reserved, extra, safe, unsafe>

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 = <HEX excluding "0">

LHEX = "a" | "b" | "c" | "d" | "e" | "f" | DIGIT
