dlang-requests — типа python-requests, только для D (часть 2)

Доброго времени суток!

Во второй части статьи опишу использование библиотеки dlang-requests для менее стандартных случаев.

Request и Response


На более низком уровне библиотеки находится структура Request, обеспечивающая всю функциональность библиотеки.

Request среди прочих методов имеет мeтоды get и post, которыми пользуются getContent () и postContent (), описанные в первой части. Параметры их вызова совпадают с параметрами для getContent () и postContent (). Зачем же она нужна, эта структура?

Для начала опишу как через ее методы можно управлять выполнением запросов.

  • verbosity — установка в значения 0,1,2 позволяет увеличить детальность вывода в stdout.
    Пример увеличения verbosity
    import std.stdio;
    import requests;
    
    void main()
    {
        auto rq = Request();
        rq.verbosity = 2;
        auto rs = rq.get("http://httpbin.org/get", ["a":"b"]);
        writeln(rs.responseBody);
    }
    
    
    > GET /get?a=b HTTP/1.1
    > Connection: Keep-Alive
    > User-Agent: dlang-requests
    > Accept-Encoding: gzip, deflate
    > Host: httpbin.org
    >
    < HTTP/1.1 200 OK
    < server: nginx
    < date: Sat, 25 Jun 2016 13:37:20 GMT
    < content-type: application/json
    < content-length: 229
    < connection: keep-alive
    < access-control-allow-origin: *
    < access-control-allow-credentials: true
    < 229 bytes of body received
    >> Connect time: 143 ms and 666 μs
    >> Request send time: 304 μs
    >> Response recv time: 144 ms and 121 μs
    {
      "args": {
        "a": "b"
      },
      "headers": {
        "Accept-Encoding": "gzip, deflate",
        "Host": "httpbin.org",
        "User-Agent": "dlang-requests"
      },
      "origin": "xxx.xxx.xxx.xxx",
      "url": "http://httpbin.org/get?a=b"
    }
    

  • timeout — устанавливает таймаут на операции ввода-вывода и установки соединения.
    Пример установки таймаута.

    auto rq = Request();
    rq.timeout = 30.seconds;


  • maxRedirects. Управляет допустимым количкством редиректов при выполнении запросов.
    Пример ограничения числа редиректов
    import std.stdio;
    import requests;
    
    void main()
    {
        auto rq = Request();
        rq.verbosity = 2;
        rq.maxRedirects = 2;
        auto rs = rq.get("https://httpbin.org/absolute-redirect/3");
        writeln(rs.code);
    }
    
    

    Вывод:
    > GET /absolute-redirect/3 HTTP/1.1
    > Connection: Keep-Alive
    > User-Agent: dlang-requests
    > Accept-Encoding: gzip, deflate
    > Host: httpbin.org
    >
    < HTTP/1.1 302 FOUND
    < server: nginx
    < date: Sat, 25 Jun 2016 13:54:16 GMT
    < content-type: text/html; charset=utf-8
    < content-length: 283
    < connection: keep-alive
    < location: http://httpbin.org/absolute-redirect/2
    < access-control-allow-origin: *
    < access-control-allow-credentials: true
    < 283 bytes of body received
    >> Connect time: 505 ms and 705 μs
    >> Request send time: 247 μs
    >> Response recv time: 145 ms and 626 μs
    > GET /absolute-redirect/2 HTTP/1.1
    > Connection: Keep-Alive
    > User-Agent: dlang-requests
    > Accept-Encoding: gzip, deflate
    > Host: httpbin.org
    >
    < HTTP/1.1 302 FOUND
    < server: nginx
    < date: Sat, 25 Jun 2016 13:54:16 GMT
    < content-type: text/html; charset=utf-8
    < content-length: 283
    < connection: keep-alive
    < location: http://httpbin.org/absolute-redirect/1
    < access-control-allow-origin: *
    < access-control-allow-credentials: true
    < 283 bytes of body received
    >> Connect time: 135 ms and 621 μs
    >> Request send time: 128 μs
    >> Response recv time: 136 ms and 689 μs
    > GET /absolute-redirect/1 HTTP/1.1
    > Connection: Keep-Alive
    > User-Agent: dlang-requests
    > Accept-Encoding: gzip, deflate
    > Host: httpbin.org
    >
    < HTTP/1.1 302 FOUND
    < server: nginx
    < date: Sat, 25 Jun 2016 13:54:16 GMT
    < content-type: text/html; charset=utf-8
    < content-length: 251
    < connection: keep-alive
    < location: http://httpbin.org/get
    < access-control-allow-origin: *
    < access-control-allow-credentials: true
    < 251 bytes of body received
    >> Connect time: 2 μs
    >> Request send time: 140 μs
    >> Response recv time: 136 ms and 279 μs
    302
    
    


  • authenticator — позволяет управлять авторизацией в запросах.
    Пример
    import std.stdio;
    import requests;
    
    void main()
    {
        auto rq = Request();
        rq.verbosity = 2;
        rq.authenticator = new BasicAuthentication("user", "passwd");
        rs = rq.get("http://httpbin.org/basic-auth/user/passwd");
        assert(rs.code==200);
    }
    
    


  • bufferSize — устанавливает размер буфера чтения (в байтах).
  • proxy — позволяет установить прокси для запросов в форме
    http://host:port/
    

Content Streaming


Изящной фичей python-requests являeтся streaming — пользователь получает ответ от сервера не по окончанию, а в процессе приёма документа. Для приёма и обработки больших документов, такой метод может помочь сэкономить память. python-requests позволяет получать документ в виде итератора. Для D естественным было-бы использовать InputRange. В этом случае мы можем использовать ответ не только для получения данных, но и для прямого использования с алгоритмами, работающими с InputRange.

import std.stdio;
import std.format;
import requests;

void main()
{
    auto rq = Request();
    rq.useStreaming = true;
    rq.verbosity = 2;
    auto rs = rq.get("https://api.github.com/search/repositories?order=desc&sort=updated&q=language:D");
    if ( rs.code == 200 ) {
        auto stream = rs.receiveAsRange();
        while( !stream.empty ) {
            writefln("portion of %d bytes received".format(stream.front.length));
            stream.popFront;
        }
    }
}
вывод
> GET /search/repositories?order=desc&sort=updated&q=language:D HTTP/1.1
> Connection: Keep-Alive
> User-Agent: dlang-requests
> Accept-Encoding: gzip, deflate
> Host: api.github.com
>
< HTTP/1.1 200 OK
< server: GitHub.com
< date: Sat, 25 Jun 2016 15:45:28 GMT
< content-type: application/json; charset=utf-8
< transfer-encoding: chunked
< content-encoding: gzip
< x-github-request-id: B077660C:560B:7F2F21:576EA717
< 277 bytes of body received
< 1370 bytes of body received
portion of 751 bytes received
portion of 2988 bytes received
portion of 4632 bytes received
portion of 6002 bytes received
portion of 7474 bytes received
portion of 9106 bytes received
portion of 10246 bytes received
portion of 11356 bytes received
portion of 12290 bytes received
portion of 12870 bytes received
portion of 63904 bytes received



Здесь видно что типом элементов для stream будет массив байт. Поэтому в следующем коде подсчета символов-цифр требуется использование joiner:

import std.stdio;
import std.ascii;
import std.algorithm;
import requests;

void main()
{
    auto rq = Request();
    rq.useStreaming = true;
    auto stream = rq.get("https://api.github.com/search/repositories?order=desc&sort=updated&q=language:D").receiveAsRange;
    writeln(stream.joiner.filter!isDigit.count);
}


Кроме обработки больших документов «на лету», стриминг даёт самый простой способ для сохранения документов на диске.

Стриминг допустим не только для запросов GET, но и для любых запросов, которые возвращают документ вместе с кодом 200.

Методы PUT/DELETE/HEAD…


Все перечисленные до сих пор методы, в конечном итоге, используют шаблонный метод Request.exec (method), который кроме шаблонного параметра, управляющего HTTP-методом, принимает все те комбинации параметров, которые были упомянуты ранее.

import std.stdio;
import std.ascii;
import std.range;
import std.algorithm;
import requests;

void main()
{
    auto rq = Request();
    rq.useStreaming = true;
    auto rs = rq.exec!"HEAD"("https://api.github.com/search/repositories?order=desc&sort=updated&q=language:D");
    rs.code.writeln;
    rs.responseHeaders.
        byKeyValue.
        take(5).
        each!(p=>writeln(p.key, ": ", p.value));
}


Вывод

200
x-frame-options: deny
cache-control: no-cache
x-xss-protection: 1; mode=block
vary: Accept-Encoding
content-type: application/json; charset=utf-8

Таким же образом можно вызывать любые HTTP методы.

На этом заканчиваю вторую часть статьи.

На всякий случай еще раз ссылка на страницу проекта на Github

Всем удачи и приятного программирования!

© Habrahabr.ru