Class HttpServer

java.lang.Object
com.codename1.backend.HttpServer

public final class HttpServer extends Object

An HTTP/1.1 server built around a reactor and a bounded worker pool.

The split is the whole design. A parked ParparVM thread costs about 243KB on musl (see vm/benchmarks ThreadCost), so ten thousand connections cannot each have one. Here an IDLE connection is a descriptor the reactor watches, and only a connection with a request in flight occupies a worker. Workers are bounded, so overload sheds as queueing rather than as memory exhaustion.

Once a worker owns a connection its descriptor is switched to BLOCKING mode, so request parsing is a straight read loop rather than a resumable state machine. That spends a worker per in-flight request to avoid a large amount of complexity, and in-flight requests are the thing there are few of.

  • Method Details

    • start

      public static HttpServer start(String host, int port, int backlog, int workerCount, HttpServer.Handler handler) throws IOException
      Throws:
      IOException
    • start

      public static HttpServer start(String host, int port, int backlog, int workerCount, HttpServer.Handler handler, Tls tls) throws IOException
      • tls: terminate TLS here, or null to serve plaintext (correct behind a load balancer that already terminated it)
      Throws:
      IOException
    • start

      public static HttpServer start(String host, int port, int backlog, int workerCount, HttpServer.Handler handler, Tls tls, HttpServer.WebSocketRoutes webSockets) throws IOException
      • webSockets: asked for its routes before the listener accepts anything

      The callback runs here rather than on the returned server because this method binds the listener and starts the poller threads before it returns: a client arriving in that window would find no websocket routes at all, and its upgrade would fall through to the ordinary handler and be answered as HTTP. There is no way to express that mistake through this signature.

      Throws:
      IOException
    • getPort

      public int getPort()
    • getOpenConnections

      public int getOpenConnections()
      Connections currently open.
    • getActiveRequests

      public int getActiveRequests()
      Requests being handled right now. This is what saturation looks like.
    • getMetrics

      public Map getMetrics()
      A snapshot for a health or metrics endpoint. "draining" is what a load balancer needs to see to take this instance out of rotation before it stops answering.
    • awaitTermination

      public void awaitTermination()

      Blocks until the server has fully stopped, draining included.

      A caller's main() must do this or something equivalent: the reactor and the workers run on threads ParparVM creates DETACHED, so when main returns the process exits and takes them with it -- silently, with status 0, which from outside looks exactly like a server that refuses connections.

      Waiting on the reactor THREAD is not enough, and that was a real bug: stop() clears the running flag, the loop returns on its next timeout, main wakes up and the process ends while a worker is still writing a response. This waits on the drain finishing instead.

    • stop

      public void stop(int drainMillis)

      Stops accepting, lets in-flight requests finish, then closes what is left.

      The order matters. Closing the listener first means no new work arrives while the pool drains; draining before closing connections means a request already being served gets to produce its response instead of having the socket pulled out from under it, which is what a client sees as a truncated reply.

    • stop

      public void stop()
      Stops with a default drain window.