{"id":19210976,"url":"https://github.com/bowbahdoe/jdk-httpserver","last_synced_at":"2025-05-12T19:26:32.654Z","repository":{"id":238844336,"uuid":"797572680","full_name":"bowbahdoe/jdk-httpserver","owner":"bowbahdoe","description":"Utilities for working with the JDK's built-in HTTP server.","archived":false,"fork":false,"pushed_at":"2024-11-18T06:31:44.000Z","size":88,"stargazers_count":12,"open_issues_count":0,"forks_count":2,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-20T17:38:11.013Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"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/bowbahdoe.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","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,"zenodo":null}},"created_at":"2024-05-08T05:26:17.000Z","updated_at":"2025-04-07T09:52:22.000Z","dependencies_parsed_at":null,"dependency_job_id":"c6fb376a-3441-4fc9-a37c-09564a55df1c","html_url":"https://github.com/bowbahdoe/jdk-httpserver","commit_stats":null,"previous_names":["bowbahdoe/jdk-httpserver"],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bowbahdoe%2Fjdk-httpserver","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bowbahdoe%2Fjdk-httpserver/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bowbahdoe%2Fjdk-httpserver/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bowbahdoe%2Fjdk-httpserver/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bowbahdoe","download_url":"https://codeload.github.com/bowbahdoe/jdk-httpserver/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253807255,"owners_count":21967326,"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":[],"created_at":"2024-11-09T13:40:03.132Z","updated_at":"2025-05-12T19:26:32.626Z","avatar_url":"https://github.com/bowbahdoe.png","language":"Java","funding_links":[],"categories":["Web服务器"],"sub_categories":["微服务框架"],"readme":"# JDK Http Server Utilities\n\n[![javadoc](https://javadoc.io/badge2/dev.mccue/jdk-httpserver/javadoc.svg)](https://javadoc.io/doc/dev.mccue/jdk-httpserver)\n[![Tests](https://github.com/bowbahdoe/jdk-httpserver/actions/workflows/test.yml/badge.svg)](https://github.com/bowbahdoe/jdk-httpserver/actions/workflows/test.yml)\n\nUtilities for working with the JDK's [built-in HTTP server](https://docs.oracle.com/en/java/javase/21/docs/api/jdk.httpserver/module-summary.html).\n\nRequires Java 21+\n\n## Dependency Information\n\n### Maven\n\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003edev.mccue\u003c/groupId\u003e\n    \u003cartifactId\u003ejdk-httpserver\u003c/artifactId\u003e\n    \u003cversion\u003e2024.11.18\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\n### Gradle\n\n```\ndependencies {\n    implementation(\"dev.mccue:jdk-httpserver:2024.11.18\")\n}\n```\n\n## Usage\n\n```java\nimport com.sun.net.httpserver.HttpServer;\nimport dev.mccue.jdk.httpserver.Body;\nimport dev.mccue.jdk.httpserver.HttpExchanges;\n\nimport java.io.IOException;\nimport java.net.InetSocketAddress;\n\nvoid main() throws IOException {\n    var server = HttpServer.create(new InetSocketAddress(8000), 0);\n    server.createContext(\"/\", exchange -\u003e {\n        exchange.getResponseHeaders().put(\"Content-Type\", \"text/html\");\n        HttpExchanges.sendResponse(exchange, 200, Body.of(\"\u003ch1\u003e Hello, world! \u003c/h1\u003e\"));\n    });\n    server.start();\n}\n```\n\n## Motivation and Explanation\n\nThe `jdk.httpserver` module has an unusually janky API.\n\nWhen responding to a request you are expected to call `sendResponseHeaders`\nwith both the status code to send and the number of bytes that you will send later.\nThen you should call `getResponseBody` to get, write to, and close the `OutputStream`\nthat returns.\n\nSo a typical usage will look like this.\n\n```java\nimport com.sun.net.httpserver.HttpServer;\n\nimport java.io.IOException;\nimport java.net.InetSocketAddress;\nimport java.nio.charset.StandardCharsets;\n\nvoid main() throws IOException {\n    var server = HttpServer.create(new InetSocketAddress(8000), 0);\n    server.createContext(\"/\", exchange -\u003e {\n        exchange.getResponseHeaders().put(\"Content-Type\", \"text/html\");\n        var body = \"\u003ch1\u003e Hello, world! \u003c/h1\u003e\".getBytes(StandardCharsets.UTF_8);\n        exchange.sendResponseHeaders(200, body.length);\n        try (var out = exchange.getResponseBody()) {\n            out.write(b);\n        }\n    });\n    server.start();\n}\n```\n\nThis has a few flaws. \n\n### 1. Response Lengths are unintuitive\n\nThe first of which is that, for some reason, a response length of `0` indicates that you want the server to send a chunked response.\n`-1` is what actually indicates an empty body. This makes many seemingly innocuous usages incorrect.\n\nI.E. the following is incorrect.\n\n```java\nexchange.sendResponseHeaders(200, body.length);\n```\n\nIt should instead be this.\n\n```java\nexchange.sendResponseHeaders(200, body.length == 0 ? -1 : body.length);\n```\n\nWhich is, at the very least, unintuitive.\n\nTo combat this, this library provides a dedicated `ResponseLength` type which more explicitly delineates\nbetween when you know how many bytes will be sent vs. when you do not.\n\n```java\nHttpExchangeUtils.sendResponseHeaders(200, ResponseLength.known(body.length));\n```\n\n```java\nHttpExchangeUtils.sendResponseHeaders(200, ResponseLength.unknown());\n```\n\n### 2. Writing a response body is error-prone.\n\nBetween when someone calls `sendResponseHeaders` and when they write out to the body\nprovided by `getResponseBody` things can go wrong.\n\n```java\nvar body = \"hello\".getBytes(StandardCharsets.UTF_8);\nexchange.sendResponseHeaders(200, body.length);\n\nmethodThatMightFail();\n\ntry (var out = exchange.getResponseBody()) {\n    out.write(body);   \n}\n```\n\nThis is troublesome because it means you might have already sent a `200 OK` response header\nbefore encountering a situation you would otherwise want to return a `500` or similar for.\n\nTo deal with that gap this library provides an explicit `Body` type. `Body`s wrap up both\nthe size that ultimately needs to be given to `sendResponseHeaders` and the process for\nwriting that response out.\n\n```java\nHttpExchangeUtils.sendResponse(exchange, 200, Body.of(\"hello\"));\n```\n\n`Body`s also, for the convenience of all, have an opinion about what content type they should be sent with.\n\n```java\n// A hypothetical JsonBody can suggest that \n// it be sent with a Content-Type: application/json header.\nHttpExchangeUtils.sendResponse(exchange, 200, new JsonBody(...));\n```\n\nAll of this should, I hope, make the API safer to use in practice.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbowbahdoe%2Fjdk-httpserver","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbowbahdoe%2Fjdk-httpserver","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbowbahdoe%2Fjdk-httpserver/lists"}