{"id":19719937,"url":"https://github.com/timboudreau/netty-http-client","last_synced_at":"2025-04-04T20:10:24.409Z","repository":{"id":38310958,"uuid":"9882334","full_name":"timboudreau/netty-http-client","owner":"timboudreau","description":"An asynchronous http client in Java, with a clean, callback-based API, using Netty 4.x","archived":false,"fork":false,"pushed_at":"2023-07-19T18:52:04.000Z","size":588,"stargazers_count":321,"open_issues_count":7,"forks_count":119,"subscribers_count":34,"default_branch":"master","last_synced_at":"2025-03-28T19:07:35.986Z","etag":null,"topics":["asynchronous","http","http-client","https","https-client","java","test-harness"],"latest_commit_sha":null,"homepage":null,"language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/timboudreau.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2013-05-06T08:14:17.000Z","updated_at":"2025-03-24T07:55:01.000Z","dependencies_parsed_at":"2025-01-18T21:43:44.173Z","dependency_job_id":"cc7ea2b2-d0d7-4a04-8ec4-080363a62e1b","html_url":"https://github.com/timboudreau/netty-http-client","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/timboudreau%2Fnetty-http-client","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/timboudreau%2Fnetty-http-client/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/timboudreau%2Fnetty-http-client/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/timboudreau%2Fnetty-http-client/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/timboudreau","download_url":"https://codeload.github.com/timboudreau/netty-http-client/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247242678,"owners_count":20907134,"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":["asynchronous","http","http-client","https","https-client","java","test-harness"],"created_at":"2024-11-11T23:09:46.086Z","updated_at":"2025-04-04T20:10:24.392Z","avatar_url":"https://github.com/timboudreau.png","language":"Java","funding_links":[],"categories":["网络编程"],"sub_categories":[],"readme":"Netty HTTP Client\n=================\n\n_This project is fairly obsolete - there is a good HTTP client in the JDK now.  Use it.  This\nproject will be maintained for a while yet, due to use of the adjacent HTTP test-harness that uses this\nlibrary under the hood._\n\n-------------------------------------------------------------------------\n\nAn asynchronous http client in Java, with a clean, callback-based API, using Netty 4.x.\n\nThe API is inspired a bit by [Node.js](http://nodejs.org)\n``http`` module; it is designed to (mostly) avoid the \nFuture pattern, and do its business via callbacks.  Wherever possible we avoid\nintroducing complicated abstractions that try to hide the business of HTTP\ncommunication;  rather your code can be involved in as much or as little\nof that as necessary.\n\nYou can use it from Maven projects [as described here](http://timboudreau.com/builds/).\n\nFeatures\n--------\n\n * HTTP and HTTPS\n * Simple support for Basic authentication\n * Optional support for HTTP cookies\n * Easy, typed API for setting headers\n * Fluent builder API for assembling requests\n * Non-blocking, asynchronous\n * Small, low-surface-area API\n * Pluggable support for SSLContext/TrustManagers for HTTPS\n\nRead the [javadoc](http://timboudreau.com/builds/job/mastfrog-parent/lastSuccessfulBuild/artifact/netty-http-client/netty-http-client/target/apidocs/index.html).  The header writing/parsing classes come from [acteur-util](http://timboudreau.com/builds/job/mastfrog-parent/lastSuccessfulBuild/artifact/acteur-modules/acteur-parent/acteur-util/target/apidocs/index.html); some URL-related classes are [documented here](http://timboudreau.com/builds/job/mastfrog-parent/lastSuccessfulBuild/artifact/acteur-modules/acteur-parent/url/target/apidocs/index.html).\n\nTo use with Maven, add the Maven repo to your project as [described here](http://timboudreau.com/builds/).  Then add groupId ``com.mastfrog`` artifactId ``netty-http-client`` to your POM file.\n\nThis project also comes with a test harness which is easy to integrate with unit tests, with built-in methods to assert that the response is what you expect, e.g.\n\n```java\n        harness.get(\"static/hello.txt\").setTimeout(Duration.seconds(1)).go()\n                .assertHasContent()\n                .assertStatus(OK)\n                .assertHasHeader(Headers.LAST_MODIFIED.name())\n                .assertHasHeader(Headers.ETAG.name())\n                .assertContent(\"hello world\")\n                .getHeader(Headers.LAST_MODIFIED);\n```\n\nSee the bottom of this document for test harness documentation.\n\nUsage\n-----\n\nThe first thing you need is an ``HttpClient``:\n\n```java\n\tHttpClient client = HttpClient.builder().followRedirects().build();\n```\n\nThe API is callback-based.  While you can block the current thread until a response is received using `ResponseFuture.await()`,\nthe entire *point* of an async I/O is defeated if you do that.  Asynchronous programming means learning to love callbacks.\n\nThere are two ways to pay attention to the results of an HTTP call - you can listen\nfor \u003ccode\u003eState\u003c/code\u003e objects which exist for every state transition in the process\nof making a request and handling the response;  or you can provide a simpler callback which\nwill be called with the response once it arrives.  This looks like\n\n\n```java\n\tResponseFuture h = client\n\t\t.get().setURL ( \"http://localhost:9333/foo/bar\" ))\n\t\t.execute ( new ResponseHandler \u003cString\u003e ( String.class ) {\n\n            protected void receive ( HttpResponseStatus status, HttpHeaders headers, String response ) {\n                System.out.println ( \"Here's the response: '\" + response + \"'\" );\n            }\n        });\n```\n\n(ResponseHandler has additional methods you can override to detect error responses, timeouts or refused connections)\n\nYou'll note the ``ResponseHandler`` callback is parameterized on String - you can get your content as a\nstring, byte array, InputStream or Netty ByteBuf.  You can also pass other types;  Jackson is used to\ndeserialize JSON, and is the default for unknown types (this may fail if Jackson does not know how to\nserialize it).\n\n\u003ch4\u003eThe Details\u003c/h4\u003e\n\nYou can get all the details\nby providing a ``Receiver\u003cState\u003c?\u003e\u003e`` when you build a request;  there are states for\nthings like Connecting, HeadersReceived;  you can even capture every chunk of chunked\nencoding individually if you want.  \n\n```java\n        ResponseFuture f = client.get()\n                .setURL( \"http://localhost:9333/foo/bar\" )\n                .setBody( \"This is a test\", MediaType.PLAIN_TEXT_UTF_8)\n                .onEvent( new Receiver\u003cState\u003c?\u003e\u003e() {\n\n            public void receive( State\u003c?\u003e state ) {\n                System.out.println( \"STATE \" + state + \" \" + state.name() + \" \" + state.get() );\n                if ( state.stateType() == StateType.Finished ) {\n                    DefaultFullHttpResponse d = (DefaultFullHttpResponse) state.get();\n\t\t    //do something\n                }\n            }\n\n        }).execute();\n```\n\n\nStatus \u0026 To-Dos\n---------------\n\nThis is a young library;  it works, but it will surely need some polish yet;  and Netty 4.x is still\nchanging, including occasional incompatible changes.  Here are some things that would be useful to add:\n\n * Caching on disk or in memory with proper use of ``If-Modified-Since`` and ``If-None-Match`` headers\n * Zero copy file streaming using Netty's FileRegion\n * Better tests (actually start a local server, etc)\n\nLicense\n-------\n\nMIT license - do what thou wilt, give credit where it's due\n\nTest Harness (netty-http-test-harness)\n======================================\n\nAlongside this project is the ``netty-http-test-harness`` project.  It provides\na fluent interface for writing tests of an HTTP server.  The server can be anything - \nthe ``Server`` interface has no particular dependencies (but is implemented in\n[Acteur](http://github.com/timboudreau/acteur) if you're using that) - it just has\nstart/stop methods and a port property.\n\nThe point is to make it very little code or setup to test something.\n\nBasically you construct a ``TestHarness`` instance - passing it a ``Server``, a\n``URL`` for the base URL and a ``ShutdownHookRegistry`` (another simple interface,\nfrom [Giulius](http://github.com/timboudreau/giulius).  Or to do it the easy way, \nand use ``TestHarness.Module`` and [Giulius-Tests](https://github.com/timboudreau/giulius-tests)\nas in [this example](https://github.com/timboudreau/acteur/blob/master/acteur-resources/src/test/java/com/mastfrog/acteur/resources/StaticResourcesTest.java).\n\nHere's an example:\n\n```java\n        DateTime helloLastModified = har.get(\"static/hello.txt\").go()\n                .assertHasContent()\n                .assertStatus(OK)\n                .assertHasHeader(Headers.LAST_MODIFIED.name())\n                .assertHasHeader(Headers.ETAG.name())\n                .assertContent(HELLO_CONTENT)\n                .getHeader(Headers.LAST_MODIFIED);\n\n        DateTime aLastModified = har.get(\"static/another.txt\").go()\n                .assertStatus(OK)\n                .assertHasContent()\n                .assertHasHeader(Headers.LAST_MODIFIED.name())\n                .assertHasHeader(Headers.ETAG.name())\n                .assertContent(\"This is another file.  It has some data in it.\\n\")\n                .getHeader(Headers.LAST_MODIFIED);\n\n        assertNotNull(helloLastModified);\n        assertNotNull(aLastModified);\n\n        har.get(\"static/hello.txt\")\n                .addHeader(Headers.IF_MODIFIED_SINCE, helloLastModified)\n                .go()\n                .assertStatus(NOT_MODIFIED);\n\n        har.get(\"static/another.txt\")\n                .addHeader(Headers.IF_MODIFIED_SINCE, aLastModified)\n                .go().assertStatus(NOT_MODIFIED);\n```\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftimboudreau%2Fnetty-http-client","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftimboudreau%2Fnetty-http-client","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftimboudreau%2Fnetty-http-client/lists"}