nanohttpd: refactor

This commit is contained in:
Haowei Wen 2020-08-25 23:29:54 +08:00
parent d72035a066
commit 05f61d92c6
No known key found for this signature in database
GPG key ID: 5BC167F73EA558E4
2 changed files with 71 additions and 100 deletions

View file

@ -165,7 +165,7 @@ public class URLProcessor {
String url = session.getQueryParameterString() == null ? upstream : upstream + "?" + session.getQueryParameterString();
Map<String, String> requestHeaders = session.getHeaders();
Map<String, String> requestHeaders = new LinkedHashMap<>(session.getHeaders());
ignoredHeaders.forEach(requestHeaders::remove);
InputStream clientIn = session.getInputStream();

View file

@ -65,7 +65,7 @@ import java.net.SocketTimeoutException;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
@ -175,30 +175,19 @@ public abstract class NanoHTTPD {
public static final int BUFSIZE = 8192;
private final OutputStream outputStream;
private final BufferedInputStream inputStream;
private String uri;
private String method;
private String queryParameterString;
private Map<String, List<String>> parms;
private Map<String, String> headers;
private String protocolVersion;
private InputStream parsedInputStream;
private int splitbyte;
private int rlen;
private String uri;
private String method;
private Map<String, List<String>> parms;
private Map<String, String> headers;
private String queryParameterString;
private String remoteIp;
private String remoteHostname;
private String protocolVersion;
private final String remoteIp;
private final String remoteHostname;
private boolean expect100Continue;
private boolean continueSent;
@ -210,29 +199,24 @@ public abstract class NanoHTTPD {
this.outputStream = outputStream;
this.remoteIp = inetAddress.isLoopbackAddress() || inetAddress.isAnyLocalAddress() ? "127.0.0.1" : inetAddress.getHostAddress();
this.remoteHostname = inetAddress.isLoopbackAddress() || inetAddress.isAnyLocalAddress() ? "localhost" : inetAddress.getHostName();
this.headers = new HashMap<>();
}
/**
* Decodes the sent headers and loads the data into Key/value pairs
*/
private void decodeHeader(BufferedReader in, Map<String, String> pre, Map<String, List<String>> parms, Map<String, String> headers) throws ResponseException {
private void parseHeader(BufferedReader in) throws ResponseException {
try {
// Read the request line
String inLine = in.readLine();
if (inLine == null) {
return;
String requestLine = in.readLine();
if (requestLine == null) {
throw new ResponseException(Status.BAD_REQUEST, "BAD REQUEST: Syntax error.");
}
StringTokenizer st = new StringTokenizer(inLine);
StringTokenizer st = new StringTokenizer(requestLine);
if (!st.hasMoreTokens()) {
throw new ResponseException(Status.BAD_REQUEST, "BAD REQUEST: Syntax error. Usage: GET /example/file.html");
throw new ResponseException(Status.BAD_REQUEST, "BAD REQUEST: Syntax error.");
}
pre.put("method", st.nextToken());
this.method = st.nextToken();
if (!st.hasMoreTokens()) {
throw new ResponseException(Status.BAD_REQUEST, "BAD REQUEST: Missing URI. Usage: GET /example/file.html");
throw new ResponseException(Status.BAD_REQUEST, "BAD REQUEST: Missing URI.");
}
String uri = st.nextToken();
@ -240,12 +224,17 @@ public abstract class NanoHTTPD {
// Decode parameters from the URI
int qmi = uri.indexOf('?');
if (qmi >= 0) {
decodeParms(uri.substring(qmi + 1), parms);
this.queryParameterString = uri.substring(qmi + 1);
this.parms = Collections.unmodifiableMap(decodeParms(this.queryParameterString));
uri = decodePercent(uri.substring(0, qmi));
} else {
this.queryParameterString = null;
this.parms = Collections.emptyMap();
uri = decodePercent(uri);
}
this.uri = uri;
// If there's another token, its protocol version,
// followed by HTTP headers.
// NOTE: this now forces header names lower case since they are
@ -256,58 +245,23 @@ public abstract class NanoHTTPD {
protocolVersion = "HTTP/1.1";
NanoHTTPD.LOG.log(Level.FINE, "no protocol version specified, strange. Assuming HTTP/1.1.");
}
Map<String, String> headers = new LinkedHashMap<>();
String line = in.readLine();
while (line != null && !line.trim().isEmpty()) {
int p = line.indexOf(':');
if (p >= 0) {
headers.put(line.substring(0, p).trim().toLowerCase(Locale.US), line.substring(p + 1).trim());
headers.put(line.substring(0, p).trim().toLowerCase(Locale.ROOT), line.substring(p + 1).trim());
}
line = in.readLine();
}
this.headers = Collections.unmodifiableMap(headers);
pre.put("uri", uri);
} catch (IOException ioe) {
throw new ResponseException(Status.INTERNAL_ERROR, "SERVER INTERNAL ERROR: IOException: " + ioe.getMessage(), ioe);
}
}
/**
* Decodes parameters in percent-encoded URI-format ( e.g.
* "name=Jack%20Daniels&pass=Single%20Malt" ) and adds them to given
* Map.
*/
private void decodeParms(String parms, Map<String, List<String>> p) {
if (parms == null) {
this.queryParameterString = "";
return;
}
this.queryParameterString = parms;
StringTokenizer st = new StringTokenizer(parms, "&");
while (st.hasMoreTokens()) {
String e = st.nextToken();
int sep = e.indexOf('=');
String key = null;
String value = null;
if (sep >= 0) {
key = decodePercent(e.substring(0, sep)).trim();
value = decodePercent(e.substring(sep + 1));
} else {
key = decodePercent(e).trim();
value = "";
}
List<String> values = p.get(key);
if (values == null) {
values = new ArrayList<>();
p.put(key, values);
}
values.add(value);
}
}
@SuppressWarnings("resource")
@Override
public void execute() throws IOException {
@ -319,8 +273,8 @@ public abstract class NanoHTTPD {
// Do NOT assume that a single read will get the entire header
// at once!
byte[] buf = new byte[HTTPSession.BUFSIZE];
this.splitbyte = 0;
this.rlen = 0;
int splitbyte = 0;
int rlen = 0;
int read = -1;
this.inputStream.mark(HTTPSession.BUFSIZE);
@ -338,36 +292,20 @@ public abstract class NanoHTTPD {
throw new SocketException("NanoHttpd Shutdown");
}
while (read > 0) {
this.rlen += read;
this.splitbyte = findHeaderEnd(buf, this.rlen);
if (this.splitbyte > 0) {
rlen += read;
splitbyte = findHeaderEnd(buf, rlen);
if (splitbyte > 0) {
break;
}
read = this.inputStream.read(buf, this.rlen, HTTPSession.BUFSIZE - this.rlen);
read = this.inputStream.read(buf, rlen, HTTPSession.BUFSIZE - rlen);
}
if (this.splitbyte < this.rlen) {
if (splitbyte < rlen) {
this.inputStream.reset();
this.inputStream.skip(this.splitbyte);
this.inputStream.skip(splitbyte);
}
this.parms = new HashMap<>();
if (null == this.headers) {
this.headers = new HashMap<>();
} else {
this.headers.clear();
}
// Create a BufferedReader for parsing the header.
BufferedReader hin = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(buf, 0, this.rlen), ISO_8859_1));
// Decode the header into parms and header java properties
Map<String, String> pre = new HashMap<>();
decodeHeader(hin, pre, this.parms, this.headers);
this.method = pre.get("method");
this.uri = pre.get("uri");
parseHeader(new BufferedReader(new InputStreamReader(new ByteArrayInputStream(buf, 0, rlen), ISO_8859_1)));
String connection = this.headers.get("connection");
boolean keepAlive = "HTTP/1.1".equals(protocolVersion) && (connection == null || !connection.matches("(?i).*close.*"));
@ -663,6 +601,39 @@ public abstract class NanoHTTPD {
return decoded;
}
/**
* Decodes parameters in percent-encoded URI-format ( e.g.
* "name=Jack%20Daniels&pass=Single%20Malt" ) and adds them to given
* Map.
*/
private static Map<String, List<String>> decodeParms(String parms) {
Map<String, List<String>> result = new LinkedHashMap<>();
StringTokenizer st = new StringTokenizer(parms, "&");
while (st.hasMoreTokens()) {
String e = st.nextToken();
int sep = e.indexOf('=');
String key = null;
String value = null;
if (sep >= 0) {
key = decodePercent(e.substring(0, sep)).trim();
value = decodePercent(e.substring(sep + 1));
} else {
key = decodePercent(e).trim();
value = "";
}
List<String> values = result.get(key);
if (values == null) {
values = new ArrayList<>();
result.put(key, values);
}
values.add(value);
}
return result;
}
public final int getListeningPort() {
return this.myServerSocket == null ? -1 : this.myServerSocket.getLocalPort();
}