Big HTTP header parsing refactoring

This commit is contained in:
Ziver Koc 2016-02-19 20:28:26 +01:00
parent 946953699f
commit 862bc7763e
50 changed files with 3114 additions and 3072 deletions

View file

@ -0,0 +1,176 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2015 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 zutil.converters.Converter;
import java.util.HashMap;
import java.util.Iterator;
public class HttpHeader {
// HTTP info
private String type;
private String url;
private HashMap<String, String> urlAttributes;
private float version;
private int httpCode;
// Parameters
private HashMap<String, String> headers;
private HashMap<String, String> cookies;
protected HttpHeader(){
urlAttributes = new HashMap<String, String>();
headers = new HashMap<String, String>();
cookies = new HashMap<String, String>();
}
/**
* @return the HTTP message type( ex. GET,POST...)
*/
public String getRequestType(){
return type;
}
/**
* @return the HTTP version of this header
*/
public float getHTTPVersion(){
return version;
}
/**
* @return the HTTP Return Code from a Server
*/
public int getHTTPCode(){
return httpCode;
}
/**
* @return the URL that the client sent the server
*/
public String getRequestURL(){
return url;
}
/**
* @return a Iterator with all defined url keys
*/
public Iterator<String> getURLAttributeKeys(){
return urlAttributes.keySet().iterator();
}
/**
* Returns the URL attribute value of the given name.
*
* returns null if there is no such attribute
*/
public String getURLAttribute(String name){
return urlAttributes.get( name );
}
/**
* @return a Iterator with all defined headers
*/
public Iterator<String> getHeaderKeys(){
return headers.keySet().iterator();
}
/**
* Returns the HTTP attribute value of the given name.
*
* returns null if there is no such attribute
*/
public String getHeader(String name){
return headers.get( name.toUpperCase() );
}
/**
* @return a Iterator with all defined cookies
*/
public Iterator<String> getCookieKeys(){
return cookies.keySet().iterator();
}
/**
* Returns the cookie value of the given name.
*
* returns null if there is no such attribute
*/
public String getCookie(String name){
return cookies.get( name );
}
protected HashMap<String,String> getCookieMap(){
return cookies;
}
protected HashMap<String,String> getUrlAttributeMap(){
return urlAttributes;
}
protected void setRequestType(String type){
this.type = type.trim();
}
protected void setHTTPVersion(float version){
this.version = version;
}
protected void setHTTPCode(int code){
this.httpCode = code;
}
protected void setRequestURL(String url){
this.url = url.trim().replaceAll("//", "/");
}
protected void putCookie(String key, String value){
cookies.put(key.trim(), value.trim());
}
protected void putURLAttribute(String key, String value){
urlAttributes.put(key.trim(), value.trim());
}
protected void putHeader(String key, String value){
headers.put(key.trim(), value.trim());
}
public String toString(){
StringBuilder tmp = new StringBuilder();
tmp.append("{Type: ").append(type);
tmp.append(", HTTP_version: HTTP/").append(version);
if(url == null)
tmp.append(", URL: null");
else
tmp.append(", URL: \"").append(url).append('\"');
tmp.append(", URL_attr: ").append(toStringAttributes());
tmp.append(", Headers: ").append(toStringHeaders());
tmp.append(", Cookies: ").append(toStringCookies());
tmp.append('}');
return tmp.toString();
}
public String toStringHeaders(){
return Converter.toString(headers);
}
public String toStringCookies(){
return Converter.toString(cookies);
}
public String toStringAttributes(){
return Converter.toString(urlAttributes);
}
}

View file

