Skip to content

Instantly share code, notes, and snippets.

@jpf91
Created March 25, 2011 09:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jpf91/886612 to your computer and use it in GitHub Desktop.
Save jpf91/886612 to your computer and use it in GitHub Desktop.
final class HeaderCollection
{
private:
struct Entry
{
//Case preserved
string Key;
string Value;
}
Entry[string] storage;
void append(string key, string value)
{
auto lk = tolower(key);
if(lk !in storage)
{
Entry ent;
ent.Key = key;
ent.Value = value;
storage[lk] = ent;
}
else
{
auto ent = storage[lk];
ent.Value ~= "," ~ value;
}
}
//TODO: toCurl
public:
this(string[string] hash)
{
foreach(key; hash.keys)
{
auto lk = tolower(key);
assert(lk !in storage);
Entry ent;
ent.Key = key;
ent.Value = hash[key];
storage[lk] = ent;
}
}
this()
{
}
bool hasHeader(T)(T key)
if(is(T == string))
{
return (tolower(key) in storage) ? true : false;
}
bool hasHeader(T)()
if(isHeader!T)
{
return (tolower(T.Key) in storage) ? true : false;
}
void setHeader(T)(T key, T value)
if(is(T == string))
{
Entry ent;
ent.Key = key;
ent.Value = value;
storage[tolower(key)] = ent;
}
void setHeader(T)(T head)
if(isHeader!T && canFormatHeader!T)
{
Entry ent;
ent.Key = T.Key;
ent.Value = formatHeaderValue(head);
storage[tolower(T.Key)] = ent;
}
string getHeaderString(T)(T key)
if(is(T == string))
{
auto lk = tolower(key);
if(lk !in storage)
{
//Correct Exception type?
throw new Exception("");
}
return storage[lk].Value;
}
string getHeaderString(T)()
if(isHeader!T)
{
return getHeaderString(T.Key);
}
T getHeader(T)()
if(isHeader!T && canParseHeader!T)
{
return parseHeaderValue!T(getHeaderString(T.Key));
}
@property size_t length()
{
return storage.length;
}
T opCast(T)()
if(is(T == string[string]))
{
string[string] tmp;
foreach(val; storage.values)
{
tmp[val.Key] = val.Value;
}
return tmp;
}
override bool opEquals(Object obj)
{
auto other = cast(HeaderCollection)obj;
if(!other)
return false;
foreach(key; this.storage.keys)
{
if(key !in other.storage)
return false;
if(this.storage[key].Value != other.storage[key].Value)
return false;
}
return true;
}
string opIndex(string key)
{
return getHeaderString(key);
}
void opIndexAssign(string val, string key)
{
return setHeader(key, val);
}
//Doesn't work with current dmd
/*
bool opBinary(string type, T)()
if(type == "in" && isHeader(T))
{
return hasHeader!T;
}
bool opBinary(string type, T)(T val)
if(type == "in" && is(T == string))
{
return hasHeader!(val);
}*/
}
unittest
{
HeaderCollection col = new HeaderCollection(["Content-Length": "1234", "Content-Type" : "text/html"]);
string[string] test = cast(string[string])col;
assert(test == ["Content-Length": "1234", "Content-Type" : "text/html"]);
assert(test.length == 2);
//Not implemented in dmd?
//assert("Content-Length" in col);
//assert("content-length" in col);
assert(col["content-length"] == "1234");
col["content-length"] = "5678";
assert(col["content-length"] == "5678");
test = cast(string[string])col;
assert(test == ["content-length": "5678", "Content-Type" : "text/html"]);
HeaderCollection col2 = new HeaderCollection(["Content-Length": "5678", "Content-Type" : "text/html"]);
assert(col2 == col);
string length = col2.getHeaderString!ContentLengthHeader;
assert(length == "5678");
length = col2.getHeaderString("ContEnt-LEngTH");
assert(length == "5678");
auto plength = col2.getHeader!ContentLengthHeader;
assert(plength.Length == 5678);
assert(col2.hasHeader("ContEnt-LEngTH"));
assert(col2.hasHeader!ContentLengthHeader);
IfModifiedSinceHeader head;
head.Date = SysTime(DateTime(2011, 3, 24, 19, 21, 0), UTC());
col2.setHeader(head);
assert(col2.hasHeader("If-Modified-Since"));
assert(col2["If-Modified-Since"] == "Thu, 24 Mar 2011 19:21:00 GMT");
col2.setHeader("If-Modified-Since", "Thu, 17 Mar 2011 19:21:00 GMT");
assert(col2.hasHeader("If-Modified-Since"));
assert(col2["If-Modified-Since"] == "Thu, 17 Mar 2011 19:21:00 GMT");
}
/**
* This module contains parsers, formatters and data types for the _http
* protocol.
*
* Headers from rfc2616 which are currently not implemented:
* $(UL
* $(LI $(LINK2 http://tools.ietf.org/html/rfc2616#section-14.8, AuthorizationHeader))
* $(LI $(LINK2 http://tools.ietf.org/html/rfc2616#section-14.9, CacheControlHeader))
* $(LI $(LINK2 http://tools.ietf.org/html/rfc2616#section-14.33, ProxyAuthenticateHeader))
* $(LI $(LINK2 http://tools.ietf.org/html/rfc2616#section-14.34, ProxyAuthorizationHeader))
* $(LI $(LINK2 http://tools.ietf.org/html/rfc2616#section-14.38, ServerHeader))
* $(LI $(LINK2 http://tools.ietf.org/html/rfc2616#section-14.43, UserAgentHeader))
* $(LI $(LINK2 http://tools.ietf.org/html/rfc2616#section-14.45, ViaHeader))
* $(LI $(LINK2 http://tools.ietf.org/html/rfc2616#section-14.46, WarningHeader))
* $(LI $(LINK2 http://tools.ietf.org/html/rfc2616#section-14.47, WWWAuthenticateHeader))
* )
* Synopsis:
* ---------------------------------------
* //Formatting a header
* IfModifiedSinceHeader head;
* head.Date = SysTime(DateTime(2011, 3, 24, 19, 21, 0), UTC());
* string headerString = formatHeaderValue(head);
* assert(headerString == "Thu, 24 Mar 2011 19:21:00 GMT");
* headerString = formatHeader(head);
* assert(headerString == "If-Modified-Since: Thu, 24 Mar 2011 19:21:00 GMT");
*
* //Parsing a response
* auto resp = parseResponse("HTTP/1.2 200 OK\r\n");
* assert(resp.Type == ResponseType.StatusLine);
* assert(resp.Line == "HTTP/1.2 200 OK\r\n");
*
* auto sline = StatusLine.parse(resp.Line);
* assert(sline.Major == 1);
* assert(sline.Minor == 2);
* assert(sline.StatusCode == 200);
* assert(sline.Reason == "OK");
*
* resp = parseResponse("Content-Length: 348\r\n");
* assert(resp.Type == ResponseType.Header);
* assert(resp.Key == "Content-Length");
* auto header = parseHeaderValue!(ContentLengthHeader)(resp.Value);
* assert (header.Length == 348);
* ---------------------------------------
* Standards: Conforms to $(LINK2 http://tools.ietf.org/html/rfc2616,rfc2616)
* License: <a href="http://www.boost.org/LICENSE_1_0.txt">Boost License 1.0</a>
* Authors: Johannes Pfau
* Copyright: Copyright Johannes Pfau 2011.
*/
/* Copyright Johannes Pfau 2011.
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
*/
module std.protocol.http;
#line 53 "http.d.rl"
import std.array, std.base64, std.conv, std.datetime, std.format;
import std.range, std.string, std.traits;
/*
* Helper data types follow.
* Names according to rfc2616
*/
/**
* Data type holding a http
* $(LINK2 http://tools.ietf.org/html/rfc2616#section-6, response message)
*/
struct Response
{
///The response line type
ResponseType Type;
union
{
///The raw value for unknown responses and StatusLine responses
string Line;
///The key in a header response
string Key;
}
///The value in a header response
string Value;
}
/**
* $(LINK2 http://tools.ietf.org/html/rfc2616#section-6,
* HTTP response message type)
*/
enum ResponseType : ushort
{
/**
* $(LINK2 http://tools.ietf.org/html/rfc2616#section-6.1, status line)
*/
StatusLine,
/**
* $(LINK2 http://tools.ietf.org/html/rfc2616#section-6.2, response-header),
* $(LINK2 http://tools.ietf.org/html/rfc2616#section-7.1, entity-header) or
* $(LINK2 http://tools.ietf.org/html/rfc2616#section-4.5, general-header)
*/
Header,
/**
* Empty line [CRLF]
*/
Empty,
/**
* Unknown line
*/
Other
}
/**
* Data type holding a http
* $(LINK2 http://tools.ietf.org/html/rfc2616#section-6.1, status line)
*/
struct StatusLine
{
///HTTP-Version
uint Major, Minor;
///
uint StatusCode;
///
string Reason;
/**
* Parses a string
*
* Examples:
* ---------------------------------------
* auto resp = parseResponse("HTTP/1.2 200 OK\r\n");
* assert(resp.Type == ResponseType.StatusLine);
* assert(resp.Line == "HTTP/1.2 200 OK\r\n");
* auto sline = StatusLine.parse(resp.Line);
*
* assert(sline.Major == 1);
* assert(sline.Minor == 2);
* assert(sline.StatusCode == 200);
* assert(sline.Reason == "OK");
* ---------------------------------------
*/
static StatusLine parse(string line)
{
StatusLine sline;
const(char)* numstart;
mixin(initParser!());
#line 147 "http.d"
{
cs = parseStatusLine_start;
}
#line 152 "http.d"
{
if ( p == pe )
goto _test_eof;
switch ( cs )
{
case 1:
if ( (*p) == 72u )
goto st2;
goto st0;
st0:
cs = 0;
goto _out;
st2:
if ( ++p == pe )
goto _test_eof2;
case 2:
if ( (*p) == 84u )
goto st3;
goto st0;
st3:
if ( ++p == pe )
goto _test_eof3;
case 3:
if ( (*p) == 84u )
goto st4;
goto st0;
st4:
if ( ++p == pe )
goto _test_eof4;
case 4:
if ( (*p) == 80u )
goto st5;
goto st0;
st5:
if ( ++p == pe )
goto _test_eof5;
case 5:
if ( (*p) == 47u )
goto st6;
goto st0;
st6:
if ( ++p == pe )
goto _test_eof6;
case 6:
if ( 48u <= (*p) && (*p) <= 57u )
goto tr6;
goto st0;
tr6:
#line 3116 "http.d.rl"
{{
numstart = p;
}}
goto st7;
st7:
if ( ++p == pe )
goto _test_eof7;
case 7:
#line 210 "http.d"
if ( (*p) == 46u )
goto tr7;
if ( 48u <= (*p) && (*p) <= 57u )
goto st7;
goto st0;
tr7:
#line 3108 "http.d.rl"
{{
sline.Major = std.conv.parse!(uint)(line[(numstart - line.ptr)
.. (p - line.ptr)]);
}}
goto st8;
st8:
if ( ++p == pe )
goto _test_eof8;
case 8:
#line 227 "http.d"
if ( 48u <= (*p) && (*p) <= 57u )
goto tr9;
goto st0;
tr9:
#line 3116 "http.d.rl"
{{
numstart = p;
}}
goto st9;
st9:
if ( ++p == pe )
goto _test_eof9;
case 9:
#line 241 "http.d"
if ( (*p) == 32u )
goto tr10;
if ( 48u <= (*p) && (*p) <= 57u )
goto st9;
goto st0;
tr10:
#line 3112 "http.d.rl"
{{
sline.Minor = std.conv.parse!(uint)(line[(numstart - line.ptr)
.. (p - line.ptr)]);
}}
goto st10;
st10:
if ( ++p == pe )
goto _test_eof10;
case 10:
#line 258 "http.d"
if ( 48u <= (*p) && (*p) <= 57u )
goto st11;
goto st0;
st11:
if ( ++p == pe )
goto _test_eof11;
case 11:
if ( 48u <= (*p) && (*p) <= 57u )
goto st12;
goto st0;
st12:
if ( ++p == pe )
goto _test_eof12;
case 12:
if ( 48u <= (*p) && (*p) <= 57u )
goto st13;
goto st0;
st13:
if ( ++p == pe )
goto _test_eof13;
case 13:
if ( (*p) == 32u )
goto tr15;
goto st0;
tr15:
#line 3119 "http.d.rl"
{{
sline.StatusCode = std.conv.parse!(uint)(line[(p - line.ptr -3)
.. (p - line.ptr)]);
}}
goto st14;
st14:
if ( ++p == pe )
goto _test_eof14;
case 14:
#line 294 "http.d"
switch( (*p) ) {
case 13u: goto tr17;
case 127u: goto st0;
default: break;
}
if ( (*p) > 8u ) {
if ( 10u <= (*p) && (*p) <= 31u )
goto st0;
} else
goto st0;
goto tr16;
tr16:
#line 3116 "http.d.rl"
{{
numstart = p;
}}
goto st15;
st15:
if ( ++p == pe )
goto _test_eof15;
case 15:
#line 316 "http.d"
switch( (*p) ) {
case 13u: goto tr19;
case 127u: goto st0;
default: break;
}
if ( (*p) > 8u ) {
if ( 10u <= (*p) && (*p) <= 31u )
goto st0;
} else
goto st0;
goto st15;
tr17:
#line 3116 "http.d.rl"
{{
numstart = p;
}}
#line 3123 "http.d.rl"
{{
sline.Reason = line[(numstart - line.ptr)
.. (p - line.ptr)];
}}
goto st16;
tr19:
#line 3123 "http.d.rl"
{{
sline.Reason = line[(numstart - line.ptr)
.. (p - line.ptr)];
}}
goto st16;
st16:
if ( ++p == pe )
goto _test_eof16;
case 16:
#line 350 "http.d"
if ( (*p) == 10u )
goto st17;
goto st0;
st17:
if ( ++p == pe )
goto _test_eof17;
case 17:
switch( (*p) ) {
case 9u: goto st15;
case 32u: goto st15;
default: break;
}
goto st0;
default: break;
}
_test_eof2: cs = 2; goto _test_eof;
_test_eof3: cs = 3; goto _test_eof;
_test_eof4: cs = 4; goto _test_eof;
_test_eof5: cs = 5; goto _test_eof;
_test_eof6: cs = 6; goto _test_eof;
_test_eof7: cs = 7; goto _test_eof;
_test_eof8: cs = 8; goto _test_eof;
_test_eof9: cs = 9; goto _test_eof;
_test_eof10: cs = 10; goto _test_eof;
_test_eof11: cs = 11; goto _test_eof;
_test_eof12: cs = 12; goto _test_eof;
_test_eof13: cs = 13; goto _test_eof;
_test_eof14: cs = 14; goto _test_eof;
_test_eof15: cs = 15; goto _test_eof;
_test_eof16: cs = 16; goto _test_eof;
_test_eof17: cs = 17; goto _test_eof;
_test_eof: {}
_out: {}
}
#line 147 "http.d.rl"
mixin(finishParser!"parseStatusLine");
return sline;
}
/**
* Formats the StatusLine and writes it to the OutputRange writer.
* Note: There is no newline appended at the end!
*
* Examples:
* ---------------------------------------
* StatusLine sline;
* sline.Major = 1;
* sline.Minor = 1;
* sline.StatusCode = 200;
* sline.Reason = "OK";
* assert(sline.formatString() == "HTTP/1.1 200 OK");
* ---------------------------------------
*
* ---------------------------------------
* StatusLine sline;
* sline.Major = 1;
* sline.Minor = 1;
* sline.StatusCode = 200;
* sline.Reason = "OK";
* auto writer = appender!string();
* this.format(writer);
* assert(writer.data == "HTTP/1.1 200 OK");
* ---------------------------------------
*/
void format(T)(T writer) if (isOutputRange!(T, string))
{
assert(this.StatusCode < 1000, "StatusCode must be max 3 digits");
assert(isText(this.Reason), "Reason must be TEXT according to rfc2616");
assert(indexOf(this.Reason, '\r') == -1, "Reason must not contain '\\r'");
assert(indexOf(this.Reason, '\n') == -1, "Reason must not contain '\\n'");
formattedWrite(writer, "HTTP/%s.%s %03s %s", this.Major, this.Minor,
this.StatusCode, this.Reason);
}
///ditto
string formatString()
{
auto writer = appender!string();
this.format(writer);
return writer.data;
}
}
unittest
{
auto resp = parseResponse("HTTP/1.2 200 OK\r\n");
assert(resp.Type == ResponseType.StatusLine);
assert(resp.Line == "HTTP/1.2 200 OK\r\n");
auto sline = StatusLine.parse(resp.Line);
assert(sline.Major == 1);
assert(sline.Minor == 2);
assert(sline.StatusCode == 200);
assert(sline.Reason == "OK");
}
unittest
{
StatusLine sline;
sline.Major = 1;
sline.Minor = 1;
sline.StatusCode = 200;
sline.Reason = "OK";
assert(sline.formatString() == "HTTP/1.1 200 OK");
sline.Reason = "";
assert(sline.formatString() == "HTTP/1.1 200 ");
}
///$(LINK2 http://tools.ietf.org/html/rfc2616#section-14.1, media-range)
struct MediaRange
{
///
string Type;
///
string SubType;
///
string[string] Parameters;
///
float AcceptParam = -1f;
///
string[string] AcceptExtension;
}
///$(LINK2 http://tools.ietf.org/html/rfc2616#section-14.2, charset / q combination)
struct Charset
{
///
string Name;
///
float Q = -1f;
}
///$(LINK2 http://tools.ietf.org/html/rfc2616#section-14.2, content-coding / q combination)
struct ContentCoding
{
///
enum : ushort
{
///
Compress,
///
Gzip,
///
Deflate,
///
Identity,
///
Other
}
///
typeof(ContentCoding.Other) Type;
///
float Q = -1f;
///If Type is ContentCoding.Other this field contains the raw value
string Raw;
}
///$(LINK2 http://tools.ietf.org/html/rfc2616#section-14.4, language-range / q combination)
struct LanguageRange
{
///
string Main;
///
string Sub;
///
float Q = -1f;
}
///$(LINK2 http://tools.ietf.org/html/rfc2616#section-3.12, range-unit)
struct RangeUnit
{
///
enum : ushort
{
///
Bytes,
///
Other
}
///
typeof(RangeUnit.Other) Type;
///If Type is RangeUnit.Other this field contains the raw value
string Raw;
}
///$(LINK2 http://tools.ietf.org/html/rfc2616#section-5.1.1, http methods)
struct Method
{
enum : ushort
{
///
OPTIONS,
///
GET,
///
HEAD,
///
POST,
///
PUT,
///
DELETE,
///
TRACE,
///
CONNECT,
///
Other
}
///
typeof(Method.CONNECT) Type;
///
public this(typeof(Method.CONNECT) method)
{
this.Type = method;
}
///If Type is Method.Other this field contains the raw value
string Raw;
}
///$(LINK2 http://tools.ietf.org/html/rfc2616#section-14.10, connection-token)
struct ConnectionToken
{
enum : ushort
{
///
Close,
///
Other
}
///
typeof(ConnectionToken.Other) Type;
///If Type is ConnectionToken.Other this field contains the raw value
string Raw;
}
///$(LINK2 http://tools.ietf.org/html/rfc2616#section-14.4, language-range / q combination)
struct LanguageTag
{
///
string Main;
///
string Sub;
}
///$(LINK2 http://tools.ietf.org/html/rfc2616#section-14.16, positions used with content-range-spec)
struct BytePos
{
///
bool Unknown = false;
///
ulong Value;
}
///$(LINK2 http://tools.ietf.org/html/rfc2616#section-14.16, content-range-spec)
struct ContentRangeSpec
{
///
BytePos From;
///
BytePos To;
///
BytePos Length;
}
///$(LINK2 http://tools.ietf.org/html/rfc2616#section-3.7, media-type)
struct MediaType
{
///
string Type;
///
string SubType;
///
string[string] Parameters;
}
///$(LINK2 http://tools.ietf.org/html/rfc2616#section-3.11, entity-tag)
struct EntityTag
{
///
bool Weak = false;
///
string Value;
}
///$(LINK2 http://tools.ietf.org/html/rfc2616#section-14.20, expectation)
struct Expectation
{
enum : ushort
{
Other,
Continue
}
typeof(Expectation.Continue) Type;
string ExtensionKey;
string ExtensionValue;
string[string] ExtensionParameters;
}
///$(LINK2 http://tools.ietf.org/html/rfc2616#section-14.32, pragma-directive)
struct PragmaDirective
{
enum : ushort
{
///
NoCache,
///
Extension
}
///
typeof(PragmaDirective.Extension) Type;
///
string ExtensionKey;
///
string ExtensionValue;
}
///$(LINK2 http://tools.ietf.org/html/rfc2616#section-14.35.1, byte-range-spec)
struct ByteRangeSpec
{
union
{
///
long First;
///
long Suffix;
}
enum : ushort
{
SuffixSpec,
ByteSpec
}
typeof(ByteRangeSpec.ByteSpec) Type;
///
BytePos Last;
}
///$(LINK2 http://tools.ietf.org/html/rfc2616#section-3.8, product)
struct Product
{
///
string Name;
///
string Version;
}
///$(LINK2 http://tools.ietf.org/html/rfc2616#section-14.39, t-codings)
struct TCoding
{
///
bool Trailers = false;
///
string[string] Parameters, AcceptParameters;
///
string Extension;
///
float Q = -1f;
}
///$(LINK2 http://tools.ietf.org/html/rfc2616#section-3.6, transfer-coding)
struct TransferCoding
{
enum : ushort
{
///
Chunked,
///
Custom
}
///
typeof(TransferCoding.Custom) Type;
///
string Raw;
///
string[string] Parameters;
}
/*
* Header types follow
*/
/**
* Data type for the $(LINK2 http://tools.ietf.org/html/rfc2616#section-14.1,
* "Accept" header)
*
* Type:
* request-header
*/
struct AcceptHeader
{
public:
///
MediaRange[] Media;
static string Key = "Accept";
void format(T)(T writer) if (isOutputRange!(T, string))
{
foreach(i, range; Media)
{
debug
{
if(range.SubType != "*")
assert(range.Type != "*");
}
assert(isToken(range.Type));
assert(isToken(range.SubType));
formattedWrite(writer, "%s/%s", range.Type, range.SubType);
foreach(key, value; range.Parameters)
{
writeParameter(writer, key, value);
}
if(range.AcceptParam >= 0)
{
assert(range.AcceptParam <= 1);
formattedWrite(writer, ";q=%.3f", range.AcceptParam);
foreach(key, value; range.AcceptExtension)
{
writeParameter(writer, key, value);
}
}
if(i != Media.length - 1)
writer.put(", ");
}
}
}
/**
* Data type for the $(LINK2 http://tools.ietf.org/html/rfc2616#section-14.2,
* "Accept-Charset" header)
*
* Type:
* request-header
*/
struct AcceptCharsetHeader
{
public:
///
Charset[] Charsets;
static string Key = "Accept-Charset";
void format(T)(T writer) if (isOutputRange!(T, string))
{
foreach(i, charset; Charsets)
{
assert(isToken(charset.Name));
writer.put(charset.Name);
if(charset.Q >= 0)
{
assert(charset.Q <= 1);
formattedWrite(writer, ";q=%.3f", charset.Q);
}
if(i != Charsets.length - 1)
writer.put(", ");
}
}
}
/**
* Data type for the $(LINK2 http://tools.ietf.org/html/rfc2616#section-14.3,
* "Accept-Encoding" header)
*
* Type:
* request-header
*/
struct AcceptEncodingHeader
{
public:
///
ContentCoding[] Codings;
static string Key = "Accept-Encoding";
void format(T)(T writer) if (isOutputRange!(T, string))
{
foreach(i, entry; Codings)
{
final switch(entry.Type)
{
case ContentCodingType.Compress:
writer.put("compress");
break;
case ContentCodingType.Deflate:
writer.put("deflate");
break;
case ContentCodingType.Gzip:
writer.put("gzip");
break;
case ContentCodingType.Identity:
writer.put("identity");
break;
case ContentCodingType.Other:
assert(isToken(entry.Raw));
writer.put(entry.Coding.Raw);
break;
}
if(entry.Q >= 0)
{
assert(entry.Q <= 1);
formattedWrite(writer, ";q=%.3f", entry.Q);
}
if(i != Codings.length - 1)
writer.put(", ");
}
}
}
/**
* Data type for the $(LINK2 http://tools.ietf.org/html/rfc2616#section-14.4,
* "Accept-Language" header)
*
* Type:
* request-header
*/
struct AcceptLanguageHeader
{
public:
///
LanguageRange[] Languages;
static string Key = "Accept-Language";
void format(T)(T writer) if (isOutputRange!(T, string))
{
foreach(i, entry; Languages)
{
assert(entry.Main.length <= 8);
assert(entry.Sub.length <= 8);
debug
{
if(entry.Tag.Main != "*")
{
assert(isalpha(entry.Sub));
assert(isalpha(entry.Main));
}
else
{
assert(entry.Tag.Sub == "");
}
}
writer.put(entry.Main);
if(entry.Sub != "")
{
writer.put("-");
writer.put(entry.Sub);
}
if(entry.Q >= 0)
{
assert(entry.Q <= 1);
formattedWrite(writer, ";q=%.3f", entry.Q);
}
if(i != Languages.length - 1)
writer.put(", ");
}
}
}
/**
* Data type for the $(LINK2 http://tools.ietf.org/html/rfc2616#section-14.5,
* "Accept-Ranges" header)
*
* Type:
* response-header
*/
struct AcceptRangesHeader
{
public:
///
bool None = false;
///
RangeUnit[] AcceptableRanges;
static string Key = "Accept-Ranges";
void format(T)(T writer) if (isOutputRange!(T, string))
{
if(this.None)
{
writer.put("none");
return;
}
foreach(i, entry; AcceptableRanges)
{
if(entry.Type == RangeUnit.Bytes)
writer.put("bytes");
else
{
assert(isToken(entry.Raw));
writer.put(entry.Raw);
}
if(i != AcceptableRanges.length - 1)
writer.put(", ");
}
}
static AcceptRangesHeader parse(string line)
{
string[] list = parseCommaList(line);
AcceptRangesHeader hdr;
if(list.length == 1 && list[0] == "none")
{
hdr.None = true;
return hdr;
}
foreach(entry; list)
{
RangeUnit unit;
if(entry == "bytes")
unit.Type = RangeUnit.Bytes;
else
{
unit.Type = RangeUnit.Other;
unit.Raw = entry;
}
hdr.AcceptableRanges ~= unit;
}
return hdr;
}
}
/**
* Data type for the $(LINK2 http://tools.ietf.org/html/rfc2616#section-14.6,
* "Age" header)
*
* Type:
* response-header
*/
struct AgeHeader
{
public:
///delta in seconds
ulong Age;
///
public this(ulong age)
{
this.Age = age;
}
static string Key = "Age";
void format(T)(T writer) if (isOutputRange!(T, string))
{
write(writer, Age);
}
static AgeHeader parse(string line)
{
return AgeHeader(std.conv.parse!ulong(line));
}
}
/**
* Data type for the $(LINK2 http://tools.ietf.org/html/rfc2616#section-14.7,
* "Allow" header)
*
* Type:
* entity-header
*/
struct AllowHeader
{
public:
///
Method[] Methods;
static string Key = "Allow";
void format(T)(T writer) if (isOutputRange!(T, string))
{
foreach(i, entry; Methods)
{
final switch(entry.Type)
{
case Method.OPTIONS:
writer.put("OPTIONS");
break;
case Method.GET:
writer.put("GET");
break;
case Method.HEAD:
writer.put("HEAD");
break;
case Method.POST:
writer.put("POST");
break;
case Method.PUT:
writer.put("PUT");
break;
case Method.DELETE:
writer.put("DELETE");
break;
case Method.TRACE:
writer.put("TRACE");
break;
case Method.CONNECT:
writer.put("CONNECT");
break;
case Method.Other:
assert(entry.Raw != "");
assert(isToken(entry.Raw));
writer.put(entry.Raw);
break;
}
if(i != Methods.length - 1)
writer.put(", ");
}
}
static AllowHeader parse(string line)
{
string[] list = parseCommaList(line);
AllowHeader allow;
foreach(entry; list)
{
switch(entry)
{
case "OPTIONS":
allow.Methods ~= Method(Method.OPTIONS);
break;
case "GET":
allow.Methods ~= Method(Method.GET);
break;
case "HEAD":
allow.Methods ~= Method(Method.HEAD);
break;
case "POST":
allow.Methods ~= Method(Method.POST);
break;
case "PUT":
allow.Methods ~= Method(Method.PUT);
break;
case "DELETE":
allow.Methods ~= Method(Method.DELETE);
break;
case "TRACE":
allow.Methods ~= Method(Method.TRACE);
break;
case "CONNECT":
allow.Methods ~= Method(Method.CONNECT);
break;
default:
auto m = Method(Method.Other);
m.Raw = entry;
allow.Methods ~= m;
break;
}
}
return allow;
}
}
/**
* Data type for the $(LINK2 http://tools.ietf.org/html/rfc2616#section-14.10,
* "Connection" header)
*
* Type:
* general-header
*/
struct ConnectionHeader
{
public:
///
ConnectionToken[] Options;
static string Key = "Connection";
void format(T)(T writer) if (isOutputRange!(T, string))
{
foreach(i, entry; Options)
{
if(entry.Type == ConnectionToken.Close)
{
writer.put("close");
assert(entry.Raw == "");
}
else
{
assert(entry.Raw != "");
assert(isToken(entry.Raw));
writer.put(entry.Raw);
}
if(i != Options.length - 1)
writer.put(", ");
}
}
static ConnectionHeader parse(string line)
{
ConnectionHeader con;
string[] list = parseCommaList(line);
foreach(entry; list)
{
switch(tolower(entry))
{
case "close":
ConnectionToken token;
token.Type = ConnectionToken.Close;
con.Options ~= token;
break;
default:
ConnectionToken token;
token.Type = ConnectionToken.Other;
token.Raw = entry;
con.Options ~= token;
break;
}
}
return con;
}
}
/**
* Data type for the $(LINK2 http://tools.ietf.org/html/rfc2616#section-14.11,
* "Content-Encoding" header)
*
* Type:
* entity-header
*/
struct ContentEncodingHeader
{
public:
///
ContentCoding[] Codings;
static string Key = "Content-Encoding";
void format(T)(T writer) if (isOutputRange!(T, string))
{
foreach(i, entry; Codings)
{
final switch(entry.Type)
{
case ContentCoding.Compress:
assert(entry.Raw == "");
writer.put("compress");
break;
case ContentCoding.Gzip:
assert(entry.Raw == "");
writer.put("gzip");
break;
case ContentCoding.Deflate:
assert(entry.Raw == "");
writer.put("deflate");
break;
case ContentCoding.Identity:
assert(entry.Raw == "");
writer.put("identity");
break;
case ContentCoding.Other:
assert(entry.Raw != "");
assert(isToken(entry.Raw));
writer.put(entry.Raw);
break;
}
if(i != Codings.length - 1)
writer.put(", ");
}
}
static ContentEncodingHeader parse(string line)
{
ContentEncodingHeader enc;
string[] list = parseCommaList(line);
foreach(entry; list)
{
ContentCoding coding;
switch(tolower(entry))
{
case "compress":
coding.Type = ContentCoding.Compress;
enc.Codings ~= coding;
break;
case "deflate":
coding.Type = ContentCoding.Deflate;
enc.Codings ~= coding;
break;
case "gzip":
coding.Type = ContentCoding.Gzip;
enc.Codings ~= coding;
break;
case "identity":
coding.Type = ContentCoding.Identity;
enc.Codings ~= coding;
break;
default:
coding.Type = ContentCoding.Other;
coding.Raw = entry;
enc.Codings ~= coding;
break;
}
}
return enc;
}
}
/**
* Data type for the $(LINK2 http://tools.ietf.org/html/rfc2616#section-14.12,
* "Content-Language" header)
*
* Type:
* entity-header
*/
struct ContentLanguageHeader
{
public:
///
LanguageTag[] Tags;
static string Key = "Content-Language";
void format(T)(T writer) if (isOutputRange!(T, string))
{
foreach(i, entry; Tags)
{
assert(isToken(entry.Main));
writer.put(entry.Main);
if(entry.Sub != "")
{
assert(isToken(entry.Sub));
writer.put("-");
writer.put(entry.Sub);
}
if(i != Tags.length - 1)
writer.put(", ");
}
}
static ContentLanguageHeader parse(string line)
{
ContentLanguageHeader lang;
string[] list = parseCommaList(line);
foreach(entry; list)
{
LanguageTag tag;
size_t i;
if((i = indexOf(entry, '-')) != -1)
{
tag.Main = strip(entry[0 .. i]);
tag.Sub = strip(entry[i+1 .. $]);
}
else
{
tag.Main = strip(entry);
}
lang.Tags ~= tag;
}
return lang;
}
}
/**
* Data type for the $(LINK2 http://tools.ietf.org/html/rfc2616#section-14.13,
* "Content-Length" header)
*
* Type:
* entity-header
*/
struct ContentLengthHeader
{
public:
///
ulong Length;
static string Key = "Content-Length";
void format(T)(T writer) if (isOutputRange!(T, string))
{
write(writer, Length);
}
static ContentLengthHeader parse(string line)
{
ContentLengthHeader len;
len.Length = std.conv.parse!ulong(line);
return len;
}
}
/**
* Data type for the $(LINK2 http://tools.ietf.org/html/rfc2616#section-14.14,
* "Content-Location" header)
*
* Type:
* entity-header
*/
struct ContentLocationHeader
{
public:
///
string Location;
static string Key = "Content-Location";
void format(T)(T writer) if (isOutputRange!(T, string))
{
//TODO: verify URI in debug mode
writer.put(Location);
}
static ContentLocationHeader parse(string line)
{
ContentLocationHeader loc;
loc.Location = line;
return loc;
}
}
/**
* Data type for the $(LINK2 http://tools.ietf.org/html/rfc2616#section-14.15,
* "Content-MD5" header)
*
* Type:
* entity-header
*/
struct ContentMD5Header
{
public:
///
ubyte[16] MD5;
static string Key = "Content-MD5";
void format(T)(T writer) if (isOutputRange!(T, string))
{
char[24] buf;
writer.put(encode(this.MD5, buf));
}
static ContentMD5Header parse(string line)
{
ContentMD5Header cmd5;
//strip trailing '=' for length calculation
size_t nlength;
for(nlength = line.length - 1; nlength >= 0; nlength--)
{
if(line[nlength] != '=')
break;
}
if(Base64.decodeLength(nlength) > 16)
return cmd5;
ubyte[24] buf;
Base64.decode!(string, ubyte[])(line, buf);
cmd5.MD5 = buf[0 .. 16];
return cmd5;
}
}
/**
* Data type for the $(LINK2 http://tools.ietf.org/html/rfc2616#section-14.16,
* "Content-Range" header)
*
* Type:
* entity-header
*/
struct ContentRangeHeader
{
public:
///
ContentRangeSpec Range;
static string Key = "Content-Range";
void format(T)(T writer) if (isOutputRange!(T, string))
{
writer.put("bytes ");
if(Range.From.Unknown || Range.To.Unknown)
{
writer.put("*");
}
else
{
formattedWrite(writer, "%s", Range.From.Value);
writer.put("-");
formattedWrite(writer, "%s", Range.To.Value);
}
writer.put("/");
if(Range.Length.Unknown)
{
writer.put("*");
}
else
{
formattedWrite(writer, "%s", Range.Length.Value);
}
}
static ContentRangeHeader parse(string line)
{
ContentRangeHeader head;
const(char)* start;
mixin(initParser!());
#line 1422 "http.d"
{
cs = parseContentRangeHeader_start;
}
#line 1427 "http.d"
{
if ( p == pe )
goto _test_eof;
switch ( cs )
{
case 1:
switch( (*p) ) {
case 66u: goto st2;
case 98u: goto st2;
default: break;
}
goto st0;
st0:
cs = 0;
goto _out;
st2:
if ( ++p == pe )
goto _test_eof2;
case 2:
switch( (*p) ) {
case 89u: goto st3;
case 121u: goto st3;
default: break;
}
goto st0;
st3:
if ( ++p == pe )
goto _test_eof3;
case 3:
switch( (*p) ) {
case 84u: goto st4;
case 116u: goto st4;
default: break;
}
goto st0;
st4:
if ( ++p == pe )
goto _test_eof4;
case 4:
switch( (*p) ) {
case 69u: goto st5;
case 101u: goto st5;
default: break;
}
goto st0;
st5:
if ( ++p == pe )
goto _test_eof5;
case 5:
switch( (*p) ) {
case 83u: goto st6;
case 115u: goto st6;
default: break;
}
goto st0;
st6:
if ( ++p == pe )
goto _test_eof6;
case 6:
if ( (*p) == 32u )
goto st7;
goto st0;
st7:
if ( ++p == pe )
goto _test_eof7;
case 7:
if ( (*p) == 42u )
goto st8;
if ( 48u <= (*p) && (*p) <= 57u )
goto tr8;
goto st0;
st8:
if ( ++p == pe )
goto _test_eof8;
case 8:
if ( (*p) == 47u )
goto tr9;
goto st0;
tr9:
#line 3204 "http.d.rl"
{{
head.Range.To.Unknown = true;
head.Range.From.Unknown = true;
}}
goto st9;
tr15:
#line 3190 "http.d.rl"
{{
head.Range.To.Value = std.conv.parse!ulong(line[(start - line.ptr)
.. (p - line.ptr)]);
}}
goto st9;
st9:
if ( ++p == pe )
goto _test_eof9;
case 9:
#line 1524 "http.d"
if ( (*p) == 42u )
goto st13;
if ( 48u <= (*p) && (*p) <= 57u )
goto tr11;
goto st0;
st13:
if ( ++p == pe )
goto _test_eof13;
case 13:
goto st0;
tr11:
#line 3181 "http.d.rl"
{{
start = p;
}}
goto st14;
st14:
if ( ++p == pe )
goto _test_eof14;
case 14:
#line 1545 "http.d"
if ( 48u <= (*p) && (*p) <= 57u )
goto st14;
goto st0;
tr8:
#line 3181 "http.d.rl"
{{
start = p;
}}
goto st10;
st10:
if ( ++p == pe )
goto _test_eof10;
case 10:
#line 1559 "http.d"
if ( (*p) == 45u )
goto tr12;
if ( 48u <= (*p) && (*p) <= 57u )
goto st10;
goto st0;
tr12:
#line 3185 "http.d.rl"
{{
head.Range.From.Value = std.conv.parse!ulong(line[(start - line.ptr)
.. (p - line.ptr)]);
}}
goto st11;
st11:
if ( ++p == pe )
goto _test_eof11;
case 11:
#line 1576 "http.d"
if ( 48u <= (*p) && (*p) <= 57u )
goto tr14;
goto st0;
tr14:
#line 3181 "http.d.rl"
{{
start = p;
}}
goto st12;
st12:
if ( ++p == pe )
goto _test_eof12;
case 12:
#line 1590 "http.d"
if ( (*p) == 47u )
goto tr15;
if ( 48u <= (*p) && (*p) <= 57u )
goto st12;
goto st0;
default: break;
}
_test_eof2: cs = 2; goto _test_eof;
_test_eof3: cs = 3; goto _test_eof;
_test_eof4: cs = 4; goto _test_eof;
_test_eof5: cs = 5; goto _test_eof;
_test_eof6: cs = 6; goto _test_eof;
_test_eof7: cs = 7; goto _test_eof;
_test_eof8: cs = 8; goto _test_eof;
_test_eof9: cs = 9; goto _test_eof;
_test_eof13: cs = 13; goto _test_eof;
_test_eof14: cs = 14; goto _test_eof;
_test_eof10: cs = 10; goto _test_eof;
_test_eof11: cs = 11; goto _test_eof;
_test_eof12: cs = 12; goto _test_eof;
_test_eof: {}
if ( p == eof )
{
switch ( cs ) {
case 14:
#line 3195 "http.d.rl"
{{
head.Range.Length.Value = std.conv.parse!ulong(line[(start - line.ptr)
.. (p - line.ptr)]);
}}
break;
case 13:
#line 3200 "http.d.rl"
{{
head.Range.Length.Unknown = true;
}}
break;
#line 1629 "http.d"
default: break;
}
}
_out: {}
}
#line 1184 "http.d.rl"
mixin(finishParser!"parseContentRangeHeader");
return head;
}
}
/**
* Data type for the $(LINK2 http://tools.ietf.org/html/rfc2616#section-14.17,
* "Content-Type" header)
*
* Type:
* entity-header
*/
struct ContentTypeHeader
{
public:
///
MediaType Type;
static string Key = "Content-Type";
void format(T)(T writer) if (isOutputRange!(T, string))
{
assert(isToken(Type.Type));
assert(isToken(Type.SubType));
writer.put(Type.Type);
writer.put("/");
writer.put(Type.SubType);
foreach(key, value; media.Parameters)
writeParameter(writer, key, value);
}
static ContentTypeHeader parse(string line)
{
ContentTypeHeader ctype;
MediaType ret;
const(char)* start;
mixin(initParser!());
#line 1676 "http.d"
{
cs = parseContentTypeHeader_start;
}
#line 1681 "http.d"
{
if ( p == pe )
goto _test_eof;
switch ( cs )
{
case 1:
switch( (*p) ) {
case 33u: goto tr0;
case 124u: goto tr0;
case 126u: goto tr0;
default: break;
}
if ( (*p) < 45u ) {
if ( (*p) > 39u ) {
if ( 42u <= (*p) && (*p) <= 43u )
goto tr0;
} else if ( (*p) >= 35u )
goto tr0;
} else if ( (*p) > 46u ) {
if ( (*p) < 65u ) {
if ( 48u <= (*p) && (*p) <= 57u )
goto tr0;
} else if ( (*p) > 90u ) {
if ( 94u <= (*p) && (*p) <= 122u )
goto tr0;
} else
goto tr0;
} else
goto tr0;
goto st0;
st0:
cs = 0;
goto _out;
tr0:
#line 3219 "http.d.rl"
{{
start = p;
}}
goto st2;
st2:
if ( ++p == pe )
goto _test_eof2;
case 2:
#line 1725 "http.d"
switch( (*p) ) {
case 33u: goto st2;
case 47u: goto tr3;
case 124u: goto st2;
case 126u: goto st2;
default: break;
}
if ( (*p) < 45u ) {
if ( (*p) > 39u ) {
if ( 42u <= (*p) && (*p) <= 43u )
goto st2;
} else if ( (*p) >= 35u )
goto st2;
} else if ( (*p) > 57u ) {
if ( (*p) > 90u ) {
if ( 94u <= (*p) && (*p) <= 122u )
goto st2;
} else if ( (*p) >= 65u )
goto st2;
} else
goto st2;
goto st0;
tr3:
#line 3223 "http.d.rl"
{{
ret.Type = line[(start - line.ptr) .. (p - line.ptr)];
}}
goto st3;
st3:
if ( ++p == pe )
goto _test_eof3;
case 3:
#line 1758 "http.d"
switch( (*p) ) {
case 33u: goto tr4;
case 124u: goto tr4;
case 126u: goto tr4;
default: break;
}
if ( (*p) < 45u ) {
if ( (*p) > 39u ) {
if ( 42u <= (*p) && (*p) <= 43u )
goto tr4;
} else if ( (*p) >= 35u )
goto tr4;
} else if ( (*p) > 46u ) {
if ( (*p) < 65u ) {
if ( 48u <= (*p) && (*p) <= 57u )
goto tr4;
} else if ( (*p) > 90u ) {
if ( 94u <= (*p) && (*p) <= 122u )
goto tr4;
} else
goto tr4;
} else
goto tr4;
goto st0;
tr4:
#line 3219 "http.d.rl"
{{
start = p;
}}
goto st6;
st6:
if ( ++p == pe )
goto _test_eof6;
case 6:
#line 1793 "http.d"
switch( (*p) ) {
case 9u: goto tr7;
case 13u: goto tr8;
case 32u: goto tr7;
case 33u: goto st6;
case 59u: goto tr10;
case 124u: goto st6;
case 126u: goto st6;
default: break;
}
if ( (*p) < 45u ) {
if ( (*p) > 39u ) {
if ( 42u <= (*p) && (*p) <= 43u )
goto st6;
} else if ( (*p) >= 35u )
goto st6;
} else if ( (*p) > 46u ) {
if ( (*p) < 65u ) {
if ( 48u <= (*p) && (*p) <= 57u )
goto st6;
} else if ( (*p) > 90u ) {
if ( 94u <= (*p) && (*p) <= 122u )
goto st6;
} else
goto st6;
} else
goto st6;
goto st0;
tr7:
#line 3227 "http.d.rl"
{{
ret.SubType = line[(start - line.ptr) .. (p - line.ptr)];
}}
goto st7;
st7:
if ( ++p == pe )
goto _test_eof7;
case 7:
#line 1832 "http.d"
switch( (*p) ) {
case 9u: goto st7;
case 13u: goto st4;
case 32u: goto st7;
case 59u: goto tr12;
default: break;
}
goto st0;
tr8:
#line 3227 "http.d.rl"
{{
ret.SubType = line[(start - line.ptr) .. (p - line.ptr)];
}}
goto st4;
st4:
if ( ++p == pe )
goto _test_eof4;
case 4:
#line 1851 "http.d"
if ( (*p) == 10u )
goto st5;
goto st0;
st5:
if ( ++p == pe )
goto _test_eof5;
case 5:
switch( (*p) ) {
case 9u: goto st7;
case 32u: goto st7;
default: break;
}
goto st0;
tr12:
#line 3219 "http.d.rl"
{{
start = p;
}}
goto st8;
tr10:
#line 3227 "http.d.rl"
{{
ret.SubType = line[(start - line.ptr) .. (p - line.ptr)];
}}
#line 3219 "http.d.rl"
{{
start = p;
}}
goto st8;
st8:
if ( ++p == pe )
goto _test_eof8;
case 8:
#line 1885 "http.d"
goto st8;
default: break;
}
_test_eof2: cs = 2; goto _test_eof;
_test_eof3: cs = 3; goto _test_eof;
_test_eof6: cs = 6; goto _test_eof;
_test_eof7: cs = 7; goto _test_eof;
_test_eof4: cs = 4; goto _test_eof;
_test_eof5: cs = 5; goto _test_eof;
_test_eof8: cs = 8; goto _test_eof;
_test_eof: {}
if ( p == eof )
{
switch ( cs ) {
case 6:
#line 3227 "http.d.rl"
{{
ret.SubType = line[(start - line.ptr) .. (p - line.ptr)];
}}
break;
case 8:
#line 3231 "http.d.rl"
{{
ret.Parameters = parseParameterList(line[(start - line.ptr)
.. (p - line.ptr)]);
}}
break;
#line 1914 "http.d"
default: break;
}
}
_out: {}
}
#line 1225 "http.d.rl"
mixin(finishParser!"parseContentTypeHeader");
ctype.Type = ret;
return ctype;
}
}
/**
* Data type for the $(LINK2 http://tools.ietf.org/html/rfc2616#section-14.18,
* "Date" header)
*
* Type:
* general-header
*/
struct DateHeader
{
public:
///
SysTime Date;
static string Key = "Date";
void format(T)(T writer) if (isOutputRange!(T, string))
{
formatDate(Date);
}
static DateHeader parse(string line)
{
DateHeader head;
head.Date = parseDate(line);
return head;
}
}
/**
* Data type for the $(LINK2 http://tools.ietf.org/html/rfc2616#section-14.19,
* "ETag" header)
*
* Type:
* response-header
*/
struct ETagHeader
{
public:
///
public EntityTag Tag;
static string Key = "ETag";
void format(T)(T writer) if (isOutputRange!(T, string))
{
formatEntityTag(writer, Tag);
}
static ETagHeader parse(string line)
{
ETagHeader head;
EntityTag tag;
const(char)* start;
mixin(initParser!());
#line 1982 "http.d"
{
cs = parseETag_start;
}
#line 1987 "http.d"
{
if ( p == pe )
goto _test_eof;
switch ( cs )
{
case 1:
switch( (*p) ) {
case 34u: goto tr2;
case 87u: goto tr3;
case 124u: goto tr0;
case 126u: goto tr0;
default: break;
}
if ( (*p) < 45u ) {
if ( (*p) > 39u ) {
if ( 42u <= (*p) && (*p) <= 43u )
goto tr0;
} else if ( (*p) >= 33u )
goto tr0;
} else if ( (*p) > 46u ) {
if ( (*p) < 65u ) {
if ( 48u <= (*p) && (*p) <= 57u )
goto tr0;
} else if ( (*p) > 90u ) {
if ( 94u <= (*p) && (*p) <= 122u )
goto tr0;
} else
goto tr0;
} else
goto tr0;
goto st0;
st0:
cs = 0;
goto _out;
tr0:
#line 3279 "http.d.rl"
{{
start = p;
}}
goto st8;
tr11:
#line 3275 "http.d.rl"
{{
tag.Weak = true;
}}
#line 3279 "http.d.rl"
{{
start = p;
}}
goto st8;
st8:
if ( ++p == pe )
goto _test_eof8;
case 8:
#line 2042 "http.d"
switch( (*p) ) {
case 33u: goto st8;
case 124u: goto st8;
case 126u: goto st8;
default: break;
}
if ( (*p) < 45u ) {
if ( (*p) > 39u ) {
if ( 42u <= (*p) && (*p) <= 43u )
goto st8;
} else if ( (*p) >= 35u )
goto st8;
} else if ( (*p) > 46u ) {
if ( (*p) < 65u ) {
if ( 48u <= (*p) && (*p) <= 57u )
goto st8;
} else if ( (*p) > 90u ) {
if ( 94u <= (*p) && (*p) <= 122u )
goto st8;
} else
goto st8;
} else
goto st8;
goto st0;
tr2:
#line 3279 "http.d.rl"
{{
start = p;
}}
goto st2;
tr12:
#line 3275 "http.d.rl"
{{
tag.Weak = true;
}}
#line 3279 "http.d.rl"
{{
start = p;
}}
goto st2;
st2:
if ( ++p == pe )
goto _test_eof2;
case 2:
#line 2087 "http.d"
switch( (*p) ) {
case 13u: goto st3;
case 34u: goto st9;
case 92u: goto st5;
case 127u: goto st0;
default: break;
}
if ( (*p) > 8u ) {
if ( 10u <= (*p) && (*p) <= 31u )
goto st0;
} else
goto st0;
goto st2;
st3:
if ( ++p == pe )
goto _test_eof3;
case 3:
if ( (*p) == 10u )
goto st4;
goto st0;
st4:
if ( ++p == pe )
goto _test_eof4;
case 4:
switch( (*p) ) {
case 9u: goto st2;
case 32u: goto st2;
default: break;
}
goto st0;
st9:
if ( ++p == pe )
goto _test_eof9;
case 9:
goto st0;
st5:
if ( ++p == pe )
goto _test_eof5;
case 5:
switch( (*p) ) {
case 13u: goto st6;
case 34u: goto st10;
case 92u: goto st5;
default: break;
}
goto st2;
st6:
if ( ++p == pe )
goto _test_eof6;
case 6:
switch( (*p) ) {
case 10u: goto st4;
case 13u: goto st3;
case 34u: goto st9;
case 92u: goto st5;
case 127u: goto st0;
default: break;
}
if ( (*p) > 8u ) {
if ( 11u <= (*p) && (*p) <= 31u )
goto st0;
} else
goto st0;
goto st2;
st10:
if ( ++p == pe )
goto _test_eof10;
case 10:
switch( (*p) ) {
case 13u: goto st3;
case 34u: goto st9;
case 92u: goto st5;
case 127u: goto st0;
default: break;
}
if ( (*p) > 8u ) {
if ( 10u <= (*p) && (*p) <= 31u )
goto st0;
} else
goto st0;
goto st2;
tr3:
#line 3279 "http.d.rl"
{{
start = p;
}}
goto st11;
st11:
if ( ++p == pe )
goto _test_eof11;
case 11:
#line 2179 "http.d"
switch( (*p) ) {
case 33u: goto st8;
case 47u: goto st7;
case 124u: goto st8;
case 126u: goto st8;
default: break;
}
if ( (*p) < 45u ) {
if ( (*p) > 39u ) {
if ( 42u <= (*p) && (*p) <= 43u )
goto st8;
} else if ( (*p) >= 35u )
goto st8;
} else if ( (*p) > 57u ) {
if ( (*p) > 90u ) {
if ( 94u <= (*p) && (*p) <= 122u )
goto st8;
} else if ( (*p) >= 65u )
goto st8;
} else
goto st8;
goto st0;
st7:
if ( ++p == pe )
goto _test_eof7;
case 7:
switch( (*p) ) {
case 34u: goto tr12;
case 124u: goto tr11;
case 126u: goto tr11;
default: break;
}
if ( (*p) < 45u ) {
if ( (*p) > 39u ) {
if ( 42u <= (*p) && (*p) <= 43u )
goto tr11;
} else if ( (*p) >= 33u )
goto tr11;
} else if ( (*p) > 46u ) {
if ( (*p) < 65u ) {
if ( 48u <= (*p) && (*p) <= 57u )
goto tr11;
} else if ( (*p) > 90u ) {
if ( 94u <= (*p) && (*p) <= 122u )
goto tr11;
} else
goto tr11;
} else
goto tr11;
goto st0;
default: break;
}
_test_eof8: cs = 8; goto _test_eof;
_test_eof2: cs = 2; goto _test_eof;
_test_eof3: cs = 3; goto _test_eof;
_test_eof4: cs = 4; goto _test_eof;
_test_eof9: cs = 9; goto _test_eof;
_test_eof5: cs = 5; goto _test_eof;
_test_eof6: cs = 6; goto _test_eof;
_test_eof10: cs = 10; goto _test_eof;
_test_eof11: cs = 11; goto _test_eof;
_test_eof7: cs = 7; goto _test_eof;
_test_eof: {}
if ( p == eof )
{
switch ( cs ) {
case 8:
case 9:
case 10:
case 11:
#line 3282 "http.d.rl"
{{
tag.Value = prepareValue(line[(start - line.ptr) .. (p - line.ptr)]);
}}
break;
#line 2256 "http.d"
default: break;
}
}
_out: {}
}
#line 1287 "http.d.rl"
mixin(finishParser!"parseETag");
head.Tag = tag;
return head;
}
}
/**
* Data type for the $(LINK2 http://tools.ietf.org/html/rfc2616#section-14.20,
* "Expect" header)
*
* Type:
* request-header
*/
struct ExpectHeader
{
public:
///
Expectation[] Expect;
static string Key = "Expect";
void format(T)(T writer) if (isOutputRange!(T, string))
{
foreach(i, entry; Expect)
{
if(entry.Type == Expectation.Continue)
{
assert(entry.ExtensionKey == "");
assert(entry.ExtensionValue == "");
assert(entry.ExtensionParameters.length == 0);
writer.put("100-continue");
}
else
{
assert(entry.ExtensionKey != "");
assert(isToken(entry.ExtensionKey));
writer.put(entry.ExtensionKey);
if(entry.ExtensionValue != "")
{
writer.put("=");
if(isToken(entry.ExtensionValue))
writer.put(entry.ExtensionValue);
else
writer.put(quote(entry.ExtensionValue));
}
foreach(key, value; entry.ExtensionParameters)
{
writeParameter(writer, key, value);
}
}
if(i != Expect.length - 1)
writer.put(", ");
}
}
}
/**
* Data type for the $(LINK2 http://tools.ietf.org/html/rfc2616#section-14.21,
* "Expires" header)
*
* Type:
* entity-header
*/
struct ExpiresHeader
{
public:
///
SysTime Date;
static string Key = "Expires";
void format(T)(T writer) if (isOutputRange!(T, string))
{
formatDate(writer, Date);
}
static ExpiresHeader parse(string line)
{
ExpiresHeader head;
head.Date = parseDate(line);
return head;
}
}
/**
* Data type for the $(LINK2 http://tools.ietf.org/html/rfc2616#section-14.22,
* "From" header)
*
* Type:
* request-header
*/
struct FromHeader
{
public:
///
string Email;
static string Key = "From";
void format(T)(T writer) if (isOutputRange!(T, string))
{
//TODO: Chek in debug with isemail
writer.put(this.email);
}
static FromHeader parse(string line)
{
FromHeader from;
from.Email = line;
return from;
}
}
/**
* Data type for the $(LINK2 http://tools.ietf.org/html/rfc2616#section-14.23,
* "Host" header)
*
* Type:
* request-header
*/
struct HostHeader
{
public:
///
string Host;
///
bool DefaultPort = true;
///
ushort Port;
static string Key = "Host";
void format(T)(T writer) if (isOutputRange!(T, string))
{
//TODO: check host in debug mode
assert(host != "");
writer.put(host);
if(!DefaultPort)
{
writer.put(':');
formattedWrite(writer, "%s", Port);
}
}
}
/**
* Data type for the $(LINK2 http://tools.ietf.org/html/rfc2616#section-14.24,
* "If-Match" header)
*
* Type:
* request-header
*/
struct IfMatchHeader
{
public:
///
bool All;
///
EntityTag[] Tags;
static string Key = "If-Match";
void format(T)(T writer) if (isOutputRange!(T, string))
{
if(All)
{
writer.put("*");
assert(Tags.length == 0);
}
else
{
assert(Tags.length > 0);
foreach(i, entry; Tags)
{
formatEntityTag(entry, writer);
if(i != header.Specific.length - 1)
writer.put(", ");
}
}
}
}
/**
* Data type for the $(LINK2 http://tools.ietf.org/html/rfc2616#section-14.25,
* "If-Modified-Since" header)
*
* Type:
* request-header
*/
struct IfModifiedSinceHeader
{
public:
///
SysTime Date;
static string Key = "If-Modified-Since";
void format(T)(T writer) if (isOutputRange!(T, string))
{
formatDate(writer, Date);
}
static IfModifiedSinceHeader parse(string line)
{
IfModifiedSinceHeader head;
head.Date = parseDate(line);
return head;
}
}
/**
* Data type for the $(LINK2 http://tools.ietf.org/html/rfc2616#section-14.26,
* "If-None-Match" header)
*
* Type:
* request-header
*/
struct IfNoneMatchHeader
{
public:
///
bool All;
///
EntityTag[] Tags;
static string Key = "If-None-Match";
void format(T)(T writer) if (isOutputRange!(T, string))
{
if(All)
{
writer.put("*");
assert(Tags.length == 0);
}
else
{
assert(Tags.length > 0);
foreach(i, entry; Tags)
{
formatEntityTag(entry, writer);
if(i != header.Specific.length - 1)
writer.put(", ");
}
}
}
}
/**
* Data type for the $(LINK2 http://tools.ietf.org/html/rfc2616#section-14.27,
* "If-Range" header)
*
* Type:
* request-header
*/
struct IfRangeHeader
{
public:
///
SysTime Time;
///
EntityTag Tag;
///Use a time value in the If-Range header instead of an EntityTag
bool useTime;
static string Key = "If-Range";
void format(T)(T writer) if (isOutputRange!(T, string))
{
if(useTime)
{
formatDate(entry, Date);
}
else
{
formatEntityTag(writer, Tag);
}
}
}
/**
* Data type for the $(LINK2 http://tools.ietf.org/html/rfc2616#section-14.28,
* "If-Unmodified-Since" header)
*
* Type:
* request-header
*/
struct IfUnmodifiedSinceHeader
{
public:
///
SysTime Date;
static string Key = "If-Unmodified-Since";
void format(T)(T writer) if (isOutputRange!(T, string))
{
formatDate(writer, Date);
}
static IfUnmodifiedSinceHeader parse(string line)
{
IfUnmodifiedSinceHeader head;
head.Date = parseDate(line);
return head;
}
}
/**
* Data type for the $(LINK2 http://tools.ietf.org/html/rfc2616#section-14.29,
* "Last-Modified" header)
*
* Type:
* entity-header
*/
struct LastModifiedHeader
{
public:
///
SysTime Date;
static string Key = "Last-Modified";
void format(T)(T writer) if (isOutputRange!(T, string))
{
formatDate(writer, Date);
}
static LastModifiedHeader parse(string line)
{
LastModifiedHeader head;
head.Date = parseDate(line);
return head;
}
}
/**
* Data type for the $(LINK2 http://tools.ietf.org/html/rfc2616#section-14.30,
* "Location" header)
*
* Type:
* response-header
*/
struct LocationHeader
{
public:
///
string Location;
static string Key = "Location";
void format(T)(T writer) if (isOutputRange!(T, string))
{
//TODO: verify URI in debug mode
writer.put(Location);
}
static LocationHeader parse(string line)
{
LocationHeader loc;
loc.Location = line;
return loc;
}
}
/**
* Data type for the $(LINK2 http://tools.ietf.org/html/rfc2616#section-14.31,
* "Max-Forwards" header)
*
* Type:
* request-header
*/
struct MaxForwardsHeader
{
public:
///
ulong Forwards;
static string Key = "Max-Forwards";
void format(T)(T writer) if (isOutputRange!(T, string))
{
formattedWrite(writer, Forwards);
}
static MaxForwardsHeader parse(string line)
{
MaxForwardsHeader mf;
mf.Forwards = std.conv.parse!ulong(line);
return mf;
}
}
/**
* Data type for the $(LINK2 http://tools.ietf.org/html/rfc2616#section-14.32,
* "Pragma" header)
*
* Type:
* general-header
*/
struct PragmaHeader
{
public:
///
PragmaDirective[] Directives;
static string Key = "Pragma";
void format(T)(T writer) if (isOutputRange!(T, string))
{
foreach(i, entry; Directives)
{
if(entry.Type == PragmaDirective.NoCache)
{
writer.put("no-cache");
assert(entry.ExtensionKey == "");
assert(entry.ExtensionValue == "");
}
else
{
assert(entry.ExtensionKey != "");
assert(isToken(entry.ExtensionKey));
writer.put(entry.ExtensionKey);
if(entry.ExtensionValue != "")
{
writer.put("=");
if(isToken(entry.ExtensionValue))
writer.put(entry.ExtensionValue);
else
writer.put(quote(entry.ExtensionValue));
}
}
if(i != Directives.length - 1)
writer.put(", ");
}
}
static PragmaHeader parse(string line)
{
PragmaHeader prag;
string[] list = parseCommaList(line);
foreach(entry; list)
{
PragmaDirective dir;
const(char)* start;
mixin(initParser!("entry"));
#line 2694 "http.d"
{
cs = parsePragmaEntry_start;
}
#line 2699 "http.d"
{
if ( p == pe )
goto _test_eof;
switch ( cs )
{
case 1:
switch( (*p) ) {
case 33u: goto tr0;
case 124u: goto tr0;
case 126u: goto tr0;
default: break;
}
if ( (*p) < 45u ) {
if ( (*p) > 39u ) {
if ( 42u <= (*p) && (*p) <= 43u )
goto tr0;
} else if ( (*p) >= 35u )
goto tr0;
} else if ( (*p) > 46u ) {
if ( (*p) < 65u ) {
if ( 48u <= (*p) && (*p) <= 57u )
goto tr0;
} else if ( (*p) > 90u ) {
if ( 94u <= (*p) && (*p) <= 122u )
goto tr0;
} else
goto tr0;
} else
goto tr0;
goto st0;
st0:
cs = 0;
goto _out;
tr0:
#line 3297 "http.d.rl"
{{
start = p;
}}
goto st8;
st8:
if ( ++p == pe )
goto _test_eof8;
case 8:
#line 2743 "http.d"
switch( (*p) ) {
case 33u: goto st8;
case 61u: goto tr12;
case 124u: goto st8;
case 126u: goto st8;
default: break;
}
if ( (*p) < 45u ) {
if ( (*p) > 39u ) {
if ( 42u <= (*p) && (*p) <= 43u )
goto st8;
} else if ( (*p) >= 35u )
goto st8;
} else if ( (*p) > 46u ) {
if ( (*p) < 65u ) {
if ( 48u <= (*p) && (*p) <= 57u )
goto st8;
} else if ( (*p) > 90u ) {
if ( 94u <= (*p) && (*p) <= 122u )
goto st8;
} else
goto st8;
} else
goto st8;
goto st0;
tr12:
#line 3300 "http.d.rl"
{{
string key = entry[(start - entry.ptr)
.. (p - entry.ptr)];
if(key == "no-cache")
{
dir.Type = PragmaDirective.NoCache;
}
else
{
dir.Type = PragmaDirective.Extension;
dir.ExtensionKey = key;
}
}}
goto st2;
st2:
if ( ++p == pe )
goto _test_eof2;
case 2:
#line 2789 "http.d"
switch( (*p) ) {
case 34u: goto tr3;
case 124u: goto tr2;
case 126u: goto tr2;
default: break;
}
if ( (*p) < 45u ) {
if ( (*p) > 39u ) {
if ( 42u <= (*p) && (*p) <= 43u )
goto tr2;
} else if ( (*p) >= 33u )
goto tr2;
} else if ( (*p) > 46u ) {
if ( (*p) < 65u ) {
if ( 48u <= (*p) && (*p) <= 57u )
goto tr2;
} else if ( (*p) > 90u ) {
if ( 94u <= (*p) && (*p) <= 122u )
goto tr2;
} else
goto tr2;
} else
goto tr2;
goto st0;
tr2:
#line 3297 "http.d.rl"
{{
start = p;
}}
goto st9;
st9:
if ( ++p == pe )
goto _test_eof9;
case 9:
#line 2824 "http.d"
switch( (*p) ) {
case 33u: goto st9;
case 124u: goto st9;
case 126u: goto st9;
default: break;
}
if ( (*p) < 45u ) {
if ( (*p) > 39u ) {
if ( 42u <= (*p) && (*p) <= 43u )
goto st9;
} else if ( (*p) >= 35u )
goto st9;
} else if ( (*p) > 46u ) {
if ( (*p) < 65u ) {
if ( 48u <= (*p) && (*p) <= 57u )
goto st9;
} else if ( (*p) > 90u ) {
if ( 94u <= (*p) && (*p) <= 122u )
goto st9;
} else
goto st9;
} else
goto st9;
goto st0;
tr3:
#line 3297 "http.d.rl"
{{
start = p;
}}
goto st3;
st3:
if ( ++p == pe )
goto _test_eof3;
case 3:
#line 2859 "http.d"
switch( (*p) ) {
case 13u: goto st4;
case 34u: goto st10;
case 92u: goto st6;
case 127u: goto st0;
default: break;
}
if ( (*p) > 8u ) {
if ( 10u <= (*p) && (*p) <= 31u )
goto st0;
} else
goto st0;
goto st3;
st4:
if ( ++p == pe )
goto _test_eof4;
case 4:
if ( (*p) == 10u )
goto st5;
goto st0;
st5:
if ( ++p == pe )
goto _test_eof5;
case 5:
switch( (*p) ) {
case 9u: goto st3;
case 32u: goto st3;
default: break;
}
goto st0;
st10:
if ( ++p == pe )
goto _test_eof10;
case 10:
goto st0;
st6:
if ( ++p == pe )
goto _test_eof6;
case 6:
switch( (*p) ) {
case 13u: goto st7;
case 34u: goto st11;
case 92u: goto st6;
default: break;
}
goto st3;
st7:
if ( ++p == pe )
goto _test_eof7;
case 7:
switch( (*p) ) {
case 10u: goto st5;
case 13u: goto st4;
case 34u: goto st10;
case 92u: goto st6;
case 127u: goto st0;
default: break;
}
if ( (*p) > 8u ) {
if ( 11u <= (*p) && (*p) <= 31u )
goto st0;
} else
goto st0;
goto st3;
st11:
if ( ++p == pe )
goto _test_eof11;
case 11:
switch( (*p) ) {
case 13u: goto st4;
case 34u: goto st10;
case 92u: goto st6;
case 127u: goto st0;
default: break;
}
if ( (*p) > 8u ) {
if ( 10u <= (*p) && (*p) <= 31u )
goto st0;
} else
goto st0;
goto st3;
default: break;
}
_test_eof8: cs = 8; goto _test_eof;
_test_eof2: cs = 2; goto _test_eof;
_test_eof9: cs = 9; goto _test_eof;
_test_eof3: cs = 3; goto _test_eof;
_test_eof4: cs = 4; goto _test_eof;
_test_eof5: cs = 5; goto _test_eof;
_test_eof10: cs = 10; goto _test_eof;
_test_eof6: cs = 6; goto _test_eof;
_test_eof7: cs = 7; goto _test_eof;
_test_eof11: cs = 11; goto _test_eof;
_test_eof: {}
if ( p == eof )
{
switch ( cs ) {
case 8:
#line 3300 "http.d.rl"
{{
string key = entry[(start - entry.ptr)
.. (p - entry.ptr)];
if(key == "no-cache")
{
dir.Type = PragmaDirective.NoCache;
}
else
{
dir.Type = PragmaDirective.Extension;
dir.ExtensionKey = key;
}
}}
break;
case 9:
case 10:
case 11:
#line 3313 "http.d.rl"
{{
dir.ExtensionValue = prepareValue(entry[(start - entry.ptr)
.. (p - entry.ptr)]);
}}
break;
#line 2983 "http.d"
default: break;
}
}
_out: {}
}
#line 1719 "http.d.rl"
mixin(finishParser!"parsePragmaEntry");
prag.Directives ~= dir;
}
return prag;
}
}
/**
* Data type for the $(LINK2 http://tools.ietf.org/html/rfc2616#section-14.35,
* "Range" header)
*
* Type:
* request-header
*/
struct RangeHeader
{
public:
///
ByteRangeSpec[] Ranges;
static string Key = "Range";
void format(T)(T writer) if (isOutputRange!(T, string))
{
writer.put("bytes=");
foreach(i, entry; Ranges)
{
if(entry.Type == ByteRangeSpec.SuffixSpec)
{
writer.put("-");
formattedWrite(writer, "%s", entry.Suffix);
}
else
{
formattedWrite(writer, "%s", entry.First);
writer.put("-");
formattedWrite(writer, "%s", entry.Last);
}
if(i != Ranges.length - 1)
writer.put(",");
}
}
}
/**
* Data type for the $(LINK2 http://tools.ietf.org/html/rfc2616#section-14.36,
* "Referer" header)
*
* Type:
* request-header
*/
struct RefererHeader
{
public:
///
string Location;
static string Key = "Referer";
void format(T)(T writer) if (isOutputRange!(T, string))
{
//TODO: in debug mode check if Location is a valid URI
writer.put(Location);
}
static RefererHeader parse(string line)
{
RefererHeader refa;
refa.Location = line;
return refa;
}
}
/**
* Data type for the $(LINK2 http://tools.ietf.org/html/rfc2616#section-14.37,
* "Retry-After" header)
*
* Type:
* response-header
*/
struct RetryAfterHeader
{
public:
///
SysTime Date;
///
ulong Delta;
///Use a time value in the Retry-After header instead of delta seconds
bool useTime = false;
static string Key = "Retry-After";
void format(T)(T writer) if (isOutputRange!(T, string))
{
if(useTime)
{
formatDate(writer, Date);
}
else
{
formattedWrite(writer, "%s", Delta);
}
}
static RetryAfterHeader parse(string line)
{
RetryAfterHeader ret;
const(char)* start;
mixin(initParser!());
#line 3098 "http.d"
{
cs = parseRetryAfter_start;
}
#line 3103 "http.d"
{
if ( p == pe )
goto _test_eof;
switch ( cs )
{
case 1:
switch( (*p) ) {
case 13u: goto st2;
case 127u: goto st0;
default: break;
}
if ( (*p) < 10u ) {
if ( (*p) <= 8u )
goto st0;
} else if ( (*p) > 31u ) {
if ( 48u <= (*p) && (*p) <= 57u )
goto st5;
} else
goto st0;
goto st4;
st0:
cs = 0;
goto _out;
st4:
if ( ++p == pe )
goto _test_eof4;
case 4:
switch( (*p) ) {
case 13u: goto st2;
case 127u: goto st0;
default: break;
}
if ( (*p) > 8u ) {
if ( 10u <= (*p) && (*p) <= 31u )
goto st0;
} else
goto st0;
goto st4;
st2:
if ( ++p == pe )
goto _test_eof2;
case 2:
if ( (*p) == 10u )
goto st3;
goto st0;
st3:
if ( ++p == pe )
goto _test_eof3;
case 3:
switch( (*p) ) {
case 9u: goto st4;
case 32u: goto st4;
default: break;
}
goto st0;
st5:
if ( ++p == pe )
goto _test_eof5;
case 5:
if ( 48u <= (*p) && (*p) <= 57u )
goto st5;
goto st0;
default: break;
}
_test_eof4: cs = 4; goto _test_eof;
_test_eof2: cs = 2; goto _test_eof;
_test_eof3: cs = 3; goto _test_eof;
_test_eof5: cs = 5; goto _test_eof;
_test_eof: {}
if ( p == eof )
{
switch ( cs ) {
case 5:
#line 3469 "http.d.rl"
{{
ret.useTime = false;
ret.Delta = std.conv.parse!ulong(line);
}}
break;
case 4:
#line 3474 "http.d.rl"
{{
ret.useTime = true;
ret.Date = parseDate(line);
}}
break;
#line 3191 "http.d"
default: break;
}
}
_out: {}
}
#line 1828 "http.d.rl"
mixin(finishParser!"parseRetryAfter");
return ret;
}
}
/**
* Data type for the $(LINK2 http://tools.ietf.org/html/rfc2616#section-14.39,
* "TE" header)
*
* Type:
* request-header
*/
struct TEHeader
{
public:
///
TCoding[] Codings;
static string Key = "TE";
void format(T)(T writer) if (isOutputRange!(T, string))
{
foreach(i, entry; Codings)
{
if(Codings.length == 1 && entr.Trailers)
{
assert(i == 0, "The 'trailers' coding must be the first coding!");
writer.put("trailers");
}
else
{
assert(isToken(entry.Extension));
writer.put(entry.Extension);
foreach(key, value; entry.Parameters)
{
writeParameter(writer, key, value);
}
if(entry.Q >= 0)
{
assert(entry.Q <= 1);
formattedWrite(writer, ";q=%.3f", entry.Q);
foreach(key, value; entry.AcceptParameters)
{
writeParameter(writer, key, value);
}
}
}
if(i != Codings.length - 1)
writer.put(", ");
}
}
}
/**
* Data type for the $(LINK2 http://tools.ietf.org/html/rfc2616#section-14.40,
* "Trailer" header)
*
* Type:
* general-header
*/
struct TrailerHeader
{
public:
///
string[] Fields;
static string Key = "Trailer";
void format(T)(T writer) if (isOutputRange!(T, string))
{
foreach(entry; Fields)
{
assert(isToken(entry));
writer.put(entry);
if(i != Fields.length - 1)
writer.put(", ");
}
}
static TrailerHeader parse(string line)
{
TrailerHeader head;
head.Fields = parseCommaList(line);
return head;
}
}
/**
* Data type for the $(LINK2 http://tools.ietf.org/html/rfc2616#section-14.41,
* "Transfer-Encoding" header)
*
* Type:
* general-header
*/
struct TransferEncodingHeader
{
public:
///
TransferCoding[] Codings;
static string Key = "Transfer-Encoding";
void format(T)(T writer) if (isOutputRange!(T, string))
{
foreach(i, entry; Codings)
{
if(entry.Type == TransferCoding.Chunked)
{
writer.put("chunked");
assert(entry.Raw == "");
assert(entry.Parameters.length == 0);
}
else
{
assert(entry.Raw != "");
assert(isToken(entry.Raw));
writer.put(entry.Raw);
foreach(key, value; entry.Parameters)
{
writeParameter(writer, key, value);
}
}
if(i != Codings.length - 1)
writer.put(", ");
}
}
static TransferEncodingHeader parse(string line)
{
TransferEncodingHeader head;
string[] list = parseCommaList(line);
foreach(entry; list)
{
TransferCoding cod;
const(char)* start;
mixin(initParser!("entry"));
#line 3334 "http.d"
{
cs = parseTransferCoding_start;
}
#line 3339 "http.d"
{
if ( p == pe )
goto _test_eof;
switch ( cs )
{
case 1:
switch( (*p) ) {
case 33u: goto tr0;
case 124u: goto tr0;
case 126u: goto tr0;
default: break;
}
if ( (*p) < 45u ) {
if ( (*p) > 39u ) {
if ( 42u <= (*p) && (*p) <= 43u )
goto tr0;
} else if ( (*p) >= 35u )
goto tr0;
} else if ( (*p) > 46u ) {
if ( (*p) < 65u ) {
if ( 48u <= (*p) && (*p) <= 57u )
goto tr0;
} else if ( (*p) > 90u ) {
if ( 94u <= (*p) && (*p) <= 122u )
goto tr0;
} else
goto tr0;
} else
goto tr0;
goto st0;
st0:
cs = 0;
goto _out;
tr0:
#line 3326 "http.d.rl"
{{
start = p;
}}
goto st4;
st4:
if ( ++p == pe )
goto _test_eof4;
case 4:
#line 3383 "http.d"
switch( (*p) ) {
case 9u: goto tr4;
case 13u: goto tr5;
case 32u: goto tr4;
case 33u: goto st4;
case 59u: goto tr7;
case 124u: goto st4;
case 126u: goto st4;
default: break;
}
if ( (*p) < 45u ) {
if ( (*p) > 39u ) {
if ( 42u <= (*p) && (*p) <= 43u )
goto st4;
} else if ( (*p) >= 35u )
goto st4;
} else if ( (*p) > 46u ) {
if ( (*p) < 65u ) {
if ( 48u <= (*p) && (*p) <= 57u )
goto st4;
} else if ( (*p) > 90u ) {
if ( 94u <= (*p) && (*p) <= 122u )
goto st4;
} else
goto st4;
} else
goto st4;
goto st0;
tr4:
#line 3329 "http.d.rl"
{{
string key = entry[(start - entry.ptr)
.. (p - entry.ptr)];
if(key == "chunked")
{
cod.Type = TransferCoding.Chunked;
}
else
{
cod.Type = TransferCoding.Custom;
cod.Raw = key;
}
}}
goto st5;
st5:
if ( ++p == pe )
goto _test_eof5;
case 5:
#line 3432 "http.d"
switch( (*p) ) {
case 9u: goto st5;
case 13u: goto st2;
case 32u: goto st5;
case 59u: goto tr9;
default: break;
}
goto st0;
tr5:
#line 3329 "http.d.rl"
{{
string key = entry[(start - entry.ptr)
.. (p - entry.ptr)];
if(key == "chunked")
{
cod.Type = TransferCoding.Chunked;
}
else
{
cod.Type = TransferCoding.Custom;
cod.Raw = key;
}
}}
goto st2;
st2:
if ( ++p == pe )
goto _test_eof2;
case 2:
#line 3461 "http.d"
if ( (*p) == 10u )
goto st3;
goto st0;
st3:
if ( ++p == pe )
goto _test_eof3;
case 3:
switch( (*p) ) {
case 9u: goto st5;
case 32u: goto st5;
default: break;
}
goto st0;
tr9:
#line 3326 "http.d.rl"
{{
start = p;
}}
goto st6;
tr7:
#line 3329 "http.d.rl"
{{
string key = entry[(start - entry.ptr)
.. (p - entry.ptr)];
if(key == "chunked")
{
cod.Type = TransferCoding.Chunked;
}
else
{
cod.Type = TransferCoding.Custom;
cod.Raw = key;
}
}}
#line 3326 "http.d.rl"
{{
start = p;
}}
goto st6;
st6:
if ( ++p == pe )
goto _test_eof6;
case 6:
#line 3505 "http.d"
goto st6;
default: break;
}
_test_eof4: cs = 4; goto _test_eof;
_test_eof5: cs = 5; goto _test_eof;
_test_eof2: cs = 2; goto _test_eof;
_test_eof3: cs = 3; goto _test_eof;
_test_eof6: cs = 6; goto _test_eof;
_test_eof: {}
if ( p == eof )
{
switch ( cs ) {
case 4:
#line 3329 "http.d.rl"
{{
string key = entry[(start - entry.ptr)
.. (p - entry.ptr)];
if(key == "chunked")
{
cod.Type = TransferCoding.Chunked;
}
else
{
cod.Type = TransferCoding.Custom;
cod.Raw = key;
}
}}
break;
case 6:
#line 3342 "http.d.rl"
{{
cod.Parameters = parseParameterList(entry[(start - entry.ptr)
.. (p - entry.ptr)]);
}}
break;
#line 3542 "http.d"
default: break;
}
}
_out: {}
}
#line 1965 "http.d.rl"
mixin(finishParser!"parseTransferCoding");
head.Codings ~= cod;
}
return head;
}
}
/**
* Data type for the $(LINK2 http://tools.ietf.org/html/rfc2616#section-14.42,
* "Upgrade" header)
*
* Type:
* general-header
*/
struct UpgradeHeader
{
public:
///
Product[] Products;
static string Key = "Upgrade";
void format(T)(T writer) if (isOutputRange!(T, string))
{
foreach(i, entry; Products)
{
assert(entry.Name != "");
assert(isToken(entry.Name));
writer.put(entry.Name);
if(entry.Version != "")
{
assert(isToken(entry.Version));
writer.put("/");
writer.put(entry.Version);
}
if(i != Products.length - 1)
writer.put(", ");
}
}
static UpgradeHeader parse(string line)
{
UpgradeHeader head;
string[] list = parseCommaList(line);
foreach(entry; list)
{
head.Products ~= parseProduct(entry);
}
return head;
}
}
/**
* Data type for the $(LINK2 http://tools.ietf.org/html/rfc2616#section-14.44,
* "Vary" header)
*
* Type:
* response-header
*/
struct VaryHeader
{
public:
///
bool All = false;
///
string[] Fields;
static string Key = "Vary";
void format(T)(T writer) if (isOutputRange!(T, string))
{
}
static VaryHeader parse(string line)
{
VaryHeader head;
if(line.length == 1 && line[0] == '*')
{
head.All = true;
}
else
{
head.Fields = parseCommaList(line);
}
return head;
}
}
unittest
{
Response rl = parseResponse("HTTP/1.2 200 OK\r\n");
assert(rl.Type == ResponseType.StatusLine);
assert(rl.Line == "HTTP/1.2 200 OK\r\n");
StatusLine sl = StatusLine.parse(rl.Line);
assert(sl.Major == 1);
assert(sl.Minor == 2);
assert(sl.StatusCode == 200);
assert(sl.Reason == "OK");
}
unittest
{
Response rl = parseResponse("\r\n");
assert(rl.Type == ResponseType.Empty);
}
unittest
{
bool thrown = false;
try
parseResponse("");
catch(Exception)
thrown = true;
assert(thrown);
}
unittest
{
Response rl = parseResponse("Server: Apache/1.3.29 (Unix) PHP/4.3.4\r\n");
assert(rl.Type == ResponseType.Header);
assert(rl.Key == "Server");
assert(rl.Value == "Apache/1.3.29 (Unix) PHP/4.3.4");
rl = parseResponse("Content-Length: 999\r\n");
assert(rl.Type == ResponseType.Header);
assert(rl.Key == "Content-Length");
assert(rl.Value == "999");
}
unittest
{
string[] list = parseCommaList(" , , Test , a,a , test,a, ,test,ab cd, ab dc ");
assert(list == ["Test", "a", "a", "test", "a", "test", "ab cd", "ab dc"]);
list = parseCommaList(" , test\r\n test , test");
assert(list == ["test\r\n test", "test"]);
}
unittest
{
Response rl = parseResponse("Allow: GET, HEAD\r\n");
assert(rl.Type == ResponseType.Header);
assert(rl.Key == "Allow");
auto head = parseHeaderValue!(AllowHeader)(rl.Value);
assert(head.Methods.length == 2);
assert(head.Methods[0].Type == Method.GET);
assert(head.Methods[1].Type == Method.HEAD);
}
unittest
{
Response rl = parseResponse("Connection: close\r\n");
assert(rl.Type == ResponseType.Header);
assert(rl.Key == "Connection");
auto head = parseHeaderValue!(ConnectionHeader)(rl.Value);
assert(head.Options.length == 1);
assert(head.Options[0].Type == ConnectionToken.Close);
}
unittest
{
Response rl = parseResponse("Pragma: no-cache, test=test234\r\n");
assert(rl.Type == ResponseType.Header);
assert(rl.Key == "Pragma");
PragmaHeader head = parseHeaderValue!(PragmaHeader)(rl.Value);
assert(head.Directives.length == 2);
assert(head.Directives[0].Type == PragmaDirective.NoCache);
assert(head.Directives[1].Type == PragmaDirective.Extension);
assert(head.Directives[1].ExtensionKey == "test");
assert(head.Directives[1].ExtensionValue == "test234");
}
unittest
{
Response rl = parseResponse("Transfer-Encoding: chunked;test=abcd\r\n");
assert(rl.Type == ResponseType.Header);
assert(rl.Key == "Transfer-Encoding");
TransferEncodingHeader head = parseHeaderValue!(TransferEncodingHeader)(rl.Value);
assert(head.Codings.length == 1);
assert(head.Codings[0].Type == TransferCoding.Chunked);
head = parseHeaderValue!(TransferEncodingHeader)("chunked; test = abcd , other ; test = value ; abcd = def");
assert(head.Codings.length == 2);
assert(head.Codings[0].Type == TransferCoding.Chunked);
assert(head.Codings[1].Type == TransferCoding.Custom);
assert(head.Codings[1].Raw == "other");
assert(head.Codings[0].Parameters == ["test": "abcd"]);
assert(head.Codings[1].Parameters == ["test": "value", "abcd": "def"]);
}
unittest
{
Response rl = parseResponse("Transfer-Encoding: chunked\r\n");
assert(rl.Type == ResponseType.Header);
assert(rl.Key == "Transfer-Encoding");
TransferEncodingHeader head = parseHeaderValue!(TransferEncodingHeader)(rl.Value);
assert(head.Codings.length == 1);
assert(head.Codings[0].Type == TransferCoding.Chunked);
}
unittest
{
Response rl = parseResponse("Upgrade: HTTP/2.0, SHTTP/1.3, IRC/6.9, RTA/x11\r\n");
assert(rl.Type == ResponseType.Header);
assert(rl.Key == "Upgrade");
UpgradeHeader prod = parseHeaderValue!(UpgradeHeader)(rl.Value);
assert(prod.Products.length == 4);
assert(prod.Products[0].Name == "HTTP");
assert(prod.Products[0].Version == "2.0");
assert(prod.Products[1].Name == "SHTTP");
assert(prod.Products[1].Version == "1.3");
assert(prod.Products[2].Name == "IRC");
assert(prod.Products[2].Version == "6.9");
assert(prod.Products[3].Name == "RTA");
assert(prod.Products[3].Version == "x11");
}
unittest
{
Response rl = parseResponse("Age: 123456789\r\n");
assert(rl.Type == ResponseType.Header);
assert(rl.Key == "Age");
auto age = parseHeaderValue!(AgeHeader)(rl.Value);
assert(age.Age == 123456789);
}
unittest
{
Response rl = parseResponse("ETag: W/\"abcdefg\"\r\n");
assert(rl.Type == ResponseType.Header);
assert(rl.Key == "ETag");
auto tag = parseHeaderValue!(ETagHeader)(rl.Value);
assert(tag.Tag.Weak == true);
assert(tag.Tag.Value == "abcdefg");
tag = parseHeaderValue!(ETagHeader)("\"abcdef\"");
assert(tag.Tag.Weak == false);
assert(tag.Tag.Value == "abcdef");
}
unittest
{
Response rl = parseResponse("Retry-After: Fri, 31 Dec 1999 23:59:59 GMT\r\n");
assert(rl.Type == ResponseType.Header);
assert(rl.Key == "Retry-After");
RetryAfterHeader ret = parseHeaderValue!(RetryAfterHeader)(rl.Value);
assert(ret.useTime);
assert(ret.Date == SysTime(DateTime(1999, 12, 31, 23, 59, 59), UTC()));
ret = parseHeaderValue!(RetryAfterHeader)("120");
assert(!ret.useTime);
assert(ret.Delta == 120);
}
unittest
{
Response rl = parseResponse("Content-Encoding: gzip, deflate, custom\r\n");
assert(rl.Type == ResponseType.Header);
assert(rl.Key == "Content-Encoding");
auto cc = parseHeaderValue!(ContentEncodingHeader)(rl.Value);
assert(cc.Codings.length == 3);
assert(cc.Codings[0].Type == ContentCoding.Gzip);
assert(cc.Codings[1].Type == ContentCoding.Deflate);
assert(cc.Codings[2].Type == ContentCoding.Other);
assert(cc.Codings[2].Raw == "custom");
}
unittest
{
Response rl = parseResponse("Content-Language: da, mi, en-US\r\n");
assert(rl.Type == ResponseType.Header);
assert(rl.Key == "Content-Language");
auto lang = parseHeaderValue!(ContentLanguageHeader)(rl.Value);
assert (lang.Tags[0].Main == "da");
assert (lang.Tags[1].Main == "mi");
assert (lang.Tags[2].Main == "en");
assert (lang.Tags[2].Sub == "US");
}
unittest
{
Response rl = parseResponse("Content-Length: 348\r\n");
assert(rl.Type == ResponseType.Header);
assert(rl.Key == "Content-Length");
auto length = parseHeaderValue!(ContentLengthHeader)(rl.Value);
assert (length.Length == 348);
}
unittest
{
Response rl = parseResponse("Content-MD5: Q2hlY2sgSW50ZWdyaXR5IQ==\r\n");
assert(rl.Type == ResponseType.Header);
assert(rl.Key == "Content-MD5");
auto md5 = parseHeaderValue!(ContentMD5Header)(rl.Value);
assert(md5.MD5 == [67, 104, 101, 99, 107, 32, 73, 110, 116, 101, 103, 114, 105, 116, 121, 33]);
}
unittest
{
Response rl = parseResponse("Content-Range: bytes 21010-47021/47022\r\n");
assert(rl.Type == ResponseType.Header);
assert(rl.Key == "Content-Range");
auto range = parseHeaderValue!(ContentRangeHeader)(rl.Value);
assert(range.Range.From.Value == 21010);
assert(range.Range.From.Unknown == false);
assert(range.Range.To.Value == 47021);
assert(range.Range.To.Unknown == false);
assert(range.Range.Length.Value == 47022);
assert(range.Range.Length.Unknown == false);
range = parseHeaderValue!(ContentRangeHeader)("bytes */*");
assert(range.Range.From.Unknown == true);
assert(range.Range.To.Unknown == true);
assert(range.Range.Length.Unknown == true);
}
unittest
{
Response rl = parseResponse("Content-Type: text/html; charset=utf-8\r\n");
assert(rl.Type == ResponseType.Header);
assert(rl.Key == "Content-Type");
auto type = parseHeaderValue!(ContentTypeHeader)(rl.Value);
assert(type.Type.Type == "text");
assert(type.Type.SubType == "html");
assert(type.Type.Parameters == ["charset": "utf-8"]);
}
/*
* Parser/Formatter helper functions & types
*/
/**
* This exception is thrown if an error occurs when parsing a header
*/
public class ParserException : Exception
{
public:
///The original input string which should have been parsed
string Input;
///The position in the input string where the error occured
uint Position;
///
this(string input, uint pos, string msg = "")
{
Input = input;
Position = pos;
string message = format("An error occured in the HTTP parser:%s\n" ~
"*Input:\t'%s'\n*Position:\t%s", msg, replace(replace(input,
"\r", "\\r"), "\n", "\\n"), pos);
super(message);
}
}
/**
* Subclass of ParserException for the special case when the passed
* input was correct, but more input was expected.
*/
public class InsufficientInputException : ParserException
{
///
public this(string input)
{
super(input, input.length, "Insufficient input");
}
}
/**
* Parses a response line and returns a $(D Response) struct
*
* Examples:
* ---------------------------------------
* auto line = parseResponse("HTTP/1.2 200 OK\r\n");
* assert(line.Type == ResponseType.StatusLine);
* assert(line.Line == "HTTP/1.2 200 OK\r\n");
* ---------------------------------------
*
* ---------------------------------------
* auto line = parseResponse("\r\n");
* assert(line.Type == ResponseType.Empty);
* ---------------------------------------
*
* ---------------------------------------
* auto line = parseResponse("Server: Apache/1.3.29 (Unix) PHP/4.3.4\r\n");
* assert(line.Type == ResponseType.Header);
* assert(line.Key == "Server");
* assert(line.Value == "Apache/1.3.29 (Unix) PHP/4.3.4");
* ---------------------------------------
*/
Response parseResponse(string line)
{
Response rline;
const (char)* hstart;
mixin(initParser!());
#line 3940 "http.d"
{
cs = parseResponse_start;
}
#line 3945 "http.d"
{
if ( p == pe )
goto _test_eof;
switch ( cs )
{
case 1:
switch( (*p) ) {
case 13u: goto st2;
case 33u: goto tr2;
case 72u: goto tr3;
case 124u: goto tr2;
case 126u: goto tr2;
default: break;
}
if ( (*p) < 45u ) {
if ( (*p) > 39u ) {
if ( 42u <= (*p) && (*p) <= 43u )
goto tr2;
} else if ( (*p) >= 35u )
goto tr2;
} else if ( (*p) > 46u ) {
if ( (*p) < 65u ) {
if ( 48u <= (*p) && (*p) <= 57u )
goto tr2;
} else if ( (*p) > 90u ) {
if ( 94u <= (*p) && (*p) <= 122u )
goto tr2;
} else
goto tr2;
} else
goto tr2;
goto st0;
st0:
cs = 0;
goto _out;
st2:
if ( ++p == pe )
goto _test_eof2;
case 2:
if ( (*p) == 10u )
goto tr4;
goto st0;
tr4:
#line 3078 "http.d.rl"
{{
rline.Type = ResponseType.Empty;
}}
goto st25;
st25:
if ( ++p == pe )
goto _test_eof25;
case 25:
#line 3998 "http.d"
goto st0;
tr2:
#line 3081 "http.d.rl"
{{
rline.Type = ResponseType.Header;
hstart = p;
}}
goto st3;
st3:
if ( ++p == pe )
goto _test_eof3;
case 3:
#line 4011 "http.d"
switch( (*p) ) {
case 9u: goto tr5;
case 13u: goto tr6;
case 32u: goto tr5;
case 33u: goto st3;
case 58u: goto tr8;
case 124u: goto st3;
case 126u: goto st3;
default: break;
}
if ( (*p) < 45u ) {
if ( (*p) > 39u ) {
if ( 42u <= (*p) && (*p) <= 43u )
goto st3;
} else if ( (*p) >= 35u )
goto st3;
} else if ( (*p) > 46u ) {
if ( (*p) < 65u ) {
if ( 48u <= (*p) && (*p) <= 57u )
goto st3;
} else if ( (*p) > 90u ) {
if ( 94u <= (*p) && (*p) <= 122u )
goto st3;
} else
goto st3;
} else
goto st3;
goto st0;
tr5:
#line 3085 "http.d.rl"
{{
rline.Key = line[(hstart - line.ptr) .. (p - line.ptr)];
}}
goto st4;
st4:
if ( ++p == pe )
goto _test_eof4;
case 4:
#line 4050 "http.d"
switch( (*p) ) {
case 9u: goto st4;
case 13u: goto st5;
case 32u: goto st4;
case 58u: goto st7;
default: break;
}
goto st0;
tr6:
#line 3085 "http.d.rl"
{{
rline.Key = line[(hstart - line.ptr) .. (p - line.ptr)];
}}
goto st5;
st5:
if ( ++p == pe )
goto _test_eof5;
case 5:
#line 4069 "http.d"
if ( (*p) == 10u )
goto st6;
goto st0;
st6:
if ( ++p == pe )
goto _test_eof6;
case 6:
switch( (*p) ) {
case 9u: goto st4;
case 32u: goto st4;
default: break;
}
goto st0;
tr13:
#line 3081 "http.d.rl"
{{
rline.Type = ResponseType.Header;
hstart = p;
}}
goto st7;
tr8:
#line 3085 "http.d.rl"
{{
rline.Key = line[(hstart - line.ptr) .. (p - line.ptr)];
}}
goto st7;
st7:
if ( ++p == pe )
goto _test_eof7;
case 7:
#line 4100 "http.d"
switch( (*p) ) {
case 9u: goto tr13;
case 13u: goto tr14;
case 32u: goto tr13;
case 127u: goto st0;
default: break;
}
if ( (*p) <= 31u )
goto st0;
goto tr15;
tr14:
#line 3081 "http.d.rl"
{{
rline.Type = ResponseType.Header;
hstart = p;
}}
#line 3088 "http.d.rl"
{{
rline.Value = line[(hstart - line.ptr) .. (p - line.ptr)];
}}
goto st8;
st8:
if ( ++p == pe )
goto _test_eof8;
case 8:
#line 4126 "http.d"
if ( (*p) == 10u )
goto st26;
goto st0;
st26:
if ( ++p == pe )
goto _test_eof26;
case 26:
switch( (*p) ) {
case 9u: goto st7;
case 32u: goto st7;
default: break;
}
goto st0;
tr15:
#line 3081 "http.d.rl"
{{
rline.Type = ResponseType.Header;
hstart = p;
}}
goto st9;
st9:
if ( ++p == pe )
goto _test_eof9;
case 9:
#line 4151 "http.d"
switch( (*p) ) {
case 13u: goto tr18;
case 127u: goto st0;
default: break;
}
if ( (*p) > 8u ) {
if ( 10u <= (*p) && (*p) <= 31u )
goto st0;
} else
goto st0;
goto st9;
tr18:
#line 3088 "http.d.rl"
{{
rline.Value = line[(hstart - line.ptr) .. (p - line.ptr)];
}}
goto st10;
st10:
if ( ++p == pe )
goto _test_eof10;
case 10:
#line 4173 "http.d"
if ( (*p) == 10u )
goto st27;
goto st0;
st27:
if ( ++p == pe )
goto _test_eof27;
case 27:
switch( (*p) ) {
case 9u: goto st9;
case 32u: goto st9;
default: break;
}
goto st0;
tr3:
#line 3081 "http.d.rl"
{{
rline.Type = ResponseType.Header;
hstart = p;
}}
goto st11;
st11:
if ( ++p == pe )
goto _test_eof11;
case 11:
#line 4198 "http.d"
switch( (*p) ) {
case 9u: goto tr5;
case 13u: goto tr6;
case 32u: goto tr5;
case 33u: goto st3;
case 58u: goto tr8;
case 84u: goto st12;
case 124u: goto st3;
case 126u: goto st3;
default: break;
}
if ( (*p) < 45u ) {
if ( (*p) > 39u ) {
if ( 42u <= (*p) && (*p) <= 43u )
goto st3;
} else if ( (*p) >= 35u )
goto st3;
} else if ( (*p) > 46u ) {
if ( (*p) < 65u ) {
if ( 48u <= (*p) && (*p) <= 57u )
goto st3;
} else if ( (*p) > 90u ) {
if ( 94u <= (*p) && (*p) <= 122u )
goto st3;
} else
goto st3;
} else
goto st3;
goto st0;
st12:
if ( ++p == pe )
goto _test_eof12;
case 12:
switch( (*p) ) {
case 9u: goto tr5;
case 13u: goto tr6;
case 32u: goto tr5;
case 33u: goto st3;
case 58u: goto tr8;
case 84u: goto st13;
case 124u: goto st3;
case 126u: goto st3;
default: break;
}
if ( (*p) < 45u ) {
if ( (*p) > 39u ) {
if ( 42u <= (*p) && (*p) <= 43u )
goto st3;
} else if ( (*p) >= 35u )
goto st3;
} else if ( (*p) > 46u ) {
if ( (*p) < 65u ) {
if ( 48u <= (*p) && (*p) <= 57u )
goto st3;
} else if ( (*p) > 90u ) {
if ( 94u <= (*p) && (*p) <= 122u )
goto st3;
} else
goto st3;
} else
goto st3;
goto st0;
st13:
if ( ++p == pe )
goto _test_eof13;
case 13:
switch( (*p) ) {
case 9u: goto tr5;
case 13u: goto tr6;
case 32u: goto tr5;
case 33u: goto st3;
case 58u: goto tr8;
case 80u: goto st14;
case 124u: goto st3;
case 126u: goto st3;
default: break;
}
if ( (*p) < 45u ) {
if ( (*p) > 39u ) {
if ( 42u <= (*p) && (*p) <= 43u )
goto st3;
} else if ( (*p) >= 35u )
goto st3;
} else if ( (*p) > 46u ) {
if ( (*p) < 65u ) {
if ( 48u <= (*p) && (*p) <= 57u )
goto st3;
} else if ( (*p) > 90u ) {
if ( 94u <= (*p) && (*p) <= 122u )
goto st3;
} else
goto st3;
} else
goto st3;
goto st0;
st14:
if ( ++p == pe )
goto _test_eof14;
case 14:
switch( (*p) ) {
case 9u: goto tr5;
case 13u: goto tr6;
case 32u: goto tr5;
case 33u: goto st3;
case 47u: goto st15;
case 58u: goto tr8;
case 124u: goto st3;
case 126u: goto st3;
default: break;
}
if ( (*p) < 45u ) {
if ( (*p) > 39u ) {
if ( 42u <= (*p) && (*p) <= 43u )
goto st3;
} else if ( (*p) >= 35u )
goto st3;
} else if ( (*p) > 57u ) {
if ( (*p) > 90u ) {
if ( 94u <= (*p) && (*p) <= 122u )
goto st3;
} else if ( (*p) >= 65u )
goto st3;
} else
goto st3;
goto st0;
st15:
if ( ++p == pe )
goto _test_eof15;
case 15:
if ( 48u <= (*p) && (*p) <= 57u )
goto st16;
goto st0;
st16:
if ( ++p == pe )
goto _test_eof16;
case 16:
if ( (*p) == 46u )
goto st17;
if ( 48u <= (*p) && (*p) <= 57u )
goto st16;
goto st0;
st17:
if ( ++p == pe )
goto _test_eof17;
case 17:
if ( 48u <= (*p) && (*p) <= 57u )
goto st18;
goto st0;
st18:
if ( ++p == pe )
goto _test_eof18;
case 18:
if ( (*p) == 32u )
goto st19;
if ( 48u <= (*p) && (*p) <= 57u )
goto st18;
goto st0;
st19:
if ( ++p == pe )
goto _test_eof19;
case 19:
if ( 48u <= (*p) && (*p) <= 57u )
goto st20;
goto st0;
st20:
if ( ++p == pe )
goto _test_eof20;
case 20:
if ( 48u <= (*p) && (*p) <= 57u )
goto st21;
goto st0;
st21:
if ( ++p == pe )
goto _test_eof21;
case 21:
if ( 48u <= (*p) && (*p) <= 57u )
goto st22;
goto st0;
st22:
if ( ++p == pe )
goto _test_eof22;
case 22:
if ( (*p) == 32u )
goto st23;
goto st0;
st23:
if ( ++p == pe )
goto _test_eof23;
case 23:
switch( (*p) ) {
case 13u: goto st24;
case 127u: goto st0;
default: break;
}
if ( (*p) > 8u ) {
if ( 10u <= (*p) && (*p) <= 31u )
goto st0;
} else
goto st0;
goto st23;
st24:
if ( ++p == pe )
goto _test_eof24;
case 24:
if ( (*p) == 10u )
goto tr33;
goto st0;
tr33:
#line 3074 "http.d.rl"
{{
rline.Type = ResponseType.StatusLine;
rline.Line = line[0 .. (p - line.ptr + 1)];
}}
goto st28;
st28:
if ( ++p == pe )
goto _test_eof28;
case 28:
#line 4417 "http.d"
switch( (*p) ) {
case 9u: goto st23;
case 32u: goto st23;
default: break;
}
goto st0;
default: break;
}
_test_eof2: cs = 2; goto _test_eof;
_test_eof25: cs = 25; goto _test_eof;
_test_eof3: cs = 3; goto _test_eof;
_test_eof4: cs = 4; goto _test_eof;
_test_eof5: cs = 5; goto _test_eof;
_test_eof6: cs = 6; goto _test_eof;
_test_eof7: cs = 7; goto _test_eof;
_test_eof8: cs = 8; goto _test_eof;
_test_eof26: cs = 26; goto _test_eof;
_test_eof9: cs = 9; goto _test_eof;
_test_eof10: cs = 10; goto _test_eof;
_test_eof27: cs = 27; goto _test_eof;
_test_eof11: cs = 11; goto _test_eof;
_test_eof12: cs = 12; goto _test_eof;
_test_eof13: cs = 13; goto _test_eof;
_test_eof14: cs = 14; goto _test_eof;
_test_eof15: cs = 15; goto _test_eof;
_test_eof16: cs = 16; goto _test_eof;
_test_eof17: cs = 17; goto _test_eof;
_test_eof18: cs = 18; goto _test_eof;
_test_eof19: cs = 19; goto _test_eof;
_test_eof20: cs = 20; goto _test_eof;
_test_eof21: cs = 21; goto _test_eof;
_test_eof22: cs = 22; goto _test_eof;
_test_eof23: cs = 23; goto _test_eof;
_test_eof24: cs = 24; goto _test_eof;
_test_eof28: cs = 28; goto _test_eof;
_test_eof: {}
_out: {}
}
#line 2357 "http.d.rl"
mixin(finishParser!"parseResponse");
return rline;
}
/**
* Check if input is a quoted string
*/
bool isQuotedString(string val)
{
if(val.length < 2)
return false;
if(val[0] != '"' || val[$ - 1] != '"')
return false;
return true;
}
/**
* Quote the input string
*/
string quote(string val)
{
auto result = appender!string;
foreach(c; val)
{
if(c == '"')
result.put('\\');
result.put(c);
}
return '"' ~ result.data ~ '"';
}
/**
* Write a parameter in a parameter list
*/
void writeParameter(Appender!string writer, string key, string value)
{
writer.put(";");
assert(isToken(key));
writer.put(key);
if(value != "")
{
writer.put("=");
if(isToken(value))
writer.put(value);
else
writer.put(quote(value));
}
}
/**
* Check if string is a token
*/
bool isToken(string line)
{
const(char)* start;
mixin(initParser!());
#line 4519 "http.d"
{
cs = isToken_start;
}
#line 4524 "http.d"
{
if ( p == pe )
goto _test_eof;
switch ( cs )
{
case 1:
switch( (*p) ) {
case 33u: goto st2;
case 124u: goto st2;
case 126u: goto st2;
default: break;
}
if ( (*p) < 45u ) {
if ( (*p) > 39u ) {
if ( 42u <= (*p) && (*p) <= 43u )
goto st2;
} else if ( (*p) >= 35u )
goto st2;
} else if ( (*p) > 46u ) {
if ( (*p) < 65u ) {
if ( 48u <= (*p) && (*p) <= 57u )
goto st2;
} else if ( (*p) > 90u ) {
if ( 94u <= (*p) && (*p) <= 122u )
goto st2;
} else
goto st2;
} else
goto st2;
goto st0;
st0:
cs = 0;
goto _out;
st2:
if ( ++p == pe )
goto _test_eof2;
case 2:
switch( (*p) ) {
case 33u: goto st2;
case 124u: goto st2;
case 126u: goto st2;
default: break;
}
if ( (*p) < 45u ) {
if ( (*p) > 39u ) {
if ( 42u <= (*p) && (*p) <= 43u )
goto st2;
} else if ( (*p) >= 35u )
goto st2;
} else if ( (*p) > 46u ) {
if ( (*p) < 65u ) {
if ( 48u <= (*p) && (*p) <= 57u )
goto st2;
} else if ( (*p) > 90u ) {
if ( 94u <= (*p) && (*p) <= 122u )
goto st2;
} else
goto st2;
} else
goto st2;
goto st0;
default: break;
}
_test_eof2: cs = 2; goto _test_eof;
_test_eof: {}
_out: {}
}
#line 2421 "http.d.rl"
mixin(finishParser!"isToken");
return true;
}
/**
* Check if string is TEXT*
*/
bool isText(string line)
{
const(char)* start;
mixin(initParser!());
#line 4608 "http.d"
{
cs = isText_start;
}
#line 4613 "http.d"
{
if ( p == pe )
goto _test_eof;
switch ( cs )
{
st3:
if ( ++p == pe )
goto _test_eof3;
case 3:
switch( (*p) ) {
case 13u: goto st1;
case 127u: goto st0;
default: break;
}
if ( (*p) > 8u ) {
if ( 10u <= (*p) && (*p) <= 31u )
goto st0;
} else
goto st0;
goto st3;
st0:
cs = 0;
goto _out;
st1:
if ( ++p == pe )
goto _test_eof1;
case 1:
if ( (*p) == 10u )
goto st2;
goto st0;
st2:
if ( ++p == pe )
goto _test_eof2;
case 2:
switch( (*p) ) {
case 9u: goto st3;
case 32u: goto st3;
default: break;
}
goto st0;
default: break;
}
_test_eof3: cs = 3; goto _test_eof;
_test_eof1: cs = 1; goto _test_eof;
_test_eof2: cs = 2; goto _test_eof;
_test_eof: {}
_out: {}
}
#line 2438 "http.d.rl"
mixin(finishParser!"isText");
return true;
}
/**
* Parses a http comma list into a list of strings
*/
string[] parseCommaList(string line)
{
string[] list;
const(char)* start;
mixin(initParser!());
#line 4679 "http.d"
{
cs = parseCommaList_start;
}
#line 4684 "http.d"
{
if ( p == pe )
goto _test_eof;
switch ( cs )
{
tr11:
#line 3166 "http.d.rl"
{{
start = p;
}}
goto st11;
st11:
if ( ++p == pe )
goto _test_eof11;
case 11:
#line 4700 "http.d"
switch( (*p) ) {
case 9u: goto tr11;
case 13u: goto tr12;
case 32u: goto tr11;
case 44u: goto tr14;
case 92u: goto tr15;
case 127u: goto st0;
default: break;
}
if ( (*p) <= 31u )
goto st0;
goto tr13;
st0:
cs = 0;
goto _out;
tr12:
#line 3166 "http.d.rl"
{{
start = p;
}}
goto st1;
st1:
if ( ++p == pe )
goto _test_eof1;
case 1:
#line 4726 "http.d"
if ( (*p) == 10u )
goto st2;
goto st0;
st2:
if ( ++p == pe )
goto _test_eof2;
case 2:
switch( (*p) ) {
case 9u: goto st11;
case 32u: goto st11;
default: break;
}
goto st0;
tr13:
#line 3166 "http.d.rl"
{{
start = p;
}}
goto st12;
st12:
if ( ++p == pe )
goto _test_eof12;
case 12:
#line 4750 "http.d"
switch( (*p) ) {
case 13u: goto st3;
case 44u: goto tr17;
case 92u: goto st15;
case 127u: goto st0;
default: break;
}
if ( (*p) > 8u ) {
if ( 10u <= (*p) && (*p) <= 31u )
goto st0;
} else
goto st0;
goto st12;
st3:
if ( ++p == pe )
goto _test_eof3;
case 3:
if ( (*p) == 10u )
goto st4;
goto st0;
st4:
if ( ++p == pe )
goto _test_eof4;
case 4:
switch( (*p) ) {
case 9u: goto st12;
case 32u: goto st12;
default: break;
}
goto st0;
tr14:
#line 3166 "http.d.rl"
{{
start = p;
}}
#line 3159 "http.d.rl"
{{
if(start != p)
{
list ~= stripr(line[(start - line.ptr)
.. (p - line.ptr)]);
}
}}
goto st13;
tr19:
#line 3166 "http.d.rl"
{{
start = p;
}}
goto st13;
tr17:
#line 3159 "http.d.rl"
{{
if(start != p)
{
list ~= stripr(line[(start - line.ptr)
.. (p - line.ptr)]);
}
}}
goto st13;
tr26:
#line 3159 "http.d.rl"
{{
if(start != p)
{
list ~= stripr(line[(start - line.ptr)
.. (p - line.ptr)]);
}
}}
#line 3166 "http.d.rl"
{{
start = p;
}}
goto st13;
st13:
if ( ++p == pe )
goto _test_eof13;
case 13:
#line 4829 "http.d"
switch( (*p) ) {
case 9u: goto tr19;
case 13u: goto tr20;
case 32u: goto tr19;
case 44u: goto tr14;
case 127u: goto st0;
default: break;
}
if ( (*p) <= 31u )
goto st0;
goto tr21;
tr20:
#line 3166 "http.d.rl"
{{
start = p;
}}
goto st5;
st5:
if ( ++p == pe )
goto _test_eof5;
case 5:
#line 4851 "http.d"
if ( (*p) == 10u )
goto st6;
goto st0;
st6:
if ( ++p == pe )
goto _test_eof6;
case 6:
switch( (*p) ) {
case 9u: goto st13;
case 32u: goto st13;
default: break;
}
goto st0;
tr21:
#line 3166 "http.d.rl"
{{
start = p;
}}
goto st14;
st14:
if ( ++p == pe )
goto _test_eof14;
case 14:
#line 4875 "http.d"
switch( (*p) ) {
case 13u: goto st7;
case 44u: goto tr17;
case 127u: goto st0;
default: break;
}
if ( (*p) > 8u ) {
if ( 10u <= (*p) && (*p) <= 31u )
goto st0;
} else
goto st0;
goto st14;
st7:
if ( ++p == pe )
goto _test_eof7;
case 7:
if ( (*p) == 10u )
goto st8;
goto st0;
st8:
if ( ++p == pe )
goto _test_eof8;
case 8:
switch( (*p) ) {
case 9u: goto st14;
case 32u: goto st14;
default: break;
}
goto st0;
tr15:
#line 3166 "http.d.rl"
{{
start = p;
}}
goto st15;
st15:
if ( ++p == pe )
goto _test_eof15;
case 15:
#line 4915 "http.d"
switch( (*p) ) {
case 13u: goto st3;
case 44u: goto tr23;
case 92u: goto st15;
case 127u: goto st0;
default: break;
}
if ( (*p) > 8u ) {
if ( 10u <= (*p) && (*p) <= 31u )
goto st0;
} else
goto st0;
goto st12;
tr24:
#line 3166 "http.d.rl"
{{
start = p;
}}
goto st16;
tr23:
#line 3159 "http.d.rl"
{{
if(start != p)
{
list ~= stripr(line[(start - line.ptr)
.. (p - line.ptr)]);
}
}}
goto st16;
st16:
if ( ++p == pe )
goto _test_eof16;
case 16:
#line 4949 "http.d"
switch( (*p) ) {
case 9u: goto tr24;
case 13u: goto tr25;
case 32u: goto tr24;
case 44u: goto tr26;
case 92u: goto tr15;
case 127u: goto st0;
default: break;
}
if ( (*p) <= 31u )
goto st0;
goto tr13;
tr25:
#line 3166 "http.d.rl"
{{
start = p;
}}
goto st9;
st9:
if ( ++p == pe )
goto _test_eof9;
case 9:
#line 4972 "http.d"
if ( (*p) == 10u )
goto st10;
goto st0;
st10:
if ( ++p == pe )
goto _test_eof10;
case 10:
switch( (*p) ) {
case 9u: goto st16;
case 32u: goto st16;
default: break;
}
goto st0;
default: break;
}
_test_eof11: cs = 11; goto _test_eof;
_test_eof1: cs = 1; goto _test_eof;
_test_eof2: cs = 2; goto _test_eof;
_test_eof12: cs = 12; goto _test_eof;
_test_eof3: cs = 3; goto _test_eof;
_test_eof4: cs = 4; goto _test_eof;
_test_eof13: cs = 13; goto _test_eof;
_test_eof5: cs = 5; goto _test_eof;
_test_eof6: cs = 6; goto _test_eof;
_test_eof14: cs = 14; goto _test_eof;
_test_eof7: cs = 7; goto _test_eof;
_test_eof8: cs = 8; goto _test_eof;
_test_eof15: cs = 15; goto _test_eof;
_test_eof16: cs = 16; goto _test_eof;
_test_eof9: cs = 9; goto _test_eof;
_test_eof10: cs = 10; goto _test_eof;
_test_eof: {}
if ( p == eof )
{
switch ( cs ) {
case 12:
case 14:
case 15:
#line 3159 "http.d.rl"
{{
if(start != p)
{
list ~= stripr(line[(start - line.ptr)
.. (p - line.ptr)]);
}
}}
break;
case 16:
#line 3159 "http.d.rl"
{{
if(start != p)
{
list ~= stripr(line[(start - line.ptr)
.. (p - line.ptr)]);
}
}}
#line 3166 "http.d.rl"
{{
start = p;
}}
break;
case 11:
case 13:
#line 3166 "http.d.rl"
{{
start = p;
}}
#line 3159 "http.d.rl"
{{
if(start != p)
{
list ~= stripr(line[(start - line.ptr)
.. (p - line.ptr)]);
}
}}
break;
#line 5050 "http.d"
default: break;
}
}
_out: {}
}
#line 2455 "http.d.rl"
mixin(finishParser!"parseCommaList");
return list;
}
/**
* Parses a http parameter list into a strings hash
*/
string[string] parseParameterList(string line)
{
string[string] list;
string key;
const(char)* start;
mixin(initParser!());
#line 5075 "http.d"
{
cs = parseParameterList_start;
}
#line 5080 "http.d"
{
if ( p == pe )
goto _test_eof;
switch ( cs )
{
case 32:
switch( (*p) ) {
case 9u: goto st1;
case 13u: goto st2;
case 32u: goto st1;
case 59u: goto st4;
default: break;
}
goto st0;
st0:
cs = 0;
goto _out;
tr46:
#line 3245 "http.d.rl"
{{
if(start != p)
{
list[key] = prepareValue(line[(start - line.ptr) .. (p - line.ptr)]);
}
}}
goto st1;
st1:
if ( ++p == pe )
goto _test_eof1;
case 1:
#line 5111 "http.d"
switch( (*p) ) {
case 9u: goto st1;
case 13u: goto st2;
case 32u: goto st1;
case 59u: goto st4;
default: break;
}
goto st0;
tr47:
#line 3245 "http.d.rl"
{{
if(start != p)
{
list[key] = prepareValue(line[(start - line.ptr) .. (p - line.ptr)]);
}
}}
goto st2;
st2:
if ( ++p == pe )
goto _test_eof2;
case 2:
#line 5133 "http.d"
if ( (*p) == 10u )
goto st3;
goto st0;
st3:
if ( ++p == pe )
goto _test_eof3;
case 3:
switch( (*p) ) {
case 9u: goto st1;
case 32u: goto st1;
default: break;
}
goto st0;
tr49:
#line 3245 "http.d.rl"
{{
if(start != p)
{
list[key] = prepareValue(line[(start - line.ptr) .. (p - line.ptr)]);
}
}}
goto st4;
st4:
if ( ++p == pe )
goto _test_eof4;
case 4:
#line 5160 "http.d"
switch( (*p) ) {
case 9u: goto st4;
case 13u: goto st5;
case 32u: goto st4;
case 33u: goto tr6;
case 124u: goto tr6;
case 126u: goto tr6;
default: break;
}
if ( (*p) < 45u ) {
if ( (*p) > 39u ) {
if ( 42u <= (*p) && (*p) <= 43u )
goto tr6;
} else if ( (*p) >= 35u )
goto tr6;
} else if ( (*p) > 46u ) {
if ( (*p) < 65u ) {
if ( 48u <= (*p) && (*p) <= 57u )
goto tr6;
} else if ( (*p) > 90u ) {
if ( 94u <= (*p) && (*p) <= 122u )
goto tr6;
} else
goto tr6;
} else
goto tr6;
goto st0;
st5:
if ( ++p == pe )
goto _test_eof5;
case 5:
if ( (*p) == 10u )
goto st6;
goto st0;
st6:
if ( ++p == pe )
goto _test_eof6;
case 6:
switch( (*p) ) {
case 9u: goto st4;
case 32u: goto st4;
default: break;
}
goto st0;
tr6:
#line 3257 "http.d.rl"
{{
start = p;
}}
goto st7;
st7:
if ( ++p == pe )
goto _test_eof7;
case 7:
#line 5215 "http.d"
switch( (*p) ) {
case 9u: goto tr8;
case 13u: goto tr9;
case 32u: goto tr8;
case 33u: goto st7;
case 61u: goto tr11;
case 124u: goto st7;
case 126u: goto st7;
default: break;
}
if ( (*p) < 45u ) {
if ( (*p) > 39u ) {
if ( 42u <= (*p) && (*p) <= 43u )
goto st7;
} else if ( (*p) >= 35u )
goto st7;
} else if ( (*p) > 46u ) {
if ( (*p) < 65u ) {
if ( 48u <= (*p) && (*p) <= 57u )
goto st7;
} else if ( (*p) > 90u ) {
if ( 94u <= (*p) && (*p) <= 122u )
goto st7;
} else
goto st7;
} else
goto st7;
goto st0;
tr8:
#line 3251 "http.d.rl"
{{
if(start != p)
{
key = line[(start - line.ptr) .. (p - line.ptr)];
}
}}
goto st8;
st8:
if ( ++p == pe )
goto _test_eof8;
case 8:
#line 5257 "http.d"
switch( (*p) ) {
case 9u: goto st8;
case 13u: goto st9;
case 32u: goto st8;
case 61u: goto st11;
default: break;
}
goto st0;
tr9:
#line 3251 "http.d.rl"
{{
if(start != p)
{
key = line[(start - line.ptr) .. (p - line.ptr)];
}
}}
goto st9;
st9:
if ( ++p == pe )
goto _test_eof9;
case 9:
#line 5279 "http.d"
if ( (*p) == 10u )
goto st10;
goto st0;
st10:
if ( ++p == pe )
goto _test_eof10;
case 10:
switch( (*p) ) {
case 9u: goto st8;
case 32u: goto st8;
default: break;
}
goto st0;
tr11:
#line 3251 "http.d.rl"
{{
if(start != p)
{
key = line[(start - line.ptr) .. (p - line.ptr)];
}
}}
goto st11;
st11:
if ( ++p == pe )
goto _test_eof11;
case 11:
#line 5306 "http.d"
switch( (*p) ) {
case 9u: goto st11;
case 13u: goto st12;
case 32u: goto st11;
case 34u: goto tr18;
case 124u: goto tr17;
case 126u: goto tr17;
default: break;
}
if ( (*p) < 45u ) {
if ( (*p) > 39u ) {
if ( 42u <= (*p) && (*p) <= 43u )
goto tr17;
} else if ( (*p) >= 33u )
goto tr17;
} else if ( (*p) > 46u ) {
if ( (*p) < 65u ) {
if ( 48u <= (*p) && (*p) <= 57u )
goto tr17;
} else if ( (*p) > 90u ) {
if ( 94u <= (*p) && (*p) <= 122u )
goto tr17;
} else
goto tr17;
} else
goto tr17;
goto st0;
st12:
if ( ++p == pe )
goto _test_eof12;
case 12:
if ( (*p) == 10u )
goto st13;
goto st0;
st13:
if ( ++p == pe )
goto _test_eof13;
case 13:
switch( (*p) ) {
case 9u: goto st11;
case 32u: goto st11;
default: break;
}
goto st0;
tr17:
#line 3257 "http.d.rl"
{{
start = p;
}}
goto st33;
st33:
if ( ++p == pe )
goto _test_eof33;
case 33:
#line 5361 "http.d"
switch( (*p) ) {
case 9u: goto tr46;
case 13u: goto tr47;
case 32u: goto tr46;
case 33u: goto st33;
case 59u: goto tr49;
case 124u: goto st33;
case 126u: goto st33;
default: break;
}
if ( (*p) < 45u ) {
if ( (*p) > 39u ) {
if ( 42u <= (*p) && (*p) <= 43u )
goto st33;
} else if ( (*p) >= 35u )
goto st33;
} else if ( (*p) > 46u ) {
if ( (*p) < 65u ) {
if ( 48u <= (*p) && (*p) <= 57u )
goto st33;
} else if ( (*p) > 90u ) {
if ( 94u <= (*p) && (*p) <= 122u )
goto st33;
} else
goto st33;
} else
goto st33;
goto st0;
tr18:
#line 3257 "http.d.rl"
{{
start = p;
}}
goto st14;
st14:
if ( ++p == pe )
goto _test_eof14;
case 14:
#line 5400 "http.d"
switch( (*p) ) {
case 13u: goto st15;
case 34u: goto st34;
case 92u: goto st17;
case 127u: goto st0;
default: break;
}
if ( (*p) > 8u ) {
if ( 10u <= (*p) && (*p) <= 31u )
goto st0;
} else
goto st0;
goto st14;
st15:
if ( ++p == pe )
goto _test_eof15;
case 15:
if ( (*p) == 10u )
goto st16;
goto st0;
st16:
if ( ++p == pe )
goto _test_eof16;
case 16:
switch( (*p) ) {
case 9u: goto st14;
case 32u: goto st14;
default: break;
}
goto st0;
st34:
if ( ++p == pe )
goto _test_eof34;
case 34:
switch( (*p) ) {
case 9u: goto tr46;
case 13u: goto tr47;
case 32u: goto tr46;
case 59u: goto tr49;
default: break;
}
goto st0;
st17:
if ( ++p == pe )
goto _test_eof17;
case 17:
switch( (*p) ) {
case 13u: goto st18;
case 34u: goto st35;
case 92u: goto st17;
default: break;
}
goto st14;
st18:
if ( ++p == pe )
goto _test_eof18;
case 18:
switch( (*p) ) {
case 10u: goto st16;
case 13u: goto st15;
case 34u: goto st34;
case 92u: goto st17;
case 127u: goto st0;
default: break;
}
if ( (*p) > 8u ) {
if ( 11u <= (*p) && (*p) <= 31u )
goto st0;
} else
goto st0;
goto st14;
tr44:
#line 3257 "http.d.rl"
{{
start = p;
}}
goto st35;
st35:
if ( ++p == pe )
goto _test_eof35;
case 35:
#line 5482 "http.d"
switch( (*p) ) {
case 9u: goto tr50;
case 13u: goto tr51;
case 32u: goto tr50;
case 34u: goto st34;
case 59u: goto tr52;
case 92u: goto st17;
case 127u: goto st0;
default: break;
}
if ( (*p) <= 31u )
goto st0;
goto st14;
tr50:
#line 3245 "http.d.rl"
{{
if(start != p)
{
list[key] = prepareValue(line[(start - line.ptr) .. (p - line.ptr)]);
}
}}
goto st19;
st19:
if ( ++p == pe )
goto _test_eof19;
case 19:
#line 5509 "http.d"
switch( (*p) ) {
case 9u: goto st19;
case 13u: goto st20;
case 32u: goto st19;
case 34u: goto st34;
case 59u: goto st22;
case 92u: goto st17;
case 127u: goto st0;
default: break;
}
if ( (*p) <= 31u )
goto st0;
goto st14;
tr51:
#line 3245 "http.d.rl"
{{
if(start != p)
{
list[key] = prepareValue(line[(start - line.ptr) .. (p - line.ptr)]);
}
}}
goto st20;
st20:
if ( ++p == pe )
goto _test_eof20;
case 20:
#line 5536 "http.d"
if ( (*p) == 10u )
goto st21;
goto st0;
st21:
if ( ++p == pe )
goto _test_eof21;
case 21:
switch( (*p) ) {
case 9u: goto st19;
case 32u: goto st19;
default: break;
}
goto st0;
tr52:
#line 3245 "http.d.rl"
{{
if(start != p)
{
list[key] = prepareValue(line[(start - line.ptr) .. (p - line.ptr)]);
}
}}
goto st22;
st22:
if ( ++p == pe )
goto _test_eof22;
case 22:
#line 5563 "http.d"
switch( (*p) ) {
case 9u: goto st22;
case 13u: goto st23;
case 32u: goto st22;
case 34u: goto st34;
case 92u: goto st17;
case 124u: goto tr32;
case 126u: goto tr32;
case 127u: goto st0;
default: break;
}
if ( (*p) < 45u ) {
if ( (*p) < 33u ) {
if ( (*p) <= 31u )
goto st0;
} else if ( (*p) > 39u ) {
if ( 42u <= (*p) && (*p) <= 43u )
goto tr32;
} else
goto tr32;
} else if ( (*p) > 46u ) {
if ( (*p) < 65u ) {
if ( 48u <= (*p) && (*p) <= 57u )
goto tr32;
} else if ( (*p) > 90u ) {
if ( 94u <= (*p) && (*p) <= 122u )
goto tr32;
} else
goto tr32;
} else
goto tr32;
goto st14;
st23:
if ( ++p == pe )
goto _test_eof23;
case 23:
if ( (*p) == 10u )
goto st24;
goto st0;
st24:
if ( ++p == pe )
goto _test_eof24;
case 24:
switch( (*p) ) {
case 9u: goto st22;
case 32u: goto st22;
default: break;
}
goto st0;
tr32:
#line 3257 "http.d.rl"
{{
start = p;
}}
goto st25;
st25:
if ( ++p == pe )
goto _test_eof25;
case 25:
#line 5623 "http.d"
switch( (*p) ) {
case 9u: goto tr34;
case 13u: goto tr35;
case 32u: goto tr34;
case 34u: goto st34;
case 61u: goto tr37;
case 92u: goto st17;
case 124u: goto st25;
case 126u: goto st25;
case 127u: goto st0;
default: break;
}
if ( (*p) < 45u ) {
if ( (*p) < 33u ) {
if ( (*p) <= 31u )
goto st0;
} else if ( (*p) > 39u ) {
if ( 42u <= (*p) && (*p) <= 43u )
goto st25;
} else
goto st25;
} else if ( (*p) > 46u ) {
if ( (*p) < 65u ) {
if ( 48u <= (*p) && (*p) <= 57u )
goto st25;
} else if ( (*p) > 90u ) {
if ( 94u <= (*p) && (*p) <= 122u )
goto st25;
} else
goto st25;
} else
goto st25;
goto st14;
tr34:
#line 3251 "http.d.rl"
{{
if(start != p)
{
key = line[(start - line.ptr) .. (p - line.ptr)];
}
}}
goto st26;
st26:
if ( ++p == pe )
goto _test_eof26;
case 26:
#line 5670 "http.d"
switch( (*p) ) {
case 9u: goto st26;
case 13u: goto st27;
case 32u: goto st26;
case 34u: goto st34;
case 61u: goto st29;
case 92u: goto st17;
case 127u: goto st0;
default: break;
}
if ( (*p) <= 31u )
goto st0;
goto st14;
tr35:
#line 3251 "http.d.rl"
{{
if(start != p)
{
key = line[(start - line.ptr) .. (p - line.ptr)];
}
}}
goto st27;
st27:
if ( ++p == pe )
goto _test_eof27;
case 27:
#line 5697 "http.d"
if ( (*p) == 10u )
goto st28;
goto st0;
st28:
if ( ++p == pe )
goto _test_eof28;
case 28:
switch( (*p) ) {
case 9u: goto st26;
case 32u: goto st26;
default: break;
}
goto st0;
tr37:
#line 3251 "http.d.rl"
{{
if(start != p)
{
key = line[(start - line.ptr) .. (p - line.ptr)];
}
}}
goto st29;
st29:
if ( ++p == pe )
goto _test_eof29;
case 29:
#line 5724 "http.d"
switch( (*p) ) {
case 9u: goto st29;
case 13u: goto st30;
case 32u: goto st29;
case 34u: goto tr44;
case 92u: goto st17;
case 124u: goto tr43;
case 126u: goto tr43;
case 127u: goto st0;
default: break;
}
if ( (*p) < 45u ) {
if ( (*p) < 33u ) {
if ( (*p) <= 31u )
goto st0;
} else if ( (*p) > 39u ) {
if ( 42u <= (*p) && (*p) <= 43u )
goto tr43;
} else
goto tr43;
} else if ( (*p) > 46u ) {
if ( (*p) < 65u ) {
if ( 48u <= (*p) && (*p) <= 57u )
goto tr43;
} else if ( (*p) > 90u ) {
if ( 94u <= (*p) && (*p) <= 122u )
goto tr43;
} else
goto tr43;
} else
goto tr43;
goto st14;
st30:
if ( ++p == pe )
goto _test_eof30;
case 30:
if ( (*p) == 10u )
goto st31;
goto st0;
st31:
if ( ++p == pe )
goto _test_eof31;
case 31:
switch( (*p) ) {
case 9u: goto st29;
case 32u: goto st29;
default: break;
}
goto st0;
tr43:
#line 3257 "http.d.rl"
{{
start = p;
}}
goto st36;
st36:
if ( ++p == pe )
goto _test_eof36;
case 36:
#line 5784 "http.d"
switch( (*p) ) {
case 9u: goto tr50;
case 13u: goto tr51;
case 32u: goto tr50;
case 34u: goto st34;
case 59u: goto tr52;
case 92u: goto st17;
case 124u: goto st36;
case 126u: goto st36;
case 127u: goto st0;
default: break;
}
if ( (*p) < 45u ) {
if ( (*p) < 33u ) {
if ( (*p) <= 31u )
goto st0;
} else if ( (*p) > 39u ) {
if ( 42u <= (*p) && (*p) <= 43u )
goto st36;
} else
goto st36;
} else if ( (*p) > 46u ) {
if ( (*p) < 65u ) {
if ( 48u <= (*p) && (*p) <= 57u )
goto st36;
} else if ( (*p) > 90u ) {
if ( 94u <= (*p) && (*p) <= 122u )
goto st36;
} else
goto st36;
} else
goto st36;
goto st14;
default: break;
}
_test_eof1: cs = 1; goto _test_eof;
_test_eof2: cs = 2; goto _test_eof;
_test_eof3: cs = 3; goto _test_eof;
_test_eof4: cs = 4; goto _test_eof;
_test_eof5: cs = 5; goto _test_eof;
_test_eof6: cs = 6; goto _test_eof;
_test_eof7: cs = 7; goto _test_eof;
_test_eof8: cs = 8; goto _test_eof;
_test_eof9: cs = 9; goto _test_eof;
_test_eof10: cs = 10; goto _test_eof;
_test_eof11: cs = 11; goto _test_eof;
_test_eof12: cs = 12; goto _test_eof;
_test_eof13: cs = 13; goto _test_eof;
_test_eof33: cs = 33; goto _test_eof;
_test_eof14: cs = 14; goto _test_eof;
_test_eof15: cs = 15; goto _test_eof;
_test_eof16: cs = 16; goto _test_eof;
_test_eof34: cs = 34; goto _test_eof;
_test_eof17: cs = 17; goto _test_eof;
_test_eof18: cs = 18; goto _test_eof;
_test_eof35: cs = 35; goto _test_eof;
_test_eof19: cs = 19; goto _test_eof;
_test_eof20: cs = 20; goto _test_eof;
_test_eof21: cs = 21; goto _test_eof;
_test_eof22: cs = 22; goto _test_eof;
_test_eof23: cs = 23; goto _test_eof;
_test_eof24: cs = 24; goto _test_eof;
_test_eof25: cs = 25; goto _test_eof;
_test_eof26: cs = 26; goto _test_eof;
_test_eof27: cs = 27; goto _test_eof;
_test_eof28: cs = 28; goto _test_eof;
_test_eof29: cs = 29; goto _test_eof;
_test_eof30: cs = 30; goto _test_eof;
_test_eof31: cs = 31; goto _test_eof;
_test_eof36: cs = 36; goto _test_eof;
_test_eof: {}
if ( p == eof )
{
switch ( cs ) {
case 33:
case 34:
case 35:
case 36:
#line 3245 "http.d.rl"
{{
if(start != p)
{
list[key] = prepareValue(line[(start - line.ptr) .. (p - line.ptr)]);
}
}}
break;
#line 5872 "http.d"
default: break;
}
}
_out: {}
}
#line 2474 "http.d.rl"
mixin(finishParser!"parseParameterList");
return list;
}
/**
* Decode a $(LINK2 http://tools.ietf.org/html/rfc2616#section-3.6,
* "value")
*/
string prepareValue(string value)
{
return unquote(stripr(value));
}
/**
* Decode a $(LINK2 http://tools.ietf.org/html/rfc2616#section-3.6,
* "quoted-string")
*/
string unquote(string value)
{
if(value.length > 1 && value[0] == '"' && value[$ - 1] == '"')
return unescape(value[1 .. $ - 1]);
return value;
}
/**
* Unescape a $(LINK2 http://tools.ietf.org/html/rfc2616#page-16,
* "quoted-string")
*/
string unescape(string value)
{
string tmp;
tmp.reserve(value.length);
uint newLength = 0;
foreach(c; value)
{
if(c == '\\')
continue;
tmp ~= c;
}
return tmp;
}
void formatEntityTag(T)(T writer, EntityTag Tag)
if (isOutputRange!(T, string))
{
if(Tag.Weak)
writer.put("W/");
assert(Tag.Value != "");
writer.put(quote(Tag.Value));
}
Product parseProduct(string line)
{
Product prd;
const(char)* start;
mixin(initParser!());
#line 5943 "http.d"
{
cs = parseProduct_start;
}
#line 5948 "http.d"
{
if ( p == pe )
goto _test_eof;
switch ( cs )
{
case 1:
switch( (*p) ) {
case 33u: goto tr0;
case 124u: goto tr0;
case 126u: goto tr0;
default: break;
}
if ( (*p) < 45u ) {
if ( (*p) > 39u ) {
if ( 42u <= (*p) && (*p) <= 43u )
goto tr0;
} else if ( (*p) >= 35u )
goto tr0;
} else if ( (*p) > 46u ) {
if ( (*p) < 65u ) {
if ( 48u <= (*p) && (*p) <= 57u )
goto tr0;
} else if ( (*p) > 90u ) {
if ( 94u <= (*p) && (*p) <= 122u )
goto tr0;
} else
goto tr0;
} else
goto tr0;
goto st0;
st0:
cs = 0;
goto _out;
tr0:
#line 3355 "http.d.rl"
{{
start = p;
}}
goto st7;
st7:
if ( ++p == pe )
goto _test_eof7;
case 7:
#line 5992 "http.d"
switch( (*p) ) {
case 9u: goto tr8;
case 13u: goto tr9;
case 32u: goto tr8;
case 33u: goto st7;
case 47u: goto tr11;
case 124u: goto st7;
case 126u: goto st7;
default: break;
}
if ( (*p) < 45u ) {
if ( (*p) > 39u ) {
if ( 42u <= (*p) && (*p) <= 43u )
goto st7;
} else if ( (*p) >= 35u )
goto st7;
} else if ( (*p) > 57u ) {
if ( (*p) > 90u ) {
if ( 94u <= (*p) && (*p) <= 122u )
goto st7;
} else if ( (*p) >= 65u )
goto st7;
} else
goto st7;
goto st0;
tr8:
#line 3358 "http.d.rl"
{{
prd.Name = line[(start - line.ptr) .. (p - line.ptr)];
}}
goto st8;
st8:
if ( ++p == pe )
goto _test_eof8;
case 8:
#line 6028 "http.d"
switch( (*p) ) {
case 9u: goto st8;
case 13u: goto st2;
case 32u: goto st8;
case 47u: goto st4;
default: break;
}
goto st0;
tr9:
#line 3358 "http.d.rl"
{{
prd.Name = line[(start - line.ptr) .. (p - line.ptr)];
}}
goto st2;
st2:
if ( ++p == pe )
goto _test_eof2;
case 2:
#line 6047 "http.d"
if ( (*p) == 10u )
goto st3;
goto st0;
st3:
if ( ++p == pe )
goto _test_eof3;
case 3:
switch( (*p) ) {
case 9u: goto st8;
case 32u: goto st8;
default: break;
}
goto st0;
tr11:
#line 3358 "http.d.rl"
{{
prd.Name = line[(start - line.ptr) .. (p - line.ptr)];
}}
goto st4;
st4:
if ( ++p == pe )
goto _test_eof4;
case 4:
#line 6071 "http.d"
switch( (*p) ) {
case 9u: goto st4;
case 13u: goto st5;
case 32u: goto st4;
case 33u: goto tr6;
case 124u: goto tr6;
case 126u: goto tr6;
default: break;
}
if ( (*p) < 45u ) {
if ( (*p) > 39u ) {
if ( 42u <= (*p) && (*p) <= 43u )
goto tr6;
} else if ( (*p) >= 35u )
goto tr6;
} else if ( (*p) > 46u ) {
if ( (*p) < 65u ) {
if ( 48u <= (*p) && (*p) <= 57u )
goto tr6;
} else if ( (*p) > 90u ) {
if ( 94u <= (*p) && (*p) <= 122u )
goto tr6;
} else
goto tr6;
} else
goto tr6;
goto st0;
st5:
if ( ++p == pe )
goto _test_eof5;
case 5:
if ( (*p) == 10u )
goto st6;
goto st0;
st6:
if ( ++p == pe )
goto _test_eof6;
case 6:
switch( (*p) ) {
case 9u: goto st4;
case 32u: goto st4;
default: break;
}
goto st0;
tr6:
#line 3355 "http.d.rl"
{{
start = p;
}}
goto st9;
st9:
if ( ++p == pe )
goto _test_eof9;
case 9:
#line 6126 "http.d"
switch( (*p) ) {
case 33u: goto st9;
case 124u: goto st9;
case 126u: goto st9;
default: break;
}
if ( (*p) < 45u ) {
if ( (*p) > 39u ) {
if ( 42u <= (*p) && (*p) <= 43u )
goto st9;
} else if ( (*p) >= 35u )
goto st9;
} else if ( (*p) > 46u ) {
if ( (*p) < 65u ) {
if ( 48u <= (*p) && (*p) <= 57u )
goto st9;
} else if ( (*p) > 90u ) {
if ( 94u <= (*p) && (*p) <= 122u )
goto st9;
} else
goto st9;
} else
goto st9;
goto st0;
default: break;
}
_test_eof7: cs = 7; goto _test_eof;
_test_eof8: cs = 8; goto _test_eof;
_test_eof2: cs = 2; goto _test_eof;
_test_eof3: cs = 3; goto _test_eof;
_test_eof4: cs = 4; goto _test_eof;
_test_eof5: cs = 5; goto _test_eof;
_test_eof6: cs = 6; goto _test_eof;
_test_eof9: cs = 9; goto _test_eof;
_test_eof: {}
if ( p == eof )
{
switch ( cs ) {
case 7:
#line 3358 "http.d.rl"
{{
prd.Name = line[(start - line.ptr) .. (p - line.ptr)];
}}
break;
case 9:
#line 3361 "http.d.rl"
{{
prd.Version = line[(start - line.ptr) .. (p - line.ptr)];
}}
break;
#line 6178 "http.d"
default: break;
}
}
_out: {}
}
#line 2539 "http.d.rl"
mixin(finishParser!"parseProduct");
return prd;
}
/**
* Parses a $(LINK2 http://tools.ietf.org/html/rfc2616#section-3.3.1, HTTP-date)
*/
SysTime parseDate(string line)
{
int year;
Month month;
int day;
int hour;
int minute;
int second;
TimeZone zone;
const(char)* start;
mixin(initParser!());
#line 6209 "http.d"
{
cs = parseDate_start;
}
#line 6214 "http.d"
{
if ( p == pe )
goto _test_eof;
switch ( cs )
{
case 1:
switch( (*p) ) {
case 70u: goto st2;
case 77u: goto st108;
case 83u: goto st110;
case 84u: goto st115;
case 87u: goto st121;
case 102u: goto st2;
case 109u: goto st108;
case 115u: goto st110;
case 116u: goto st115;
case 119u: goto st121;
default: break;
}
goto st0;
st0:
cs = 0;
goto _out;
st2:
if ( ++p == pe )
goto _test_eof2;
case 2:
switch( (*p) ) {
case 82u: goto st3;
case 114u: goto st3;
default: break;
}
goto st0;
st3:
if ( ++p == pe )
goto _test_eof3;
case 3:
switch( (*p) ) {
case 73u: goto st4;
case 105u: goto st4;
default: break;
}
goto st0;
st4:
if ( ++p == pe )
goto _test_eof4;
case 4:
switch( (*p) ) {
case 32u: goto st5;
case 44u: goto st41;
case 68u: goto st105;
case 100u: goto st105;
default: break;
}
goto st0;
st5:
if ( ++p == pe )
goto _test_eof5;
case 5:
switch( (*p) ) {
case 65u: goto tr11;
case 68u: goto tr12;
case 70u: goto tr13;
case 74u: goto tr14;
case 77u: goto tr15;
case 78u: goto tr16;
case 79u: goto tr17;
case 83u: goto tr18;
case 97u: goto tr11;
case 100u: goto tr12;
case 102u: goto tr13;
case 106u: goto tr14;
case 109u: goto tr15;
case 110u: goto tr16;
case 111u: goto tr17;
case 115u: goto tr18;
default: break;
}
goto st0;
tr11:
#line 3372 "http.d.rl"
{{
start = p;
}}
goto st6;
st6:
if ( ++p == pe )
goto _test_eof6;
case 6:
#line 6304 "http.d"
switch( (*p) ) {
case 80u: goto st7;
case 85u: goto st25;
case 112u: goto st7;
case 117u: goto st25;
default: break;
}
goto st0;
st7:
if ( ++p == pe )
goto _test_eof7;
case 7:
switch( (*p) ) {
case 82u: goto st8;
case 114u: goto st8;
default: break;
}
goto st0;
st8:
if ( ++p == pe )
goto _test_eof8;
case 8:
if ( (*p) == 32u )
goto tr22;
goto st0;
tr22:
#line 3378 "http.d.rl"
{{
string mon = tolower(line[(start - line.ptr) .. (p - line.ptr)]);
switch(mon)
{
case "jan":
month = Month.jan;
break;
case "feb":
month = Month.feb;
break;
case "mar":
month = Month.mar;
break;
case "apr":
month = Month.apr;
break;
case "may":
month = Month.may;
break;
case "jun":
month = Month.jun;
break;
case "jul":
month = Month.jul;
break;
case "aug":
month = Month.aug;
break;
case "sep":
month = Month.sep;
break;
case "oct":
month = Month.oct;
break;
case "nov":
month = Month.nov;
break;
case "dec":
month = Month.dec;
break;
}
}}
goto st9;
st9:
if ( ++p == pe )
goto _test_eof9;
case 9:
#line 6379 "http.d"
if ( (*p) == 32u )
goto tr23;
if ( 48u <= (*p) && (*p) <= 57u )
goto tr23;
goto st0;
tr23:
#line 3372 "http.d.rl"
{{
start = p;
}}
goto st10;
st10:
if ( ++p == pe )
goto _test_eof10;
case 10:
#line 6395 "http.d"
if ( 48u <= (*p) && (*p) <= 57u )
goto st11;
goto st0;
st11:
if ( ++p == pe )
goto _test_eof11;
case 11:
if ( (*p) == 32u )
goto tr25;
goto st0;
tr25:
#line 3420 "http.d.rl"
{{
day = std.conv.parse!int(line[(start - line.ptr) .. (p - line.ptr)]);
}}
goto st12;
st12:
if ( ++p == pe )
goto _test_eof12;
case 12:
#line 6416 "http.d"
if ( 48u <= (*p) && (*p) <= 57u )
goto tr26;
goto st0;
tr26:
#line 3372 "http.d.rl"
{{
start = p;
}}
goto st13;
st13:
if ( ++p == pe )
goto _test_eof13;
case 13:
#line 6430 "http.d"
if ( 48u <= (*p) && (*p) <= 57u )
goto st14;
goto st0;
st14:
if ( ++p == pe )
goto _test_eof14;
case 14:
if ( (*p) == 58u )
goto tr28;
goto st0;
tr28:
#line 3426 "http.d.rl"
{{
hour = std.conv.parse!int(line[(start - line.ptr) .. (p - line.ptr)]);
}}
goto st15;
st15:
if ( ++p == pe )
goto _test_eof15;
case 15:
#line 6451 "http.d"
if ( 48u <= (*p) && (*p) <= 57u )
goto tr29;
goto st0;
tr29:
#line 3372 "http.d.rl"
{{
start = p;
}}
goto st16;
st16:
if ( ++p == pe )
goto _test_eof16;
case 16:
#line 6465 "http.d"
if ( 48u <= (*p) && (*p) <= 57u )
goto st17;
goto st0;
st17:
if ( ++p == pe )
goto _test_eof17;
case 17:
if ( (*p) == 58u )
goto tr31;
goto st0;
tr31:
#line 3429 "http.d.rl"
{{
minute = std.conv.parse!int(line[(start - line.ptr) .. (p - line.ptr)]);
}}
goto st18;
st18:
if ( ++p == pe )
goto _test_eof18;
case 18:
#line 6486 "http.d"
if ( 48u <= (*p) && (*p) <= 57u )
goto tr32;
goto st0;
tr32:
#line 3372 "http.d.rl"
{{
start = p;
}}
goto st19;
st19:
if ( ++p == pe )
goto _test_eof19;
case 19:
#line 6500 "http.d"
if ( 48u <= (*p) && (*p) <= 57u )
goto st20;
goto st0;
st20:
if ( ++p == pe )
goto _test_eof20;
case 20:
if ( (*p) == 32u )
goto tr34;
goto st0;
tr34:
#line 3432 "http.d.rl"
{{
second = std.conv.parse!int(line[(start - line.ptr) .. (p - line.ptr)]);
}}
goto st21;
st21:
if ( ++p == pe )
goto _test_eof21;
case 21:
#line 6521 "http.d"
if ( 48u <= (*p) && (*p) <= 57u )
goto tr35;
goto st0;
tr35:
#line 3372 "http.d.rl"
{{
start = p;
}}
goto st22;
st22:
if ( ++p == pe )
goto _test_eof22;
case 22:
#line 6535 "http.d"
if ( 48u <= (*p) && (*p) <= 57u )
goto st23;
goto st0;
st23:
if ( ++p == pe )
goto _test_eof23;
case 23:
if ( 48u <= (*p) && (*p) <= 57u )
goto st24;
goto st0;
st24:
if ( ++p == pe )
goto _test_eof24;
case 24:
if ( 48u <= (*p) && (*p) <= 57u )
goto st125;
goto st0;
st125:
if ( ++p == pe )
goto _test_eof125;
case 125:
goto st0;
st25:
if ( ++p == pe )
goto _test_eof25;
case 25:
switch( (*p) ) {
case 71u: goto st8;
case 103u: goto st8;
default: break;
}
goto st0;
tr12:
#line 3372 "http.d.rl"
{{
start = p;
}}
goto st26;
st26:
if ( ++p == pe )
goto _test_eof26;
case 26:
#line 6578 "http.d"
switch( (*p) ) {
case 69u: goto st27;
case 101u: goto st27;
default: break;
}
goto st0;
st27:
if ( ++p == pe )
goto _test_eof27;
case 27:
switch( (*p) ) {
case 67u: goto st8;
case 99u: goto st8;
default: break;
}
goto st0;
tr13:
#line 3372 "http.d.rl"
{{
start = p;
}}
goto st28;
st28:
if ( ++p == pe )
goto _test_eof28;
case 28:
#line 6605 "http.d"
switch( (*p) ) {
case 69u: goto st29;
case 101u: goto st29;
default: break;
}
goto st0;
st29:
if ( ++p == pe )
goto _test_eof29;
case 29:
switch( (*p) ) {
case 66u: goto st8;
case 98u: goto st8;
default: break;
}
goto st0;
tr14:
#line 3372 "http.d.rl"
{{
start = p;
}}
goto st30;
st30:
if ( ++p == pe )
goto _test_eof30;
case 30:
#line 6632 "http.d"
switch( (*p) ) {
case 65u: goto st31;
case 85u: goto st32;
case 97u: goto st31;
case 117u: goto st32;
default: break;
}
goto st0;
st31:
if ( ++p == pe )
goto _test_eof31;
case 31:
switch( (*p) ) {
case 78u: goto st8;
case 110u: goto st8;
default: break;
}
goto st0;
st32:
if ( ++p == pe )
goto _test_eof32;
case 32:
switch( (*p) ) {
case 76u: goto st8;
case 78u: goto st8;
case 108u: goto st8;
case 110u: goto st8;
default: break;
}
goto st0;
tr15:
#line 3372 "http.d.rl"
{{
start = p;
}}
goto st33;
st33:
if ( ++p == pe )
goto _test_eof33;
case 33:
#line 6673 "http.d"
switch( (*p) ) {
case 65u: goto st34;
case 97u: goto st34;
default: break;
}
goto st0;
st34:
if ( ++p == pe )
goto _test_eof34;
case 34:
switch( (*p) ) {
case 82u: goto st8;
case 89u: goto st8;
case 114u: goto st8;
case 121u: goto st8;
default: break;
}
goto st0;
tr16:
#line 3372 "http.d.rl"
{{
start = p;
}}
goto st35;
st35:
if ( ++p == pe )
goto _test_eof35;
case 35:
#line 6702 "http.d"
switch( (*p) ) {
case 79u: goto st36;
case 111u: goto st36;
default: break;
}
goto st0;
st36:
if ( ++p == pe )
goto _test_eof36;
case 36:
switch( (*p) ) {
case 86u: goto st8;
case 118u: goto st8;
default: break;
}
goto st0;
tr17:
#line 3372 "http.d.rl"
{{
start = p;
}}
goto st37;
st37:
if ( ++p == pe )
goto _test_eof37;
case 37:
#line 6729 "http.d"
switch( (*p) ) {
case 67u: goto st38;
case 99u: goto st38;
default: break;
}
goto st0;
st38:
if ( ++p == pe )
goto _test_eof38;
case 38:
switch( (*p) ) {
case 84u: goto st8;
case 116u: goto st8;
default: break;
}
goto st0;
tr18:
#line 3372 "http.d.rl"
{{
start = p;
}}
goto st39;
st39:
if ( ++p == pe )
goto _test_eof39;
case 39:
#line 6756 "http.d"
switch( (*p) ) {
case 69u: goto st40;
case 101u: goto st40;
default: break;
}
goto st0;
st40:
if ( ++p == pe )
goto _test_eof40;
case 40:
switch( (*p) ) {
case 80u: goto st8;
case 112u: goto st8;
default: break;
}
goto st0;
st41:
if ( ++p == pe )
goto _test_eof41;
case 41:
if ( (*p) == 32u )
goto st42;
goto st0;
st42:
if ( ++p == pe )
goto _test_eof42;
case 42:
if ( 48u <= (*p) && (*p) <= 57u )
goto tr48;
goto st0;
tr48:
#line 3372 "http.d.rl"
{{
start = p;
}}
goto st43;
st43:
if ( ++p == pe )
goto _test_eof43;
case 43:
#line 6797 "http.d"
if ( 48u <= (*p) && (*p) <= 57u )
goto st44;
goto st0;
st44:
if ( ++p == pe )
goto _test_eof44;
case 44:
switch( (*p) ) {
case 32u: goto tr50;
case 45u: goto tr51;
default: break;
}
goto st0;
tr50:
#line 3420 "http.d.rl"
{{
day = std.conv.parse!int(line[(start - line.ptr) .. (p - line.ptr)]);
}}
goto st45;
st45:
if ( ++p == pe )
goto _test_eof45;
case 45:
#line 6821 "http.d"
switch( (*p) ) {
case 65u: goto tr52;
case 68u: goto tr53;
case 70u: goto tr54;
case 74u: goto tr55;
case 77u: goto tr56;
case 78u: goto tr57;
case 79u: goto tr58;
case 83u: goto tr59;
case 97u: goto tr52;
case 100u: goto tr53;
case 102u: goto tr54;
case 106u: goto tr55;
case 109u: goto tr56;
case 110u: goto tr57;
case 111u: goto tr58;
case 115u: goto tr59;
default: break;
}
goto st0;
tr52:
#line 3372 "http.d.rl"
{{
start = p;
}}
goto st46;
st46:
if ( ++p == pe )
goto _test_eof46;
case 46:
#line 6852 "http.d"
switch( (*p) ) {
case 80u: goto st47;
case 85u: goto st66;
case 112u: goto st47;
case 117u: goto st66;
default: break;
}
goto st0;
st47:
if ( ++p == pe )
goto _test_eof47;
case 47:
switch( (*p) ) {
case 82u: goto st48;
case 114u: goto st48;
default: break;
}
goto st0;
st48:
if ( ++p == pe )
goto _test_eof48;
case 48:
if ( (*p) == 32u )
goto tr63;
goto st0;
tr63:
#line 3378 "http.d.rl"
{{
string mon = tolower(line[(start - line.ptr) .. (p - line.ptr)]);
switch(mon)
{
case "jan":
month = Month.jan;
break;
case "feb":
month = Month.feb;
break;
case "mar":
month = Month.mar;
break;
case "apr":
month = Month.apr;
break;
case "may":
month = Month.may;
break;
case "jun":
month = Month.jun;
break;
case "jul":
month = Month.jul;
break;
case "aug":
month = Month.aug;
break;
case "sep":
month = Month.sep;
break;
case "oct":
month = Month.oct;
break;
case "nov":
month = Month.nov;
break;
case "dec":
month = Month.dec;
break;
}
}}
goto st49;
st49:
if ( ++p == pe )
goto _test_eof49;
case 49:
#line 6927 "http.d"
if ( 48u <= (*p) && (*p) <= 57u )
goto tr64;
goto st0;
tr64:
#line 3372 "http.d.rl"
{{
start = p;
}}
goto st50;
st50:
if ( ++p == pe )
goto _test_eof50;
case 50:
#line 6941 "http.d"
if ( 48u <= (*p) && (*p) <= 57u )
goto st51;
goto st0;
st51:
if ( ++p == pe )
goto _test_eof51;
case 51:
if ( 48u <= (*p) && (*p) <= 57u )
goto st52;
goto st0;
st52:
if ( ++p == pe )
goto _test_eof52;
case 52:
if ( 48u <= (*p) && (*p) <= 57u )
goto st53;
goto st0;
st53:
if ( ++p == pe )
goto _test_eof53;
case 53:
if ( (*p) == 32u )
goto tr68;
goto st0;
tr68:
#line 3375 "http.d.rl"
{{
year = std.conv.parse!int(line[(start - line.ptr) .. (p - line.ptr)]);
}}
goto st54;
st54:
if ( ++p == pe )
goto _test_eof54;
case 54:
#line 6976 "http.d"
if ( 48u <= (*p) && (*p) <= 57u )
goto tr69;
goto st0;
tr69:
#line 3372 "http.d.rl"
{{
start = p;
}}
goto st55;
st55:
if ( ++p == pe )
goto _test_eof55;
case 55:
#line 6990 "http.d"
if ( 48u <= (*p) && (*p) <= 57u )
goto st56;
goto st0;
st56:
if ( ++p == pe )
goto _test_eof56;
case 56:
if ( (*p) == 58u )
goto tr71;
goto st0;
tr71:
#line 3426 "http.d.rl"
{{
hour = std.conv.parse!int(line[(start - line.ptr) .. (p - line.ptr)]);
}}
goto st57;
st57:
if ( ++p == pe )
goto _test_eof57;
case 57:
#line 7011 "http.d"
if ( 48u <= (*p) && (*p) <= 57u )
goto tr72;
goto st0;
tr72:
#line 3372 "http.d.rl"
{{
start = p;
}}
goto st58;
st58:
if ( ++p == pe )
goto _test_eof58;
case 58:
#line 7025 "http.d"
if ( 48u <= (*p) && (*p) <= 57u )
goto st59;
goto st0;
st59:
if ( ++p == pe )
goto _test_eof59;
case 59:
if ( (*p) == 58u )
goto tr74;
goto st0;
tr74:
#line 3429 "http.d.rl"
{{
minute = std.conv.parse!int(line[(start - line.ptr) .. (p - line.ptr)]);
}}
goto st60;
st60:
if ( ++p == pe )
goto _test_eof60;
case 60:
#line 7046 "http.d"
if ( 48u <= (*p) && (*p) <= 57u )
goto tr75;
goto st0;
tr75:
#line 3372 "http.d.rl"
{{
start = p;
}}
goto st61;
st61:
if ( ++p == pe )
goto _test_eof61;
case 61:
#line 7060 "http.d"
if ( 48u <= (*p) && (*p) <= 57u )
goto st62;
goto st0;
st62:
if ( ++p == pe )
goto _test_eof62;
case 62:
if ( (*p) == 32u )
goto tr77;
goto st0;
tr77:
#line 3432 "http.d.rl"
{{
second = std.conv.parse!int(line[(start - line.ptr) .. (p - line.ptr)]);
}}
goto st63;
st63:
if ( ++p == pe )
goto _test_eof63;
case 63:
#line 7081 "http.d"
switch( (*p) ) {
case 71u: goto st64;
case 103u: goto st64;
default: break;
}
goto st0;
st64:
if ( ++p == pe )
goto _test_eof64;
case 64:
switch( (*p) ) {
case 77u: goto st65;
case 109u: goto st65;
default: break;
}
goto st0;
st65:
if ( ++p == pe )
goto _test_eof65;
case 65:
switch( (*p) ) {
case 84u: goto st126;
case 116u: goto st126;
default: break;
}
goto st0;
st126:
if ( ++p == pe )
goto _test_eof126;
case 126:
goto st0;
st66:
if ( ++p == pe )
goto _test_eof66;
case 66:
switch( (*p) ) {
case 71u: goto st48;
case 103u: goto st48;
default: break;
}
goto st0;
tr53:
#line 3372 "http.d.rl"
{{
start = p;
}}
goto st67;
st67:
if ( ++p == pe )
goto _test_eof67;
case 67:
#line 7133 "http.d"
switch( (*p) ) {
case 69u: goto st68;
case 101u: goto st68;
default: break;
}
goto st0;
st68:
if ( ++p == pe )
goto _test_eof68;
case 68:
switch( (*p) ) {
case 67u: goto st48;
case 99u: goto st48;
default: break;
}
goto st0;
tr54:
#line 3372 "http.d.rl"
{{
start = p;
}}
goto st69;
st69:
if ( ++p == pe )
goto _test_eof69;
case 69:
#line 7160 "http.d"
switch( (*p) ) {
case 69u: goto st70;
case 101u: goto st70;
default: break;
}
goto st0;
st70:
if ( ++p == pe )
goto _test_eof70;
case 70:
switch( (*p) ) {
case 66u: goto st48;
case 98u: goto st48;
default: break;
}
goto st0;
tr55:
#line 3372 "http.d.rl"
{{
start = p;
}}
goto st71;
st71:
if ( ++p == pe )
goto _test_eof71;
case 71:
#line 7187 "http.d"
switch( (*p) ) {
case 65u: goto st72;
case 85u: goto st73;
case 97u: goto st72;
case 117u: goto st73;
default: break;
}
goto st0;
st72:
if ( ++p == pe )
goto _test_eof72;
case 72:
switch( (*p) ) {
case 78u: goto st48;
case 110u: goto st48;
default: break;
}
goto st0;
st73:
if ( ++p == pe )
goto _test_eof73;
case 73:
switch( (*p) ) {
case 76u: goto st48;
case 78u: goto st48;
case 108u: goto st48;
case 110u: goto st48;
default: break;
}
goto st0;
tr56:
#line 3372 "http.d.rl"
{{
start = p;
}}
goto st74;
st74:
if ( ++p == pe )
goto _test_eof74;
case 74:
#line 7228 "http.d"
switch( (*p) ) {
case 65u: goto st75;
case 97u: goto st75;
default: break;
}
goto st0;
st75:
if ( ++p == pe )
goto _test_eof75;
case 75:
switch( (*p) ) {
case 82u: goto st48;
case 89u: goto st48;
case 114u: goto st48;
case 121u: goto st48;
default: break;
}
goto st0;
tr57:
#line 3372 "http.d.rl"
{{
start = p;
}}
goto st76;
st76:
if ( ++p == pe )
goto _test_eof76;
case 76:
#line 7257 "http.d"
switch( (*p) ) {
case 79u: goto st77;
case 111u: goto st77;
default: break;
}
goto st0;
st77:
if ( ++p == pe )
goto _test_eof77;
case 77:
switch( (*p) ) {
case 86u: goto st48;
case 118u: goto st48;
default: break;
}
goto st0;
tr58:
#line 3372 "http.d.rl"
{{
start = p;
}}
goto st78;
st78:
if ( ++p == pe )
goto _test_eof78;
case 78:
#line 7284 "http.d"
switch( (*p) ) {
case 67u: goto st79;
case 99u: goto st79;
default: break;
}
goto st0;
st79:
if ( ++p == pe )
goto _test_eof79;
case 79:
switch( (*p) ) {
case 84u: goto st48;
case 116u: goto st48;
default: break;
}
goto st0;
tr59:
#line 3372 "http.d.rl"
{{
start = p;
}}
goto st80;
st80:
if ( ++p == pe )
goto _test_eof80;
case 80:
#line 7311 "http.d"
switch( (*p) ) {
case 69u: goto st81;
case 101u: goto st81;
default: break;
}
goto st0;
st81:
if ( ++p == pe )
goto _test_eof81;
case 81:
switch( (*p) ) {
case 80u: goto st48;
case 112u: goto st48;
default: break;
}
goto st0;
tr51:
#line 3420 "http.d.rl"
{{
day = std.conv.parse!int(line[(start - line.ptr) .. (p - line.ptr)]);
}}
goto st82;
st82:
if ( ++p == pe )
goto _test_eof82;
case 82:
#line 7338 "http.d"
switch( (*p) ) {
case 65u: goto tr89;
case 68u: goto tr90;
case 70u: goto tr91;
case 74u: goto tr92;
case 77u: goto tr93;
case 78u: goto tr94;
case 79u: goto tr95;
case 83u: goto tr96;
case 97u: goto tr89;
case 100u: goto tr90;
case 102u: goto tr91;
case 106u: goto tr92;
case 109u: goto tr93;
case 110u: goto tr94;
case 111u: goto tr95;
case 115u: goto tr96;
default: break;
}
goto st0;
tr89:
#line 3372 "http.d.rl"
{{
start = p;
}}
goto st83;
st83:
if ( ++p == pe )
goto _test_eof83;
case 83:
#line 7369 "http.d"
switch( (*p) ) {
case 80u: goto st84;
case 85u: goto st89;
case 112u: goto st84;
case 117u: goto st89;
default: break;
}
goto st0;
st84:
if ( ++p == pe )
goto _test_eof84;
case 84:
switch( (*p) ) {
case 82u: goto st85;
case 114u: goto st85;
default: break;
}
goto st0;
st85:
if ( ++p == pe )
goto _test_eof85;
case 85:
if ( (*p) == 45u )
goto tr100;
goto st0;
tr100:
#line 3378 "http.d.rl"
{{
string mon = tolower(line[(start - line.ptr) .. (p - line.ptr)]);
switch(mon)
{
case "jan":
month = Month.jan;
break;
case "feb":
month = Month.feb;
break;
case "mar":
month = Month.mar;
break;
case "apr":
month = Month.apr;
break;
case "may":
month = Month.may;
break;
case "jun":
month = Month.jun;
break;
case "jul":
month = Month.jul;
break;
case "aug":
month = Month.aug;
break;
case "sep":
month = Month.sep;
break;
case "oct":
month = Month.oct;
break;
case "nov":
month = Month.nov;
break;
case "dec":
month = Month.dec;
break;
}
}}
goto st86;
st86:
if ( ++p == pe )
goto _test_eof86;
case 86:
#line 7444 "http.d"
if ( 48u <= (*p) && (*p) <= 57u )
goto tr101;
goto st0;
tr101:
#line 3372 "http.d.rl"
{{
start = p;
}}
goto st87;
st87:
if ( ++p == pe )
goto _test_eof87;
case 87:
#line 7458 "http.d"
if ( 48u <= (*p) && (*p) <= 57u )
goto st88;
goto st0;
st88:
if ( ++p == pe )
goto _test_eof88;
case 88:
if ( (*p) == 32u )
goto tr68;
if ( 48u <= (*p) && (*p) <= 57u )
goto st52;
goto st0;
st89:
if ( ++p == pe )
goto _test_eof89;
case 89:
switch( (*p) ) {
case 71u: goto st85;
case 103u: goto st85;
default: break;
}
goto st0;
tr90:
#line 3372 "http.d.rl"
{{
start = p;
}}
goto st90;
st90:
if ( ++p == pe )
goto _test_eof90;
case 90:
#line 7491 "http.d"
switch( (*p) ) {
case 69u: goto st91;
case 101u: goto st91;
default: break;
}
goto st0;
st91:
if ( ++p == pe )
goto _test_eof91;
case 91:
switch( (*p) ) {
case 67u: goto st85;
case 99u: goto st85;
default: break;
}
goto st0;
tr91:
#line 3372 "http.d.rl"
{{
start = p;
}}
goto st92;
st92:
if ( ++p == pe )
goto _test_eof92;
case 92:
#line 7518 "http.d"
switch( (*p) ) {
case 69u: goto st93;
case 101u: goto st93;
default: break;
}
goto st0;
st93:
if ( ++p == pe )
goto _test_eof93;
case 93:
switch( (*p) ) {
case 66u: goto st85;
case 98u: goto st85;
default: break;
}
goto st0;
tr92:
#line 3372 "http.d.rl"
{{
start = p;
}}
goto st94;
st94:
if ( ++p == pe )
goto _test_eof94;
case 94:
#line 7545 "http.d"
switch( (*p) ) {
case 65u: goto st95;
case 85u: goto st96;
case 97u: goto st95;
case 117u: goto st96;
default: break;
}
goto st0;
st95:
if ( ++p == pe )
goto _test_eof95;
case 95:
switch( (*p) ) {
case 78u: goto st85;
case 110u: goto st85;
default: break;
}
goto st0;
st96:
if ( ++p == pe )
goto _test_eof96;
case 96:
switch( (*p) ) {
case 76u: goto st85;
case 78u: goto st85;
case 108u: goto st85;
case 110u: goto st85;
default: break;
}
goto st0;
tr93:
#line 3372 "http.d.rl"
{{
start = p;
}}
goto st97;
st97:
if ( ++p == pe )
goto _test_eof97;
case 97:
#line 7586 "http.d"
switch( (*p) ) {
case 65u: goto st98;
case 97u: goto st98;
default: break;
}
goto st0;
st98:
if ( ++p == pe )
goto _test_eof98;
case 98:
switch( (*p) ) {
case 82u: goto st85;
case 89u: goto st85;
case 114u: goto st85;
case 121u: goto st85;
default: break;
}
goto st0;
tr94:
#line 3372 "http.d.rl"
{{
start = p;
}}
goto st99;
st99:
if ( ++p == pe )
goto _test_eof99;
case 99:
#line 7615 "http.d"
switch( (*p) ) {
case 79u: goto st100;
case 111u: goto st100;
default: break;
}
goto st0;
st100:
if ( ++p == pe )
goto _test_eof100;
case 100:
switch( (*p) ) {
case 86u: goto st85;
case 118u: goto st85;
default: break;
}
goto st0;
tr95:
#line 3372 "http.d.rl"
{{
start = p;
}}
goto st101;
st101:
if ( ++p == pe )
goto _test_eof101;
case 101:
#line 7642 "http.d"
switch( (*p) ) {
case 67u: goto st102;
case 99u: goto st102;
default: break;
}
goto st0;
st102:
if ( ++p == pe )
goto _test_eof102;
case 102:
switch( (*p) ) {
case 84u: goto st85;
case 116u: goto st85;
default: break;
}
goto st0;
tr96:
#line 3372 "http.d.rl"
{{
start = p;
}}
goto st103;
st103:
if ( ++p == pe )
goto _test_eof103;
case 103:
#line 7669 "http.d"
switch( (*p) ) {
case 69u: goto st104;
case 101u: goto st104;
default: break;
}
goto st0;
st104:
if ( ++p == pe )
goto _test_eof104;
case 104:
switch( (*p) ) {
case 80u: goto st85;
case 112u: goto st85;
default: break;
}
goto st0;
st105:
if ( ++p == pe )
goto _test_eof105;
case 105:
switch( (*p) ) {
case 65u: goto st106;
case 97u: goto st106;
default: break;
}
goto st0;
st106:
if ( ++p == pe )
goto _test_eof106;
case 106:
switch( (*p) ) {
case 89u: goto st107;
case 121u: goto st107;
default: break;
}
goto st0;
st107:
if ( ++p == pe )
goto _test_eof107;
case 107:
switch( (*p) ) {
case 32u: goto st5;
case 44u: goto st41;
default: break;
}
goto st0;
st108:
if ( ++p == pe )
goto _test_eof108;
case 108:
switch( (*p) ) {
case 79u: goto st109;
case 111u: goto st109;
default: break;
}
goto st0;
st109:
if ( ++p == pe )
goto _test_eof109;
case 109:
switch( (*p) ) {
case 78u: goto st4;
case 110u: goto st4;
default: break;
}
goto st0;
st110:
if ( ++p == pe )
goto _test_eof110;
case 110:
switch( (*p) ) {
case 65u: goto st111;
case 85u: goto st109;
case 97u: goto st111;
case 117u: goto st109;
default: break;
}
goto st0;
st111:
if ( ++p == pe )
goto _test_eof111;
case 111:
switch( (*p) ) {
case 84u: goto st112;
case 116u: goto st112;
default: break;
}
goto st0;
st112:
if ( ++p == pe )
goto _test_eof112;
case 112:
switch( (*p) ) {
case 32u: goto st5;
case 44u: goto st41;
case 85u: goto st113;
case 117u: goto st113;
default: break;
}
goto st0;
st113:
if ( ++p == pe )
goto _test_eof113;
case 113:
switch( (*p) ) {
case 82u: goto st114;
case 114u: goto st114;
default: break;
}
goto st0;
st114:
if ( ++p == pe )
goto _test_eof114;
case 114:
switch( (*p) ) {
case 68u: goto st105;
case 100u: goto st105;
default: break;
}
goto st0;
st115:
if ( ++p == pe )
goto _test_eof115;
case 115:
switch( (*p) ) {
case 72u: goto st116;
case 85u: goto st119;
case 104u: goto st116;
case 117u: goto st119;
default: break;
}
goto st0;
st116:
if ( ++p == pe )
goto _test_eof116;
case 116:
switch( (*p) ) {
case 85u: goto st117;
case 117u: goto st117;
default: break;
}
goto st0;
st117:
if ( ++p == pe )
goto _test_eof117;
case 117:
switch( (*p) ) {
case 32u: goto st5;
case 44u: goto st41;
case 82u: goto st118;
case 114u: goto st118;
default: break;
}
goto st0;
st118:
if ( ++p == pe )
goto _test_eof118;
case 118:
switch( (*p) ) {
case 83u: goto st114;
case 115u: goto st114;
default: break;
}
goto st0;
st119:
if ( ++p == pe )
goto _test_eof119;
case 119:
switch( (*p) ) {
case 69u: goto st120;
case 101u: goto st120;
default: break;
}
goto st0;
st120:
if ( ++p == pe )
goto _test_eof120;
case 120:
switch( (*p) ) {
case 32u: goto st5;
case 44u: goto st41;
case 83u: goto st114;
case 115u: goto st114;
default: break;
}
goto st0;
st121:
if ( ++p == pe )
goto _test_eof121;
case 121:
switch( (*p) ) {
case 69u: goto st122;
case 101u: goto st122;
default: break;
}
goto st0;
st122:
if ( ++p == pe )
goto _test_eof122;
case 122:
switch( (*p) ) {
case 68u: goto st123;
case 100u: goto st123;
default: break;
}
goto st0;
st123:
if ( ++p == pe )
goto _test_eof123;
case 123:
switch( (*p) ) {
case 32u: goto st5;
case 44u: goto st41;
case 78u: goto st124;
case 110u: goto st124;
default: break;
}
goto st0;
st124:
if ( ++p == pe )
goto _test_eof124;
case 124:
switch( (*p) ) {
case 69u: goto st118;
case 101u: goto st118;
default: break;
}
goto st0;
default: break;
}
_test_eof2: cs = 2; goto _test_eof;
_test_eof3: cs = 3; goto _test_eof;
_test_eof4: cs = 4; goto _test_eof;
_test_eof5: cs = 5; goto _test_eof;
_test_eof6: cs = 6; goto _test_eof;
_test_eof7: cs = 7; goto _test_eof;
_test_eof8: cs = 8; goto _test_eof;
_test_eof9: cs = 9; goto _test_eof;
_test_eof10: cs = 10; goto _test_eof;
_test_eof11: cs = 11; goto _test_eof;
_test_eof12: cs = 12; goto _test_eof;
_test_eof13: cs = 13; goto _test_eof;
_test_eof14: cs = 14; goto _test_eof;
_test_eof15: cs = 15; goto _test_eof;
_test_eof16: cs = 16; goto _test_eof;
_test_eof17: cs = 17; goto _test_eof;
_test_eof18: cs = 18; goto _test_eof;
_test_eof19: cs = 19; goto _test_eof;
_test_eof20: cs = 20; goto _test_eof;
_test_eof21: cs = 21; goto _test_eof;
_test_eof22: cs = 22; goto _test_eof;
_test_eof23: cs = 23; goto _test_eof;
_test_eof24: cs = 24; goto _test_eof;
_test_eof125: cs = 125; goto _test_eof;
_test_eof25: cs = 25; goto _test_eof;
_test_eof26: cs = 26; goto _test_eof;
_test_eof27: cs = 27; goto _test_eof;
_test_eof28: cs = 28; goto _test_eof;
_test_eof29: cs = 29; goto _test_eof;
_test_eof30: cs = 30; goto _test_eof;
_test_eof31: cs = 31; goto _test_eof;
_test_eof32: cs = 32; goto _test_eof;
_test_eof33: cs = 33; goto _test_eof;
_test_eof34: cs = 34; goto _test_eof;
_test_eof35: cs = 35; goto _test_eof;
_test_eof36: cs = 36; goto _test_eof;
_test_eof37: cs = 37; goto _test_eof;
_test_eof38: cs = 38; goto _test_eof;
_test_eof39: cs = 39; goto _test_eof;
_test_eof40: cs = 40; goto _test_eof;
_test_eof41: cs = 41; goto _test_eof;
_test_eof42: cs = 42; goto _test_eof;
_test_eof43: cs = 43; goto _test_eof;
_test_eof44: cs = 44; goto _test_eof;
_test_eof45: cs = 45; goto _test_eof;
_test_eof46: cs = 46; goto _test_eof;
_test_eof47: cs = 47; goto _test_eof;
_test_eof48: cs = 48; goto _test_eof;
_test_eof49: cs = 49; goto _test_eof;
_test_eof50: cs = 50; goto _test_eof;
_test_eof51: cs = 51; goto _test_eof;
_test_eof52: cs = 52; goto _test_eof;
_test_eof53: cs = 53; goto _test_eof;
_test_eof54: cs = 54; goto _test_eof;
_test_eof55: cs = 55; goto _test_eof;
_test_eof56: cs = 56; goto _test_eof;
_test_eof57: cs = 57; goto _test_eof;
_test_eof58: cs = 58; goto _test_eof;
_test_eof59: cs = 59; goto _test_eof;
_test_eof60: cs = 60; goto _test_eof;
_test_eof61: cs = 61; goto _test_eof;
_test_eof62: cs = 62; goto _test_eof;
_test_eof63: cs = 63; goto _test_eof;
_test_eof64: cs = 64; goto _test_eof;
_test_eof65: cs = 65; goto _test_eof;
_test_eof126: cs = 126; goto _test_eof;
_test_eof66: cs = 66; goto _test_eof;
_test_eof67: cs = 67; goto _test_eof;
_test_eof68: cs = 68; goto _test_eof;
_test_eof69: cs = 69; goto _test_eof;
_test_eof70: cs = 70; goto _test_eof;
_test_eof71: cs = 71; goto _test_eof;
_test_eof72: cs = 72; goto _test_eof;
_test_eof73: cs = 73; goto _test_eof;
_test_eof74: cs = 74; goto _test_eof;
_test_eof75: cs = 75; goto _test_eof;
_test_eof76: cs = 76; goto _test_eof;
_test_eof77: cs = 77; goto _test_eof;
_test_eof78: cs = 78; goto _test_eof;
_test_eof79: cs = 79; goto _test_eof;
_test_eof80: cs = 80; goto _test_eof;
_test_eof81: cs = 81; goto _test_eof;
_test_eof82: cs = 82; goto _test_eof;
_test_eof83: cs = 83; goto _test_eof;
_test_eof84: cs = 84; goto _test_eof;
_test_eof85: cs = 85; goto _test_eof;
_test_eof86: cs = 86; goto _test_eof;
_test_eof87: cs = 87; goto _test_eof;
_test_eof88: cs = 88; goto _test_eof;
_test_eof89: cs = 89; goto _test_eof;
_test_eof90: cs = 90; goto _test_eof;
_test_eof91: cs = 91; goto _test_eof;
_test_eof92: cs = 92; goto _test_eof;
_test_eof93: cs = 93; goto _test_eof;
_test_eof94: cs = 94; goto _test_eof;
_test_eof95: cs = 95; goto _test_eof;
_test_eof96: cs = 96; goto _test_eof;
_test_eof97: cs = 97; goto _test_eof;
_test_eof98: cs = 98; goto _test_eof;
_test_eof99: cs = 99; goto _test_eof;
_test_eof100: cs = 100; goto _test_eof;
_test_eof101: cs = 101; goto _test_eof;
_test_eof102: cs = 102; goto _test_eof;
_test_eof103: cs = 103; goto _test_eof;
_test_eof104: cs = 104; goto _test_eof;
_test_eof105: cs = 105; goto _test_eof;
_test_eof106: cs = 106; goto _test_eof;
_test_eof107: cs = 107; goto _test_eof;
_test_eof108: cs = 108; goto _test_eof;
_test_eof109: cs = 109; goto _test_eof;
_test_eof110: cs = 110; goto _test_eof;
_test_eof111: cs = 111; goto _test_eof;
_test_eof112: cs = 112; goto _test_eof;
_test_eof113: cs = 113; goto _test_eof;
_test_eof114: cs = 114; goto _test_eof;
_test_eof115: cs = 115; goto _test_eof;
_test_eof116: cs = 116; goto _test_eof;
_test_eof117: cs = 117; goto _test_eof;
_test_eof118: cs = 118; goto _test_eof;
_test_eof119: cs = 119; goto _test_eof;
_test_eof120: cs = 120; goto _test_eof;
_test_eof121: cs = 121; goto _test_eof;
_test_eof122: cs = 122; goto _test_eof;
_test_eof123: cs = 123; goto _test_eof;
_test_eof124: cs = 124; goto _test_eof;
_test_eof: {}
if ( p == eof )
{
switch ( cs ) {
case 126:
#line 3423 "http.d.rl"
{{
zone = cast(TimeZone)UTC();;
}}
break;
case 125:
#line 3375 "http.d.rl"
{{
year = std.conv.parse!int(line[(start - line.ptr) .. (p - line.ptr)]);
}}
#line 3423 "http.d.rl"
{{
zone = cast(TimeZone)UTC();;
}}
break;
#line 8046 "http.d"
default: break;
}
}
_out: {}
}
#line 2564 "http.d.rl"
mixin(finishParser!"parseDate");
if(zone)
return SysTime(DateTime(year, month, day, hour, minute, second), cast(immutable TimeZone)zone);
else
return SysTime(DateTime(year, month, day, hour, minute, second));
}
unittest
{
auto time = parseDate("Fri, 31 Dec 1999 23:59:57 GMT");
assert(time.year == 1999);
assert(time.month == Month.dec);
assert(time.day == 31);
assert(time.hour == 23);
assert(time.minute == 59);
assert(time.second == 57);
assert(time.timezone == UTC());
}
unittest
{
auto time = parseDate("Fri, 31 Dec 1999 23:59:57 GMT");
assert(time.year == 1999);
assert(time.month == Month.dec);
assert(time.day == 31);
assert(time.hour == 23);
assert(time.minute == 59);
assert(time.second == 57);
assert(time.timezone == UTC());
}
unittest
{
auto time = parseDate("Sat, 30-Apr-2011 15:59:28 GMT");
assert(time.year == 2011);
assert(time.month == Month.apr);
assert(time.day == 30);
assert(time.hour == 15);
assert(time.minute == 59);
assert(time.second == 28);
assert(time.timezone == UTC());
}
unittest
{
auto time = parseDate("fri, 10-aug-2012 16:48:59 gmt");
assert(time.year == 2012);
assert(time.month == Month.aug);
assert(time.day == 10);
assert(time.hour == 16);
assert(time.minute == 48);
assert(time.second == 59);
assert(time.timezone == UTC());
}
unittest
{
auto time = parseDate("Sat May 20 15:21:51 2000");
assert(time.year == 2000);
assert(time.month == Month.may);
assert(time.day == 20);
assert(time.hour == 15);
assert(time.minute == 21);
assert(time.second == 51);
assert(time.timezone == UTC());
}
/**
* Formats SysTime into $(LINK2 http://tools.ietf.org/html/rfc2616#section-3.3.1, HTTP-date)
* (rfc1123-date format). time is automatically converted to UTC.
*/
string formatDateString(SysTime time)
{
auto writer = appender!string();
formatDate(writer, time);
return writer.data;
}
///ditto
void formatDate(T)(T writer, SysTime time)
if (isOutputRange!(T, string))
{
time.timezone = UTC();
//wkday
final switch(time.dayOfWeek)
{
case DayOfWeek.sun:
writer.put("Sun");
break;
case DayOfWeek.mon:
writer.put("Mon");
break;
case DayOfWeek.tue:
writer.put("Tue");
break;
case DayOfWeek.wed:
writer.put("Wed");
break;
case DayOfWeek.thu:
writer.put("Thu");
break;
case DayOfWeek.fri:
writer.put("Fri");
break;
case DayOfWeek.sat:
writer.put("Sat");
break;
}
//"," SP
writer.put(", ");
//date1: 2DIGIT SP
formattedWrite(writer, "%02s ", time.day);
//date1: month
final switch(time.month)
{
case Month.jan:
writer.put("Jan");
break;
case Month.feb:
writer.put("Feb");
break;
case Month.mar:
writer.put("Mar");
break;
case Month.apr:
writer.put("Apr");
break;
case Month.may:
writer.put("May");
break;
case Month.jun:
writer.put("Jun");
break;
case Month.jul:
writer.put("Jul");
break;
case Month.aug:
writer.put("Aug");
break;
case Month.sep:
writer.put("Sep");
break;
case Month.oct:
writer.put("Oct");
break;
case Month.nov:
writer.put("Nov");
break;
case Month.dec:
writer.put("Dec");
break;
}
//date1: SP 4DIGIT
formattedWrite(writer, " %04s", time.year);
//SP
writer.put(" ");
//time: 2DIGIT ":" 2DIGIT ":" 2DIGIT
formattedWrite(writer, "%02s:%02s:%02s", time.hour, time.minute, time.second);
//SP "GMT"
writer.put(" GMT");
}
unittest
{
string test = formatDateString(SysTime(DateTime(2011, 3, 24, 17, 52, 01), UTC()));
assert(test == "Thu, 24 Mar 2011 17:52:01 GMT");
test = formatDateString(SysTime(DateTime(89, 1, 2, 3, 4, 5), UTC()));
assert(test == "Sun, 02 Jan 0089 03:04:05 GMT");
test = formatDateString(SysTime(DateTime(89, 2, 2, 3, 4, 5), UTC()));
assert(test == "Wed, 02 Feb 0089 03:04:05 GMT");
test = formatDateString(SysTime(DateTime(89, 3, 2, 3, 4, 5), UTC()));
assert(test == "Wed, 02 Mar 0089 03:04:05 GMT");
test = formatDateString(SysTime(DateTime(89, 4, 2, 3, 4, 5), UTC()));
assert(test == "Sat, 02 Apr 0089 03:04:05 GMT");
test = formatDateString(SysTime(DateTime(89, 5, 2, 3, 4, 5), UTC()));
assert(test == "Mon, 02 May 0089 03:04:05 GMT");
test = formatDateString(SysTime(DateTime(89, 6, 2, 3, 4, 5), UTC()));
assert(test == "Thu, 02 Jun 0089 03:04:05 GMT");
test = formatDateString(SysTime(DateTime(89, 7, 2, 3, 4, 5), UTC()));
assert(test == "Sat, 02 Jul 0089 03:04:05 GMT");
test = formatDateString(SysTime(DateTime(89, 8, 2, 3, 4, 5), UTC()));
assert(test == "Tue, 02 Aug 0089 03:04:05 GMT");
test = formatDateString(SysTime(DateTime(89, 9, 2, 3, 4, 5), UTC()));
assert(test == "Fri, 02 Sep 0089 03:04:05 GMT");
test = formatDateString(SysTime(DateTime(89, 10, 2, 3, 4, 5), UTC()));
assert(test == "Sun, 02 Oct 0089 03:04:05 GMT");
test = formatDateString(SysTime(DateTime(89, 11, 2, 3, 4, 5), UTC()));
assert(test == "Wed, 02 Nov 0089 03:04:05 GMT");
test = formatDateString(SysTime(DateTime(89, 12, 2, 3, 4, 5), UTC()));
assert(test == "Fri, 02 Dec 0089 03:04:05 GMT");
}
/*
* Public Api to format and parse Headers
*/
/**
* Test if a string can be parsed into a T
*
* Examples:
* ---------------------------------------
* assert(canParseHeader!AgeHeader);
* ---------------------------------------
*/
template canParseHeader(T)
{
enum bool canParseHeader = __traits(compiles, T.parse(""))
&& is(ReturnType!(T.parse) == T)
&& __traits(compiles, T.Key)
&& is(typeof(T.Key) == string);
}
/**
* Test if a T can be formatted into a string
*
* Examples:
* ---------------------------------------
* assert(canFormatHeader!AcceptHeader);
* ---------------------------------------
*/
template canFormatHeader(T)
{
enum bool canFormatHeader = __traits(compiles, T.init.format(appender!string()))
&& __traits(compiles, T.Key)
&& is(typeof(T.Key) == string);
}
/**
* Format a header.
* Output format is like this: "[Key]: [Value]" --> "Host: www.w3.org"
*
* Note: There is no newline appended at the end!
*
* Examples:
* ---------------------------------------
* IfModifiedSinceHeader head;
* head.Date = SysTime(DateTime(2011, 3, 24, 19, 21, 0), UTC());
* string headerString = formatHeader(head);
* assert(headerString == "If-Modified-Since: Thu, 24 Mar 2011 19:21:00 GMT");
* ---------------------------------------
*/
string formatHeader(T)(T header)
if (canFormatHeader!T)
{
auto writer = appender!string();
writer.put(T.Key);
writer.put(": ");
header.format(writer);
return writer.data;
}
///ditto
void formatHeader(T, O)(T header, O output)
if (canFormatHeader!T && isOutputRange!O)
{
writer.put(T.Key);
writer.put(": ");
header.format(output);
}
unittest
{
IfModifiedSinceHeader head;
head.Date = SysTime(DateTime(2011, 3, 24, 19, 21, 0), UTC());
string headerString = formatHeader(head);
assert(headerString == "If-Modified-Since: Thu, 24 Mar 2011 19:21:00 GMT");
}
/**
* Format a header.
* Output format is like this: "[Value]" --> "www.w3.org"
*
* Note: There is no newline appended at the end!
* Examples:
* ---------------------------------------
* IfModifiedSinceHeader head;
* head.Date = SysTime(DateTime(2011, 3, 24, 19, 21, 0), UTC());
* string headerString = formatHeaderValue(head);
* assert(headerString == "Thu, 24 Mar 2011 19:21:00 GMT");
* ---------------------------------------
*/
string formatHeaderValue(T)(T header)
if (canFormatHeader!T)
{
auto writer = appender!string();
header.format(writer);
return writer.data;
}
///ditto
void formatHeaderValue(T, O)(T header, O output)
if (canFormatHeader!T && isOutputRange!O)
{
header.format(output);
}
unittest
{
IfModifiedSinceHeader head;
head.Date = SysTime(DateTime(2011, 3, 24, 19, 21, 0), UTC());
string headerString = formatHeaderValue(head);
assert(headerString == "Thu, 24 Mar 2011 19:21:00 GMT");
}
/++
+ Parse a string into a T header.
+ Input format is like this: "[Key]: [Value]" --> "Host: www.w3.org"
+
+ This function does a case insensitive check if the [Key] in the input
+ string matches the T header type.
+
+ Note:
+ The input must be exactly one line with the newline char at the end.
+ Continuations are not handled by this function.
+
+ Throws:
+ ParserException
+ Examples:
+ ---------------------------------------
+ auto header = parseHeader!(ContentRangeHeader)("Content-Range: bytes 21010-47021/47022\r\n");
+ assert(header.Range.From.Value == 21010);
+ assert(header.Range.From.Unknown == false);
+ assert(header.Range.To.Value == 47021);
+ assert(header.Range.To.Unknown == false);
+ assert(header.Range.Length.Value == 47022);
+ assert(header.Range.Length.Unknown == false);
+
+ header = parseHeaderValue!(ContentRangeHeader)("bytes */*");
+ assert(header.Range.From.Unknown == true);
+ assert(header.Range.To.Unknown == true);
+ assert(header.Range.Length.Unknown == true);
+ ---------------------------------------
+/
T parseHeader(T)(string line)
if(canParseHeader!T)
{
auto resp = parseResponse(line);
if(resp.Type != ResponseType.Header)
{
throw new ParserException(line, 0, format("Unexpected response: " ~
"Expected a header response, but got '%s'", resp.Type));
}
else if(tolower(resp.Key) != tolower(T.Key))
{
throw new ParserException(line, 0, format("Unexpected header: " ~
"Expected '%s' Got '%s'", T.Key, resp.Key));
}
return T.parse(resp.Value);
}
unittest
{
auto header = parseHeader!(ContentRangeHeader)("Content-Range: bytes 21010-47021/47022\r\n");
assert(header.Range.From.Value == 21010);
assert(header.Range.From.Unknown == false);
assert(header.Range.To.Value == 47021);
assert(header.Range.To.Unknown == false);
assert(header.Range.Length.Value == 47022);
assert(header.Range.Length.Unknown == false);
header = parseHeaderValue!(ContentRangeHeader)("bytes */*");
assert(header.Range.From.Unknown == true);
assert(header.Range.To.Unknown == true);
assert(header.Range.Length.Unknown == true);
}
/++
+ Parse a string into a T header.
+ Input format is like this: "[Value]" --> "www.w3.org"
+
+ Note:
+ The input must be exactly one line with the newline char at the end.
+ Continuations are not handled by this function.
+
+ Throws:
+ ParserException
+ Examples:
+ ---------------------------------------
+ auto header = parseHeaderValue!(ContentRangeHeader)("bytes 21010-47021/47022");
+ assert(header.Range.From.Value == 21010);
+ assert(header.Range.From.Unknown == false);
+ assert(header.Range.To.Value == 47021);
+ assert(header.Range.To.Unknown == false);
+ assert(header.Range.Length.Value == 47022);
+ assert(header.Range.Length.Unknown == false);
+
+ header = parseHeaderValue!(ContentRangeHeader)("bytes */*");
+ assert(header.Range.From.Unknown == true);
+ assert(header.Range.To.Unknown == true);
+ assert(header.Range.Length.Unknown == true);
+ ---------------------------------------
+/
T parseHeaderValue(T)(string line)
if(canParseHeader!T)
{
return T.parse(line);
}
unittest
{
assert(canFormatHeader!AcceptHeader);
}
/*
* Helper templates for ragel parsers
*/
template initParser()
{
const string initParser = "int cs;\n" ~
"const (char)* p = line.ptr;\n" ~
"const char* pe = line.ptr + line.length;\n" ~
"const char* eof = pe;\n";
}
template initParser(string source)
{
const string initParser = "int cs;\n" ~
"const (char)* p = " ~ source ~ ".ptr;\n" ~
"const char* pe = " ~ source ~ ".ptr + " ~ source ~ ".length;\n" ~
"const char* eof = pe;\n";
}
template finishParser(string parserName)
{
const string finishParser = "if(cs == " ~ parserName ~ "_error)\n" ~
"throw new ParserException(line, p - line.ptr);\n" ~
"if(cs < " ~ parserName ~ "_first_final)\n" ~
"throw new InsufficientInputException(line);\n";
}
/*
* Ragel FSM definitions
*/
#line 3068 "http.d.rl"
#line 8502 "http.d"
enum int parseResponse_start = 1;
enum int parseResponse_first_final = 25;
enum int parseResponse_error = 0;
enum int parseResponse_en_main = 1;
#line 3102 "http.d.rl"
#line 8514 "http.d"
enum int parseStatusLine_start = 1;
enum int parseStatusLine_first_final = 17;
enum int parseStatusLine_error = 0;
enum int parseStatusLine_en_main = 1;
#line 3135 "http.d.rl"
#line 8526 "http.d"
enum int isToken_start = 1;
enum int isToken_first_final = 2;
enum int isToken_error = 0;
enum int isToken_en_main = 1;
#line 3144 "http.d.rl"
#line 8538 "http.d"
enum int isText_start = 3;
enum int isText_first_final = 3;
enum int isText_error = 0;
enum int isText_en_main = 3;
#line 3153 "http.d.rl"
#line 8550 "http.d"
enum int parseCommaList_start = 11;
enum int parseCommaList_first_final = 11;
enum int parseCommaList_error = 0;
enum int parseCommaList_en_main = 11;
#line 3175 "http.d.rl"
#line 8562 "http.d"
enum int parseContentRangeHeader_start = 1;
enum int parseContentRangeHeader_first_final = 13;
enum int parseContentRangeHeader_error = 0;
enum int parseContentRangeHeader_en_main = 1;
#line 3213 "http.d.rl"
#line 8574 "http.d"
enum int parseContentTypeHeader_start = 1;
enum int parseContentTypeHeader_first_final = 6;
enum int parseContentTypeHeader_error = 0;
enum int parseContentTypeHeader_en_main = 1;
#line 3239 "http.d.rl"
#line 8586 "http.d"
enum int parseParameterList_start = 32;
enum int parseParameterList_first_final = 32;
enum int parseParameterList_error = 0;
enum int parseParameterList_en_main = 32;
#line 3269 "http.d.rl"
#line 8598 "http.d"
enum int parseETag_start = 1;
enum int parseETag_first_final = 8;
enum int parseETag_error = 0;
enum int parseETag_en_main = 1;
#line 3291 "http.d.rl"
#line 8610 "http.d"
enum int parsePragmaEntry_start = 1;
enum int parsePragmaEntry_first_final = 8;
enum int parsePragmaEntry_error = 0;
enum int parsePragmaEntry_en_main = 1;
#line 3320 "http.d.rl"
#line 8622 "http.d"
enum int parseTransferCoding_start = 1;
enum int parseTransferCoding_first_final = 4;
enum int parseTransferCoding_error = 0;
enum int parseTransferCoding_en_main = 1;
#line 3349 "http.d.rl"
#line 8634 "http.d"
enum int parseProduct_start = 1;
enum int parseProduct_first_final = 7;
enum int parseProduct_error = 0;
enum int parseProduct_en_main = 1;
#line 3367 "http.d.rl"
#line 8646 "http.d"
enum int parseDate_start = 1;
enum int parseDate_first_final = 125;
enum int parseDate_error = 0;
enum int parseDate_en_main = 1;
#line 3463 "http.d.rl"
#line 8658 "http.d"
enum int parseRetryAfter_start = 1;
enum int parseRetryAfter_first_final = 4;
enum int parseRetryAfter_error = 0;
enum int parseRetryAfter_en_main = 1;
#line 3481 "http.d.rl"
/**
* This module contains parsers, formatters and data types for the _http
* protocol.
*
* Headers from rfc2616 which are currently not implemented:
* $(UL
* $(LI $(LINK2 http://tools.ietf.org/html/rfc2616#section-14.8, AuthorizationHeader))
* $(LI $(LINK2 http://tools.ietf.org/html/rfc2616#section-14.9, CacheControlHeader))
* $(LI $(LINK2 http://tools.ietf.org/html/rfc2616#section-14.33, ProxyAuthenticateHeader))
* $(LI $(LINK2 http://tools.ietf.org/html/rfc2616#section-14.34, ProxyAuthorizationHeader))
* $(LI $(LINK2 http://tools.ietf.org/html/rfc2616#section-14.38, ServerHeader))
* $(LI $(LINK2 http://tools.ietf.org/html/rfc2616#section-14.43, UserAgentHeader))
* $(LI $(LINK2 http://tools.ietf.org/html/rfc2616#section-14.45, ViaHeader))
* $(LI $(LINK2 http://tools.ietf.org/html/rfc2616#section-14.46, WarningHeader))
* $(LI $(LINK2 http://tools.ietf.org/html/rfc2616#section-14.47, WWWAuthenticateHeader))
* )
* Synopsis:
* ---------------------------------------
* //Formatting a header
* IfModifiedSinceHeader head;
* head.Date = SysTime(DateTime(2011, 3, 24, 19, 21, 0), UTC());
* string headerString = formatHeaderValue(head);
* assert(headerString == "Thu, 24 Mar 2011 19:21:00 GMT");
* headerString = formatHeader(head);
* assert(headerString == "If-Modified-Since: Thu, 24 Mar 2011 19:21:00 GMT");
*
* //Parsing a response
* auto resp = parseResponse("HTTP/1.2 200 OK\r\n");
* assert(resp.Type == ResponseType.StatusLine);
* assert(resp.Line == "HTTP/1.2 200 OK\r\n");
*
* auto sline = StatusLine.parse(resp.Line);
* assert(sline.Major == 1);
* assert(sline.Minor == 2);
* assert(sline.StatusCode == 200);
* assert(sline.Reason == "OK");
*
* resp = parseResponse("Content-Length: 348\r\n");
* assert(resp.Type == ResponseType.Header);
* assert(resp.Key == "Content-Length");
* auto header = parseHeaderValue!(ContentLengthHeader)(resp.Value);
* assert (header.Length == 348);
* ---------------------------------------
* Standards: Conforms to $(LINK2 http://tools.ietf.org/html/rfc2616,rfc2616)
* License: <a href="http://www.boost.org/LICENSE_1_0.txt">Boost License 1.0</a>
* Authors: Johannes Pfau
* Copyright: Copyright Johannes Pfau 2011.
*/
/* Copyright Johannes Pfau 2011.
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
*/
module std.protocol.http;
import std.array, std.base64, std.conv, std.datetime, std.format;
import std.range, std.string, std.traits;
/*
* Helper data types follow.
* Names according to rfc2616
*/
/**
* Data type holding a http
* $(LINK2 http://tools.ietf.org/html/rfc2616#section-6, response message)
*/
struct Response
{
///The response line type
ResponseType Type;
union
{
///The raw value for unknown responses and StatusLine responses
string Line;
///The key in a header response
string Key;
}
///The value in a header response
string Value;
}
/**
* $(LINK2 http://tools.ietf.org/html/rfc2616#section-6,
* HTTP response message type)
*/
enum ResponseType : ushort
{
/**
* $(LINK2 http://tools.ietf.org/html/rfc2616#section-6.1, status line)
*/
StatusLine,
/**
* $(LINK2 http://tools.ietf.org/html/rfc2616#section-6.2, response-header),
* $(LINK2 http://tools.ietf.org/html/rfc2616#section-7.1, entity-header) or
* $(LINK2 http://tools.ietf.org/html/rfc2616#section-4.5, general-header)
*/
Header,
/**
* Empty line [CRLF]
*/
Empty,
/**
* Unknown line
*/
Other
}
/**
* Data type holding a http
* $(LINK2 http://tools.ietf.org/html/rfc2616#section-6.1, status line)
*/
struct StatusLine
{
///HTTP-Version
uint Major, Minor;
///
uint StatusCode;
///
string Reason;
/**
* Parses a string
*
* Examples:
* ---------------------------------------
* auto resp = parseResponse("HTTP/1.2 200 OK\r\n");
* assert(resp.Type == ResponseType.StatusLine);
* assert(resp.Line == "HTTP/1.2 200 OK\r\n");
* auto sline = StatusLine.parse(resp.Line);
*
* assert(sline.Major == 1);
* assert(sline.Minor == 2);
* assert(sline.StatusCode == 200);
* assert(sline.Reason == "OK");
* ---------------------------------------
*/
static StatusLine parse(string line)
{
StatusLine sline;
const(char)* numstart;
mixin(initParser!());
%%{
machine parseStatusLine;
write init;
write exec;
}%%
mixin(finishParser!"parseStatusLine");
return sline;
}
/**
* Formats the StatusLine and writes it to the OutputRange writer.
* Note: There is no newline appended at the end!
*
* Examples:
* ---------------------------------------
* StatusLine sline;
* sline.Major = 1;
* sline.Minor = 1;
* sline.StatusCode = 200;
* sline.Reason = "OK";
* assert(sline.formatString() == "HTTP/1.1 200 OK");
* ---------------------------------------
*
* ---------------------------------------
* StatusLine sline;
* sline.Major = 1;
* sline.Minor = 1;
* sline.StatusCode = 200;
* sline.Reason = "OK";
* auto writer = appender!string();
* this.format(writer);
* assert(writer.data == "HTTP/1.1 200 OK");
* ---------------------------------------
*/
void format(T)(T writer) if (isOutputRange!(T, string))
{
assert(this.StatusCode < 1000, "StatusCode must be max 3 digits");
assert(isText(this.Reason), "Reason must be TEXT according to rfc2616");
assert(indexOf(this.Reason, '\r') == -1, "Reason must not contain '\\r'");
assert(indexOf(this.Reason, '\n') == -1, "Reason must not contain '\\n'");
formattedWrite(writer, "HTTP/%s.%s %03s %s", this.Major, this.Minor,
this.StatusCode, this.Reason);
}
///ditto
string formatString()
{
auto writer = appender!string();
this.format(writer);
return writer.data;
}
}
unittest
{
auto resp = parseResponse("HTTP/1.2 200 OK\r\n");
assert(resp.Type == ResponseType.StatusLine);
assert(resp.Line == "HTTP/1.2 200 OK\r\n");
auto sline = StatusLine.parse(resp.Line);
assert(sline.Major == 1);
assert(sline.Minor == 2);
assert(sline.StatusCode == 200);
assert(sline.Reason == "OK");
}
unittest
{
StatusLine sline;
sline.Major = 1;
sline.Minor = 1;
sline.StatusCode = 200;
sline.Reason = "OK";
assert(sline.formatString() == "HTTP/1.1 200 OK");
sline.Reason = "";
assert(sline.formatString() == "HTTP/1.1 200 ");
}
///$(LINK2 http://tools.ietf.org/html/rfc2616#section-14.1, media-range)
struct MediaRange
{
///
string Type;
///
string SubType;
///
string[string] Parameters;
///
float AcceptParam = -1f;
///
string[string] AcceptExtension;
}
///$(LINK2 http://tools.ietf.org/html/rfc2616#section-14.2, charset / q combination)
struct Charset
{
///
string Name;
///
float Q = -1f;
}
///$(LINK2 http://tools.ietf.org/html/rfc2616#section-14.2, content-coding / q combination)
struct ContentCoding
{
///
enum : ushort
{
///
Compress,
///
Gzip,
///
Deflate,
///
Identity,
///
Other
}
///
typeof(ContentCoding.Other) Type;
///
float Q = -1f;
///If Type is ContentCoding.Other this field contains the raw value
string Raw;
}
///$(LINK2 http://tools.ietf.org/html/rfc2616#section-14.4, language-range / q combination)
struct LanguageRange
{
///
string Main;
///
string Sub;
///
float Q = -1f;
}
///$(LINK2 http://tools.ietf.org/html/rfc2616#section-3.12, range-unit)
struct RangeUnit
{
///
enum : ushort
{
///
Bytes,
///
Other
}
///
typeof(RangeUnit.Other) Type;
///If Type is RangeUnit.Other this field contains the raw value
string Raw;
}
///$(LINK2 http://tools.ietf.org/html/rfc2616#section-5.1.1, http methods)
struct Method
{
enum : ushort
{
///
OPTIONS,
///
GET,
///
HEAD,
///
POST,
///
PUT,
///
DELETE,
///
TRACE,
///
CONNECT,
///
Other
}
///
typeof(Method.CONNECT) Type;
///
public this(typeof(Method.CONNECT) method)
{
this.Type = method;
}
///If Type is Method.Other this field contains the raw value
string Raw;
}
///$(LINK2 http://tools.ietf.org/html/rfc2616#section-14.10, connection-token)
struct ConnectionToken
{
enum : ushort
{
///
Close,
///
Other
}
///
typeof(ConnectionToken.Other) Type;
///If Type is ConnectionToken.Other this field contains the raw value
string Raw;
}
///$(LINK2 http://tools.ietf.org/html/rfc2616#section-14.4, language-range / q combination)
struct LanguageTag
{
///
string Main;
///
string Sub;
}
///$(LINK2 http://tools.ietf.org/html/rfc2616#section-14.16, positions used with content-range-spec)
struct BytePos
{
///
bool Unknown = false;
///
ulong Value;
}
///$(LINK2 http://tools.ietf.org/html/rfc2616#section-14.16, content-range-spec)
struct ContentRangeSpec
{
///
BytePos From;
///
BytePos To;
///
BytePos Length;
}
///$(LINK2 http://tools.ietf.org/html/rfc2616#section-3.7, media-type)
struct MediaType
{
///
string Type;
///
string SubType;
///
string[string] Parameters;
}
///$(LINK2 http://tools.ietf.org/html/rfc2616#section-3.11, entity-tag)
struct EntityTag
{
///
bool Weak = false;
///
string Value;
}
///$(LINK2 http://tools.ietf.org/html/rfc2616#section-14.20, expectation)
struct Expectation
{
enum : ushort
{
Other,
Continue
}
typeof(Expectation.Continue) Type;
string ExtensionKey;
string ExtensionValue;
string[string] ExtensionParameters;
}
///$(LINK2 http://tools.ietf.org/html/rfc2616#section-14.32, pragma-directive)
struct PragmaDirective
{
enum : ushort
{
///
NoCache,
///
Extension
}
///
typeof(PragmaDirective.Extension) Type;
///
string ExtensionKey;
///
string ExtensionValue;
}
///$(LINK2 http://tools.ietf.org/html/rfc2616#section-14.35.1, byte-range-spec)
struct ByteRangeSpec
{
union
{
///
long First;
///
long Suffix;
}
enum : ushort
{
SuffixSpec,
ByteSpec
}
typeof(ByteRangeSpec.ByteSpec) Type;
///
BytePos Last;
}
///$(LINK2 http://tools.ietf.org/html/rfc2616#section-3.8, product)
struct Product
{
///
string Name;
///
string Version;
}
///$(LINK2 http://tools.ietf.org/html/rfc2616#section-14.39, t-codings)
struct TCoding
{
///
bool Trailers = false;
///
string[string] Parameters, AcceptParameters;
///
string Extension;
///
float Q = -1f;
}
///$(LINK2 http://tools.ietf.org/html/rfc2616#section-3.6, transfer-coding)
struct TransferCoding
{
enum : ushort
{
///
Chunked,
///
Custom
}
///
typeof(TransferCoding.Custom) Type;
///
string Raw;
///
string[string] Parameters;
}
/*
* Header types follow
*/
/**
* Data type for the $(LINK2 http://tools.ietf.org/html/rfc2616#section-14.1,
* "Accept" header)
*
* Type:
* request-header
*/
struct AcceptHeader
{
public:
///
MediaRange[] Media;
static string Key = "Accept";
void format(T)(T writer) if (isOutputRange!(T, string))
{
foreach(i, range; Media)
{
debug
{
if(range.SubType != "*")
assert(range.Type != "*");
}
assert(isToken(range.Type));
assert(isToken(range.SubType));
formattedWrite(writer, "%s/%s", range.Type, range.SubType);
foreach(key, value; range.Parameters)
{
writeParameter(writer, key, value);
}
if(range.AcceptParam >= 0)
{
assert(range.AcceptParam <= 1);
formattedWrite(writer, ";q=%.3f", range.AcceptParam);
foreach(key, value; range.AcceptExtension)
{
writeParameter(writer, key, value);
}
}
if(i != Media.length - 1)
writer.put(", ");
}
}
}
/**
* Data type for the $(LINK2 http://tools.ietf.org/html/rfc2616#section-14.2,
* "Accept-Charset" header)
*
* Type:
* request-header
*/
struct AcceptCharsetHeader
{
public:
///
Charset[] Charsets;
static string Key = "Accept-Charset";
void format(T)(T writer) if (isOutputRange!(T, string))
{
foreach(i, charset; Charsets)
{
assert(isToken(charset.Name));
writer.put(charset.Name);
if(charset.Q >= 0)
{
assert(charset.Q <= 1);
formattedWrite(writer, ";q=%.3f", charset.Q);
}
if(i != Charsets.length - 1)
writer.put(", ");
}
}
}
/**
* Data type for the $(LINK2 http://tools.ietf.org/html/rfc2616#section-14.3,
* "Accept-Encoding" header)
*
* Type:
* request-header
*/
struct AcceptEncodingHeader
{
public:
///
ContentCoding[] Codings;
static string Key = "Accept-Encoding";
void format(T)(T writer) if (isOutputRange!(T, string))
{
foreach(i, entry; Codings)
{
final switch(entry.Type)
{
case ContentCodingType.Compress:
writer.put("compress");
break;
case ContentCodingType.Deflate:
writer.put("deflate");
break;
case ContentCodingType.Gzip:
writer.put("gzip");
break;
case ContentCodingType.Identity:
writer.put("identity");
break;
case ContentCodingType.Other:
assert(isToken(entry.Raw));
writer.put(entry.Coding.Raw);
break;
}
if(entry.Q >= 0)
{
assert(entry.Q <= 1);
formattedWrite(writer, ";q=%.3f", entry.Q);
}
if(i != Codings.length - 1)
writer.put(", ");
}
}
}
/**
* Data type for the $(LINK2 http://tools.ietf.org/html/rfc2616#section-14.4,
* "Accept-Language" header)
*
* Type:
* request-header
*/
struct AcceptLanguageHeader
{
public:
///
LanguageRange[] Languages;
static string Key = "Accept-Language";
void format(T)(T writer) if (isOutputRange!(T, string))
{
foreach(i, entry; Languages)
{
assert(entry.Main.length <= 8);
assert(entry.Sub.length <= 8);
debug
{
if(entry.Tag.Main != "*")
{
assert(isalpha(entry.Sub));
assert(isalpha(entry.Main));
}
else
{
assert(entry.Tag.Sub == "");
}
}
writer.put(entry.Main);
if(entry.Sub != "")
{
writer.put("-");
writer.put(entry.Sub);
}
if(entry.Q >= 0)
{
assert(entry.Q <= 1);
formattedWrite(writer, ";q=%.3f", entry.Q);
}
if(i != Languages.length - 1)
writer.put(", ");
}
}
}
/**
* Data type for the $(LINK2 http://tools.ietf.org/html/rfc2616#section-14.5,
* "Accept-Ranges" header)
*
* Type:
* response-header
*/
struct AcceptRangesHeader
{
public:
///
bool None = false;
///
RangeUnit[] AcceptableRanges;
static string Key = "Accept-Ranges";
void format(T)(T writer) if (isOutputRange!(T, string))
{
if(this.None)
{
writer.put("none");
return;
}
foreach(i, entry; AcceptableRanges)
{
if(entry.Type == RangeUnit.Bytes)
writer.put("bytes");
else
{
assert(isToken(entry.Raw));
writer.put(entry.Raw);
}
if(i != AcceptableRanges.length - 1)
writer.put(", ");
}
}
static AcceptRangesHeader parse(string line)
{
string[] list = parseCommaList(line);
AcceptRangesHeader hdr;
if(list.length == 1 && list[0] == "none")
{
hdr.None = true;
return hdr;
}
foreach(entry; list)
{
RangeUnit unit;
if(entry == "bytes")
unit.Type = RangeUnit.Bytes;
else
{
unit.Type = RangeUnit.Other;
unit.Raw = entry;
}
hdr.AcceptableRanges ~= unit;
}
return hdr;
}
}
/**
* Data type for the $(LINK2 http://tools.ietf.org/html/rfc2616#section-14.6,
* "Age" header)
*
* Type:
* response-header
*/
struct AgeHeader
{
public:
///delta in seconds
ulong Age;
///
public this(ulong age)
{
this.Age = age;
}
static string Key = "Age";
void format(T)(T writer) if (isOutputRange!(T, string))
{
write(writer, Age);
}
static AgeHeader parse(string line)
{
return AgeHeader(std.conv.parse!ulong(line));
}
}
/**
* Data type for the $(LINK2 http://tools.ietf.org/html/rfc2616#section-14.7,
* "Allow" header)
*
* Type:
* entity-header
*/
struct AllowHeader
{
public:
///
Method[] Methods;
static string Key = "Allow";
void format(T)(T writer) if (isOutputRange!(T, string))
{
foreach(i, entry; Methods)
{
final switch(entry.Type)
{
case Method.OPTIONS:
writer.put("OPTIONS");
break;
case Method.GET:
writer.put("GET");
break;
case Method.HEAD:
writer.put("HEAD");
break;
case Method.POST:
writer.put("POST");
break;
case Method.PUT:
writer.put("PUT");
break;
case Method.DELETE:
writer.put("DELETE");
break;
case Method.TRACE:
writer.put("TRACE");
break;
case Method.CONNECT:
writer.put("CONNECT");
break;
case Method.Other:
assert(entry.Raw != "");
assert(isToken(entry.Raw));
writer.put(entry.Raw);
break;
}
if(i != Methods.length - 1)
writer.put(", ");
}
}
static AllowHeader parse(string line)
{
string[] list = parseCommaList(line);
AllowHeader allow;
foreach(entry; list)
{
switch(entry)
{
case "OPTIONS":
allow.Methods ~= Method(Method.OPTIONS);
break;
case "GET":
allow.Methods ~= Method(Method.GET);
break;
case "HEAD":
allow.Methods ~= Method(Method.HEAD);
break;
case "POST":
allow.Methods ~= Method(Method.POST);
break;
case "PUT":
allow.Methods ~= Method(Method.PUT);
break;
case "DELETE":
allow.Methods ~= Method(Method.DELETE);
break;
case "TRACE":
allow.Methods ~= Method(Method.TRACE);
break;
case "CONNECT":
allow.Methods ~= Method(Method.CONNECT);
break;
default:
auto m = Method(Method.Other);
m.Raw = entry;
allow.Methods ~= m;
break;
}
}
return allow;
}
}
/**
* Data type for the $(LINK2 http://tools.ietf.org/html/rfc2616#section-14.10,
* "Connection" header)
*
* Type:
* general-header
*/
struct ConnectionHeader
{
public:
///
ConnectionToken[] Options;
static string Key = "Connection";
void format(T)(T writer) if (isOutputRange!(T, string))
{
foreach(i, entry; Options)
{
if(entry.Type == ConnectionToken.Close)
{
writer.put("close");
assert(entry.Raw == "");
}
else
{
assert(entry.Raw != "");
assert(isToken(entry.Raw));
writer.put(entry.Raw);
}
if(i != Options.length - 1)
writer.put(", ");
}
}
static ConnectionHeader parse(string line)
{
ConnectionHeader con;
string[] list = parseCommaList(line);
foreach(entry; list)
{
switch(tolower(entry))
{
case "close":
ConnectionToken token;
token.Type = ConnectionToken.Close;
con.Options ~= token;
break;
default:
ConnectionToken token;
token.Type = ConnectionToken.Other;
token.Raw = entry;
con.Options ~= token;
break;
}
}
return con;
}
}
/**
* Data type for the $(LINK2 http://tools.ietf.org/html/rfc2616#section-14.11,
* "Content-Encoding" header)
*
* Type:
* entity-header
*/
struct ContentEncodingHeader
{
public:
///
ContentCoding[] Codings;
static string Key = "Content-Encoding";
void format(T)(T writer) if (isOutputRange!(T, string))
{
foreach(i, entry; Codings)
{
final switch(entry.Type)
{
case ContentCoding.Compress:
assert(entry.Raw == "");
writer.put("compress");
break;
case ContentCoding.Gzip:
assert(entry.Raw == "");
writer.put("gzip");
break;
case ContentCoding.Deflate:
assert(entry.Raw == "");
writer.put("deflate");
break;
case ContentCoding.Identity:
assert(entry.Raw == "");
writer.put("identity");
break;
case ContentCoding.Other:
assert(entry.Raw != "");
assert(isToken(entry.Raw));
writer.put(entry.Raw);
break;
}
if(i != Codings.length - 1)
writer.put(", ");
}
}
static ContentEncodingHeader parse(string line)
{
ContentEncodingHeader enc;
string[] list = parseCommaList(line);
foreach(entry; list)
{
ContentCoding coding;
switch(tolower(entry))
{
case "compress":
coding.Type = ContentCoding.Compress;
enc.Codings ~= coding;
break;
case "deflate":
coding.Type = ContentCoding.Deflate;
enc.Codings ~= coding;
break;
case "gzip":
coding.Type = ContentCoding.Gzip;
enc.Codings ~= coding;
break;
case "identity":
coding.Type = ContentCoding.Identity;
enc.Codings ~= coding;
break;
default:
coding.Type = ContentCoding.Other;
coding.Raw = entry;
enc.Codings ~= coding;
break;
}
}
return enc;
}
}
/**
* Data type for the $(LINK2 http://tools.ietf.org/html/rfc2616#section-14.12,
* "Content-Language" header)
*
* Type:
* entity-header
*/
struct ContentLanguageHeader
{
public:
///
LanguageTag[] Tags;
static string Key = "Content-Language";
void format(T)(T writer) if (isOutputRange!(T, string))
{
foreach(i, entry; Tags)
{
assert(isToken(entry.Main));
writer.put(entry.Main);
if(entry.Sub != "")
{
assert(isToken(entry.Sub));
writer.put("-");
writer.put(entry.Sub);
}
if(i != Tags.length - 1)
writer.put(", ");
}
}
static ContentLanguageHeader parse(string line)
{
ContentLanguageHeader lang;
string[] list = parseCommaList(line);
foreach(entry; list)
{
LanguageTag tag;
size_t i;
if((i = indexOf(entry, '-')) != -1)
{
tag.Main = strip(entry[0 .. i]);
tag.Sub = strip(entry[i+1 .. $]);
}
else
{
tag.Main = strip(entry);
}
lang.Tags ~= tag;
}
return lang;
}
}
/**
* Data type for the $(LINK2 http://tools.ietf.org/html/rfc2616#section-14.13,
* "Content-Length" header)
*
* Type:
* entity-header
*/
struct ContentLengthHeader
{
public:
///
ulong Length;
static string Key = "Content-Length";
void format(T)(T writer) if (isOutputRange!(T, string))
{
write(writer, Length);
}
static ContentLengthHeader parse(string line)
{
ContentLengthHeader len;
len.Length = std.conv.parse!ulong(line);
return len;
}
}
/**
* Data type for the $(LINK2 http://tools.ietf.org/html/rfc2616#section-14.14,
* "Content-Location" header)
*
* Type:
* entity-header
*/
struct ContentLocationHeader
{
public:
///
string Location;
static string Key = "Content-Location";
void format(T)(T writer) if (isOutputRange!(T, string))
{
//TODO: verify URI in debug mode
writer.put(Location);
}
static ContentLocationHeader parse(string line)
{
ContentLocationHeader loc;
loc.Location = line;
return loc;
}
}
/**
* Data type for the $(LINK2 http://tools.ietf.org/html/rfc2616#section-14.15,
* "Content-MD5" header)
*
* Type:
* entity-header
*/
struct ContentMD5Header
{
public:
///
ubyte[16] MD5;
static string Key = "Content-MD5";
void format(T)(T writer) if (isOutputRange!(T, string))
{
char[24] buf;
writer.put(encode(this.MD5, buf));
}
static ContentMD5Header parse(string line)
{
ContentMD5Header cmd5;
//strip trailing '=' for length calculation
size_t nlength;
for(nlength = line.length - 1; nlength >= 0; nlength--)
{
if(line[nlength] != '=')
break;
}
if(Base64.decodeLength(nlength) > 16)
return cmd5;
ubyte[24] buf;
Base64.decode!(string, ubyte[])(line, buf);
cmd5.MD5 = buf[0 .. 16];
return cmd5;
}
}
/**
* Data type for the $(LINK2 http://tools.ietf.org/html/rfc2616#section-14.16,
* "Content-Range" header)
*
* Type:
* entity-header
*/
struct ContentRangeHeader
{
public:
///
ContentRangeSpec Range;
static string Key = "Content-Range";
void format(T)(T writer) if (isOutputRange!(T, string))
{
writer.put("bytes ");
if(Range.From.Unknown || Range.To.Unknown)
{
writer.put("*");
}
else
{
formattedWrite(writer, "%s", Range.From.Value);
writer.put("-");
formattedWrite(writer, "%s", Range.To.Value);
}
writer.put("/");
if(Range.Length.Unknown)
{
writer.put("*");
}
else
{
formattedWrite(writer, "%s", Range.Length.Value);
}
}
static ContentRangeHeader parse(string line)
{
ContentRangeHeader head;
const(char)* start;
mixin(initParser!());
%%{
machine parseContentRangeHeader;
write init;
write exec;
}%%
mixin(finishParser!"parseContentRangeHeader");
return head;
}
}
/**
* Data type for the $(LINK2 http://tools.ietf.org/html/rfc2616#section-14.17,
* "Content-Type" header)
*
* Type:
* entity-header
*/
struct ContentTypeHeader
{
public:
///
MediaType Type;
static string Key = "Content-Type";
void format(T)(T writer) if (isOutputRange!(T, string))
{
assert(isToken(Type.Type));
assert(isToken(Type.SubType));
writer.put(Type.Type);
writer.put("/");
writer.put(Type.SubType);
foreach(key, value; media.Parameters)
writeParameter(writer, key, value);
}
static ContentTypeHeader parse(string line)
{
ContentTypeHeader ctype;
MediaType ret;
const(char)* start;
mixin(initParser!());
%%{
machine parseContentTypeHeader;
write init;
write exec;
}%%
mixin(finishParser!"parseContentTypeHeader");
ctype.Type = ret;
return ctype;
}
}
/**
* Data type for the $(LINK2 http://tools.ietf.org/html/rfc2616#section-14.18,
* "Date" header)
*
* Type:
* general-header
*/
struct DateHeader
{
public:
///
SysTime Date;
static string Key = "Date";
void format(T)(T writer) if (isOutputRange!(T, string))
{
formatDate(Date);
}
static DateHeader parse(string line)
{
DateHeader head;
head.Date = parseDate(line);
return head;
}
}
/**
* Data type for the $(LINK2 http://tools.ietf.org/html/rfc2616#section-14.19,
* "ETag" header)
*
* Type:
* response-header
*/
struct ETagHeader
{
public:
///
public EntityTag Tag;
static string Key = "ETag";
void format(T)(T writer) if (isOutputRange!(T, string))
{
formatEntityTag(writer, Tag);
}
static ETagHeader parse(string line)
{
ETagHeader head;
EntityTag tag;
const(char)* start;
mixin(initParser!());
%%{
machine parseETag;
write init;
write exec;
}%%
mixin(finishParser!"parseETag");
head.Tag = tag;
return head;
}
}
/**
* Data type for the $(LINK2 http://tools.ietf.org/html/rfc2616#section-14.20,
* "Expect" header)
*
* Type:
* request-header
*/
struct ExpectHeader
{
public:
///
Expectation[] Expect;
static string Key = "Expect";
void format(T)(T writer) if (isOutputRange!(T, string))
{
foreach(i, entry; Expect)
{
if(entry.Type == Expectation.Continue)
{
assert(entry.ExtensionKey == "");
assert(entry.ExtensionValue == "");
assert(entry.ExtensionParameters.length == 0);
writer.put("100-continue");
}
else
{
assert(entry.ExtensionKey != "");
assert(isToken(entry.ExtensionKey));
writer.put(entry.ExtensionKey);
if(entry.ExtensionValue != "")
{
writer.put("=");
if(isToken(entry.ExtensionValue))
writer.put(entry.ExtensionValue);
else
writer.put(quote(entry.ExtensionValue));
}
foreach(key, value; entry.ExtensionParameters)
{
writeParameter(writer, key, value);
}
}
if(i != Expect.length - 1)
writer.put(", ");
}
}
}
/**
* Data type for the $(LINK2 http://tools.ietf.org/html/rfc2616#section-14.21,
* "Expires" header)
*
* Type:
* entity-header
*/
struct ExpiresHeader
{
public:
///
SysTime Date;
static string Key = "Expires";
void format(T)(T writer) if (isOutputRange!(T, string))
{
formatDate(writer, Date);
}
static ExpiresHeader parse(string line)
{
ExpiresHeader head;
head.Date = parseDate(line);
return head;
}
}
/**
* Data type for the $(LINK2 http://tools.ietf.org/html/rfc2616#section-14.22,
* "From" header)
*
* Type:
* request-header
*/
struct FromHeader
{
public:
///
string Email;
static string Key = "From";
void format(T)(T writer) if (isOutputRange!(T, string))
{
//TODO: Chek in debug with isemail
writer.put(this.email);
}
static FromHeader parse(string line)
{
FromHeader from;
from.Email = line;
return from;
}
}
/**
* Data type for the $(LINK2 http://tools.ietf.org/html/rfc2616#section-14.23,
* "Host" header)
*
* Type:
* request-header
*/
struct HostHeader
{
public:
///
string Host;
///
bool DefaultPort = true;
///
ushort Port;
static string Key = "Host";
void format(T)(T writer) if (isOutputRange!(T, string))
{
//TODO: check host in debug mode
assert(host != "");
writer.put(host);
if(!DefaultPort)
{
writer.put(':');
formattedWrite(writer, "%s", Port);
}
}
}
/**
* Data type for the $(LINK2 http://tools.ietf.org/html/rfc2616#section-14.24,
* "If-Match" header)
*
* Type:
* request-header
*/
struct IfMatchHeader
{
public:
///
bool All;
///
EntityTag[] Tags;
static string Key = "If-Match";
void format(T)(T writer) if (isOutputRange!(T, string))
{
if(All)
{
writer.put("*");
assert(Tags.length == 0);
}
else
{
assert(Tags.length > 0);
foreach(i, entry; Tags)
{
formatEntityTag(entry, writer);
if(i != header.Specific.length - 1)
writer.put(", ");
}
}
}
}
/**
* Data type for the $(LINK2 http://tools.ietf.org/html/rfc2616#section-14.25,
* "If-Modified-Since" header)
*
* Type:
* request-header
*/
struct IfModifiedSinceHeader
{
public:
///
SysTime Date;
static string Key = "If-Modified-Since";
void format(T)(T writer) if (isOutputRange!(T, string))
{
formatDate(writer, Date);
}
static IfModifiedSinceHeader parse(string line)
{
IfModifiedSinceHeader head;
head.Date = parseDate(line);
return head;
}
}
/**
* Data type for the $(LINK2 http://tools.ietf.org/html/rfc2616#section-14.26,
* "If-None-Match" header)
*
* Type:
* request-header
*/
struct IfNoneMatchHeader
{
public:
///
bool All;
///
EntityTag[] Tags;
static string Key = "If-None-Match";
void format(T)(T writer) if (isOutputRange!(T, string))
{
if(All)
{
writer.put("*");
assert(Tags.length == 0);
}
else
{
assert(Tags.length > 0);
foreach(i, entry; Tags)
{
formatEntityTag(entry, writer);
if(i != header.Specific.length - 1)
writer.put(", ");
}
}
}
}
/**
* Data type for the $(LINK2 http://tools.ietf.org/html/rfc2616#section-14.27,
* "If-Range" header)
*
* Type:
* request-header
*/
struct IfRangeHeader
{
public:
///
SysTime Time;
///
EntityTag Tag;
///Use a time value in the If-Range header instead of an EntityTag
bool useTime;
static string Key = "If-Range";
void format(T)(T writer) if (isOutputRange!(T, string))
{
if(useTime)
{
formatDate(entry, Date);
}
else
{
formatEntityTag(writer, Tag);
}
}
}
/**
* Data type for the $(LINK2 http://tools.ietf.org/html/rfc2616#section-14.28,
* "If-Unmodified-Since" header)
*
* Type:
* request-header
*/
struct IfUnmodifiedSinceHeader
{
public:
///
SysTime Date;
static string Key = "If-Unmodified-Since";
void format(T)(T writer) if (isOutputRange!(T, string))
{
formatDate(writer, Date);
}
static IfUnmodifiedSinceHeader parse(string line)
{
IfUnmodifiedSinceHeader head;
head.Date = parseDate(line);
return head;
}
}
/**
* Data type for the $(LINK2 http://tools.ietf.org/html/rfc2616#section-14.29,
* "Last-Modified" header)
*
* Type:
* entity-header
*/
struct LastModifiedHeader
{
public:
///
SysTime Date;
static string Key = "Last-Modified";
void format(T)(T writer) if (isOutputRange!(T, string))
{
formatDate(writer, Date);
}
static LastModifiedHeader parse(string line)
{
LastModifiedHeader head;
head.Date = parseDate(line);
return head;
}
}
/**
* Data type for the $(LINK2 http://tools.ietf.org/html/rfc2616#section-14.30,
* "Location" header)
*
* Type:
* response-header
*/
struct LocationHeader
{
public:
///
string Location;
static string Key = "Location";
void format(T)(T writer) if (isOutputRange!(T, string))
{
//TODO: verify URI in debug mode
writer.put(Location);
}
static LocationHeader parse(string line)
{
LocationHeader loc;
loc.Location = line;
return loc;
}
}
/**
* Data type for the $(LINK2 http://tools.ietf.org/html/rfc2616#section-14.31,
* "Max-Forwards" header)
*
* Type:
* request-header
*/
struct MaxForwardsHeader
{
public:
///
ulong Forwards;
static string Key = "Max-Forwards";
void format(T)(T writer) if (isOutputRange!(T, string))
{
formattedWrite(writer, Forwards);
}
static MaxForwardsHeader parse(string line)
{
MaxForwardsHeader mf;
mf.Forwards = std.conv.parse!ulong(line);
return mf;
}
}
/**
* Data type for the $(LINK2 http://tools.ietf.org/html/rfc2616#section-14.32,
* "Pragma" header)
*
* Type:
* general-header
*/
struct PragmaHeader
{
public:
///
PragmaDirective[] Directives;
static string Key = "Pragma";
void format(T)(T writer) if (isOutputRange!(T, string))
{
foreach(i, entry; Directives)
{
if(entry.Type == PragmaDirective.NoCache)
{
writer.put("no-cache");
assert(entry.ExtensionKey == "");
assert(entry.ExtensionValue == "");
}
else
{
assert(entry.ExtensionKey != "");
assert(isToken(entry.ExtensionKey));
writer.put(entry.ExtensionKey);
if(entry.ExtensionValue != "")
{
writer.put("=");
if(isToken(entry.ExtensionValue))
writer.put(entry.ExtensionValue);
else
writer.put(quote(entry.ExtensionValue));
}
}
if(i != Directives.length - 1)
writer.put(", ");
}
}
static PragmaHeader parse(string line)
{
PragmaHeader prag;
string[] list = parseCommaList(line);
foreach(entry; list)
{
PragmaDirective dir;
const(char)* start;
mixin(initParser!("entry"));
%%{
machine parsePragmaEntry;
write init;
write exec;
}%%
mixin(finishParser!"parsePragmaEntry");
prag.Directives ~= dir;
}
return prag;
}
}
/**
* Data type for the $(LINK2 http://tools.ietf.org/html/rfc2616#section-14.35,
* "Range" header)
*
* Type:
* request-header
*/
struct RangeHeader
{
public:
///
ByteRangeSpec[] Ranges;
static string Key = "Range";
void format(T)(T writer) if (isOutputRange!(T, string))
{
writer.put("bytes=");
foreach(i, entry; Ranges)
{
if(entry.Type == ByteRangeSpec.SuffixSpec)
{
writer.put("-");
formattedWrite(writer, "%s", entry.Suffix);
}
else
{
formattedWrite(writer, "%s", entry.First);
writer.put("-");
formattedWrite(writer, "%s", entry.Last);
}
if(i != Ranges.length - 1)
writer.put(",");
}
}
}
/**
* Data type for the $(LINK2 http://tools.ietf.org/html/rfc2616#section-14.36,
* "Referer" header)
*
* Type:
* request-header
*/
struct RefererHeader
{
public:
///
string Location;
static string Key = "Referer";
void format(T)(T writer) if (isOutputRange!(T, string))
{
//TODO: in debug mode check if Location is a valid URI
writer.put(Location);
}
static RefererHeader parse(string line)
{
RefererHeader refa;
refa.Location = line;
return refa;
}
}
/**
* Data type for the $(LINK2 http://tools.ietf.org/html/rfc2616#section-14.37,
* "Retry-After" header)
*
* Type:
* response-header
*/
struct RetryAfterHeader
{
public:
///
SysTime Date;
///
ulong Delta;
///Use a time value in the Retry-After header instead of delta seconds
bool useTime = false;
static string Key = "Retry-After";
void format(T)(T writer) if (isOutputRange!(T, string))
{
if(useTime)
{
formatDate(writer, Date);
}
else
{
formattedWrite(writer, "%s", Delta);
}
}
static RetryAfterHeader parse(string line)
{
RetryAfterHeader ret;
const(char)* start;
mixin(initParser!());
%%{
machine parseRetryAfter;
write init;
write exec;
}%%
mixin(finishParser!"parseRetryAfter");
return ret;
}
}
/**
* Data type for the $(LINK2 http://tools.ietf.org/html/rfc2616#section-14.39,
* "TE" header)
*
* Type:
* request-header
*/
struct TEHeader
{
public:
///
TCoding[] Codings;
static string Key = "TE";
void format(T)(T writer) if (isOutputRange!(T, string))
{
foreach(i, entry; Codings)
{
if(Codings.length == 1 && entr.Trailers)
{
assert(i == 0, "The 'trailers' coding must be the first coding!");
writer.put("trailers");
}
else
{
assert(isToken(entry.Extension));
writer.put(entry.Extension);
foreach(key, value; entry.Parameters)
{
writeParameter(writer, key, value);
}
if(entry.Q >= 0)
{
assert(entry.Q <= 1);
formattedWrite(writer, ";q=%.3f", entry.Q);
foreach(key, value; entry.AcceptParameters)
{
writeParameter(writer, key, value);
}
}
}
if(i != Codings.length - 1)
writer.put(", ");
}
}
}
/**
* Data type for the $(LINK2 http://tools.ietf.org/html/rfc2616#section-14.40,
* "Trailer" header)
*
* Type:
* general-header
*/
struct TrailerHeader
{
public:
///
string[] Fields;
static string Key = "Trailer";
void format(T)(T writer) if (isOutputRange!(T, string))
{
foreach(entry; Fields)
{
assert(isToken(entry));
writer.put(entry);
if(i != Fields.length - 1)
writer.put(", ");
}
}
static TrailerHeader parse(string line)
{
TrailerHeader head;
head.Fields = parseCommaList(line);
return head;
}
}
/**
* Data type for the $(LINK2 http://tools.ietf.org/html/rfc2616#section-14.41,
* "Transfer-Encoding" header)
*
* Type:
* general-header
*/
struct TransferEncodingHeader
{
public:
///
TransferCoding[] Codings;
static string Key = "Transfer-Encoding";
void format(T)(T writer) if (isOutputRange!(T, string))
{
foreach(i, entry; Codings)
{
if(entry.Type == TransferCoding.Chunked)
{
writer.put("chunked");
assert(entry.Raw == "");
assert(entry.Parameters.length == 0);
}
else
{
assert(entry.Raw != "");
assert(isToken(entry.Raw));
writer.put(entry.Raw);
foreach(key, value; entry.Parameters)
{
writeParameter(writer, key, value);
}
}
if(i != Codings.length - 1)
writer.put(", ");
}
}
static TransferEncodingHeader parse(string line)
{
TransferEncodingHeader head;
string[] list = parseCommaList(line);
foreach(entry; list)
{
TransferCoding cod;
const(char)* start;
mixin(initParser!("entry"));
%%{
machine parseTransferCoding;
write init;
write exec;
}%%
mixin(finishParser!"parseTransferCoding");
head.Codings ~= cod;
}
return head;
}
}
/**
* Data type for the $(LINK2 http://tools.ietf.org/html/rfc2616#section-14.42,
* "Upgrade" header)
*
* Type:
* general-header
*/
struct UpgradeHeader
{
public:
///
Product[] Products;
static string Key = "Upgrade";
void format(T)(T writer) if (isOutputRange!(T, string))
{
foreach(i, entry; Products)
{
assert(entry.Name != "");
assert(isToken(entry.Name));
writer.put(entry.Name);
if(entry.Version != "")
{
assert(isToken(entry.Version));
writer.put("/");
writer.put(entry.Version);
}
if(i != Products.length - 1)
writer.put(", ");
}
}
static UpgradeHeader parse(string line)
{
UpgradeHeader head;
string[] list = parseCommaList(line);
foreach(entry; list)
{
head.Products ~= parseProduct(entry);
}
return head;
}
}
/**
* Data type for the $(LINK2 http://tools.ietf.org/html/rfc2616#section-14.44,
* "Vary" header)
*
* Type:
* response-header
*/
struct VaryHeader
{
public:
///
bool All = false;
///
string[] Fields;
static string Key = "Vary";
void format(T)(T writer) if (isOutputRange!(T, string))
{
}
static VaryHeader parse(string line)
{
VaryHeader head;
if(line.length == 1 && line[0] == '*')
{
head.All = true;
}
else
{
head.Fields = parseCommaList(line);
}
return head;
}
}
unittest
{
Response rl = parseResponse("HTTP/1.2 200 OK\r\n");
assert(rl.Type == ResponseType.StatusLine);
assert(rl.Line == "HTTP/1.2 200 OK\r\n");
StatusLine sl = StatusLine.parse(rl.Line);
assert(sl.Major == 1);
assert(sl.Minor == 2);
assert(sl.StatusCode == 200);
assert(sl.Reason == "OK");
}
unittest
{
Response rl = parseResponse("\r\n");
assert(rl.Type == ResponseType.Empty);
}
unittest
{
bool thrown = false;
try
parseResponse("");
catch(Exception)
thrown = true;
assert(thrown);
}
unittest
{
Response rl = parseResponse("Server: Apache/1.3.29 (Unix) PHP/4.3.4\r\n");
assert(rl.Type == ResponseType.Header);
assert(rl.Key == "Server");
assert(rl.Value == "Apache/1.3.29 (Unix) PHP/4.3.4");
rl = parseResponse("Content-Length: 999\r\n");
assert(rl.Type == ResponseType.Header);
assert(rl.Key == "Content-Length");
assert(rl.Value == "999");
}
unittest
{
string[] list = parseCommaList(" , , Test , a,a , test,a, ,test,ab cd, ab dc ");
assert(list == ["Test", "a", "a", "test", "a", "test", "ab cd", "ab dc"]);
list = parseCommaList(" , test\r\n test , test");
assert(list == ["test\r\n test", "test"]);
}
unittest
{
Response rl = parseResponse("Allow: GET, HEAD\r\n");
assert(rl.Type == ResponseType.Header);
assert(rl.Key == "Allow");
auto head = parseHeaderValue!(AllowHeader)(rl.Value);
assert(head.Methods.length == 2);
assert(head.Methods[0].Type == Method.GET);
assert(head.Methods[1].Type == Method.HEAD);
}
unittest
{
Response rl = parseResponse("Connection: close\r\n");
assert(rl.Type == ResponseType.Header);
assert(rl.Key == "Connection");
auto head = parseHeaderValue!(ConnectionHeader)(rl.Value);
assert(head.Options.length == 1);
assert(head.Options[0].Type == ConnectionToken.Close);
}
unittest
{
Response rl = parseResponse("Pragma: no-cache, test=test234\r\n");
assert(rl.Type == ResponseType.Header);
assert(rl.Key == "Pragma");
PragmaHeader head = parseHeaderValue!(PragmaHeader)(rl.Value);
assert(head.Directives.length == 2);
assert(head.Directives[0].Type == PragmaDirective.NoCache);
assert(head.Directives[1].Type == PragmaDirective.Extension);
assert(head.Directives[1].ExtensionKey == "test");
assert(head.Directives[1].ExtensionValue == "test234");
}
unittest
{
Response rl = parseResponse("Transfer-Encoding: chunked;test=abcd\r\n");
assert(rl.Type == ResponseType.Header);
assert(rl.Key == "Transfer-Encoding");
TransferEncodingHeader head = parseHeaderValue!(TransferEncodingHeader)(rl.Value);
assert(head.Codings.length == 1);
assert(head.Codings[0].Type == TransferCoding.Chunked);
head = parseHeaderValue!(TransferEncodingHeader)("chunked; test = abcd , other ; test = value ; abcd = def");
assert(head.Codings.length == 2);
assert(head.Codings[0].Type == TransferCoding.Chunked);
assert(head.Codings[1].Type == TransferCoding.Custom);
assert(head.Codings[1].Raw == "other");
assert(head.Codings[0].Parameters == ["test": "abcd"]);
assert(head.Codings[1].Parameters == ["test": "value", "abcd": "def"]);
}
unittest
{
Response rl = parseResponse("Transfer-Encoding: chunked\r\n");
assert(rl.Type == ResponseType.Header);
assert(rl.Key == "Transfer-Encoding");
TransferEncodingHeader head = parseHeaderValue!(TransferEncodingHeader)(rl.Value);
assert(head.Codings.length == 1);
assert(head.Codings[0].Type == TransferCoding.Chunked);
}
unittest
{
Response rl = parseResponse("Upgrade: HTTP/2.0, SHTTP/1.3, IRC/6.9, RTA/x11\r\n");
assert(rl.Type == ResponseType.Header);
assert(rl.Key == "Upgrade");
UpgradeHeader prod = parseHeaderValue!(UpgradeHeader)(rl.Value);
assert(prod.Products.length == 4);
assert(prod.Products[0].Name == "HTTP");
assert(prod.Products[0].Version == "2.0");
assert(prod.Products[1].Name == "SHTTP");
assert(prod.Products[1].Version == "1.3");
assert(prod.Products[2].Name == "IRC");
assert(prod.Products[2].Version == "6.9");
assert(prod.Products[3].Name == "RTA");
assert(prod.Products[3].Version == "x11");
}
unittest
{
Response rl = parseResponse("Age: 123456789\r\n");
assert(rl.Type == ResponseType.Header);
assert(rl.Key == "Age");
auto age = parseHeaderValue!(AgeHeader)(rl.Value);
assert(age.Age == 123456789);
}
unittest
{
Response rl = parseResponse("ETag: W/\"abcdefg\"\r\n");
assert(rl.Type == ResponseType.Header);
assert(rl.Key == "ETag");
auto tag = parseHeaderValue!(ETagHeader)(rl.Value);
assert(tag.Tag.Weak == true);
assert(tag.Tag.Value == "abcdefg");
tag = parseHeaderValue!(ETagHeader)("\"abcdef\"");
assert(tag.Tag.Weak == false);
assert(tag.Tag.Value == "abcdef");
}
unittest
{
Response rl = parseResponse("Retry-After: Fri, 31 Dec 1999 23:59:59 GMT\r\n");
assert(rl.Type == ResponseType.Header);
assert(rl.Key == "Retry-After");
RetryAfterHeader ret = parseHeaderValue!(RetryAfterHeader)(rl.Value);
assert(ret.useTime);
assert(ret.Date == SysTime(DateTime(1999, 12, 31, 23, 59, 59), UTC()));
ret = parseHeaderValue!(RetryAfterHeader)("120");
assert(!ret.useTime);
assert(ret.Delta == 120);
}
unittest
{
Response rl = parseResponse("Content-Encoding: gzip, deflate, custom\r\n");
assert(rl.Type == ResponseType.Header);
assert(rl.Key == "Content-Encoding");
auto cc = parseHeaderValue!(ContentEncodingHeader)(rl.Value);
assert(cc.Codings.length == 3);
assert(cc.Codings[0].Type == ContentCoding.Gzip);
assert(cc.Codings[1].Type == ContentCoding.Deflate);
assert(cc.Codings[2].Type == ContentCoding.Other);
assert(cc.Codings[2].Raw == "custom");
}
unittest
{
Response rl = parseResponse("Content-Language: da, mi, en-US\r\n");
assert(rl.Type == ResponseType.Header);
assert(rl.Key == "Content-Language");
auto lang = parseHeaderValue!(ContentLanguageHeader)(rl.Value);
assert (lang.Tags[0].Main == "da");
assert (lang.Tags[1].Main == "mi");
assert (lang.Tags[2].Main == "en");
assert (lang.Tags[2].Sub == "US");
}
unittest
{
Response rl = parseResponse("Content-Length: 348\r\n");
assert(rl.Type == ResponseType.Header);
assert(rl.Key == "Content-Length");
auto length = parseHeaderValue!(ContentLengthHeader)(rl.Value);
assert (length.Length == 348);
}
unittest
{
Response rl = parseResponse("Content-MD5: Q2hlY2sgSW50ZWdyaXR5IQ==\r\n");
assert(rl.Type == ResponseType.Header);
assert(rl.Key == "Content-MD5");
auto md5 = parseHeaderValue!(ContentMD5Header)(rl.Value);
assert(md5.MD5 == [67, 104, 101, 99, 107, 32, 73, 110, 116, 101, 103, 114, 105, 116, 121, 33]);
}
unittest
{
Response rl = parseResponse("Content-Range: bytes 21010-47021/47022\r\n");
assert(rl.Type == ResponseType.Header);
assert(rl.Key == "Content-Range");
auto range = parseHeaderValue!(ContentRangeHeader)(rl.Value);
assert(range.Range.From.Value == 21010);
assert(range.Range.From.Unknown == false);
assert(range.Range.To.Value == 47021);
assert(range.Range.To.Unknown == false);
assert(range.Range.Length.Value == 47022);
assert(range.Range.Length.Unknown == false);
range = parseHeaderValue!(ContentRangeHeader)("bytes */*");
assert(range.Range.From.Unknown == true);
assert(range.Range.To.Unknown == true);
assert(range.Range.Length.Unknown == true);
}
unittest
{
Response rl = parseResponse("Content-Type: text/html; charset=utf-8\r\n");
assert(rl.Type == ResponseType.Header);
assert(rl.Key == "Content-Type");
auto type = parseHeaderValue!(ContentTypeHeader)(rl.Value);
assert(type.Type.Type == "text");
assert(type.Type.SubType == "html");
assert(type.Type.Parameters == ["charset": "utf-8"]);
}
/*
* Parser/Formatter helper functions & types
*/
/**
* This exception is thrown if an error occurs when parsing a header
*/
public class ParserException : Exception
{
public:
///The original input string which should have been parsed
string Input;
///The position in the input string where the error occured
uint Position;
///
this(string input, uint pos, string msg = "")
{
Input = input;
Position = pos;
string message = format("An error occured in the HTTP parser:%s\n" ~
"*Input:\t'%s'\n*Position:\t%s", msg, replace(replace(input,
"\r", "\\r"), "\n", "\\n"), pos);
super(message);
}
}
/**
* Subclass of ParserException for the special case when the passed
* input was correct, but more input was expected.
*/
public class InsufficientInputException : ParserException
{
///
public this(string input)
{
super(input, input.length, "Insufficient input");
}
}
/**
* Parses a response line and returns a $(D Response) struct
*
* Examples:
* ---------------------------------------
* auto line = parseResponse("HTTP/1.2 200 OK\r\n");
* assert(line.Type == ResponseType.StatusLine);
* assert(line.Line == "HTTP/1.2 200 OK\r\n");
* ---------------------------------------
*
* ---------------------------------------
* auto line = parseResponse("\r\n");
* assert(line.Type == ResponseType.Empty);
* ---------------------------------------
*
* ---------------------------------------
* auto line = parseResponse("Server: Apache/1.3.29 (Unix) PHP/4.3.4\r\n");
* assert(line.Type == ResponseType.Header);
* assert(line.Key == "Server");
* assert(line.Value == "Apache/1.3.29 (Unix) PHP/4.3.4");
* ---------------------------------------
*/
Response parseResponse(string line)
{
Response rline;
const (char)* hstart;
mixin(initParser!());
%%{
machine parseResponse;
write init;
write exec;
}%%
mixin(finishParser!"parseResponse");
return rline;
}
/**
* Check if input is a quoted string
*/
bool isQuotedString(string val)
{
if(val.length < 2)
return false;
if(val[0] != '"' || val[$ - 1] != '"')
return false;
return true;
}
/**
* Quote the input string
*/
string quote(string val)
{
auto result = appender!string;
foreach(c; val)
{
if(c == '"')
result.put('\\');
result.put(c);
}
return '"' ~ result.data ~ '"';
}
/**
* Write a parameter in a parameter list
*/
void writeParameter(Appender!string writer, string key, string value)
{
writer.put(";");
assert(isToken(key));
writer.put(key);
if(value != "")
{
writer.put("=");
if(isToken(value))
writer.put(value);
else
writer.put(quote(value));
}
}
/**
* Check if string is a token
*/
bool isToken(string line)
{
const(char)* start;
mixin(initParser!());
%%{
machine isToken;
write init;
write exec;
}%%
mixin(finishParser!"isToken");
return true;
}
/**
* Check if string is TEXT*
*/
bool isText(string line)
{
const(char)* start;
mixin(initParser!());
%%{
machine isText;
write init;
write exec;
}%%
mixin(finishParser!"isText");
return true;
}
/**
* Parses a http comma list into a list of strings
*/
string[] parseCommaList(string line)
{
string[] list;
const(char)* start;
mixin(initParser!());
%%{
machine parseCommaList;
write init;
write exec;
}%%
mixin(finishParser!"parseCommaList");
return list;
}
/**
* Parses a http parameter list into a strings hash
*/
string[string] parseParameterList(string line)
{
string[string] list;
string key;
const(char)* start;
mixin(initParser!());
%%{
machine parseParameterList;
write init;
write exec;
}%%
mixin(finishParser!"parseParameterList");
return list;
}
/**
* Decode a $(LINK2 http://tools.ietf.org/html/rfc2616#section-3.6,
* "value")
*/
string prepareValue(string value)
{
return unquote(stripr(value));
}
/**
* Decode a $(LINK2 http://tools.ietf.org/html/rfc2616#section-3.6,
* "quoted-string")
*/
string unquote(string value)
{
if(value.length > 1 && value[0] == '"' && value[$ - 1] == '"')
return unescape(value[1 .. $ - 1]);
return value;
}
/**
* Unescape a $(LINK2 http://tools.ietf.org/html/rfc2616#page-16,
* "quoted-string")
*/
string unescape(string value)
{
string tmp;
tmp.reserve(value.length);
uint newLength = 0;
foreach(c; value)
{
if(c == '\\')
continue;
tmp ~= c;
}
return tmp;
}
void formatEntityTag(T)(T writer, EntityTag Tag)
if (isOutputRange!(T, string))
{
if(Tag.Weak)
writer.put("W/");
assert(Tag.Value != "");
writer.put(quote(Tag.Value));
}
Product parseProduct(string line)
{
Product prd;
const(char)* start;
mixin(initParser!());
%%{
machine parseProduct;
write init;
write exec;
}%%
mixin(finishParser!"parseProduct");
return prd;
}
/**
* Parses a $(LINK2 http://tools.ietf.org/html/rfc2616#section-3.3.1, HTTP-date)
*/
SysTime parseDate(string line)
{
int year;
Month month;
int day;
int hour;
int minute;
int second;
TimeZone zone;
const(char)* start;
mixin(initParser!());
%%{
machine parseDate;
write init;
write exec;
}%%
mixin(finishParser!"parseDate");
if(zone)
return SysTime(DateTime(year, month, day, hour, minute, second), cast(immutable TimeZone)zone);
else
return SysTime(DateTime(year, month, day, hour, minute, second));
}
unittest
{
auto time = parseDate("Fri, 31 Dec 1999 23:59:57 GMT");
assert(time.year == 1999);
assert(time.month == Month.dec);
assert(time.day == 31);
assert(time.hour == 23);
assert(time.minute == 59);
assert(time.second == 57);
assert(time.timezone == UTC());
}
unittest
{
auto time = parseDate("Fri, 31 Dec 1999 23:59:57 GMT");
assert(time.year == 1999);
assert(time.month == Month.dec);
assert(time.day == 31);
assert(time.hour == 23);
assert(time.minute == 59);
assert(time.second == 57);
assert(time.timezone == UTC());
}
unittest
{
auto time = parseDate("Sat, 30-Apr-2011 15:59:28 GMT");
assert(time.year == 2011);
assert(time.month == Month.apr);
assert(time.day == 30);
assert(time.hour == 15);
assert(time.minute == 59);
assert(time.second == 28);
assert(time.timezone == UTC());
}
unittest
{
auto time = parseDate("fri, 10-aug-2012 16:48:59 gmt");
assert(time.year == 2012);
assert(time.month == Month.aug);
assert(time.day == 10);
assert(time.hour == 16);
assert(time.minute == 48);
assert(time.second == 59);
assert(time.timezone == UTC());
}
unittest
{
auto time = parseDate("Sat May 20 15:21:51 2000");
assert(time.year == 2000);
assert(time.month == Month.may);
assert(time.day == 20);
assert(time.hour == 15);
assert(time.minute == 21);
assert(time.second == 51);
assert(time.timezone == UTC());
}
/**
* Formats SysTime into $(LINK2 http://tools.ietf.org/html/rfc2616#section-3.3.1, HTTP-date)
* (rfc1123-date format). time is automatically converted to UTC.
*/
string formatDateString(SysTime time)
{
auto writer = appender!string();
formatDate(writer, time);
return writer.data;
}
///ditto
void formatDate(T)(T writer, SysTime time)
if (isOutputRange!(T, string))
{
time.timezone = UTC();
//wkday
final switch(time.dayOfWeek)
{
case DayOfWeek.sun:
writer.put("Sun");
break;
case DayOfWeek.mon:
writer.put("Mon");
break;
case DayOfWeek.tue:
writer.put("Tue");
break;
case DayOfWeek.wed:
writer.put("Wed");
break;
case DayOfWeek.thu:
writer.put("Thu");
break;
case DayOfWeek.fri:
writer.put("Fri");
break;
case DayOfWeek.sat:
writer.put("Sat");
break;
}
//"," SP
writer.put(", ");
//date1: 2DIGIT SP
formattedWrite(writer, "%02s ", time.day);
//date1: month
final switch(time.month)
{
case Month.jan:
writer.put("Jan");
break;
case Month.feb:
writer.put("Feb");
break;
case Month.mar:
writer.put("Mar");
break;
case Month.apr:
writer.put("Apr");
break;
case Month.may:
writer.put("May");
break;
case Month.jun:
writer.put("Jun");
break;
case Month.jul:
writer.put("Jul");
break;
case Month.aug:
writer.put("Aug");
break;
case Month.sep:
writer.put("Sep");
break;
case Month.oct:
writer.put("Oct");
break;
case Month.nov:
writer.put("Nov");
break;
case Month.dec:
writer.put("Dec");
break;
}
//date1: SP 4DIGIT
formattedWrite(writer, " %04s", time.year);
//SP
writer.put(" ");
//time: 2DIGIT ":" 2DIGIT ":" 2DIGIT
formattedWrite(writer, "%02s:%02s:%02s", time.hour, time.minute, time.second);
//SP "GMT"
writer.put(" GMT");
}
unittest
{
string test = formatDateString(SysTime(DateTime(2011, 3, 24, 17, 52, 01), UTC()));
assert(test == "Thu, 24 Mar 2011 17:52:01 GMT");
test = formatDateString(SysTime(DateTime(89, 1, 2, 3, 4, 5), UTC()));
assert(test == "Sun, 02 Jan 0089 03:04:05 GMT");
test = formatDateString(SysTime(DateTime(89, 2, 2, 3, 4, 5), UTC()));
assert(test == "Wed, 02 Feb 0089 03:04:05 GMT");
test = formatDateString(SysTime(DateTime(89, 3, 2, 3, 4, 5), UTC()));
assert(test == "Wed, 02 Mar 0089 03:04:05 GMT");
test = formatDateString(SysTime(DateTime(89, 4, 2, 3, 4, 5), UTC()));
assert(test == "Sat, 02 Apr 0089 03:04:05 GMT");
test = formatDateString(SysTime(DateTime(89, 5, 2, 3, 4, 5), UTC()));
assert(test == "Mon, 02 May 0089 03:04:05 GMT");
test = formatDateString(SysTime(DateTime(89, 6, 2, 3, 4, 5), UTC()));
assert(test == "Thu, 02 Jun 0089 03:04:05 GMT");
test = formatDateString(SysTime(DateTime(89, 7, 2, 3, 4, 5), UTC()));
assert(test == "Sat, 02 Jul 0089 03:04:05 GMT");
test = formatDateString(SysTime(DateTime(89, 8, 2, 3, 4, 5), UTC()));
assert(test == "Tue, 02 Aug 0089 03:04:05 GMT");
test = formatDateString(SysTime(DateTime(89, 9, 2, 3, 4, 5), UTC()));
assert(test == "Fri, 02 Sep 0089 03:04:05 GMT");
test = formatDateString(SysTime(DateTime(89, 10, 2, 3, 4, 5), UTC()));
assert(test == "Sun, 02 Oct 0089 03:04:05 GMT");
test = formatDateString(SysTime(DateTime(89, 11, 2, 3, 4, 5), UTC()));
assert(test == "Wed, 02 Nov 0089 03:04:05 GMT");
test = formatDateString(SysTime(DateTime(89, 12, 2, 3, 4, 5), UTC()));
assert(test == "Fri, 02 Dec 0089 03:04:05 GMT");
}
/*
* Public Api to format and parse Headers
*/
/**
* Test if a string can be parsed into a T
*
* Examples:
* ---------------------------------------
* assert(canParseHeader!AgeHeader);
* ---------------------------------------
*/
template canParseHeader(T)
{
enum bool canParseHeader = __traits(compiles, T.parse(""))
&& is(ReturnType!(T.parse) == T)
&& __traits(compiles, T.Key)
&& is(typeof(T.Key) == string);
}
/**
* Test if a T can be formatted into a string
*
* Examples:
* ---------------------------------------
* assert(canFormatHeader!AcceptHeader);
* ---------------------------------------
*/
template canFormatHeader(T)
{
enum bool canFormatHeader = __traits(compiles, T.init.format(appender!string()))
&& __traits(compiles, T.Key)
&& is(typeof(T.Key) == string);
}
/**
* Format a header.
* Output format is like this: "[Key]: [Value]" --> "Host: www.w3.org"
*
* Note: There is no newline appended at the end!
*
* Examples:
* ---------------------------------------
* IfModifiedSinceHeader head;
* head.Date = SysTime(DateTime(2011, 3, 24, 19, 21, 0), UTC());
* string headerString = formatHeader(head);
* assert(headerString == "If-Modified-Since: Thu, 24 Mar 2011 19:21:00 GMT");
* ---------------------------------------
*/
string formatHeader(T)(T header)
if (canFormatHeader!T)
{
auto writer = appender!string();
writer.put(T.Key);
writer.put(": ");
header.format(writer);
return writer.data;
}
///ditto
void formatHeader(T, O)(T header, O output)
if (canFormatHeader!T && isOutputRange!O)
{
writer.put(T.Key);
writer.put(": ");
header.format(output);
}
unittest
{
IfModifiedSinceHeader head;
head.Date = SysTime(DateTime(2011, 3, 24, 19, 21, 0), UTC());
string headerString = formatHeader(head);
assert(headerString == "If-Modified-Since: Thu, 24 Mar 2011 19:21:00 GMT");
}
/**
* Format a header.
* Output format is like this: "[Value]" --> "www.w3.org"
*
* Note: There is no newline appended at the end!
* Examples:
* ---------------------------------------
* IfModifiedSinceHeader head;
* head.Date = SysTime(DateTime(2011, 3, 24, 19, 21, 0), UTC());
* string headerString = formatHeaderValue(head);
* assert(headerString == "Thu, 24 Mar 2011 19:21:00 GMT");
* ---------------------------------------
*/
string formatHeaderValue(T)(T header)
if (canFormatHeader!T)
{
auto writer = appender!string();
header.format(writer);
return writer.data;
}
///ditto
void formatHeaderValue(T, O)(T header, O output)
if (canFormatHeader!T && isOutputRange!O)
{
header.format(output);
}
unittest
{
IfModifiedSinceHeader head;
head.Date = SysTime(DateTime(2011, 3, 24, 19, 21, 0), UTC());
string headerString = formatHeaderValue(head);
assert(headerString == "Thu, 24 Mar 2011 19:21:00 GMT");
}
/++
+ Parse a string into a T header.
+ Input format is like this: "[Key]: [Value]" --> "Host: www.w3.org"
+
+ This function does a case insensitive check if the [Key] in the input
+ string matches the T header type.
+
+ Note:
+ The input must be exactly one line with the newline char at the end.
+ Continuations are not handled by this function.
+
+ Throws:
+ ParserException
+ Examples:
+ ---------------------------------------
+ auto header = parseHeader!(ContentRangeHeader)("Content-Range: bytes 21010-47021/47022\r\n");
+ assert(header.Range.From.Value == 21010);
+ assert(header.Range.From.Unknown == false);
+ assert(header.Range.To.Value == 47021);
+ assert(header.Range.To.Unknown == false);
+ assert(header.Range.Length.Value == 47022);
+ assert(header.Range.Length.Unknown == false);
+
+ header = parseHeaderValue!(ContentRangeHeader)("bytes */*");
+ assert(header.Range.From.Unknown == true);
+ assert(header.Range.To.Unknown == true);
+ assert(header.Range.Length.Unknown == true);
+ ---------------------------------------
+/
T parseHeader(T)(string line)
if(canParseHeader!T)
{
auto resp = parseResponse(line);
if(resp.Type != ResponseType.Header)
{
throw new ParserException(line, 0, format("Unexpected response: " ~
"Expected a header response, but got '%s'", resp.Type));
}
else if(tolower(resp.Key) != tolower(T.Key))
{
throw new ParserException(line, 0, format("Unexpected header: " ~
"Expected '%s' Got '%s'", T.Key, resp.Key));
}
return T.parse(resp.Value);
}
unittest
{
auto header = parseHeader!(ContentRangeHeader)("Content-Range: bytes 21010-47021/47022\r\n");
assert(header.Range.From.Value == 21010);
assert(header.Range.From.Unknown == false);
assert(header.Range.To.Value == 47021);
assert(header.Range.To.Unknown == false);
assert(header.Range.Length.Value == 47022);
assert(header.Range.Length.Unknown == false);
header = parseHeaderValue!(ContentRangeHeader)("bytes */*");
assert(header.Range.From.Unknown == true);
assert(header.Range.To.Unknown == true);
assert(header.Range.Length.Unknown == true);
}
/++
+ Parse a string into a T header.
+ Input format is like this: "[Value]" --> "www.w3.org"
+
+ Note:
+ The input must be exactly one line with the newline char at the end.
+ Continuations are not handled by this function.
+
+ Throws:
+ ParserException
+ Examples:
+ ---------------------------------------
+ auto header = parseHeaderValue!(ContentRangeHeader)("bytes 21010-47021/47022");
+ assert(header.Range.From.Value == 21010);
+ assert(header.Range.From.Unknown == false);
+ assert(header.Range.To.Value == 47021);
+ assert(header.Range.To.Unknown == false);
+ assert(header.Range.Length.Value == 47022);
+ assert(header.Range.Length.Unknown == false);
+
+ header = parseHeaderValue!(ContentRangeHeader)("bytes */*");
+ assert(header.Range.From.Unknown == true);
+ assert(header.Range.To.Unknown == true);
+ assert(header.Range.Length.Unknown == true);
+ ---------------------------------------
+/
T parseHeaderValue(T)(string line)
if(canParseHeader!T)
{
return T.parse(line);
}
unittest
{
assert(canFormatHeader!AcceptHeader);
}
/*
* Helper templates for ragel parsers
*/
template initParser()
{
const string initParser = "int cs;\n" ~
"const (char)* p = line.ptr;\n" ~
"const char* pe = line.ptr + line.length;\n" ~
"const char* eof = pe;\n";
}
template initParser(string source)
{
const string initParser = "int cs;\n" ~
"const (char)* p = " ~ source ~ ".ptr;\n" ~
"const char* pe = " ~ source ~ ".ptr + " ~ source ~ ".length;\n" ~
"const char* eof = pe;\n";
}
template finishParser(string parserName)
{
const string finishParser = "if(cs == " ~ parserName ~ "_error)\n" ~
"throw new ParserException(line, p - line.ptr);\n" ~
"if(cs < " ~ parserName ~ "_first_final)\n" ~
"throw new InsufficientInputException(line);\n";
}
/*
* Ragel FSM definitions
*/
%%{
#RFC 2616
machine general;
OCTET = any;
CHAR = ascii;
UPALPHA = [A-Z];
LOALPHA = [a-z];
ALPHA = UPALPHA | LOALPHA;
DIGIT = digit;
CTL = cntrl | 127;
CR = "\r";
LF = "\n";
SP = " ";
HT = "\t";
DQUOTE = '"';
CRLF = CR LF;
LWS = CRLF? (SP | HT)+;
TEXT = (OCTET - CTL) | LWS;
HEX = xdigit;
separators = "(" | ")" | "<" | ">" | "@"
| "," | ";" | ":" | "\\" | '"'
| "/" | "[" | "]" | "?" | "="
| "{" | "}" | SP | HT;
token = (CHAR - CTL - separators)+;
ctext = TEXT - [()];
quoted_pair = "\\" CHAR;
#not working
#comment = "(" (ctext | quoted_pair | comment)* ")";
qdtext = TEXT - DQUOTE;
quoted_string = DQUOTE (qdtext | quoted_pair)* DQUOTE;
field_name = token;
wkday = "Mon"i | "Tue"i | "Wed"i
| "Thu"i | "Fri"i | "Sat"i | "Sun"i;
weekday = "Monday"i | "Tuesday"i | "Wednesday"i
| "Thursday"i | "Friday"i | "Saturday"i | "Sunday"i;
month = "Jan"i | "Feb"i | "Mar"i | "Apr"i
| "May"i | "Jun"i | "Jul"i | "Aug"i
| "Sep"i | "Oct"i | "Nov"i | "Dec"i;
date1 = DIGIT{2} SP month SP DIGIT{4}; #day month year (e.g., 02 Jun 1982)
#Also accept 4 digit Years
date2 = DIGIT{2} "-" month "-" (DIGIT{2} | DIGIT{4}); #day-month-year (e.g., 02-Jun-82)
date3 = month SP ( DIGIT{2} | ( SP DIGIT )); #month day (e.g., Jun 2)
time = DIGIT{2} ":" DIGIT{2} ":" DIGIT{2}; #00:00:00 - 23:59:59
#always accept wkday and weekday: This is against the spec,
#but it's needed to work around some broken formatters.
#Also accepting GMT case independent
asctime_date = (wkday | weekday) SP date3 SP time SP DIGIT{4};
rfc850_date = (weekday | wkday) "," SP date2 SP time SP "GMT"i;
rfc1123_date = (wkday | weekday) "," SP date1 SP time SP "GMT"i;
HTTP_date = rfc1123_date | rfc850_date | asctime_date;
}%%
%%{
machine parseResponse;
include general;
action setStatusLineEnd {
rline.Type = ResponseType.StatusLine;
rline.Line = line[0 .. (p - line.ptr + 1)];
}
action setEmptyLine {
rline.Type = ResponseType.Empty;
}
action headerStart {
rline.Type = ResponseType.Header;
hstart = p;
}
action headerKeyEnd {
rline.Key = line[(hstart - line.ptr) .. (p - line.ptr)];
}
action headerValueEnd {
rline.Value = line[(hstart - line.ptr) .. (p - line.ptr)];
}
HTTP_Version = "HTTP" "/" DIGIT+ "." DIGIT+;
Status_Code = DIGIT{3};
Reason_Phrase = (TEXT - CR - LF)*;
Status_Line = HTTP_Version SP Status_Code SP Reason_Phrase CRLF;
raw_header = (token >headerStart %headerKeyEnd) LWS* ":" LWS* (TEXT* >headerStart %headerValueEnd);
Response_line = (Status_Line @setStatusLineEnd) | (raw_header CRLF)
| (CRLF @setEmptyLine);
main := Response_line;
write data;
}%%
%%{
machine parseStatusLine;
include general;
action httpMajor {
sline.Major = std.conv.parse!(uint)(line[(numstart - line.ptr)
.. (p - line.ptr)]);
}
action httpMinor {
sline.Minor = std.conv.parse!(uint)(line[(numstart - line.ptr)
.. (p - line.ptr)]);
}
action numStart {
numstart = p;
}
action statusCode {
sline.StatusCode = std.conv.parse!(uint)(line[(p - line.ptr -3)
.. (p - line.ptr)]);
}
action reason {
sline.Reason = line[(numstart - line.ptr)
.. (p - line.ptr)];
}
HTTP_Version = "HTTP" "/" (DIGIT+ >numStart %httpMajor) "." (DIGIT+ >numStart %httpMinor);
Status_Code = DIGIT{3};
Reason_Phrase = (TEXT - CR - LF)*;
Status_Line = HTTP_Version SP (Status_Code %statusCode) SP (Reason_Phrase >numStart %reason) CRLF;
main := Status_Line;
write data;
}%%
%%{
machine isToken;
include general;
main := token;
write data;
}%%
%%{
machine isText;
include general;
main := TEXT*;
write data;
}%%
%%{
machine parseCommaList;
include general;
action listEntry {
if(start != p)
{
list ~= stripr(line[(start - line.ptr)
.. (p - line.ptr)]);
}
}
action Start {
start = p;
}
Comma_List_Entry = (((TEXT - ",") | "\\,")*) >Start %listEntry;
Comma_List = LWS* Comma_List_Entry? ("," LWS* Comma_List_Entry? )*;
main := Comma_List;
write data;
}%%
%%{
machine parseContentRangeHeader;
include general;
action Start {
start = p;
}
action From {
head.Range.From.Value = std.conv.parse!ulong(line[(start - line.ptr)
.. (p - line.ptr)]);
}
action To {
head.Range.To.Value = std.conv.parse!ulong(line[(start - line.ptr)
.. (p - line.ptr)]);
}
action Length {
head.Range.Length.Value = std.conv.parse!ulong(line[(start - line.ptr)
.. (p - line.ptr)]);
}
action UnknownLength {
head.Range.Length.Unknown = true;
}
action UnknownPos {
head.Range.To.Unknown = true;
head.Range.From.Unknown = true;
}
byte_range_resp_spec = ((DIGIT+ >Start %From) "-" (DIGIT+ >Start %To)) | ("*" %UnknownPos);
main := "bytes"i SP byte_range_resp_spec "/" ( (DIGIT+ >Start %Length) | ("*" %UnknownLength));
write data;
}%%
%%{
machine parseContentTypeHeader;
include general;
action Start {
start = p;
}
action Type {
ret.Type = line[(start - line.ptr) .. (p - line.ptr)];
}
action SubType {
ret.SubType = line[(start - line.ptr) .. (p - line.ptr)];
}
action ValueEnd {
ret.Parameters = parseParameterList(line[(start - line.ptr)
.. (p - line.ptr)]);
}
main := (token >Start %Type) "/" (token >Start %SubType) LWS* ((";" any*) >Start %ValueEnd)?;
write data;
}%%
%%{
machine parseParameterList;
include general;
action Value {
if(start != p)
{
list[key] = prepareValue(line[(start - line.ptr) .. (p - line.ptr)]);
}
}
action Attribute {
if(start != p)
{
key = line[(start - line.ptr) .. (p - line.ptr)];
}
}
action Start {
start = p;
}
n_value = (token | quoted_string) >Start %Value;
n_attribute = token >Start %Attribute;
n_parameter = LWS* n_attribute LWS* "=" LWS* n_value;
Comma_List = (LWS* ";" (n_parameter))*;
main := Comma_List;
write data;
}%%
%%{
machine parseETag;
include general;
action Weak {
tag.Weak = true;
}
action Start {
start = p;
}
action End {
tag.Value = prepareValue(line[(start - line.ptr) .. (p - line.ptr)]);
}
weak = "W/";
#Spec says quoted_string only, but some servers do not quote ETags
opaque_tag = quoted_string | token;
main := (weak %Weak)? (opaque_tag >Start %End);
write data;
}%%
%%{
machine parsePragmaEntry;
include general;
action Start {
start = p;
}
action KeyEnd {
string key = entry[(start - entry.ptr)
.. (p - entry.ptr)];
if(key == "no-cache")
{
dir.Type = PragmaDirective.NoCache;
}
else
{
dir.Type = PragmaDirective.Extension;
dir.ExtensionKey = key;
}
}
action ValueEnd {
dir.ExtensionValue = prepareValue(entry[(start - entry.ptr)
.. (p - entry.ptr)]);
}
main := (token >Start %KeyEnd) ("=" (( token | quoted_string ) >Start %ValueEnd))?;
write data;
}%%
%%{
machine parseTransferCoding;
include general;
action Start {
start = p;
}
action KeyEnd {
string key = entry[(start - entry.ptr)
.. (p - entry.ptr)];
if(key == "chunked")
{
cod.Type = TransferCoding.Chunked;
}
else
{
cod.Type = TransferCoding.Custom;
cod.Raw = key;
}
}
action ValueEnd {
cod.Parameters = parseParameterList(entry[(start - entry.ptr)
.. (p - entry.ptr)]);
}
main := (token >Start %KeyEnd) LWS* ((";" any*) >Start %ValueEnd)?;
write data;
}%%
%%{
machine parseProduct;
include general;
action Start {
start = p;
}
action KeyEnd {
prd.Name = line[(start - line.ptr) .. (p - line.ptr)];
}
action ValueEnd {
prd.Version = line[(start - line.ptr) .. (p - line.ptr)];
}
main := (token >Start %KeyEnd) LWS* ("/" LWS* (token >Start %ValueEnd))?;
write data;
}%%
%%{
machine parseDate;
action Start {
start = p;
}
action Year {
year = std.conv.parse!int(line[(start - line.ptr) .. (p - line.ptr)]);
}
action Month {
string mon = tolower(line[(start - line.ptr) .. (p - line.ptr)]);
switch(mon)
{
case "jan":
month = Month.jan;
break;
case "feb":
month = Month.feb;
break;
case "mar":
month = Month.mar;
break;
case "apr":
month = Month.apr;
break;
case "may":
month = Month.may;
break;
case "jun":
month = Month.jun;
break;
case "jul":
month = Month.jul;
break;
case "aug":
month = Month.aug;
break;
case "sep":
month = Month.sep;
break;
case "oct":
month = Month.oct;
break;
case "nov":
month = Month.nov;
break;
case "dec":
month = Month.dec;
break;
}
}
action Day {
day = std.conv.parse!int(line[(start - line.ptr) .. (p - line.ptr)]);
}
action GMT {
zone = cast(TimeZone)UTC();;
}
action Hour {
hour = std.conv.parse!int(line[(start - line.ptr) .. (p - line.ptr)]);
}
action Minute {
minute = std.conv.parse!int(line[(start - line.ptr) .. (p - line.ptr)]);
}
action Second {
second = std.conv.parse!int(line[(start - line.ptr) .. (p - line.ptr)]);
}
DIGIT = digit;
SP = " ";
wkday = "Mon"i | "Tue"i | "Wed"i
| "Thu"i | "Fri"i | "Sat"i | "Sun"i;
weekday = "Monday"i | "Tuesday"i | "Wednesday"i
| "Thursday"i | "Friday"i | "Saturday"i | "Sunday"i;
month = (("Jan"i | "Feb"i | "Mar"i | "Apr"i
| "May"i | "Jun"i | "Jul"i | "Aug"i
| "Sep"i | "Oct"i | "Nov"i | "Dec"i) >Start %Month);
date1 = (DIGIT{2} >Start %Day) SP month SP (DIGIT{4} >Start %Year); #day month year (e.g., 02 Jun 1982)
#Also accept 4 digit Years
date2 = (DIGIT{2} >Start %Day) "-" month "-" ((DIGIT{2} | DIGIT{4}) >Start %Year); #day-month-year (e.g., 02-Jun-82)
date3 = month SP (( DIGIT{2} | ( SP DIGIT )) >Start %Day); #month day (e.g., Jun 2)
time = (DIGIT{2} >Start %Hour) ":" (DIGIT{2} >Start %Minute) ":" (DIGIT{2} >Start %Second); #00:00:00 - 23:59:59
#always accept wkday and weekday: This is against the spec,
#but it's needed to work around some broken formatters.
#Also accepting GMT case independent
asctime_date = (wkday | weekday) SP date3 SP time SP (DIGIT{4} >Start %Year %GMT);
rfc850_date = (weekday | wkday) "," SP date2 SP time SP ("GMT"i %GMT);
rfc1123_date = (wkday | weekday) "," SP date1 SP time SP ("GMT"i %GMT);
HTTP_date = rfc1123_date | rfc850_date | asctime_date;
main := HTTP_date;
write data;
}%%
%%{
machine parseRetryAfter;
include general;
action Seconds {
ret.useTime = false;
ret.Delta = std.conv.parse!ulong(line);
}
action Text {
ret.useTime = true;
ret.Date = parseDate(line);
}
main := (DIGIT+ %Seconds) | ((TEXT - DIGIT) TEXT* %Text);
write data;
}%%
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment