1 module requests.base;
2 
3 import requests.streams;
4 import requests.utils;
5 import requests.uri;
6 
7 import std.format;
8 import std.datetime;
9 import core.time;
10 
11 public interface Auth {
12     string[string] authHeaders(string domain);
13     string         userName();
14     string         password();
15 }
16 
17 public class RequestException: Exception {
18     this(string msg, string file = __FILE__, size_t line = __LINE__, Throwable next = null) @safe pure nothrow {
19         super(msg, file, line, next);
20     }
21 }
22 
23 public struct ReceiveAsRange {
24     bool empty() {
25         return data.length == 0;
26     };
27     ubyte[] front() {
28         return data;
29     };
30     void popFront() {
31         if ( read ) {
32             // get new portion
33             data = read();
34         } else {
35             // we can't read any new data
36             data.length = 0;
37         }
38     };
39     package {
40         bool            activated;
41         ubyte[]         data;
42         ubyte[]         delegate() read;
43     }
44 }
45 
46 public class Response {
47     package {
48         ushort           _code;
49         Buffer!ubyte     _responseBody;
50         string[string]   _responseHeaders;
51         /// Initial URI
52         URI              _uri;
53         /// Final URI. Can differ from __URI if request go through redirections.
54         URI              _finalURI;
55         ReceiveAsRange   _receiveAsRange;
56         SysTime          _startedAt,
57                          _connectedAt,
58                          _requestSentAt,
59                          _finishedAt;
60         mixin(Setter!ushort("code"));
61         mixin(Setter!URI("uri"));
62         mixin(Setter!URI("finalURI"));
63     }
64     mixin(Getter!ushort("code"));
65     mixin(Getter!URI("uri"));
66     mixin(Getter!URI("finalURI"));
67     @property auto ref responseBody() @safe nothrow {
68         return _responseBody;
69     }
70     @property auto ref responseHeaders() pure @safe nothrow {
71         return _responseHeaders;
72     }
73     @property auto ref receiveAsRange() pure @safe nothrow {
74         return _receiveAsRange;
75     }
76     override string toString() const {
77         return "Response(%d, %s)".format(_code, _uri.uri());
78     }
79     string format(string fmt) const {
80         import std.array;
81         auto a = appender!string();
82         auto f = FormatSpec!char(fmt);
83         while (f.writeUpToNextSpec(a)) {
84             switch (f.spec) {
85                 case 'h':
86                     // Remote hostname.
87                     a.put(_uri.host);
88                     break;
89                 case 'p':
90                     // Remote port.
91                     a.put("%d".format(_uri.port));
92                     break;
93                 case 'P':
94                     // Path.
95                     a.put(_uri.path);
96                     break;
97                 case 'q':
98                     // query parameters supplied with url.
99                     a.put(_uri.query);
100                     break;
101                 case 's':
102                     a.put("Response(%d, %s)".format(_code, _uri.uri()));
103                     break;
104                 case 'B': // received bytes
105                     a.put("%d".format(_responseBody.length));
106                     break;
107                 case 'T': // request total time, ms
108                     a.put("%d".format((_finishedAt - _startedAt).total!"msecs"));
109                     break;
110                 case 'U':
111                     a.put(_uri.uri());
112                     break;
113                 case 'S':
114                     a.put("%d".format(_code));
115                     break;
116                 default:
117                     throw new FormatException("Unknown Response format specifier: %" ~ f.spec);
118             }
119         }
120         return a.data();
121     }
122 }