Giter Club home page Giter Club logo

jdk-httpserver's Introduction

JDK Http Server Utilities

javadoc Tests

Utilities for working with the JDK's built-in HTTP server.

Requires Java 21+

Dependency Information

Maven

<dependency>
    <groupId>dev.mccue</groupId>
    <artifactId>jdk-httpserver</artifactId>
    <version>2024.05.08</version>
</dependency>

Gradle

dependencies {
    implementation("dev.mccue:jdk-httpserver:2024.05.08")
}

Usage

import com.sun.net.httpserver.HttpServer;
import dev.mccue.jdk.httpserver.Body;
import dev.mccue.jdk.httpserver.HttpExchangeUtils;

import java.io.IOException;
import java.net.InetSocketAddress;

void main() throws IOException {
    var server = HttpServer.create(new InetSocketAddress(8000), 0);
    server.createContext("/", exchange -> {
        exchange.getResponseHeaders().put("Content-Type", "text/html");
        HttpExchangeUtils.sendResponse(exchange, 200, Body.of("<h1> Hello, world! </h1>"));
    });
    server.start();
}

Motivation and Explanation

The jdk.httpserver module has an unusually janky API.

When responding to a request you are expected to call sendResponseHeaders with both the status code to send and the number of bytes that you will send later. Then you should call getResponseBody to get, write to, and close the OutputStream that returns.

So a typical usage will look like this.

import com.sun.net.httpserver.HttpServer;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.charset.StandardCharsets;

void main() throws IOException {
    var server = HttpServer.create(new InetSocketAddress(8000), 0);
    server.createContext("/", exchange -> {
        exchange.getResponseHeaders().put("Content-Type", "text/html");
        var body = "<h1> Hello, world! </h1>".getBytes(StandardCharsets.UTF_8);
        exchange.sendResponseHeaders(200, body.length);
        try (var out = exchange.getResponseBody()) {
            out.write(b);
        }
    });
    server.start();
}

This has a few flaws.

1. Response Lengths are unintuitive

The first of which is that, for some reason, a response length of 0 indicates that you want the server to send a chunked response. -1 is what actually indicates an empty body. This makes many seemingly innocuous usages incorrect.

I.E. the following is incorrect.

exchange.sendResponseHeaders(200, body.length);

It should instead be this.

exchange.sendResponseHeaders(200, body.length == 0 ? -1 : body.length);

Which is, at the very least, unintuitive.

To combat this, this library provides a dedicated ResponseLength type which more explicitly delineates between when you know how many bytes will be sent vs. when you do not.

HttpExchangeUtils.sendResponseHeaders(200, ResponseLength.known(body.length));
HttpExchangeUtils.sendResponseHeaders(200, ResponseLength.unknown());

2. Writing a response body is error-prone.

Between when someone calls sendResponseHeaders and when they write out to the body provided by getResponseBody things can go wrong.

var body = "hello".getBytes(StandardCharsets.UTF_8);
exchange.sendResponseHeaders(200, body.length);

methodThatMightFail();

try (var out = exchange.getResponseBody()) {
    out.write(body);   
}

This is troublesome because it means you might have already sent a 200 OK response header before encountering a situation you would otherwise want to return a 500 or similar for.

To deal with that gap this library provides an explicit Body type. Bodys wrap up both the size that ultimately needs to be given to sendResponseHeaders and the process for writing that response out.

HttpExchangeUtils.sendResponse(exchange, 200, Body.of("hello"));

Bodys also, for the convenience of all, have an opinion about what content type they should be sent with.

// A hypothetical JsonBody can suggest that 
// it be sent with a Content-Type: application/json header.
HttpExchangeUtils.sendResponse(exchange, 200, new JsonBody(...));

All of this should, I hope, make the API safer to use in practice.

jdk-httpserver's People

Contributors

bowbahdoe avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.