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 @property void socketFactory(NetworkStream delegate(string, string, ushort) f) { 98 _http.socketFactory = f; 99 } 100 /++ Set Cookie for http requests. 101 $(B v) - array of cookie. 102 103 You can set and read cookies. In the next example server set cookie and we read it. 104 Example: 105 --- 106 void main() { 107 rs = rq.get("http://httpbin.org/cookies/set?A=abcd&b=cdef"); 108 assert(rs.code == 200); 109 auto json = parseJSON(cast(string)rs.responseBody.data).object["cookies"].object; 110 assert(json["A"].str == "abcd"); 111 assert(json["b"].str == "cdef"); 112 foreach(c; rq.cookie) { 113 final switch(c.attr) { 114 case "A": 115 assert(c.value == "abcd"); 116 break; 117 case "b": 118 assert(c.value == "cdef"); 119 break; 120 } 121 } 122 } 123 --- 124 +/ 125 @property void cookie(Cookie[] v) pure @nogc nothrow { 126 _http.cookie = v; 127 } 128 /// Get Cookie for http requests. 129 @property Cookie[] cookie() pure @nogc nothrow { 130 return _http.cookie; 131 } 132 /++ 133 set "streaming" property 134 $(B v) = value to set (true - use streaming). 135 136 Use streaming when you do not want to keep whole response in memory. 137 Example: 138 --- 139 import requests; 140 import std.stdio; 141 142 void main() { 143 Request rq = Request(); 144 145 rq.useStreaming = true; 146 auto rs = rq.get("http://example.com/SomeHugePicture.png"); 147 auto stream = rs.receiveAsRange(); 148 File file = File("SomeHugePicture.png", "wb"); 149 150 while(!stream.empty) { 151 file.rawWrite(stream.front); 152 stream.popFront; 153 } 154 file.close(); 155 } 156 --- 157 +/ 158 159 @property void useStreaming(bool v) pure @nogc nothrow { 160 _http.useStreaming = v; 161 _ftp.useStreaming = v; 162 } 163 /// 164 /// get length og actually received content. 165 /// this value increase over time, while we receive data 166 /// 167 @property long contentReceived() pure @nogc nothrow { 168 final switch ( _uri.scheme ) { 169 case "http", "https": 170 return _http.contentReceived; 171 case "ftp": 172 return _ftp.contentReceived; 173 } 174 } 175 /// get contentLength of the responce 176 @property long contentLength() pure @nogc nothrow { 177 final switch ( _uri.scheme ) { 178 case "http", "https": 179 return _http.contentLength; 180 case "ftp": 181 return _ftp.contentLength; 182 } 183 } 184 /++ 185 + Enable or disable ssl peer verification. 186 + $(B v) - enable if `true`, disable if `false`. 187 + 188 + Default - false. 189 + Example: 190 + --- 191 + auto rq = Request(); 192 + rq.sslSetVerifyPeer(true); 193 + auto rs = rq.get("https://localhost:4443/"); 194 + --- 195 +/ 196 @property void sslSetVerifyPeer(bool v) { 197 _http.sslSetVerifyPeer(v); 198 } 199 200 /++ 201 + Set path to ssl key file. 202 + 203 + file type can be SSLOptions.filetype.pem (default) or SSLOptions.filetype.der or SSLOptions.filetype.asn1. 204 + 205 + if you configured only key file or only cert file, we will try to load counterpart from the same file. 206 + 207 + Example: 208 + --- 209 + auto rq = Request(); 210 + rq.sslSetKeyFile("client01.key"); 211 + auto rs = rq.get("https://localhost:4443/"); 212 + --- 213 +/ 214 @property void sslSetKeyFile(string path, SSLOptions.filetype type = SSLOptions.filetype.pem) pure @safe nothrow @nogc { 215 _http.sslSetKeyFile(path, type); 216 } 217 218 /++ 219 + Set path to ssl cert file. 220 + 221 + file type can be SSLOptions.filetype.pem (default) or SSLOptions.filetype.der or SSLOptions.filetype.asn1. 222 + 223 + if you configured only key file or only cert file, we will try to load counterpart from the same file. 224 + 225 + Example: 226 + --- 227 + auto rq = Request(); 228 + rq.sslSetCertFile("client01.crt"); 229 + auto rs = rq.get("https://localhost:4443/"); 230 + --- 231 +/ 232 @property void sslSetCertFile(string path, SSLOptions.filetype type = SSLOptions.filetype.pem) pure @safe nothrow @nogc { 233 _http.sslSetCertFile(path, type); 234 } 235 236 /++ 237 + Set path to ssl ca cert file. 238 + Example: 239 + --- 240 + auto rq = Request(); 241 + rq.sslSetCaCert("/opt/local/etc/openssl/cert.pem"); 242 + auto rs = rq.get("https://localhost:4443/"); 243 + --- 244 +/ 245 @property void sslSetCaCert(string path) pure @safe nothrow @nogc { 246 _http.sslSetCaCert(path); 247 } 248 249 @property auto sslOptions() { 250 return _http.sslOptions(); 251 } 252 253 /++ 254 + Set local address for any outgoing requests. 255 + $(B v) can be string with hostname or ip address. 256 +/ 257 @property void bind(string v) { 258 _http.bind(v); 259 _ftp.bind(v); 260 } 261 262 /++ 263 + Add headers to request 264 + Params: 265 + headers = headers to send. 266 + Example: 267 + --- 268 + rq = Request(); 269 + rq.keepAlive = true; 270 + rq.addHeaders(["X-Header": "test"]); 271 + --- 272 +/ 273 void addHeaders(in string[string] headers) { 274 _http.addHeaders(headers); 275 } 276 void clearHeaders() { 277 _http.clearHeaders(); 278 } 279 /// Execute GET for http and retrieve file for FTP. 280 /// You have to provide at least $(B uri). All other arguments should conform to HTTPRequest.get or FTPRequest.get depending on the URI scheme. 281 /// 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) 282 /// you will receive Exception("Operation not supported for ftp") 283 /// 284 Response get(A...)(string uri, A args) { 285 if ( uri ) { 286 _uri = URI(uri); 287 _uri.idn_encode(); 288 } 289 _method = "GET"; 290 final switch ( _uri.scheme ) { 291 case "http", "https": 292 _http.uri = _uri; 293 static if (__traits(compiles, _http.get(null, args))) { 294 return _http.get(null, args); 295 } else { 296 throw new Exception("Operation not supported for http"); 297 } 298 case "ftp": 299 static if (args.length == 0) { 300 return _ftp.get(uri); 301 } else { 302 throw new Exception("Operation not supported for ftp"); 303 } 304 } 305 } 306 /// Execute POST for http and STOR file for FTP. 307 /// You have to provide $(B uri) and data. Data should conform to HTTPRequest.post or FTPRequest.post depending on the URI scheme. 308 /// When arguments do not conform scheme you will receive Exception("Operation not supported for ftp") 309 /// 310 Response post(A...)(string uri, A args) { 311 if ( uri ) { 312 _uri = URI(uri); 313 _uri.idn_encode(); 314 } 315 _method = "POST"; 316 final switch ( _uri.scheme ) { 317 case "http", "https": 318 _http.uri = _uri; 319 static if (__traits(compiles, _http.post(null, args))) { 320 return _http.post(null, args); 321 } else { 322 throw new Exception("Operation not supported for http"); 323 } 324 case "ftp": 325 static if (__traits(compiles, _ftp.post(uri, args))) { 326 return _ftp.post(uri, args); 327 } else { 328 throw new Exception("Operation not supported for ftp"); 329 } 330 } 331 } 332 /++ 333 + Execute request with method 334 +/ 335 Response exec(string method="GET", A...)(string uri, A args) { 336 _method = method; 337 _uri = URI(uri); 338 _uri.idn_encode(); 339 _http.uri = _uri; 340 return _http.exec!(method)(null, args); 341 } 342 343 string toString() const { 344 return "Request(%s, %s)".format(_method, _uri.uri()); 345 } 346 string format(string fmt) const { 347 final switch(_uri.scheme) { 348 case "http", "https": 349 return _http.format(fmt); 350 case "ftp": 351 return _ftp.format(fmt); 352 } 353 } 354 }