package com.jpexs.proxy; import java.io.IOException; import java.net.Socket; import java.util.Enumeration; import java.util.Hashtable; import java.util.Vector; class Http extends HttpConnection { /* XXX - more than 1 should work now. */ static final int MAX_PENDING_REQUESTS = 1; static Hashtable cache = new Hashtable(33); private static Object httpLock = new Object(); String host; int port; boolean proxy = false; boolean persistent = false; boolean closed = false; long idle = 0; Vector queue = new Vector(); public Http(String host, int port) throws IOException { this(host, port, false); } public Http(String host, int port, boolean isProxy) throws IOException { super(host, port); this.host = host; this.port = port; this.proxy = isProxy; } public Http(String host, int port, boolean isProxy, Socket sock) throws IOException { super(sock); this.host = host; this.port = port; this.proxy = isProxy; } public synchronized void sendRequest(Request request) throws IOException, RetryRequestException { queue.addElement(request); try { send(request); } catch (IOException e) { if (persistent) { persistent = false; throw new RetryRequestException(); } throw e; } } public synchronized Reply recvReply(Request request) throws IOException, RetryRequestException { while (queue.firstElement() != request) { try { wait(); } catch (InterruptedException e) { } } if (closed) { throw new RetryRequestException(); } try { return recv(); } catch (IOException e) { if (persistent) { persistent = false; throw new RetryRequestException(); } throw e; } } public void reallyClose() { persistent = false; close(); } public synchronized void close() { if (persistent) { idle = System.currentTimeMillis(); } else { cacheRemove(host, port, this); super.close(); closed = true; } if (queue.size() > 0) { queue.removeElementAt(0); notify(); } } private void send(Request request) throws IOException { /* Prepare HTTP/1.1 request */ request.removeHeaderField("Proxy-Connection"); if (!proxy) { if (request.containsHeaderField("Connection") && (request.getHeaderField("Connection").toLowerCase().equals("keep-alive"))) { } else { request.setHeaderField("Connection", "open"); } if (!request.containsHeaderField("Host")) { request.setHeaderField("Host", request.getHost()); } } if (proxy) { request.write(getOutputStream()); } else { String oldStatusLine = request.statusLine; StringBuffer head = new StringBuffer(); head.append(request.getCommand()); head.append(" "); head.append(request.getPath()); head.append(" "); head.append("HTTP/1.0"); request.statusLine = head.toString(); request.write(getOutputStream()); /* flush? */ request.statusLine = oldStatusLine; } } private Reply recv() throws IOException { Reply reply = new Reply(getInputStream()); reply.read(); String conn = reply.getHeaderField("Connection"); if (reply.containsHeaderField("Connection") && reply.getHeaderField("Connection").equals("close")) { persistent = false; } else if (reply.getProtocol().equals("HTTP/1.1")) { persistent = true; } else { persistent = false; } /* Received HTTP/1.1 "Continue". Read another Reply. */ if (reply.getStatusCode() == 100) { reply = recv(); } return reply; } protected boolean isBusy() { return queue.size() >= MAX_PENDING_REQUESTS; } protected boolean isPersistent() { return persistent; } private static String cacheKey(String host, int port) { return host.toLowerCase() + ":" + port; } private static Vector cacheLookup(String host, int port) { Vector v = (Vector) cache.get(cacheKey(host, port)); return v; } private static boolean cacheContains(Http http) { Vector v = (Vector) cache.get(cacheKey(http.host, http.port)); return v != null ? v.contains(http) : false; } private static void cacheInsert(String host, int port, Http http) { String key = cacheKey(host, port); Vector v = (Vector) cache.get(key); if (v == null) { v = new Vector(); } v.addElement(http); cache.put(key, v); } private static void cacheRemove(String host, int port, Http http) { Vector v = (Vector) cache.get(cacheKey(host, port)); if (v != null) { v.removeElement(http); if (v.isEmpty()) { cache.remove(cacheKey(host, port)); } } } private static void cacheClean() { long now = System.currentTimeMillis(); Enumeration e = cache.keys(); while (e.hasMoreElements()) { Vector v = (Vector) cache.get(e.nextElement()); for (int i = 0; i < v.size(); i++) { Http http = (Http) v.elementAt(i); if (http.idle > 0 && now - http.idle > 30000) /* 30 seconds */ { http.persistent = false; http.close(); } } } } static Http open(String host, int port, boolean isProxy) throws IOException { Http http = null; synchronized (httpLock) { Vector v = cacheLookup(host, port); if (v != null) { for (int i = 0; i < v.size(); i++) { Http pick = (Http) v.elementAt(i); /* find an http connection that isn't busy */ if (pick.isPersistent() && !pick.isBusy()) { http = pick; break; } } if (http != null) { http.idle = 0; } } } if (http == null) { http = new Http(host, port, isProxy); cacheInsert(host, port, http); } return http; } static Http open(String host, int port) throws IOException { return open(host, port, false); } static Enumeration enumerate() { Vector list = new Vector(); Enumeration e = cache.keys(); while (e.hasMoreElements()) { Vector v = (Vector) cache.get(e.nextElement()); for (int i = 0; i < v.size(); i++) { list.addElement(v.elementAt(i)); } } return list.elements(); } static synchronized void clean() { cacheClean(); } public String toString() { StringBuffer buf = new StringBuffer(); buf.append("SERVER "); buf.append(super.toString()); if (isPersistent()) { buf.append(" - "); if (queue.size() > 0) { buf.append(queue.size()); buf.append(" pending"); } else { buf.append("idle " + ((System.currentTimeMillis() - idle) / 1000.0) + " sec"); } } return buf.toString(); } }