/*
* FrameConnection.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.CloseCode.NORMAL_CLOSURE;
import static org.simpleframework.http.socket.service.ServiceEvent.OPEN_SOCKET;
import java.io.IOException;
import org.simpleframework.http.Request;
import org.simpleframework.http.Response;
import org.simpleframework.http.socket.Frame;
import org.simpleframework.http.socket.FrameListener;
import org.simpleframework.http.socket.Reason;
import org.simpleframework.http.socket.Session;
import org.simpleframework.http.socket.FrameChannel;
import org.simpleframework.transport.Channel;
import org.simpleframework.transport.ByteWriter;
import org.simpleframework.transport.reactor.Reactor;
import org.simpleframework.transport.trace.Trace;
/**
* The <code>FrameConnection</code> represents a connection that can
* send and receivd WebSocket frames. Any instance of this will provide
* a means to perform asynchronous writes and reads to a remote client
* using a lightweight framing protocol. A frame is a finite length
* sequence of bytes that can hold either text or binary data. Also,
* control frames are used to perform heartbeat monitoring and closure.
* <p>
* For convenience frames can be consumed from the socket via a
* callback to a registered listener. This avoids having to poll each
* socket for data and provides a asynchronous event driven model of
* communication, which greatly reduces overhead and complication.
*
* @author Niall Gallagher
*/
class FrameConnection implements FrameChannel {
/**
* The collector is used to collect frames from the TCP channel.
*/
private final FrameCollector operation;
/**
* This encoder is used to encode data as RFC 6455 frames.
*/
private final FrameEncoder encoder;
/**
* This is the sender used to send frames over the channel.
*/
private final ByteWriter writer;
/**
* This is the session object that has a synchronized channel.
*/
private final Session session;
/**
* This is the underlying TCP channel that frames are sent over.
*/
private final Channel channel;
/**
* The reason that is sent if at any time the channel is closed.
*/
private final Reason reason;
/**
* This is used to trace all events that occur on the channel.
*/
private final Trace trace;
/**
* Constructor for the <code>FrameConnection</code> object. This is used
* to create a channel that can read and write frames over a TCP
* channel. For asynchronous read and dispatch operations this will
* produce an operation to collect and process RFC 6455 frames.
*
* @param request this is the initiating request for the WebSocket
* @param response this is the initiating response for the WebSocket
* @param reactor this is the reactor used to process frames
*/
public FrameConnection(Request request, Response response, Reactor reactor) {
this.encoder = new FrameEncoder(request);
this.session = new ServiceSession(this, request, response);
this.operation = new FrameCollector(encoder, session, request, reactor);
this.reason = new Reason(NORMAL_CLOSURE);
this.channel = request.getChannel();
this.writer = channel.getWriter();
this.trace = channel.getTrace();
}
/**
* This is used to open the channel and begin consuming frames. This
* will also return the session that contains the details for the
* created WebSocket such as the initiating request and response as
* well as the <code>FrameChannel</code> object.
*
* @return the session associated with the WebSocket
*/
public Session open() throws IOException {
trace.trace(OPEN_SOCKET);
operation.run();
return session;
}
/**
* 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) throws IOException {
operation.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) throws IOException {
operation.remove(listener);
}
/**
* This is used to send data to the connected client. To prevent
* an application code from causing resource issues this will block
* as soon as a configured linked list of mapped memory buffers has
* been exhausted. Caution should be taken when writing a broadcast
* implementation that can write to multiple sockets as a badly
* behaving socket that has filled its output buffering capacity
* can cause congestion.
*
* @param data this is the data that is to be sent
*/
public void send(byte[] data) throws IOException {
encoder.encode(data);
}
/**
* This is used to send text to the connected client. To prevent
* an application code from causing resource issues this will block
* as soon as a configured linked list of mapped memory buffers has
* been exhausted. Caution should be taken when writing a broadcast
* implementation that can write to multiple sockets as a badly
* behaving socket that has filled its output buffering capacity
* can cause congestion.
*
* @param text this is the text that is to be sent
*/
public void send(String text) throws IOException {
encoder.encode(text);
}
/**
* This is used to send data to the connected client. To prevent
* an application code from causing resource issues this will block
* as soon as a configured linked list of mapped memory buffers has
* been exhausted. Caution should be taken when writing a broadcast
* implementation that can write to multiple sockets as a badly
* behaving socket that has filled its output buffering capacity
* can cause congestion.
*
* @param frame this is the frame that is to be sent
*/
public void send(Frame frame) throws IOException {
encoder.encode(frame);
}
/**
* This is used to close the connection with a specific reason.
* The close reason will be sent as a control frame before the
* TCP connection is terminated.
*
* @param reason the reason for closing the connection
*/
public void close(Reason reason) throws IOException {
encoder.encode(reason);
writer.close();
}
/**
* This is used to close the connection without a specific reason.
* The close reason will be sent as a control frame before the
* TCP connection is terminated.
*/
public void close() throws IOException {
encoder.encode(reason);
writer.close();
}
}