Java tutorial
// (c) Alexandre Fenyo 2012, 2013, 2014, 2015, 2016 - alex@fenyo.net - http://fenyo.net - GPLv3 licensed package net.fenyo.mail4hotspot.service; import java.io.IOException; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.nio.ByteBuffer; import java.nio.channels.SocketChannel; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; // StageWebView avec fichier local : http://forums.adobe.com/thread/841945 public class HttpProxy { protected final Log log = LogFactory.getLog(getClass()); private SocketChannel socket_channel = null; private final String uuid; private final int remote_port; private int local_port = -1; private final String remote_host; private final long first_use = System.currentTimeMillis(); private long last_use = System.currentTimeMillis(); private boolean closed = false; // http://docs.oracle.com/javase/6/docs/api/java/nio/Buffer.html // pas thread safe => Attention ! // A buffer's capacity is the number of elements it contains. The capacity of a buffer is never negative and never changes. // A buffer's limit is the index of the first element that should not be read or written. A buffer's limit is never negative and is never greater than its capacity. // A buffer's position is the index of the next element to be read or written. A buffer's position is never negative and is never greater than its limit. // A buffer's mark is the index to which its position will be reset when the reset method is invoked // A newly-created buffer always has a position of zero and a mark that is undefined. // 0 <= mark <= position <= limit <= capacity // read et write : incrmente la position // reset() resets this buffer's position to the previously-marked position // clear() makes a buffer ready for a new sequence of channel-read or relative put operations: It sets the limit to the capacity and the position to zero. // flip() makes a buffer ready for a new sequence of channel-write or relative get operations: It sets the limit to the current position and then sets the position to zero. // rewind() makes a buffer ready for re-reading the data that it already contains: It leaves the limit unchanged and sets the position to zero. // taille max d'un buffer mis vers le client lors d'une redirection de ports, // c'est donc environ la taille max d'un message en direction du client // donc plus c'est bas, plus un ssh sera ractif et plus c'est grand meilleur est le dbit // BUG avec 64 * 1024 sur www.lemonde.fr => on met 1024 au lieu de 64k et c'est en fait bien plus ractif et a compense le bug // ide du pb : peut etre que quand le distant ferme la connexion, on n'attend pas la rcupration locale avant de fermer la socket locale // private ByteBuffer from_socket_buffer = ByteBuffer.allocate(64 * 1024); // private ByteBuffer from_socket_buffer = ByteBuffer.allocate(1 * 1024); // 12/8/2012 : on a chang MAX_REPLY_LEN de 32 63, pour presque doubler le dbit, donc on passe de 1024 2048 ici pour que la dure de chargement de chaque message ne change pas, // on gagne ainsi en latence car prs de 2 fois moins de messages pour une mme page web, par ex // fin aot, dbut sept 2012 : on a mis 48 dans MAX_REPLY_LEN // on pourrait vouloir mettre 1 * 512 pour le premier message d'un flux HTTP puis 1 * 4096 pour la suite, afin de contourner le bug d'Android qui consiste afficher une page blanche quand aprs quelques dizaines de seconde une requte HTTP n'a rien renvoy private ByteBuffer from_socket_buffer = ByteBuffer.allocate(1 * 4096); private byte[] to_socket_array = new byte[0]; public HttpProxy(final String uuid, final int remote_port, final String remote_host) { this.uuid = uuid; this.remote_port = remote_port; this.remote_host = remote_host; } public long getFirstUse() { return first_use; } public long getLastUse() { return last_use; } public int getLocalPort() { return local_port; } public int getRemotePort() { return remote_port; } public int connect() throws IOException { last_use = System.currentTimeMillis(); // log.debug("connect(): new Socket()"); // final InetAddress address = InetAddress.getByAddress("xyz", new byte [] { (byte) 192, (byte) 168, (byte) 0, (byte) 5 /* 19 */ } ); final InetAddress address = InetAddress.getByName(remote_host); final SocketAddress s_address = new InetSocketAddress(address, remote_port); socket_channel = SocketChannel.open(); socket_channel.configureBlocking(true); socket_channel.connect(s_address); socket_channel.configureBlocking(false); // log.debug("local port: " + ((InetSocketAddress) socket_channel.getLocalAddress()).getPort()); local_port = ((InetSocketAddress) socket_channel.getLocalAddress()).getPort(); return local_port; } public void sendData(final byte data[]) throws IOException { // log.debug("sendData on id " + local_port); last_use = System.currentTimeMillis(); // log.debug("sendData(): " + data.length + " bytes"); if (closed) return; try { final ByteBuffer bb = ByteBuffer.allocate(to_socket_array.length + data.length); bb.put(to_socket_array); bb.put(data); bb.flip(); final int nbytes = socket_channel.write(bb); to_socket_array = ArrayUtils.subarray(bb.array(), nbytes, bb.array().length); } catch (final IOException ex) { log.warn(ex); ex.printStackTrace(); socket_channel.close(); closed = true; throw ex; } } public byte[] receiveData() throws IOException { // log.debug("receiveData on id " + local_port); last_use = System.currentTimeMillis(); // log.debug("receiveData()"); if (closed) return null; try { // log.debug("position=" + from_socket_buffer.position() + " - limit=" + from_socket_buffer.limit() + " - capacity=" + from_socket_buffer.capacity()); // log.debug("REMAINING: " + from_socket_buffer.remaining()); final int nbytes = socket_channel.read(from_socket_buffer); // log.debug("octets lus: " + nbytes); if (nbytes == -1) return null; from_socket_buffer.flip(); final byte[] ret_array = new byte[from_socket_buffer.limit()]; from_socket_buffer.get(ret_array); from_socket_buffer.clear(); // log.debug("ret array len: " + ret_array.length); return ret_array; } catch (final IOException ex) { log.warn(ex); ex.printStackTrace(); closed = true; socket_channel.close(); throw ex; } } public void close() { last_use = System.currentTimeMillis(); if (!closed) { closed = true; try { socket_channel.close(); } catch (IOException ex) { log.warn(ex); ex.printStackTrace(); } } } public String getUuid() { last_use = System.currentTimeMillis(); return uuid; } }