{"id":19133705,"url":"https://github.com/chazmcgarvey/http-anyua","last_synced_at":"2026-06-23T05:31:55.988Z","repository":{"id":47937055,"uuid":"84690909","full_name":"chazmcgarvey/HTTP-AnyUA","owner":"chazmcgarvey","description":"An HTTP user agent programming interface unification layer","archived":false,"fork":false,"pushed_at":"2022-11-13T11:31:08.000Z","size":92,"stargazers_count":0,"open_issues_count":5,"forks_count":3,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-02-22T17:47:02.847Z","etag":null,"topics":["http","user-agent"],"latest_commit_sha":null,"homepage":"https://metacpan.org/pod/HTTP::AnyUA","language":"Perl","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/chazmcgarvey.png","metadata":{"files":{"readme":"README.md","changelog":"Changes","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":"2017-03-12T01:12:16.000Z","updated_at":"2019-03-17T06:48:28.000Z","dependencies_parsed_at":"2023-01-23T00:00:29.507Z","dependency_job_id":null,"html_url":"https://github.com/chazmcgarvey/HTTP-AnyUA","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/chazmcgarvey/HTTP-AnyUA","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chazmcgarvey%2FHTTP-AnyUA","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chazmcgarvey%2FHTTP-AnyUA/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chazmcgarvey%2FHTTP-AnyUA/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chazmcgarvey%2FHTTP-AnyUA/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/chazmcgarvey","download_url":"https://codeload.github.com/chazmcgarvey/HTTP-AnyUA/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chazmcgarvey%2FHTTP-AnyUA/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":284156474,"owners_count":26956828,"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","status":"online","status_checked_at":"2025-11-13T02:00:06.582Z","response_time":61,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["http","user-agent"],"created_at":"2024-11-09T06:23:33.657Z","updated_at":"2025-11-13T05:03:31.781Z","avatar_url":"https://github.com/chazmcgarvey.png","language":"Perl","funding_links":[],"categories":[],"sub_categories":[],"readme":"# NAME\n\nHTTP::AnyUA - An HTTP user agent programming interface unification layer\n\n# VERSION\n\nversion 0.904\n\n# SYNOPSIS\n\n    my $any_ua = HTTP::AnyUA-\u003enew(ua =\u003e LWP::UserAgent-\u003enew);\n    # OR: my $any_ua = HTTP::AnyUA-\u003enew(ua =\u003e Furl-\u003enew);\n    # OR: my $any_ua = HTTP::AnyUA-\u003enew(ua =\u003e HTTP::Tiny-\u003enew);\n    # etc...\n\n    my $response = $any_ua-\u003eget('http://www.example.com/');\n\n    print \"$response-\u003e{status} $response-\u003e{reason}\\n\";\n\n    while (my ($k, $v) = each %{$response-\u003e{headers}}) {\n        for (ref $v eq 'ARRAY' ? @$v : $v) {\n            print \"$k: $_\\n\";\n        }\n    }\n\n    print $response-\u003e{content} if length $response-\u003e{content};\n\n    ### Non-blocking user agents cause Future objects to be returned:\n\n    my $any_ua = HTTP::AnyUA-\u003enew(ua =\u003e HTTP::Tiny-\u003enew, response_is_future =\u003e 1);\n    # OR: my $any_ua = HTTP::AnyUA-\u003enew(ua =\u003e 'AnyEvent::HTTP');\n    # OR: my $any_ua = HTTP::AnyUA-\u003enew(ua =\u003e Mojo::UserAgent-\u003enew);\n    # etc...\n\n    my $future = $any_ua-\u003eget('http://www.example.com/');\n\n    $future-\u003eon_done(sub {\n        my $response = shift;\n\n        print \"$response-\u003e{status} $response-\u003e{reason}\\n\";\n\n        while (my ($k, $v) = each %{$response-\u003e{headers}}) {\n            for (ref $v eq 'ARRAY' ? @$v : $v) {\n                print \"$k: $_\\n\";\n            }\n        }\n\n        print $response-\u003e{content} if length $response-\u003e{content};\n    });\n\n    $future-\u003eon_fail(sub { print STDERR \"Oh no!!\\n\" });\n\n# DESCRIPTION\n\nThis module provides a small wrapper for unifying the programming interfaces of several different\nactual user agents (HTTP clients) under one **familiar** interface.\n\nRather than providing yet another programming interface for you to learn, HTTP::AnyUA follows the\n[HTTP::Tiny](https://metacpan.org/pod/HTTP::Tiny) interface. This also means that you can plug in any supported HTTP client\n([LWP::UserAgent](https://metacpan.org/pod/LWP::UserAgent), [Furl](https://metacpan.org/pod/Furl), etc.) and use it as if it were [HTTP::Tiny](https://metacpan.org/pod/HTTP::Tiny).\n\nThere are a lot of great HTTP clients available for Perl, each with different goals, different\nfeature sets, and of course different programming interfaces! If you're an end user, you can just\npick one of these clients according to the needs of your project (or personal preference). But if\nyou're writing a module that needs to interface with a web server (like perhaps a RESTful API\nwrapper) and you want your users to be able to use whatever HTTP client they want, HTTP::AnyUA can\nhelp you support that!\n\nIt's a good idea to let the end user pick whatever HTTP client they want to use, because they're the\none who knows the requirements of their application or script. If you're writing an event-driven\napplication, you'll need to use a non-blocking user agent like [Mojo::UserAgent](https://metacpan.org/pod/Mojo::UserAgent). If you're writing\na simple command-line script, you may decide that your priority is to minimize dependencies and so\nmay want to go with [HTTP::Tiny](https://metacpan.org/pod/HTTP::Tiny).\n\nUnfortunately, many modules on CPAN are hardcoded to work with specific HTTP clients, leaving the\nend user unable to use the HTTP client that would be best for them. Although the end user won't --\nor at least doesn't need to -- use HTTP::AnyUA directly, they will benefit from client choice if\ntheir third-party modules use HTTP::AnyUA or something like it.\n\nThe primary goal of HTTP::AnyUA is to make it easy for module developers to write HTTP code once\nthat can work with any HTTP client the end user may decide to plug in. A secondary goal is to make\nit easy for anyone to add support for new or yet-unsupported user agents.\n\n# ATTRIBUTES\n\n## ua\n\nGet the user agent that was passed to [\"new\"](#new).\n\n## response\\_is\\_future\n\nGet and set whether or not responses are [Future](https://metacpan.org/pod/Future) objects.\n\n## backend\n\nGet the backend instance. You normally shouldn't need this.\n\n# METHODS\n\n## new\n\n    $any_ua = HTTP::AnyUA-\u003enew(ua =\u003e $user_agent, %attr);\n    $any_ua = HTTP::AnyUA-\u003enew($user_agent, %attr);\n\nConstruct a new HTTP::AnyUA.\n\n## request\n\n    $response = $any_ua-\u003erequest($method, $url);\n    $response = $any_ua-\u003erequest($method, $url, \\%options);\n\nMake a [request](#the-request), get a [response](#the-response).\n\nCompare to [\"request\" in HTTP::Tiny](https://metacpan.org/pod/HTTP::Tiny#request).\n\n## get, head, put, post, delete\n\n    $response = $any_ua-\u003eget($url);\n    $response = $any_ua-\u003eget($url, \\%options);\n    $response = $any_ua-\u003ehead($url);\n    $response = $any_ua-\u003ehead($url, \\%options);\n    # etc.\n\nShortcuts for [\"request\"](#request) where the method is the method name rather than the first argument.\n\nCompare to [\"get|head|put|post|delete\" in HTTP::Tiny](https://metacpan.org/pod/HTTP::Tiny#get-head-put-post-delete).\n\n## post\\_form\n\n    $response = $any_ua-\u003epost_form($url, $formdata);\n    $response = $any_ua-\u003epost_form($url, $formdata, \\%options);\n\nDoes a `POST` request with the form data encoded and sets the `Content-Type` header to\n`application/x-www-form-urlencoded`.\n\nCompare to [\"post\\_form\" in HTTP::Tiny](https://metacpan.org/pod/HTTP::Tiny#post_form).\n\n## mirror\n\n    $response = $http-\u003emirror($url, $filepath, \\%options);\n    if ($response-\u003e{success}) {\n        print \"$filepath is up to date\\n\";\n    }\n\nDoes a `GET` request and saves the downloaded document to a file. If the file already exists, its\ntimestamp will be sent using the `If-Modified-Since` request header (which you can override). If\nthe server responds with a `304` (Not Modified) status, the `success` field will be true; this is\nusually only the case for `2XX` statuses. If the server responds with a `Last-Modified` header,\nthe file will be updated to have the same modification timestamp.\n\nCompare to [\"mirror\" in HTTP::Tiny](https://metacpan.org/pod/HTTP::Tiny#mirror). This version differs slightly in that this returns internal\nexception responses (for cases like being unable to write the file locally, etc.) rather than\nactually throwing the exceptions. The reason for this is that exceptions as responses are easier to\ndeal with for non-blocking HTTP clients, and the fact that this method throws exceptions in\n[HTTP::Tiny](https://metacpan.org/pod/HTTP::Tiny) seems like an inconsistency in its interface.\n\n## apply\\_middleware\n\n    $any_ua-\u003eapply_middleware($middleware_package);\n    $any_ua-\u003eapply_middleware($middleware_package, %args);\n    $any_ua-\u003eapply_middleware($middleware_obj);\n\nWrap the backend with some new middleware. Middleware packages are relative to the\n`HTTP::AnyUA::Middleware::` namespace unless prefixed with a `+`.\n\nThis effectively replaces the [\"backend\"](#backend) with a new object that wraps the previous backend.\n\nThis can be used multiple times to add multiple layers of middleware, and order matters. The last\nmiddleware applied is the first one to see the request and last one to get the response. For\nexample, if you apply middleware that does logging and middleware that does caching (and\nshort-circuits on a cache hit), applying your logging middleware _first_ will cause only cache\nmisses to be logged whereas applying your cache middleware first will allow all requests to be\nlogged.\n\nSee [HTTP::AnyUA::Middleware](https://metacpan.org/pod/HTTP::AnyUA::Middleware) for more information about what middleware is and how to write your\nown middleware.\n\n## register\\_backend\n\n    HTTP::AnyUA-\u003eregister_backend($user_agent_package =\u003e $backend_package);\n    HTTP::AnyUA-\u003eregister_backend('MyAgent' =\u003e 'MyBackend');    # HTTP::AnyUA::Backend::MyBackend\n    HTTP::AnyUA-\u003eregister_backend('LWP::UserAgent' =\u003e '+SpecialBackend');   # SpecialBackend\n\nRegister a backend for a new user agent type or override a default backend. Backend packages are\nrelative to the `HTTP::AnyUA::Backend::` namespace unless prefixed with a `+`.\n\nIf you only need to set a backend as a one-off thing, you could also pass an instantiated backend to\n[\"new\"](#new).\n\n# SPECIFICATION\n\nThis section specifies a standard set of data structures that can be used to make a request and get\na response from a user agent. This is the specification HTTP::AnyUA uses for its programming\ninterface. It is heavily based on [HTTP::Tiny](https://metacpan.org/pod/HTTP::Tiny)'s interface, and parts of this specification were\nadapted or copied verbatim from that module's documentation. The intent is for this specification to\nbe written such that [HTTP::Tiny](https://metacpan.org/pod/HTTP::Tiny) is already a compliant implementor of the specification (at least\nas of the specification's publication date).\n\n## The Request\n\nA request is a tuple of the form `(Method, URL)` or `(Method, URL, Options)`.\n\n### Method\n\nMethod **MUST** be a string representing the HTTP verb. This is commonly `\"GET\"`, `\"POST\"`,\n`\"HEAD\"`, `\"DELETE\"`, etc.\n\n### URL\n\nURL **MUST** be a string representing the remote resource to be acted upon. The URL **MUST** have\nunsafe characters escaped and international domain names encoded before being passed to the user\nagent. A user agent **MUST** generate a `\"Host\"` header based on the URL in accordance with RFC\n2616; a user agent **MAY** throw an error if a `\"Host\"` header is given with the [\"headers\"](#headers).\n\n### Options\n\nOptions, if present, **MUST** be a hash reference containing zero or more of the following keys with\nappropriate values. A user agent **MAY** support more options than are specified here.\n\n#### headers\n\nThe value for the `headers` key **MUST** be a hash reference containing zero or more HTTP header\nnames (as keys) and header values. The value for a header **MUST** be either a string containing the\nheader value OR an array reference where each item is a string. If the value for a header is an\narray reference, the user agent **MUST** output the header multiple times with each value in the\narray.\n\nUser agents **MAY** may add headers, but **SHOULD NOT** replace user-specified headers unless\notherwise documented.\n\n#### content\n\nThe value for the `content` key **MUST** be a string OR a code reference. If the value is a string,\nits contents will be included with the request as the body. If the value is a code reference, the\nreferenced code will be called iteratively to produce the body of the request, and the code **MUST**\nreturn an empty string or undef value to indicate the end of the request body. If the value is\na code reference, a user agent **SHOULD** use chunked transfer encoding if it supports it, otherwise\na user agent **MAY** completely drain the code of content before sending the request.\n\n#### data\\_callback\n\nThe value for the `data_callback` key **MUST** be a code reference that will be called zero or more\ntimes, once for each \"chunk\" of response body received. A user agent **MAY** send the entire response\nbody in one call. The referenced code **MUST** be given two arguments; the first is a string\ncontaining a chunk of the response body, the second is an in-progress [response](#the-response).\n\n## The Response\n\nA response **MUST** be a hash reference containg some required keys and values. A response **MAY**\ncontain some optional keys and values.\n\n### success\n\nA response **MUST** include a `success` key, the value of which is a boolean indicating whether or\nnot the request is to be considered a success (true is a success). Unless otherwise documented,\na successful result means that the operation returned a 2XX status code.\n\n### url\n\nA response **MUST** include a `url` key, the value of which is the URL that provided the response.\nThis is the URL used in the request unless there were redirections, in which case it is the last URL\nqueried in a redirection chain.\n\n### status\n\nA response **MUST** include a `status` key, the value of which is the HTTP status code of the\nresponse. If an internal exception occurs (e.g. connection error), then the status code **MUST** be\n`599`.\n\n### reason\n\nA response **MUST** include a `reason` key, the value of which is the response phrase returned by\nthe server OR \"Internal Exception\" if an internal exception occurred.\n\n### content\n\nA response **MAY** include a `content` key, the value of which is the response body returned by the\nserver OR the text of the exception if an internal exception occurred. This field **MUST** be missing\nor empty if the server provided no response OR if the body was already provided via\n[\"data\\_callback\"](#data_callback).\n\n### headers\n\nA response **SHOULD** include a `headers` key, the value of which is a hash reference containing\nzero or more HTTP header names (as keys) and header values. Keys **MUST** be lowercased. The value\nfor a header **MUST** be either a string containing the header value OR an array reference where each\nitem is the value of one of the repeated headers.\n\n### redirects\n\nA response **MAY** include a `redirects` key, the value of which is an array reference of one or\nmore responses from redirections that occurred to fulfill the current request, in chronological\norder.\n\n# FREQUENTLY ASKED QUESTIONS\n\n## How do I set up proxying, SSL, cookies, timeout, etc.?\n\nHTTP::AnyUA provides a common interface for _using_ HTTP clients, not for instantiating or\nconfiguring them. Proxying, SSL, and other custom settings can be configured directly through the\nunderlying HTTP client; see the documentation for your particular user agent to learn how to\nconfigure these things.\n\n[AnyEvent::HTTP](https://metacpan.org/pod/AnyEvent::HTTP) is a bit of a special case because there is no instantiated object representing\nthe client. For this particular user agent, you can configure the backend to pass a default set of\noptions whenever it calls `http_request`. See [\"options\" in HTTP::AnyUA::Backend::AnyEvent::HTTP](https://metacpan.org/pod/HTTP::AnyUA::Backend::AnyEvent::HTTP#options):\n\n    $any_ua-\u003ebackend-\u003eoptions({recurse =\u003e 5, timeout =\u003e 15});\n\nIf you are a module writer, you should probably receive a user agent from your end user and leave\nthis type of configuration up to them.\n\n## Why use HTTP::AnyUA instead of some other HTTP client?\n\nMaybe you shouldn't. If you're an end user writing a script or application, you can just pick the\nHTTP client that suits you best and use it. For example, if you're writing a [Mojolicious](https://metacpan.org/pod/Mojolicious) app,\nyou're not going wrong by using [Mojo::UserAgent](https://metacpan.org/pod/Mojo::UserAgent); it's loaded with features and is well-integrated\nwith that particular environment.\n\nAs an end user, you _could_ wrap the HTTP client you pick in an HTTP::AnyUA object, but the only\nreason to do this is if you prefer using the [HTTP::Tiny](https://metacpan.org/pod/HTTP::Tiny) interface.\n\nThe real benefit of HTTP::AnyUA (or something like it) is if module writers use it to allow end\nusers of their modules to be able to plug in whatever HTTP client they want. For example, a module\nthat implements an API wrapper that has a hard dependency on [LWP::UserAgent](https://metacpan.org/pod/LWP::UserAgent) or even [HTTP::Tiny](https://metacpan.org/pod/HTTP::Tiny)\nis essentially useless for non-blocking applications. If the same hypothetical module had been\nwritten using HTTP::AnyUA then it would be useful in any scenario.\n\n## Why use the HTTP::Tiny interface?\n\nThe [HTTP::Tiny](https://metacpan.org/pod/HTTP::Tiny) interface is simple but provides all the essential functionality needed for\na capable HTTP client and little more. That makes it easy to provide an implementation for, and it\nalso makes it straightforward for module authors to use.\n\nMarrying the [HTTP::Tiny](https://metacpan.org/pod/HTTP::Tiny) interface with [Future](https://metacpan.org/pod/Future) gives us these benefits for both blocking and\nnon-blocking modules and applications.\n\n# SUPPORTED USER AGENTS\n\n- [AnyEvent::HTTP](https://metacpan.org/pod/AnyEvent::HTTP)\n- [Furl](https://metacpan.org/pod/Furl)\n- [HTTP::AnyUA](https://metacpan.org/pod/HTTP::AnyUA) - a little bit meta, but why not?\n- [HTTP::Tiny](https://metacpan.org/pod/HTTP::Tiny)\n- [LWP::UserAgent](https://metacpan.org/pod/LWP::UserAgent)\n- [Mojo::UserAgent](https://metacpan.org/pod/Mojo::UserAgent)\n- [Net::Curl::Easy](https://metacpan.org/pod/Net::Curl::Easy)\n\nAny HTTP client that inherits from one of these in a well-behaved manner should also be supported.\n\nOf course, there are many other HTTP clients on CPAN that HTTP::AnyUA doesn't yet support. I'm more\nthan happy to help add support for others, so send me a message if you know of an HTTP client that\nneeds support. See [HTTP::AnyUA::Backend](https://metacpan.org/pod/HTTP::AnyUA::Backend) for how to write support for a new HTTP client.\n\n# NON-BLOCKING USER AGENTS\n\nHTTP::AnyUA tries to target the [HTTP::Tiny](https://metacpan.org/pod/HTTP::Tiny) interface, which is a blocking interface. This means\nthat when you call [\"request\"](#request), it is supposed to not return until either the response is received\nor an error occurs. This doesn't jive well with non-blocking HTTP clients which expect the flow to\nreenter an event loop so that the request can complete concurrently.\n\nIn order to reconcile this, a [Future](https://metacpan.org/pod/Future) will be returned instead of the normal hashref response if\nthe wrapped HTTP client is non-blocking (such as [Mojo::UserAgent](https://metacpan.org/pod/Mojo::UserAgent) or [AnyEvent::HTTP](https://metacpan.org/pod/AnyEvent::HTTP)). This\n[Future](https://metacpan.org/pod/Future) object may be used to set up callbacks that will be called when the request is completed.\nYou can call [\"response\\_is\\_future\"](#response_is_future) to know if the response is or will be a [Future](https://metacpan.org/pod/Future).\n\nThis is typically okay for the end user; since they're the one who chose which HTTP client to use in\nthe first place, they should know whether they should expect a [Future](https://metacpan.org/pod/Future) or a direct response when\nthey make an HTTP request, but it does add some burden on you as a module writer because if you ever\nneed to examine the response, you may need to write code like this:\n\n    my $resp = $any_ua-\u003eget('http://www.perl.org/');\n\n    if ($any_ua-\u003eresponse_is_future) {\n        $resp-\u003eon_done(sub {\n            my $real_resp = shift;\n            handle_response($real_resp);\n        });\n    }\n    else {\n        handle_response($resp);     # response is the real response already\n    }\n\nThis actually isn't too annoying to deal with in practice, but you can avoid it if you like by\nforcing the response to always be a [Future](https://metacpan.org/pod/Future). Just set the [\"response\\_is\\_future\"](#response_is_future) attribute. Then\nyou don't need to do an if-else because the response will always be the same type:\n\n    $any_ua-\u003eresponse_is_future(1);\n\n    my $resp = $any_ua-\u003eget('http://www.perl.org/');\n\n    $resp-\u003eon_done(sub {            # response is always a Future\n        my $real_resp = shift;\n        handle_response($real_resp);\n    });\n\nNote that this doesn't make a blocking HTTP client magically non-blocking. The call to [\"request\"](#request)\nwill still block if the client is blocking, and your \"done\" callback will simply be fired\nimmediately. But this does let you write the same code in your module and have it work regardless of\nwhether the underlying HTTP client is blocking or non-blocking.\n\nThe default behavior is to return a direct hashref response if the HTTP client is blocking and\na [Future](https://metacpan.org/pod/Future) if the client is non-blocking. It's up to you to decide whether or not to set\n`response_is_future`, and you should also consider whether you want to expose the possibility of\neither type of response or always returning [Future](https://metacpan.org/pod/Future) objects to the end user of your module. It\ndoesn't matter for users who choose non-blocking HTTP clients because they will be using [Future](https://metacpan.org/pod/Future)\nobjects either way, but users who know they are using a blocking HTTP client may appreciate not\nhaving to deal with [Future](https://metacpan.org/pod/Future) objects at all.\n\n# ENVIRONMENT\n\n- `PERL_HTTP_ANYUA_DEBUG` - If 1, print some info useful for debugging to `STDERR`.\n\n# CAVEATS\n\nNot all HTTP clients implement the same features or in the same ways. While the point of HTTP::AnyUA\nis to hide those differences, you may notice some (hopefully) _insignificant_ differences when\nplugging in different clients. For example, [LWP::UserAgent](https://metacpan.org/pod/LWP::UserAgent) sets some headers on the response such\nas `client-date` and `client-peer` that won't appear when using other clients. Little differences\nlike these probably aren't a big deal. Other differences may be a bigger deal, depending on what's\nimportant to you. For example, some clients (like [HTTP::Tiny](https://metacpan.org/pod/HTTP::Tiny)) may do chunked transfer encoding in\nsituations where other clients won't (probably because they don't support it). It's not a goal of\nthis project to eliminate _all_ of the differences, but if you come across a difference that is\nsignificant enough that you think you need to detect the user agent and write special logic, I would\nlike to learn about your use case.\n\n# SEE ALSO\n\nThese modules share similar goals or provide overlapping functionality:\n\n- [Future::HTTP](https://metacpan.org/pod/Future::HTTP)\n- [HTTP::Any](https://metacpan.org/pod/HTTP::Any)\n- [HTTP::Tinyish](https://metacpan.org/pod/HTTP::Tinyish)\n- [Plient](https://metacpan.org/pod/Plient)\n\n# BUGS\n\nPlease report any bugs or feature requests on the bugtracker website\n[https://github.com/chazmcgarvey/HTTP-AnyUA/issues](https://github.com/chazmcgarvey/HTTP-AnyUA/issues)\n\nWhen submitting a bug or request, please include a test-file or a\npatch to an existing test-file that illustrates the bug or desired\nfeature.\n\n# AUTHOR\n\nCharles McGarvey \u003cchazmcgarvey@brokenzipper.com\u003e\n\n# COPYRIGHT AND LICENSE\n\nThis software is copyright (c) 2019 by Charles McGarvey.\n\nThis is free software; you can redistribute it and/or modify it under\nthe same terms as the Perl 5 programming language system itself.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchazmcgarvey%2Fhttp-anyua","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fchazmcgarvey%2Fhttp-anyua","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchazmcgarvey%2Fhttp-anyua/lists"}