i2ptunnel HTTP client: Replace all getBytes() calls

with a Writer or getBytes("UTF-8") for efficiency and to
avoid encoding issues.
Store strings as strings, not bytes.
Catch IOEs to prevent cascading error pages.
Minor cleanups
This commit is contained in:
zzz
2015-03-25 12:10:14 +00:00
parent 9e18c7ea18
commit f3d573cab0
7 changed files with 297 additions and 213 deletions

View File

@@ -328,9 +328,9 @@ class HTTPResponseOutputStream extends FilterOutputStream {
if (compressed > 0 && expanded > 0) { if (compressed > 0 && expanded > 0) {
// only update the stats if we did something // only update the stats if we did something
double ratio = compressed/expanded; double ratio = compressed/expanded;
_context.statManager().addRateData("i2ptunnel.httpCompressionRatio", (int)(100d*ratio), 0); _context.statManager().addRateData("i2ptunnel.httpCompressionRatio", (int)(100d*ratio));
_context.statManager().addRateData("i2ptunnel.httpCompressed", (long)compressed, 0); _context.statManager().addRateData("i2ptunnel.httpCompressed", (long)compressed);
_context.statManager().addRateData("i2ptunnel.httpExpanded", (long)expanded, 0); _context.statManager().addRateData("i2ptunnel.httpExpanded", (long)expanded);
} }
} }
} }

View File

@@ -59,24 +59,22 @@ public class I2PTunnelConnectClient extends I2PTunnelHTTPClientBase implements R
public static final String AUTH_REALM = "I2P SSL Proxy"; public static final String AUTH_REALM = "I2P SSL Proxy";
private final static byte[] ERR_BAD_PROTOCOL = private final static String ERR_BAD_PROTOCOL =
("HTTP/1.1 405 Bad Method\r\n"+ "HTTP/1.1 405 Bad Method\r\n"+
"Content-Type: text/html; charset=iso-8859-1\r\n"+ "Content-Type: text/html; charset=iso-8859-1\r\n"+
"Cache-control: no-cache\r\n"+ "Cache-control: no-cache\r\n"+
"\r\n"+ "\r\n"+
"<html><body><H1>I2P ERROR: METHOD NOT ALLOWED</H1>"+ "<html><body><H1>I2P ERROR: METHOD NOT ALLOWED</H1>"+
"The request uses a bad protocol. "+ "The request uses a bad protocol. "+
"The Connect Proxy supports CONNECT requests ONLY. Other methods such as GET are not allowed - Maybe you wanted the HTTP Proxy?.<BR>") "The Connect Proxy supports CONNECT requests ONLY. Other methods such as GET are not allowed - Maybe you wanted the HTTP Proxy?.<BR>";
.getBytes();
private final static byte[] ERR_LOCALHOST = private final static String ERR_LOCALHOST =
("HTTP/1.1 403 Access Denied\r\n"+ "HTTP/1.1 403 Access Denied\r\n"+
"Content-Type: text/html; charset=iso-8859-1\r\n"+ "Content-Type: text/html; charset=iso-8859-1\r\n"+
"Cache-control: no-cache\r\n"+ "Cache-control: no-cache\r\n"+
"\r\n"+ "\r\n"+
"<html><body><H1>I2P ERROR: REQUEST DENIED</H1>"+ "<html><body><H1>I2P ERROR: REQUEST DENIED</H1>"+
"Your browser is misconfigured. Do not use the proxy to access the router console or other localhost destinations.<BR>") "Your browser is misconfigured. Do not use the proxy to access the router console or other localhost destinations.<BR>";
.getBytes();
/** /**
* @throws IllegalArgumentException if the I2PTunnel does not contain * @throws IllegalArgumentException if the I2PTunnel does not contain
@@ -273,7 +271,7 @@ public class I2PTunnelConnectClient extends I2PTunnelHTTPClientBase implements R
Destination clientDest = _context.namingService().lookup(destination); Destination clientDest = _context.namingService().lookup(destination);
if (clientDest == null) { if (clientDest == null) {
byte[] header; String header;
if (usingWWWProxy) if (usingWWWProxy)
header = getErrorPage("dnfp", ERR_DESTINATION_UNKNOWN); header = getErrorPage("dnfp", ERR_DESTINATION_UNKNOWN);
else else
@@ -289,7 +287,7 @@ public class I2PTunnelConnectClient extends I2PTunnelHTTPClientBase implements R
if (usingWWWProxy) if (usingWWWProxy)
data = newRequest.toString().getBytes("ISO-8859-1"); data = newRequest.toString().getBytes("ISO-8859-1");
else else
response = SUCCESS_RESPONSE; response = SUCCESS_RESPONSE.getBytes("UTF-8");
OnTimeout onTimeout = new OnTimeout(s, s.getOutputStream(), targetRequest, usingWWWProxy, currentProxy, requestId); OnTimeout onTimeout = new OnTimeout(s, s.getOutputStream(), targetRequest, usingWWWProxy, currentProxy, requestId);
Thread t = new I2PTunnelRunner(s, i2ps, sockLock, data, response, mySockets, onTimeout); Thread t = new I2PTunnelRunner(s, i2ps, sockLock, data, response, mySockets, onTimeout);
// we are called from an unlimited thread pool, so run inline // we are called from an unlimited thread pool, so run inline
@@ -311,10 +309,10 @@ public class I2PTunnelConnectClient extends I2PTunnelHTTPClientBase implements R
} }
} }
private static void writeErrorMessage(byte[] errMessage, OutputStream out) throws IOException { private static void writeErrorMessage(String errMessage, OutputStream out) throws IOException {
if (out == null) if (out == null)
return; return;
out.write(errMessage); out.write(errMessage.getBytes("UTF-8"));
writeFooter(out); writeFooter(out);
} }
} }

View File

@@ -3,9 +3,12 @@
*/ */
package net.i2p.i2ptunnel; package net.i2p.i2ptunnel;
import java.io.BufferedWriter;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.net.Socket; import java.net.Socket;
import java.net.SocketException; import java.net.SocketException;
import java.net.URI; import java.net.URI;
@@ -84,13 +87,13 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
/** /**
* These are backups if the xxx.ht error page is missing. * These are backups if the xxx.ht error page is missing.
*/ */
private final static byte[] ERR_REQUEST_DENIED = private final static String ERR_REQUEST_DENIED =
("HTTP/1.1 403 Access Denied\r\n" + "HTTP/1.1 403 Access Denied\r\n" +
"Content-Type: text/html; charset=iso-8859-1\r\n" + "Content-Type: text/html; charset=iso-8859-1\r\n" +
"Cache-control: no-cache\r\n" + "Cache-control: no-cache\r\n" +
"\r\n" + "\r\n" +
"<html><body><H1>I2P ERROR: REQUEST DENIED</H1>" + "<html><body><H1>I2P ERROR: REQUEST DENIED</H1>" +
"You attempted to connect to a non-I2P website or location.<BR>").getBytes(); "You attempted to connect to a non-I2P website or location.<BR>";
/***** /*****
private final static byte[] ERR_TIMEOUT = private final static byte[] ERR_TIMEOUT =
@@ -105,17 +108,17 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
"the following Destination:<BR><BR>") "the following Destination:<BR><BR>")
.getBytes(); .getBytes();
*****/ *****/
private final static byte[] ERR_NO_OUTPROXY = private final static String ERR_NO_OUTPROXY =
("HTTP/1.1 503 Service Unavailable\r\n" + "HTTP/1.1 503 Service Unavailable\r\n" +
"Content-Type: text/html; charset=iso-8859-1\r\n" + "Content-Type: text/html; charset=iso-8859-1\r\n" +
"Cache-control: no-cache\r\n" + "Cache-control: no-cache\r\n" +
"\r\n" + "\r\n" +
"<html><body><H1>I2P ERROR: No outproxy found</H1>" + "<html><body><H1>I2P ERROR: No outproxy found</H1>" +
"Your request was for a site outside of I2P, but you have no " + "Your request was for a site outside of I2P, but you have no " +
"HTTP outproxy configured. Please configure an outproxy in I2PTunnel").getBytes(); "HTTP outproxy configured. Please configure an outproxy in I2PTunnel";
private final static byte[] ERR_AHELPER_CONFLICT = private final static String ERR_AHELPER_CONFLICT =
("HTTP/1.1 409 Conflict\r\n" + "HTTP/1.1 409 Conflict\r\n" +
"Content-Type: text/html; charset=iso-8859-1\r\n" + "Content-Type: text/html; charset=iso-8859-1\r\n" +
"Cache-control: no-cache\r\n" + "Cache-control: no-cache\r\n" +
"\r\n" + "\r\n" +
@@ -127,20 +130,20 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
"You can resolve the conflict by considering which key you trust, " + "You can resolve the conflict by considering which key you trust, " +
"and either discarding the addresshelper link, " + "and either discarding the addresshelper link, " +
"discarding the host entry from your host database, " + "discarding the host entry from your host database, " +
"or naming one of them differently.<p>").getBytes(); "or naming one of them differently.<p>";
private final static byte[] ERR_AHELPER_NOTFOUND = private final static String ERR_AHELPER_NOTFOUND =
("HTTP/1.1 404 Not Found\r\n" + "HTTP/1.1 404 Not Found\r\n" +
"Content-Type: text/html; charset=iso-8859-1\r\n" + "Content-Type: text/html; charset=iso-8859-1\r\n" +
"Cache-control: no-cache\r\n" + "Cache-control: no-cache\r\n" +
"\r\n" + "\r\n" +
"<html><body><H1>I2P ERROR: Helper key not resolvable.</H1>" + "<html><body><H1>I2P ERROR: Helper key not resolvable.</H1>" +
"The helper key you put for i2paddresshelper= is not resolvable. " + "The helper key you put for i2paddresshelper= is not resolvable. " +
"It seems to be garbage data, or a mistyped b32. Check your URL " + "It seems to be garbage data, or a mistyped b32. Check your URL " +
"to try and fix the helper key to be either a b32 or a base64.").getBytes(); "to try and fix the helper key to be either a b32 or a base64.";
private final static byte[] ERR_AHELPER_NEW = private final static String ERR_AHELPER_NEW =
("HTTP/1.1 409 New Address\r\n" + "HTTP/1.1 409 New Address\r\n" +
"Content-Type: text/html; charset=iso-8859-1\r\n" + "Content-Type: text/html; charset=iso-8859-1\r\n" +
"Cache-control: no-cache\r\n" + "Cache-control: no-cache\r\n" +
"\r\n" + "\r\n" +
@@ -148,42 +151,42 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
"The address helper link you followed is for a new host name that is not in your address book. " + "The address helper link you followed is for a new host name that is not in your address book. " +
"You may either save the destination for this host name to your address book, or remember it only until your router restarts. " + "You may either save the destination for this host name to your address book, or remember it only until your router restarts. " +
"If you save it to your address book, you will not see this message again. " + "If you save it to your address book, you will not see this message again. " +
"If you do not wish to visit this host, click the \"back\" button on your browser.").getBytes(); "If you do not wish to visit this host, click the \"back\" button on your browser.";
private final static byte[] ERR_BAD_PROTOCOL = private final static String ERR_BAD_PROTOCOL =
("HTTP/1.1 403 Bad Protocol\r\n" + "HTTP/1.1 403 Bad Protocol\r\n" +
"Content-Type: text/html; charset=iso-8859-1\r\n" + "Content-Type: text/html; charset=iso-8859-1\r\n" +
"Cache-control: no-cache\r\n" + "Cache-control: no-cache\r\n" +
"\r\n" + "\r\n" +
"<html><body><H1>I2P ERROR: NON-HTTP PROTOCOL</H1>" + "<html><body><H1>I2P ERROR: NON-HTTP PROTOCOL</H1>" +
"The request uses a bad protocol. " + "The request uses a bad protocol. " +
"The I2P HTTP Proxy supports HTTP and HTTPS requests only. Other protocols such as FTP are not allowed.<BR>").getBytes(); "The I2P HTTP Proxy supports HTTP and HTTPS requests only. Other protocols such as FTP are not allowed.<BR>";
private final static byte[] ERR_BAD_URI = private final static String ERR_BAD_URI =
("HTTP/1.1 403 Bad URI\r\n" + "HTTP/1.1 403 Bad URI\r\n" +
"Content-Type: text/html; charset=iso-8859-1\r\n" + "Content-Type: text/html; charset=iso-8859-1\r\n" +
"Cache-control: no-cache\r\n" + "Cache-control: no-cache\r\n" +
"\r\n" + "\r\n" +
"<html><body><H1>I2P ERROR: INVALID REQUEST URI</H1>" + "<html><body><H1>I2P ERROR: INVALID REQUEST URI</H1>" +
"The request URI is invalid, and probably contains illegal characters. " + "The request URI is invalid, and probably contains illegal characters. " +
"If you clicked e.g. a forum link, check the end of the URI for any characters the browser has mistakenly added on.<BR>").getBytes(); "If you clicked e.g. a forum link, check the end of the URI for any characters the browser has mistakenly added on.<BR>";
private final static byte[] ERR_LOCALHOST = private final static String ERR_LOCALHOST =
("HTTP/1.1 403 Access Denied\r\n" + "HTTP/1.1 403 Access Denied\r\n" +
"Content-Type: text/html; charset=iso-8859-1\r\n" + "Content-Type: text/html; charset=iso-8859-1\r\n" +
"Cache-control: no-cache\r\n" + "Cache-control: no-cache\r\n" +
"\r\n" + "\r\n" +
"<html><body><H1>I2P ERROR: REQUEST DENIED</H1>" + "<html><body><H1>I2P ERROR: REQUEST DENIED</H1>" +
"Your browser is misconfigured. Do not use the proxy to access the router console or other localhost destinations.<BR>").getBytes(); "Your browser is misconfigured. Do not use the proxy to access the router console or other localhost destinations.<BR>";
private final static byte[] ERR_INTERNAL_SSL = private final static String ERR_INTERNAL_SSL =
("HTTP/1.1 403 SSL Rejected\r\n" + "HTTP/1.1 403 SSL Rejected\r\n" +
"Content-Type: text/html; charset=iso-8859-1\r\n" + "Content-Type: text/html; charset=iso-8859-1\r\n" +
"Cache-control: no-cache\r\n" + "Cache-control: no-cache\r\n" +
"\r\n" + "\r\n" +
"<html><body><H1>I2P ERROR: SSL to I2P address rejected</H1>" + "<html><body><H1>I2P ERROR: SSL to I2P address rejected</H1>" +
"SSL for to .i2p addresses denied by configuration." + "SSL for to .i2p addresses denied by configuration." +
"You may change the configuration in I2PTunnel").getBytes(); "You may change the configuration in I2PTunnel";
/** /**
* This constructor always starts the tunnel (ignoring the i2cp.delayOpen option). * This constructor always starts the tunnel (ignoring the i2cp.delayOpen option).
@@ -464,10 +467,15 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
if(_log.shouldLog(Log.WARN)) { if(_log.shouldLog(Log.WARN)) {
_log.warn(getPrefix(requestId) + "Bad request [" + request + "]", use); _log.warn(getPrefix(requestId) + "Bad request [" + request + "]", use);
} }
out.write(getErrorPage("baduri", ERR_BAD_URI)); try {
writeFooter(out); out.write(getErrorPage("baduri", ERR_BAD_URI).getBytes("UTF-8"));
reader.drain(); writeFooter(out);
s.close(); reader.drain();
} catch (IOException ioe) {
// ignore
} finally {
closeSocket(s);
}
return; return;
} }
@@ -596,12 +604,19 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
if(_log.shouldLog(Log.WARN)) { if(_log.shouldLog(Log.WARN)) {
_log.warn(getPrefix(requestId) + "Could not find destination for " + ahelperKey); _log.warn(getPrefix(requestId) + "Could not find destination for " + ahelperKey);
} }
byte[] header = getErrorPage("ahelper-notfound", ERR_AHELPER_NOTFOUND); String header = getErrorPage("ahelper-notfound", ERR_AHELPER_NOTFOUND);
out.write(header); try {
out.write(("<p>" + _("This seems to be a bad destination:") + " " + ahelperKey + " " + _("i2paddresshelper cannot help you with a destination like that!") + "</p>").getBytes("UTF-8")); out.write(header.getBytes("UTF-8"));
writeFooter(out); out.write(("<p>" + _("This seems to be a bad destination:") + " " + ahelperKey + " " +
// XXX: should closeSocket(s) be in a finally block? _("i2paddresshelper cannot help you with a destination like that!") +
closeSocket(s); "</p>").getBytes("UTF-8"));
writeFooter(out);
// XXX: should closeSocket(s) be in a finally block?
} catch (IOException ioe) {
// ignore
} finally {
closeSocket(s);
}
return; return;
} }
ahelperKey = _dest.toBase64(); ahelperKey = _dest.toBase64();
@@ -645,11 +660,12 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
// Did addresshelper key conflict? // Did addresshelper key conflict?
if(ahelperConflict) { if(ahelperConflict) {
try {
// convert ahelperKey to b32 // convert ahelperKey to b32
String alias = getHostName(ahelperKey); String alias = getHostName(ahelperKey);
if(alias.equals("i2p")) { if(alias.equals("i2p")) {
// bad ahelperKey // bad ahelperKey
byte[] header = getErrorPage("dnfb", ERR_DESTINATION_UNKNOWN); String header = getErrorPage("dnfb", ERR_DESTINATION_UNKNOWN);
writeErrorMessage(header, out, targetRequest, false, destination); writeErrorMessage(header, out, targetRequest, false, destination);
} else { } else {
String trustedURL = requestURI.toASCIIString(); String trustedURL = requestURI.toASCIIString();
@@ -663,14 +679,19 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
break; break;
} }
String conflictURL = conflictURI.toASCIIString(); String conflictURL = conflictURI.toASCIIString();
byte[] header = getErrorPage("ahelper-conflict", ERR_AHELPER_CONFLICT); String header = getErrorPage("ahelper-conflict", ERR_AHELPER_CONFLICT);
out.write(header); out.write(header.getBytes("UTF-8"));
out.write(_("To visit the destination in your host database, click <a href=\"{0}\">here</a>. To visit the conflicting addresshelper destination, click <a href=\"{1}\">here</a>.", trustedURL, conflictURL).getBytes("UTF-8")); out.write(_("To visit the destination in your host database, click <a href=\"{0}\">here</a>. To visit the conflicting addresshelper destination, click <a href=\"{1}\">here</a>.",
out.write(("</p></div>").getBytes()); trustedURL, conflictURL).getBytes("UTF-8"));
out.write("</p></div>".getBytes("UTF-8"));
writeFooter(out); writeFooter(out);
}
reader.drain();
} catch (IOException ioe) {
// ignore
} finally {
closeSocket(s);
} }
reader.drain();
s.close();
return; return;
} }
} // end query processing } // end query processing
@@ -700,10 +721,16 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
} else if(hostLowerCase.equals("localhost") || host.equals("127.0.0.1") || } else if(hostLowerCase.equals("localhost") || host.equals("127.0.0.1") ||
host.startsWith("192.168.") || host.equals("[::1]")) { host.startsWith("192.168.") || host.equals("[::1]")) {
// if somebody is trying to get to 192.168.example.com, oh well // if somebody is trying to get to 192.168.example.com, oh well
out.write(getErrorPage("localhost", ERR_LOCALHOST)); try {
writeFooter(out); out.write(getErrorPage("localhost", ERR_LOCALHOST).getBytes("UTF-8"));
reader.drain(); writeFooter(out);
s.close(); reader.drain();
s.close();
} catch (IOException ioe) {
// ignore
} finally {
closeSocket(s);
}
return; return;
} else if(host.contains(".") || host.startsWith("[")) { } else if(host.contains(".") || host.startsWith("[")) {
if (Boolean.parseBoolean(getTunnel().getClientOptions().getProperty(PROP_USE_OUTPROXY_PLUGIN, "true"))) { if (Boolean.parseBoolean(getTunnel().getClientOptions().getProperty(PROP_USE_OUTPROXY_PLUGIN, "true"))) {
@@ -748,10 +775,15 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
_log.warn(getPrefix(requestId) + "Host wants to be outproxied, but we dont have any!"); _log.warn(getPrefix(requestId) + "Host wants to be outproxied, but we dont have any!");
} }
l.log("No outproxy found for the request."); l.log("No outproxy found for the request.");
out.write(getErrorPage("noproxy", ERR_NO_OUTPROXY)); try {
writeFooter(out); out.write(getErrorPage("noproxy", ERR_NO_OUTPROXY).getBytes("UTF-8"));
reader.drain(); writeFooter(out);
s.close(); reader.drain();
} catch (IOException ioe) {
// ignore
} finally {
closeSocket(s);
}
return; return;
} }
destination = currentProxy; destination = currentProxy;
@@ -769,10 +801,15 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
if(_log.shouldLog(Log.WARN)) { if(_log.shouldLog(Log.WARN)) {
_log.warn("NODOTS, NOI2P: " + request); _log.warn("NODOTS, NOI2P: " + request);
} }
out.write(getErrorPage("denied", ERR_REQUEST_DENIED)); try {
writeFooter(out); out.write(getErrorPage("denied", ERR_REQUEST_DENIED).getBytes("UTF-8"));
reader.drain(); writeFooter(out);
s.close(); reader.drain();
} catch (IOException ioe) {
// ignore
} finally {
closeSocket(s);
}
return; return;
} // end host name processing } // end host name processing
@@ -898,7 +935,8 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
pw = getTunnel().getClientOptions().getProperty(PROP_OUTPROXY_PW); pw = getTunnel().getClientOptions().getProperty(PROP_OUTPROXY_PW);
} }
if(user != null && pw != null) { if(user != null && pw != null) {
newRequest.append("Proxy-Authorization: Basic ").append(Base64.encode((user + ':' + pw).getBytes(), true)) // true = use standard alphabet newRequest.append("Proxy-Authorization: Basic ")
.append(Base64.encode((user + ':' + pw).getBytes("UTF-8"), true)) // true = use standard alphabet
.append("\r\n"); .append("\r\n");
} }
} }
@@ -915,13 +953,18 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
if(method == null || (destination == null && !usingInternalOutproxy)) { if(method == null || (destination == null && !usingInternalOutproxy)) {
//l.log("No HTTP method found in the request."); //l.log("No HTTP method found in the request.");
if (protocol != null && "http".equals(protocol.toLowerCase(Locale.US))) { try {
out.write(getErrorPage("denied", ERR_REQUEST_DENIED)); if (protocol != null && "http".equals(protocol.toLowerCase(Locale.US))) {
} else { out.write(getErrorPage("denied", ERR_REQUEST_DENIED).getBytes("UTF-8"));
out.write(getErrorPage("protocol", ERR_BAD_PROTOCOL)); } else {
out.write(getErrorPage("protocol", ERR_BAD_PROTOCOL).getBytes("UTF-8"));
}
writeFooter(out);
} catch (IOException ioe) {
// ignore
} finally {
closeSocket(s);
} }
writeFooter(out);
s.close();
return; return;
} }
@@ -939,23 +982,33 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
_log.warn(getPrefix(requestId) + "Auth required, sending 407"); _log.warn(getPrefix(requestId) + "Auth required, sending 407");
} }
} }
out.write(getAuthError(result == AuthResult.AUTH_STALE).getBytes()); try {
writeFooter(out); out.write(getAuthError(result == AuthResult.AUTH_STALE).getBytes("UTF-8"));
s.close(); writeFooter(out);
} catch (IOException ioe) {
// ignore
} finally {
closeSocket(s);
}
return; return;
} }
// Serve local proxy files (images, css linked from error pages) // Serve local proxy files (images, css linked from error pages)
// Ignore all the headers // Ignore all the headers
if(usingInternalServer) { if (usingInternalServer) {
// disable the add form if address helper is disabled try {
if(internalPath.equals("/add") && // disable the add form if address helper is disabled
Boolean.parseBoolean(getTunnel().getClientOptions().getProperty(PROP_DISABLE_HELPER))) { if(internalPath.equals("/add") &&
out.write(ERR_HELPER_DISABLED); Boolean.parseBoolean(getTunnel().getClientOptions().getProperty(PROP_DISABLE_HELPER))) {
} else { out.write(ERR_HELPER_DISABLED.getBytes("UTF-8"));
LocalHTTPServer.serveLocalFile(out, method, internalPath, internalRawQuery, _proxyNonce); } else {
LocalHTTPServer.serveLocalFile(out, method, internalPath, internalRawQuery, _proxyNonce);
}
} catch (IOException ioe) {
// ignore
} finally {
closeSocket(s);
} }
s.close();
return; return;
} }
@@ -967,7 +1020,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
byte[] response; byte[] response;
if (method.toUpperCase(Locale.US).equals("CONNECT")) { if (method.toUpperCase(Locale.US).equals("CONNECT")) {
data = null; data = null;
response = SUCCESS_RESPONSE; response = SUCCESS_RESPONSE.getBytes("UTF-8");
} else { } else {
data = newRequest.toString().getBytes("ISO-8859-1"); data = newRequest.toString().getBytes("ISO-8859-1");
response = null; response = null;
@@ -993,9 +1046,14 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
if(_log.shouldLog(Log.WARN)) { if(_log.shouldLog(Log.WARN)) {
_log.warn(getPrefix(requestId) + "Could not find destination for " + addressHelper); _log.warn(getPrefix(requestId) + "Could not find destination for " + addressHelper);
} }
byte[] header = getErrorPage("ahelper-notfound", ERR_AHELPER_NOTFOUND); String header = getErrorPage("ahelper-notfound", ERR_AHELPER_NOTFOUND);
writeErrorMessage(header, out, targetRequest, false, destination); try {
s.close(); writeErrorMessage(header, out, targetRequest, false, destination);
} catch (IOException ioe) {
// ignore
} finally {
closeSocket(s);
}
return; return;
} }
} else if("i2p".equals(host)) { } else if("i2p".equals(host)) {
@@ -1025,7 +1083,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
if(_log.shouldLog(Log.WARN)) { if(_log.shouldLog(Log.WARN)) {
_log.warn("Unable to resolve " + destination + " (proxy? " + usingWWWProxy + ", request: " + targetRequest); _log.warn("Unable to resolve " + destination + " (proxy? " + usingWWWProxy + ", request: " + targetRequest);
} }
byte[] header; String header;
String jumpServers = null; String jumpServers = null;
String extraMessage = null; String extraMessage = null;
if(usingWWWProxy) { if(usingWWWProxy) {
@@ -1042,16 +1100,26 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
jumpServers = DEFAULT_JUMP_SERVERS; jumpServers = DEFAULT_JUMP_SERVERS;
} }
} }
writeErrorMessage(header, extraMessage, out, targetRequest, usingWWWProxy, destination, jumpServers); try {
s.close(); writeErrorMessage(header, extraMessage, out, targetRequest, usingWWWProxy, destination, jumpServers);
} catch (IOException ioe) {
// ignore
} finally {
closeSocket(s);
}
return; return;
} }
if (method.toUpperCase(Locale.US).equals("CONNECT") && if (method.toUpperCase(Locale.US).equals("CONNECT") &&
!usingWWWProxy && !usingWWWProxy &&
!Boolean.parseBoolean(getTunnel().getClientOptions().getProperty(PROP_INTERNAL_SSL))) { !Boolean.parseBoolean(getTunnel().getClientOptions().getProperty(PROP_INTERNAL_SSL))) {
writeErrorMessage(ERR_INTERNAL_SSL, out, targetRequest, false, destination); try {
s.close(); writeErrorMessage(ERR_INTERNAL_SSL, out, targetRequest, false, destination);
} catch (IOException ioe) {
// ignore
} finally {
closeSocket(s);
}
if (_log.shouldLog(Log.WARN)) if (_log.shouldLog(Log.WARN))
_log.warn("SSL to i2p destinations denied by configuration: " + targetRequest); _log.warn("SSL to i2p destinations denied by configuration: " + targetRequest);
return; return;
@@ -1063,8 +1131,13 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
if(ahelperNew && "GET".equals(method) && if(ahelperNew && "GET".equals(method) &&
(userAgent == null || !userAgent.startsWith("Wget")) && (userAgent == null || !userAgent.startsWith("Wget")) &&
!Boolean.parseBoolean(getTunnel().getClientOptions().getProperty(PROP_DISABLE_HELPER))) { !Boolean.parseBoolean(getTunnel().getClientOptions().getProperty(PROP_DISABLE_HELPER))) {
writeHelperSaveForm(out, destination, ahelperKey, targetRequest, referer); try {
s.close(); writeHelperSaveForm(out, destination, ahelperKey, targetRequest, referer);
} catch (IOException ioe) {
// ignore
} finally {
closeSocket(s);
}
return; return;
} }
@@ -1077,10 +1150,15 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
if(_log.shouldLog(Log.DEBUG)) { if(_log.shouldLog(Log.DEBUG)) {
_log.debug("Auto redirecting to " + uri); _log.debug("Auto redirecting to " + uri);
} }
out.write(("HTTP/1.1 301 Address Helper Accepted\r\n" + try {
out.write(("HTTP/1.1 301 Address Helper Accepted\r\n" +
"Location: " + uri + "\r\n" + "Location: " + uri + "\r\n" +
"\r\n").getBytes("UTF-8")); "\r\n").getBytes("UTF-8"));
s.close(); } catch (IOException ioe) {
// ignore
} finally {
closeSocket(s);
}
return; return;
} }
@@ -1103,7 +1181,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
response = null; response = null;
} else { } else {
data = null; data = null;
response = SUCCESS_RESPONSE; response = SUCCESS_RESPONSE.getBytes("UTF-8");
} }
t = new I2PTunnelRunner(s, i2ps, sockLock, data, response, mySockets, onTimeout); t = new I2PTunnelRunner(s, i2ps, sockLock, data, response, mySockets, onTimeout);
} else { } else {
@@ -1157,22 +1235,22 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
} }
/** @since 0.8.7 */ /** @since 0.8.7 */
private void writeHelperSaveForm(OutputStream out, String destination, String ahelperKey, private void writeHelperSaveForm(OutputStream outs, String destination, String ahelperKey,
String targetRequest, String referer) throws IOException { String targetRequest, String referer) throws IOException {
if(out == null) { if(outs == null)
return; return;
} Writer out = new BufferedWriter(new OutputStreamWriter(outs, "UTF-8"));
byte[] header = getErrorPage("ahelper-new", ERR_AHELPER_NEW); String header = getErrorPage("ahelper-new", ERR_AHELPER_NEW);
out.write(header); out.write(header);
out.write(("<table><tr><td class=\"mediumtags\" align=\"right\">" + _("Host") + out.write("<table><tr><td class=\"mediumtags\" align=\"right\">" + _("Host") +
"</td><td class=\"mediumtags\">" + destination + "</td></tr>\n").getBytes()); "</td><td class=\"mediumtags\">" + destination + "</td></tr>\n");
try { try {
String b32 = Base32.encode(SHA256Generator.getInstance().calculateHash(Base64.decode(ahelperKey)).getData()); String b32 = Base32.encode(SHA256Generator.getInstance().calculateHash(Base64.decode(ahelperKey)).getData());
out.write(("<tr><td class=\"mediumtags\" align=\"right\">" + _("Base 32") + "</td>" + out.write("<tr><td class=\"mediumtags\" align=\"right\">" + _("Base 32") + "</td>" +
"<td><a href=\"http://" + b32 + ".b32.i2p/\">" + b32 + ".b32.i2p</a></td></tr>").getBytes()); "<td><a href=\"http://" + b32 + ".b32.i2p/\">" + b32 + ".b32.i2p</a></td></tr>");
} catch(Exception e) { } catch(Exception e) {
} }
out.write(("<tr><td class=\"mediumtags\" align=\"right\">" + _("Destination") + "</td><td>" + out.write("<tr><td class=\"mediumtags\" align=\"right\">" + _("Destination") + "</td><td>" +
"<textarea rows=\"1\" style=\"height: 4em; min-width: 0; min-height: 0;\" cols=\"70\" wrap=\"off\" readonly=\"readonly\" >" + "<textarea rows=\"1\" style=\"height: 4em; min-width: 0; min-height: 0;\" cols=\"70\" wrap=\"off\" readonly=\"readonly\" >" +
ahelperKey + "</textarea></td></tr></table>\n" + ahelperKey + "</textarea></td></tr></table>\n" +
"<hr><div class=\"formaction\">" + "<hr><div class=\"formaction\">" +
@@ -1183,18 +1261,19 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
"<input type=\"hidden\" name=\"host\" value=\"" + destination + "\">\n" + "<input type=\"hidden\" name=\"host\" value=\"" + destination + "\">\n" +
"<input type=\"hidden\" name=\"dest\" value=\"" + ahelperKey + "\">\n" + "<input type=\"hidden\" name=\"dest\" value=\"" + ahelperKey + "\">\n" +
"<input type=\"hidden\" name=\"nonce\" value=\"" + _proxyNonce + "\">\n" + "<input type=\"hidden\" name=\"nonce\" value=\"" + _proxyNonce + "\">\n" +
"<button type=\"submit\" class=\"accept\" name=\"router\" value=\"router\">" + _("Save {0} to router address book and continue to website", destination) + "</button><br>\n").getBytes("UTF-8")); "<button type=\"submit\" class=\"accept\" name=\"router\" value=\"router\">" +
_("Save {0} to router address book and continue to website", destination) + "</button><br>\n");
if(_context.namingService().getName().equals("BlockfileNamingService")) { if(_context.namingService().getName().equals("BlockfileNamingService")) {
// only blockfile supports multiple books // only blockfile supports multiple books
out.write(("<br><button type=\"submit\" name=\"master\" value=\"master\">" + _("Save {0} to master address book and continue to website", destination) + "</button><br>\n").getBytes("UTF-8")); out.write("<br><button type=\"submit\" name=\"master\" value=\"master\">" + _("Save {0} to master address book and continue to website", destination) + "</button><br>\n");
out.write(("<button type=\"submit\" name=\"private\" value=\"private\">" + _("Save {0} to private address book and continue to website", destination) + "</button>\n").getBytes("UTF-8")); out.write("<button type=\"submit\" name=\"private\" value=\"private\">" + _("Save {0} to private address book and continue to website", destination) + "</button>\n");
} }
// Firefox (and others?) don't send referer to meta refresh target, which is // Firefox (and others?) don't send referer to meta refresh target, which is
// what the jump servers use, so this isn't that useful. // what the jump servers use, so this isn't that useful.
if (referer != null) if (referer != null)
out.write(("<input type=\"hidden\" name=\"referer\" value=\"" + referer + "\">\n").getBytes("UTF-8")); out.write("<input type=\"hidden\" name=\"referer\" value=\"" + referer + "\">\n");
out.write(("<input type=\"hidden\" name=\"url\" value=\"" + targetRequest + "\">\n" + out.write("<input type=\"hidden\" name=\"url\" value=\"" + targetRequest + "\">\n" +
"</form></div></div>").getBytes()); "</form></div></div>");
writeFooter(out); writeFooter(out);
} }
@@ -1296,11 +1375,11 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
return lc.equals("http") || lc.equals("https"); return lc.equals("http") || lc.equals("https");
} }
private final static byte[] ERR_HELPER_DISABLED = private final static String ERR_HELPER_DISABLED =
("HTTP/1.1 403 Disabled\r\n" + "HTTP/1.1 403 Disabled\r\n" +
"Content-Type: text/plain\r\n" + "Content-Type: text/plain\r\n" +
"\r\n" + "\r\n" +
"Address helpers disabled").getBytes(); "Address helpers disabled";
/** /**
* Change various parts of the URI. * Change various parts of the URI.

View File

@@ -3,12 +3,15 @@
*/ */
package net.i2p.i2ptunnel; package net.i2p.i2ptunnel;
import java.io.BufferedWriter;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Reader; import java.io.Reader;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.net.Socket; import java.net.Socket;
import java.net.URI; import java.net.URI;
import java.net.URISyntaxException; import java.net.URISyntaxException;
@@ -71,18 +74,17 @@ public abstract class I2PTunnelHTTPClientBase extends I2PTunnelClientBase implem
protected final List<String> _proxyList; protected final List<String> _proxyList;
protected final static byte[] ERR_NO_OUTPROXY = protected final static String ERR_NO_OUTPROXY =
("HTTP/1.1 503 Service Unavailable\r\n"+ "HTTP/1.1 503 Service Unavailable\r\n"+
"Content-Type: text/html; charset=iso-8859-1\r\n"+ "Content-Type: text/html; charset=iso-8859-1\r\n"+
"Cache-control: no-cache\r\n"+ "Cache-control: no-cache\r\n"+
"\r\n"+ "\r\n"+
"<html><body><H1>I2P ERROR: No outproxy found</H1>"+ "<html><body><H1>I2P ERROR: No outproxy found</H1>"+
"Your request was for a site outside of I2P, but you have no "+ "Your request was for a site outside of I2P, but you have no "+
"HTTP outproxy configured. Please configure an outproxy in I2PTunnel") "HTTP outproxy configured. Please configure an outproxy in I2PTunnel";
.getBytes();
protected final static byte[] ERR_DESTINATION_UNKNOWN = protected final static String ERR_DESTINATION_UNKNOWN =
("HTTP/1.1 503 Service Unavailable\r\n" + "HTTP/1.1 503 Service Unavailable\r\n" +
"Content-Type: text/html; charset=iso-8859-1\r\n" + "Content-Type: text/html; charset=iso-8859-1\r\n" +
"Cache-control: no-cache\r\n" + "Cache-control: no-cache\r\n" +
"\r\n" + "\r\n" +
@@ -91,13 +93,12 @@ public abstract class I2PTunnelHTTPClientBase extends I2PTunnelClientBase implem
"wrong BASE64 I2P Destination or the link you are following is " + "wrong BASE64 I2P Destination or the link you are following is " +
"bad. The host (or the WWW proxy, if you're using one) could also " + "bad. The host (or the WWW proxy, if you're using one) could also " +
"be temporarily offline. You may want to <b>retry</b>. " + "be temporarily offline. You may want to <b>retry</b>. " +
"Could not find the following Destination:<BR><BR><div>").getBytes(); "Could not find the following Destination:<BR><BR><div>";
protected final static byte[] SUCCESS_RESPONSE = protected final static String SUCCESS_RESPONSE =
("HTTP/1.1 200 Connection Established\r\n"+ "HTTP/1.1 200 Connection Established\r\n"+
"Proxy-agent: I2P\r\n"+ "Proxy-agent: I2P\r\n"+
"\r\n") "\r\n";
.getBytes();
private final byte[] _proxyNonce; private final byte[] _proxyNonce;
private final ConcurrentHashMap<String, NonceInfo> _nonces; private final ConcurrentHashMap<String, NonceInfo> _nonces;
@@ -484,7 +485,7 @@ public abstract class I2PTunnelHTTPClientBase extends I2PTunnelClientBase implem
* @return non-null * @return non-null
* @since 0.9.4 moved from I2PTunnelHTTPClient * @since 0.9.4 moved from I2PTunnelHTTPClient
*/ */
protected byte[] getErrorPage(String base, byte[] backup) { protected String getErrorPage(String base, String backup) {
return getErrorPage(_context, base, backup); return getErrorPage(_context, base, backup);
} }
@@ -499,7 +500,7 @@ public abstract class I2PTunnelHTTPClientBase extends I2PTunnelClientBase implem
* @return non-null * @return non-null
* @since 0.9.4 moved from I2PTunnelHTTPClient * @since 0.9.4 moved from I2PTunnelHTTPClient
*/ */
protected static byte[] getErrorPage(I2PAppContext ctx, String base, byte[] backup) { protected static String getErrorPage(I2PAppContext ctx, String base, String backup) {
File errorDir = new File(ctx.getBaseDir(), "docs"); File errorDir = new File(ctx.getBaseDir(), "docs");
File file = new File(errorDir, base + "-header.ht"); File file = new File(errorDir, base + "-header.ht");
try { try {
@@ -515,7 +516,7 @@ public abstract class I2PTunnelHTTPClientBase extends I2PTunnelClientBase implem
/** /**
* @since 0.9.4 moved from I2PTunnelHTTPClient * @since 0.9.4 moved from I2PTunnelHTTPClient
*/ */
private static byte[] readFile(I2PAppContext ctx, File file) throws IOException { private static String readFile(I2PAppContext ctx, File file) throws IOException {
Reader reader = null; Reader reader = null;
char[] buf = new char[512]; char[] buf = new char[512];
StringBuilder out = new StringBuilder(2048); StringBuilder out = new StringBuilder(2048);
@@ -525,7 +526,7 @@ public abstract class I2PTunnelHTTPClientBase extends I2PTunnelClientBase implem
while((len = reader.read(buf)) > 0) { while((len = reader.read(buf)) > 0) {
out.append(buf, 0, len); out.append(buf, 0, len);
} }
return out.toString().getBytes("UTF-8"); return out.toString();
} finally { } finally {
try { try {
if(reader != null) if(reader != null)
@@ -578,7 +579,7 @@ public abstract class I2PTunnelHTTPClientBase extends I2PTunnelClientBase implem
boolean usingWWWProxy, String wwwProxy, long requestId) { boolean usingWWWProxy, String wwwProxy, long requestId) {
if (out == null) if (out == null)
return; return;
byte[] header; String header;
if (usingWWWProxy) if (usingWWWProxy)
header = getErrorPage(I2PAppContext.getGlobalContext(), "dnfp", ERR_DESTINATION_UNKNOWN); header = getErrorPage(I2PAppContext.getGlobalContext(), "dnfp", ERR_DESTINATION_UNKNOWN);
else else
@@ -612,7 +613,7 @@ public abstract class I2PTunnelHTTPClientBase extends I2PTunnelClientBase implem
} else { } else {
error = usingWWWProxy ? "dnfp" : "dnf"; error = usingWWWProxy ? "dnfp" : "dnf";
} }
byte[] header = getErrorPage(error, ERR_DESTINATION_UNKNOWN); String header = getErrorPage(error, ERR_DESTINATION_UNKNOWN);
String message = ise != null ? ise.getLocalizedMessage() : "unknown error"; String message = ise != null ? ise.getLocalizedMessage() : "unknown error";
try { try {
writeErrorMessage(header, message, out, targetRequest, usingWWWProxy, wwwProxy); writeErrorMessage(header, message, out, targetRequest, usingWWWProxy, wwwProxy);
@@ -623,7 +624,7 @@ public abstract class I2PTunnelHTTPClientBase extends I2PTunnelClientBase implem
* No jump servers or extra message * No jump servers or extra message
* @since 0.9.14 * @since 0.9.14
*/ */
protected void writeErrorMessage(byte[] errMessage, OutputStream out, String targetRequest, protected void writeErrorMessage(String errMessage, OutputStream out, String targetRequest,
boolean usingWWWProxy, String wwwProxy) throws IOException { boolean usingWWWProxy, String wwwProxy) throws IOException {
writeErrorMessage(errMessage, null, out, targetRequest, usingWWWProxy, wwwProxy, null); writeErrorMessage(errMessage, null, out, targetRequest, usingWWWProxy, wwwProxy, null);
} }
@@ -633,7 +634,7 @@ public abstract class I2PTunnelHTTPClientBase extends I2PTunnelClientBase implem
* @param jumpServers comma- or space-separated list, or null * @param jumpServers comma- or space-separated list, or null
* @since 0.9.14 moved from subclasses * @since 0.9.14 moved from subclasses
*/ */
protected void writeErrorMessage(byte[] errMessage, OutputStream out, String targetRequest, protected void writeErrorMessage(String errMessage, OutputStream out, String targetRequest,
boolean usingWWWProxy, String wwwProxy, String jumpServers) throws IOException { boolean usingWWWProxy, String wwwProxy, String jumpServers) throws IOException {
writeErrorMessage(errMessage, null, out, targetRequest, usingWWWProxy, wwwProxy, jumpServers); writeErrorMessage(errMessage, null, out, targetRequest, usingWWWProxy, wwwProxy, jumpServers);
} }
@@ -643,7 +644,7 @@ public abstract class I2PTunnelHTTPClientBase extends I2PTunnelClientBase implem
* @param extraMessage extra message or null, will be HTML-escaped * @param extraMessage extra message or null, will be HTML-escaped
* @since 0.9.14 * @since 0.9.14
*/ */
protected void writeErrorMessage(byte[] errMessage, String extraMessage, protected void writeErrorMessage(String errMessage, String extraMessage,
OutputStream out, String targetRequest, OutputStream out, String targetRequest,
boolean usingWWWProxy, String wwwProxy) throws IOException { boolean usingWWWProxy, String wwwProxy) throws IOException {
writeErrorMessage(errMessage, extraMessage, out, targetRequest, usingWWWProxy, wwwProxy, null); writeErrorMessage(errMessage, extraMessage, out, targetRequest, usingWWWProxy, wwwProxy, null);
@@ -654,27 +655,28 @@ public abstract class I2PTunnelHTTPClientBase extends I2PTunnelClientBase implem
* @param extraMessage extra message or null, will be HTML-escaped * @param extraMessage extra message or null, will be HTML-escaped
* @since 0.9.14 * @since 0.9.14
*/ */
protected void writeErrorMessage(byte[] errMessage, String extraMessage, protected void writeErrorMessage(String errMessage, String extraMessage,
OutputStream out, String targetRequest, OutputStream outs, String targetRequest,
boolean usingWWWProxy, String wwwProxy, boolean usingWWWProxy, String wwwProxy,
String jumpServers) throws IOException { String jumpServers) throws IOException {
if (out == null) if (outs == null)
return; return;
Writer out = new BufferedWriter(new OutputStreamWriter(outs, "UTF-8"));
out.write(errMessage); out.write(errMessage);
if (targetRequest != null) { if (targetRequest != null) {
String uri = targetRequest.replace("&", "&amp;"); String uri = targetRequest.replace("&", "&amp;");
out.write("<a href=\"".getBytes()); out.write("<a href=\"");
out.write(uri.getBytes()); out.write(uri);
out.write("\">".getBytes()); out.write("\">");
out.write(uri.getBytes()); out.write(uri);
out.write("</a>".getBytes()); out.write("</a>");
if (usingWWWProxy) { if (usingWWWProxy) {
out.write(("<br><br><b>").getBytes()); out.write("<br><br><b>");
out.write(_("HTTP Outproxy").getBytes("UTF-8")); out.write(_("HTTP Outproxy"));
out.write((":</b> " + wwwProxy).getBytes()); out.write(":</b> " + wwwProxy);
} }
if (extraMessage != null) { if (extraMessage != null) {
out.write(("<br><br><b>" + DataHelper.escapeHTML(extraMessage) + "</b>").getBytes()); out.write("<br><br><b>" + DataHelper.escapeHTML(extraMessage) + "</b>");
} }
if (jumpServers != null && jumpServers.length() > 0) { if (jumpServers != null && jumpServers.length() > 0) {
boolean first = true; boolean first = true;
@@ -708,23 +710,23 @@ public abstract class I2PTunnelHTTPClientBase extends I2PTunnelClientBase implem
if (first) { if (first) {
first = false; first = false;
out.write("<br><br><h3>".getBytes()); out.write("<br><br><h3>");
out.write(_("Click a link below for an address helper from a jump service").getBytes("UTF-8")); out.write(_("Click a link below for an address helper from a jump service"));
out.write("</h3>\n".getBytes()); out.write("</h3>\n");
} else { } else {
out.write("<br>".getBytes()); out.write("<br>");
} }
out.write("<a href=\"".getBytes()); out.write("<a href=\"");
out.write(jurl.getBytes()); out.write(jurl);
out.write(uri.getBytes()); out.write(uri);
out.write("\">".getBytes()); out.write("\">");
// Translators: parameter is a host name // Translators: parameter is a host name
out.write(_("{0} jump service", jumphost).getBytes()); out.write(_("{0} jump service", jumphost));
out.write("</a>\n".getBytes()); out.write("</a>\n");
} }
} }
} }
out.write("</div>".getBytes()); out.write("</div>");
writeFooter(out); writeFooter(out);
} }
@@ -735,12 +737,29 @@ public abstract class I2PTunnelHTTPClientBase extends I2PTunnelClientBase implem
* @since 0.9.14 moved from I2PTunnelHTTPClient * @since 0.9.14 moved from I2PTunnelHTTPClient
*/ */
public static void writeFooter(OutputStream out) throws IOException { public static void writeFooter(OutputStream out) throws IOException {
out.write(getFooter().getBytes("UTF-8"));
out.flush();
}
/**
* Flushes.
*
* Public only for LocalHTTPServer, not for general use
* @since 0.9.19
*/
public static void writeFooter(Writer out) throws IOException {
out.write(getFooter());
out.flush();
}
private static String getFooter() {
// The css is hiding this div for now, but we'll keep it here anyway // The css is hiding this div for now, but we'll keep it here anyway
// Tag the strings below for translation if we unhide it. // Tag the strings below for translation if we unhide it.
out.write("<div class=\"proxyfooter\"><p><i>I2P HTTP Proxy Server<br>Generated on: ".getBytes()); StringBuilder buf = new StringBuilder(128);
out.write(new Date().toString().getBytes()); buf.append("<div class=\"proxyfooter\"><p><i>I2P HTTP Proxy Server<br>Generated on: ")
out.write("</i></div></body></html>\n".getBytes()); .append(new Date().toString())
out.flush(); .append("</i></div></body></html>\n");
return buf.toString();
} }
/** /**

View File

@@ -327,7 +327,7 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
long afterHandle = getTunnel().getContext().clock().now(); long afterHandle = getTunnel().getContext().clock().now();
long timeToHandle = afterHandle - afterAccept; long timeToHandle = afterHandle - afterAccept;
getTunnel().getContext().statManager().addRateData("i2ptunnel.httpserver.blockingHandleTime", timeToHandle, 0); getTunnel().getContext().statManager().addRateData("i2ptunnel.httpserver.blockingHandleTime", timeToHandle);
if ( (timeToHandle > 1000) && (_log.shouldLog(Log.WARN)) ) if ( (timeToHandle > 1000) && (_log.shouldLog(Log.WARN)) )
_log.warn("Took a while to handle the request for " + remoteHost + ':' + remotePort + _log.warn("Took a while to handle the request for " + remoteHost + ':' + remotePort +
" [" + timeToHandle + " [" + timeToHandle +
@@ -696,7 +696,7 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
} }
} }
if (trimmed > 0) if (trimmed > 0)
ctx.statManager().addRateData("i2ptunnel.httpNullWorkaround", trimmed, 0); ctx.statManager().addRateData("i2ptunnel.httpNullWorkaround", trimmed);
int i = 0; int i = 0;
while (true) { while (true) {

View File

@@ -983,7 +983,7 @@ public class TunnelController implements Logging {
* @return list of messages pulled off (each is a String, earliest first) * @return list of messages pulled off (each is a String, earliest first)
*/ */
public List<String> clearMessages() { public List<String> clearMessages() {
List<String> rv = null; List<String> rv;
synchronized (_messages) { synchronized (_messages) {
rv = new ArrayList<String>(_messages); rv = new ArrayList<String>(_messages);
_messages.clear(); _messages.clear();

View File

@@ -33,19 +33,17 @@ import net.i2p.util.Translate;
*/ */
public abstract class LocalHTTPServer { public abstract class LocalHTTPServer {
private final static byte[] ERR_404 = private final static String ERR_404 =
("HTTP/1.1 404 Not Found\r\n"+ "HTTP/1.1 404 Not Found\r\n"+
"Content-Type: text/plain\r\n"+ "Content-Type: text/plain\r\n"+
"\r\n"+ "\r\n"+
"HTTP Proxy local file not found") "HTTP Proxy local file not found";
.getBytes();
private final static byte[] ERR_ADD = private final static String ERR_ADD =
("HTTP/1.1 409 Bad\r\n"+ "HTTP/1.1 409 Bad\r\n"+
"Content-Type: text/plain\r\n"+ "Content-Type: text/plain\r\n"+
"\r\n"+ "\r\n"+
"Add to addressbook failed - bad parameters") "Add to addressbook failed - bad parameters";
.getBytes();
/** /**
* Very simple web server. * Very simple web server.
@@ -69,14 +67,13 @@ public abstract class LocalHTTPServer {
* @param targetRequest decoded path only, non-null * @param targetRequest decoded path only, non-null
* @param query raw (encoded), may be null * @param query raw (encoded), may be null
*/ */
public static void serveLocalFile(OutputStream out, String method, String targetRequest, String query, String proxyNonce) { public static void serveLocalFile(OutputStream out, String method, String targetRequest,
String query, String proxyNonce) throws IOException {
//System.err.println("targetRequest: \"" + targetRequest + "\""); //System.err.println("targetRequest: \"" + targetRequest + "\"");
// a home page message for the curious... // a home page message for the curious...
if (targetRequest.equals("/")) { if (targetRequest.equals("/")) {
try { out.write(("HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nCache-Control: max-age=86400\r\n\r\nI2P HTTP proxy OK").getBytes("UTF-8"));
out.write(("HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nCache-Control: max-age=86400\r\n\r\nI2P HTTP proxy OK").getBytes()); out.flush();
out.flush();
} catch (IOException ioe) {}
return; return;
} }
if ((method.equals("GET") || method.equals("HEAD")) && if ((method.equals("GET") || method.equals("HEAD")) &&
@@ -104,12 +101,10 @@ public abstract class LocalHTTPServer {
else if (filename.endsWith(".jpg")) else if (filename.endsWith(".jpg"))
type = "image/jpeg"; type = "image/jpeg";
else type = "text/html"; else type = "text/html";
try { out.write("HTTP/1.1 200 OK\r\nContent-Type: ".getBytes("UTF-8"));
out.write("HTTP/1.1 200 OK\r\nContent-Type: ".getBytes()); out.write(type.getBytes("UTF-8"));
out.write(type.getBytes()); out.write("\r\nCache-Control: max-age=86400\r\n\r\n".getBytes("UTF-8"));
out.write("\r\nCache-Control: max-age=86400\r\n\r\n".getBytes()); FileUtil.readFile(filename, themesDir.getAbsolutePath(), out);
FileUtil.readFile(filename, themesDir.getAbsolutePath(), out);
} catch (IOException ioe) {}
return; return;
} }
} }
@@ -153,31 +148,24 @@ public abstract class LocalHTTPServer {
//System.err.println("book : \"" + book + "\""); //System.err.println("book : \"" + book + "\"");
//System.err.println("nonce : \"" + nonce + "\""); //System.err.println("nonce : \"" + nonce + "\"");
if (proxyNonce.equals(nonce) && url != null && host != null && dest != null) { if (proxyNonce.equals(nonce) && url != null && host != null && dest != null) {
try { NamingService ns = I2PAppContext.getGlobalContext().namingService();
NamingService ns = I2PAppContext.getGlobalContext().namingService(); Properties nsOptions = new Properties();
Properties nsOptions = new Properties(); nsOptions.setProperty("list", book);
nsOptions.setProperty("list", book); if (referer != null && referer.startsWith("http")) {
if (referer != null && referer.startsWith("http")) { String from = "<a href=\"" + referer + "\">" + referer + "</a>";
String from = "<a href=\"" + referer + "\">" + referer + "</a>"; nsOptions.setProperty("s", _("Added via address helper from {0}", from));
nsOptions.setProperty("s", _("Added via address helper from {0}", from)); } else {
} else { nsOptions.setProperty("s", _("Added via address helper"));
nsOptions.setProperty("s", _("Added via address helper")); }
} boolean success = ns.put(host, dest, nsOptions);
boolean success = ns.put(host, dest, nsOptions); writeRedirectPage(out, success, host, book, url);
writeRedirectPage(out, success, host, book, url); return;
return;
} catch (IOException ioe) {}
} }
try { out.write(ERR_ADD.getBytes("UTF-8"));
out.write(ERR_ADD); } else {
out.flush(); out.write(ERR_404.getBytes("UTF-8"));
} catch (IOException ioe) {}
return;
} }
try { out.flush();
out.write(ERR_404);
out.flush();
} catch (IOException ioe) {}
} }
/** @since 0.8.7 */ /** @since 0.8.7 */