From 806e2f88c822a1a0205e5e27f4fb42616fbc400e Mon Sep 17 00:00:00 2001 From: zzz <zzz@mail.i2p> Date: Thu, 12 Feb 2009 16:50:20 +0000 Subject: [PATCH] Dont buffer all the POST data so we wont OOM on huge POSTs. Use unbuffered read for the first line, and for all the headers if POST --- .../i2p/i2ptunnel/I2PTunnelHTTPClient.java | 84 +++++++++---------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java index 4b5379ac12..b42376dbf7 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java @@ -5,6 +5,7 @@ package net.i2p.i2ptunnel; import java.io.BufferedReader; import java.io.IOException; +import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.net.Socket; @@ -21,6 +22,7 @@ import net.i2p.I2PException; import net.i2p.client.streaming.I2PSocket; import net.i2p.client.streaming.I2PSocketOptions; import net.i2p.data.DataFormatException; +import net.i2p.data.DataHelper; import net.i2p.data.Destination; import net.i2p.util.EventDispatcher; import net.i2p.util.FileUtil; @@ -131,16 +133,6 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable "Your browser is misconfigured. Do not use the proxy to access the router console or other localhost destinations.<BR>") .getBytes(); - private final static int MAX_POSTBYTES = 20*1024*1024; // arbitrary but huge - all in memory, no temp file - private final static byte[] ERR_MAXPOST = - ("HTTP/1.1 503 Bad POST\r\n"+ - "Content-Type: text/html; charset=iso-8859-1\r\n"+ - "Cache-control: no-cache\r\n"+ - "\r\n"+ - "<html><body><H1>I2P ERROR: REQUEST DENIED</H1>"+ - "The maximum POST size is " + MAX_POSTBYTES + " bytes.<BR>") - .getBytes(); - /** used to assign unique IDs to the threads / clients. no logic or functionality */ private static volatile long __clientId = 0; @@ -232,6 +224,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable private static long __requestId = 0; protected void clientConnectionRun(Socket s) { + InputStream in = null; OutputStream out = null; String targetRequest = null; boolean usingWWWProxy = false; @@ -239,11 +232,12 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable long requestId = ++__requestId; try { out = s.getOutputStream(); - BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream(), "ISO-8859-1")); + InputReader reader = new InputReader(s.getInputStream()); String line, method = null, protocol = null, host = null, destination = null; StringBuffer newRequest = new StringBuffer(); int ahelper = 0; - while ((line = br.readLine()) != null) { + while ((line = reader.readLine(method)) != null) { + line = line.trim(); if (_log.shouldLog(Log.DEBUG)) _log.debug(getPrefix(requestId) + "Line=[" + line + "]"); @@ -257,7 +251,6 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable if (_log.shouldLog(Log.DEBUG)) _log.debug(getPrefix(requestId) + "Method is null for [" + line + "]"); - line = line.trim(); int pos = line.indexOf(" "); if (pos == -1) break; method = line.substring(0, pos); @@ -514,30 +507,11 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable newRequest.append("Connection: close\r\n\r\n"); break; } else { - newRequest.append(line.trim()).append("\r\n"); // HTTP spec + newRequest.append(line).append("\r\n"); // HTTP spec } } if (_log.shouldLog(Log.DEBUG)) _log.debug(getPrefix(requestId) + "NewRequest header: [" + newRequest.toString() + "]"); - - int postbytes = 0; - while (br.ready()) { // empty the buffer (POST requests) - int i = br.read(); - if (i != -1) { - newRequest.append((char) i); - if (++postbytes > MAX_POSTBYTES) { - if (out != null) { - out.write(ERR_MAXPOST); - out.write("<p /><i>Generated on: ".getBytes()); - out.write(new Date().toString().getBytes()); - out.write("</i></body></html>\n".getBytes()); - out.flush(); - } - s.close(); - return; - } - } - } if (method == null || destination == null) { l.log("No HTTP method found in the request."); @@ -610,8 +584,8 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable l.log(ex.getMessage()); handleHTTPClientException(ex, out, targetRequest, usingWWWProxy, currentProxy, requestId); closeSocket(s); - } catch (OutOfMemoryError oom) { // mainly for huge POSTs - IOException ex = new IOException("OOM (in POST?)"); + } catch (OutOfMemoryError oom) { + IOException ex = new IOException("OOM"); _log.info("getPrefix(requestId) + Error trying to connect", ex); l.log(ex.getMessage()); handleHTTPClientException(ex, out, targetRequest, usingWWWProxy, currentProxy, requestId); @@ -619,6 +593,29 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable } } + /** + * Read the first line unbuffered. + * After that, switch to a BufferedReader, unless the method is "POST". + * We can't use BufferedReader for POST because we can't have readahead, + * since we are passing the stream on to I2PTunnelRunner for the POST data. + * + */ + private static class InputReader { + BufferedReader _br; + InputStream _s; + public InputReader(InputStream s) { + _br = null; + _s = s; + } + String readLine(String method) throws IOException { + if (method == null || "POST".equals(method)) + return DataHelper.readLine(_s); + if (_br == null) + _br = new BufferedReader(new InputStreamReader(_s, "ISO-8859-1")); + return _br.readLine(); + } + } + private final static String getHostName(String host) { if (host == null) return null; try { @@ -630,7 +627,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable } } - private class OnTimeout implements Runnable { + private static class OnTimeout implements Runnable { private Socket _socket; private OutputStream _out; private String _target; @@ -708,11 +705,12 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable } } - private void handleHTTPClientException(Exception ex, OutputStream out, String targetRequest, + private static void handleHTTPClientException(Exception ex, OutputStream out, String targetRequest, boolean usingWWWProxy, String wwwProxy, long requestId) { - if (_log.shouldLog(Log.WARN)) - _log.warn(getPrefix(requestId) + "Error sending to " + wwwProxy + " (proxy? " + usingWWWProxy + ", request: " + targetRequest, ex); + // static + //if (_log.shouldLog(Log.WARN)) + // _log.warn(getPrefix(requestId) + "Error sending to " + wwwProxy + " (proxy? " + usingWWWProxy + ", request: " + targetRequest, ex); if (out != null) { try { String str; @@ -727,16 +725,18 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable header = ERR_DESTINATION_UNKNOWN; writeErrorMessage(header, out, targetRequest, usingWWWProxy, wwwProxy, false); } catch (IOException ioe) { - _log.warn(getPrefix(requestId) + "Error writing out the 'destination was unknown' " + "message", ioe); + // static + //_log.warn(getPrefix(requestId) + "Error writing out the 'destination was unknown' " + "message", ioe); } } else { - _log.warn(getPrefix(requestId) + "Client disconnected before we could say that destination " + "was unknown", ex); + // static + //_log.warn(getPrefix(requestId) + "Client disconnected before we could say that destination " + "was unknown", ex); } } private final static String SUPPORTED_HOSTS[] = { "i2p", "www.i2p.com", "i2p."}; - private boolean isSupportedAddress(String host, String protocol) { + private static boolean isSupportedAddress(String host, String protocol) { if ((host == null) || (protocol == null)) return false; boolean found = false; String lcHost = host.toLowerCase(); -- GitLab