Request

This is simplest interface to both http and ftp protocols. Request has methods get, post and exec which routed to proper concrete handler (http or ftp, etc). To enable some protocol-specific featutes you have to use protocol interface directly (see docs for HTTPRequest or FTPRequest)

Members

Functions

addHeaders
void addHeaders(string[string] headers)

Add headers to request

exec
Response exec(A args)
Undocumented in source. Be warned that the author may not have intended to support it.
get
Response get(string uri, A args)

Execute GET for http and retrieve file for FTP. You have to provide at least uri. All other arguments should conform to HTTPRequest.get or FTPRequest.get depending on the URI scheme. When arguments do not conform scheme (for example you try to call get("ftp://somehost.net/pub/README", {"a":"b"}) which doesn't make sense) you will receive Exception("Operation not supported for ftp")

post
Response post(string uri, A args)

Execute POST for http and STOR file for FTP. You have to provide uri and data. Data should conform to HTTPRequest.post or FTPRequest.post depending on the URI scheme. When arguments do not conform scheme you will receive Exception("Operation not supported for ftp")

Properties

authenticator
Auth authenticator [@property setter]

Set authenticator for http requests. v - Auth instance.

bufferSize
size_t bufferSize [@property setter]

Set IO buffer size for http and ftp requests v - buffer size in bytes.

contentLength
long contentLength [@property getter]

get contentLength of the responce

contentReceived
long contentReceived [@property getter]

get length og actually received content. this value increase over time, while we receive data

cookie
Cookie[] cookie [@property setter]

Set Cookie for http requests. v - array of cookie.

cookie
Cookie[] cookie [@property getter]

Get Cookie for http requests. v - array of cookie.

keepAlive
bool keepAlive [@property setter]

Set http keepAlive value v - use keepalive requests - true, or not - false

maxContentLength
size_t maxContentLength [@property setter]

Set maximum content lenth both for http and ftp requests v - maximum content length in bytes. When limit reached - throw RequestException

maxHeadersLength
size_t maxHeadersLength [@property setter]

Set maximum length for HTTP headers v - maximum length of the HTTP response. When limit reached - throw RequestException

maxRedirects
uint maxRedirects [@property setter]

Set limit on HTTP redirects v - limit on redirect depth

sslOptions
auto sslOptions [@property getter]
Undocumented in source. Be warned that the author may not have intended to support it.
sslSetCaCert
string sslSetCaCert [@property setter]
Undocumented in source. Be warned that the author may not have intended to support it.
sslSetCertFile
void sslSetCertFile(string path, SSLOptions.filetype type)
Undocumented in source. Be warned that the author may not have intended to support it.
sslSetKeyFile
void sslSetKeyFile(string path, SSLOptions.filetype type)
Undocumented in source. Be warned that the author may not have intended to support it.
sslSetVerifyPeer
bool sslSetVerifyPeer [@property setter]
Undocumented in source. Be warned that the author may not have intended to support it.
timeout
Duration timeout [@property setter]

Set timeout on IO operation. v - timeout value

useStreaming
bool useStreaming [@property setter]

set "streaming" property

verbosity
uint verbosity [@property setter]

Set verbosity for HTTP or FTP requests. v - verbosity level (0 - no output, 1 - headers to stdout, 2 - headers and body progress to stdout). default = 0.

Examples

1 import std.algorithm;
2 import std.range;
3 import std.array;
4 import std.json;
5 import std.stdio;
6 import std.string;
7 import std.exception;
8 
9 string httpbinUrl = httpTestServer();
10 
11 version(vibeD) {
12 }
13 else {
14     import httpbin;
15     auto server = httpbinApp();
16     server.start();
17     scope(exit) {
18         server.stop();
19     }
20 }
21 
22 globalLogLevel(LogLevel.info);
23 
24 infof("testing Request");
25 Request rq;
26 Response rs;
27 //
28 rs = rq.get(httpbinUrl);
29 assert(rs.code==200);
30 assert(rs.responseBody.length > 0);
31 rs = rq.get(httpbinUrl ~ "get", ["c":" d", "a":"b"]);
32 assert(rs.code == 200);
33 auto json = parseJSON(rs.responseBody.data).object["args"].object;
34 assert(json["c"].str == " d");
35 assert(json["a"].str == "b");
36 
37 rq = Request();
38 rq.keepAlive = true;
39 // handmade json
40 info("Check POST json");
41 rs = rq.post(httpbinUrl ~ "post?b=x", `{"a":"b ", "c":[1,2,3]}`, "application/json");
42 assert(rs.code==200);
43 json = parseJSON(rs.responseBody.data).object["args"].object;
44 assert(json["b"].str == "x");
45 json = parseJSON(rs.responseBody.data).object["json"].object;
46 assert(json["a"].str == "b ");
47 assert(json["c"].array.map!(a=>a.integer).array == [1,2,3]);
48 {
49     import std.file;
50     import std.path;
51     auto tmpd = tempDir();
52     auto tmpfname = tmpd ~ dirSeparator ~ "request_test.txt";
53     auto f = File(tmpfname, "wb");
54     f.rawWrite("abcdefgh\n12345678\n");
55     f.close();
56     // files
57     info("Check POST files");
58     PostFile[] files = [
59         {fileName: tmpfname, fieldName:"abc", contentType:"application/octet-stream"}, 
60         {fileName: tmpfname}
61     ];
62     rs = rq.post(httpbinUrl ~ "post", files);
63     assert(rs.code==200);
64     info("Check POST chunked from file.byChunk");
65     f = File(tmpfname, "rb");
66     rs = rq.post(httpbinUrl ~ "post", f.byChunk(3), "application/octet-stream");
67     assert(rs.code==200);
68     auto data = fromJsonArrayToStr(parseJSON(rs.responseBody).object["data"]);
69     assert(data=="abcdefgh\n12345678\n");
70     f.close();
71 }
72 // ranges
73 {
74     info("Check POST chunked from lineSplitter");
75     auto s = lineSplitter("one,\ntwo,\nthree.");
76     rs = rq.exec!"POST"(httpbinUrl ~ "post", s, "application/octet-stream");
77     assert(rs.code==200);
78     auto data = fromJsonArrayToStr(parseJSON(rs.responseBody).object["data"]);
79     assert(data=="one,two,three.");
80 }
81 {
82     info("Check POST chunked from array");
83     auto s = ["one,", "two,", "three."];
84     rs = rq.post(httpbinUrl ~ "post", s, "application/octet-stream");
85     assert(rs.code==200);
86     auto data = fromJsonArrayToStr(parseJSON(rs.responseBody).object["data"]);
87     assert(data=="one,two,three.");
88 }
89 {
90     info("Check POST chunked using std.range.chunks()");
91     auto s = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
92     rs = rq.post(httpbinUrl ~ "post", s.representation.chunks(10), "application/octet-stream");
93     assert(rs.code==200);
94     auto data = fromJsonArrayToStr(parseJSON(rs.responseBody).object["data"]);
95     assert(data==s);
96 }
97 // associative array
98 rs = rq.post(httpbinUrl ~ "post", ["a":"b ", "c":"d"]);
99 assert(rs.code==200);
100 auto form = parseJSON(rs.responseBody.data).object["form"].object;
101 assert(form["a"].str == "b ");
102 assert(form["c"].str == "d");
103 info("Check HEAD");
104 rs = rq.exec!"HEAD"(httpbinUrl);
105 assert(rs.code==200);
106 info("Check DELETE");
107 rs = rq.exec!"DELETE"(httpbinUrl ~ "delete");
108 assert(rs.code==200);
109 info("Check PUT");
110 rs = rq.exec!"PUT"(httpbinUrl ~ "put",  `{"a":"b", "c":[1,2,3]}`, "application/json");
111 assert(rs.code==200);
112 info("Check PATCH");
113 rs = rq.exec!"PATCH"(httpbinUrl ~ "patch", "привiт, свiт!", "application/octet-stream");
114 assert(rs.code==200);
115 
116 info("Check compressed content");
117 rq = Request();
118 rq.keepAlive = true;
119 rq.addHeaders(["X-Header": "test"]);
120 rs = rq.get(httpbinUrl ~ "gzip");
121 assert(rs.code==200);
122 info("gzip - ok");
123 rs = rq.get(httpbinUrl ~ "deflate");
124 assert(rs.code==200);
125 info("deflate - ok");
126 
127 info("Check redirects");
128 rq = Request();
129 rq.keepAlive = true;
130 rs = rq.get(httpbinUrl ~ "relative-redirect/2");
131 assert((cast(HTTPResponse)rs).history.length == 2);
132 assert((cast(HTTPResponse)rs).code==200);
133 
134 info("Check cookie");
135 rq = Request();
136 rs = rq.get(httpbinUrl ~ "cookies/set?A=abcd&b=cdef");
137 assert(rs.code == 200);
138 json = parseJSON(rs.responseBody.data).object["cookies"].object;
139 assert(json["A"].str == "abcd");
140 assert(json["b"].str == "cdef");
141 auto cookie = rq.cookie();
142 foreach(c; rq.cookie) {
143     final switch(c.attr) {
144         case "A":
145             assert(c.value == "abcd");
146             break;
147         case "b":
148             assert(c.value == "cdef");
149             break;
150     }
151 }
152 rs = rq.get(httpbinUrl ~ "absolute-redirect/2");
153 assert((cast(HTTPResponse)rs).history.length == 2);
154 assert((cast(HTTPResponse)rs).code==200);
155 //    rq = Request();
156 rq.maxRedirects = 2;
157 rq.keepAlive = false;
158 assertThrown!MaxRedirectsException(rq.get(httpbinUrl ~ "absolute-redirect/3"));
159 
160 info("Check chunked content");
161 rq = Request();
162 rq.keepAlive = true;
163 rq.bufferSize = 16*1024;
164 rs = rq.get(httpbinUrl ~ "range/1024");
165 assert(rs.code==200);
166 assert(rs.responseBody.length==1024);
167 
168 info("Check basic auth");
169 rq = Request();
170 rq.authenticator = new BasicAuthentication("user", "passwd");
171 rs = rq.get(httpbinUrl ~ "basic-auth/user/passwd");
172 assert(rs.code==200);
173 
174 info("Check limits");
175 rq = Request();
176 rq.maxContentLength = 1;
177 assertThrown!RequestException(rq.get(httpbinUrl));
178 rq = Request();
179 rq.maxHeadersLength = 1;
180 assertThrown!RequestException(rq.get(httpbinUrl));
181 
182 info("Test getContent");
183 auto r = getContent(httpbinUrl ~ "stream/20");
184 assert(r.splitter('\n').filter!("a.length>0").count == 20);
185 r = getContent(httpbinUrl ~ "get", ["a":"b", "c":"d"]);
186 string name = "user", sex = "male";
187 int    age = 42;
188 r = getContent(httpbinUrl ~ "get", "name", name, "age", age, "sex", sex);
189 
190 info("Test receiveAsRange with GET");
191 rq = Request();
192 rq.useStreaming = true;
193 rq.bufferSize = 16;
194 rs = rq.get(httpbinUrl ~ "stream/20");
195 auto stream = rs.receiveAsRange();
196 ubyte[] streamedContent;
197 while( !stream.empty() ) {
198     streamedContent ~= stream.front;
199     stream.popFront();
200 }
201 rq = Request();
202 rs = rq.get(httpbinUrl ~ "stream/20");
203 assert(streamedContent == rs.responseBody.data);
204 info("Test postContent");
205 r = postContent(httpbinUrl ~ "post", `{"a":"b", "c":1}`, "application/json");
206 assert(parseJSON(r).object["json"].object["c"].integer == 1);
207 
208 /// Posting to forms (for small data)
209 ///
210 /// posting query parameters using "application/x-www-form-urlencoded"
211 info("Test postContent using query params");
212 postContent(httpbinUrl ~ "post", queryParams("first", "a", "second", 2));
213 
214 /// posting using multipart/form-data (large data and files). See docs fot HTTPRequest
215 info("Test postContent form");
216 MultipartForm mpform;
217 mpform.add(formData(/* field name */ "greeting", /* content */ cast(ubyte[])"hello"));
218 postContent(httpbinUrl ~ "post", mpform);
219 
220 /// you can do this using Request struct to access response details
221 info("Test postContent form via Request()");
222 rq = Request();
223 mpform = MultipartForm().add(formData(/* field name */ "greeting", /* content */ cast(ubyte[])"hello"));
224 rs = rq.post(httpbinUrl ~ "post", mpform);
225 assert(rs.code == 200);
226 
227 info("Test receiveAsRange with POST");
228 streamedContent.length = 0;
229 rq = Request();
230 rq.useStreaming = true;
231 rq.bufferSize = 16;
232 string s = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
233 rs = rq.post(httpbinUrl ~ "post", s.representation.chunks(10), "application/octet-stream");
234 stream = rs.receiveAsRange();
235 while( !stream.empty() ) {
236     streamedContent ~= stream.front;
237     stream.popFront();
238 }
239 rq = Request();
240 rs = rq.post(httpbinUrl ~ "post", s.representation.chunks(10), "application/octet-stream");
241 assert(streamedContent == rs.responseBody.data);
242 info("Test get in parallel");
243 {
244     import std.stdio;
245     import std.parallelism;
246     import std.algorithm;
247     import std.string;
248     import core.atomic;
249 
250     immutable auto urls = [
251         "stream/10",
252         "stream/20",
253         "stream/30",
254         "stream/40",
255         "stream/50",
256         "stream/60",
257         "stream/70",
258     ].map!(a => httpbinUrl ~ a).array.idup;
259 
260     defaultPoolThreads(4);
261 
262     shared short lines;
263 
264     foreach(url; parallel(urls)) {
265         atomicOp!"+="(lines, getContent(url).splitter("\n").count);
266     }
267     assert(lines == 287);
268 
269 }

Meta