Created
April 28, 2012 06:18
-
-
Save jpf91/2516504 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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 is a header (has a .Key property) | |
* | |
* Examples: | |
* --------------------------------------- | |
* assert(isHeader!AgeHeader); | |
* --------------------------------------- | |
*/ | |
template isHeader(T) | |
{ | |
enum bool isHeader = __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" | |
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 file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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 is a header (has a .Key property) | |
* | |
* Examples: | |
* --------------------------------------- | |
* assert(isHeader!AgeHeader); | |
* --------------------------------------- | |
*/ | |
template isHeader(T) | |
{ | |
enum bool isHeader = __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