Some cleanup with HTTP classes

This commit is contained in:
Ziver Koc 2020-09-07 18:37:43 +02:00
parent 2249298c93
commit 337c0392bd
7 changed files with 709 additions and 633 deletions

View file

@ -31,13 +31,12 @@ import java.util.HashMap;
import java.util.Iterator; import java.util.Iterator;
public class HttpHeader { public class HttpHeader {
// HTTP info
private boolean request = true; private boolean request = true;
private String type = "GET"; private String type = "GET";
private String url = "/"; private String url = "/";
private HashMap<String, String> urlAttributes; private HashMap<String, String> urlAttributes;
private float version = 1.0f; private float protocolVersion = 1.0f;
private int httpCode = 200; private int statusCode = 200;
private InputStream in; private InputStream in;
// Parameters // Parameters
@ -45,7 +44,7 @@ public class HttpHeader {
private HashMap<String, String> cookies; private HashMap<String, String> cookies;
public HttpHeader(){ public HttpHeader() {
urlAttributes = new HashMap<>(); urlAttributes = new HashMap<>();
headers = new HashMap<>(); headers = new HashMap<>();
cookies = new HashMap<>(); cookies = new HashMap<>();
@ -53,46 +52,52 @@ public class HttpHeader {
/** /**
* @return true if this header represents a server response * @return true if this header represents a server response
*/ */
public boolean isResponse(){ public boolean isResponse() {
return !request; return !request;
} }
/** /**
* @return true if this header represents a client request * @return true if this header represents a client request
*/ */
public boolean isRequest(){ public boolean isRequest() {
return request; return request;
} }
/** /**
* @return the HTTP message type( ex. GET,POST...) * @return the HTTP message type( ex. GET,POST...)
*/ */
public String getRequestType(){ public String getRequestType() {
return type; return type;
} }
/** /**
* @return the HTTP version of this header * @return the protocol version from this header
*/ */
public float getHTTPVersion(){ public float getProtocolVersion() {
return version; return protocolVersion;
} }
/** /**
* @return the HTTP Return Code from a Server * @return the HTTP Return Code from a Server
*/ */
public int getHTTPCode(){ public int getStatusCode() {
return httpCode; return statusCode;
} }
/** /**
* @return the URL that the client sent the server * @return the URL that the client sent the server
*/ */
public String getRequestURL(){ public String getRequestURL() {
return url; return url;
} }
/** /**
* @return parses out the page name from the request url and returns it. * @return parses out the page name from the request url and returns it.
*/ */
public String getRequestPage() { public String getRequestPage() {
if (url != null){ if (url != null) {
int start = 0; int start = 0;
if (url.charAt(0) == '/') if (url.charAt(0) == '/')
start = 1; start = 1;
@ -104,87 +109,103 @@ public class HttpHeader {
} }
return null; return null;
} }
/** /**
* @return a Iterator with all defined url keys * @return a Iterator with all defined url keys
*/ */
public Iterator<String> getURLAttributeKeys(){ public Iterator<String> getURLAttributeKeys() {
return urlAttributes.keySet().iterator(); return urlAttributes.keySet().iterator();
} }
/** /**
* @return the URL attribute value of the given name. null if there is no such attribute * @return the URL attribute value of the given name. null if there is no such attribute
*/ */
public String getURLAttribute(String name){ public String getURLAttribute(String name) {
return urlAttributes.get( name ); return urlAttributes.get(name);
} }
/** /**
* @return a Iterator with all defined headers * @return a Iterator with all defined headers
*/ */
public Iterator<String> getHeaderKeys(){ public Iterator<String> getHeaderKeys() {
return headers.keySet().iterator(); return headers.keySet().iterator();
} }
/** /**
* @return the HTTP attribute value of the given name. null if there is no such attribute * @return the HTTP attribute value of the given name. null if there is no such attribute
*/ */
public String getHeader(String name){ public String getHeader(String name) {
return headers.get( name.toUpperCase() ); return headers.get(name.toUpperCase());
} }
/** /**
* @return a Iterator with all defined cookies * @return a Iterator with all defined cookies
*/ */
public Iterator<String> getCookieKeys(){ public Iterator<String> getCookieKeys() {
return cookies.keySet().iterator(); return cookies.keySet().iterator();
} }
/** /**
* @return the cookie value of the given name. null if there is no such attribute. * @return the cookie value of the given name. null if there is no such attribute.
*/ */
public String getCookie(String name){ public String getCookie(String name) {
return cookies.get( name ); return cookies.get(name);
} }
/** /**
* @return a Reader that contains the body of the http request. * @return a Reader that contains the body of the http request.
*/ */
public InputStream getInputStream(){ public InputStream getInputStream() {
return in; return in;
} }
public void setIsRequest(boolean request) { this.request = request; } public void setIsRequest(boolean request) {
public void setRequestType(String type){ this.request = request;
}
public void setRequestType(String type) {
this.type = type; this.type = type;
} }
public void setHTTPVersion(float version){
this.version = version; public void setProtocolVersion(float version) {
this.protocolVersion = version;
} }
public void setHTTPCode(int code){
this.httpCode = code; public void setStatusCode(int code) {
this.statusCode = code;
} }
public void setRequestURL(String url){
public void setRequestURL(String url) {
this.url = url.trim().replaceAll("//", "/"); this.url = url.trim().replaceAll("//", "/");
} }
public void setHeader(String key, String value){
public void setHeader(String key, String value) {
this.headers.put(key.toUpperCase(), value); this.headers.put(key.toUpperCase(), value);
} }
protected void setInputStream(InputStream in){
protected void setInputStream(InputStream in) {
this.in = in; this.in = in;
} }
protected HashMap<String,String> getHeaderMap(){ protected HashMap<String, String> getHeaderMap() {
return headers; return headers;
} }
protected HashMap<String,String> getCookieMap(){
protected HashMap<String, String> getCookieMap() {
return cookies; return cookies;
} }
protected HashMap<String,String> getUrlAttributeMap(){
protected HashMap<String, String> getUrlAttributeMap() {
return urlAttributes; return urlAttributes;
} }
public String toString() {
public String toString(){
StringBuilder tmp = new StringBuilder(); StringBuilder tmp = new StringBuilder();
tmp.append("{Type: ").append(type); tmp.append("{Type: ").append(type);
tmp.append(", HTTP_version: HTTP/").append(version); tmp.append(", HTTP_version: HTTP/").append(protocolVersion);
if(url == null) if (url == null)
tmp.append(", URL: null"); tmp.append(", URL: null");
else else
tmp.append(", URL: \"").append(url).append('\"'); tmp.append(", URL: \"").append(url).append('\"');
@ -196,13 +217,13 @@ public class HttpHeader {
tmp.append('}'); tmp.append('}');
return tmp.toString(); return tmp.toString();
} }
public String toStringHeaders(){ public String toStringHeaders() {
return Converter.toString(headers); return Converter.toString(headers);
} }
public String toStringCookies(){ public String toStringCookies() {
return Converter.toString(cookies); return Converter.toString(cookies);
} }
public String toStringAttributes(){ public String toStringAttributes() {
return Converter.toString(urlAttributes); return Converter.toString(urlAttributes);
} }

View file

@ -50,9 +50,9 @@ public class HttpHeaderParser {
/** /**
* Parses the HTTP header information from a stream * Parses the HTTP header information from a stream
* *
* @param in is the stream * @param in is the stream
*/ */
public HttpHeaderParser(InputStream in){ public HttpHeaderParser(InputStream in) {
this.in = in; this.in = in;
this.skipStatusLine = false; this.skipStatusLine = false;
} }
@ -60,9 +60,9 @@ public class HttpHeaderParser {
/** /**
* Parses the HTTP header information from a String * Parses the HTTP header information from a String
* *
* @param in is the String * @param in is the String
*/ */
public HttpHeaderParser(String in){ public HttpHeaderParser(String in) {
this(new StringInputStream(in)); this(new StringInputStream(in));
} }
@ -73,13 +73,13 @@ public class HttpHeaderParser {
// First line // First line
if (!skipStatusLine) { if (!skipStatusLine) {
if( (line=IOUtil.readLine(in)) != null && !line.isEmpty() ) if ((line = IOUtil.readLine(in)) != null && !line.isEmpty())
parseStatusLine(header, line); parseStatusLine(header, line);
else else
return null; return null;
} }
// Read header body // Read header body
while( (line=IOUtil.readLine(in)) != null && !line.isEmpty() ){ while ((line = IOUtil.readLine(in)) != null && !line.isEmpty()) {
parseHeaderLine(header.getHeaderMap(), line); parseHeaderLine(header.getHeaderMap(), line);
} }
@ -90,40 +90,39 @@ public class HttpHeaderParser {
} }
/** /**
* @param skipStatusLine indicates if the parser should expect a http status lines. (default: true) * @param skipStatusLine indicates if the parser should expect a http status lines. (default: true)
*/ */
public void skipStatusLine(boolean skipStatusLine){ public void skipStatusLine(boolean skipStatusLine) {
this.skipStatusLine = skipStatusLine; this.skipStatusLine = skipStatusLine;
} }
/** /**
* Parses the first line of a http request/response and stores the values in a HttpHeader object * Parses the first line of a http request/response and stores the values in a HttpHeader object
* *
* @param header the header object where the cookies will be stored. * @param header the header object where the cookies will be stored.
* @param statusLine the status line String * @param statusLine the status line String
*/ */
private static void parseStatusLine(HttpHeader header, String statusLine){ private static void parseStatusLine(HttpHeader header, String statusLine) {
// Server Response // Server Response
if( statusLine.startsWith("HTTP/") ){ if (statusLine.startsWith("HTTP/")) {
header.setIsRequest(false); header.setIsRequest(false);
header.setHTTPVersion( Float.parseFloat( statusLine.substring( 5 , 8))); header.setProtocolVersion(Float.parseFloat(statusLine.substring(5, 8)));
header.setHTTPCode( Integer.parseInt( statusLine.substring( 9, statusLine.indexOf(' ', 9) ))); header.setStatusCode(Integer.parseInt(statusLine.substring(9, statusLine.indexOf(' ', 9))));
} }
// Client Request // Client Request
else if(statusLine.contains("HTTP/")){ else if (statusLine.contains("HTTP/")) {
header.setIsRequest(true); header.setIsRequest(true);
header.setRequestType( statusLine.substring(0, statusLine.indexOf(" ")).trim() ); header.setRequestType(statusLine.substring(0, statusLine.indexOf(" ")).trim());
header.setHTTPVersion( Float.parseFloat( statusLine.substring(statusLine.lastIndexOf("HTTP/")+5 , statusLine.length()).trim())); header.setProtocolVersion(Float.parseFloat(statusLine.substring(statusLine.lastIndexOf("HTTP/") + 5).trim()));
statusLine = (statusLine.substring(header.getRequestType().length()+1, statusLine.lastIndexOf("HTTP/"))); statusLine = (statusLine.substring(header.getRequestType().length() + 1, statusLine.lastIndexOf("HTTP/")));
// parse URL and attributes // parse URL and attributes
int index = statusLine.indexOf('?'); int index = statusLine.indexOf('?');
if(index > -1){ if (index > -1) {
header.setRequestURL( statusLine.substring(0, index)); header.setRequestURL(statusLine.substring(0, index));
statusLine = statusLine.substring( index+1, statusLine.length()); statusLine = statusLine.substring(index + 1);
parseURLParameters(header.getUrlAttributeMap(), statusLine); parseURLParameters(header.getUrlAttributeMap(), statusLine);
} } else {
else{
header.setRequestURL(statusLine); header.setRequestURL(statusLine);
} }
} }
@ -134,23 +133,23 @@ public class HttpHeaderParser {
* Note that all header keys will be stored with * Note that all header keys will be stored with
* uppercase notation to make them case insensitive. * uppercase notation to make them case insensitive.
* *
* @param map a map where the header key(Uppercase) and value will be stored. * @param map a map where the header key(Uppercase) and value will be stored.
* @param line is the next line in the header * @param line is the next line in the header
*/ */
public static void parseHeaderLine(Map<String,String> map, String line){ public static void parseHeaderLine(Map<String, String> map, String line) {
String[] data = PATTERN_COLON.split( line, 2 ); String[] data = PATTERN_COLON.split(line, 2);
map.put( map.put(
data[0].trim().toUpperCase(), // Key data[0].trim().toUpperCase(), // Key
(data.length>1 ? data[1] : "").trim()); //Value (data.length > 1 ? data[1] : "").trim()); //Value
} }
/** /**
* Parses a raw cookie header into key and value pairs * Parses a raw cookie header into key and value pairs
* *
* @param map the Map where the cookies will be stored. * @param map the Map where the cookies will be stored.
* @param cookieValue the raw cookie header value String that will be parsed * @param cookieValue the raw cookie header value String that will be parsed
*/ */
public static void parseCookieValues(Map<String,String> map, String cookieValue){ public static void parseCookieValues(Map<String, String> map, String cookieValue) {
parseHeaderValues(map, cookieValue, ";"); parseHeaderValues(map, cookieValue, ";");
} }
@ -159,34 +158,37 @@ public class HttpHeaderParser {
* Parses a header value string that contains key and value paired data and * Parses a header value string that contains key and value paired data and
* stores them in a HashMap. If a pair only contain a key then the value * stores them in a HashMap. If a pair only contain a key then the value
* will be set as a empty string. * will be set as a empty string.
* * <p>
* TODO: method is not quote aware * TODO: method is not quote aware
* @param headerValue the raw header value String that will be parsed. *
* @param delimiter the delimiter that separates key and value pairs (e.g. ';' for Cookies or ',' for Cache-Control) * @param headerValue the raw header value String that will be parsed.
* @param delimiter the delimiter that separates key and value pairs (e.g. ';' for Cookies or ',' for Cache-Control)
*/ */
public static HashMap<String, String> parseHeaderValues(String headerValue, String delimiter){ public static HashMap<String, String> parseHeaderValues(String headerValue, String delimiter) {
HashMap<String, String> map = new HashMap<>(); HashMap<String, String> map = new HashMap<>();
parseHeaderValues(map, headerValue, delimiter); parseHeaderValues(map, headerValue, delimiter);
return map; return map;
} }
/** /**
* Parses a header value string that contains key and value paired data and * Parses a header value string that contains key and value paired data and
* stores them in a HashMap. If a pair only contain a key then the value * stores them in a HashMap. If a pair only contain a key then the value
* will be set as a empty string. * will be set as a empty string.
* * <p>
* TODO: method is not quote aware * TODO: method is not quote aware
* @param map the Map where key and values will be stored. *
* @param headerValue the raw header value String that will be parsed. * @param map the Map where key and values will be stored.
* @param delimiter the delimiter that separates key and value pairs (e.g. ';' for Cookies or ',' for Cache-Control) * @param headerValue the raw header value String that will be parsed.
* @param delimiter the delimiter that separates key and value pairs (e.g. ';' for Cookies or ',' for Cache-Control)
*/ */
public static void parseHeaderValues(Map<String,String> map, String headerValue, String delimiter){ public static void parseHeaderValues(Map<String, String> map, String headerValue, String delimiter) {
if(headerValue != null && !headerValue.isEmpty()){ if (headerValue != null && !headerValue.isEmpty()) {
String[] tmpArr = headerValue.split(delimiter); String[] tmpArr = headerValue.split(delimiter);
for(String cookie : tmpArr){ for (String cookie : tmpArr) {
String[] tmpStr = PATTERN_EQUAL.split(cookie, 2); String[] tmpStr = PATTERN_EQUAL.split(cookie, 2);
map.put( map.put(
tmpStr[0].trim(), // Key tmpStr[0].trim(), // Key
StringUtil.trim((tmpStr.length>1 ? tmpStr[1] : "").trim(), '\"')); //Value StringUtil.trim((tmpStr.length > 1 ? tmpStr[1] : "").trim(), '\"')); //Value
} }
} }
} }
@ -195,28 +197,29 @@ public class HttpHeaderParser {
/** /**
* Parses a string with variables from a get or post request that was sent from a client * Parses a string with variables from a get or post request that was sent from a client
* *
* @param header the header object where the url attributes key and value will be stored. * @param header the header object where the url attributes key and value will be stored.
* @param urlAttributes is the String containing all the attributes * @param urlAttributes is the String containing all the attributes
*/ */
public static void parseURLParameters(HttpHeader header, String urlAttributes){ public static void parseURLParameters(HttpHeader header, String urlAttributes) {
parseURLParameters(header.getUrlAttributeMap(), urlAttributes); parseURLParameters(header.getUrlAttributeMap(), urlAttributes);
} }
/** /**
* Parses a string with variables from a get or post request that was sent from a client * Parses a string with variables from a get or post request that was sent from a client
* *
* @param map a map where the url attributes key and value will be stored. * @param map a map where the url attributes key and value will be stored.
* @param urlAttributes is the String containing all the attributes * @param urlAttributes is the String containing all the attributes
*/ */
public static void parseURLParameters(Map<String,String> map, String urlAttributes){ public static void parseURLParameters(Map<String, String> map, String urlAttributes) {
String[] tmp; String[] tmp;
urlAttributes = URLDecoder.decode(urlAttributes); urlAttributes = URLDecoder.decode(urlAttributes);
// get the variables // get the variables
String[] data = PATTERN_AND.split( urlAttributes ); String[] data = PATTERN_AND.split(urlAttributes);
for(String element : data){ for (String element : data) {
tmp = PATTERN_EQUAL.split(element, 2); tmp = PATTERN_EQUAL.split(element, 2);
map.put( map.put(
tmp[0].trim(), // Key tmp[0].trim(), // Key
(tmp.length>1 ? tmp[1] : "").trim()); //Value (tmp.length > 1 ? tmp[1] : "").trim()); //Value
} }
} }
} }

View file

@ -36,8 +36,8 @@ import java.util.Map;
public interface HttpPage{ public interface HttpPage{
/** /**
* This method has to be implemented for every page. * This method has to be implemented for every page.
* This method is called when a client wants a response * The method is called when a client sends a request
* from this specific page. * and is expecting a response from the specific page.
* *
* @param out is a output stream to the client * @param out is a output stream to the client
* @param headers is the header received from the client * @param headers is the header received from the client

View file

@ -1,353 +1,396 @@
/* /*
* The MIT License (MIT) * The MIT License (MIT)
* *
* Copyright (c) 2015 Ziver Koc * Copyright (c) 2015 Ziver Koc
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights * in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is * copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions: * furnished to do so, subject to the following conditions:
* *
* The above copyright notice and this permission notice shall be included in * The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software. * all copies or substantial portions of the Software.
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * 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 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE. * THE SOFTWARE.
*/ */
package zutil.net.http; package zutil.net.http;
import zutil.converter.Converter; import zutil.converter.Converter;
import java.io.IOException; import java.io.OutputStream;
import java.io.OutputStream; import java.io.PrintStream;
import java.io.PrintStream; import java.util.HashMap;
import java.util.HashMap;
/**
/** * This PrintStream is written for HTTP use
* This PrintStream is written for HTTP use * It has buffer capabilities and cookie management.
* It has buffer capabilities and cookie management. *
* * @author Ziver
* @author Ziver */
* public class HttpPrintStream extends OutputStream {
*/ /**
public class HttpPrintStream extends OutputStream{ * Specifies if the HTTP message is a request (client) or a response (server).
// Defines the type of message */
public enum HttpMessageType{ public enum HttpMessageType {
REQUEST, REQUEST,
RESPONSE RESPONSE
} }
/** The actual output stream */ /**
private PrintStream out; * The actual output stream
/** This defines the supported http version */ */
private String httpVersion; private PrintStream out;
/** This defines the type of message that will be generated */ /**
private HttpMessageType message_type; * This defines the type of message that will be generated
/** The status code of the message, ONLY for response */ */
private Integer res_status_code; private final HttpMessageType messageType;
/** The request type of the message ONLY for request */ /**
private String req_type; * Specifies the protocol that should be used
/** The requesting url ONLY for request */ */
private String req_url; private String protocol;
/** An Map of all the header values */ /**
private HashMap<String, String> headers; * Specifies the protocol version that should be used
/** An Map of all the cookies */ */
private HashMap<String, String> cookies; private String protocolVersion;
/** The buffered header */ /**
private StringBuffer buffer; * The status code of the message, ONLY for response
/** If the header buffering is enabled */ */
private boolean buffer_enabled; private Integer responseStatusCode;
/**
/** * The request type of the message ONLY for request
* Creates an new instance of HttpPrintStream with */
* message type of RESPONSE and buffering disabled. private String requestType;
* /**
* @param out is the OutputStream where the data will be written to * The requesting url ONLY for request
*/ */
public HttpPrintStream(OutputStream out) { private String requestUrl;
this( out, HttpMessageType.RESPONSE ); /**
} * An Map of all the header values
/** */
* Creates an new instance of HttpPrintStream with private HashMap<String, String> headers;
* message type buffering disabled. /**
* * An Map of all the cookies
* @param out is the OutputStream where the data will be written to */
* @param type is the type of message private HashMap<String, String> cookies;
*/ /**
public HttpPrintStream(OutputStream out, HttpMessageType type) { * The header data buffer
this.out = new PrintStream(out); */
this.httpVersion = "1.0"; private StringBuffer buffer;
this.message_type = type; /**
this.res_status_code = 200; * If the header buffering is enabled
this.headers = new HashMap<>(); */
this.cookies = new HashMap<>(); private boolean bufferEnabled;
this.buffer = new StringBuffer();
this.buffer_enabled = false;
} /**
* Creates an new instance of HttpPrintStream with
/** * message type of RESPONSE and buffering disabled.
* Enable the buffering capability of the PrintStream. *
* Nothing will be sent to the client when buffering * @param out is the OutputStream where the data will be written to
* is enabled until you close or flush the stream. */
* This function will flush the stream if buffering is public HttpPrintStream(OutputStream out) {
* disabled. this(out, HttpMessageType.RESPONSE);
*/ }
public void enableBuffering(boolean enable) throws IOException {
buffer_enabled = enable; /**
if(!buffer_enabled) flush(); * Creates an new instance of HttpPrintStream with
} * message type buffering disabled.
*
/** * @param out is the OutputStream where the data will be written to
* Set the http version that will be used in the http header * @param type is the type of message
*/ */
public void setHttpVersion(String version){ public HttpPrintStream(OutputStream out, HttpMessageType type) {
this.httpVersion = version; this.out = new PrintStream(out);
} this.protocol = "HTTP";
this.protocolVersion = "1.0";
/** this.messageType = type;
* Adds a cookie that will be sent to the client this.responseStatusCode = 200;
* this.headers = new HashMap<>();
* @param key is the name of the cookie this.cookies = new HashMap<>();
* @param value is the value of the cookie this.buffer = new StringBuffer();
* @throws IOException if the header has already been sent this.bufferEnabled = false;
*/ }
public void setCookie(String key, String value) throws IOException{
if(cookies == null)
throw new IOException("Header already sent!"); /**
cookies.put(key, value); * Enable the buffering capability of the PrintStream.
} * Nothing will be sent to the client when buffering
* is enabled until you close or flush the stream.
/** * This function will flush the stream if buffering is
* Adds an header value * disabled.
* */
* @param key is the header name public void enableBuffering(boolean enable) {
* @param value is the value of the header bufferEnabled = enable;
* @throws IOException if the header has already been sent if (!bufferEnabled) flush();
*/ }
public void setHeader(String key, String value) throws IOException{
if(headers == null) /**
throw new IOException("Header already sent!"); * Set the http version that will be used in the http header
headers.put(key, value); */
} public void setProtocolVersion(String version) {
this.protocolVersion = version;
/** }
* Sets the status code of the message, ONLY available in HTTP RESPONSE
* /**
* @param code the code from 100 up to 599 * Adds a cookie that will be sent to the client
* @throws IOException if the header has already been sent or the message type is wrong *
*/ * @param key is the name of the cookie
public void setStatusCode(int code) throws IOException{ * @param value is the value of the cookie
if( res_status_code == null ) * @throws IllegalStateException if the header has already been sent
throw new IOException("Header already sent!"); */
if( message_type != HttpMessageType.RESPONSE ) public void setCookie(String key, String value) {
throw new IOException("Status Code is only available in HTTP RESPONSE!"); if (cookies == null)
res_status_code = code; throw new IllegalStateException("Header already sent");
} cookies.put(key, value);
}
/**
* Sets the request type of the message, ONLY available in HTTP REQUEST /**
* * Adds an header value
* @param req_type is the type of the message, e.g. GET, POST... *
* @throws IOException if the header has already been sent or the message type is wrong * @param key is the header name
*/ * @param value is the value of the header
public void setRequestType(String req_type) throws IOException{ * @throws IllegalStateException if the header has already been sent
if( req_type == null ) */
throw new IOException("Header already sent!"); public void setHeader(String key, String value) {
if( message_type != HttpMessageType.REQUEST ) if (headers == null)
throw new IOException("Request Message Type is only available in HTTP REQUEST!"); throw new IllegalStateException("Header already sent");
this.req_type = req_type; headers.put(key, value);
} }
/**
* Sets the requesting URL of the message, ONLY available in HTTP REQUEST /**
* * Sets the status code of the message, ONLY available in HTTP RESPONSE
* @param req_url is the URL *
* @throws IOException if the header has already been sent or the message type is wrong * @param code the code from 100 up to 599
*/ * @throws IllegalStateException if the header has already been sent or the message type is wrong
public void setRequestURL(String req_url) throws IOException{ */
if( req_url == null ) public void setStatusCode(int code) {
throw new IOException("Header already sent!"); if (responseStatusCode == null)
if( message_type != HttpMessageType.REQUEST ) throw new IllegalStateException("Header already sent.");
throw new IOException("Request URL is only available in HTTP REQUEST!"); if (messageType != HttpMessageType.RESPONSE)
this.req_url = req_url; throw new IllegalStateException("Status Code is only available with HTTP requests");
} responseStatusCode = code;
}
protected void setHeaders( HashMap<String,String> map ){
headers = map; /**
} * Sets the request type of the message, ONLY available in HTTP REQUEST
protected void setCookies( HashMap<String,String> map ){ *
cookies = map; * @param req_type is the type of the message, e.g. GET, POST...
} * @throws IllegalStateException if the header has already been sent or the message type is wrong
*/
public void setRequestType(String req_type) {
/** if (req_type == null)
* Prints a new line throw new IllegalStateException("Header already sent.");
*/ if (messageType != HttpMessageType.REQUEST)
public void println() throws IOException { throw new IllegalStateException("Request Message Type is only available with HTTP requests");
printOrBuffer(System.lineSeparator()); this.requestType = req_type;
} }
/**
* Prints with a new line /**
*/ * Sets the requesting URL of the message. Only available with HTTP requests
public void println(String s) throws IOException { *
printOrBuffer(s + System.lineSeparator()); * @param req_url is the URL
} * @throws IllegalStateException if the header has already been sent or the message type is wrong
/** */
* Prints an string public void setRequestURL(String req_url) {
*/ if (req_url == null)
public void print(String s) throws IOException { throw new IllegalStateException("Header already sent.");
printOrBuffer(s); if (messageType != HttpMessageType.REQUEST)
} throw new IllegalStateException("Request URL is only available with a HTTP request");
this.requestUrl = req_url;
/** }
* Will buffer String or directly output headers if needed and then the String
*/ protected void setHeaders(HashMap<String, String> map) {
private void printOrBuffer(String s) { headers = map;
if(buffer_enabled){ }
buffer.append(s);
} protected void setCookies(HashMap<String, String> map) {
else{ cookies = map;
if(res_status_code != null){ }
if( message_type==HttpMessageType.REQUEST )
out.print(req_type + " " + req_url + " HTTP/"+httpVersion);
else /**
out.print("HTTP/"+httpVersion+" " + res_status_code + " " + getStatusString(res_status_code)); * Prints a new line
out.println(); */
res_status_code = null; public void println() {
req_type = null; printOrBuffer(System.lineSeparator());
req_url = null; }
}
if(headers != null){ /**
for(String key : headers.keySet()){ * Prints with a new line
out.println(key + ": " + headers.get(key)); */
} public void println(String s) {
headers = null; printOrBuffer(s + System.lineSeparator());
} }
if(cookies != null){
if( !cookies.isEmpty() ){ /**
if( message_type==HttpMessageType.REQUEST ){ * Prints an string
out.print("Cookie: "); */
for(String key : cookies.keySet()){ public void print(String s) {
out.print(key + "=" + cookies.get(key) + "; "); printOrBuffer(s);
} }
out.println();
} /**
else{ * Will buffer the body data or directly send the headers if needed and then the append the body
for(String key : cookies.keySet()){ */
out.print("Set-Cookie: " + key + "=" + cookies.get(key) + ";"); private void printOrBuffer(String s) {
out.println(); if (bufferEnabled) {
} buffer.append(s);
} } else {
} if (responseStatusCode != null) {
out.println(); if (messageType == HttpMessageType.REQUEST)
cookies = null; out.print(requestType + " " + requestUrl + " " + protocol + "/" + protocolVersion);
} else
out.print(s); out.print("HTTP/" + protocolVersion + " " + responseStatusCode + " " + getStatusString(responseStatusCode));
} out.println();
} responseStatusCode = null;
requestType = null;
/** requestUrl = null;
* @return if headers has been sent. The setHeader, setStatusCode, setCookie method will throw Exceptions }
*/
public boolean isHeaderSent() { if (headers != null) {
return res_status_code == null && headers == null && cookies == null; for (String key : headers.keySet()) {
} out.println(key + ": " + headers.get(key));
}
headers = null;
/** }
* Sends out the buffer and clears it
*/ if (cookies != null) {
@Override if (!cookies.isEmpty()) {
public void flush() throws IOException { if (messageType == HttpMessageType.REQUEST) {
flushBuffer(); out.print("Cookie: ");
out.flush(); for (String key : cookies.keySet()) {
} out.print(key + "=" + cookies.get(key) + "; ");
}
@Override out.println();
public void close() throws IOException { } else {
flush(); for (String key : cookies.keySet()) {
out.close(); out.print("Set-Cookie: " + key + "=" + cookies.get(key) + ";");
} out.println();
}
protected void flushBuffer() throws IOException { }
if(buffer_enabled){ }
buffer_enabled = false; out.println();
printOrBuffer(buffer.toString()); cookies = null;
buffer.delete(0, buffer.length()); }
buffer_enabled = true; out.print(s);
} }
else if(res_status_code != null || headers != null || cookies != null){ }
printOrBuffer("");
} /**
} * @return if headers has been sent. The setHeader, setStatusCode, setCookie method will throw IllegalStateException
*/
/** public boolean isHeaderSent() {
* Will flush all buffers and write binary data to stream return responseStatusCode == null && headers == null && cookies == null;
*/ }
@Override
public void write(int b) throws IOException {
flushBuffer(); /**
out.write(b); * Sends out the buffer and clear it
} */
/** @Override
* * Will flush all buffers and write binary data to stream public void flush() {
*/ flushBuffer();
@Override out.flush();
public void write(byte[] buf, int off, int len) throws IOException { }
flushBuffer();
out.write(buf, off, len); @Override
} public void close() {
flush();
private String getStatusString(int type){ out.close();
switch(type){ }
case 100: return "Continue";
case 200: return "OK"; protected void flushBuffer() {
case 301: return "Moved Permanently"; if (bufferEnabled) {
case 304: return "Not Modified"; bufferEnabled = false;
case 307: return "Temporary Redirect"; printOrBuffer(buffer.toString());
case 400: return "Bad Request"; buffer.delete(0, buffer.length());
case 401: return "Unauthorized"; bufferEnabled = true;
case 403: return "Forbidden"; } else if (responseStatusCode != null || headers != null || cookies != null) {
case 404: return "Not Found"; printOrBuffer("");
case 500: return "Internal Server Error"; }
case 501: return "Not Implemented"; }
default: return "";
} /**
} * Will flush all buffers and write binary data to stream
*/
public String toString() { @Override
StringBuilder str = new StringBuilder(); public void write(int b) {
str.append("{http_type: ").append(message_type); flushBuffer();
if (res_status_code != null) { out.write(b);
if (message_type == HttpMessageType.REQUEST) { }
str.append(", req_type: ").append(req_type);
if(req_url == null) /**
str.append(", req_url: null"); * * Will flush all buffers and write binary data to stream
else */
str.append(", req_url: \"").append(req_url).append('\"'); @Override
} else if (message_type == HttpMessageType.RESPONSE){ public void write(byte[] buf, int off, int len) {
str.append(", status_code: ").append(res_status_code); flushBuffer();
str.append(", status_str: ").append(getStatusString(res_status_code)); out.write(buf, off, len);
} }
if (headers != null) { private String getStatusString(int type) {
str.append(", Headers: ").append(Converter.toString(headers)); switch (type) {
} case 100:
if (cookies != null) { return "Continue";
str.append(", Cookies: ").append(Converter.toString(cookies)); case 200:
} return "OK";
} case 301:
else return "Moved Permanently";
str.append(", HEADER ALREADY SENT "); case 304:
str.append('}'); return "Not Modified";
case 307:
return str.toString(); return "Temporary Redirect";
} case 400:
} return "Bad Request";
case 401:
return "Unauthorized";
case 403:
return "Forbidden";
case 404:
return "Not Found";
case 500:
return "Internal Server Error";
case 501:
return "Not Implemented";
default:
return "";
}
}
public String toString() {
StringBuilder str = new StringBuilder();
str.append("{http_type: ").append(messageType);
if (responseStatusCode != null) {
if (messageType == HttpMessageType.REQUEST) {
str.append(", req_type: ").append(requestType);
if (requestUrl == null)
str.append(", req_url: null");
else
str.append(", req_url: \"").append(requestUrl).append('\"');
} else if (messageType == HttpMessageType.RESPONSE) {
str.append(", status_code: ").append(responseStatusCode);
str.append(", status_str: ").append(getStatusString(responseStatusCode));
}
if (headers != null) {
str.append(", Headers: ").append(Converter.toString(headers));
}
if (cookies != null) {
str.append(", Cookies: ").append(Converter.toString(cookies));
}
} else
str.append(", HEADER ALREADY SENT ");
str.append('}');
return str.toString();
}
}

View file

@ -210,7 +210,7 @@ public class HttpServer extends ThreadedTCPNetworkServer{
} }
//**************************** RESPONSE ************************************ //**************************** RESPONSE ************************************
out.setHttpVersion("1.0"); out.setProtocolVersion("1.0");
out.setStatusCode(200); out.setStatusCode(200);
out.setHeader("Server", SERVER_NAME); out.setHeader("Server", SERVER_NAME);
out.setHeader("Content-Type", "text/html"); out.setHeader("Content-Type", "text/html");

View file

@ -1,162 +1,171 @@
/* /*
* The MIT License (MIT) * The MIT License (MIT)
* *
* Copyright (c) 2015 Ziver Koc * Copyright (c) 2015 Ziver Koc
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights * in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is * copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions: * furnished to do so, subject to the following conditions:
* *
* The above copyright notice and this permission notice shall be included in * The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software. * all copies or substantial portions of the Software.
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * 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 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE. * THE SOFTWARE.
*/ */
package zutil.net.http; package zutil.net.http;
import java.net.URL; import java.net.URL;
import java.util.HashMap; import java.util.HashMap;
/** /**
* Handles URLs in the HTTP protocol * Handles URLs in the HTTP protocol
* *
* @author Ziver * @author Ziver
*/ */
public class HttpURL { public class HttpURL {
public static final String PROTOCOL_SEPARATOR = "://"; public static final String PROTOCOL_SEPARATOR = "://";
public static final String PORT_SEPARATOR = ":"; public static final String PORT_SEPARATOR = ":";
public static final String PATH_SEPARATOR = "/"; public static final String PATH_SEPARATOR = "/";
public static final String PARAMETER_SEPARATOR = "?"; public static final String PARAMETER_SEPARATOR = "?";
public static final String ANCHOR_SEPARATOR = "#"; public static final String ANCHOR_SEPARATOR = "#";
private String protocol = ""; private String protocol = "";
private String host = "127.0.0.1"; private String host = "127.0.0.1";
private int port = -1; private int port = -1;
private String path; private String path;
private String anchor; private String anchor;
private HashMap<String,String> parameters = new HashMap<>(); private HashMap<String, String> parameters = new HashMap<>();
public HttpURL(){} public HttpURL() {}
public HttpURL( URL url ){ public HttpURL(URL url) {
this.setProtocol( url.getProtocol() ); this.setProtocol(url.getProtocol());
this.setHost( url.getHost() ); this.setHost(url.getHost());
this.setPort( url.getPort() ); this.setPort(url.getPort());
this.setPath( url.getPath() ); this.setPath(url.getPath());
} }
public String getProtocol( ){ public String getProtocol() {
return protocol; return protocol;
} }
public String getHost( ){
return host; public String getHost() {
} return host;
public int getPort( ){ }
return port;
} public int getPort() {
public String getPath( ){ return port;
return path; }
}
public String getAnchor( ){ public String getPath() {
return anchor; return path;
} }
public void setProtocol( String prot ){ public String getAnchor() {
this.protocol = prot; return anchor;
} }
public void setHost( String host ){
this.host = host; public void setProtocol(String prot) {
} this.protocol = prot;
public void setPort( int port ){ }
this.port = port;
} public void setHost(String host) {
public void setPath( String path ){ this.host = host;
if( path.length() >= 1 && !path.startsWith(PATH_SEPARATOR)) }
path = PATH_SEPARATOR + path;
this.path = path; public void setPort(int port) {
} this.port = port;
public void setAnchor( String anch ){ }
this.anchor = anch;
} public void setPath(String path) {
public void setParameter( String key, String value ){ if (path.length() >= 1 && !path.startsWith(PATH_SEPARATOR))
this.parameters.put(key, value); path = PATH_SEPARATOR + path;
} this.path = path;
}
protected void setParameters( HashMap<String,String> pars ){
this.parameters = pars; public void setAnchor(String anch) {
} this.anchor = anch;
}
/**
* Generates the parameter string in a URL. public void setParameter(String key, String value) {
* this.parameters.put(key, value);
* e.g. }
* "key=value&amp;key2=value&amp;..."
*/ protected void setParameters(HashMap<String, String> pars) {
public String getParameterString(){ this.parameters = pars;
StringBuilder param = new StringBuilder(); }
for(String key : parameters.keySet()){
if (param.length() > 0) /**
param.append('&'); * Generates the parameter string in a URL.
param.append(key); * <p>
param.append('='); * e.g.
param.append( parameters.get(key) ); * "key=value&amp;key2=value&amp;..."
} */
return param.toString(); public String getParameterString() {
} StringBuilder param = new StringBuilder();
for (String key : parameters.keySet()) {
/** if (param.length() > 0)
* Generates a path that are used in the HTTP header param.append('&');
*/ param.append(key);
public String getHttpURL(){ param.append('=');
StringBuilder url = new StringBuilder(); param.append(parameters.get(key));
url.append( path ); }
if( !parameters.isEmpty() ) return param.toString();
url.append( PARAMETER_SEPARATOR ).append( getParameterString() ); }
return url.toString(); /**
} * Generates a path that are used in the HTTP header
*/
/** public String getHttpURL() {
* Generates a full URL StringBuilder url = new StringBuilder();
*/ url.append(path);
public String getURL(){ if (!parameters.isEmpty())
return toString(); url.append(PARAMETER_SEPARATOR).append(getParameterString());
}
return url.toString();
/** }
* Generates the whole URL
*/ /**
public String toString(){ * Generates a full URL
StringBuilder url = new StringBuilder(); */
url.append( protocol ); public String getURL() {
url.append( PROTOCOL_SEPARATOR ); return toString();
url.append( host ); }
if( port > 0 )
url.append( PORT_SEPARATOR ).append( port ); /**
* Generates the whole URL
if( path != null ) */
url.append( path ); public String toString() {
else StringBuilder url = new StringBuilder();
url.append( PATH_SEPARATOR ); url.append(protocol);
url.append(PROTOCOL_SEPARATOR);
if( !parameters.isEmpty() ) url.append(host);
url.append( PARAMETER_SEPARATOR ).append( getParameterString() ); if (port > 0)
if( anchor != null ) url.append(PORT_SEPARATOR).append(port);
url.append( ANCHOR_SEPARATOR ).append( anchor );
if (path != null)
return url.toString(); url.append(path);
} else
} url.append(PATH_SEPARATOR);
if (!parameters.isEmpty())
url.append(PARAMETER_SEPARATOR).append(getParameterString());
if (anchor != null)
url.append(ANCHOR_SEPARATOR).append(anchor);
return url.toString();
}
}

View file

@ -36,7 +36,7 @@ public class HttpDigestAuthPageTest {
public void cleanRequest() throws IOException { public void cleanRequest() throws IOException {
HttpHeader rspHeader = HttpTestUtil.makeRequest(page); HttpHeader rspHeader = HttpTestUtil.makeRequest(page);
assertEquals(401, rspHeader.getHTTPCode()); assertEquals(401, rspHeader.getStatusCode());
assertNotNull(rspHeader.getHeader("WWW-Authenticate")); assertNotNull(rspHeader.getHeader("WWW-Authenticate"));
assertEquals("Digest", parseAuthType(rspHeader)); assertEquals("Digest", parseAuthType(rspHeader));
Map<String,String> authHeader = parseAuthHeader(rspHeader); Map<String,String> authHeader = parseAuthHeader(rspHeader);
@ -49,21 +49,21 @@ public class HttpDigestAuthPageTest {
@Test @Test
public void authenticate() throws IOException { public void authenticate() throws IOException {
HttpHeader rspHeader = authenticate(PAGE_USERNAME, PAGE_PASSWORD); HttpHeader rspHeader = authenticate(PAGE_USERNAME, PAGE_PASSWORD);
assertEquals(200, rspHeader.getHTTPCode()); assertEquals(200, rspHeader.getStatusCode());
assertThat(IOUtil.readContentAsString(rspHeader.getInputStream()), assertThat(IOUtil.readContentAsString(rspHeader.getInputStream()),
containsString(PAGE_CONTENT)); containsString(PAGE_CONTENT));
} }
@Test @Test
public void wrongUsername() throws IOException { public void wrongUsername() throws IOException {
HttpHeader rspHeader = authenticate(PAGE_USERNAME+"wrong", PAGE_PASSWORD); HttpHeader rspHeader = authenticate(PAGE_USERNAME+"wrong", PAGE_PASSWORD);
assertEquals(403, rspHeader.getHTTPCode()); assertEquals(403, rspHeader.getStatusCode());
assertThat(IOUtil.readContentAsString(rspHeader.getInputStream()), assertThat(IOUtil.readContentAsString(rspHeader.getInputStream()),
not(containsString(PAGE_CONTENT))); not(containsString(PAGE_CONTENT)));
} }
@Test @Test
public void wrongPassword() throws IOException { public void wrongPassword() throws IOException {
HttpHeader rspHeader = authenticate(PAGE_USERNAME, PAGE_PASSWORD+"wrong"); HttpHeader rspHeader = authenticate(PAGE_USERNAME, PAGE_PASSWORD+"wrong");
assertEquals(403, rspHeader.getHTTPCode()); assertEquals(403, rspHeader.getStatusCode());
assertThat(IOUtil.readContentAsString(rspHeader.getInputStream()), assertThat(IOUtil.readContentAsString(rspHeader.getInputStream()),
not(containsString(PAGE_CONTENT))); not(containsString(PAGE_CONTENT)));
} }