1 module requests.request; 2 import requests.http; 3 import requests.ftp; 4 import requests.streams; 5 import requests.base; 6 import requests.uri; 7 8 import std.datetime; 9 import std.conv; 10 import std.experimental.logger; 11 import std.format; 12 import requests.utils; 13 14 15 /** 16 This is simplest interface to both http and ftp protocols. 17 Request has methods get, post and exec which routed to proper concrete handler (http or ftp, etc). 18 To enable some protocol-specific featutes you have to use protocol interface directly (see docs for HTTPRequest or FTPRequest) 19 */ 20 public struct Request { 21 private { 22 URI _uri; 23 HTTPRequest _http; // route all http/https requests here 24 FTPRequest _ftp; // route all ftp requests here 25 string _method; 26 } 27 /// Set timeout on connection and IO operation. 28 /// $(B v) - timeout value 29 /// If timeout expired Request operation will throw $(B TimeoutException). 30 @property void timeout(Duration v) pure @nogc nothrow { 31 _http.timeout = v; 32 _ftp.timeout = v; 33 } 34 /// Set http keepAlive value 35 /// $(B v) - use keepalive requests - $(B true), or not - $(B false) 36 /// Request will automatically reopen connection when host, protocol 37 /// or port change (so it is safe to send different requests through 38 /// single instance of Request). 39 /// It also recovers if server prematurely close keep-alive connection. 40 @property void keepAlive(bool v) pure @nogc nothrow { 41 _http.keepAlive = v; 42 } 43 /// Set limit on HTTP redirects 44 /// $(B v) - limit on redirect depth 45 /// Throws $(B MaxRedirectsException) when limit is reached. 46 @property void maxRedirects(uint v) pure @nogc nothrow { 47 _http.maxRedirects = v; 48 } 49 /// Set maximum content lenth both for http and ftp requests 50 /// $(B v) - maximum content length in bytes. When limit reached - throws $(B RequestException) 51 @property void maxContentLength(size_t v) pure @nogc nothrow { 52 _http.maxContentLength = v; 53 _ftp.maxContentLength = v; 54 } 55 /// Set maximum length for HTTP headers 56 /// $(B v) - maximum length of the HTTP response. When limit reached - throws $(B RequestException) 57 @property void maxHeadersLength(size_t v) pure @nogc nothrow { 58 _http.maxHeadersLength = v; 59 } 60 /// Set IO buffer size for http and ftp requests 61 /// $(B v) - buffer size in bytes. 62 @property void bufferSize(size_t v) { 63 _http.bufferSize = v; 64 _ftp.bufferSize = v; 65 } 66 /// Set verbosity for HTTP or FTP requests. 67 /// $(B v) - verbosity level (0 - no output, 1 - headers to stdout, 2 - headers and data hexdump to stdout). default = 0. 68 @property void verbosity(uint v) { 69 _http.verbosity = v; 70 _ftp.verbosity = v; 71 } 72 /++ Set authenticator for http requests. 73 + $(B v) - Auth instance. 74 + Example: 75 + --- 76 + import requests; 77 + void main() { 78 + rq = Request(); 79 + rq.authenticator = new BasicAuthentication("user", "passwd"); 80 + rs = rq.get("http://httpbin.org/basic-auth/user/passwd"); 81 + } 82 + --- 83 +/ 84 @property void authenticator(Auth v) { 85 _http.authenticator = v; 86 _ftp.authenticator = v; 87 } 88 /// set proxy property. 89 /// $(B v) - full url to proxy. 90 /// 91 /// Note that we recognize proxy settings from process environment (see $(LINK https://github.com/ikod/dlang-requests/issues/46)): 92 /// you can use http_proxy, https_proxy, all_proxy (as well as uppercase names). 93 @property void proxy(string v) { 94 _http.proxy = v; 95 _ftp.proxy = v; 96 } 97 /++ Set Cookie for http requests. 98 $(B v) - array of cookie. 99 100 You can set and read cookies. In the next example server set cookie and we read it. 101 Example: 102 --- 103 void main() { 104 rs = rq.get("http://httpbin.org/cookies/set?A=abcd&b=cdef"); 105 assert(rs.code == 200); 106 auto json = parseJSON(cast(string)rs.responseBody.data).object["cookies"].object; 107 assert(json["A"].str == "abcd"); 108 assert(json["b"].str == "cdef"); 109 foreach(c; rq.cookie) { 110 final switch(c.attr) { 111 case "A": 112 assert(c.value == "abcd"); 113 break; 114 case "b": 115 assert(c.value == "cdef"); 116 break; 117 } 118 } 119 } 120 --- 121 +/ 122 @property void cookie(Cookie[] v) pure @nogc nothrow { 123 _http.cookie = v; 124 } 125 /// Get Cookie for http requests. 126 @property Cookie[] cookie() pure @nogc nothrow { 127 return _http.cookie; 128 } 129 /++ 130 set "streaming" property 131 $(B v) = value to set (true - use streaming). 132 133 Use streaming when you do not want to keep whole response in memory. 134 Example: 135 --- 136 import requests; 137 import std.stdio; 138 139 void main() { 140 Request rq = Request(); 141 142 rq.useStreaming = true; 143 auto rs = rq.get("http://example.com/SomeHugePicture.png"); 144 auto stream = rs.receiveAsRange(); 145 File file = File("SomeHugePicture.png", "wb"); 146 147 while(!stream.empty) { 148 file.rawWrite(stream.front); 149 stream.popFront; 150 } 151 file.close(); 152 } 153 --- 154 +/ 155 156 @property void useStreaming(bool v) pure @nogc nothrow { 157 _http.useStreaming = v; 158 _ftp.useStreaming = v; 159 } 160 /// 161 /// get length og actually received content. 162 /// this value increase over time, while we receive data 163 /// 164 @property long contentReceived() pure @nogc nothrow { 165 final switch ( _uri.scheme ) { 166 case "http", "https": 167 return _http.contentReceived; 168 case "ftp": 169 return _ftp.contentReceived; 170 } 171 } 172 /// get contentLength of the responce 173 @property long contentLength() pure @nogc nothrow { 174 final switch ( _uri.scheme ) { 175 case "http", "https": 176 return _http.contentLength; 177 case "ftp": 178 return _ftp.contentLength; 179 } 180 } 181 /++ 182 + Enable or disable ssl peer verification. 183 + $(B v) - enable if `true`, disable if `false`. 184 + 185 + Default - false. 186 + Example: 187 + --- 188 + auto rq = Request(); 189 + rq.sslSetVerifyPeer(true); 190 + auto rs = rq.get("https://localhost:4443/"); 191 + --- 192 +/ 193 @property void sslSetVerifyPeer(bool v) { 194 _http.sslSetVerifyPeer(v); 195 } 196 197 /++ 198 + Set path to ssl key file. 199 + 200 + file type can be SSLOptions.filetype.pem (default) or SSLOptions.filetype.der or SSLOptions.filetype.asn1. 201 + 202 + if you configured only key file or only cert file, we will try to load counterpart from the same file. 203 + 204 + Example: 205 + --- 206 + auto rq = Request(); 207 + rq.sslSetKeyFile("client01.key"); 208 + auto rs = rq.get("https://localhost:4443/"); 209 + --- 210 +/ 211 @property void sslSetKeyFile(string path, SSLOptions.filetype type = SSLOptions.filetype.pem) pure @safe nothrow @nogc { 212 _http.sslSetKeyFile(path, type); 213 } 214 215 /++ 216 + Set path to ssl cert file. 217 + 218 + file type can be SSLOptions.filetype.pem (default) or SSLOptions.filetype.der or SSLOptions.filetype.asn1. 219 + 220 + if you configured only key file or only cert file, we will try to load counterpart from the same file. 221 + 222 + Example: 223 + --- 224 + auto rq = Request(); 225 + rq.sslSetCertFile("client01.crt"); 226 + auto rs = rq.get("https://localhost:4443/"); 227 + --- 228 +/ 229 @property void sslSetCertFile(string path, SSLOptions.filetype type = SSLOptions.filetype.pem) pure @safe nothrow @nogc { 230 _http.sslSetCertFile(path, type); 231 } 232 233 /++ 234 + Set path to ssl ca cert file. 235 + Example: 236 + --- 237 + auto rq = Request(); 238 + rq.sslSetCaCert("/opt/local/etc/openssl/cert.pem"); 239 + auto rs = rq.get("https://localhost:4443/"); 240 + --- 241 +/ 242 @property void sslSetCaCert(string path) pure @safe nothrow @nogc { 243 _http.sslSetCaCert(path); 244 } 245 246 @property auto sslOptions() { 247 return _http.sslOptions(); 248 } 249 250 /++ 251 + Set local address for any outgoing requests. 252 + $(B v) can be string with hostname or ip address. 253 +/ 254 @property void bind(string v) { 255 _http.bind(v); 256 _ftp.bind(v); 257 } 258 259 /++ 260 + Add headers to request 261 + Params: 262 + headers = headers to send. 263 + Example: 264 + --- 265 + rq = Request(); 266 + rq.keepAlive = true; 267 + rq.addHeaders(["X-Header": "test"]); 268 + --- 269 +/ 270 void addHeaders(in string[string] headers) { 271 _http.addHeaders(headers); 272 } 273 void clearHeaders() { 274 _http.clearHeaders(); 275 } 276 /// Execute GET for http and retrieve file for FTP. 277 /// You have to provide at least $(B uri). All other arguments should conform to HTTPRequest.get or FTPRequest.get depending on the URI scheme. 278 /// 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) 279 /// you will receive Exception("Operation not supported for ftp") 280 /// 281 Response get(A...)(string uri, A args) { 282 if ( uri ) { 283 _uri = URI(uri); 284 _uri.idn_encode(); 285 } 286 _method = "GET"; 287 final switch ( _uri.scheme ) { 288 case "http", "https": 289 _http.uri = _uri; 290 static if (__traits(compiles, _http.get(null, args))) { 291 return _http.get(null, args); 292 } else { 293 throw new Exception("Operation not supported for http"); 294 } 295 case "ftp": 296 static if (args.length == 0) { 297 return _ftp.get(uri); 298 } else { 299 throw new Exception("Operation not supported for ftp"); 300 } 301 } 302 } 303 /// Execute POST for http and STOR file for FTP. 304 /// You have to provide $(B uri) and data. Data should conform to HTTPRequest.post or FTPRequest.post depending on the URI scheme. 305 /// When arguments do not conform scheme you will receive Exception("Operation not supported for ftp") 306 /// 307 Response post(A...)(string uri, A args) { 308 if ( uri ) { 309 _uri = URI(uri); 310 _uri.idn_encode(); 311 } 312 _method = "POST"; 313 final switch ( _uri.scheme ) { 314 case "http", "https": 315 _http.uri = _uri; 316 static if (__traits(compiles, _http.post(null, args))) { 317 return _http.post(null, args); 318 } else { 319 throw new Exception("Operation not supported for http"); 320 } 321 case "ftp": 322 static if (__traits(compiles, _ftp.post(uri, args))) { 323 return _ftp.post(uri, args); 324 } else { 325 throw new Exception("Operation not supported for ftp"); 326 } 327 } 328 } 329 /++ 330 + Execute request with method 331 +/ 332 Response exec(string method="GET", A...)(string uri, A args) { 333 _method = method; 334 _uri = URI(uri); 335 _uri.idn_encode(); 336 _http.uri = _uri; 337 return _http.exec!(method)(null, args); 338 } 339 340 string toString() const { 341 return "Request(%s, %s)".format(_method, _uri.uri()); 342 } 343 string format(string fmt) const { 344 final switch(_uri.scheme) { 345 case "http", "https": 346 return _http.format(fmt); 347 case "ftp": 348 return _ftp.format(fmt); 349 } 350 } 351 }