@ -1,297 +1,158 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2015 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 zutil.parser.URLDecoder;
import java.io.BufferedReader;
import java.io.IOException;
import java.util.HashMap;
import java.util.Scanner;
import java.util.regex.Pattern;
public class HttpHeaderParser {
// Some Cached regex's
private static final Pattern colonPattern = Pattern.compile(":");
private static final Pattern equalPattern = Pattern.compile("=");
private static final Pattern andPattern = Pattern.compile("&");
private static final Pattern semiColonPattern = Pattern.compile(";");
// HTTP info
private String type;
private String url;
private HashMap<String, String> url_attr;
private float version;
private int httpCode;
// Parameters
private HashMap<String, String> headers;
private HashMap<String, String> cookies;
/**
* Parses the HTTP header information from a stream
*
* @param in is the stream
* @throws IOException
*/
public HttpHeaderParser(BufferedReader in) throws IOException{
url_attr = new HashMap<String, String>();
headers = new HashMap<String, String>();
cookies = new HashMap<String, String>();
String tmp = null;
if( (tmp=in.readLine()) != null && !tmp.isEmpty() ){
parseStatusLine( tmp );
while( (tmp=in.readLine()) != null && !tmp.isEmpty() ){
parseLine( tmp );
}
}
parseCookies();
}
/**
* Parses the HTTP header information from an String
*
* @param in is the string
*/
public HttpHeaderParser(String in){
url_attr = new HashMap<String, String>();
headers = new HashMap<String, String>();
cookies = new HashMap<String, String>();
Scanner sc = new Scanner(in);
sc.useDelimiter("\n");
String tmp = null;
if( sc.hasNext() && !(tmp=sc.next()).isEmpty() ){
parseStatusLine( tmp );
while( sc.hasNext() && !(tmp=sc.next()).isEmpty() ){
parseLine( tmp );
}
}
sc.close();
parseCookies();
}
/**
* Parses the first header line and ads the values to
* the map and returns the file name and path
*
* @param line The header String
* @return The path and file name as a String
*/
protected void parseStatusLine(String line){
// Server Response
if( line.startsWith("HTTP/") ){
version = Float.parseFloat( line.substring( 5 , 8) );
httpCode = Integer.parseInt( line.substring( 9, 12 ));
}
// Client Request
else if(line.contains("HTTP/")){
type = (line.substring(0, line.indexOf(" "))).trim();
version = Float.parseFloat( line.substring(line.lastIndexOf("HTTP/")+5 , line.length()).trim() );
line = (line.substring(type.length()+1, line.lastIndexOf("HTTP/"))).trim();
// parse URL and attributes
int index = line.indexOf('?');
if(index > -1){
url = line.substring(0, index );
line = line.substring( index+1, line.length());
parseURLParameters(line, url_attr);
}
else{
url = line;
}
url = url.replaceAll("//", "/");
}
}
/**
* Parses a String with variables from a get or post
* that was sent from a client and puts the data into a HashMap
*
* @param attributes is the String containing all the attributes
*/
public static HashMap<String, String> parseURLParameters( String attributes ){
HashMap<String, String> map = new HashMap<String, String>();
parseURLParameters(attributes, map);
return map;
}
/**
* Parses a String with variables from a get or post
* that was sent from a client and puts the data into a HashMap
*
* @param attributes is the String containing all the attributes
* @param map is the HashMap to put all the values into
*/
public static void parseURLParameters(String attributes, HashMap<String, String> map){
String[] tmp;
attributes = URLDecoder.decode(attributes);
// get the variables
String[] data = andPattern.split( attributes );
for(String element : data){
tmp = equalPattern.split(element, 2);
map.put(
tmp[0].trim(), // Key
(tmp.length>1 ? tmp[1] : "").trim()); //Value
}
}
/**
* Parses the rest of the header
*
* @param line is the next line in the header
*/
protected void parseLine(String line){
String[] data = colonPattern.split( line, 2 );
headers.put(
data[0].trim().toUpperCase(), // Key
(data.length>1 ? data[1] : "").trim()); //Value
}
/**
* Parses the attribute "Cookie" and returns a HashMap
* with the values
*
* @return a HashMap with cookie values
*/
protected void parseCookies(){
if( headers.containsKey("COOKIE") ){
String[] tmp = semiColonPattern.split( headers.get("COOKIE") );
String[] tmp2;
for(String cookie : tmp){
tmp2 = equalPattern.split(cookie, 2);
cookies.put(
tmp2[0].trim(), // Key
(tmp2.length>1 ? tmp2[1] : "").trim()); //Value
}
}
}
/**
* @return the HTTP message type( ex. GET,POST...)
*/
public String getRequestType(){
return type;
}
/**
* @return the HTTP version of this header
*/
public float getHTTPVersion(){
return version;
}
/**
* @return the HTTP Return Code from a Server
*/
public float getHTTPCode(){
return httpCode;
}
/**
* @return the URL that the client sent the server
*/
public String getRequestURL(){
return url;
}
/**
* Returns the URL attribute value of the given name.
*
* returns null if there is no such attribute
*/
public String getURLAttribute(String name){
return url_attr.get( name );
}
/**
* Returns the HTTP attribute value of the given name.
*
* returns null if there is no such attribute
*/
public String getHeader(String name){
return headers.get( name.toUpperCase() );
}
/**
* Returns the cookie value of the given name.
*
* returns null if there is no such attribute
*/
public String getCookie(String name){
return cookies.get( name );
}
/**
* @return a map of the parsed cookies
*/
public HashMap<String, String> getCookies(){
return cookies;
}
/**
* @return a map of the parsed URL attributes
*/
public HashMap<String, String> getURLAttributes(){
return url_attr;
}
/**
* @return a map of the parsed headers
*/
public HashMap<String, String> getHeaders(){
return headers;
}
public String toString(){
StringBuilder tmp = new StringBuilder();
tmp.append("{Type: ").append(type);
tmp.append(", HTTP_version: HTTP/").append(version);
if(url == null)
tmp.append(", URL: null");
else
tmp.append(", URL: \"").append(url).append('\"');
tmp.append(", URL_attr: { ");
for( String key : url_attr.keySet() ){
tmp.append(key).append(": \"");
tmp.append(url_attr.get(key)).append("\", ");
}
tmp.append('}');
tmp.append(", Headers: {");
for( String key : headers.keySet() ){
tmp.append(key).append(": \"");
tmp.append( headers.get(key) ).append("\", ");
}
tmp.append('}');
tmp.append(", Cookies: {");
for( String key : cookies.keySet() ){
tmp.append(key).append(": \"");
tmp.append( cookies.get(key) ).append("\", ");
}
tmp.append('}');
tmp.append('}');
return tmp.toString();
}
}
/*
* The MIT License (MIT)
*
* Copyright (c) 2015 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 zutil.parser.URLDecoder;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
import java.util.regex.Pattern;
public class HttpHeaderParser {
public static final String HEADER_COOKIE = "COOKIE";
private static final Pattern PATTERN_COLON = Pattern.compile(":");
private static final Pattern PATTERN_EQUAL = Pattern.compile("=");
private static final Pattern PATTERN_AND = Pattern.compile("&");
private static final Pattern PATTERN_SEMICOLON = Pattern.compile(";");
private BufferedReader in;
/**
* Parses the HTTP header information from a stream
*
* @param in is the stream
*/
public HttpHeaderParser(BufferedReader in){
this.in = in;
}
/**
* Parses the HTTP header information from a String
*
* @param in is the String
*/
public HttpHeaderParser(String in){
this.in = new BufferedReader(new StringReader(in));
}
public HttpHeader read() throws IOException {
HttpHeader header = null;
String line = null;
if( (line=in.readLine()) != null && !line.isEmpty() ){
header = new HttpHeader();
parseStatusLine(header, line);
while( (line=in.readLine()) != null && !line.isEmpty() ){
parseLine(header, line);
}
parseCookies(header);
}
return header;
}
/**
* Parses the first header line and ads the values to
* the map and returns the file name and path
*
* @param line The header String
* @return The path and file name as a String
*/
protected static void parseStatusLine(HttpHeader header, String line){
// Server Response
if( line.startsWith("HTTP/") ){
header.setHTTPVersion( Float.parseFloat( line.substring( 5 , 8)));
header.setHTTPCode( Integer.parseInt( line.substring( 9, 12 )));
}
// Client Request
else if(line.contains("HTTP/")){
header.setRequestType( line.substring(0, line.indexOf(" ")));
header.setHTTPVersion( Float.parseFloat( line.substring(line.lastIndexOf("HTTP/")+5 , line.length()).trim()));
line = (line.substring(header.getRequestType().length()+1, line.lastIndexOf("HTTP/")));
// parse URL and attributes
int index = line.indexOf('?');
if(index > -1){
header.setRequestURL( line.substring(0, index));
line = line.substring( index+1, line.length());
parseURLParameters(header, line);
}
else{
header.setRequestURL(line);
}
}
}
/**
* Parses the rest of the header
*
* @param line is the next line in the header
*/
protected void parseLine(HttpHeader header, String line){
String[] data = PATTERN_COLON.split( line, 2 );
header.putHeader(
data[0].trim().toUpperCase(), // Key
(data.length>1 ? data[1] : "").trim()); //Value
}
/**
* Parses the header "Cookie" and stores all cookies in the HttpHeader object
*/
protected void parseCookies(HttpHeader header){
String cookieHeader = header.getHeader(HEADER_COOKIE);
if(cookieHeader != null && !cookieHeader.isEmpty()){
String[] tmp = PATTERN_SEMICOLON.split(cookieHeader);
for(String cookie : tmp){
String[] tmp2 = PATTERN_EQUAL.split(cookie, 2);
header.putCookie(
tmp2[0].trim(), // Key
(tmp2.length>1 ? tmp2[1] : "").trim()); //Value
}
}
}
/**
* Parses a String with variables from a get or post
* that was sent from a client
*
* @param attributes is the String containing all the attributes
*/
protected static void parseURLParameters(HttpHeader header, String attributes){
String[] tmp;
attributes = URLDecoder.decode(attributes);
// get the variables
String[] data = PATTERN_AND.split( attributes );
for(String element : data){
tmp = PATTERN_EQUAL.split(element, 2);
header.putURLAttribute(
tmp[0].trim(), // Key
(tmp.length>1 ? tmp[1] : "").trim()); //Value
}
}
}

