{"id":26866421,"url":"https://github.com/jauntsdn/netty-websocket-http2","last_synced_at":"2025-05-07T01:23:32.015Z","repository":{"id":50244052,"uuid":"261869229","full_name":"jauntsdn/netty-websocket-http2","owner":"jauntsdn","description":"java/netty implementation of rfc8441 - bootstrapping websockets with http/2, and websocket multiprotocol support(http1, http2).","archived":false,"fork":false,"pushed_at":"2025-03-29T08:49:38.000Z","size":734,"stargazers_count":15,"open_issues_count":0,"forks_count":4,"subscribers_count":1,"default_branch":"develop","last_synced_at":"2025-03-29T09:29:22.511Z","etag":null,"topics":["java","netty","rfc8441","websocket","websocket-over-http2"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jauntsdn.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"license.md","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":"2020-05-06T20:18:00.000Z","updated_at":"2025-03-29T08:49:42.000Z","dependencies_parsed_at":"2023-12-23T18:22:23.277Z","dependency_job_id":"4dd41549-29f9-47c8-860e-b607597b79d3","html_url":"https://github.com/jauntsdn/netty-websocket-http2","commit_stats":null,"previous_names":[],"tags_count":23,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jauntsdn%2Fnetty-websocket-http2","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jauntsdn%2Fnetty-websocket-http2/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jauntsdn%2Fnetty-websocket-http2/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jauntsdn%2Fnetty-websocket-http2/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jauntsdn","download_url":"https://codeload.github.com/jauntsdn/netty-websocket-http2/tar.gz/refs/heads/develop","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252794364,"owners_count":21805188,"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":["java","netty","rfc8441","websocket","websocket-over-http2"],"created_at":"2025-03-31T04:54:35.951Z","updated_at":"2025-05-07T01:23:31.967Z","avatar_url":"https://github.com/jauntsdn.png","language":"Java","funding_links":[],"categories":["网络编程"],"sub_categories":["Spring Cloud框架"],"readme":"![Maven Central](https://img.shields.io/maven-central/v/com.jauntsdn.netty/netty-websocket-http2)\n[![Build](https://github.com/jauntsdn/netty-websocket-http2/actions/workflows/ci-build.yml/badge.svg)](https://github.com/jauntsdn/netty-websocket-http2/actions/workflows/ci-build.yml)\n# netty-websocket-http2\n\nNetty based implementation of [rfc8441](https://tools.ietf.org/html/rfc8441) - bootstrapping websockets with http/2, and multiprotocol support (websocket-over-http1, websocket-over-http2).\n\n### use cases\n\n* Websocket channel API: for application servers and clients, It provides transparent use of existing http1 websocket handlers on top of http2 streams. Compatible with\ncallbacks codec (described below).\n \n* Websocket handshake only API: for gateways/proxies, It provides websockets-over-http2 support with no http1 dependencies and minimal overhead.\n\n* Websocket multiprotocol: for application servers, It provides transparent use of existing http1 websocket handlers to process both http1 and http2 websockets. \nCompatible with callbacks codec (described below).\n\n[https://jauntsdn.com/post/netty-websocket-http2/](https://jauntsdn.github.io/post/netty-websocket-http2/)\n\n### much faster http1 codec\nIntegration with [jauntsdn/netty-websocket-http1](https://github.com/jauntsdn/netty-websocket-http1) codec (callbacks codec) for websocket-http1 \nframes processing [improves](https://github.com/jauntsdn/netty-websocket-http2/tree/develop/netty-websocket-http2-perftest/src/main/java/com/jauntsdn/netty/handler/codec/http2/websocketx/perftest/callbackscodec) \nthroughput 1.4x - 1.7x for small messages compared to one provided by netty (default codec).\n\n### websocket channel API  \nIntended for application servers and clients.  \nAllows transparent application of existing http1 websocket handlers on top of http2 stream.  \n\n* Server\n```groovy\nEchoWebSocketHandler http1WebSocketHandler = new EchoWebSocketHandler();\n\n Http2WebSocketServerHandler http2webSocketHandler =\n       Http2WebSocketServerBuilder.create()\n              .codec(Http1WebSocketCodec.DEFAULT)\n              .acceptor(\n                   (ctx, path, subprotocols, request, response) -\u003e {\n                     switch (path) {\n                       case \"/echo\":\n                         if (subprotocols.contains(\"echo.jauntsdn.com\")\n                             \u0026\u0026 acceptUserAgent(request, response)) {\n                           /*selecting subprotocol for accepted requests is mandatory*/\n                           Http2WebSocketAcceptor.Subprotocol\n                                  .accept(\"echo.jauntsdn.com\", response);\n                           return ctx.executor()\n                                  .newSucceededFuture(http1WebSocketHandler);\n                         }\n                         break;\n                       case \"/echo_all\":\n                         if (subprotocols.isEmpty() \n                                  \u0026\u0026 acceptUserAgent(request, response)) {\n                           return ctx.executor()\n                                  .newSucceededFuture(http1WebSocketHandler);\n                         }\n                         break;\n                     }\n                     return ctx.executor()\n                         .newFailedFuture(\n                             new WebSocketHandshakeException(\n                                  \"websocket rejected, path: \" + path));\n                   })\n              .build();\n\n      ch.pipeline()\n           .addLast(sslHandler, \n                    http2frameCodec, \n                    http2webSocketHandler);\n```  \n\n* Client\n```groovy\n Channel channel =\n        new Bootstrap()\n            .handler(\n                new ChannelInitializer\u003cSocketChannel\u003e() {\n                  @Override\n                  protected void initChannel(SocketChannel ch) {\n\n                    Http2WebSocketClientHandler http2WebSocketClientHandler =\n                        Http2WebSocketClientBuilder.create()\n                            .codec(Http1WebSocketCodec.DEFAULT)\n                            .handshakeTimeoutMillis(15_000)\n                            .build();\n\n                    ch.pipeline()\n                        .addLast(\n                            sslHandler,\n                            http2FrameCodec,\n                            http2WebSocketClientHandler);\n                  }\n                })\n            .connect(address)\n            .sync()\n            .channel();\n\nHttp2WebSocketClientHandshaker handShaker = Http2WebSocketClientHandshaker.create(channel);\n\nHttp2Headers headers =\n   new DefaultHttp2Headers().set(\"user-agent\", \"jauntsdn-websocket-http2-client/1.4.0\");\nChannelFuture handshakeFuture =\n   /*http1 websocket handler*/\n   handShaker.handshake(\"/echo\", headers, new EchoWebSocketHandler());\n    \nhandshakeFuture.channel().writeAndFlush(new TextWebSocketFrame(\"hello http2 websocket\"));\n```\nSuccessfully handshaked http2 stream spawns websocket subchannel, with provided  \nhttp1 websocket handlers on its pipeline.\n\nRunnable demo is available in `netty-websocket-http2-example` module - \n[channelserver](https://github.com/jauntsdn/netty-websocket-http2/blob/develop/netty-websocket-http2-example/src/main/java/com/jauntsdn/netty/handler/codec/http2/websocketx/example/channelserver/Main.java), \n[channelclient](https://github.com/jauntsdn/netty-websocket-http2/blob/develop/netty-websocket-http2-example/src/main/java/com/jauntsdn/netty/handler/codec/http2/websocketx/example/channelclient/Main.java).\n\n### websocket handshake only API\nIntended for intermediaries/proxies.   \nOnly verifies whether http2 stream is valid websocket, then passes it down the pipeline as `POST` request with `x-protocol=websocket` header.\n \n```groovy\n      Http2WebSocketServerHandler http2webSocketHandler =\n          Http2WebSocketServerBuilder.buildHandshakeOnly();\n\n      Http2StreamsHandler http2StreamsHandler = new Http2StreamsHandler();\n      ch.pipeline()\n           .addLast(sslHandler, \n                    frameCodec, \n                    http2webSocketHandler, \n                    http2StreamsHandler);\n``` \n\nWorks with both callbacks-style `Http2ConnectionHandler` and frames based `Http2FrameCodec`.      \n\n```\nHttp2WebSocketServerBuilder.buildHandshakeOnly();\n```\n \nRunnable demo is available in `netty-websocket-http2-example` module - \n[handshakeserver](https://github.com/jauntsdn/netty-websocket-http2/blob/develop/netty-websocket-http2-example/src/main/java/com/jauntsdn/netty/handler/codec/http2/websocketx/example/handshakeserver/Main.java), \n[channelclient](https://github.com/jauntsdn/netty-websocket-http2/blob/develop/netty-websocket-http2-example/src/main/java/com/jauntsdn/netty/handler/codec/http2/websocketx/example/channelclient/Main.java).\n\n### websocket multiprotocol\nProvides transparent use of existing http1 websocket handlers to process both http1 and http2 websockets. \n\n* Server\n```groovy\n       MultiProtocolWebSocketServerHandler multiprotocolHandler =\n           MultiprotocolWebSocketServerBuilder.create()\n               .path(\"/echo\")\n               .subprotocols(\"echo.jauntsdn.com\")\n               .defaultCodec()\n               .handler(new DefaultEchoWebSocketHandler())\n               .build();\n       ch.pipeline().addLast(sslHandler, multiprotocolHandler);\n```\n\nRunnable demo is available in `netty-websocket-http2-example` module - \n[multiprotocol.server.defaultcodec](https://github.com/jauntsdn/netty-websocket-http2/blob/develop/netty-websocket-http2-example/src/main/java/com/jauntsdn/netty/handler/codec/http2/websocketx/example/multiprotocol/server/defaultcodec/Main.java), \n[multiprotocol.server.callbackscodec](https://github.com/jauntsdn/netty-websocket-http2/blob/develop/netty-websocket-http2-example/src/main/java/com/jauntsdn/netty/handler/codec/http2/websocketx/example/multiprotocol/server/callbackscodec/Main.java), \n[multiprotocol.client.defaultcodec](https://github.com/jauntsdn/netty-websocket-http2/blob/develop/netty-websocket-http2-example/src/main/java/com/jauntsdn/netty/handler/codec/http2/websocketx/example/multiprotocol/client/Main.java), \n\n### configuration\nInitial settings of server http2 codecs (`Http2ConnectionHandler` or `Http2FrameCodec`)  should contain [SETTINGS_ENABLE_CONNECT_PROTOCOL=1](https://tools.ietf.org/html/rfc8441#section-9.1)\nparameter to advertise websocket-over-http2 support.\n\nAlso server http2 codecs must disable built-in headers validation because It is not compatible\nwith rfc8441 due to newly introduced `:protocol` pseudo-header. All websocket handlers provided by this library\ndo headers validation on their own - both for websocket and non-websocket requests.\n\nAbove configuration may be done with utility methods of `Http2WebSocketServerBuilder`\n\n```\npublic static Http2FrameCodecBuilder configureHttp2Server(\n                                          Http2FrameCodecBuilder http2Builder);\n\npublic static Http2ConnectionHandlerBuilder configureHttp2Server(\n                                          Http2ConnectionHandlerBuilder http2Builder)\n```    \n\n### compression \u0026 subprotocols\nClient and server `permessage-deflate` compression configuration is shared by all streams\n```groovy\nHttp2WebSocketServerBuilder.compression(enabled);\n```\nor\n```groovy\nHttp2WebSocketServerBuilder.compression(\n      compressionLevel,\n      allowServerWindowSize,\n      preferredClientWindowSize,\n      allowServerNoContext,\n      preferredClientNoContext);\n``` \nClient subprotocols are configured on per-path basis\n```groovy\nEchoWebSocketHandler http1WebsocketHandler = new EchoWebSocketHandler();\nChannelFuture handshake =\n        handShaker.handshake(\"/echo\", \"subprotocol\", headers, http1WebsocketHandler);\n``` \nOn a server It is responsibility of `Http2WebSocketAcceptor` to select supported subprotocol with\n```groovy\nHttp2WebSocketAcceptor.Subprotocol.accept(subprotocol, response);\n```\n### lifecycle \n\nHandshake events and several shutdown options are available when \nusing `Websocket channel` style APIs.  \n\n#### handshake events  \n\nEvents are fired on parent channel, also on websocket channel if one gets created  \n* `Http2WebSocketHandshakeStartEvent(websocketId, path, subprotocols, timestampNanos, requestHeaders)`\n* `Http2WebSocketHandshakeErrorEvent(webSocketId, path, subprotocols, timestampNanos, responseHeaders, error)`\n* `Http2WebSocketHandshakeSuccessEvent(webSocketId, path, subprotocols, timestampNanos, responseHeaders)`\n\nThese events are accompanied by transport agnostic variants\n\n* `WebSocketHandshakeStartEvent(websocketId, path, subprotocols, timestampNanos, requestHeaders)`\n* `WebSocketHandshakeErrorEvent(webSocketId, path, subprotocols, timestampNanos, responseHeaders, error)`\n* `WebSocketHandshakeSuccessEvent(webSocketId, path, subprotocols, timestampNanos, responseHeaders)`\n\n#### close events\n\nOutbound `Http2WebSocketLocalCloseEvent` on websocket channel pipeline closes\nhttp2 stream by sending empty `DATA` frame with `END_STREAM` flag set.\n\nGraceful and `RST` stream shutdown by remote endpoint is represented with inbound `Http2WebSocketRemoteCloseEvent` \n(with type `CLOSE_REMOTE_ENDSTREAM` and `CLOSE_REMOTE_RESET` respectively)  on websocket channel pipeline. \n\nGraceful connection shutdown by remote with `GO_AWAY` frame is represented by inbound `Http2WebSocketRemoteGoAwayEvent` \non websocket channel pipeline.  \n\n#### shutdown\n\nClosing websocket channel terminates its http2 stream by sending `RST` frame.\n\n#### validation \u0026 write error events\n\nBoth API style handlers send `Http2WebSocketHandshakeErrorEvent` for invalid websocket-over-http2 and http requests.\nFor http2 frame write errors `Http2WebSocketWriteErrorEvent` is sent on parent channel if auto-close is not enabled;\notherwise exception is delivered with `ChannelPipeline.fireExceptionCaught` followed by immediate close.\n\n### flow control\n\nInbound flow control is done automatically as soon as `DATA` frames are received. \nLibrary relies on netty's `DefaultHttp2LocalFlowController` for refilling receive window.\n\nOutbound flow control is expressed as websocket channels writability change on send window\nexhaust/refill, provided by `DefaultHttp2RemoteFlowController`.\n\n### websocket stream weight\n\nInitial stream weight is configured with \n\n```groovy\nHttp2WebSocketClientBuilder.streamWeight(weight);\n```\nit can be updated by firing `Http2WebSocketStreamWeightUpdateEvent` on websocket channel pipeline.\n\n### performance\n\nLibrary relies on capabilities provided by netty's `Http2ConnectionHandler` so performance characteristics should be similar.\n[netty-websocket-http2-perftest](https://github.com/jauntsdn/netty-websocket-http2/tree/develop/netty-websocket-http2-perftest/src/main/java/com/jauntsdn/netty/handler/codec/http2/websocketx/perftest) \nmodule contains application that gives rough throughput/latency estimate. The application is started with `perf_server.sh`, `perf_client.sh`. \n\nOn modern box one can expect following results for single websocket, 140 bytes payload (TLS connection, per-core throughput):\n\n```properties\n15:15:23.978 epollEventLoopGroup-2-1 com.jauntsdn.netty.handler.codec.http2.websocketx.perftest.messagecodec.client.Main p50 =\u003e 639 micros\n15:15:23.978 epollEventLoopGroup-2-1 com.jauntsdn.netty.handler.codec.http2.websocketx.perftest.messagecodec.client.Main p95 =\u003e 947 micros\n15:15:23.978 epollEventLoopGroup-2-1 com.jauntsdn.netty.handler.codec.http2.websocketx.perftest.messagecodec.client.Main p99 =\u003e 1110 micros\n15:15:23.978 epollEventLoopGroup-2-1 com.jauntsdn.netty.handler.codec.http2.websocketx.perftest.messagecodec.client.Main throughput =\u003e 505140 messages\n15:15:23.978 epollEventLoopGroup-2-1 com.jauntsdn.netty.handler.codec.http2.websocketx.perftest.messagecodec.client.Main throughput =\u003e 69062.11 kbytes\n```\n\nIntegration with [jauntsdn/netty-websocket-http1](https://github.com/jauntsdn/netty-websocket-http2/tree/develop/netty-websocket-http2-callbacks-codec) codec for websocket-http1 frames \nprocessing significantly [improves](https://github.com/jauntsdn/netty-websocket-http2/tree/develop/netty-websocket-http2-perftest/src/main/java/com/jauntsdn/netty/handler/codec/http2/websocketx/perftest/callbackscodec) \nthroughput:\n\n```properties\n15:13:10.483 epollEventLoopGroup-2-1 com.jauntsdn.netty.handler.codec.http2.websocketx.perftest.callbackscodec.client.Main p50 =\u003e 492 micros\n15:13:10.484 epollEventLoopGroup-2-1 com.jauntsdn.netty.handler.codec.http2.websocketx.perftest.callbackscodec.client.Main p95 =\u003e 702 micros\n15:13:10.484 epollEventLoopGroup-2-1 com.jauntsdn.netty.handler.codec.http2.websocketx.perftest.callbackscodec.client.Main p99 =\u003e 825 micros\n15:13:10.484 epollEventLoopGroup-2-1 com.jauntsdn.netty.handler.codec.http2.websocketx.perftest.callbackscodec.client.Main throughput =\u003e 746880 messages\n15:13:10.484 epollEventLoopGroup-2-1 com.jauntsdn.netty.handler.codec.http2.websocketx.perftest.callbackscodec.client.Main throughput =\u003e 102112.5 kbytes\n```\n\nTo evaluate performance with multiple connections we compose an application comprised with simple echo server, and client\nsending batches of messages periodically over single websocket per connection (approximately models chat application)  \n\nWith 25k active connections each sending batches of 5-10 messages of 0.2-0.5 KBytes over single websocket every 15-30seconds,\nthe results are as follows (measured over time spans of 5 seconds): \n```properties\n11:32:44.080 pool-2-thread-1 com.jauntsdn.netty.handler.codec.http2.websocketx.stresstest.client.Main connection success   ==\u003e 25000\n11:32:44.080 pool-2-thread-1 com.jauntsdn.netty.handler.codec.http2.websocketx.stresstest.client.Main handshake success    ==\u003e 25000\n11:32:44.080 pool-2-thread-1 com.jauntsdn.netty.handler.codec.http2.websocketx.stresstest.client.Main messages p99, micros ==\u003e 177\n11:32:44.080 pool-2-thread-1 com.jauntsdn.netty.handler.codec.http2.websocketx.stresstest.client.Main messages p50, micros ==\u003e 91\n```\n\n### examples\n\n`netty-websocket-http2-example` module contains demos showcasing both API styles, with this library/browser as clients.\n \n* `channelserver, channelclient` packages for websocket subchannel API demos. \n* `handshakeserver, channelclient` packages for handshake only API demo.\n* `multiprotocol` packages for demo of server handling htt1/http2 websockets on the same port.\n* `lwsclient` package for client demo that runs against [https://libwebsockets.org/testserver/](https://libwebsockets.org/testserver/) which hosts websocket-over-http2\nserver implemented with [libwebsockets](https://github.com/warmcat/libwebsockets) - popular C-based networking library. \n\n### browser example\n`Channelserver` example serves web page at `https://www.localhost:8099` that sends pings to `/echo` endpoint.   \n\nCurrently Google Chrome, Mozilla Firefox and Microsoft Edge support websockets-over-http2.\n\n### build \u0026 binaries\n```\n./gradlew clean build installDist\n```\n\nReleases are published on MavenCentral\n```groovy\nrepositories {\n    mavenCentral()\n}\n\ndependencies {\n    implementation 'com.jauntsdn.netty:netty-websocket-http2:1.4.0'\n}\n```\n\n## LICENSE\n\nCopyright 2020-Present Maksym Ostroverkhov.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\nhttp://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjauntsdn%2Fnetty-websocket-http2","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjauntsdn%2Fnetty-websocket-http2","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjauntsdn%2Fnetty-websocket-http2/lists"}