Tag: http

  • Jetty HTTP/3 Support

    Introduction

    HTTP/3 is the next iteration of the HTTP protocol.

    HTTP/1.0 was released in 1996 and HTTP/1.1 in 1997; HTTP/1.x is a fairly simple textual protocol based on TCP, possibly wrapped in TLS, that experienced over the years a tremendous growth that was not anticipated in the late ’90s.
    With the growth, a few issues in the HTTP/1.x scalability were identified, and addressed first by the SPDY protocol (HTTP/2 precursor) and then by HTTP/2.

    The design of HTTP/2, released in 2015 (and also based on TCP), resolved many of the HTTP/1.x shortcomings and  protocol became binary and multiplexed.

    The deployment at large of HTTP/2 revealed some issues in the HTTP/2 protocol itself, mainly due a shift towards mobile devices where connectivity is less reliable and packet loss more frequent.

    Enter HTTP/3, which ditches TCP for QUIC (RFC 9000) to address the connectivity issues of HTTP/2.
    HTTP/3 and QUIC are inextricably entangled together because HTTP/3 relies heavily on QUIC features that are not provided by any other lower-level protocol.

    QUIC is based on UDP (rather than TCP) and has TLS built-in, rather than layered on top.
    This means that you cannot offload TLS in a front-end server, like with HTTP/1.x and HTTP/2, and then forward the clear-text HTTP/x bytes to back-end servers.

    Due to HTTP/3 relying heavily on QUIC features, it’s not possible anymore to separate the “carrier” protocol (QUIC) from the “semantic” protocol (HTTP). Therefor reverse proxying should either:

    • decrypt QUIC+HTTP/3, perform some proxy processing, and re-encrypt QUIC+HTTP/3 to forward to back-end servers; or
    • decrypt QUIC+HTTP/3, perform some proxy processing, and re-encode into a different protocol such as HTTP/2 or HTTP/1.x to forward to back-end servers, with the risk of losing features by using older HTTP protocol versions.

    The Jetty Project has always been on the front at implementing Web protocols and standard, and QUIC+HTTP/3 is no exception.

    Jetty’s HTTP/3 Support

    At this time, Jetty’s support for HTTP/3 is still experimental and not recommended for production use.

    We decided to use the Cloudflare’s Quiche library because QUIC’s use of TLS requires new APIs that are not available in OpenJDK; we could not implement QUIC in pure Java.

    We wrapped the native calls to Quiche with either JNA or with Java 17’s Foreign APIs (JEP 412) and retrofitted the existing Jetty’s I/O library to work with UDP as well.
    A nice side effect of this work is that now Jetty is a truly generic network server, as it can be used to implement any generic protocol (not just web protocols) on either TCP or UDP.

    HTTP/3 was implemented in Jetty 10.0.8/11.0.8 for both the client and the server.
    The implementation is quite similar to Jetty’s HTTP/2 implementation, since the protocols are quite similar as well.

    HTTP/3 on the client is available in two forms:

    • Using the high-level APIs provided by Jetty’s HttpClient with the HTTP/3 specific transport (that only speaks HTTP/3), or with the dynamic transport (that can speak multiple protocols).
    • Using the low-level HTTP/3 APIs provided by Jetty’s HTTP3Client that allow you to deal directly with HTTP/3 sessions, streams and frames.

    HTTP/3 on the server is available in two forms:

    • Using embedded code via HTTP3ServerConnector listening on a specific network port.
    • Using Jetty as a standalone server by enabling the http3 Jetty module.

    In both cases, an incoming HTTP/3 request is processed and forwarded to your standard Web Applications, or to your Jetty Handlers.

    Finally, the HTTP/3 specification at the IETF is still a draft and may change, and we prioritized a working implementation over performance.

  • UnixDomain Support in Jetty

    UnixDomain sockets support was added in Jetty 9.4.0, back in 2015, based on the JNR UnixSocket library.

    The support for UnixDomain sockets with JNR was experimental, and has remained so until now.

    In Jetty 10.0.7/11.0.7 we re-implemented support for UnixDomain sockets based on JEP 380, which shipped with Java 16.

    We have kept the source compatibility at Java 11 and used a little bit of Java reflection to access the new APIs introduced by JEP 380, so that Jetty 10/11 can still be built with Java 11.
    However, if you run Jetty 10.0.7/11.0.7 or later with Java 16 or later, then you will be able to use UnixDomain sockets based on JEP 380.

    The UnixDomain implementation from Java 16 is very stable, so we have switched our own website to use it.
    The page that you are reading right now has been requested by your browser and processed on the server by Jetty using Jetty’s HttpClient to send the request via UnixDomain sockets to our local WordPress.

    We have therefore deprecated the old Jetty modules based on JNR in favor of the new Jetty modules based on JEP 380.

    Note that since UnixDomain sockets are an alternative to TCP network sockets, any TCP-based protocol can be carried via UnixDomain sockets: HTTP/1.1, HTTP/2 and FastCGI.

    We have improved the documentation to detail how to use the new APIs introduced to support JEP 380, for the client and for the server.
    If you are configuring Jetty behind a load balancer (or Apache HTTPD or Nginx) you can now use UnixDomain sockets to communicate from the load balancer to Jetty, as explained in this section of the documentation.

    Enjoy!

  • HTTP/2 with HAProxy and Jetty

    HTTP/2 is now the official RFC 7540, and it’s about time to deploy your website on HTTP/2, to get the numerous benefits that HTTP/2 brings.
    A very typical deployment is to have Apache (or Nginx) working as a reverse proxy to a Servlet Container such as Jetty or Tomcat.
    This configuration cannot be used for HTTP/2, because Apache does not support yet HTTP/2 (nor does Nginx).
    We want to propose an alternative deployment replacing Apache (or Nginx) with HAProxy, so that we can leverage Jetty’s 9.3.0 HTTP/2 support, and retain most if not all the features that Apache (or Nginx) were providing as reverse proxy.
    For those that don’t know HAProxy, it’s a very fast load balancer and proxy that powers quite a number of the world’s most visited sites, see here.
    What you will get is a very efficient TLS offloading (performed by HAProxy via OpenSSL), and Jetty HTTP/2 support, including HTTP/2 Push.
    The setup to make HAProxy + Jetty + HTTP/2 work is fairly simple, and documented in detail here.
    Don’t wait years to update your website to HTTP/2: whether you run a JEE web application, or a PHP application like WordPress, HAProxy and Jetty can speed up your website considerably, and many studies have shown that this results in more business.
    Browsers like Firefox and Chrome already support HTTP/2, so you will get more than half of the world potentially accessing your website with HTTP/2.
    Contact us if you want to know more about HTTP/2 and how we can help you to speed up your website.

  • Jetty HTTP/2 cleartext upgrade

    With the approach of the release candidate for Jetty 9.3.0 in the next days, we have implemented support for HTTP/2 cleartext upgrade mechanism, on server side, resolving issue #465857.
    This means that you can configure a Jetty server to speak cleartext HTTP/1.1 and cleartext HTTP/2 on the same server port.
    This feature is mostly useful for server data centers, where nodes communicate with each other via HTTP/2 using a Java client (for example Jetty’s HttpClient using the HTTP/2 transport) because you want to leverage the HTTP/2 protocol advantages, in particular multiplexing, for a more efficient communication.
    This scenario is typical for microservices deployed using embedded Jetty (just run them via java -jar my_microservice.jar) or, in general, for HTTP services (REST or similar) that reside on different nodes and that are coordinated by a façade service.
    In such scenario, the Java client knows before hand that the server port it is connecting to speaks HTTP/2, so the server needs to be configured to speak cleartext HTTP/2 on that port.
    However, it is also common during development/troubleshooting of REST services to point a browser to a particular node, craft the right URL with the expected path and/or query parameters, and obtain back the result of the processing (or the error) of your service request.
    But browsers don’t speak cleartext HTTP/2 (at the time of this blog, no browser is supporting cleartext HTTP/2, neither directly nor via the standard HTTP/1.1 upgrade mechanism to a different protocol, and there are no known plans for browsers to support this feature in the future), so they will speak HTTP/1.1 to a server port that is configured to speak HTTP/2.
    Before the implementation of issue #465857, this scenario resulted in a communication failure between the browser and the server.
    Sure, you can configure two different ports, one that speaks HTTP/2 for Java clients, and one that speaks HTTP/1.1 for browsers, but that is cumbersome.
    With the resolution of issue #465857, you can now configure Jetty to speak HTTP/1.1 and HTTP/2 on the same server port:

    public static void main(String[] args) throws Exception
    {
      // The Jetty Server.
      Server server = new Server();
      // Common HTTP configuration.
      HttpConfiguration config = new HttpConfiguration();
      // HTTP/1.1 support.
      HttpConnectionFactory http1 = new HttpConnectionFactory(config);
      // HTTP/2 cleartext support.
      HTTP2CServerConnectionFactory http2c = new HTTP2CServerConnectionFactory(config);
      ServerConnector connector = new ServerConnector(server, http1, http2c);
      connector.setPort(8080);
      server.addConnector(connector);
      // Here configure contexts / servlets / etc.
      server.start();
    }
    

    If a browser speaking HTTP/1.1 connects to the server, Jetty will speak HTTP/1.1.
    If a Java client speaking HTTP/2 connects to the server, Jetty will detect that and internally upgrade the connection from HTTP/1.1 to HTTP/2, so that the Java client will benefit of the HTTP/2 protocol advantages.
    Jetty also supports the standard HTTP/1.1 upgrade mechanism (on the server side, not yet on HttpClient), so that if you are using tools like nghttp you will be able to speak to a Jetty server either using directly HTTP/2, or by sending a HTTP/1.1 upgrade request to HTTP/2:

    # Direct HTTP/2
    $ nghttp -v http://localhost:8080/
    # Upgrade from HTTP/1.1 to HTTP/2
    $ nghttp -vu http://localhost:8080/
    

    If you are interested in how you can benefit from HTTP/2, contact Webtide, and you will have all our expertise at your hands.

  • CometD 3: RPC, PubSub, Peer-to-Peer Web Messaging

    A couple of months ago the CometD Project released its third major version, CometD 3.0.0 (announcement).
    Since then I wanted to write a blog about this major release, but work on HTTP 2 kept me busy.
    Today CometD 3.0.1 was released, so it’s time for a new CometD blog entry.
    CometD is an open source (Apache 2 licensed) project that started in 2008 under the umbrella of the Dojo Foundation, and already at the time it designed a web application messaging protocol named Bayeux.
    Similar efforts have started more recently, see for example WAMP.
    The Bayeux protocol is transport independent (works with HTTP and WebSocket) which allows CometD to easily fallback to HTTP when WebSocket, for any reason, does not work. Of course it works also with SPDY and HTTP 2.
    The Bayeux protocol supports two types of message delivery: the pubsub (broadcast) message delivery, and the peer-to-peer message delivery.
    In the pubsub style, a publisher sends a message to the server, which broadcast it to all clients that subscribed.
    In the peer-to-peer style, a sender sends a message to the server, which then decide what to do: it may broadcast the message to certain clients only, or to only one, or back to the original sender. This latter case allows for an implementation to offer RPC (remote procedure call) message delivery.
    Bayeux is also an extensible protocol, that allows to implement extensions on top of the protocol itself, so that implementations can add additional features such as time synchronization between client and server, and various grades of reliable message delivery (also called “quality of service” by MQTT).
    The CometD Project is an implementation of the Bayeux protocol, and over the years implemented not only the major features that the Bayeux protocol enables (pubsub messaging, peer-to-peer messaging, RPC messaging, message acknowledgment, etc.), but also additional features that make CometD the right choice if you have a web messaging use case.
    One such features is a clustering system, called Oort, that enables even more features such as tracking which node a client is connected to (Seti) and distributed objects and services.
    CometD 3.x leverages the standards for its implementation.
    In particular, it ships a HTTP transport based on the Servlet 3.1 Async I/O API, which makes CometD very scalable (no threads will be blocked by I/O operations).
    Furthermore, it ships a WebSocket transport based on the standard Java WebSocket API, also using asynchronous features, that make CometD even more scalable.
    If you have a web messaging use case, being it RPC style, pubsub style or peer-to-peer style, CometD is the one stop shop for you.
    Webtide provides commercial support for CometD, so you are not alone when using CometD. You don’t have to spend countless hours googling around in search of solutions when you can have the CometD committers helping you to design, deploy and scale your project.
    Contact us.

  • The new Jetty 9 HTTP client

    Introduction

    One of the big refactorings in Jetty 9 is the complete rewrite of the HTTP client.
    The reasons behind the rewrite are many:

    • We wrote the codebase several years ago; while we have actively maintained, it was starting to show its age.
    • The HTTP client guarded internal data structures from multithreaded access using the synchronized keyword, rather than using non-blocking data structures.
    • We exposed as main concept the HTTP exchange that, while representing correctly what an HTTP request/response cycle is,  did not match user expectations of a request and a response.
    • HTTP client did not have out of the box features such as authentication, redirect and cookie support.
    • Users somehow perceived the Jetty HTTP client as cumbersome to program.

    The rewrite takes into account many community inputs, requires JDK 7 to take advantage of the latest programming features, and is forward-looking because the new API is JDK 8 Lambda-ready (that is, you can use Jetty 9’s HTTP client with JDK 7 without Lambda, but if you use it in JDK 8 you can use lambda expressions to specify callbacks; see examples below).

    Programming with Jetty 9’s HTTP Client

    The main class is named, as in Jetty 7 and Jetty 8, org.eclipse.jetty.client.HttpClient (although it is not backward compatible with the same class in Jetty 7 and Jetty 8).
    You can think of an HttpClient instance as a browser instance.
    Like a browser, it can make requests to different domains, it manages redirects, cookies and authentications, you can configure it with a proxy, and it provides you with the responses to the requests you make.
    You need to configure an HttpClient instance and then start it:

    HttpClient httpClient = new HttpClient();
    // Configure HttpClient here
    httpClient.start();
    

    Simple GET requests require just  one line:

    ContentResponse response = httpClient
            .GET("http://domain.com/path?query")
            .get();
    

    Method HttpClient.GET(...) returns a Future<ContentResponse> that you can use to cancel the request or to impose a total timeout for the request/response conversation.
    Class ContentResponse represents a response with content; the content is limited by default to 2 MiB, but you can configure it to be larger.
    Simple POST requests also require just one line:

    ContentResponse response = httpClient
            .POST("http://domain.com/entity/1")
            .param("p", "value")
            .send()
            .get(5, TimeUnit.SECONDS);
    

    Jetty 9’s HttpClient automatically follows redirects, so automatically handles the typical web pattern POST/Redirect/GET, and the response object contains the content of the response of the GET request. Following redirects is a feature that you can enable/disable on a per-request basis or globally.
    File uploads also require one line, and make use of JDK 7’s java.nio.file classes:

    ContentResponse response = httpClient
            .newRequest("http://domain.com/entity/1")
            .file(Paths.get("file_to_upload.txt"))
            .send()
            .get(5, TimeUnit.SECONDS);
    

    Asynchronous Programming

    So far we have shown how to use HttpClient in a blocking style, that is the thread that issues the request blocks until the request/response conversation is complete. However, to unleash the full power of Jetty 9’s HttpClient you should look at its non-blocking (asynchronous) features.
    Jetty 9’s HttpClient fully supports the asynchronous programming style. You can write a simple GET request in this way:

    httpClient.newRequest("http://domain.com/path")
            .send(new Response.CompleteListener()
            {
                @Override
                public void onComplete(Result result)
                {
                    // Your logic here
                }
            });
    

    Method send(Response.CompleteListener) returns void and does not block; the Listener provided as a parameter is notified when the request/response conversation is complete, and the Result parameter  allows you to access the response object.
    You can write the same code using JDK 8’s lambda expressions:

    httpClient.newRequest("http://domain.com/path")
            .send((result) -> { /* Your logic here */ });
    

    HttpClient uses Listeners extensively to provide hooks for all possible request and response events, and with JDK 8’s lambda expressions they’re even more fun to use:

    httpClient.newRequest("http://domain.com/path")
            // Add request hooks
            .onRequestQueued((request) -> { ... })
            .onRequestBegin((request) -> { ... })
            // More request hooks available
            // Add response hooks
            .onResponseBegin((response) -> { ... })
            .onResponseHeaders((response) -> { ... })
            .onResponseContent((response, buffer) -> { ... })
            // More response hooks available
            .send((result) -> { ... });
    

    This makes Jetty 9’s HttpClient suitable for HTTP load testing because, for example, you can accurately time every step of the request/response conversation (thus knowing where the request/response time is really spent).

    Content Handling

    Jetty 9’s HTTP client provides a number of utility classes off the shelf to handle request content and response content.
    You can provide request content as String, byte[], ByteBuffer, java.nio.file.Path, InputStream, and provide your own implementation of ContentProvider. Here’s an example that provides the request content using an InputStream:

    httpClient.newRequest("http://domain.com/path")
            .content(new InputStreamContentProvider(
                getClass().getResourceAsStream("R.properties")))
            .send((result) -> { ... });
    

    HttpClient can handle Response content in different ways:
    The most common is via blocking calls that return a ContentResponse, as shown above.
    When using non-blocking calls, you can use a BufferingResponseListener in this way:

    httpClient.newRequest("http://domain.com/path")
            // Buffer response content up to 8 MiB
            .send(new BufferingResponseListener(8 * 1024 * 1024)
            {
                @Override
                public void onComplete(Result result)
                {
                    if (!result.isFailed())
                    {
                        byte[] responseContent = getContent();
                        // Your logic here
                    }
                }
            });
    

    To be efficient and avoid copying to a buffer the response content, you can use a Response.ContentListener, or a subclass:

    ContentResponse response = httpClient
            .newRequest("http://domain.com/path")
            .send(new Response.Listener.Empty()
            {
                @Override
                public void onContent(Response r, ByteBuffer b)
                {
                    // Your logic here
                }
            });
    

    To stream the response content, you can use InputStreamResponseListener in this way:

    InputStreamResponseListener listener =
            new InputStreamResponseListener();
    httpClient.newRequest("http://domain.com/path")
            .send(listener);
    // Wait for the response headers to arrive
    Response response = listener.get(5, TimeUnit.SECONDS);
    // Look at the response
    if (response.getStatus() == 200)
    {
        InputStream stream = listener.getInputStream();
        // Your logic here
    }
    

    Cookies Support

    HttpClient stores and accesses HTTP cookies through a CookieStore:

    Destination d = httpClient
            .getDestination("http", "domain.com", 80);
    CookieStore c = httpClient.getCookieStore();
    List cookies = c.findCookies(d, "/path");
    

    You can add cookies that you want to send along with your requests (if they match the domain and path and are not expired), and responses containing cookies automatically populate the cookie store, so that you can query it to find the cookies you are expecting with your responses.

    Authentication Support

    HttpClient suports HTTP Basic and Digest authentications, and other mechanisms are pluggable.
    You can configure authentication credentials in the HTTP client instance as follows:

    String uri = "http://domain.com/secure";
    String realm = "MyRealm";
    String u = "username";
    String p = "password";
    // Add authentication credentials
    AuthenticationStore a = httpClient.getAuthenticationStore();
    a.addAuthentication(
        new BasicAuthentication(uri, realm, u, p));
    ContentResponse response = httpClient
            .newRequest(uri)
            .send()
            .get(5, TimeUnit.SECONDS);
    

    HttpClient tests authentication credentials against the challenge(s) the server issues, and if they match it automatically sends the right authentication headers to the server for authentication. If the authentication is successful, it caches the result and reuses it for subsequent requests for the same domain and matching URIs.

    Proxy Support

    You can also configure HttpClient  with a proxy:

    httpClient.setProxyConfiguration(
        new ProxyConfiguration("proxyHost", proxyPort);
    ContentResponse response = httpClient
            .newRequest(uri)
            .send()
            .get(5, TimeUnit.SECONDS);
    

    Configured in this way, HttpClient makes requests to the proxy (for plain-text HTTP requests) or establishes a tunnel via HTTP CONNECT (for encrypted HTTPS requests).

    Conclusions

    The new Jetty 9  HTTP client is easier to use, has more features and it’s faster and better than Jetty 7’s or Jetty 8’s.
    The Jetty project continues to lead the way when it’s about the Web: years ago with Jetty Continuations, then with Jetty WebSocket, recently with Jetty SPDY and now with the first complete, ready to use, JDK 8’s Lambda -ready HTTP client.
    Go get it while it’s hot !
    Maven coordinates:

    
        org.eclipse.jetty
        jetty-client
        9.0.0.M3
    
    

    Direct Downloads:
    Main jar: jetty-client.jar
    Dependencies: jetty-http.jar, jetty-io.jar, jetty-util.jar