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

org.eclipse.jetty.websocket.client.WebSocketClient 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.websocket.client;

import java.io.IOException;
import java.net.CookieStore;
import java.net.SocketAddress;
import java.net.URI;
import java.time.Duration;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;

import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.HttpRequest;
import org.eclipse.jetty.client.HttpResponse;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.io.Connection;
import org.eclipse.jetty.util.DecoratedObjectFactory;
import org.eclipse.jetty.util.component.ContainerLifeCycle;
import org.eclipse.jetty.util.component.Graceful;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.util.thread.ShutdownThread;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.WebSocketBehavior;
import org.eclipse.jetty.websocket.api.WebSocketContainer;
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
import org.eclipse.jetty.websocket.api.WebSocketSessionListener;
import org.eclipse.jetty.websocket.client.impl.JettyClientUpgradeRequest;
import org.eclipse.jetty.websocket.common.JettyWebSocketFrameHandler;
import org.eclipse.jetty.websocket.common.JettyWebSocketFrameHandlerFactory;
import org.eclipse.jetty.websocket.common.SessionTracker;
import org.eclipse.jetty.websocket.core.Configuration;
import org.eclipse.jetty.websocket.core.CoreSession;
import org.eclipse.jetty.websocket.core.client.UpgradeListener;
import org.eclipse.jetty.websocket.core.client.WebSocketCoreClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class WebSocketClient extends ContainerLifeCycle implements WebSocketPolicy, WebSocketContainer
{
    private static final Logger LOG = LoggerFactory.getLogger(WebSocketClient.class);
    private final WebSocketCoreClient coreClient;
    private final int id = ThreadLocalRandom.current().nextInt();
    private final JettyWebSocketFrameHandlerFactory frameHandlerFactory;
    private final List sessionListeners = new CopyOnWriteArrayList<>();
    private final SessionTracker sessionTracker = new SessionTracker();
    private final Configuration.ConfigurationCustomizer configurationCustomizer = new Configuration.ConfigurationCustomizer();
    private boolean stopAtShutdown = false;
    private long _stopTimeout = Long.MAX_VALUE;

    /**
     * Instantiates a WebSocketClient with a default {@link HttpClient}.
     */
    public WebSocketClient()
    {
        this(null);
    }

    /**
     * 

Instantiates a WebSocketClient with the given {@link HttpClient}.

* * @param httpClient the HttpClient to use */ public WebSocketClient(HttpClient httpClient) { coreClient = new WebSocketCoreClient(httpClient, null); addManaged(coreClient); frameHandlerFactory = new JettyWebSocketFrameHandlerFactory(this, coreClient.getWebSocketComponents()); sessionListeners.add(sessionTracker); addBean(sessionTracker); } public CompletableFuture connect(Object websocket, URI toUri) throws IOException { return connect(websocket, toUri, null); } /** * Connect to remote websocket endpoint * * @param websocket the websocket object * @param toUri the websocket uri to connect to * @param request the upgrade request information * @return the future for the session, available on success of connect * @throws IOException if unable to connect */ public CompletableFuture connect(Object websocket, URI toUri, ClientUpgradeRequest request) throws IOException { return connect(websocket, toUri, request, null); } /** * Connect to remote websocket endpoint * * @param websocket the websocket object * @param toUri the websocket uri to connect to * @param request the upgrade request information * @param upgradeListener the upgrade listener * @return the future for the session, available on success of connect * @throws IOException if unable to connect */ public CompletableFuture connect(Object websocket, URI toUri, ClientUpgradeRequest request, JettyUpgradeListener upgradeListener) throws IOException { for (Connection.Listener listener : getBeans(Connection.Listener.class)) { coreClient.addBean(listener); } JettyClientUpgradeRequest upgradeRequest = new JettyClientUpgradeRequest(coreClient, request, toUri, frameHandlerFactory, websocket); upgradeRequest.setConfiguration(configurationCustomizer); for (Request.Listener l : getBeans(Request.Listener.class)) { upgradeRequest.listener(l); } if (upgradeListener != null) { upgradeRequest.addListener(new UpgradeListener() { @Override public void onHandshakeRequest(HttpRequest request) { upgradeListener.onHandshakeRequest(request); } @Override public void onHandshakeResponse(HttpRequest request, HttpResponse response) { upgradeListener.onHandshakeResponse(request, response); } }); } CompletableFuture futureSession = new CompletableFuture<>(); CompletableFuture coreConnect = coreClient.connect(upgradeRequest); coreConnect.whenComplete((coreSession, error) -> { if (error != null) { futureSession.completeExceptionally(JettyWebSocketFrameHandler.convertCause(error)); return; } JettyWebSocketFrameHandler frameHandler = (JettyWebSocketFrameHandler)upgradeRequest.getFrameHandler(); futureSession.complete(frameHandler.getSession()); }); // If the returned future is cancelled we want to try to cancel the core future if possible. futureSession.whenComplete((session, throwable) -> { if (throwable != null) coreConnect.completeExceptionally(throwable); }); return futureSession; } @Override public void dump(Appendable out, String indent) throws IOException { dumpObjects(out, indent, getOpenSessions()); } @Override public WebSocketBehavior getBehavior() { return WebSocketBehavior.CLIENT; } @Override public void addSessionListener(WebSocketSessionListener listener) { sessionListeners.add(listener); } @Override public boolean removeSessionListener(WebSocketSessionListener listener) { return sessionListeners.remove(listener); } @Override public void notifySessionListeners(Consumer consumer) { for (WebSocketSessionListener listener : sessionListeners) { try { consumer.accept(listener); } catch (Throwable x) { LOG.info("Exception while invoking listener {}", listener, x); } } } @Override public Duration getIdleTimeout() { return configurationCustomizer.getIdleTimeout(); } @Override public int getInputBufferSize() { return configurationCustomizer.getInputBufferSize(); } @Override public int getOutputBufferSize() { return configurationCustomizer.getOutputBufferSize(); } @Override public long getMaxBinaryMessageSize() { return configurationCustomizer.getMaxBinaryMessageSize(); } @Override public long getMaxTextMessageSize() { return configurationCustomizer.getMaxTextMessageSize(); } @Override public long getMaxFrameSize() { return configurationCustomizer.getMaxFrameSize(); } @Override public boolean isAutoFragment() { return configurationCustomizer.isAutoFragment(); } @Override public void setIdleTimeout(Duration duration) { configurationCustomizer.setIdleTimeout(duration); getHttpClient().setIdleTimeout(duration.toMillis()); } @Override public void setInputBufferSize(int size) { configurationCustomizer.setInputBufferSize(size); } @Override public void setOutputBufferSize(int size) { configurationCustomizer.setOutputBufferSize(size); } @Override public void setMaxBinaryMessageSize(long size) { configurationCustomizer.setMaxBinaryMessageSize(size); } @Override public void setMaxTextMessageSize(long size) { configurationCustomizer.setMaxTextMessageSize(size); } @Override public void setMaxFrameSize(long maxFrameSize) { configurationCustomizer.setMaxFrameSize(maxFrameSize); } @Override public void setAutoFragment(boolean autoFragment) { configurationCustomizer.setAutoFragment(autoFragment); } public SocketAddress getBindAddress() { return getHttpClient().getBindAddress(); } public void setBindAddress(SocketAddress bindAddress) { getHttpClient().setBindAddress(bindAddress); } public long getConnectTimeout() { return getHttpClient().getConnectTimeout(); } /** * Set the timeout for connecting to the remote server. * * @param ms the timeout in milliseconds */ public void setConnectTimeout(long ms) { getHttpClient().setConnectTimeout(ms); } public CookieStore getCookieStore() { return getHttpClient().getCookieStore(); } public void setCookieStore(CookieStore cookieStore) { getHttpClient().setCookieStore(cookieStore); } public ByteBufferPool getBufferPool() { return getHttpClient().getByteBufferPool(); } @Override public Executor getExecutor() { return getHttpClient().getExecutor(); } public HttpClient getHttpClient() { return coreClient.getHttpClient(); } public DecoratedObjectFactory getObjectFactory() { return coreClient.getObjectFactory(); } public Collection getOpenSessions() { return sessionTracker.getSessions(); } /** * @return the {@link SslContextFactory} that manages TLS encryption */ public SslContextFactory getSslContextFactory() { return getHttpClient().getSslContextFactory(); } /** * Set JVM shutdown behavior. * * @param stop If true, this client instance will be explicitly stopped when the * JVM is shutdown. Otherwise the application is responsible for maintaining the WebSocketClient lifecycle. * @see Runtime#addShutdownHook(Thread) * @see ShutdownThread */ public void setStopAtShutdown(boolean stop) { if (stop) { if (!stopAtShutdown && !ShutdownThread.isRegistered(this)) ShutdownThread.register(this); } else ShutdownThread.deregister(this); stopAtShutdown = stop; } /** * The timeout to allow all remaining open Sessions to be closed gracefully using the close code {@link org.eclipse.jetty.websocket.api.StatusCode#SHUTDOWN}. * * @param stopTimeout the time in ms to wait for the graceful close, use a value less than or equal to 0 to not gracefully close. */ public void setStopTimeout(long stopTimeout) { _stopTimeout = stopTimeout; } public long getStopTimeout() { return _stopTimeout; } public boolean isStopAtShutdown() { return stopAtShutdown; } @Override protected void doStop() throws Exception { if (getStopTimeout() > 0) Graceful.shutdown(this).get(getStopTimeout(), TimeUnit.MILLISECONDS); super.doStop(); } @Override public String toString() { final StringBuilder sb = new StringBuilder("WebSocketClient@"); sb.append(Integer.toHexString(id)); sb.append("[coreClient=").append(coreClient); sb.append(",openSessions.size="); sb.append(getOpenSessions().size()); sb.append(']'); return sb.toString(); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy