All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.eclipse.jetty.http2.client.HTTP2Client Maven / Gradle / Ivy

//
// ========================================================================
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//

package org.eclipse.jetty.http2.client;

import java.net.SocketAddress;
import java.nio.channels.SocketChannel;
import java.time.Duration;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;

import org.eclipse.jetty.alpn.client.ALPNClientConnectionFactory;
import org.eclipse.jetty.http2.BufferingFlowControlStrategy;
import org.eclipse.jetty.http2.FlowControlStrategy;
import org.eclipse.jetty.http2.api.Session;
import org.eclipse.jetty.http2.frames.Frame;
import org.eclipse.jetty.http2.frames.SettingsFrame;
import org.eclipse.jetty.http2.hpack.HpackContext;
import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.io.ClientConnectionFactory;
import org.eclipse.jetty.io.ClientConnector;
import org.eclipse.jetty.io.ssl.SslClientConnectionFactory;
import org.eclipse.jetty.util.Promise;
import org.eclipse.jetty.util.annotation.ManagedAttribute;
import org.eclipse.jetty.util.annotation.ManagedObject;
import org.eclipse.jetty.util.component.ContainerLifeCycle;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.util.thread.Scheduler;

/**
 * 

HTTP2Client provides an asynchronous, non-blocking implementation * to send HTTP/2 frames to a server.

*

Typical usage:

*
 * // Create and start HTTP2Client.
 * HTTP2Client client = new HTTP2Client();
 * client.start();
 * SslContextFactory sslContextFactory = client.getClientConnector().getSslContextFactory();
 *
 * // Connect to host.
 * String host = "webtide.com";
 * int port = 443;
 *
 * FuturePromise<Session> sessionPromise = new FuturePromise<>();
 * client.connect(sslContextFactory, new InetSocketAddress(host, port), new ServerSessionListener.Adapter(), sessionPromise);
 *
 * // Obtain the client Session object.
 * Session session = sessionPromise.get(5, TimeUnit.SECONDS);
 *
 * // Prepare the HTTP request headers.
 * HttpFields requestFields = new HttpFields();
 * requestFields.put("User-Agent", client.getClass().getName() + "/" + Jetty.VERSION);
 * // Prepare the HTTP request object.
 * MetaData.Request request = new MetaData.Request("PUT", HttpURI.from("https://" + host + ":" + port + "/"), HttpVersion.HTTP_2, requestFields);
 * // Create the HTTP/2 HEADERS frame representing the HTTP request.
 * HeadersFrame headersFrame = new HeadersFrame(request, null, false);
 *
 * // Prepare the listener to receive the HTTP response frames.
 * Stream.Listener responseListener = new new Stream.Listener.Adapter()
 * {
 *      @Override
 *      public void onHeaders(Stream stream, HeadersFrame frame)
 *      {
 *          System.err.println(frame);
 *      }
 *
 *      @Override
 *      public void onData(Stream stream, DataFrame frame, Callback callback)
 *      {
 *          System.err.println(frame);
 *          callback.succeeded();
 *      }
 * };
 *
 * // Send the HEADERS frame to create a stream.
 * FuturePromise<Stream> streamPromise = new FuturePromise<>();
 * session.newStream(headersFrame, streamPromise, responseListener);
 * Stream stream = streamPromise.get(5, TimeUnit.SECONDS);
 *
 * // Use the Stream object to send request content, if any, using a DATA frame.
 * ByteBuffer content = ...;
 * DataFrame requestContent = new DataFrame(stream.getId(), content, true);
 * stream.data(requestContent, Callback.NOOP);
 *
 * // When done, stop the client.
 * client.stop();
 * 
*/ @ManagedObject public class HTTP2Client extends ContainerLifeCycle { private final ClientConnector connector; private int inputBufferSize = 8192; private List protocols = List.of("h2"); private int initialSessionRecvWindow = 16 * 1024 * 1024; private int initialStreamRecvWindow = 8 * 1024 * 1024; private int maxFrameSize = Frame.DEFAULT_MAX_LENGTH; private int maxConcurrentPushedStreams = 32; private int maxSettingsKeys = SettingsFrame.DEFAULT_MAX_KEYS; private int maxDecoderTableCapacity = HpackContext.DEFAULT_MAX_TABLE_CAPACITY; private int maxEncoderTableCapacity = HpackContext.DEFAULT_MAX_TABLE_CAPACITY; private int maxHeaderBlockFragment = 0; private int maxResponseHeadersSize = 8 * 1024; private FlowControlStrategy.Factory flowControlStrategyFactory = () -> new BufferingFlowControlStrategy(0.5F); private long streamIdleTimeout; private boolean useInputDirectByteBuffers = true; private boolean useOutputDirectByteBuffers = true; private boolean isUseALPN = true; public HTTP2Client() { this(new ClientConnector()); } public HTTP2Client(ClientConnector connector) { this.connector = connector; addBean(connector); } public ClientConnector getClientConnector() { return connector; } public Executor getExecutor() { return connector.getExecutor(); } public void setExecutor(Executor executor) { connector.setExecutor(executor); } public Scheduler getScheduler() { return connector.getScheduler(); } public void setScheduler(Scheduler scheduler) { connector.setScheduler(scheduler); } public ByteBufferPool getByteBufferPool() { return connector.getByteBufferPool(); } public void setByteBufferPool(ByteBufferPool bufferPool) { connector.setByteBufferPool(bufferPool); } public FlowControlStrategy.Factory getFlowControlStrategyFactory() { return flowControlStrategyFactory; } public void setFlowControlStrategyFactory(FlowControlStrategy.Factory flowControlStrategyFactory) { this.flowControlStrategyFactory = flowControlStrategyFactory; } @ManagedAttribute("The number of selectors") public int getSelectors() { return connector.getSelectors(); } public void setSelectors(int selectors) { connector.setSelectors(selectors); } @ManagedAttribute("The idle timeout in milliseconds") public long getIdleTimeout() { return connector.getIdleTimeout().toMillis(); } public void setIdleTimeout(long idleTimeout) { connector.setIdleTimeout(Duration.ofMillis(idleTimeout)); } @ManagedAttribute("The stream idle timeout in milliseconds") public long getStreamIdleTimeout() { return streamIdleTimeout; } public void setStreamIdleTimeout(long streamIdleTimeout) { this.streamIdleTimeout = streamIdleTimeout; } @ManagedAttribute("The connect timeout in milliseconds") public long getConnectTimeout() { return connector.getConnectTimeout().toMillis(); } public void setConnectTimeout(long connectTimeout) { connector.setConnectTimeout(Duration.ofMillis(connectTimeout)); } @ManagedAttribute("Whether the connect() operation is blocking") public boolean isConnectBlocking() { return connector.isConnectBlocking(); } public void setConnectBlocking(boolean connectBlocking) { connector.setConnectBlocking(connectBlocking); } public SocketAddress getBindAddress() { return connector.getBindAddress(); } public void setBindAddress(SocketAddress bindAddress) { connector.setBindAddress(bindAddress); } @ManagedAttribute("The size of the buffer used to read from the network") public int getInputBufferSize() { return inputBufferSize; } public void setInputBufferSize(int inputBufferSize) { this.inputBufferSize = inputBufferSize; } @ManagedAttribute("The ALPN protocol list") public List getProtocols() { return protocols; } public void setProtocols(List protocols) { this.protocols = protocols; } @ManagedAttribute("The initial size of session's flow control receive window") public int getInitialSessionRecvWindow() { return initialSessionRecvWindow; } public void setInitialSessionRecvWindow(int initialSessionRecvWindow) { this.initialSessionRecvWindow = initialSessionRecvWindow; } @ManagedAttribute("The initial size of stream's flow control receive window") public int getInitialStreamRecvWindow() { return initialStreamRecvWindow; } public void setInitialStreamRecvWindow(int initialStreamRecvWindow) { this.initialStreamRecvWindow = initialStreamRecvWindow; } @Deprecated public int getMaxFrameLength() { return getMaxFrameSize(); } @Deprecated public void setMaxFrameLength(int maxFrameSize) { setMaxFrameSize(maxFrameSize); } @ManagedAttribute("The max frame size in bytes") public int getMaxFrameSize() { return maxFrameSize; } public void setMaxFrameSize(int maxFrameSize) { this.maxFrameSize = maxFrameSize; } @ManagedAttribute("The max number of concurrent pushed streams") public int getMaxConcurrentPushedStreams() { return maxConcurrentPushedStreams; } public void setMaxConcurrentPushedStreams(int maxConcurrentPushedStreams) { this.maxConcurrentPushedStreams = maxConcurrentPushedStreams; } @ManagedAttribute("The max number of keys in all SETTINGS frames") public int getMaxSettingsKeys() { return maxSettingsKeys; } public void setMaxSettingsKeys(int maxSettingsKeys) { this.maxSettingsKeys = maxSettingsKeys; } @ManagedAttribute("The HPACK encoder dynamic table maximum capacity") public int getMaxEncoderTableCapacity() { return maxEncoderTableCapacity; } /** *

Sets the limit for the encoder HPACK dynamic table capacity.

*

Setting this value to {@code 0} disables the use of the dynamic table.

* * @param maxEncoderTableCapacity The HPACK encoder dynamic table maximum capacity */ public void setMaxEncoderTableCapacity(int maxEncoderTableCapacity) { this.maxEncoderTableCapacity = maxEncoderTableCapacity; } @ManagedAttribute("The HPACK decoder dynamic table maximum capacity") public int getMaxDecoderTableCapacity() { return maxDecoderTableCapacity; } public void setMaxDecoderTableCapacity(int maxDecoderTableCapacity) { this.maxDecoderTableCapacity = maxDecoderTableCapacity; } @Deprecated public int getMaxDynamicTableSize() { return getMaxDecoderTableCapacity(); } @Deprecated public void setMaxDynamicTableSize(int maxTableSize) { setMaxDecoderTableCapacity(maxTableSize); } @ManagedAttribute("The max size of header block fragments") public int getMaxHeaderBlockFragment() { return maxHeaderBlockFragment; } public void setMaxHeaderBlockFragment(int maxHeaderBlockFragment) { this.maxHeaderBlockFragment = maxHeaderBlockFragment; } @ManagedAttribute("The max size of response headers") public int getMaxResponseHeadersSize() { return maxResponseHeadersSize; } public void setMaxResponseHeadersSize(int maxResponseHeadersSize) { this.maxResponseHeadersSize = maxResponseHeadersSize; } @ManagedAttribute("Whether to use direct ByteBuffers for reading") public boolean isUseInputDirectByteBuffers() { return useInputDirectByteBuffers; } public void setUseInputDirectByteBuffers(boolean useInputDirectByteBuffers) { this.useInputDirectByteBuffers = useInputDirectByteBuffers; } @ManagedAttribute("Whether to use direct ByteBuffers for writing") public boolean isUseOutputDirectByteBuffers() { return useOutputDirectByteBuffers; } public void setUseOutputDirectByteBuffers(boolean useOutputDirectByteBuffers) { this.useOutputDirectByteBuffers = useOutputDirectByteBuffers; } @ManagedAttribute(value = "Whether ALPN should be used when establishing connections") public boolean isUseALPN() { return isUseALPN; } public void setUseALPN(boolean useALPN) { isUseALPN = useALPN; } public CompletableFuture connect(SocketAddress address, Session.Listener listener) { return connect(null, address, listener); } public void connect(SocketAddress address, Session.Listener listener, Promise promise) { // Prior-knowledge clear-text HTTP/2 (h2c). connect(null, address, listener, promise); } public CompletableFuture connect(SslContextFactory sslContextFactory, SocketAddress address, Session.Listener listener) { Promise.Completable result = new Promise.Completable<>(); connect(sslContextFactory, address, listener, result); return result; } public void connect(SslContextFactory sslContextFactory, SocketAddress address, Session.Listener listener, Promise promise) { connect(sslContextFactory, address, listener, promise, null); } public void connect(SslContextFactory sslContextFactory, SocketAddress address, Session.Listener listener, Promise promise, Map context) { ClientConnectionFactory factory = newClientConnectionFactory(sslContextFactory); connect(address, factory, listener, promise, context); } public void connect(SocketAddress address, ClientConnectionFactory factory, Session.Listener listener, Promise promise, Map context) { context = contextFrom(factory, listener, promise, context); context.put(ClientConnector.CONNECTION_PROMISE_CONTEXT_KEY, Promise.from(ioConnection -> {}, promise::failed)); connector.connect(address, context); } public void accept(SslContextFactory sslContextFactory, SocketChannel channel, Session.Listener listener, Promise promise) { ClientConnectionFactory factory = newClientConnectionFactory(sslContextFactory); accept(channel, factory, listener, promise); } public void accept(SocketChannel channel, ClientConnectionFactory factory, Session.Listener listener, Promise promise) { Map context = contextFrom(factory, listener, promise, null); context.put(ClientConnector.CONNECTION_PROMISE_CONTEXT_KEY, Promise.from(ioConnection -> {}, promise::failed)); connector.accept(channel, context); } private Map contextFrom(ClientConnectionFactory factory, Session.Listener listener, Promise promise, Map context) { if (context == null) context = new ConcurrentHashMap<>(); context.put(HTTP2ClientConnectionFactory.CLIENT_CONTEXT_KEY, this); context.put(HTTP2ClientConnectionFactory.SESSION_LISTENER_CONTEXT_KEY, listener); context.put(HTTP2ClientConnectionFactory.SESSION_PROMISE_CONTEXT_KEY, promise); context.put(ClientConnector.CLIENT_CONNECTION_FACTORY_CONTEXT_KEY, factory); return context; } private ClientConnectionFactory newClientConnectionFactory(SslContextFactory sslContextFactory) { ClientConnectionFactory factory = new HTTP2ClientConnectionFactory(); if (sslContextFactory != null) { if (isUseALPN()) factory = new ALPNClientConnectionFactory(getExecutor(), factory, getProtocols()); factory = new SslClientConnectionFactory(sslContextFactory, getByteBufferPool(), getExecutor(), factory); } return factory; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy