/*
* socket.io-java-client XhrTransport.java
*
* Copyright (c) 2012, Enno Boland
* socket.io-java-client is a implementation of the socket.io protocol in Java.
*
* See LICENSE file for more information
*/
package io.socket;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Arrays;
import java.util.Iterator;
import java.util.concurrent.ConcurrentLinkedQueue;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
/**
* The Class XhrTransport.
*/
class XhrTransport implements IOTransport {
/** The String to identify this Transport. */
public static final String TRANSPORT_NAME = "xhr-polling";
/** The connection. */
private IOConnection connection;
/** The url. */
private URL url;
/** The queue holding elements to send. */
ConcurrentLinkedQueue<String> queue = new ConcurrentLinkedQueue<String>();
/** background thread for managing the server connection. */
PollThread pollThread = null;
/** Indicates whether the {@link IOConnection} wants us to be connected. */
private boolean connect;
/** Indicates whether {@link PollThread} is blocked. */
private boolean blocked;
HttpURLConnection urlConnection;
/**
* The Class ReceiverThread.
*/
private class PollThread extends Thread {
private static final String CHARSET = "UTF-8";
/**
* Instantiates a new receiver thread.
*/
public PollThread() {
super(TRANSPORT_NAME);
}
/*
* (non-Javadoc)
*
* @see java.lang.Thread#run()
*/
@Override
public void run() {
connection.transportConnected();
while (isConnect()) {
try {
String line;
URL url = new URL(XhrTransport.this.url.toString() + "?t="
+ System.currentTimeMillis());
urlConnection = (HttpURLConnection) url.openConnection();
SSLContext context = IOConnection.getSslContext();
if(urlConnection instanceof HttpsURLConnection && context != null) {
((HttpsURLConnection)urlConnection).setSSLSocketFactory(context.getSocketFactory());
}
if (!queue.isEmpty()) {
urlConnection.setDoOutput(true);
OutputStream output = urlConnection.getOutputStream();
if (queue.size() == 1) {
line = queue.poll();
output.write(line.getBytes(CHARSET));
} else {
Iterator<String> iter = queue.iterator();
while (iter.hasNext()) {
String junk = iter.next();
line = IOConnection.FRAME_DELIMITER + junk.length()
+ IOConnection.FRAME_DELIMITER + junk;
output.write(line.getBytes(CHARSET));
iter.remove();
}
}
output.close();
InputStream input = urlConnection.getInputStream();
byte[] buffer = new byte[1024];
while(input.read(buffer) > 0) {
}
input.close();
} else {
setBlocked(true);
InputStream plainInput = urlConnection.getInputStream();
BufferedReader input = new BufferedReader(
new InputStreamReader(plainInput, CHARSET));
while ((line = input.readLine()) != null) {
if (connection != null)
connection.transportData(line);
}
setBlocked(false);
}
} catch (IOException e) {
if (connection != null && interrupted() == false) {
connection.transportError(e);
return;
}
}
try {
sleep(100);
} catch (InterruptedException e) {
}
}
connection.transportDisconnected();
}
}
/**
* Creates a new Transport for the given url an {@link IOConnection}.
*
* @param url
* the url
* @param connection
* the connection
* @return the iO transport
*/
public static IOTransport create(URL url, IOConnection connection) {
try {
URL xhrUrl = new URL(url.toString() + IOConnection.SOCKET_IO_1
+ TRANSPORT_NAME + "/" + connection.getSessionId());
return new XhrTransport(xhrUrl, connection);
} catch (MalformedURLException e) {
throw new RuntimeException(
"Malformed Internal url. This should never happen. Please report a bug.",
e);
}
}
/**
* Instantiates a new xhr transport.
*
* @param url
* the url
* @param connection
* the connection
*/
public XhrTransport(URL url, IOConnection connection) {
this.connection = connection;
this.url = url;
}
/*
* (non-Javadoc)
*
* @see io.socket.IOTransport#connect()
*/
@Override
public void connect() {
this.setConnect(true);
pollThread = new PollThread();
pollThread.start();
}
/*
* (non-Javadoc)
*
* @see io.socket.IOTransport#disconnect()
*/
@Override
public void disconnect() {
this.setConnect(false);
pollThread.interrupt();
}
/*
* (non-Javadoc)
*
* @see io.socket.IOTransport#send(java.lang.String)
*/
@Override
public void send(String text) throws IOException {
sendBulk(new String[] { text });
}
/*
* (non-Javadoc)
*
* @see io.socket.IOTransport#canSendBulk()
*/
@Override
public boolean canSendBulk() {
return true;
}
/*
* (non-Javadoc)
*
* @see io.socket.IOTransport#sendBulk(java.lang.String[])
*/
@Override
public void sendBulk(String[] texts) throws IOException {
queue.addAll(Arrays.asList(texts));
if (isBlocked()) {
pollThread.interrupt();
urlConnection.disconnect();
}
}
/*
* (non-Javadoc)
*
* @see io.socket.IOTransport#invalidate()
*/
@Override
public void invalidate() {
this.connection = null;
}
/**
* Checks if is connect.
*
* @return true, if is connect
*/
private synchronized boolean isConnect() {
return connect;
}
/**
* Sets the connect.
*
* @param connect
* the new connect
*/
private synchronized void setConnect(boolean connect) {
this.connect = connect;
}
/**
* Checks if is blocked.
*
* @return true, if is blocked
*/
private synchronized boolean isBlocked() {
return blocked;
}
/**
* Sets the blocked.
*
* @param blocked
* the new blocked
*/
private synchronized void setBlocked(boolean blocked) {
this.blocked = blocked;
}
@Override
public String getName() {
return TRANSPORT_NAME;
}
}