{"id":19155922,"url":"https://github.com/lyokha/http-client-brread-timeout","last_synced_at":"2025-10-16T06:57:01.149Z","repository":{"id":37727909,"uuid":"506092635","full_name":"lyokha/http-client-brread-timeout","owner":"lyokha","description":"Http client with timeouts applied in between body read events","archived":false,"fork":false,"pushed_at":"2023-11-29T20:21:30.000Z","size":19,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-02-05T02:04:53.594Z","etag":null,"topics":["haskell","http-client","network","read","timeout"],"latest_commit_sha":null,"homepage":"","language":"Haskell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/lyokha.png","metadata":{"files":{"readme":"README.md","changelog":"Changelog.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-06-22T04:07:20.000Z","updated_at":"2023-01-07T18:00:25.000Z","dependencies_parsed_at":"2022-09-16T06:03:18.614Z","dependency_job_id":null,"html_url":"https://github.com/lyokha/http-client-brread-timeout","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lyokha%2Fhttp-client-brread-timeout","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lyokha%2Fhttp-client-brread-timeout/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lyokha%2Fhttp-client-brread-timeout/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lyokha%2Fhttp-client-brread-timeout/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lyokha","download_url":"https://codeload.github.com/lyokha/http-client-brread-timeout/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":240241799,"owners_count":19770463,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["haskell","http-client","network","read","timeout"],"created_at":"2024-11-09T08:32:42.683Z","updated_at":"2025-10-16T06:56:56.093Z","avatar_url":"https://github.com/lyokha.png","language":"Haskell","funding_links":[],"categories":[],"sub_categories":[],"readme":"Http client with time-limited brRead\n====================================\n\n[![Build Status](https://github.com/lyokha/http-client-brread-timeout/workflows/CI/badge.svg)](https://github.com/lyokha/http-client-brread-timeout/actions?query=workflow%3ACI)\n[![Hackage](https://img.shields.io/hackage/v/http-client-brread-timeout.svg?label=hackage%20%7C%20http-client-brread-timeout\u0026logo=haskell\u0026logoColor=%239580D1)](https://hackage.haskell.org/package/http-client-brread-timeout)\n\nHttp client with timeouts applied in between body read events.\n\nNote that the response timeout in\n[*http-client*](https://github.com/snoyberg/http-client) is applied only when\nreceiving the response headers which is not always satisfactory given that a\nslow server may send the rest of the response very slowly.\n\n### How do I test this?\n\nA slow server can be emulated in *Nginx* with the following configuration.\n\n```nginx\nuser                    nobody;\nworker_processes        2;\n\nevents {\n    worker_connections  1024;\n}\n\nhttp {\n    default_type        application/octet-stream;\n    sendfile            on;\n\n    server {\n        listen          8010;\n        server_name     main;\n\n        location /slow {\n            echo 1; echo_flush;\n            # send extra chunks of the response body once in 20 sec\n            echo_sleep 20; echo 2; echo_flush;\n            echo_sleep 20; echo 3; echo_flush;\n            echo_sleep 20; echo 4;\n        }\n\n        location /very/slow {\n            echo 1; echo_flush;\n            echo_sleep 20; echo 2; echo_flush;\n            # chunk 3 is extremely slow (40 sec)\n            echo_sleep 40; echo 3; echo_flush;\n            echo_sleep 20; echo 4;\n        }\n    }\n}\n```\n\n*GHCI* session.\n\n\u003cpre\u003e\n\u003cb\u003ePrelude\u0026gt;\u003c/b\u003e import Network.HTTP.Client as HTTP.Client\n\u003cb\u003ePrelude HTTP.Client\u0026gt;\u003c/b\u003e import Network.HTTP.Client.BrReadWithTimeout as BrReadWithTimeout\n\u003cb\u003ePrelude HTTP.Client BrReadWithTimeout\u0026gt;\u003c/b\u003e httpManager = newManager defaultManagerSettings\n\u003cb\u003ePrelude HTTP.Client BrReadWithTimeout\u0026gt;\u003c/b\u003e man \u0026lt;- httpManager\n\u003cb\u003ePrelude HTTP.Client BrReadWithTimeout\u0026gt;\u003c/b\u003e reqVerySlow \u0026lt;- parseRequest \"GET http://127.0.0.1:8010/very/slow\"\n\u003cb\u003ePrelude HTTP.Client BrReadWithTimeout\u0026gt;\u003c/b\u003e reqSlow \u0026lt;- parseRequest \"GET http://127.0.0.1:8010/slow\"\n\u003cb\u003ePrelude HTTP.Client BrReadWithTimeout\u0026gt;\u003c/b\u003e :set +s\n\u003cb\u003ePrelude HTTP.Client BrReadWithTimeout\u0026gt;\u003c/b\u003e httpLbs reqVerySlow man\nResponse {responseStatus = Status {statusCode = 200, statusMessage = \"OK\"}, responseVersion = HTTP/1.1, responseHeaders = [(\"Server\",\"nginx/1.22.0\"),(\"Date\",\"Thu, 23 Jun 2022 22:04:02 GMT\"),(\"Content-Type\",\"application/octet-stream\"),(\"Transfer-Encoding\",\"chunked\"),(\"Connection\",\"keep-alive\")], responseBody = \"1\\n2\\n3\\n4\\n\", responseCookieJar = CJ {expose = []}, responseClose' = ResponseClose, responseOriginalRequest = Request {\n  host                 = \"127.0.0.1\"\n  port                 = 8010\n  secure               = False\n  requestHeaders       = []\n  path                 = \"/very/slow\"\n  queryString          = \"\"\n  method               = \"GET\"\n  proxy                = Nothing\n  rawBody              = False\n  redirectCount        = 10\n  responseTimeout      = ResponseTimeoutDefault\n  requestVersion       = HTTP/1.1\n  proxySecureMode      = ProxySecureWithConnect\n}\n}\n(80.09 secs, 1,084,840 bytes)\n\u003cb\u003ePrelude HTTP.Client BrReadWithTimeout\u0026gt;\u003c/b\u003e httpLbsBrReadWithTimeout reqVerySlow man\n\u0026ast;\u0026ast;\u0026ast; Exception: HttpExceptionRequest Request {\n  host                 = \"127.0.0.1\"\n  port                 = 8010\n  secure               = False\n  requestHeaders       = []\n  path                 = \"/very/slow\"\n  queryString          = \"\"\n  method               = \"GET\"\n  proxy                = Nothing\n  rawBody              = False\n  redirectCount        = 10\n  responseTimeout      = ResponseTimeoutMicro 30000000\n  requestVersion       = HTTP/1.1\n  proxySecureMode      = ProxySecureWithConnect\n}\n ResponseTimeout\n\u003cb\u003ePrelude HTTP.Client BrReadWithTimeout\u0026gt;\u003c/b\u003e httpLbsBrReadWithTimeout reqSlow man\nResponse {responseStatus = Status {statusCode = 200, statusMessage = \"OK\"}, responseVersion = HTTP/1.1, responseHeaders = [(\"Server\",\"nginx/1.22.0\"),(\"Date\",\"Thu, 23 Jun 2022 22:08:46 GMT\"),(\"Content-Type\",\"application/octet-stream\"),(\"Transfer-Encoding\",\"chunked\"),(\"Connection\",\"keep-alive\")], responseBody = \"1\\n2\\n3\\n4\\n\", responseCookieJar = CJ {expose = []}, responseClose' = ResponseClose, responseOriginalRequest = Request {\n  host                 = \"127.0.0.1\"\n  port                 = 8010\n  secure               = False\n  requestHeaders       = []\n  path                 = \"/slow\"\n  queryString          = \"\"\n  method               = \"GET\"\n  proxy                = Nothing\n  rawBody              = False\n  redirectCount        = 10\n  responseTimeout      = ResponseTimeoutDefault\n  requestVersion       = HTTP/1.1\n  proxySecureMode      = ProxySecureWithConnect\n}\n}\n(60.07 secs, 1,082,880 bytes)\n\u003c/pre\u003e\n\nHere, the first request comes from the standard `httpLbs` which, after timely\nreceiving of the first chunk of the response (including headers and the first\nchunk of the body), no longer applies any timeouts and may last as long as the\nresponse endures: in this case, it lasts 80 seconds and successfully returns.\nIn the second request, `httpLbsBrReadWithTimeout` timely receives the first\nchunk of the response too, the second chunk comes in 20 seconds, and finally,\nas the third chunk is going to come in 40 seconds which exceeds the default\nresponse timeout value (30 seconds), the function throws `ResponseTimeout`\nexception after 50 seconds from the start of the request. In the third request,\n`httpLbsBrReadWithTimeout` returns successfully after 60 seconds because every\nextra chunk of the response was coming every 20 seconds without triggering the\ntimeout.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flyokha%2Fhttp-client-brread-timeout","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flyokha%2Fhttp-client-brread-timeout","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flyokha%2Fhttp-client-brread-timeout/lists"}