Http-клиент Gun не печатает ответное тело - PullRequest
2 голосов
/ 16 октября 2019

У меня есть http gen_server, который использует gun в качестве http-библиотеки клиента. Я могу открыть соединение и сделать запрос GET. Тем не менее, я не получаю тело ответа, но получено сообщение 'fin. Как получить ответ пистолета в асинхронном режиме?

(defun get (url-map)
  (io:format "http get ~p~n" `(,url-map))
  (let* ((`#(ok ,con-pid) (gun:open (binary:bin_to_list (map-get url-map 'host))
                                    (if (== (map-get url-map 'scheme) #"https")
                                      443
                                      80)))
         (mon-ref (monitor 'process con-pid)))
    (receive
      (msg
       (progn
         (io:format "gun:open msg ~p~n" `(,msg))
         (handle-get-request url-map con-pid mon-ref)))
     ((after 3000) (exit 'timeout)))))

(defun handle-get-request (url-map con-pid mon-ref)
  (io:format "handle get request path: ~p~n" `(,(map-get url-map 'path))) 
  (let ((stream-ref (gun:get con-pid (map-get url-map 'path))))
    (receive
      ((tuple 'gun_response con-pid stream-ref 'fin status headers)
       (progn
         (io:format "get response body fin~nstatus ~p~nheaders ~p~n"
                    `(,status ,headers))
         'no_data))  ; <--- how to get the response body content?
      ((tuple 'gun_response con-pid stream-ref 'nofin status headers)
       (progn
         (io:format "get response body nofin status ~p~n" `(,status))
         (handle-get-response url-map con-pid mon-ref stream-ref)))
      ((tuple 'DOWN mon-ref 'process con-pid reason)
       (io:format "handle get request error ~p~n" `(,reason))
       (exit reason))
      ((after 5000) (exit 'timeout)))))


(defun handle-get-response (url-map con-pid mon-ref stream-ref)
  (io:format "handle-get-response~n")
  (receive
    ((tuple 'gun_data con-pid stream-ref 'nofin data)
     (progn
       (io:format "get response data ~p~n" `(,data))
       (handle-get-response url-map con-pid mon-ref stream-ref)))
    ((tuple 'gun_data con-pid stream-ref 'fin data)
     (io:format "handle get response finished ~p~n" `(,data)))
    ((tuple 'DOWN mon-ref 'process con-pid reason)
     (io:format "handle get request error ~p~n" `(,reason))
     (exit reason))
    ((after 5000) (exit 'timeout))))

Журнал запросов:

http cast get state: #{host => <<"httpstat.us">>,path => <<"/200">>,
                       scheme => <<"https">>}
http get #{host => <<"httpstat.us">>,path => <<"/200">>,scheme => <<"https">>}
gun:open msg {gun_up,<0.146.0>,http}
handle get request path: <<"/200">>
get response body fin  <--- how to get the response body content?
status 200
headers [{<<"cache-control">>,<<"private">>},
         {<<"server">>,<<"Microsoft-IIS/10.0">>},
         {<<"x-aspnetmvc-version">>,<<"5.1">>},
         {<<"access-control-allow-origin">>,<<"*">>},
         {<<"x-aspnet-version">>,<<"4.0.30319">>},
         {<<"x-powered-by">>,<<"ASP.NET">>},
         {<<"set-cookie">>,
          <<"ARRAffinity=93fdbab9d364704de8ef77182b4d13811344b7dd1ec45d3a9682bbd6fa154ead;Path=/;HttpOnly;Domain=httpstat.us:443">>},
         {<<"date">>,<<"Wed, 16 Oct 2019 19:07:29 GMT">>},
         {<<"content-length">>,<<"0">>}]

Я могу получить тело ответа для других URL-адресов. Например:

handle-post <<"http://date.jsontest.com/">>
dreamnet-svc-sup start child [#{url => <<"http://date.jsontest.com/">>}]
http cast get state: #{host => <<"date.jsontest.com">>,path => <<"/">>,
                       scheme => <<"http">>}
http get #{host => <<"date.jsontest.com">>,path => <<"/">>,
           scheme => <<"http">>}
gun:open msg {gun_up,<0.188.0>,http}
handle get request path: <<"/">>
get response body nofin status 200
handle-get-response
handle get response finished <<"{\n   \"date\": \"10-17-2019\",\n   \"milliseconds_since_epoch\": 1571333596738,\n   \"time\": \"05:33:16 PM\"\n}\n">>
lfe>                 

Если разобраться в этом, используя http-клиент erlang, тело будет пустым.

lfe> (lists:map (lambda (a) (application:start a)) '(crypto asn1 public_key ssl inets))
lfe> (httpc:request 'get `#("https://httpstat.us/200" ()) '() '())
#(ok
  #(#("HTTP/1.1" 200 "OK")
    (#("cache-control" "private")
     #("date" "Thu, 17 Oct 2019 17:29:50 GMT")
     #("server" "Microsoft-IIS/10.0")
     #("content-length" "0")
     #("x-aspnetmvc-version" "5.1")
     #("access-control-allow-origin" "*")
     #("x-aspnet-version" "4.0.30319")
     #("x-powered-by" "ASP.NET")
     #("set-cookie"
       "ARRAffinity=93fdbab9d364704de8ef77182b4d13811344b7dd1ec45d3a9682bbd6fa154ead;Path=/;HttpOnly;Domain=httpstat.us"))
    ()))

Возможно, это проблема с сервером, но я не могуполучить ответ с помощью curl.

curl -X GET "https://httpstat.us/200"
200 OK%

1 Ответ

0 голосов
/ 17 октября 2019

Если вы хотите получить ответ синхронно, то после вызова gun:get вы можете использовать возвращенный stream-ref для вызова gun:await. Смотрите документацию по оружию для gun: ждите . Итак, что-то вроде:

(let (stream-ref (gun:get con-pid (map-get url-map 'path))))
(receive
((tuple 'data 'fin data)
 (progn
   (io:format "get response data ~p~n" `(,data))

Но вы не хотите ждать ответа, поэтому вы можете попытаться перебрать исходный прием, пока не получите нужный тип сообщения. Вы можете поместить исходный прием в рекурсивную функцию, а затем завершить рекурсивную функцию, когда получите нужный тип сообщения. Другими словами, вы все равно получите сообщение 'fin, но ваша функция на этом не закончится - он будет продолжать получать сообщения, пока не получит сообщение 'data.

...