Added test for http parser and fixed some issues

This commit is contained in:
Ziver Koc 2020-11-10 21:18:36 +01:00
parent c196a7cca0
commit 9934ff1df5
8 changed files with 163 additions and 55 deletions

View file

@ -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()

View file

@ -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";

View file

@ -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());
}
}

View file

@ -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);
}
}

View file

@ -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);

View 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());
}
}

View file

@ -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());

View file

@ -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