/* * Copyright (c) 2011-2013 The original author or authors * ------------------------------------------------------ * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * and Apache License v2.0 which accompanies this distribution. * * The Eclipse Public License is available at * http://www.eclipse.org/legal/epl-v10.html * * The Apache License v2.0 is available at * http://www.opensource.org/licenses/apache2.0.php * * You may elect to redistribute this code under either of these licenses. */ package io.vertx.core.http.impl; import io.vertx.core.MultiMap; import io.vertx.core.http.ServerWebSocket; import io.vertx.core.http.WebSocketFrame; import io.vertx.core.impl.VertxInternal; import io.vertx.core.net.impl.ConnectionBase; import javax.net.ssl.SSLPeerUnverifiedException; import javax.security.cert.X509Certificate; /** * This class is optimised for performance when used on the same event loop. However it can be used safely from other threads. * * The internal state is protected using the synchronized keyword. If always used on the same event loop, then * we benefit from biased locking which makes the overhead of synchronized near zero. * * @author <a href="http://tfox.org">Tim Fox</a> * */ public class ServerWebSocketImpl extends WebSocketImplBase<ServerWebSocket> implements ServerWebSocket { private final String uri; private final String path; private final String query; private final Runnable connectRunnable; private final MultiMap headers; private boolean connected; private boolean rejected; public ServerWebSocketImpl(VertxInternal vertx, String uri, String path, String query, MultiMap headers, ConnectionBase conn, boolean supportsContinuation, Runnable connectRunnable, int maxWebSocketFrameSize, int maxWebSocketMessageSize) { super(vertx, conn, supportsContinuation, maxWebSocketFrameSize, maxWebSocketMessageSize); this.uri = uri; this.path = path; this.query = query; this.headers = headers; this.connectRunnable = connectRunnable; } @Override public String uri() { return uri; } @Override public String path() { return path; } @Override public String query() { return query; } @Override public MultiMap headers() { return headers; } @Override public void reject() { synchronized (conn) { checkClosed(); if (connectRunnable == null) { throw new IllegalStateException("Cannot reject websocket on the client side"); } if (connected) { throw new IllegalStateException("Cannot reject websocket, it has already been written to"); } rejected = true; } } @Override public X509Certificate[] peerCertificateChain() throws SSLPeerUnverifiedException { return conn.peerCertificateChain(); } @Override public void close() { synchronized (conn) { checkClosed(); if (connectRunnable != null) { // Server side if (rejected) { throw new IllegalStateException("Cannot close websocket, it has been rejected"); } if (!connected && !closed) { connect(); } } super.close(); } } @Override public ServerWebSocket writeFrame(WebSocketFrame frame) { synchronized (conn) { if (connectRunnable != null) { if (rejected) { throw new IllegalStateException("Cannot write to websocket, it has been rejected"); } if (!connected && !closed) { connect(); } } return super.writeFrame(frame); } } private void connect() { connectRunnable.run(); connected = true; } // Connect if not already connected void connectNow() { synchronized (conn) { if (!connected && !rejected) { connect(); } } } boolean isRejected() { synchronized (conn) { return rejected; } } }