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

org.glassfish.tyrus.servlet.TyrusHttpUpgradeHandler Maven / Gradle / Ivy

There is a newer version: 2.2.0
Show newest version
/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 2012-2014 Oracle and/or its affiliates. All rights reserved.
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License.  You can
 * obtain a copy of the License at
 * http://glassfish.java.net/public/CDDL+GPL_1_1.html
 * or packager/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 *
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at packager/legal/LICENSE.txt.
 *
 * GPL Classpath Exception:
 * Oracle designates this particular file as subject to the "Classpath"
 * exception as provided by Oracle in the GPL Version 2 section of the License
 * file that accompanied this code.
 *
 * Modifications:
 * If applicable, add the following below the License Header, with the fields
 * enclosed by brackets [] replaced by your own identifying information:
 * "Portions Copyright [year] [name of copyright owner]"
 *
 * Contributor(s):
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */

package org.glassfish.tyrus.servlet;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.concurrent.CountDownLatch;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.websocket.CloseReason;

import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpUpgradeHandler;
import javax.servlet.http.WebConnection;

import org.glassfish.tyrus.core.CloseReasons;
import org.glassfish.tyrus.spi.Connection;
import org.glassfish.tyrus.spi.WebSocketEngine;
import org.glassfish.tyrus.spi.Writer;

/**
 * {@link HttpUpgradeHandler} and {@link ReadListener} implementation.
 * 

* Reads data from {@link ServletInputStream} and passes it further to the * Tyrus runtime. * * @author Jitendra Kotamraju * @author Pavel Bucek (pavel.bucek at oracle.com) */ public class TyrusHttpUpgradeHandler implements HttpUpgradeHandler, ReadListener { public static final String FRAME_BUFFER_SIZE = "org.glassfish.tyrus.servlet.incoming-buffer-size"; private final CountDownLatch connectionLatch = new CountDownLatch(1); private ServletInputStream is; private ServletOutputStream os; private WebConnection wc; private ByteBuffer buf; private volatile boolean closed = false; private int incomingBufferSize = 4194315; // 4M (payload) + 11 (frame overhead) private static final Logger LOGGER = Logger.getLogger(TyrusHttpUpgradeHandler.class.getName()); private Connection connection; private WebSocketEngine.UpgradeInfo upgradeInfo; private Writer writer; private boolean authenticated = false; @Override public void init(WebConnection wc) { LOGGER.config("Servlet 3.1 Upgrade"); try { is = wc.getInputStream(); os = wc.getOutputStream(); this.wc = wc; } catch (IOException ioe) { throw new RuntimeException(ioe); } try { is.setReadListener(this); } catch (IllegalStateException e) { LOGGER.log(Level.WARNING, e.getMessage(), e); } connection = upgradeInfo.createConnection(writer, new Connection.CloseListener() { @Override public void close(CloseReason reason) { try { TyrusHttpUpgradeHandler.this.getWebConnection().close(); } catch (Exception e) { LOGGER.log(Level.FINE, e.getMessage(), e); } } }); connectionLatch.countDown(); } public void preInit(WebSocketEngine.UpgradeInfo upgradeInfo, Writer writer, boolean authenticated) { this.upgradeInfo = upgradeInfo; this.writer = writer; this.authenticated = authenticated; } @Override public void onDataAvailable() { try { connectionLatch.await(); } catch (InterruptedException e) { // do nothing. } do { try { int available = is.available(); if (available == 0) { // tomcat impl returns always 0 available = 16384; } int toRead = (buf == null ? (available > incomingBufferSize ? incomingBufferSize : available) : buf.remaining() + available > incomingBufferSize ? incomingBufferSize - buf.remaining() : buf.remaining() + available ); if (toRead == 0) { throw new IOException(String.format("Tyrus input buffer exceeded. Current buffer size is %s bytes.", incomingBufferSize)); } available -= fillBuf(toRead); if (buf != null) { LOGGER.finest(String.format("Remaining Data = %d", buf.remaining())); if (buf.hasRemaining()) { connection.getReadHandler().handle(buf); } } } catch (IOException e) { connection.close(CloseReasons.CANNOT_ACCEPT.getCloseReason()); } } while (!closed && is.isReady()); } /** * Fill the buf with some more websocket protocol data. * * @param length length of data available to read. * @return legth of actually read data. * @throws IOException if some other I/O error occurs. */ private int fillBuf(int length) throws IOException { byte[] data = new byte[length]; int len = is.read(data); if (len == 0) { return 0; } if (buf == null) { LOGGER.finest("No Buffer. Allocating new one"); buf = ByteBuffer.wrap(data); buf.limit(len); } else { int limit = buf.limit(); int capacity = buf.capacity(); int remaining = buf.remaining(); if (capacity - limit >= len) { // Remaining data need not be changed. New data is just appended LOGGER.finest("Remaining data need not be moved. New data is just appended"); buf.mark(); buf.position(limit); buf.limit(capacity); buf.put(data, 0, len); buf.limit(limit + len); buf.reset(); } else if (remaining + len < capacity) { // Remaining data is moved to left. Then new data is appended LOGGER.finest("Remaining data is moved to left. Then new data is appended"); buf.compact(); buf.put(data, 0, len); buf.flip(); } else { // Remaining data + new > capacity. So allocate new one LOGGER.finest("Remaining data + new > capacity. So allocate new one"); byte[] array = new byte[remaining + len]; buf.get(array, 0, remaining); System.arraycopy(data, 0, array, remaining, len); buf = ByteBuffer.wrap(array); buf.limit(remaining + len); } } return len; } @Override public void onAllDataRead() { close(CloseReason.CloseCodes.NORMAL_CLOSURE.getCode(), null); } @Override public void onError(Throwable t) { close(CloseReason.CloseCodes.CLOSED_ABNORMALLY.getCode(), t.getMessage() == null ? "No reason given." : t.getMessage()); } @Override public void destroy() { close(CloseReason.CloseCodes.CLOSED_ABNORMALLY.getCode(), "No reason given."); } /** * Called when related {@link javax.servlet.http.HttpSession} is destroyed or invalidated. *

* Implementation is required to call onClose() on server-side with corresponding close code (1008, see * WebSocket spec 7.2) - only when there is an authorized user for this session. */ public void sessionDestroyed() { if (authenticated) { // websocket spec 7.2 [WSC-7.2-3] httpSessionForcedClose(CloseReason.CloseCodes.VIOLATED_POLICY.getCode(), "No reason given."); } // else do nothing. } @Override public String toString() { final StringBuilder sb = new StringBuilder("TyrusHttpUpgradeHandler{"); sb.append("is=").append(is); sb.append(", os=").append(os); sb.append(", wc=").append(wc); sb.append(", closed=").append(closed); sb.append('}'); return sb.toString(); } public void setIncomingBufferSize(int incomingBufferSize) { this.incomingBufferSize = incomingBufferSize; } private void httpSessionForcedClose(int closeCode, String closeReason) { if (!closed) { try { // TODO // initiates connection close without sending close frame to the client - session is already invalidated // so we should not send anything. // ((TyrusWebSocket) ((TyrusWebSocketEngine) engine).getWebSocketHolder(writer).webSocket).setClosed(); connection.close(new CloseReason(CloseReason.CloseCodes.getCloseCode(closeCode), closeReason)); closed = true; wc.close(); } catch (Exception e) { LOGGER.log(Level.CONFIG, e.getMessage(), e); } } } private void close(int closeCode, String closeReason) { if (!closed) { try { connection.close(new CloseReason(CloseReason.CloseCodes.getCloseCode(closeCode), closeReason)); closed = true; wc.close(); } catch (Exception e) { LOGGER.log(Level.CONFIG, e.getMessage(), e); } } } WebConnection getWebConnection() { if (wc == null) { throw new IllegalStateException(); } return wc; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy