/** Copyright 2013 the original author or authors. * * 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. */ /** * * @author <a href='mailto:th33musk3t33rs@gmail.com'>3.musket33rs</a> * * @since 0.1 */ package org.threemusketeers.eventsource; import io.netty.bootstrap.Bootstrap; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioSocketChannel; import io.netty.handler.codec.DelimiterBasedFrameDecoder; import io.netty.handler.codec.http.HttpRequestEncoder; import io.netty.handler.codec.string.StringDecoder; import io.netty.util.CharsetUtil; import java.net.URI; /** * Implementation of the EventSource java client compliant with * http://www.w3.org/TR/2012/WD-eventsource-20121023/ */ public class EventSource { private EventLoopGroup group; private Bootstrap bootstrap; private URI uri; private EventSourceClientHandler handler; public EventSource(String url, EventLoopGroup group, EventSourceNotification notification) { this.group = group; this.uri = URI.create(url); String protocol = uri.getScheme(); if (!"http".equals(protocol) && !"https".equals(protocol)) { notification.onerror("Unsupported protocol: " + protocol + " for URL " + url); return; } handler = new EventSourceClientHandler(uri, notification, this); createBootstrap(); } public void close() { if (handler != null) { handler.close(); } } void createBootstrap() { bootstrap = new Bootstrap(); bootstrap.group(group) .channel(NioSocketChannel.class) .handler(new ChannelInitializer<SocketChannel>() { @Override public void initChannel(SocketChannel ch) throws Exception { ChannelPipeline p = ch.pipeline(); //Lines must be separated by either a U+000D CARRIAGE RETURN U+000A LINE FEED (CRLF) character pair, //a single U+000A LINE FEED (LF) character, //or a single U+000D CARRIAGE RETURN (CR) character. p.addLast(new HttpRequestEncoder(), new DelimiterBasedFrameDecoder(Integer.MAX_VALUE, new ByteBuf[] { Unpooled.wrappedBuffer(new byte[]{'\r', '\n'}), Unpooled.wrappedBuffer(new byte[] { '\n' }), Unpooled.wrappedBuffer(new byte[] { '\r' })}), new StringDecoder(CharsetUtil.UTF_8), handler); } }); int port = uri.getPort(); if(port <= 0) { String protocol = uri.getScheme(); if ("http".equals(protocol)) { port = 80; } else { port = 443; } } bootstrap.connect(uri.getHost(), port); } protected void finalize() throws Throwable { this.close(); } }