105
src/zutil/net/http/HttpPage.java Normal file → Executable file
View file

@ -1,52 +1,53 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2015 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 java.io.IOException;
import java.util.Map;
/**
* This is a interface for a ordinary page for the HttpServer
*
* @author Ziver
*
*/
public interface HttpPage{
/**
* This method has to be implemented for every page.
* This method is called when a client wants a response
* from this specific page.
* @param out is the PrintStream to the client
* @param client_info is information about the client
* @param session is session values for the client
* @param cookie is cookie information from the client
* @param request is POST and GET requests from the client
*/
public abstract void respond(HttpPrintStream out,
HttpHeaderParser client_info,
Map<String,Object> session,
Map<String,String> cookie,
Map<String,String> request) throws IOException;
}
/*
* The MIT License (MIT)
*
* Copyright (c) 2015 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 java.io.IOException;
import java.util.Map;
/**
* This is a interface for a ordinary page for the HttpServer
*
* @author Ziver
*
*/
public interface HttpPage{
/**
* This method has to be implemented for every page.
* This method is called when a client wants a response
* from this specific page.
*
* @param out is a output stream to the client
* @param headers is the header received from the client
* @param session is the session associated with the current client
* @param cookie is cookie information from the client
* @param request is POST and GET requests from the client
*/
public abstract void respond(HttpPrintStream out,
HttpHeader headers,
Map<String,Object> session,
Map<String,String> cookie,
Map<String,String> request) throws IOException;
}

View file

