/* * FrameCollector.java February 2014 * * Copyright (C) 2014, Niall Gallagher <niallg@users.sf.net> * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. See the License for the specific language governing * permissions and limitations under the License. */ package org.simpleframework.http.socket.service; import static org.simpleframework.http.socket.service.ServiceEvent.ERROR; import java.nio.channels.SelectableChannel; import java.nio.channels.SelectionKey; import org.simpleframework.http.Request; import org.simpleframework.http.socket.FrameListener; import org.simpleframework.http.socket.Session; import org.simpleframework.transport.Channel; import org.simpleframework.transport.ByteCursor; import org.simpleframework.transport.reactor.Operation; import org.simpleframework.transport.reactor.Reactor; import org.simpleframework.transport.trace.Trace; /** * The <code>FrameCollector</code> operation is used to collect frames * from a channel and dispatch them to a <code>FrameListener</code>. * To ensure that stale connections do not linger any connection that * does not send a control ping or pong frame within two minutes will * be terminated and the close control frame will be sent. * * @author Niall Gallagher */ class FrameCollector implements Operation { /** * This decodes the frame bytes from the channel and processes it. */ private final FrameProcessor processor; /** * This is the cursor used to maintain a stream seek position. */ private final ByteCursor cursor; /** * This is the underlying channel for this frame collector. */ private final Channel channel; /** * This is the reactor used to schedule this operation for reads. */ private final Reactor reactor; /** * This is the tracer that is used to trace the frame collection. */ private final Trace trace; /** * Constructor for the <code>FrameCollector</code> object. This is * used to create a collector that will process and dispatch web * socket frames as defined by RFC 6455. * * @param encoder this is the encoder used to send messages * @param session this is the web socket session * @param channel this is the underlying TCP communication channel * @param reactor this is the reactor used for read notifications */ public FrameCollector(FrameEncoder encoder, Session session, Request request, Reactor reactor) { this.processor = new FrameProcessor(encoder, session, request); this.channel = request.getChannel(); this.cursor = channel.getCursor(); this.trace = channel.getTrace(); this.reactor = reactor; } /** * This is used to acquire the trace object that is associated * with the operation. A trace object is used to collection details * on what operations are being performed. For instance it may * contain information relating to I/O events or errors. * * @return this returns the trace associated with this operation */ public Trace getTrace() { return trace; } /** * This is the channel associated with this collector. This is used * to register for notification of read events. If at any time the * remote endpoint is closed then this will cause the collector * to perform a final execution before closing. * * @return this returns the selectable TCP channel */ public SelectableChannel getChannel() { return channel.getSocket(); } /** * This is used to register a <code>FrameListener</code> to this * instance. The registered listener will receive all user frames * and control frames sent from the client. Also, when the frame * is closed or when an unexpected error occurs the listener is * notified. Any number of listeners can be registered at any time. * * @param listener this is the listener that is to be registered */ public void register(FrameListener listener) { processor.register(listener); } /** * This is used to remove a <code>FrameListener</code> from this * instance. After removal the listener will no longer receive * any user frames or control messages from this specific instance. * * @param listener this is the listener to be removed */ public void remove(FrameListener listener) { processor.remove(listener); } /** * This is used to execute the collection operation. Collection is * done by reading the frame header from the incoming data, once * consumed the remainder of the frame is collected until such * time as it has been fully consumed. When consumed it will be * dispatched to the registered frame listeners. */ public void run() { try { processor.process(); if(cursor.isOpen()) { reactor.process(this, SelectionKey.OP_READ); } else { processor.close(); } } catch(Exception cause) { trace.trace(ERROR, cause); try { processor.failure(cause); } catch(Exception fatal) { trace.trace(ERROR, fatal); } finally { channel.close(); } } } /** * This is called when a read operation has timed out. To ensure * that stale channels do not remain registered they are cleared * out with this method and a close frame is sent if possible. */ public void cancel() { try{ processor.close(); } catch(Exception cause) { trace.trace(ERROR, cause); channel.close(); } } }