Added test for http parser and fixed some issues
This commit is contained in:
parent
c196a7cca0
commit
9934ff1df5
8 changed files with 163 additions and 55 deletions
|
|
@ -26,6 +26,7 @@ group = 'se.koc'
|
|||
version = '1.0.0-SNAPSHOT'
|
||||
sourceCompatibility = 1.8
|
||||
targetCompatibility = 1.8
|
||||
compileJava.options.encoding = 'UTF-8'
|
||||
|
||||
java {
|
||||
withSourcesJar()
|
||||
|
|
|
|||
|
|
@ -35,31 +35,34 @@ import java.util.TreeMap;
|
|||
public class HttpHeader {
|
||||
// Constants
|
||||
|
||||
public static final String HEADER_CACHE_CONTROL = "Cache-Control";
|
||||
public static final String HEADER_COOKIE = "Cookie";
|
||||
public static final String HEADER_CONTENT_TYPE = "Content-Type";
|
||||
public static final String HEADER_CONTENT_LENGTH = "Content-Length";
|
||||
public static final String HEADER_COOKIE = "Cookie";
|
||||
public static final String HEADER_SET_COOKIE = "Set-Cookie";
|
||||
public static final String HEADER_SERVER = "Server";
|
||||
public static final String HEADER_USER_AGENT = "User-Agent";
|
||||
public static final String HEADER_IF_NONE_MATCH = "If-None-Match";
|
||||
|
||||
// Variables
|
||||
|
||||
private boolean isRequest = true;
|
||||
|
||||
/** Specifies the protocol that should be used */
|
||||
private String protocol = "HTTP";
|
||||
private String protocol = null;
|
||||
/** The protocol version specified in the header */
|
||||
private float protocolVersion = 1.0f;
|
||||
private float protocolVersion = -1;
|
||||
|
||||
/** HTTP type specified in a HTTP request, e.g GET POST DELETE PUT etc */
|
||||
private String requestType = "GET";
|
||||
private String requestType = null;
|
||||
/** String containing the target URL */
|
||||
private String requestUrl = "/";
|
||||
private String requestUrl = null;
|
||||
/** Map containing all the properties from the URL */
|
||||
private Map<String, String> requestUrlAttributes = new HashMap<>();
|
||||
|
||||
/** Status code specified in a HTTP response message */
|
||||
private int responseStatusCode = 200;
|
||||
private int responseStatusCode = -1;
|
||||
private String responseStatusString = null;
|
||||
|
||||
/** An Map of all header fields */
|
||||
private Map<String, String> headers = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
|
||||
|
|
@ -136,8 +139,16 @@ public class HttpHeader {
|
|||
|
||||
public void setResponseStatusCode(int code) {
|
||||
this.responseStatusCode = code;
|
||||
this.responseStatusString = getResponseStatusString(code);
|
||||
}
|
||||
|
||||
public String getResponseStatusString() {
|
||||
return responseStatusString;
|
||||
}
|
||||
|
||||
public void getResponseStatusString(String msg) {
|
||||
this.responseStatusString = msg;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the URL that the client sent the server
|
||||
|
|
@ -303,10 +314,6 @@ public class HttpHeader {
|
|||
}
|
||||
|
||||
|
||||
public String getResponseStatusString() {
|
||||
return getResponseStatusString(responseStatusCode);
|
||||
}
|
||||
|
||||
public static String getResponseStatusString(int type) {
|
||||
switch (type) {
|
||||
case 100: return "Continue";
|
||||
|
|
|
|||
|
|
@ -101,28 +101,34 @@ public class HttpHeaderParser {
|
|||
* @param statusLine the status line String
|
||||
*/
|
||||
private static void parseStatusLine(HttpHeader header, String statusLine) {
|
||||
String[] lineArr = statusLine.split(" ", 3);
|
||||
|
||||
if (lineArr.length != 3)
|
||||
return;
|
||||
|
||||
// Server Response
|
||||
if (statusLine.startsWith("HTTP/")) {
|
||||
if (lineArr[0].contains("/")) {
|
||||
header.setIsRequest(false);
|
||||
header.setProtocolVersion(Float.parseFloat(statusLine.substring(5, 8)));
|
||||
header.setResponseStatusCode(Integer.parseInt(statusLine.substring(9, statusLine.indexOf(' ', 9))));
|
||||
header.setProtocol(lineArr[0].substring(0, lineArr[0].indexOf('/')));
|
||||
header.setProtocolVersion(Float.parseFloat(lineArr[0].substring(lineArr[0].indexOf('/')+1)));
|
||||
header.setResponseStatusCode(Integer.parseInt(lineArr[1]));
|
||||
header.getResponseStatusString(lineArr[2]);
|
||||
}
|
||||
// Client Request
|
||||
else if (statusLine.contains("HTTP/")) {
|
||||
else {
|
||||
header.setIsRequest(true);
|
||||
header.setRequestType(statusLine.substring(0, statusLine.indexOf(" ")).trim());
|
||||
header.setProtocolVersion(Float.parseFloat(statusLine.substring(statusLine.lastIndexOf("HTTP/") + 5).trim()));
|
||||
statusLine = (statusLine.substring(header.getRequestType().length() + 1, statusLine.lastIndexOf("HTTP/")));
|
||||
header.setRequestType(lineArr[0]);
|
||||
header.setProtocol(lineArr[2].substring(0, lineArr[2].indexOf('/')));
|
||||
header.setProtocolVersion(Float.parseFloat(lineArr[2].substring(lineArr[2].indexOf('/')+1)));
|
||||
String url = lineArr[1];
|
||||
|
||||
// parse URL and attributes
|
||||
int index = statusLine.indexOf('?');
|
||||
if (index > -1) {
|
||||
header.setRequestURL(statusLine.substring(0, index));
|
||||
statusLine = statusLine.substring(index + 1);
|
||||
parseURLParameters(header.getURLAttributeMap(), statusLine);
|
||||
} else {
|
||||
header.setRequestURL(statusLine);
|
||||
int paramStartIndex = url.indexOf('?');
|
||||
if (paramStartIndex >= 0) {
|
||||
url = statusLine.substring(0, paramStartIndex);
|
||||
parseURLParameters(header.getURLAttributeMap(), statusLine.substring(paramStartIndex + 1));
|
||||
}
|
||||
header.setRequestURL(url.trim());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -78,7 +78,20 @@ public class HttpPrintStream extends OutputStream {
|
|||
*/
|
||||
public HttpPrintStream(OutputStream out, HttpMessageType type) {
|
||||
this.out = new PrintStream(out);
|
||||
header.setIsRequest(type == HttpMessageType.REQUEST);
|
||||
|
||||
// Set defaults
|
||||
|
||||
header.setProtocol("HTTP");
|
||||
header.setProtocolVersion(1.0f);
|
||||
|
||||
if (type == HttpMessageType.REQUEST) {
|
||||
header.setIsRequest(true);
|
||||
header.setRequestType("GET");
|
||||
header.setRequestURL("/");
|
||||
} else {
|
||||
header.setIsRequest(false);
|
||||
header.setResponseStatusCode(200);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -50,11 +50,12 @@ public class HttpFilePage implements HttpPage{
|
|||
private static final Logger log = LogUtil.getLogger();
|
||||
private static final int MAX_CACHE_AGE_SECONDS = 120;
|
||||
|
||||
private File resource_root;
|
||||
private final File resource_root;
|
||||
private boolean showFolders;
|
||||
private boolean redirectToIndex;
|
||||
|
||||
private HashMap<File,FileCache> cache;
|
||||
private final HashMap<File,FileCache> cache;
|
||||
|
||||
private static class FileCache{
|
||||
public long lastModified;
|
||||
public String hash;
|
||||
|
|
@ -64,6 +65,9 @@ public class HttpFilePage implements HttpPage{
|
|||
* @param file a reference to a root directory or a file.
|
||||
*/
|
||||
public HttpFilePage(File file){
|
||||
if (file == null)
|
||||
throw new IllegalArgumentException("Root path cannot be null.");;
|
||||
|
||||
this.resource_root = file;
|
||||
this.showFolders = true;
|
||||
this.redirectToIndex = true;
|
||||
|
|
@ -76,7 +80,7 @@ public class HttpFilePage implements HttpPage{
|
|||
HttpHeader headers,
|
||||
Map<String, Object> session,
|
||||
Map<String, String> cookie,
|
||||
Map<String, String> request) throws IOException{
|
||||
Map<String, String> request) {
|
||||
|
||||
try {
|
||||
// Is the root only one file or a folder
|
||||
|
|
@ -95,15 +99,21 @@ public class HttpFilePage implements HttpPage{
|
|||
}
|
||||
// Show folder contents
|
||||
else if (showFolders) {
|
||||
out.println("<HTML><BODY><H1>Directory: " + headers.getRequestURL() + "</H1>");
|
||||
out.println("<HR><UL>");
|
||||
for (String f : file.list()) {
|
||||
out.println("<html>");
|
||||
out.println("<body>");
|
||||
out.println(" <h1>Directory: " + headers.getRequestURL() + "</h1>");
|
||||
out.println(" <hr>");
|
||||
out.println(" <ul>");
|
||||
for (String containingFile : file.list()) {
|
||||
String url = headers.getRequestURL();
|
||||
out.println("<LI><A href='" +
|
||||
url + (url.charAt(url.length()-1)=='/'?"":"/")+ f
|
||||
+"'>" + f + "</A></LI>");
|
||||
out.println(" <li><a href='" +
|
||||
url + (url.endsWith("/") ? "" : "/") + containingFile
|
||||
+"'>" + containingFile + "</a></li>");
|
||||
}
|
||||
out.println("</UL><HR></BODY></HTML>");
|
||||
out.println(" </ul>");
|
||||
out.println(" <hr>");
|
||||
out.println("</body>");
|
||||
out.println("</html>");
|
||||
}
|
||||
else {
|
||||
throw new SecurityException("User not allowed to view folder: root=" + resource_root.getAbsolutePath());
|
||||
|
|
@ -133,13 +143,13 @@ public class HttpFilePage implements HttpPage{
|
|||
|
||||
private void deliverFileWithCache(HttpHeader headers, File file, HttpPrintStream out) throws IOException {
|
||||
String eTag = getFileHash(file);
|
||||
out.setHeader("Cache-Control", "max-age=" + MAX_CACHE_AGE_SECONDS);
|
||||
out.setHeader(HttpHeader.HEADER_CACHE_CONTROL, "max-age=" + MAX_CACHE_AGE_SECONDS);
|
||||
|
||||
if (eTag != null) {
|
||||
out.setHeader("ETag", "\"" + eTag + "\"");
|
||||
|
||||
if (headers.getHeader("If-None-Match") != null &&
|
||||
eTag.equals(StringUtil.trimQuotes(headers.getHeader("If-None-Match")))) { // File has not changed
|
||||
if (headers.getHeader(HttpHeader.HEADER_IF_NONE_MATCH) != null &&
|
||||
eTag.equals(StringUtil.trimQuotes(headers.getHeader(HttpHeader.HEADER_IF_NONE_MATCH)))) { // File has not changed
|
||||
out.setResponseStatusCode(304);
|
||||
} else {
|
||||
deliverFile(file, out);
|
||||
|
|
@ -150,8 +160,8 @@ public class HttpFilePage implements HttpPage{
|
|||
String fileExt = FileUtil.getFileExtension(file);
|
||||
|
||||
if (MimeTypeUtil.getMimeByExtension(fileExt) != null)
|
||||
out.setHeader("Content-Type", MimeTypeUtil.getMimeByExtension(fileExt).toString());
|
||||
out.setHeader("Content-Length", "" + file.length());
|
||||
out.setHeader(HttpHeader.HEADER_CONTENT_TYPE, MimeTypeUtil.getMimeByExtension(fileExt).toString());
|
||||
out.setHeader(HttpHeader.HEADER_CONTENT_LENGTH, "" + file.length());
|
||||
out.flush();
|
||||
|
||||
InputStream in = new FileInputStream(file);
|
||||
|
|
|
|||
80
test/zutil/net/http/HttpHeaderParserTest.java
Normal file
80
test/zutil/net/http/HttpHeaderParserTest.java
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2020 Ziver Koc
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package zutil.net.http;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class HttpHeaderParserTest {
|
||||
|
||||
@Test
|
||||
public void firstLineRequest() throws IOException {
|
||||
HttpHeaderParser parser = new HttpHeaderParser("GET / HTTP/1.1");
|
||||
HttpHeader header = parser.read();
|
||||
|
||||
assertTrue(header.isRequest());
|
||||
assertEquals("GET", header.getRequestType());
|
||||
assertEquals("/", header.getRequestURL());
|
||||
assertEquals("HTTP", header.getProtocol());
|
||||
assertEquals(1.1f, header.getProtocolVersion(), 0);
|
||||
assertEquals(-1, header.getResponseStatusCode());
|
||||
assertEquals(null, header.getResponseStatusString());
|
||||
|
||||
parser = new HttpHeaderParser("DESCRIBE http://example.com RTSP/1.0");
|
||||
header = parser.read();
|
||||
|
||||
assertTrue(header.isRequest());
|
||||
assertEquals("DESCRIBE", header.getRequestType());
|
||||
assertEquals("http://example.com", header.getRequestURL());
|
||||
assertEquals("RTSP", header.getProtocol());
|
||||
assertEquals(1.0f, header.getProtocolVersion(), 0f);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void firstLineResponse() throws IOException {
|
||||
HttpHeaderParser parser = new HttpHeaderParser("HTTP/1.0 200 OK");
|
||||
HttpHeader header = parser.read();
|
||||
|
||||
assertTrue(header.isResponse());
|
||||
assertEquals("HTTP", header.getProtocol());
|
||||
assertEquals(1.0f, header.getProtocolVersion(), 0f);
|
||||
assertEquals(200, header.getResponseStatusCode(), 0f);
|
||||
assertEquals("OK", header.getResponseStatusString());
|
||||
assertNull(header.getRequestType());
|
||||
assertNull(header.getRequestURL());
|
||||
|
||||
parser = new HttpHeaderParser("RTSP/1.0 404 File not found Special");
|
||||
header = parser.read();
|
||||
|
||||
assertTrue(header.isResponse());
|
||||
assertEquals("RTSP", header.getProtocol());
|
||||
assertEquals(1.0f, header.getProtocolVersion(), 0f);
|
||||
assertEquals(404, header.getResponseStatusCode(), 0f);
|
||||
assertEquals("File not found Special", header.getResponseStatusString());
|
||||
}
|
||||
}
|
||||
|
|
@ -44,7 +44,7 @@ public class HttpHeaderTest {
|
|||
@Test
|
||||
public void setProtocol() {
|
||||
HttpHeader header = new HttpHeader();
|
||||
assertEquals("HTTP", header.getProtocol());
|
||||
assertEquals(null, header.getProtocol());
|
||||
|
||||
header.setProtocol("RTSP");
|
||||
assertEquals("RTSP", header.getProtocol());
|
||||
|
|
@ -54,7 +54,7 @@ public class HttpHeaderTest {
|
|||
@Test
|
||||
public void setProtocolVersion() {
|
||||
HttpHeader header = new HttpHeader();
|
||||
assertEquals(1.0f, header.getProtocolVersion(), 0);
|
||||
assertEquals(-1, header.getProtocolVersion(), 0);
|
||||
|
||||
header.setProtocolVersion(1.1f);
|
||||
assertEquals(1.1f, header.getProtocolVersion(), 0);
|
||||
|
|
@ -64,7 +64,7 @@ public class HttpHeaderTest {
|
|||
@Test
|
||||
public void setRequestType() {
|
||||
HttpHeader header = new HttpHeader();
|
||||
assertEquals("GET", header.getRequestType());
|
||||
assertEquals(null, header.getRequestType());
|
||||
|
||||
header.setRequestType("POST");
|
||||
assertEquals("POST", header.getRequestType());
|
||||
|
|
@ -74,7 +74,7 @@ public class HttpHeaderTest {
|
|||
@Test
|
||||
public void setResponseStatusCode() {
|
||||
HttpHeader header = new HttpHeader();
|
||||
assertEquals(200, header.getResponseStatusCode());
|
||||
assertEquals(-1, header.getResponseStatusCode());
|
||||
|
||||
header.setResponseStatusCode(400);
|
||||
assertEquals(400, header.getResponseStatusCode());
|
||||
|
|
@ -83,16 +83,10 @@ public class HttpHeaderTest {
|
|||
@Test
|
||||
public void setRequestURL() {
|
||||
HttpHeader header = new HttpHeader();
|
||||
assertEquals("/", header.getRequestURL());
|
||||
assertEquals(null, header.getRequestURL());
|
||||
|
||||
header.setRequestURL("/page/test");
|
||||
assertEquals("/page/test", header.getRequestURL());
|
||||
|
||||
header.setRequestURL(" /page/1test ");
|
||||
assertEquals("/page/1test", header.getRequestURL());
|
||||
|
||||
header.setRequestURL("/page//2test ");
|
||||
assertEquals("/page/2test", header.getRequestURL());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -150,7 +144,7 @@ public class HttpHeaderTest {
|
|||
@Test
|
||||
public void getResponseStatusString() {
|
||||
HttpHeader header = new HttpHeader();
|
||||
assertEquals("OK", header.getResponseStatusString());
|
||||
assertEquals(null, header.getResponseStatusString());
|
||||
|
||||
header.setResponseStatusCode(400);
|
||||
assertEquals("Bad Request", header.getResponseStatusString());
|
||||
|
|
|
|||
|
|
@ -32,9 +32,6 @@ import static org.junit.Assert.assertEquals;
|
|||
import static org.junit.Assert.assertNull;
|
||||
|
||||
|
||||
/**
|
||||
* Created by ezivkoc on 2015-12-11.
|
||||
*/
|
||||
public class URLDecoderTest {
|
||||
|
||||
@Test
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue