Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/sshtools/uhttpd
A very simple Java embeddable HTTP/HTTPS server that has no external dependencies
https://github.com/sshtools/uhttpd
http http-server https https-server java unit-testing websocket
Last synced: 11 days ago
JSON representation
A very simple Java embeddable HTTP/HTTPS server that has no external dependencies
- Host: GitHub
- URL: https://github.com/sshtools/uhttpd
- Owner: sshtools
- License: apache-2.0
- Created: 2023-01-08T23:40:12.000Z (about 2 years ago)
- Default Branch: main
- Last Pushed: 2024-10-13T16:07:15.000Z (4 months ago)
- Last Synced: 2024-11-22T12:12:12.697Z (2 months ago)
- Topics: http, http-server, https, https-server, java, unit-testing, websocket
- Language: Java
- Homepage:
- Size: 210 KB
- Stars: 1
- Watchers: 3
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# µHTTPD
![Maven Build/Test JDK 17](https://github.com/sshtools/uhttpd/actions/workflows/maven.yml/badge.svg)
[![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.sshtools/uhttpd/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.sshtools/uhttpd)
[![Coverage Status](https://coveralls.io/repos/github/sshtools/uhttpd/badge.svg)](https://coveralls.io/github/sshtools/uhttpd)
[![javadoc](https://javadoc.io/badge2/com.sshtools/uhttpd/javadoc.svg)](https://javadoc.io/doc/com.sshtools/uhttpd)
![JPMS](https://img.shields.io/badge/JPMS-com.sshtools.uhttpd-purple)A very small HTTP/HTTPS server, intended for embedding into other applications generating dynamic content.
## Status
* Version 0.9.7 is close to production quality.
* Known to be in use internally for several projects and one external public server.## Quick Start
```java
public class SimpleServer {
public static void main(String[] args) throws Exception {
try(var httpd = UHTTPD.server().
get("/index\\.txt", (tx) -> tx.response("Hello World!")).
build()); {
httpd.run();
}
}
}
```This will run a server in the foreground on `localhost:8080`. Point your browser to [http://localhost:8080/index.txt](http://localhost:8080/index.txt)
## About
### Features
* Supports HTTP and HTTPS (HTTP/1.0 and HTTP/1.1).
* Easily generate dynamic content with simple handlers.
* Serve static content from classpath resources or files.
* Zero dependencies.
* Basic HTTP authentication.
* WebSockets.
* Single source file. Can be just dropped into your project with ease.
* Cookies.
* CONNECT tunnels.
* Chunked output and input
* Compressed output and input
* Multiple contexts.
* Can work with [fibers](https://www.infoworld.com/article/3652596/project-loom-understand-the-new-java-concurrency-model.html).
* Great for unit / integration testing.
### WIP* Full JavaDoc.
* WebSocket compression.
* Tests
### TODO* HTTP 2 and 3 (version 2).
* Other authentication (version 2).
* Lots of tests, testing and tuning.
### Anti Features* It will not support the servlet spec (although an extension could).
* It will not support non-programmatic configuration (although an extension could).
* It will not allow configuration change at runtime.
* It will not use non-blocking IO framework.
## SetupNow in Maven Central, so to add to your project just include this single dependency (adjust for other build systems that use Maven repositories).
```xml
com.sshtools
uhttpd
[VERSION]```
_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/)._
## More Examples
Simple examples. Most will start the server in the foreground indefinitely.
* [Serving some HTML](#serving-some-html)
* [Handling `GET` parameters](#handling-get-parameters)
* [Handling `POST` parameters](#handling-post-parameters)
* [Responder](#responder)
* [Response Writer](#response-writer)
* [Contexts](#contexts)
* [Authentication](#authentication)
* [Static Content](#static-content)
* [Cookies](#cookies)
* [WebSockets](#websockets)
* [Tunnels](#tunnels)
* [Error Pages](#error-pages)
* [SSL](#ssl)
* [Using Fibers](#using-fibers)
* [Running In Background](#running-in-background)
### Serving some HTML
```java
try(var httpd = UHTTPD.server().
get("/index\\.html", (tx) -> {
tx.response("text/html", """
Click here to go to another page
""");
}).
get("/other\\.html", (tx) -> {
tx.response("text/html", """
Click here to go back to the home page
""");
}).
build()) {
httpd.run();
}
```
### Handling `GET` parameters
```java
try(var httpd = UHTTPD.server().
get("/calc\\.html", (tx) -> {
tx.response(MessageFormat.format("{0} + {1} = {2}",
tx.parameter("a").asString(),
tx.parameter("b").asString(),
tx.parameter("a").asFloat() + tx.parameter("b").asFloat()));
}).
build()) {
httpd.run();
}
```
### Handling `POST` parametersFor example, file uploads.
```java
try(var httpd = UHTTPD.server().
post("/upload", (tx) -> {
var content = tx.request();
var tmpFile = Files.createTempFile("upload", ".test");
var file = content.asFormData("file");
try(var in = file.asStream()) {
try(var out = Files.newOutputStream(tmpFile)) {
in.transferTo(out);
}
}
req.response(MessageFormat.format("Uploaded to {0} (Content type: {1})", tmpFile, file.contentType().orElse("Unknown")));
}).
build()) {
httpd.run();
}
```### Responder
Use a `responder()` to feed response content chunk by chunk.
```java
try(var httpd = UHTTPD.server().
get("/respond", (tx) -> {
var line = new AtomicInteger(0);
tx.responder(buf -> {
if(line.incrementAndGet() < 10) {
buf.put(ByteBuffer.wrap(("Line " + line.get() + "\n").getBytes()));
}
});
}).
build()) {
httpd.run();
}
```### Response Writer
You 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.
Make sure you `close()` the writer, as this marks the end of the response (for chunked encoding etc).
```java
try(var httpd = UHTTPD.server().
get("/writer.html", (tx) -> {
tx.responseType("text/html");
try(var w = new PrintWriter(Channels.newWriter(tx.responseWriter(), tx.client().charset()), true)) {
w.println("");
w.println("");
w.println("Some title
");
w.println("A paragraph of text
");
w.println("");
w.println("");
}
}).
build()) {
httpd.run();
}
```
### ContextsContexts 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.
Contexts are themselves a `Handler`, so can be added with a `HandlerSelector`, or preceeded by authentication handlers etc.
```java
try(var httpd = UHTTPD.server().
context(UHTTPD.context("/others/(.*)").
get("/file.txt", tx -> tx.response("Some more text.")).
get("/file2.txt", tx -> tx.response("More other text.")).
build()).
get("/file.txt", tx -> tx.response("Some text")).
get("/file2.txt", tx -> tx.response("Other text")).
withHttps().
build()) {
httpd.run();
}
```
### AuthenticationAdding authentication (HTTP Basic) to some pages.
```java
try(var httpd = UHTTPD.server().
get("/login\\.html",
(tx) -> {
tx.response("text/html", """
Click here to login to protected page.
The username is user and the password is password
""");
}).
get("/protected\\.html",
UHTTPD.httpBasicAuthentication((creds) ->
creds.result(
creds.username().equals("user") &&
new String(creds.password()).equals("password")))
.withRealm("MyRealm")).build(),
(tx) -> {
tx.response("text/html", """
This is a protected page.
""");
}).
build()) {
httpd.run();
}
```
### Static ContentServe static files and classpath resources. The matching pattern usings regular expression capture groups.
```java
try(var httpd = UHTTPD.server().
classpathResources("/cp/(.*)", "web").
fileResources("/local/(.*)", Paths.get("/home/auser/share")).
build()) {
httpd.run();
}
```### Cookies
Receiving cookie string values and responding with `Cookie` objects.```java
try (var httpd = UHTTPD.server().get("/set-cookie\\.html", (tx) -> {
tx.cookie(UHTTPD.cookie("MyCookie", "A Value").build());
tx.response("text/html", """
I have set a cookie!
""");
}).get("/get-cookie\\.html", (tx) -> {
tx.response("text/html", """
The cookie value is __cookie__.
""".replace("__cookie__", tx.cookie("MyCookie").value()));
}).build()) {
httpd.run();
}
```### WebSockets
Send and receive text and binary messages.
```java
try (var httpd = UHTTPD.server()
.webSocket("/ws", UHTTPD.webSocket().
onText((txt, ws) -> {
System.out.println("got '" + txt + "'");
ws.send("I received '" + txt + "'"); // text reply
}).
onData((buf, fin, ws) -> {
System.out.println("got " + buf.remaining() + " bytes");
ws.send(ByteBuffer.wrap(new byte[] {1,2,3})); // single binary reply
ws.fragment(ByteBuffer.wrap(new byte[] {1}), false); // 1st fragment
ws.fragment(ByteBuffer.wrap(new byte[] {2}), false); // 2nd fragment
ws.fragment(ByteBuffer.wrap(new byte[] {3}), true); // final fragment
}).
onClose((code, text, ws) -> {
// web socket closed
}).
onOpen((ws) -> {
ws.send("Hello!");
}).
build())
.classpathResources("(.*)", "web")
.build()) {
httpd.run();
}
```
### TunnelsTunnelling 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.
```java
try(var httpd = UHTTPD.server().tunnel(UHTTPD.socketTunnel()).build()) {
httpd.run();
}
```### Error Pages
Setting error pages. You can set a handler that is invoked when a particular status code occurs.
```java
try(var httpd = UHTTPD.server().
status(Status.NOT_FOUND, UHTTPD.classpathResource("web/404.html")).build()) {
httpd.run();
}
```### SSL
To 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.
```java
try(var httpd = UHTTPD.server().
get("/text.txt", tx -> {
tx.response("text/plain", "This is some text.");
}).
withHttps().
build()) {
httpd.run();
}
```To generate a self signed certificate for development use, run `keytool`.
```
keytool -genkey -alias uhttpd -keyalg RSA
```### Using Fibers
If 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`.
```java
try(var httpd = server().
get("/file.txt", tx -> tx.response(Paths.get("/home/tanktarta/Desktop/SMS and EMAIL API Example.java"))).
withRunner(r -> Thread.startVirtualThread(r)).
build()) {
httpd.run();
}
```### Running In Background
Running the server in the background.
```java
var builder = UTTPD.server();
builder.fileResources("/local/(.*)", Paths.get("/home/auser/share"));
builder.withHttp(8081);
var server = builder.build();
server.start(); // starts in background
// ...
// do other stuff
// ...
server.close();
server.join(); // optionally wait for threads to shutdown
```