Package com.corundumstudio.socketio

Source Code of com.corundumstudio.socketio.SocketIOEncoder$XHRClientEntry

/**
* Copyright 2012 Nikita Koksharov
*
* 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 com.corundumstudio.socketio;

import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.CONNECTION;
import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.CONTENT_TYPE;
import static org.jboss.netty.handler.codec.http.HttpHeaders.Values.KEEP_ALIVE;
import static org.jboss.netty.handler.codec.http.HttpVersion.HTTP_1_1;

import java.io.IOException;
import java.util.Queue;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicInteger;

import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelFuture;
import org.jboss.netty.channel.ChannelFutureListener;
import org.jboss.netty.channel.ChannelHandler.Sharable;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelDownstreamHandler;
import org.jboss.netty.handler.codec.http.DefaultHttpResponse;
import org.jboss.netty.handler.codec.http.HttpHeaders;
import org.jboss.netty.handler.codec.http.HttpResponse;
import org.jboss.netty.handler.codec.http.HttpResponseStatus;
import org.jboss.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import org.jboss.netty.handler.codec.http.websocketx.WebSocketFrame;
import org.jboss.netty.util.CharsetUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.corundumstudio.socketio.messages.AuthorizeMessage;
import com.corundumstudio.socketio.messages.BaseMessage;
import com.corundumstudio.socketio.messages.WebSocketPacketMessage;
import com.corundumstudio.socketio.messages.WebsocketErrorMessage;
import com.corundumstudio.socketio.messages.XHRErrorMessage;
import com.corundumstudio.socketio.messages.XHRNewChannelMessage;
import com.corundumstudio.socketio.messages.XHRPacketMessage;
import com.corundumstudio.socketio.messages.XHROutMessage;
import com.corundumstudio.socketio.parser.Encoder;
import com.corundumstudio.socketio.parser.Packet;

@Sharable
public class SocketIOEncoder extends SimpleChannelDownstreamHandler implements MessageHandler {

    class XHRClientEntry {

        // AtomicInteger works faster than locking
        final AtomicInteger lastChannelId = new AtomicInteger();
        final Queue<Packet> packets = new ConcurrentLinkedQueue<Packet>();

        public void addPacket(Packet packet) {
            packets.add(packet);
        }

        public Queue<Packet> getPackets() {
            return packets;
        }

        /**
         * We can write to channel only once.
         *
         * @param channel
         * @return true - can write
         */
        public boolean tryToWrite(Channel channel) {
            int prevVal = lastChannelId.get();
            return prevVal != channel.getId()
                            && lastChannelId.compareAndSet(prevVal, channel.getId());
        }

    }

    private final Logger log = LoggerFactory.getLogger(getClass());

    private final ConcurrentMap<UUID, XHRClientEntry> sessionId2ActiveChannelId = new ConcurrentHashMap<UUID, XHRClientEntry>();
    private final Encoder encoder;

    public SocketIOEncoder(Encoder encoder) {
        this.encoder = encoder;
    }

    private XHRClientEntry getXHRClientEntry(Channel channel, UUID sessionId) {
        XHRClientEntry clientEntry = sessionId2ActiveChannelId.get(sessionId);
        if (clientEntry == null) {
            clientEntry = new XHRClientEntry();
            XHRClientEntry old = sessionId2ActiveChannelId.putIfAbsent(sessionId, clientEntry);
            if (old != null) {
                clientEntry = old;
            }
        }
        return clientEntry;
    }

    private void write(UUID sessionId, String origin, XHRClientEntry clientEntry,
            Channel channel) throws IOException {
        if (!channel.isConnected() || clientEntry.getPackets().isEmpty()
                    || !clientEntry.tryToWrite(channel)) {
            return;
        }

        ChannelBuffer message = encoder.encodePackets(clientEntry.getPackets());
        sendMessage(origin, sessionId, channel, message);
    }

    private void sendMessage(String origin, UUID sessionId, Channel channel,
            ChannelBuffer message) {
        HttpResponse res = new DefaultHttpResponse(HTTP_1_1, HttpResponseStatus.OK);
        addHeaders(origin, res);

        res.setContent(message);
        HttpHeaders.setContentLength(res, res.getContent().readableBytes());

        if (log.isTraceEnabled()) {
            log.trace("Out message: {} - sessionId: {} - channelId: {}",
                        new Object[] { message.toString(CharsetUtil.UTF_8),
                            sessionId, channel.getId() });
        }
        ChannelFuture f = channel.write(res);
        f.addListener(ChannelFutureListener.CLOSE);
    }

    private void addHeaders(String origin, HttpResponse res) {
        res.addHeader(CONTENT_TYPE, "text/plain; charset=UTF-8");
        res.addHeader(CONNECTION, KEEP_ALIVE);
        if (origin != null) {
            res.addHeader("Access-Control-Allow-Origin", origin);
            res.addHeader("Access-Control-Allow-Credentials", "true");
        }
    }

    @Override
    public void writeRequested(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
        Object msg = e.getMessage();
        if (msg instanceof BaseMessage) {
            BaseMessage message = (BaseMessage) msg;
            message.handleMessage(this, ctx.getChannel());
        } else {
            ctx.sendDownstream(e);
        }
    }

    @Override
    public void handle(XHRNewChannelMessage xhrNewChannelMessage, Channel channel) throws IOException {
        XHRClientEntry clientEntry = getXHRClientEntry(channel, xhrNewChannelMessage.getSessionId());

        write(xhrNewChannelMessage.getSessionId(), xhrNewChannelMessage.getOrigin(), clientEntry, channel);
    }

    @Override
    public void handle(XHRPacketMessage xhrPacketMessage, Channel channel) throws IOException {
        XHRClientEntry clientEntry = getXHRClientEntry(channel, xhrPacketMessage.getSessionId());
        clientEntry.addPacket(xhrPacketMessage.getPacket());

        write(xhrPacketMessage.getSessionId(), xhrPacketMessage.getOrigin(), clientEntry, channel);
    }

    @Override
    public void handle(XHROutMessage xhrPostMessage, Channel channel) {
        sendMessage(xhrPostMessage.getOrigin(), null, channel, ChannelBuffers.EMPTY_BUFFER);
    }

    @Override
    public void handle(AuthorizeMessage authMsg, Channel channel) throws IOException {
        ChannelBuffer msg;
        String message = authMsg.getMsg();
        if (authMsg.getJsonpParam() != null) {
            msg = encoder.encodeJsonP(authMsg.getJsonpParam(), message);
        } else {
            msg = ChannelBuffers.wrappedBuffer(message.getBytes());
        }
        sendMessage(authMsg.getOrigin(), authMsg.getSessionId(), channel, msg);
    }

    @Override
    public void handle(WebSocketPacketMessage webSocketPacketMessage, Channel channel) throws IOException {
        ChannelBuffer message = encoder.encodePacket(webSocketPacketMessage.getPacket());
        WebSocketFrame res = new TextWebSocketFrame(message);
        log.trace("Out message: {} sessionId: {}", new Object[] {
                message.toString(CharsetUtil.UTF_8), webSocketPacketMessage.getSessionId()});
        if (channel.isOpen()) {
            channel.write(res);
        } else {
            log.trace("Channel was closed, for sessionId: {}", webSocketPacketMessage.getSessionId());
        }
    }

    @Override
    public void handle(WebsocketErrorMessage websocketErrorMessage, Channel channel) throws IOException {
        ChannelBuffer message = encoder.encodePacket(websocketErrorMessage.getPacket());
        TextWebSocketFrame frame = new TextWebSocketFrame(message);
        channel.write(frame);
    }

    @Override
    public void handle(XHRErrorMessage xhrErrorMessage, Channel channel) throws IOException {
        ChannelBuffer message = encoder.encodePacket(xhrErrorMessage.getPacket());
        sendMessage(xhrErrorMessage.getOrigin(), null, channel, message);
    }

}
TOP

Related Classes of com.corundumstudio.socketio.SocketIOEncoder$XHRClientEntry

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.