@ -24,6 +24,8 @@
package zutil.net.http;
import zutil.converters.Converter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
@ -66,7 +68,7 @@ public class HttpPrintStream extends OutputStream{
* Creates an new instance of HttpPrintStream with
* message type of RESPONSE and buffering disabled.
*
* @param out is the OutputStream to send the message
* @param out is the OutputStream where the data will be written to
*/
public HttpPrintStream(OutputStream out) {
this( out, HttpMessageType.RESPONSE );
@ -75,7 +77,7 @@ public class HttpPrintStream extends OutputStream{
* Creates an new instance of HttpPrintStream with
* message type buffering disabled.
*
* @param out is the OutputStream to send the message
* @param out is the OutputStream where the data will be written to
* @param type is the type of message
*/
public HttpPrintStream(OutputStream out, HttpMessageType type) {
@ -107,7 +109,7 @@ public class HttpPrintStream extends OutputStream{
*
* @param key is the name of the cookie
* @param value is the value of the cookie
* @throws IOException Throws exception if the header has already been sent
* @throws IOException if the header has already been sent
*/
public void setCookie(String key, String value) throws IOException{
if(cookies == null)
@ -120,7 +122,7 @@ public class HttpPrintStream extends OutputStream{
*
* @param key is the header name
* @param value is the value of the header
* @throws IOException Throws exception if the header has already been sent
* @throws IOException if the header has already been sent
*/
public void setHeader(String key, String value) throws IOException{
if(headers == null)
@ -325,24 +327,16 @@ public class HttpPrintStream extends OutputStream{
str.append(", req_url: null");
else
str.append(", req_url: \"").append(req_url).append('\"');
} else {
} else if (message_type == HttpMessageType.RESPONSE){
str.append(", status_code: ").append(res_status_code);
str.append(", status_str: ").append(getStatusString(res_status_code));
}
if (headers != null) {
str.append(", Headers: {");
for (String key : headers.keySet()) {
str.append(key).append(": \"").append(headers.get(key)).append("\", ");
}
str.append('}');
str.append(", Headers: ").append(Converter.toString(headers));
}
if (cookies != null) {
str.append(", Cookies: {");
for (String key : cookies.keySet()) {
str.append(key).append(": \"").append(cookies.get(key)).append("\", ");
}
str.append('}');
str.append(", Cookies: ").append(Converter.toString(cookies));
}
}
else

View file

@ -1,296 +1,298 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2015 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 zutil.StringUtil;
import zutil.log.LogUtil;
import zutil.net.threaded.ThreadedTCPNetworkServer;
import zutil.net.threaded.ThreadedTCPNetworkServerThread;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* A simple web server that handles both cookies and
* sessions for all the clients
*
* @author Ziver
*/
public class HttpServer extends ThreadedTCPNetworkServer{
private static final Logger logger = LogUtil.getLogger();
public static final String SERVER_VERSION = "Ziver HttpServer 1.0";
public static final int COOKIE_TTL = 200;
public static final int SESSION_TTL = 10*60*1000; // in milliseconds
private Map<String,HttpPage> pages;
private HttpPage defaultPage;
private Map<String,Map<String,Object>> sessions;
private int nextSessionId;
/**
* Creates a new instance of the sever
*
* @param port The port that the server should listen to
*/
public HttpServer(int port){
this(port, null, null);
}
/**
* Creates a new instance of the sever
*
* @param port The port that the server should listen to
* @param keyStore If this is not null then the server will use SSL connection with this keyStore file path
* @param keyStorePass If this is not null then the server will use a SSL connection with the given certificate
*/
public HttpServer(int port, File keyStore, String keyStorePass){
super( port, keyStore, keyStorePass );
pages = new ConcurrentHashMap<String,HttpPage>();
sessions = new ConcurrentHashMap<String,Map<String,Object>>();
nextSessionId = 0;
Timer timer = new Timer();
timer.schedule(new GarbageCollector(), 10000, SESSION_TTL / 2);
logger.info("HTTP"+(keyStore==null?"":"S")+" Server ready!");
}
/**
* This class acts as an garbage collector that
* removes old sessions from the session HashMap
*
* @author Ziver
*/
private class GarbageCollector extends TimerTask {
public void run(){
Object[] keys = sessions.keySet().toArray();
for(Object key : keys){
Map<String,Object> client_session = sessions.get(key);
// Check if session is still valid
if((Long)client_session.get("ttl") < System.currentTimeMillis()){
sessions.remove(key);
logger.fine("Removing Session: "+key);
}
}
}
}
/**
* Add a HttpPage to a specific URL
*
* @param name The URL or name of the page
* @param page The page itself
*/
public void setPage(String name, HttpPage page){
if(name.charAt(0) != '/')
name = "/"+name;
pages.put(name, page);
}
/**
* This is a default page that will be shown
* if there is no other matching page,
*
* @param page The HttpPage that will be shown
*/
public void setDefaultPage(HttpPage page){
defaultPage = page;
}
protected ThreadedTCPNetworkServerThread getThreadInstance( Socket s ){
try {
return new HttpServerThread( s );
} catch (IOException e) {
logger.log(Level.SEVERE, "Could not start new Thread", e);
}
return null;
}
/**
* Internal class that handles all the requests
*
* @author Ziver
*
*/
protected class HttpServerThread implements ThreadedTCPNetworkServerThread{
private HttpPrintStream out;
private BufferedReader in;
private Socket socket;
public HttpServerThread(Socket socket) throws IOException{
out = new HttpPrintStream(socket.getOutputStream());
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
this.socket = socket;
}
public void run(){
String tmp = null;
HashMap<String,String> cookie = new HashMap<String,String>();
HashMap<String,String> request = new HashMap<String,String>();
//**************************** REQUEST *********************************
try {
long time = System.currentTimeMillis();
HttpHeaderParser parser = new HttpHeaderParser(in);
request = parser.getURLAttributes();
cookie = parser.getCookies();
//******* Read in the post data if available
if( parser.getHeader("Content-Length")!=null ){
// Reads the post data size
tmp = parser.getHeader("Content-Length");
int post_data_length = Integer.parseInt( tmp );
// read the data
StringBuffer tmpb = new StringBuffer();
// read the data
for(int i=0; i<post_data_length ;i++){
tmpb.append((char)in.read());
}
tmp = parser.getHeader("Content-Type");
if( tmp.contains("application/x-www-form-urlencoded") ){
// get the variables
HttpHeaderParser.parseURLParameters( tmpb.toString(), request );
}
else if( tmp.contains("application/soap+xml" ) ||
tmp.contains("text/xml") ||
tmp.contains("text/plain") ){
// save the variables
request.put( "" , tmpb.toString() );
}
else if( tmp.contains("multipart/form-data") ){
// TODO: File upload
throw new Exception( "\"multipart-form-data\" Not implemented." );
}
}
//**************************** HANDLE REQUEST *********************************
// Get the client session or create one
Map<String, Object> client_session;
long ttl_time = System.currentTimeMillis()+SESSION_TTL;
if( cookie.containsKey("session_id") && sessions.containsKey(cookie.get("session_id")) ){
client_session = sessions.get( cookie.get("session_id") );
// Check if session is still valid
if( (Long)client_session.get("ttl") < System.currentTimeMillis() ){
int session_id = (Integer)client_session.get("session_id");
client_session = Collections.synchronizedMap(new HashMap<String, Object>());
client_session.put( "session_id", session_id);
sessions.put( ""+session_id, client_session);
}
// renew the session TTL
client_session.put("ttl", ttl_time);
}
else{
client_session = Collections.synchronizedMap(new HashMap<String, Object>());
client_session.put( "session_id", nextSessionId );
client_session.put( "ttl", ttl_time );
sessions.put( ""+nextSessionId, client_session );
++nextSessionId;
}
//**************************** RESPONSE ************************************
out.setStatusCode(200);
out.setHeader( "Server", SERVER_VERSION );
out.setHeader( "Content-Type", "text/html" );
out.setCookie( "session_id", ""+client_session.get("session_id") );
if( parser.getRequestURL() != null && pages.containsKey(parser.getRequestURL()) ){
HttpPage page = pages.get(parser.getRequestURL());
page.respond(out, parser, client_session, cookie, request);
if(LogUtil.isLoggable(page.getClass(), Level.FINER))
logRequest(parser, client_session, cookie, request, time);
}
else if( parser.getRequestURL() != null && defaultPage != null ){
defaultPage.respond(out, parser, client_session, cookie, request);
if(LogUtil.isLoggable(defaultPage.getClass(), Level.FINER))
logRequest(parser, client_session, cookie, request, time);
}
else{
out.setStatusCode( 404 );
out.println( "404 Page Not Found: "+parser.getRequestURL() );
logger.warning("Page not defined: " + parser.getRequestURL());
}
//********************************************************************************
} catch (Exception e) {
logger.log(Level.SEVERE, "500 Internal Server Error", e);
try {
if (!out.isHeaderSent())
out.setStatusCode(500);
if (e.getMessage() != null)
out.println("500 Internal Server Error: " + e.getMessage());
else if (e.getCause() != null) {
out.println("500 Internal Server Error: " + e.getCause().getMessage());
} else {
out.println("500 Internal Server Error: " + e);
}
}catch(IOException ioe){
logger.log(Level.SEVERE, null, ioe);
}
}
try{
out.close();
in.close();
socket.close();
} catch( Exception e ) {
logger.log(Level.WARNING, "Could not close connection", e);
}
}
}
protected static void logRequest(HttpHeaderParser parser,
Map<String,Object> client_session,
Map<String,String> cookie,
Map<String,String> request,
long time){
// Debug
if(logger.isLoggable(Level.FINEST) ){
logger.finer(
"Received request: " + parser.getRequestURL()
+ " (client_session: " + client_session
+ ", cookie: " + cookie
+ ", request: " + request + ")"
+ ", time: "+ StringUtil.formatTimeToString(System.currentTimeMillis() - time));
} else if(logger.isLoggable(Level.FINER)){
logger.finer(
"Received request: " + parser.getRequestURL()
+ ", time: "+ StringUtil.formatTimeToString(System.currentTimeMillis() - time));
}
}
}
/*
* The MIT License (MIT)
*
* Copyright (c) 2015 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 zutil.StringUtil;
import zutil.log.LogUtil;
import zutil.net.threaded.ThreadedTCPNetworkServer;
import zutil.net.threaded.ThreadedTCPNetworkServerThread;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* A simple web server that handles both cookies and
* sessions for all the clients
*
* @author Ziver
*/
public class HttpServer extends ThreadedTCPNetworkServer{
private static final Logger logger = LogUtil.getLogger();
public static final String SESSION_ID_KEY = "session_id";
public static final String SESSION_TTL_KEY = "session_ttl";
public static final String SERVER_VERSION = "Ziver HttpServer 1.1";
public static final int SESSION_TTL = 10*60*1000; // in milliseconds
private Map<String,HttpPage> pages;
private HttpPage defaultPage;
private Map<Integer,Map<String,Object>> sessions;
private int nextSessionId;
/**
* Creates a new instance of the sever
*
* @param port The port that the server should listen to
*/
public HttpServer(int port){
this(port, null, null);
}
/**
* Creates a new instance of the sever
*
* @param port The port that the server should listen to
* @param keyStore If this is not null then the server will use SSL connection with this keyStore file path
* @param keyStorePass If this is not null then the server will use a SSL connection with the given certificate
*/
public HttpServer(int port, File keyStore, String keyStorePass){
super( port, keyStore, keyStorePass );
pages = new ConcurrentHashMap<>();
sessions = new ConcurrentHashMap<>();
nextSessionId = 0;
Timer timer = new Timer();
timer.schedule(new SessionGarbageCollector(), 10000, SESSION_TTL / 2);
logger.info("HTTP"+(keyStore==null?"":"S")+" Server ready!");
}
/**
* This class acts as an garbage collector that
* removes old sessions from the session HashMap
*
* @author Ziver
*/
private class SessionGarbageCollector extends TimerTask {
public void run(){
Object[] keys = sessions.keySet().toArray();
for(Object key : keys){
Map<String,Object> session = sessions.get(key);
// Check if session is still valid
if((Long)session.get(SESSION_TTL_KEY) < System.currentTimeMillis()){
sessions.remove(key);
logger.fine("Removing Session: "+key);
}
}
}
}
/**
* Add a HttpPage to a specific URL
*
* @param name The URL or name of the page
* @param page The page itself
*/
public void setPage(String name, HttpPage page){
if(name.charAt(0) != '/')
name = "/"+name;
pages.put(name, page);
}
/**
* This is a default page that will be shown
* if there is no other matching page,
*
* @param page The HttpPage that will be shown
*/
public void setDefaultPage(HttpPage page){
defaultPage = page;
}
protected ThreadedTCPNetworkServerThread getThreadInstance( Socket s ){
try {
return new HttpServerThread( s );
} catch (IOException e) {
logger.log(Level.SEVERE, "Could not start new Thread", e);
}
return null;
}
/**
* Internal class that handles all the requests
*
* @author Ziver
*
*/
protected class HttpServerThread implements ThreadedTCPNetworkServerThread{
private HttpPrintStream out;
private BufferedReader in;
private Socket socket;
public HttpServerThread(Socket socket) throws IOException{
out = new HttpPrintStream(socket.getOutputStream());
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
this.socket = socket;
}
public void run(){
String tmp = null;
HttpHeaderParser headerParser = new HttpHeaderParser(in);
//**************************** REQUEST *********************************
try {
long time = System.currentTimeMillis();
HttpHeader header = headerParser.read();
if(header == null){
logger.finer("No header received");
return;
}
//******* Read in the post data if available
if( header.getHeader("Content-Length") != null ){
// Reads the post data size
tmp = header.getHeader("Content-Length");
int post_data_length = Integer.parseInt( tmp );
// read the data
StringBuilder tmpBuff = new StringBuilder();
// read the data
for(int i=0; i<post_data_length ;i++){
tmpBuff.append((char)in.read());
}
tmp = header.getHeader("Content-Type");
if( tmp.contains("application/x-www-form-urlencoded") ){
// get the variables
HttpHeaderParser.parseURLParameters(header, tmpBuff.toString());
}
else if( tmp.contains("application/soap+xml" ) ||
tmp.contains("text/xml") ||
tmp.contains("text/plain") ){
// save the variables
header.putURLAttribute("" , tmpBuff.toString());
}
else if( tmp.contains("multipart/form-data") ){
// TODO: File upload
throw new UnsupportedOperationException("HTTP Content-Type 'multipart-form-data' not supported." );
}
}
//**************************** HANDLE REQUEST *********************************
// Get the client session or create one
Map<String, Object> session;
long ttlTime = System.currentTimeMillis() + SESSION_TTL;
String sessionCookie = header.getCookie(SESSION_ID_KEY);
if( sessionCookie != null && sessions.containsKey(sessionCookie) &&
(Long)sessions.get(sessionCookie).get(SESSION_TTL_KEY) < System.currentTimeMillis()){ // Check if session is still valid
session = sessions.get(sessionCookie);
// renew the session TTL
session.put(SESSION_TTL_KEY, ttlTime);
}
else{
session = Collections.synchronizedMap(new HashMap<String, Object>());
session.put(SESSION_ID_KEY, nextSessionId );
session.put(SESSION_TTL_KEY, ttlTime );
sessions.put(nextSessionId, session );
++nextSessionId;
}
//**************************** RESPONSE ************************************
out.setStatusCode(200);
out.setHeader("Server", SERVER_VERSION);
out.setHeader("Content-Type", "text/html");
out.setCookie(SESSION_ID_KEY, ""+session.get(SESSION_ID_KEY));
if( header.getRequestURL() != null && pages.containsKey(header.getRequestURL())){
HttpPage page = pages.get(header.getRequestURL());
page.respond(out, header, session, header.getCookieMap(), header.getUrlAttributeMap());
if(LogUtil.isLoggable(page.getClass(), Level.FINER))
logRequest(header, session, time);
}
else if( header.getRequestURL() != null && defaultPage != null ){
defaultPage.respond(out, header, session, header.getCookieMap(), header.getUrlAttributeMap());
if(LogUtil.isLoggable(defaultPage.getClass(), Level.FINER))
logRequest(header, session, time);
}
else{
out.setStatusCode(404);
out.println("404 Page Not Found: "+header.getRequestURL());
logger.warning("Page not defined: " + header.getRequestURL());
}
//********************************************************************************
} catch (Exception e) {
logger.log(Level.SEVERE, "500 Internal Server Error", e);
try {
if (!out.isHeaderSent())
out.setStatusCode(500);
if (e.getMessage() != null)
out.println("500 Internal Server Error: " + e.getMessage());
else if (e.getCause() != null) {
out.println("500 Internal Server Error: " + e.getCause().getMessage());
} else {
out.println("500 Internal Server Error: " + e);
}
}catch(IOException ioe){
logger.log(Level.SEVERE, null, ioe);
}
}
finally {
try{
out.close();
in.close();
socket.close();
} catch( Exception e ) {
logger.log(Level.WARNING, "Could not close connection", e);
}
}
}
}
protected static void logRequest(HttpHeader header,
Map<String,Object> session,
long time){
// Debug
if(logger.isLoggable(Level.FINEST) ){
StringBuilder buff = new StringBuilder();
buff.append("Received request: ").append(header.getRequestURL());
buff.append(", (");
buff.append("request: ").append(header.toStringAttributes());
buff.append(", cookies: ").append(header.toStringCookies());
buff.append(", session: ").append(session);
buff.append(")");
buff.append(", time: "+ StringUtil.formatTimeToString(System.currentTimeMillis() - time));
logger.finer(buff.toString());
} else if(logger.isLoggable(Level.FINER)){
logger.finer(
"Received request: " + header.getRequestURL()
+ ", time: "+ StringUtil.formatTimeToString(System.currentTimeMillis() - time));
}
}
}

290
src/zutil/net/http/multipart/MultipartParser.java Normal file → Executable file
View file

@ -1,145 +1,145 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2015 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.multipart;
import zutil.ProgressListener;
import zutil.net.http.HttpHeaderParser;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* Parses a multipart/form-data http request,
* saves files to temporary location.
*
* http://www.ietf.org/rfc/rfc1867.txt
*
* @author Ziver
*
*/
public class MultipartParser {
/** This is the temporary directory for the received files */
private File tempDir;
/** This is the delimiter that will separate the fields */
private String delimiter;
/** The length of the HTTP Body */
private long contentLength;
/** This is the input stream */
private BufferedReader in;
/** This is the listener that will listen on the progress */
private ProgressListener<MultipartParser,MultipartField> listener;
public MultipartParser(BufferedReader in, HttpHeaderParser header){
this.in = in;
String cotype = header.getHeader("Content-type");
cotype = cotype.split(" *; *")[1];
delimiter = cotype.split(" *= *")[1];
contentLength = Long.parseLong( header.getHeader("Content-Length") );
}
public MultipartParser(BufferedReader in, HttpServletRequest req){
this.in = in;
String cotype = req.getHeader("Content-type");
cotype = cotype.split(" *; *")[1];
delimiter = cotype.split(" *= *")[1];
contentLength = req.getContentLength();
}
public MultipartParser(BufferedReader in, String delimiter, long length){
this.in = in;
this.delimiter = delimiter;
this.contentLength = length;
}
/**
* @param listener is the listener that will be called for progress
*/
public void setListener(ProgressListener<MultipartParser,MultipartField> listener){
this.listener = listener;
}
/**
* Parses the HTTP Body and returns a list of fields
*
* @return A list of FormField
*/
public List<MultipartField> parse() throws IOException{
ArrayList<MultipartField> list = new ArrayList<MultipartField>();
parse(list, delimiter);
return list;
}
private void parse(List<MultipartField> list, String delimiter) throws IOException{
// TODO:
}
/**
* Creates a temporary file in either the system
* temporary folder or by the setTempDir() function
*
* @return the temporary file
*/
protected File createTempFile() throws IOException{
if(tempDir != null)
return File.createTempFile("upload", ".part", tempDir.getAbsoluteFile());
else
return File.createTempFile("upload", ".part");
}
/**
* Sets the initial delimiter
*
* @param delimiter is the new delimiter
*/
public void setDelimiter(String delimiter){
this.delimiter = delimiter;
}
public void setTempDir(File dir){
if(!dir.isDirectory())
throw new RuntimeException("\""+dir.getAbsolutePath()+"\" is not a directory!");
if(!dir.canWrite())
throw new RuntimeException("\""+dir.getAbsolutePath()+"\" is not writable!");
tempDir = dir;
}
public long getContentLength(){
return contentLength;
}
}
/*
* The MIT License (MIT)
*
* Copyright (c) 2015 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.multipart;
import zutil.ProgressListener;
import zutil.net.http.HttpHeader;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* Parses a multipart/form-data http request,
* saves files to temporary location.
*
* http://www.ietf.org/rfc/rfc1867.txt
*
* @author Ziver
*
*/
public class MultipartParser {
/** This is the temporary directory for the received files */
private File tempDir;
/** This is the delimiter that will separate the fields */
private String delimiter;
/** The length of the HTTP Body */
private long contentLength;
/** This is the input stream */
private BufferedReader in;
/** This is the listener that will listen on the progress */
private ProgressListener<MultipartParser,MultipartField> listener;
public MultipartParser(BufferedReader in, HttpHeader header){
this.in = in;
String cotype = header.getHeader("Content-type");
cotype = cotype.split(" *; *")[1];
delimiter = cotype.split(" *= *")[1];
contentLength = Long.parseLong( header.getHeader("Content-Length") );
}
public MultipartParser(BufferedReader in, HttpServletRequest req){
this.in = in;
String cotype = req.getHeader("Content-type");
cotype = cotype.split(" *; *")[1];
delimiter = cotype.split(" *= *")[1];
contentLength = req.getContentLength();
}
public MultipartParser(BufferedReader in, String delimiter, long length){
this.in = in;
this.delimiter = delimiter;
this.contentLength = length;
}
/**
* @param listener is the listener that will be called for progress
*/
public void setListener(ProgressListener<MultipartParser,MultipartField> listener){
this.listener = listener;
}
/**
* Parses the HTTP Body and returns a list of fields
*
* @return A list of FormField
*/
public List<MultipartField> parse() throws IOException{
ArrayList<MultipartField> list = new ArrayList<MultipartField>();
parse(list, delimiter);
return list;
}
private void parse(List<MultipartField> list, String delimiter) throws IOException{
// TODO:
}
/**
* Creates a temporary file in either the system
* temporary folder or by the setTempDir() function
*
* @return the temporary file
*/
protected File createTempFile() throws IOException{
if(tempDir != null)
return File.createTempFile("upload", ".part", tempDir.getAbsoluteFile());
else
return File.createTempFile("upload", ".part");
}
/**
* Sets the initial delimiter
*
* @param delimiter is the new delimiter
*/
public void setDelimiter(String delimiter){
this.delimiter = delimiter;
}
public void setTempDir(File dir){
if(!dir.isDirectory())
throw new RuntimeException("\""+dir.getAbsolutePath()+"\" is not a directory!");
if(!dir.canWrite())
throw new RuntimeException("\""+dir.getAbsolutePath()+"\" is not writable!");
tempDir = dir;
}
public long getContentLength(){
return contentLength;
}
}

328
src/zutil/net/http/pages/HttpFilePage.java Normal file → Executable file
View file

@ -1,164 +1,164 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2015 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.pages;
import zutil.io.IOUtil;
import zutil.io.file.FileUtil;
import zutil.log.LogUtil;
import zutil.net.http.HttpHeaderParser;
import zutil.net.http.HttpPage;
import zutil.net.http.HttpPrintStream;
import java.io.*;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* This Http Page will host static content from the server.
*
* Created by Ziver on 2015-03-30.
*/
public class HttpFilePage implements HttpPage{
private static final Logger log = LogUtil.getLogger();
private File resource_root;
private boolean showFolders;
private boolean redirectToIndex;
/**
* @param file a reference to a root directory or a file.
*/
public HttpFilePage(File file){
this.resource_root = file;
this.showFolders = true;
this.redirectToIndex = true;
}
@Override
public void respond(HttpPrintStream out,
HttpHeaderParser client_info,
Map<String, Object> session,
Map<String, String> cookie,
Map<String, String> request) throws IOException{
try {
// Is the root only one file or a folder
if (resource_root.isFile()) {
deliverFile(resource_root, out);
}
else { // Resource root is a folder
File file = new File(resource_root,
client_info.getRequestURL());
if(file.getCanonicalPath().startsWith(resource_root.getCanonicalPath())){
if(file.isDirectory() && showFolders){
File indexFile = new File(file, "index.html");
// Redirect to index.html
if(redirectToIndex && indexFile.isFile()) {
deliverFile(indexFile, out);
}
// Show folder contents
else if(showFolders){
out.println("<HTML><BODY><H1>Directory: " + client_info.getRequestURL() + "</H1>");
out.println("<HR><UL>");
for (String f : file.list()) {
String url = client_info.getRequestURL();
out.println("<LI><A href='" +
url + (url.charAt(url.length()-1)=='/'?"":"/")+ f
+"'>" + f + "</A></LI>");
}
out.println("</UL><HR></BODY></HTML>");
}
else {
throw new SecurityException("User not allowed to view folder: root=" + resource_root.getAbsolutePath());
}
}
else {
deliverFile(file, out);
}
}
else {
throw new SecurityException("File is outside of root directory: root=" + resource_root.getAbsolutePath() + " file=" + file.getAbsolutePath());
}
}
}catch (FileNotFoundException e){
if(!out.isHeaderSent())
out.setStatusCode(404);
log.log(Level.WARNING, e.getMessage());
out.println("404 Page Not Found: " + client_info.getRequestURL());
}catch (SecurityException e){
if(!out.isHeaderSent())
out.setStatusCode(404);
log.log(Level.WARNING, e.getMessage());
out.println("404 Page Not Found: " + client_info.getRequestURL() );
}catch (IOException e){
if(!out.isHeaderSent())
out.setStatusCode(500);
log.log(Level.WARNING, null, e);
out.println("500 Internal Server Error: "+e.getMessage() );
}
}
private void deliverFile(File file, HttpPrintStream out) throws IOException {
out.setHeader("Content-Type", getMIMEType(file));
out.flush();
//InputStream in = new BufferedInputStream(new FileInputStream(file));
InputStream in = new FileInputStream(file);
IOUtil.copyStream(in, out);
in.close();
}
private String getMIMEType(File file){
switch(FileUtil.getFileExtension(file)){
case "css": return "text/css";
case "cvs": return "text/csv";
case "jpg": return "image/jpeg";
case "js": return "application/javascript";
case "png": return "image/png";
case "htm":
case "html": return "text/html";
case "xml": return "text/xml";
default: return "text/plain";
}
}
/**
* Enable or disable showing of folder contents
*/
public void showFolders(boolean enabled){
this.showFolders = enabled;
}
/**
* If directory links should be redirected to index files
*/
public void redirectToIndexFile(boolean enabled){
this.redirectToIndex = enabled;
}
}
/*
* The MIT License (MIT)
*
* Copyright (c) 2015 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.pages;
import zutil.io.IOUtil;
import zutil.io.file.FileUtil;
import zutil.log.LogUtil;
import zutil.net.http.HttpHeader;
import zutil.net.http.HttpPage;
import zutil.net.http.HttpPrintStream;
import java.io.*;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* This Http Page will host static content from the server.
*
* Created by Ziver on 2015-03-30.
*/
public class HttpFilePage implements HttpPage{
private static final Logger log = LogUtil.getLogger();
private File resource_root;
private boolean showFolders;
private boolean redirectToIndex;
/**
* @param file a reference to a root directory or a file.
*/
public HttpFilePage(File file){
this.resource_root = file;
this.showFolders = true;
this.redirectToIndex = true;
}
@Override
public void respond(HttpPrintStream out,
HttpHeader headers,
Map<String, Object> session,
Map<String, String> cookie,
Map<String, String> request) throws IOException{
try {
// Is the root only one file or a folder
if (resource_root.isFile()) {
deliverFile(resource_root, out);
}
else { // Resource root is a folder
File file = new File(resource_root,
headers.getRequestURL());
if(file.getCanonicalPath().startsWith(resource_root.getCanonicalPath())){
if(file.isDirectory() && showFolders){
File indexFile = new File(file, "index.html");
// Redirect to index.html
if(redirectToIndex && indexFile.isFile()) {
deliverFile(indexFile, out);
}
// Show folder contents
else if(showFolders){
out.println("<HTML><BODY><H1>Directory: " + headers.getRequestURL() + "</H1>");
out.println("<HR><UL>");
for (String f : file.list()) {
String url = headers.getRequestURL();
out.println("<LI><A href='" +
url + (url.charAt(url.length()-1)=='/'?"":"/")+ f
+"'>" + f + "</A></LI>");
}
out.println("</UL><HR></BODY></HTML>");
}
else {
throw new SecurityException("User not allowed to view folder: root=" + resource_root.getAbsolutePath());
}
}
else {
deliverFile(file, out);
}
}
else {
throw new SecurityException("File is outside of root directory: root=" + resource_root.getAbsolutePath() + " file=" + file.getAbsolutePath());
}
}
}catch (FileNotFoundException e){
if(!out.isHeaderSent())
out.setStatusCode(404);
log.log(Level.WARNING, e.getMessage());
out.println("404 Page Not Found: " + headers.getRequestURL());
}catch (SecurityException e){
if(!out.isHeaderSent())
out.setStatusCode(404);
log.log(Level.WARNING, e.getMessage());
out.println("404 Page Not Found: " + headers.getRequestURL() );
}catch (IOException e){
if(!out.isHeaderSent())
out.setStatusCode(500);
log.log(Level.WARNING, null, e);
out.println("500 Internal Server Error: "+e.getMessage() );
}
}
private void deliverFile(File file, HttpPrintStream out) throws IOException {
out.setHeader("Content-Type", getMIMEType(file));
out.flush();
//InputStream in = new BufferedInputStream(new FileInputStream(file));
InputStream in = new FileInputStream(file);
IOUtil.copyStream(in, out);
in.close();
}
private String getMIMEType(File file){
switch(FileUtil.getFileExtension(file)){
case "css": return "text/css";
case "cvs": return "text/csv";
case "jpg": return "image/jpeg";
case "js": return "application/javascript";
case "png": return "image/png";
case "htm":
case "html": return "text/html";
case "xml": return "text/xml";
default: return "text/plain";
}
}
/**
* Enable or disable showing of folder contents
*/
public void showFolders(boolean enabled){
this.showFolders = enabled;
}
/**
* If directory links should be redirected to index files
*/
public void redirectToIndexFile(boolean enabled){
this.redirectToIndex = enabled;
}
}