{"id":21387170,"url":"https://github.com/sshtools/uhttpd","last_synced_at":"2026-03-01T12:31:30.390Z","repository":{"id":65507960,"uuid":"586673548","full_name":"sshtools/uhttpd","owner":"sshtools","description":"A very simple Java embeddable HTTP/HTTPS server that has no external dependencies","archived":false,"fork":false,"pushed_at":"2025-11-12T10:58:20.000Z","size":235,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-11-12T11:22:16.755Z","etag":null,"topics":["http","http-server","https","https-server","java","unit-testing","websocket"],"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/sshtools.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":"2023-01-08T23:40:12.000Z","updated_at":"2025-11-12T10:58:24.000Z","dependencies_parsed_at":"2023-11-16T22:54:37.651Z","dependency_job_id":"d05c8bee-23e6-4303-9f34-12d533cc05b4","html_url":"https://github.com/sshtools/uhttpd","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/sshtools/uhttpd","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sshtools%2Fuhttpd","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sshtools%2Fuhttpd/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sshtools%2Fuhttpd/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sshtools%2Fuhttpd/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sshtools","download_url":"https://codeload.github.com/sshtools/uhttpd/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sshtools%2Fuhttpd/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29969243,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-01T11:43:06.159Z","status":"ssl_error","status_checked_at":"2026-03-01T11:43:03.887Z","response_time":124,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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","http-server","https","https-server","java","unit-testing","websocket"],"created_at":"2024-11-22T12:12:02.509Z","updated_at":"2026-03-01T12:31:29.947Z","avatar_url":"https://github.com/sshtools.png","language":"Java","readme":"# µHTTPD\n\n![Maven Build/Test JDK 17](https://github.com/sshtools/uhttpd/actions/workflows/maven.yml/badge.svg)\n[![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.sshtools/uhttpd/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.sshtools/uhttpd)\n[![Coverage Status](https://coveralls.io/repos/github/sshtools/uhttpd/badge.svg)](https://coveralls.io/github/sshtools/uhttpd)\n[![javadoc](https://javadoc.io/badge2/com.sshtools/uhttpd/javadoc.svg)](https://javadoc.io/doc/com.sshtools/uhttpd)\n![JPMS](https://img.shields.io/badge/JPMS-com.sshtools.uhttpd-purple) \n\nA very small HTTP/HTTPS server, intended for embedding into other applications generating dynamic content.\n\n## Status\n\n * Version 0.9.11 is close to production quality.\n * Known to be in use internally for several projects and one external public server.\n\n## Quick Start\n\n```java\n\npublic class SimpleServer {\n\tpublic static void main(String[] args) throws Exception {\n\t\ttry(var httpd = UHTTPD.server().\n\t\t\tget(\"/index\\\\.txt\", (tx) -\u003e tx.response(\"Hello World!\")).\n\t\t\tbuild()); {\n\t\t\thttpd.run();\n\t\t}\n\t}\n}\n```\n\nThis will run a server in the foreground on `localhost:8080`. Point your browser to [http://localhost:8080/index.txt](http://localhost:8080/index.txt)\n\n## About\n\n### Features\n\n * Supports HTTP and HTTPS (HTTP/1.0 and HTTP/1.1).\n * Easily generate dynamic content with simple handlers.\n * Serve static content from classpath resources or files.\n * Zero dependencies.\n * Basic HTTP authentication.\n * WebSockets.\n * Single source file. Can be just dropped into your project with ease.\n * Cookies.\n * CONNECT tunnels.\n * Chunked output and input\n * Compressed output and input\n * Multiple contexts.\n * Can work with [fibers](https://www.infoworld.com/article/3652596/project-loom-understand-the-new-java-concurrency-model.html).\n * Great for unit / integration testing.\n \n### WIP\n\n * Full JavaDoc.\n * WebSocket compression.\n * Tests\n \n### TODO\n\n * HTTP 2 and 3 (version 2).\n * Other authentication (version 2).\n * Lots of tests, testing and tuning.\n \n### Anti Features\n\n * It will not support the servlet spec (although an extension could).\n * It will not support non-programmatic configuration (although an extension could).\n * It will not allow configuration change at runtime.\n * It will not use non-blocking IO framework. \n \n## Setup\n\nNow in Maven Central, so to add to your project just include this single dependency (adjust for other build systems that use Maven repositories).\n\n```xml\n\t\n\u003cdependency\u003e\n\t\u003cgroupId\u003ecom.sshtools\u003c/groupId\u003e\n\t\u003cartifactId\u003euhttpd\u003c/artifactId\u003e\n\t\u003cversion\u003e[VERSION]\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\n_See badge above for version available on Maven Central. Snapshot versions are in the [Sonatype OSS Snapshot Repository](https://oss.sonatype.org/content/repositories/snapshots/)._\n\n## More Examples\n\nSimple examples. Most will start the server in the foreground indefinitely.\n\n * [Serving some HTML](#serving-some-html)\n * [Handling `GET` parameters](#handling-get-parameters)\n * [Handling `POST` parameters](#handling-post-parameters)\n * [Responder](#responder)\n * [Response Writer](#response-writer)\n * [Contexts](#contexts)\n * [Authentication](#authentication)\n * [Static Content](#static-content)\n * [Cookies](#cookies)\n * [WebSockets](#websockets)\n * [Tunnels](#tunnels)\n * [Error Pages](#error-pages)\n * [SSL](#ssl) \n * [Using Fibers](#using-fibers) \n * [Running In Background](#running-in-background)\n \n### Serving some HTML\n\nJust generate a page of HTML from a Java string.\n \n ```java\ntry(var httpd = UHTTPD.server().\n\tget(\"/index\\\\.html\", (tx) -\u003e { \n\t\ttx.response(\"text/html\", \"\"\"\n\t\t\u003chtml\u003e\n\t\t\u003cbody\u003e\n\t\t\u003cp\u003eClick \u003ca href=\"other.html\"\u003ehere\u003c/a\u003e to go to another page\u003c/p\u003e\n\t\t\u003c/body\u003e\n\t\t\u003c/html\u003e\n\t\t\"\"\"); \n\t}).\n\tget(\"/other\\\\.html\", (tx) -\u003e { \n\t\ttx.response(\"text/html\", \"\"\"\n\t\t\u003chtml\u003e\n\t\t\u003cbody\u003e\n\t\t\u003cp\u003eClick \u003ca href=\"other.html\"\u003ehere\u003c/a\u003e to go back to the home page\u003c/p\u003e\n\t\t\u003c/body\u003e\n\t\t\u003c/html\u003e\n\t\t\"\"\"); \n\t}).\n\tbuild()) {\n\thttpd.run();\n}\n\n```\n\n### Handling GET parameters\n\nAccess the `Named` via `Transaction.parameter`. From this, you can get as various types.\n \n ```java\ntry(var httpd = UHTTPD.server().\n\tget(\"/calc\\\\.html\", (tx) -\u003e {\n\t\ttx.response(MessageFormat.format(\"{0} + {1} = {2}\", \n\t\t\t\ttx.parameter(\"a\").asString(), \n\t\t\t\ttx.parameter(\"b\").asString(),\n\t\t\t\ttx.parameter(\"a\").asFloat() + tx.parameter(\"b\").asFloat()));\n\t}).\n\tbuild()) {\n\thttpd.run();\n}\n ```\n \n### Handling POST parameters\n\nFor example, file uploads.\n \n ```java\n try(var httpd = UHTTPD.server().\n\tpost(\"/upload\", (tx) -\u003e {\n\t\tvar content = tx.request();\n\t\tvar tmpFile = Files.createTempFile(\"upload\", \".test\");\n\t\tvar file = content.asFormData(\"file\");\n\t\ttry(var in = file.asStream()) {\n\t\t\ttry(var out = Files.newOutputStream(tmpFile)) {\n\t\t\t\tin.transferTo(out);\n\t\t\t}\n\t\t}\n\t\treq.response(MessageFormat.format(\"Uploaded to {0} (Content type: {1})\", tmpFile, file.contentType().orElse(\"Unknown\")));\n\t}).\n\tbuild()) {\n\thttpd.run();\n}\n ```\n\n### Responder\n\nUse a `responder()` to feed response content chunk by chunk.\n \n ```java\ntry(var httpd = UHTTPD.server().\n\tget(\"/respond\", (tx) -\u003e {\n\t\tvar line = new AtomicInteger(0);\n\t\ttx.responder(buf -\u003e {\n\t\t\tif(line.incrementAndGet() \u003c 10) {\n\t\t\t\tbuf.put(ByteBuffer.wrap((\"Line \" + line.get() + \"\\n\").getBytes()));\n\t\t\t}\n\t\t});\n\t}).\n\tbuild()) {\n\thttpd.run();\n}\n ```\n\n### Response Writer\n\nYou can also get a `WritableByteChannel` (and so also create a traditional `Writer` using `Channels` utility methods). As soon as you use this, all other methods of `Transaction` that would modify the response can no longer be used.\n\nMake sure you `close()` the writer, as this marks the end of the response (for chunked encoding etc).\n \n ```java\ntry(var httpd = UHTTPD.server().\n\tget(\"/writer.html\", (tx) -\u003e {\n\t\ttx.responseType(\"text/html\");\n\t\ttry(var w = new PrintWriter(Channels.newWriter(tx.responseWriter(), tx.client().charset()), true)) {\n\t\t\tw.println(\"\u003chtml\u003e\");\n\t\t\tw.println(\"\u003cbody\u003e\");\n\t\t\tw.println(\"\u003ch1\u003eSome title\u003c/h1\u003e\");\n\t\t\tw.println(\"\u003cp\u003eA paragraph of text\u003c/p\u003e\");\n\t\t\tw.println(\"\u003c/body\u003e\");\n\t\t\tw.println(\"\u003c/html\u003e\");\n\t\t}\n\t}).\n\tbuild()) {\n\thttpd.run();\n}\n ```\n \n### Contexts\n\nContexts let you isolate and group any `Handler` under a single path. Any paths of the contained handlers are then relative to the context path. Contexts can be nested. \n\nContexts are themselves a `Handler`, so can be added with a `HandlerSelector`, or preceeded by authentication handlers etc. \n \n```java\ntry(var httpd = UHTTPD.server().\n\tcontext(UHTTPD.context(\"/others/(.*)\").\n\t\tget(\"/file.txt\", tx -\u003e tx.response(\"Some more text.\")).\n\t\tget(\"/file2.txt\", tx -\u003e tx.response(\"More other text.\")).\n\t\tbuild()).\n\tget(\"/file.txt\", tx -\u003e tx.response(\"Some text\")).\n\tget(\"/file2.txt\", tx -\u003e tx.response(\"Other text\")).\n\twithHttps().\n\tbuild()) {\n\thttpd.run();\n}\n```\n### Authentication\n\nAdding authentication (HTTP Basic) to some pages.\n \n ```java\ntry(var httpd = UHTTPD.server().\n\tget(\"/login\\\\.html\", \n\t\t(tx) -\u003e { \n\t\t\ttx.response(\"text/html\", \"\"\"\n\t\t\t\u003chtml\u003e\n\t\t\t\u003cbody\u003e\n\t\t\t\u003cp\u003eClick \u003ca href=\"protected.html\"\u003ehere\u003c/a\u003e to login to protected page.\u003c/p\u003e\n\t\t\t\u003cp\u003eThe username is \u003cbold\u003euser\u003c/bold\u003e and the password is \u003cbold\u003epassword\u003c/bold\u003e\n\t\t\t\u003c/body\u003e\n\t\t\t\u003c/html\u003e\n\t\t\t\"\"\"); \n\t}).\n\tget(\"/protected\\\\.html\",\n\t\t\t\n\t\tUHTTPD.httpBasicAuthentication((creds) -\u003e \n\t\t\t\tcreds.result(\n\t\t\t\t\tcreds.username().equals(\"user\") \u0026\u0026 \n\t\t\t\t\tnew String(creds.password()).equals(\"password\")))\n\t\t\t\t.withRealm(\"MyRealm\")).build(),\n\t\t\t\t\n\t\t(tx) -\u003e { \n\t\t\ttx.response(\"text/html\", \"\"\"\n\t\t\t\u003chtml\u003e\n\t\t\t\u003cbody\u003e\n\t\t\t\u003cp\u003eThis is a protected page.\u003c/p\u003e\n\t\t\t\u003c/body\u003e\n\t\t\t\u003c/html\u003e\n\t\t\t\"\"\"); \n\t}).\n\tbuild()) {\n\thttpd.run();\n}\n ```\n \n### Static Content\n\nServe static files and classpath resources. The matching pattern usings regular expression capture groups. \n \n ```java\n try(var httpd = UHTTPD.server().\n\tclasspathResources(\"/cp/(.*)\", \"web\").\n\tfileResources(\"/local/(.*)\", Paths.get(\"/home/auser/share\")).\n\tbuild()) {\n\thttpd.run();\n}\n ```\n\n### Cookies\n \nReceiving cookie string values and responding with `Cookie` objects.\n\n```java\ntry (var httpd = UHTTPD.server().get(\"/set-cookie\\\\.html\", (tx) -\u003e {\n\ttx.cookie(UHTTPD.cookie(\"MyCookie\", \"A Value\").build());\n\ttx.response(\"text/html\", \"\"\"\n\t\t\t\u003chtml\u003e\n\t\t\t\u003cbody\u003e\n\t\t\t\u003cp\u003eI have set a cookie!\u003c/p\u003e\n\t\t\t\u003c/body\u003e\n\t\t\t\u003c/html\u003e\n\t\t\t\"\"\");\n}).get(\"/get-cookie\\\\.html\", (tx) -\u003e {\n\ttx.response(\"text/html\", \"\"\"\n\t\t\t\u003chtml\u003e\n\t\t\t\u003cbody\u003e\n\t\t\t\u003cp\u003eThe cookie value is __cookie__.\u003c/p\u003e\n\t\t\t\u003c/body\u003e\n\t\t\t\u003c/html\u003e\n\t\t\t\"\"\".replace(\"__cookie__\", tx.cookie(\"MyCookie\").value()));\n}).build()) {\n\thttpd.run();\n}\n```\n\n### WebSockets\n\nSend and receive text and binary messages. \n\n ```java\ntry (var httpd = UHTTPD.server()\n\t.webSocket(\"/ws\", UHTTPD.webSocket().\n\t\tonText((txt, ws) -\u003e {\n\t\t\n\t\t\tSystem.out.println(\"got '\" + txt + \"'\");\n\t\t\n\t\t\tws.send(\"I received '\" + txt + \"'\"); // text reply\n\t\t}).\n\t\tonData((buf, fin, ws) -\u003e {\n\t\t\tSystem.out.println(\"got \" + buf.remaining() + \" bytes\");\n\t\t\n\t\t\tws.send(ByteBuffer.wrap(new byte[] {1,2,3})); // single binary reply\n\t\t\t\n\t\t\tws.fragment(ByteBuffer.wrap(new byte[] {1}), false); // 1st fragment\n\t\t\tws.fragment(ByteBuffer.wrap(new byte[] {2}), false); // 2nd fragment\n\t\t\tws.fragment(ByteBuffer.wrap(new byte[] {3}), true); // final fragment\n\t\t}).\n\t\tonClose((code, text, ws) -\u003e {\n\t\t\t// web socket closed\n\t\t}).\n\t\tonOpen((ws) -\u003e {\n\t\t\tws.send(\"Hello!\");\n\t\t}).\n\t\tbuild())\n\t.classpathResources(\"(.*)\", \"web\")\n\t.build()) {\n\thttpd.run();\n} \n ```\n \n### Tunnels\n\nTunnelling using the `CONNECT` header. This directly connects a new Socket. You can optionally use `UHTTPD.tunnel()` which returns a `TunnelBuilder` which allows you to handle the incoming and outgoing data yourself.  \n \n ```java\n try(var httpd = UHTTPD.server().tunnel(UHTTPD.socketTunnel()).build()) {\n\thttpd.run();\n}\n ```\n\n### Error Pages\n\nSetting error pages. You can set a handler that is invoked when a particular status code occurs.\n\n```java\ntry(var httpd = UHTTPD.server().\t\n\tstatus(Status.NOT_FOUND, UHTTPD.classpathResource(\"web/404.html\")).build()) {\n\thttpd.run();\n}\n```\n\n### SSL\n\nTo use SSL you must provide a `KeyStore`. If you don't specifically supply one, there must a keystore file at `$HOME/.keystore` with a passphrase of `changeit` (this is the default used by the `keytool` command). Otherwise, either provide the path to a keystore file along with passwords, or provide an instance of `KeyStore`. The default port for SSL is 8443.\n\n\n```java\ntry(var httpd = UHTTPD.server().\n\tget(\"/text.txt\", tx -\u003e {\n\t\ttx.response(\"text/plain\", \"This is some text.\");\n\t}).\n\twithHttps().\n\tbuild()) {\n\thttpd.run();\n}\n```\n\nTo generate a self signed certificate for development use, run `keytool`. \n\n```\nkeytool -genkey -alias uhttpd -keyalg RSA\n```\n\n### Using Fibers\n\nIf you hava Java 19 and use the `--enable-preview` argument to both compile and run, you can try out the use of [fibers](https://www.infoworld.com/article/3652596/project-loom-understand-the-new-java-concurrency-model.html). These are lightweight threads that should greatly increase scalability. Once the feature is enabled, simply set a custom `Runner`.\n\n```java\ntry(var httpd = server().\n\tget(\"/file.txt\", tx -\u003e tx.response(Paths.get(\"/home/tanktarta/Desktop/SMS and EMAIL API Example.java\"))).\n\twithRunner(r -\u003e Thread.startVirtualThread(r)).\n\tbuild()) {\n\thttpd.run();\n}\n```\n\n### Running In Background\n \nRunning the server in the background.\n \n```java\nvar builder = UTTPD.server();\n \nbuilder.fileResources(\"/local/(.*)\", Paths.get(\"/home/auser/share\"));\nbuilder.withHttp(8081);\n \nvar server = builder.build();\nserver.start(); // starts in background\n \n// ...\n// do other stuff \n// ...\n \nserver.close();\nserver.join(); // optionally wait for threads to shutdown\n```\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsshtools%2Fuhttpd","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsshtools%2Fuhttpd","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsshtools%2Fuhttpd/lists"}