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

@ -27,11 +27,7 @@ package zutil;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType; import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet; import java.util.HashSet;
import java.util.Objects;
/** /**
* This class include some utility functions for classes * This class include some utility functions for classes

View file

@ -27,7 +27,6 @@ package zutil;
import zutil.converters.Converter; import zutil.converters.Converter;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator; import java.util.Iterator;
/** /**

View file

@ -29,6 +29,8 @@ import zutil.parser.Base64Decoder;
import java.io.*; import java.io.*;
import java.util.BitSet; import java.util.BitSet;
import java.util.Iterator;
import java.util.Map;
public class Converter { public class Converter {
private Converter(){} private Converter(){}
@ -265,6 +267,35 @@ public class Converter {
return ret.toString(); return ret.toString();
} }
/**
* Generates a comma separated string with key and value pairs
*
* @return a comma separated String
*/
public static String toString(Map map){
StringBuilder tmp = new StringBuilder();
tmp.append("{");
Iterator<Object> it = map.keySet().iterator();
while(it.hasNext()){
Object key = it.next();
Object value = map.get(key);
tmp.append(key);
if (value != null) {
if (value instanceof String)
tmp.append(": \"").append(value).append("\"");
else
tmp.append(value);
}
else
tmp.append("null");
if(it.hasNext())
tmp.append(", ");
}
tmp.append('}');
return tmp.toString();
}
/** /**
* Converts a BitSet to a Integer * Converts a BitSet to a Integer
* *

View file

@ -31,7 +31,6 @@ import javax.naming.InitialContext;
import javax.naming.NamingException; import javax.naming.NamingException;
import javax.sql.DataSource; import javax.sql.DataSource;
import java.io.Closeable; import java.io.Closeable;
import java.math.BigInteger;
import java.sql.*; import java.sql.*;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;

View file

@ -25,7 +25,6 @@
package zutil.db; package zutil.db;
import zutil.StringUtil; import zutil.StringUtil;
import zutil.db.handler.ListSQLResult;
import zutil.db.handler.SimpleSQLResult; import zutil.db.handler.SimpleSQLResult;
import zutil.log.LogUtil; import zutil.log.LogUtil;
@ -37,7 +36,6 @@ import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
/** /**

View file

@ -24,7 +24,6 @@
package zutil.db.bean; package zutil.db.bean;
import zutil.ClassUtil;
import zutil.log.LogUtil; import zutil.log.LogUtil;
import java.lang.reflect.Field; import java.lang.reflect.Field;

View file

@ -31,7 +31,6 @@ import java.sql.SQLException;
import java.sql.Statement; import java.sql.Statement;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Properties;
/** /**
* Adds the result of the query to a List. * Adds the result of the query to a List.

View file

@ -31,8 +31,6 @@ import zutil.log.LogUtil;
import java.io.*; import java.io.*;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.net.URL; import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.logging.Logger; import java.util.logging.Logger;

View file

@ -26,7 +26,6 @@ package zutil.log;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;

View file

@ -27,7 +27,6 @@ package zutil.log;
import zutil.io.file.FileUtil; import zutil.io.file.FileUtil;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.util.Enumeration;
import java.util.logging.*; import java.util.logging.*;
/** /**

View file

@ -25,7 +25,6 @@
package zutil.log; package zutil.log;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;

View file

@ -24,13 +24,6 @@
package zutil.log; package zutil.log;
import com.mysql.jdbc.log.Log;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.logging.Level;
import java.util.logging.Logger;
/** /**
* Created by Ziver on 2015-10-15. * Created by Ziver on 2015-10-15.
*/ */

View file

@ -25,7 +25,6 @@
package zutil.net.dns; package zutil.net.dns;
import zutil.parser.binary.BinaryStruct; import zutil.parser.binary.BinaryStruct;
import zutil.parser.binary.BinaryStruct.*;
/** /**
* Created by Ziver on 2016-02-09. * Created by Ziver on 2016-02-09.

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

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

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

View file

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

View file

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

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

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

View file

@ -1,243 +1,249 @@
/* /*
* 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.ssdp; package zutil.net.ssdp;
import zutil.io.StringOutputStream; import zutil.io.StringOutputStream;
import zutil.log.LogUtil; import zutil.log.LogUtil;
import zutil.net.http.HttpHeaderParser; import zutil.net.http.HttpHeader;
import zutil.net.http.HttpPrintStream; import zutil.net.http.HttpHeaderParser;
import zutil.net.threaded.ThreadedUDPNetwork; import zutil.net.http.HttpPrintStream;
import zutil.net.threaded.ThreadedUDPNetworkThread; import zutil.net.threaded.ThreadedUDPNetwork;
import zutil.net.threaded.ThreadedUDPNetworkThread;
import java.io.IOException;
import java.net.DatagramPacket; import java.io.IOException;
import java.net.InetAddress; import java.net.DatagramPacket;
import java.util.HashMap; import java.net.InetAddress;
import java.util.LinkedList; import java.util.HashMap;
import java.util.logging.Level; import java.util.LinkedList;
import java.util.logging.Logger; import java.util.logging.Level;
import java.util.logging.Logger;
/**
* An SSDP client class that will request /**
* service information. * An SSDP client class that will request
* * service information.
* @author Ziver *
*/ * @author Ziver
public class SSDPClient extends ThreadedUDPNetwork implements ThreadedUDPNetworkThread{ */
private static final Logger logger = LogUtil.getLogger(); public class SSDPClient extends ThreadedUDPNetwork implements ThreadedUDPNetworkThread{
/** Mapping of search targets and list of associated services **/ private static final Logger logger = LogUtil.getLogger();
private HashMap<String, LinkedList<StandardSSDPInfo>> services_st; /** Mapping of search targets and list of associated services **/
/** Map of all unique services received **/ private HashMap<String, LinkedList<StandardSSDPInfo>> services_st;
private HashMap<String, StandardSSDPInfo> services_usn; /** Map of all unique services received **/
private HashMap<String, StandardSSDPInfo> services_usn;
private SSDPServiceListener listener;
private SSDPServiceListener listener;
/**
* Creates new instance of this class. An UDP /**
* listening socket at the SSDP port. * Creates new instance of this class. An UDP
* * listening socket at the SSDP port.
* @throws IOException *
*/ * @throws IOException
public SSDPClient() throws IOException{ */
super(null); public SSDPClient() throws IOException{
super.setThread(this); super(null);
super.setThread(this);
services_st = new HashMap<>();
services_usn = new HashMap<>(); services_st = new HashMap<>();
} services_usn = new HashMap<>();
}
/**
* Sends an request for an service /**
* * Sends an request for an service
* @param searchTarget is the SearchTarget of the service *
* * @param searchTarget is the SearchTarget of the service
* ***** REQUEST: *
* M-SEARCH * HTTP/1.1 * ***** REQUEST:
* Host: 239.255.255.250:reservedSSDPport * M-SEARCH * HTTP/1.1
* Man: "ssdp:discover" * Host: 239.255.255.250:reservedSSDPport
* ST: ge:fridge * Man: "ssdp:discover"
* MX: 3 * ST: ge:fridge
* * MX: 3
*/ *
public void requestService(String searchTarget){ */
requestService(searchTarget, null); public void requestService(String searchTarget){
} requestService(searchTarget, null);
public void requestService(String searchTarget, HashMap<String,String> headers){ }
try { public void requestService(String searchTarget, HashMap<String,String> headers){
// Generate an SSDP discover message try {
StringOutputStream msg = new StringOutputStream(); // Generate an SSDP discover message
HttpPrintStream http = new HttpPrintStream( msg, HttpPrintStream.HttpMessageType.REQUEST ); StringOutputStream msg = new StringOutputStream();
http.setRequestType("M-SEARCH"); HttpPrintStream http = new HttpPrintStream( msg, HttpPrintStream.HttpMessageType.REQUEST );
http.setRequestURL("*"); http.setRequestType("M-SEARCH");
http.setHeader("Host", SSDPServer.SSDP_MULTICAST_ADDR +":"+ SSDPServer.SSDP_PORT ); http.setRequestURL("*");
http.setHeader("ST", searchTarget ); http.setHeader("Host", SSDPServer.SSDP_MULTICAST_ADDR +":"+ SSDPServer.SSDP_PORT );
http.setHeader("Man", "\"ssdp:discover\"" ); http.setHeader("ST", searchTarget );
http.setHeader("MX", "3" ); http.setHeader("Man", "\"ssdp:discover\"" );
if(headers != null) { http.setHeader("MX", "3" );
for (String key : headers.keySet()) { if(headers != null) {
http.setHeader(key, headers.get(key)); for (String key : headers.keySet()) {
} http.setHeader(key, headers.get(key));
} }
logger.log(Level.FINEST, "Sending Multicast: "+ http); }
http.flush(); logger.log(Level.FINEST, "Sending Multicast: "+ http);
http.flush();
byte[] data = msg.toString().getBytes();
DatagramPacket packet = new DatagramPacket( byte[] data = msg.toString().getBytes();
data, data.length, DatagramPacket packet = new DatagramPacket(
InetAddress.getByName( SSDPServer.SSDP_MULTICAST_ADDR ), data, data.length,
SSDPServer.SSDP_PORT ); InetAddress.getByName( SSDPServer.SSDP_MULTICAST_ADDR ),
super.send( packet ); SSDPServer.SSDP_PORT );
http.close(); super.send( packet );
} catch (Exception e) { http.close();
e.printStackTrace(); } catch (Exception e) {
} e.printStackTrace();
} }
}
/**
* Set a listener that will be notified when new services are detected /**
*/ * Set a listener that will be notified when new services are detected
public void setListener(SSDPServiceListener listener){ */
this.listener = listener; public void setListener(SSDPServiceListener listener){
} this.listener = listener;
}
/**
* Returns a list of received services by /**
* the given search target. * Returns a list of received services by
* * the given search target.
* @param searchTarget is the search target *
* @return a list of received services * @param searchTarget is the search target
*/ * @return a list of received services
public LinkedList<StandardSSDPInfo> getServices(String searchTarget){ */
if(services_st.get(searchTarget) == null) public LinkedList<StandardSSDPInfo> getServices(String searchTarget){
return new LinkedList<>(); if(services_st.get(searchTarget) == null)
return services_st.get(searchTarget); return new LinkedList<>();
} return services_st.get(searchTarget);
}
/**
* Returns the amount of services in the search target /**
* * Returns the amount of services in the search target
* @param searchTarget is the search target *
* @return the amount of services cached * @param searchTarget is the search target
*/ * @return the amount of services cached
public int getServicesCount(String searchTarget){ */
if(services_st.containsKey(searchTarget)){ public int getServicesCount(String searchTarget){
return services_st.get(searchTarget).size(); if(services_st.containsKey(searchTarget)){
} return services_st.get(searchTarget).size();
return 0; }
} return 0;
}
/**
* Returns a service with the given USN. /**
* * Returns a service with the given USN.
* @param usn is the unique identifier for a service *
* @return an service, null if there is no such service * @param usn is the unique identifier for a service
*/ * @return an service, null if there is no such service
public StandardSSDPInfo getService(String usn){ */
return services_usn.get( usn ); public StandardSSDPInfo getService(String usn){
} return services_usn.get( usn );
}
/**
* Clears all the received information of the services /**
*/ * Clears all the received information of the services
public void clearServices(){ */
services_usn.clear(); public void clearServices(){
services_st.clear(); services_usn.clear();
} services_st.clear();
/** }
* Clears all services matching the search target /**
*/ * Clears all services matching the search target
public void clearServices(String st){ */
if(services_st.get(st) != null) { public void clearServices(String st){
for (StandardSSDPInfo service : services_st.get(st)) { if(services_st.get(st) != null) {
services_usn.remove(service.getUSN()); for (StandardSSDPInfo service : services_st.get(st)) {
services_usn.remove(service.getUSN());
}
}
}
/**
* Waits for responses
*
* ***** RESPONSE;
* HTTP/1.1 200 OK
* Ext:
* Cache-Control: no-cache="Ext", max-age = 5000
* ST: ge:fridge
* USN: uuid:abcdefgh-7dec-11d0-a765-00a0c91e6bf6
* Location: http://localhost:80
*/
public void receivedPacket(DatagramPacket packet, ThreadedUDPNetwork network) {
try {
String msg = new String(packet.getData(), packet.getOffset(), packet.getLength());
HttpHeaderParser headerParser = new HttpHeaderParser(msg);
HttpHeader header = headerParser.read();
logger.log(Level.FINEST, "Received(from: " + packet.getAddress() + "): " + header);
String usn = header.getHeader("USN");
String st = header.getHeader("ST");
boolean newService = false;
StandardSSDPInfo service;
// Get existing service
if (services_usn.containsKey(usn)) {
service = services_usn.get(usn);
} }
// Add new service
else {
newService = true;
service = new StandardSSDPInfo();
services_usn.put(usn, service);
if (!services_st.containsKey(st))
services_st.put(st, new LinkedList<StandardSSDPInfo>());
services_st.get(header.getHeader("ST")).add(service);
}
service.setLocation(header.getHeader("LOCATION"));
service.setST(st);
service.setUSN(usn);
service.setInetAddress(packet.getAddress());
if (header.getHeader("Cache-Control") != null) {
service.setExpirationTime(
System.currentTimeMillis() + 1000 * getCacheTime(header.getHeader("Cache-Control")));
}
service.readHeaders(header);
if (listener != null && newService)
listener.newService(service);
} catch (IOException e){
logger.log(Level.SEVERE, null, e);
} }
} }
/** private long getCacheTime(String cache_control){
* Waits for responses long ret = 0;
* String[] tmp = cache_control.split(",");
* ***** RESPONSE; for( String element : tmp ){
* HTTP/1.1 200 OK element = element.replaceAll("\\s", "").toLowerCase();
* Ext: if( element.startsWith("max-age=") ){
* Cache-Control: no-cache="Ext", max-age = 5000 ret = Long.parseLong( element.substring( "max-age=".length() ) );
* ST: ge:fridge }
* USN: uuid:abcdefgh-7dec-11d0-a765-00a0c91e6bf6 }
* Location: http://localhost:80 return ret;
*/ }
public void receivedPacket(DatagramPacket packet, ThreadedUDPNetwork network) {
String msg = new String(packet.getData(), packet.getOffset(), packet.getLength()); public interface SSDPServiceListener{
HttpHeaderParser header = new HttpHeaderParser( msg ); public void newService(StandardSSDPInfo service);
logger.log(Level.FINEST, "Received(from: "+packet.getAddress()+"): "+ header); }
}
String usn = header.getHeader("USN");
String st = header.getHeader("ST");
boolean newService = false;
StandardSSDPInfo service;
// Get existing service
if( services_usn.containsKey( usn )){
service = services_usn.get( usn );
}
// Add new service
else{
newService = true;
service = new StandardSSDPInfo();
services_usn.put( usn, service);
if( !services_st.containsKey(st) )
services_st.put( st, new LinkedList<StandardSSDPInfo>() );
services_st.get( header.getHeader("ST") ).add( service );
}
service.setLocation( header.getHeader("LOCATION") );
service.setST( st );
service.setUSN( usn );
service.setInetAddress(packet.getAddress());
if(header.getHeader("Cache-Control") != null) {
service.setExpirationTime(
System.currentTimeMillis() + 1000 * getCacheTime(header.getHeader("Cache-Control")));
}
service.readHeaders(header);
if(listener != null && newService)
listener.newService(service);
}
private long getCacheTime(String cache_control){
long ret = 0;
String[] tmp = cache_control.split(",");
for( String element : tmp ){
element = element.replaceAll("\\s", "").toLowerCase();
if( element.startsWith("max-age=") ){
ret = Long.parseLong( element.substring( "max-age=".length() ) );
}
}
return ret;
}
public interface SSDPServiceListener{
public void newService(StandardSSDPInfo service);
}
}

View file

@ -1,38 +1,38 @@
/* /*
* 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.ssdp; package zutil.net.ssdp;
import zutil.net.http.HttpHeaderParser; import zutil.net.http.HttpHeader;
import zutil.net.http.HttpPrintStream; import zutil.net.http.HttpPrintStream;
/** /**
* Created by Ziver on 2014-11-07. * Created by Ziver on 2014-11-07.
*/ */
public interface SSDPCustomInfo extends SSDPServiceInfo{ public interface SSDPCustomInfo extends SSDPServiceInfo{
public void readHeaders(HttpHeaderParser http); public void readHeaders(HttpHeader http);
public void writeHeaders(HttpPrintStream http); public void writeHeaders(HttpPrintStream http);
} }

View file

@ -1,334 +1,336 @@
/* /*
* 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.ssdp; package zutil.net.ssdp;
import zutil.StringUtil; import zutil.StringUtil;
import zutil.io.StringOutputStream; import zutil.io.StringOutputStream;
import zutil.log.LogUtil; import zutil.log.LogUtil;
import zutil.net.http.HttpHeaderParser; import zutil.net.http.HttpHeader;
import zutil.net.http.HttpPrintStream; import zutil.net.http.HttpHeaderParser;
import zutil.net.threaded.ThreadedUDPNetwork; import zutil.net.http.HttpPrintStream;
import zutil.net.threaded.ThreadedUDPNetworkThread; import zutil.net.threaded.ThreadedUDPNetwork;
import zutil.net.threaded.ThreadedUDPNetworkThread;
import java.io.IOException;
import java.net.DatagramPacket; import java.io.IOException;
import java.net.InetAddress; import java.net.DatagramPacket;
import java.util.HashMap; import java.net.InetAddress;
import java.util.Timer; import java.util.HashMap;
import java.util.TimerTask; import java.util.Timer;
import java.util.logging.Level; import java.util.TimerTask;
import java.util.logging.Logger; import java.util.logging.Level;
import java.util.logging.Logger;
/**
* A Server class that announces an service by the SSDP /**
* protocol specified at: * A Server class that announces an service by the SSDP
* http://coherence.beebits.net/chrome/site/draft-cai-ssdp-v1-03.txt * protocol specified at:
* ftp://ftp.pwg.org/pub/pwg/www/hypermail/ps/att-0188/01-psi_SSDP.pdf * http://coherence.beebits.net/chrome/site/draft-cai-ssdp-v1-03.txt
* * ftp://ftp.pwg.org/pub/pwg/www/hypermail/ps/att-0188/01-psi_SSDP.pdf
* @author Ziver *
* * @author Ziver
* ********* Message clarification: *
* ****** Incoming: * ********* Message clarification:
* ST: Search Target, this is object of the discovery request, (e.g., ssdp:all, etc.) * ****** Incoming:
* HOST: This is the SSDP multicast address * ST: Search Target, this is object of the discovery request, (e.g., ssdp:all, etc.)
* MAN: Description of packet type, (e.g., "ssdp:discover", ) * HOST: This is the SSDP multicast address
* MX: Wait these few seconds and then send response * MAN: Description of packet type, (e.g., "ssdp:discover", )
* * MX: Wait these few seconds and then send response
* ****** Outgoing: *
* EXT: required by HTTP - not used with SSDP * ****** Outgoing:
* SERVER: informational * EXT: required by HTTP - not used with SSDP
* LOCATION: This is the URL to request the QueryEndpointsInterface endpoint * SERVER: informational
* USN: advertisement UUID * LOCATION: This is the URL to request the QueryEndpointsInterface endpoint
* CACHE-CONTROL: max-age = seconds until advertisement expires * USN: advertisement UUID
* NT: Notify target same as ST * CACHE-CONTROL: max-age = seconds until advertisement expires
* NTS: same as Man but for Notify messages * NT: Notify target same as ST
*/ * NTS: same as Man but for Notify messages
public class SSDPServer extends ThreadedUDPNetwork implements ThreadedUDPNetworkThread{ */
private static final Logger logger = LogUtil.getLogger(); public class SSDPServer extends ThreadedUDPNetwork implements ThreadedUDPNetworkThread{
public static final String SERVER_INFO = "Zutil SSDP Java Server"; private static final Logger logger = LogUtil.getLogger();
public static final int DEFAULT_CACHE_TIME = 60*30; // 30 min public static final String SERVER_INFO = "Zutil SSDP Java Server";
public static final int BUFFER_SIZE = 512; public static final int DEFAULT_CACHE_TIME = 60*30; // 30 min
public static final String SSDP_MULTICAST_ADDR = "239.255.255.250"; public static final int BUFFER_SIZE = 512;
public static final int SSDP_PORT = 1900; public static final String SSDP_MULTICAST_ADDR = "239.255.255.250";
public static final int SSDP_PORT = 1900;
// instance specific values
private int cache_time; // instance specific values
private NotifyTimer notifyTimer = null; private int cache_time;
/** HashMap that contains services as < SearchTargetName, SSDPServiceInfo > */ private NotifyTimer notifyTimer = null;
private HashMap<String, SSDPServiceInfo> services; /** HashMap that contains services as < SearchTargetName, SSDPServiceInfo > */
private HashMap<String, SSDPServiceInfo> services;
public SSDPServer() throws IOException{
super( null, SSDP_MULTICAST_ADDR, SSDP_PORT ); public SSDPServer() throws IOException{
super.setThread( this ); super( null, SSDP_MULTICAST_ADDR, SSDP_PORT );
super.setThread( this );
services = new HashMap<String, SSDPServiceInfo>();
services = new HashMap<String, SSDPServiceInfo>();
setChacheTime( DEFAULT_CACHE_TIME );
enableNotify( true ); setChacheTime( DEFAULT_CACHE_TIME );
} enableNotify( true );
}
/**
* Adds an service that will be announced. /**
* * Adds an service that will be announced.
* @param service add a new service to be announced *
*/ * @param service add a new service to be announced
public void addService(SSDPServiceInfo service){ */
services.put( service.getSearchTarget(), service ); public void addService(SSDPServiceInfo service){
} services.put( service.getSearchTarget(), service );
/** }
* Remove a service from being announced. This function will /**
* send out an byebye message to the clients that the service is down. * Remove a service from being announced. This function will
* * send out an byebye message to the clients that the service is down.
* @param searchTarget is the ST value in SSDP *
*/ * @param searchTarget is the ST value in SSDP
public void removeService(String searchTarget){ */
sendByeBye( searchTarget ); public void removeService(String searchTarget){
services.remove( searchTarget ); sendByeBye( searchTarget );
} services.remove( searchTarget );
}
/**
* Sets the cache time that will be sent to /**
* the clients. If notification is enabled then an * Sets the cache time that will be sent to
* notification message will be sent every cache_time/2 seconds * the clients. If notification is enabled then an
* * notification message will be sent every cache_time/2 seconds
* @param time is the time in seconds *
*/ * @param time is the time in seconds
public void setChacheTime(int time){ */
cache_time = time; public void setChacheTime(int time){
if( isNotifyEnabled() ){ cache_time = time;
enableNotify(false); if( isNotifyEnabled() ){
enableNotify(true); enableNotify(false);
} enableNotify(true);
} }
}
/**
* Enable or disable notification messages to clients /**
* every cache_time/2 seconds * Enable or disable notification messages to clients
*/ * every cache_time/2 seconds
public void enableNotify(boolean enable){ */
if( enable && notifyTimer==null ){ public void enableNotify(boolean enable){
notifyTimer = new NotifyTimer(); if( enable && notifyTimer==null ){
Timer timer = new Timer(); notifyTimer = new NotifyTimer();
timer.schedule(new NotifyTimer(), 0, cache_time*1000/2); Timer timer = new Timer();
}else if( !enable && notifyTimer!=null ){ timer.schedule(new NotifyTimer(), 0, cache_time*1000/2);
notifyTimer.cancel(); }else if( !enable && notifyTimer!=null ){
notifyTimer = null; notifyTimer.cancel();
} notifyTimer = null;
} }
/** }
* @return if notification messages is enabled /**
*/ * @return if notification messages is enabled
public boolean isNotifyEnabled(){ */
return notifyTimer != null; public boolean isNotifyEnabled(){
} return notifyTimer != null;
}
/**
* Handles the incoming packets like this: /**
* * Handles the incoming packets like this:
* ***** REQUEST: *
* M-SEARCH * HTTP/1.1 * ***** REQUEST:
* Host: 239.255.255.250:reservedSSDPport * M-SEARCH * HTTP/1.1
* Man: "ssdp:discover" * Host: 239.255.255.250:reservedSSDPport
* ST: ge:fridge * Man: "ssdp:discover"
* MX: 3 * ST: ge:fridge
* * MX: 3
* ***** RESPONSE; *
* HTTP/1.1 200 OK * ***** RESPONSE;
* Ext: * HTTP/1.1 200 OK
* Cache-Control: no-cache="Ext", max-age = 5000 * Ext:
* ST: ge:fridge * Cache-Control: no-cache="Ext", max-age = 5000
* USN: uuid:abcdefgh-7dec-11d0-a765-00a0c91e6bf6 * ST: ge:fridge
* Location: http://localhost:80 * USN: uuid:abcdefgh-7dec-11d0-a765-00a0c91e6bf6
* * Location: http://localhost:80
*/ *
public void receivedPacket(DatagramPacket packet, ThreadedUDPNetwork network) { */
try { public void receivedPacket(DatagramPacket packet, ThreadedUDPNetwork network) {
String msg = new String( packet.getData(), packet.getOffset(), packet.getLength() ); try {
HttpHeaderParser header = new HttpHeaderParser( msg ); String msg = new String( packet.getData(), packet.getOffset(), packet.getLength() );
HttpHeaderParser headerParser = new HttpHeaderParser( msg );
// ******* Respond HttpHeader header = headerParser.read();
// Check that the message is an ssdp discovery message
if( header.getRequestType() != null && header.getRequestType().equalsIgnoreCase("M-SEARCH") ){ // ******* Respond
String man = header.getHeader("Man"); // Check that the message is an ssdp discovery message
if(man != null) if( header.getRequestType() != null && header.getRequestType().equalsIgnoreCase("M-SEARCH") ){
man = StringUtil.trim(man, '\"'); String man = header.getHeader("Man");
String st = header.getHeader("ST"); if(man != null)
// Check that its the correct URL and that its an ssdp:discover message man = StringUtil.trim(man, '\"');
if( header.getRequestURL().equals("*") && "ssdp:discover".equalsIgnoreCase(man) ){ String st = header.getHeader("ST");
// Check if the requested service exists // Check that its the correct URL and that its an ssdp:discover message
if( services.containsKey( st ) ){ if( header.getRequestURL().equals("*") && "ssdp:discover".equalsIgnoreCase(man) ){
logger.log(Level.FINEST, "Received Multicast(from: "+packet.getAddress()+"): "+ header); // Check if the requested service exists
if( services.containsKey( st ) ){
// Generate the SSDP response logger.log(Level.FINEST, "Received Multicast(from: "+packet.getAddress()+"): "+ header);
StringOutputStream response = new StringOutputStream();
HttpPrintStream http = new HttpPrintStream( response ); // Generate the SSDP response
http.setStatusCode(200); StringOutputStream response = new StringOutputStream();
http.setHeader("Location", services.get(st).getLocation() ); HttpPrintStream http = new HttpPrintStream( response );
http.setHeader("USN", services.get(st).getUSN() ); http.setStatusCode(200);
http.setHeader("Server", SERVER_INFO ); http.setHeader("Location", services.get(st).getLocation() );
http.setHeader("ST", st ); http.setHeader("USN", services.get(st).getUSN() );
http.setHeader("EXT", "" ); http.setHeader("Server", SERVER_INFO );
http.setHeader("Cache-Control", "max-age = "+ cache_time ); http.setHeader("ST", st );
if(services.get(st) instanceof SSDPCustomInfo) http.setHeader("EXT", "" );
((SSDPCustomInfo)services.get(st)).writeHeaders(http); http.setHeader("Cache-Control", "max-age = "+ cache_time );
logger.log(Level.FINEST, "Sending Response: "+ http); if(services.get(st) instanceof SSDPCustomInfo)
http.flush(); ((SSDPCustomInfo)services.get(st)).writeHeaders(http);
logger.log(Level.FINEST, "Sending Response: "+ http);
String strData = response.toString(); http.flush();
byte[] data = strData.getBytes();
packet = new DatagramPacket( String strData = response.toString();
data, data.length, byte[] data = strData.getBytes();
packet.getAddress(), packet = new DatagramPacket(
packet.getPort()); data, data.length,
network.send( packet ); packet.getAddress(),
http.close(); packet.getPort());
} network.send( packet );
} http.close();
} }
} catch (IOException e) { }
logger.log(Level.SEVERE, null, e); }
} } catch (IOException e) {
} logger.log(Level.SEVERE, null, e);
}
}
/**
* This thread is a timer task that sends an
* notification message to the network every /**
* cache_time/2 seconds. * This thread is a timer task that sends an
* * notification message to the network every
* @author Ziver * cache_time/2 seconds.
*/ *
private class NotifyTimer extends TimerTask { * @author Ziver
public void run(){ */
sendNotify(); private class NotifyTimer extends TimerTask {
} public void run(){
} sendNotify();
/** }
* Sends keep-alive messages to update the cache of the clients }
*/ /**
public void sendNotify(){ * Sends keep-alive messages to update the cache of the clients
for(String st : services.keySet()){ */
sendNotify( st ); public void sendNotify(){
} for(String st : services.keySet()){
} sendNotify( st );
/** }
* Sends an keepalive message to update the cache of the clients }
* /**
* @param searchTarget is the ST value of the service * Sends an keepalive message to update the cache of the clients
* *
* ********** Message ex: * @param searchTarget is the ST value of the service
* NOTIFY * HTTP/1.1 *
* Host: 239.255.255.250:reservedSSDPport * ********** Message ex:
* NT: blenderassociation:blender * NOTIFY * HTTP/1.1
* NTS: ssdp:alive * Host: 239.255.255.250:reservedSSDPport
* USN: someunique:idscheme3 * NT: blenderassociation:blender
* Location: http://localhost:80 * NTS: ssdp:alive
* Cache-Control: max-age = 7393 * USN: someunique:idscheme3
*/ * Location: http://localhost:80
public void sendNotify(String searchTarget){ * Cache-Control: max-age = 7393
try { */
SSDPServiceInfo service = services.get(searchTarget); public void sendNotify(String searchTarget){
// Generate the SSDP response try {
StringOutputStream msg = new StringOutputStream(); SSDPServiceInfo service = services.get(searchTarget);
HttpPrintStream http = new HttpPrintStream( msg, HttpPrintStream.HttpMessageType.REQUEST ); // Generate the SSDP response
http.setRequestType("NOTIFY"); StringOutputStream msg = new StringOutputStream();
http.setRequestURL("*"); HttpPrintStream http = new HttpPrintStream( msg, HttpPrintStream.HttpMessageType.REQUEST );
http.setHeader("Server", SERVER_INFO ); http.setRequestType("NOTIFY");
http.setHeader("Host", SSDP_MULTICAST_ADDR+":"+SSDP_PORT ); http.setRequestURL("*");
http.setHeader("NT", searchTarget ); http.setHeader("Server", SERVER_INFO );
http.setHeader("NTS", "ssdp:alive" ); http.setHeader("Host", SSDP_MULTICAST_ADDR+":"+SSDP_PORT );
http.setHeader("Location", service.getLocation() ); http.setHeader("NT", searchTarget );
http.setHeader("Cache-Control", "max-age = "+cache_time ); http.setHeader("NTS", "ssdp:alive" );
http.setHeader("USN", service.getUSN() ); http.setHeader("Location", service.getLocation() );
if(service instanceof SSDPCustomInfo) http.setHeader("Cache-Control", "max-age = "+cache_time );
((SSDPCustomInfo) service).writeHeaders(http); http.setHeader("USN", service.getUSN() );
logger.log(Level.FINEST, "Sending Notification: " + http); if(service instanceof SSDPCustomInfo)
http.flush(); ((SSDPCustomInfo) service).writeHeaders(http);
logger.log(Level.FINEST, "Sending Notification: " + http);
byte[] data = msg.toString().getBytes(); http.flush();
DatagramPacket packet = new DatagramPacket(
data, data.length, byte[] data = msg.toString().getBytes();
InetAddress.getByName( SSDP_MULTICAST_ADDR ), DatagramPacket packet = new DatagramPacket(
SSDP_PORT ); data, data.length,
super.send( packet ); InetAddress.getByName( SSDP_MULTICAST_ADDR ),
http.close(); SSDP_PORT );
super.send( packet );
} catch (Exception e) { http.close();
logger.log(Level.SEVERE, null, e);
} } catch (Exception e) {
} logger.log(Level.SEVERE, null, e);
}
}
/**
* Shutdown message is sent to the clients that all
* the service is shutting down. /**
*/ * Shutdown message is sent to the clients that all
public void sendByeBye(){ * the service is shutting down.
for(String st : services.keySet()){ */
sendByeBye( st ); public void sendByeBye(){
} for(String st : services.keySet()){
} sendByeBye( st );
/** }
* Shutdown message is sent to the clients that the service is shutting down }
* /**
* @param searchTarget is the ST value of the service * Shutdown message is sent to the clients that the service is shutting down
* *
* ********** Message ex: * @param searchTarget is the ST value of the service
* NOTIFY * HTTP/1.1 *
* Host: 239.255.255.250:reservedSSDPport * ********** Message ex:
* NT: someunique:idscheme3 * NOTIFY * HTTP/1.1
* NTS: ssdp:byebye * Host: 239.255.255.250:reservedSSDPport
* USN: someunique:idscheme3 * NT: someunique:idscheme3
*/ * NTS: ssdp:byebye
public void sendByeBye(String searchTarget){ * USN: someunique:idscheme3
try { */
// Generate the SSDP response public void sendByeBye(String searchTarget){
StringOutputStream msg = new StringOutputStream(); try {
HttpPrintStream http = new HttpPrintStream( msg, HttpPrintStream.HttpMessageType.REQUEST ); // Generate the SSDP response
http.setRequestType("NOTIFY"); StringOutputStream msg = new StringOutputStream();
http.setRequestURL("*"); HttpPrintStream http = new HttpPrintStream( msg, HttpPrintStream.HttpMessageType.REQUEST );
http.setHeader("Server", SERVER_INFO ); http.setRequestType("NOTIFY");
http.setHeader("Host", SSDP_MULTICAST_ADDR+":"+SSDP_PORT ); http.setRequestURL("*");
http.setHeader("NT", searchTarget ); http.setHeader("Server", SERVER_INFO );
http.setHeader("NTS", "ssdp:byebye" ); http.setHeader("Host", SSDP_MULTICAST_ADDR+":"+SSDP_PORT );
http.setHeader("USN", services.get(searchTarget).getUSN() ); http.setHeader("NT", searchTarget );
logger.log(Level.FINEST, "Sending ByeBye: " + http); http.setHeader("NTS", "ssdp:byebye" );
http.flush(); http.setHeader("USN", services.get(searchTarget).getUSN() );
logger.log(Level.FINEST, "Sending ByeBye: " + http);
byte[] data = msg.toString().getBytes(); http.flush();
DatagramPacket packet = new DatagramPacket(
data, data.length, byte[] data = msg.toString().getBytes();
InetAddress.getByName( SSDP_MULTICAST_ADDR ), DatagramPacket packet = new DatagramPacket(
SSDP_PORT ); data, data.length,
super.send( packet ); InetAddress.getByName( SSDP_MULTICAST_ADDR ),
http.close(); SSDP_PORT );
super.send( packet );
} catch (Exception e) { http.close();
logger.log(Level.SEVERE, null, e);
} } catch (Exception e) {
} logger.log(Level.SEVERE, null, e);
} }
}
}

View file

@ -24,8 +24,6 @@
package zutil.net.ssdp; package zutil.net.ssdp;
import java.net.InetAddress;
/** /**
* This class contains information about a service from * This class contains information about a service from
* or through the SSDP protocol * or through the SSDP protocol

View file

@ -1,161 +1,164 @@
/* /*
* 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.ssdp; package zutil.net.ssdp;
import zutil.net.http.HttpHeaderParser; import zutil.net.http.HttpHeader;
import zutil.net.http.HttpPrintStream; import zutil.net.http.HttpPrintStream;
import java.io.IOException; import java.io.IOException;
import java.net.InetAddress; import java.net.InetAddress;
import java.util.Date; import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.UUID; import java.util.Iterator;
import java.util.UUID;
/**
* This class contains information about a service from /**
* or through the SSDP protocol * This class contains information about a service from
* * or through the SSDP protocol
* @author Ziver *
*/ * @author Ziver
public class StandardSSDPInfo implements SSDPServiceInfo, SSDPCustomInfo{ */
private String location; public class StandardSSDPInfo implements SSDPServiceInfo, SSDPCustomInfo{
private String st; private String location;
private String usn; private String st;
private long expiration_time; private String usn;
// All header parameters private long expiration_time;
private HashMap<String, String> headers = new HashMap<>(); // All header parameters
private InetAddress inetAddress; private HashMap<String, String> headers = new HashMap<>();
private InetAddress inetAddress;
/**
* @param l is the value to set the Location variable /**
*/ * @param l is the value to set the Location variable
public void setLocation(String l) { */
location = l; public void setLocation(String l) {
} location = l;
}
/**
* @param st is the value to set the SearchTarget variable /**
*/ * @param st is the value to set the SearchTarget variable
public void setST(String st) { */
this.st = st; public void setST(String st) {
} this.st = st;
}
/**
* @param usn is the value to set the USN variable /**
*/ * @param usn is the value to set the USN variable
protected void setUSN(String usn) { */
this.usn = usn; protected void setUSN(String usn) {
} this.usn = usn;
}
/**
* @param time sets the expiration time of values in this object /**
*/ * @param time sets the expiration time of values in this object
protected void setExpirationTime(long time) { */
expiration_time = time; protected void setExpirationTime(long time) {
} expiration_time = time;
}
/**
* @return The URL to the Service, e.g. "http://192.168.0.1:80/index.html" /**
*/ * @return The URL to the Service, e.g. "http://192.168.0.1:80/index.html"
public String getLocation(){ */
return location; public String getLocation(){
} return location;
}
/**
* @return the Search Target, e.g. "upnp:rootdevice" /**
*/ * @return the Search Target, e.g. "upnp:rootdevice"
public String getSearchTarget(){ */
return st; public String getSearchTarget(){
} return st;
}
/**
* @return the expiration time for the values in this object /**
*/ * @return the expiration time for the values in this object
public long getExpirationTime(){ */
return expiration_time; public long getExpirationTime(){
} return expiration_time;
}
/**
* @return the USN value, e.g. "uuid:abcdefgh-7dec-11d0-a765-00a0c91e6bf6 " /**
*/ * @return the USN value, e.g. "uuid:abcdefgh-7dec-11d0-a765-00a0c91e6bf6 "
public String getUSN(){ */
if( usn==null ) public String getUSN(){
usn = genUSN(); if( usn==null )
return usn+"::"+st; usn = genUSN();
} return usn+"::"+st;
}
/**
* @return only the USN UUID String /**
*/ * @return only the USN UUID String
public String getUUID(){ */
if( usn==null ) public String getUUID(){
usn = genUSN(); if( usn==null )
return usn; usn = genUSN();
} return usn;
}
/**
* Generates an unique USN for the service /**
* * Generates an unique USN for the service
* @return an unique string that corresponds to the service *
*/ * @return an unique string that corresponds to the service
private String genUSN(){ */
return "uuid:" + UUID.nameUUIDFromBytes( (st+location+Math.random()).getBytes() ); private String genUSN(){
} return "uuid:" + UUID.nameUUIDFromBytes( (st+location+Math.random()).getBytes() );
}
public String toString(){
return "USN: "+usn+"\nLocation: "+location+"\nST: "+st+"\nExpiration-Time: "+new Date(expiration_time); public String toString(){
} return "USN: "+usn+"\nLocation: "+location+"\nST: "+st+"\nExpiration-Time: "+new Date(expiration_time);
}
public void setHeader(String key, String value) {
headers.put(key, value); public void setHeader(String key, String value) {
} headers.put(key, value);
public String getHeader(String header){ }
return headers.get(header); public String getHeader(String header){
} return headers.get(header);
@Override }
public void writeHeaders(HttpPrintStream http) { @Override
try { public void writeHeaders(HttpPrintStream http) {
for (String key : headers.keySet()) try {
http.setHeader(key, headers.get(key)); for (String key : headers.keySet())
}catch(IOException e){ http.setHeader(key, headers.get(key));
e.printStackTrace(); }catch(IOException e){
} e.printStackTrace();
} }
@Override }
public void readHeaders(HttpHeaderParser http) { @Override
HashMap<String,String> httpHeaders = http.getHeaders(); public void readHeaders(HttpHeader header) {
for (String key : httpHeaders.keySet()) Iterator<String> it = header.getHeaderKeys();
headers.put(key, httpHeaders.get(key)); while (it.hasNext()) {
} String key = it.next();
headers.put(key, header.getHeader(key));
public InetAddress getInetAddress(){ }
return inetAddress; }
}
public InetAddress getInetAddress(){
protected void setInetAddress(InetAddress inetAddress) { return inetAddress;
this.inetAddress = inetAddress; }
}
} protected void setInetAddress(InetAddress inetAddress) {
this.inetAddress = inetAddress;
}
}

232
src/zutil/net/upnp/UPnPMediaServer.java Normal file → Executable file
View file

@ -1,115 +1,117 @@
/* /*
* 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.upnp; package zutil.net.upnp;
import zutil.net.http.HttpHeaderParser; import zutil.net.http.HttpHeader;
import zutil.net.http.HttpPrintStream; import zutil.net.http.HttpPrintStream;
import java.io.IOException; import java.io.IOException;
import java.util.Map; import java.util.Map;
import java.util.UUID; import java.util.UUID;
/** /**
* This class is a UPnP AV Media Server that handles all the * This class is a UPnP AV Media Server that handles all the
* other UPnP services * other UPnP services
* *
* @author Ziver * @author Ziver
*/ */
public class UPnPMediaServer extends UPnPRootDevice{ public class UPnPMediaServer extends UPnPRootDevice{
public static final String RELATIVE_URL = "upnp/rootdev"; public static final String RELATIVE_URL = "upnp/rootdev";
private String url; private String url;
private String uuid; private String uuid;
public UPnPMediaServer(String location){ public UPnPMediaServer(String location){
url = location; url = location;
} }
public void respond(HttpPrintStream out, HttpHeaderParser clientInfo, public void respond(HttpPrintStream out,
Map<String, Object> session, Map<String, String> cookie, HttpHeader headers,
Map<String, String> request) throws IOException { Map<String, Object> session,
Map<String, String> cookie,
out.enableBuffering(true); Map<String, String> request) throws IOException {
out.setHeader("Content-Type", "text/xml");
out.enableBuffering(true);
out.println("<?xml version=\"1.0\" encoding=\"utf-8\"?>"); out.setHeader("Content-Type", "text/xml");
out.println("<root xmlns=\"urn:schemas-upnp-org:service:ContentDirectory:1\">");
out.println(" <specVersion>"); out.println("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
out.println(" <major>1</major>"); out.println("<root xmlns=\"urn:schemas-upnp-org:service:ContentDirectory:1\">");
out.println(" <minor>0</minor>"); out.println(" <specVersion>");
out.println(" </specVersion>"); out.println(" <major>1</major>");
out.println(" <URLBase>"+url+"</URLBase>");//"+ssdp.getLocation()+" out.println(" <minor>0</minor>");
out.println(" <device>"); out.println(" </specVersion>");
out.println(" <deviceType>urn:schemas-upnp-org:device:MediaServer:1</deviceType>"); out.println(" <URLBase>"+url+"</URLBase>");//"+ssdp.getLocation()+"
out.println(" <friendlyName>ZupNP AV Media Server</friendlyName>"); out.println(" <device>");
out.println(" <manufacturer>Ziver Koc</manufacturer>"); out.println(" <deviceType>urn:schemas-upnp-org:device:MediaServer:1</deviceType>");
out.println(" <manufacturerURL>http://ziver.koc.se</manufacturerURL>"); out.println(" <friendlyName>ZupNP AV Media Server</friendlyName>");
out.println(""); out.println(" <manufacturer>Ziver Koc</manufacturer>");
out.println(" <modelName>ZupNP Server</modelName>"); out.println(" <manufacturerURL>http://ziver.koc.se</manufacturerURL>");
out.println(" <modelDescription>UPnP AV Media Server</modelDescription>"); out.println("");
out.println(" <modelNumber>0.1</modelNumber>"); out.println(" <modelName>ZupNP Server</modelName>");
out.println(" <UDN>"+getUUID()+"</UDN>"); out.println(" <modelDescription>UPnP AV Media Server</modelDescription>");
out.println(" <serviceList>"); out.println(" <modelNumber>0.1</modelNumber>");
out.println(" <service>"); out.println(" <UDN>"+getUUID()+"</UDN>");
out.println(" <serviceType>urn:schemas-upnp-org:service:ConnectionManager:1</serviceType>"); out.println(" <serviceList>");
out.println(" <serviceId>urn:upnp-org:serviceId:CMGR_1-0</serviceId>"); out.println(" <service>");
out.println(" <SCPDURL>CMGR_Control/GetServDesc</SCPDURL>"); out.println(" <serviceType>urn:schemas-upnp-org:service:ConnectionManager:1</serviceType>");
out.println(" <controlURL>CMGR_Control</controlURL>"); out.println(" <serviceId>urn:upnp-org:serviceId:CMGR_1-0</serviceId>");
out.println(" <eventSubURL>CMGR_Event</eventSubURL>"); out.println(" <SCPDURL>CMGR_Control/GetServDesc</SCPDURL>");
out.println(" </service>"); out.println(" <controlURL>CMGR_Control</controlURL>");
out.println(" <service>"); out.println(" <eventSubURL>CMGR_Event</eventSubURL>");
out.println(" <serviceType>urn:schemas-upnp-org:service:ContentDirectory:1</serviceType>"); out.println(" </service>");
out.println(" <serviceId>urn:upnp-org:serviceId:CDS_1-0</serviceId>"); out.println(" <service>");
out.println(" <SCPDURL>SCP/ContentDir</SCPDURL>"); out.println(" <serviceType>urn:schemas-upnp-org:service:ContentDirectory:1</serviceType>");
out.println(" <controlURL>Action/ContentDir</controlURL>"); out.println(" <serviceId>urn:upnp-org:serviceId:CDS_1-0</serviceId>");
out.println(" <eventSubURL>Event/ContentDir</eventSubURL>"); out.println(" <SCPDURL>SCP/ContentDir</SCPDURL>");
out.println(" </service>"); out.println(" <controlURL>Action/ContentDir</controlURL>");
out.println(" </serviceList>"); out.println(" <eventSubURL>Event/ContentDir</eventSubURL>");
out.println(" </device>"); out.println(" </service>");
out.println("</root>"); out.println(" </serviceList>");
out.flush(); out.println(" </device>");
} out.println("</root>");
out.flush();
}
public long getExpirationTime() {
return 60*30; // 30min
} public long getExpirationTime() {
public String getLocation() { return 60*30; // 30min
return url+"RootDesc"; }
} public String getLocation() {
public String getSearchTarget() { return url+"RootDesc";
return "upnp:rootdevice"; }
} public String getSearchTarget() {
public String getUSN() { return "upnp:rootdevice";
return getUUID()+"::upnp:rootdevice"; }
} public String getUSN() {
public String getUUID() { return getUUID()+"::upnp:rootdevice";
if(uuid==null){ }
uuid = "uuid:"+UUID.nameUUIDFromBytes( this.getClass().toString().getBytes() ); //(url+Math.random()).getBytes() public String getUUID() {
} if(uuid==null){
return uuid; uuid = "uuid:"+UUID.nameUUIDFromBytes( this.getClass().toString().getBytes() ); //(url+Math.random()).getBytes()
} }
return uuid;
} }
}

1231
src/zutil/net/upnp/services/UPnPContentDirectory.java Normal file → Executable file

File diff suppressed because it is too large Load diff

View file

@ -25,7 +25,6 @@
package zutil.net.ws; package zutil.net.ws;
import zutil.log.LogUtil; import zutil.log.LogUtil;
import zutil.net.ws.soap.SOAPClientInvocationHandler;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationHandler;

View file

@ -1,108 +1,110 @@
/* /*
* 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.ws.rest; package zutil.net.ws.rest;
import zutil.converters.Converter; import zutil.converters.Converter;
import zutil.io.StringOutputStream; import zutil.io.StringOutputStream;
import zutil.net.http.HttpHeaderParser; import zutil.net.http.HttpHeader;
import zutil.net.http.HttpPage; import zutil.net.http.HttpPage;
import zutil.net.http.HttpPrintStream; import zutil.net.http.HttpPrintStream;
import zutil.net.ws.*; import zutil.net.ws.WSInterface;
import zutil.parser.json.JSONObjectOutputStream; import zutil.net.ws.WSMethodDef;
import zutil.net.ws.WSParameterDef;
import java.io.IOException; import zutil.net.ws.WebServiceDef;
import java.lang.reflect.Field; import zutil.parser.json.JSONObjectOutputStream;
import java.util.Map;
import java.io.IOException;
/** import java.util.Map;
* User: Ziver
*/ /**
public class RestHttpPage implements HttpPage { * User: Ziver
*/
/** The object that the functions will be invoked from **/ public class RestHttpPage implements HttpPage {
private WebServiceDef wsDef;
/** This instance of the web service class is used if session is disabled **/ /** The object that the functions will be invoked from **/
private WSInterface ws; private WebServiceDef wsDef;
/** This instance of the web service class is used if session is disabled **/
private WSInterface ws;
public RestHttpPage( WSInterface wsObject ){
this.ws = wsObject;
this.wsDef = new WebServiceDef(ws.getClass()); public RestHttpPage( WSInterface wsObject ){
} this.ws = wsObject;
this.wsDef = new WebServiceDef(ws.getClass());
}
@Override
public void respond(HttpPrintStream out,
HttpHeaderParser client_info, @Override
Map<String, Object> session, public void respond(HttpPrintStream out,
Map<String, String> cookie, HttpHeader headers,
Map<String, String> request) throws IOException { Map<String, Object> session,
try { Map<String, String> cookie,
out.println( Map<String, String> request) throws IOException {
execute(client_info.getRequestURL(), request)); try {
} catch (Throwable throwable) { out.println(
throw new IOException(throwable); execute(headers.getRequestURL(), request));
} } catch (Throwable throwable) {
} throw new IOException(throwable);
}
}
private String execute(String targetMethod, Map<String, String> input) throws Throwable {
if( wsDef.hasMethod(targetMethod) ){
// Parse request private String execute(String targetMethod, Map<String, String> input) throws Throwable {
WSMethodDef m = wsDef.getMethod(targetMethod); if( wsDef.hasMethod(targetMethod) ){
Object[] params = prepareInputParams(m, input); // Parse request
WSMethodDef m = wsDef.getMethod(targetMethod);
// Invoke Object[] params = prepareInputParams(m, input);
Object ret = m.invoke(ws, params);
// Invoke
// Generate Response Object ret = m.invoke(ws, params);
StringOutputStream dummyOut = new StringOutputStream();
JSONObjectOutputStream out = new JSONObjectOutputStream(dummyOut); // Generate Response
out.writeObject(ret); StringOutputStream dummyOut = new StringOutputStream();
out.close(); JSONObjectOutputStream out = new JSONObjectOutputStream(dummyOut);
return dummyOut.toString(); out.writeObject(ret);
} out.close();
return "{error: \"Unknown target: "+targetMethod+"\"}"; return dummyOut.toString();
} }
return "{error: \"Unknown target: "+targetMethod+"\"}";
private Object[] prepareInputParams(WSMethodDef method, Map<String, String> input){ }
Object[] inputParams = new Object[method.getInputCount()];
private Object[] prepareInputParams(WSMethodDef method, Map<String, String> input){
// Get the parameter values Object[] inputParams = new Object[method.getInputCount()];
for(int i=0; i<method.getInputCount() ;i++){
WSParameterDef param = method.getInput( i ); // Get the parameter values
if( input.containsKey(param.getName()) ){ for(int i=0; i<method.getInputCount() ;i++){
inputParams[i] = Converter.fromString( WSParameterDef param = method.getInput( i );
input.get(param.getName()), if( input.containsKey(param.getName()) ){
param.getParamClass()); inputParams[i] = Converter.fromString(
} input.get(param.getName()),
} param.getParamClass());
return inputParams; }
} }
return inputParams;
private void generateResponse(){ }
} private void generateResponse(){
}
}
}

View file

@ -29,11 +29,7 @@ import zutil.net.ws.WSClientFactory;
import zutil.net.ws.WSInterface; import zutil.net.ws.WSInterface;
import zutil.net.ws.WebServiceDef; import zutil.net.ws.WebServiceDef;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.net.URL; import java.net.URL;
import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
/** /**

View file

@ -1,372 +1,372 @@
/* /*
* 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.ws.soap; package zutil.net.ws.soap;
import org.dom4j.Document; import org.dom4j.Document;
import org.dom4j.DocumentException; import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper; import org.dom4j.DocumentHelper;
import org.dom4j.Element; import org.dom4j.Element;
import org.dom4j.io.OutputFormat; import org.dom4j.io.OutputFormat;
import org.dom4j.io.XMLWriter; import org.dom4j.io.XMLWriter;
import org.xml.sax.SAXException; import org.xml.sax.SAXException;
import zutil.converters.Converter; import zutil.converters.Converter;
import zutil.log.LogUtil; import zutil.log.LogUtil;
import zutil.net.http.HttpHeaderParser; import zutil.net.http.HttpHeader;
import zutil.net.http.HttpPage; import zutil.net.http.HttpPage;
import zutil.net.http.HttpPrintStream; import zutil.net.http.HttpPrintStream;
import zutil.net.ws.*; import zutil.net.ws.*;
import zutil.net.ws.WSReturnObject.WSValueName; import zutil.net.ws.WSReturnObject.WSValueName;
import java.lang.reflect.Array; import java.lang.reflect.Array;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.util.Iterator; import java.util.Iterator;
import java.util.Map; import java.util.Map;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
/** /**
* This is an HTTPPage for the HTTPServer that * This is an HTTPPage for the HTTPServer that
* handles soap messages. * handles soap messages.
* *
* TODO: Header should be variables not methods * TODO: Header should be variables not methods
* TODO: Read WSReturnObjects as input parameter * TODO: Read WSReturnObjects as input parameter
* TODO: Ability to have multiple arrays of same WSReturnObject * TODO: Ability to have multiple arrays of same WSReturnObject
* *
* Features: * Features:
* Input: * Input:
* <br>-int * <br>-int
* <br>-double * <br>-double
* <br>-float * <br>-float
* <br>-char * <br>-char
* <br>-String * <br>-String
* <br>-byte[] * <br>-byte[]
* <br>-And the Wrappers Classes except for Byte * <br>-And the Wrappers Classes except for Byte
* *
* Output: * Output:
* <br>-WSReturnObject * <br>-WSReturnObject
* <br>-byte[] * <br>-byte[]
* <br>-int * <br>-int
* <br>-double * <br>-double
* <br>-float * <br>-float
* <br>-char * <br>-char
* <br>-String * <br>-String
* <br>-Arrays of Output * <br>-Arrays of Output
* <br>-And the Wrappers Classes except for Byte * <br>-And the Wrappers Classes except for Byte
* *
* @author Ziver * @author Ziver
*/ */
public class SOAPHttpPage implements HttpPage{ public class SOAPHttpPage implements HttpPage{
private static final Logger logger = LogUtil.getLogger(); private static final Logger logger = LogUtil.getLogger();
/** The object that the functions will be invoked from **/ /** The object that the functions will be invoked from **/
private WebServiceDef wsDef; private WebServiceDef wsDef;
/** This instance of the web service class is used if session is disabled **/ /** This instance of the web service class is used if session is disabled **/
private WSInterface ws; private WSInterface ws;
/** Session enabled **/ /** Session enabled **/
private boolean session_enabled; private boolean session_enabled;
public SOAPHttpPage( WebServiceDef wsDef ){ public SOAPHttpPage( WebServiceDef wsDef ){
this.wsDef = wsDef; this.wsDef = wsDef;
this.session_enabled = false; this.session_enabled = false;
} }
/** /**
* Enables session support, if enabled then a new instance * Enables session support, if enabled then a new instance
* of the SOAPInterface will be created, if disabled then * of the SOAPInterface will be created, if disabled then
* only the given object will be used as an static interface * only the given object will be used as an static interface
* *
* @param enabled is if session should be enabled * @param enabled is if session should be enabled
*/ */
public void enableSession(boolean enabled){ public void enableSession(boolean enabled){
this.session_enabled = enabled; this.session_enabled = enabled;
} }
/** /**
* Sets the web service object to the specified one. * Sets the web service object to the specified one.
* Only used when session is disabled * Only used when session is disabled
*/ */
public void setObject(WSInterface obj) { public void setObject(WSInterface obj) {
this.ws = obj; this.ws = obj;
} }
public void respond(HttpPrintStream out, public void respond(HttpPrintStream out,
HttpHeaderParser client_info, HttpHeader headers,
Map<String, Object> session, Map<String, Object> session,
Map<String, String> cookie, Map<String, String> cookie,
Map<String, String> request) { Map<String, String> request) {
try { try {
out.setHeader("Content-Type", "text/xml"); out.setHeader("Content-Type", "text/xml");
out.flush(); out.flush();
WSInterface obj = null; WSInterface obj = null;
if(session_enabled){ if(session_enabled){
if( session.containsKey("SOAPInterface")) if( session.containsKey("SOAPInterface"))
obj = (WSInterface)session.get("SOAPInterface"); obj = (WSInterface)session.get("SOAPInterface");
else{ else{
obj = wsDef.newInstance(); obj = wsDef.newInstance();
session.put("SOAPInterface", obj); session.put("SOAPInterface", obj);
} }
} }
else{ else{
if( ws == null ) if( ws == null )
ws = wsDef.newInstance(); ws = wsDef.newInstance();
obj = ws; obj = ws;
} }
Document document = genSOAPResponse( request.get(""), obj); Document document = genSOAPResponse( request.get(""), obj);
OutputFormat format = OutputFormat.createPrettyPrint(); OutputFormat format = OutputFormat.createPrettyPrint();
XMLWriter writer = new XMLWriter( out, format ); XMLWriter writer = new XMLWriter( out, format );
writer.write( document ); writer.write( document );
// DEBUG // DEBUG
if( logger.isLoggable(Level.FINEST) ){ if( logger.isLoggable(Level.FINEST) ){
System.out.println("********** Request"); System.out.println("********** Request");
System.out.println(request); System.out.println(request);
System.out.println("********** Response"); System.out.println("********** Response");
writer = new XMLWriter( System.out, format ); writer = new XMLWriter( System.out, format );
writer.write( document ); writer.write( document );
} }
} catch (Exception e) { } catch (Exception e) {
logger.log(Level.WARNING, "Unhandled request", e); logger.log(Level.WARNING, "Unhandled request", e);
} }
} }
/** /**
* Generates a soap response for the given XML * Generates a soap response for the given XML
* *
* @param xml is the XML request * @param xml is the XML request
* @return a Document with the response * @return a Document with the response
*/ */
public Document genSOAPResponse(String xml){ public Document genSOAPResponse(String xml){
try { try {
WSInterface obj = null; WSInterface obj = null;
if( ws == null ) if( ws == null )
ws = wsDef.newInstance(); ws = wsDef.newInstance();
obj = ws; obj = ws;
return genSOAPResponse(xml, obj ); return genSOAPResponse(xml, obj );
} catch (Exception e) { } catch (Exception e) {
logger.log(Level.WARNING, "Exception in SOAP generation", e); logger.log(Level.WARNING, "Exception in SOAP generation", e);
} }
return null; return null;
} }
protected Document genSOAPResponse(String xml, WSInterface obj){ protected Document genSOAPResponse(String xml, WSInterface obj){
Document document = DocumentHelper.createDocument(); Document document = DocumentHelper.createDocument();
Element envelope = document.addElement("soap:Envelope"); Element envelope = document.addElement("soap:Envelope");
try { try {
envelope.addNamespace("soap", "http://schemas.xmlsoap.org/soap/envelope/"); envelope.addNamespace("soap", "http://schemas.xmlsoap.org/soap/envelope/");
envelope.addNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance"); envelope.addNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance");
envelope.addNamespace("xsd", "http://www.w3.org/2001/XMLSchema"); envelope.addNamespace("xsd", "http://www.w3.org/2001/XMLSchema");
Element body = envelope.addElement( "soap:Body" ); Element body = envelope.addElement( "soap:Body" );
try{ try{
Element request = getXMLRoot(xml); Element request = getXMLRoot(xml);
if(request == null) return document; if(request == null) return document;
// Header // Header
if( request.element("Header") != null){ if( request.element("Header") != null){
Element header = envelope.addElement( "soap:Header" ); Element header = envelope.addElement( "soap:Header" );
prepareInvoke( obj, request.element("Header"), header ); prepareInvoke( obj, request.element("Header"), header );
} }
// Body // Body
if( request.element("Body") != null){ if( request.element("Body") != null){
prepareInvoke( obj, request.element("Body"), body ); prepareInvoke( obj, request.element("Body"), body );
} }
}catch(Throwable e){ }catch(Throwable e){
body.clearContent(); body.clearContent();
Element fault = body.addElement("soap:Fault"); Element fault = body.addElement("soap:Fault");
// The fault source // The fault source
if(e instanceof SOAPException || e instanceof SAXException || e instanceof DocumentException) if(e instanceof SOAPException || e instanceof SAXException || e instanceof DocumentException)
fault.addElement("faultcode").setText( "soap:Client" ); fault.addElement("faultcode").setText( "soap:Client" );
else else
fault.addElement("faultcode").setText( "soap:Server" ); fault.addElement("faultcode").setText( "soap:Server" );
// The fault message // The fault message
if( e.getMessage() == null || e.getMessage().isEmpty()) if( e.getMessage() == null || e.getMessage().isEmpty())
fault.addElement("faultstring").setText( ""+e.getClass().getSimpleName() ); fault.addElement("faultstring").setText( ""+e.getClass().getSimpleName() );
else else
fault.addElement("faultstring").setText( ""+e.getMessage() ); fault.addElement("faultstring").setText( ""+e.getMessage() );
logger.log(Level.WARNING, "Caught exception from SOAP Class", e); logger.log(Level.WARNING, "Caught exception from SOAP Class", e);
} }
} catch (Exception e) { } catch (Exception e) {
logger.log(Level.WARNING, "Exception in SOAP generation", e); logger.log(Level.WARNING, "Exception in SOAP generation", e);
} }
return document; return document;
} }
/** /**
* Converts an String XML to an Element * Converts an String XML to an Element
* *
* @param xml is the string XML * @param xml is the string XML
* @return the XML root Element * @return the XML root Element
*/ */
protected static Element getXMLRoot(String xml) throws DocumentException { protected static Element getXMLRoot(String xml) throws DocumentException {
if(xml != null && !xml.isEmpty()){ if(xml != null && !xml.isEmpty()){
Document document = DocumentHelper.parseText(xml); Document document = DocumentHelper.parseText(xml);
return document.getRootElement(); return document.getRootElement();
} }
return null; return null;
} }
/** /**
* Takes an XML Element and invokes all the it's child elements as methods. * Takes an XML Element and invokes all the it's child elements as methods.
* *
* @param obj is the object that the methods will be called from * @param obj is the object that the methods will be called from
* @param requestRoot is the Element where the children lies * @param requestRoot is the Element where the children lies
* @param responseRoot is the root element of the response * @param responseRoot is the root element of the response
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private void prepareInvoke(WSInterface obj, Element requestRoot, Element responseRoot) throws Throwable{ private void prepareInvoke(WSInterface obj, Element requestRoot, Element responseRoot) throws Throwable{
Iterator<Element> it = requestRoot.elementIterator(); Iterator<Element> it = requestRoot.elementIterator();
while( it.hasNext() ){ while( it.hasNext() ){
Element e = it.next(); Element e = it.next();
if( wsDef.hasMethod( e.getQName().getName()) ){ if( wsDef.hasMethod( e.getQName().getName()) ){
WSMethodDef m = wsDef.getMethod( e.getQName().getName() ); WSMethodDef m = wsDef.getMethod( e.getQName().getName() );
Object[] params = new Object[ m.getInputCount() ]; Object[] params = new Object[ m.getInputCount() ];
// Get the parameter values // Get the parameter values
for(int i=0; i<m.getInputCount() ;i++){ for(int i=0; i<m.getInputCount() ;i++){
WSParameterDef param = m.getInput( i ); WSParameterDef param = m.getInput( i );
if( e.element(param.getName()) != null ){ if( e.element(param.getName()) != null ){
params[i] = Converter.fromString( params[i] = Converter.fromString(
e.element(param.getName()).getTextTrim(), e.element(param.getName()).getTextTrim(),
param.getParamClass()); param.getParamClass());
} }
} }
// Invoke // Invoke
Object ret = m.invoke(obj, params); Object ret = m.invoke(obj, params);
// generate response XML // generate response XML
if( m.getOutputCount()>0 ){ if( m.getOutputCount()>0 ){
Element response = responseRoot.addElement(""); Element response = responseRoot.addElement("");
response.addNamespace("m", m.getNamespace() ); response.addNamespace("m", m.getNamespace() );
response.setName("m:"+m.getName()+"Response"); response.setName("m:"+m.getName()+"Response");
if( ret instanceof WSReturnObject ){ if( ret instanceof WSReturnObject ){
Field[] f = ret.getClass().getFields(); Field[] f = ret.getClass().getFields();
for(int i=0; i<m.getOutputCount() ;i++){ for(int i=0; i<m.getOutputCount() ;i++){
WSParameterDef param = m.getOutput( i ); WSParameterDef param = m.getOutput( i );
generateSOAPXMLForObj(response,((WSReturnObject)ret).getValue(f[i]) , param.getName()); generateSOAPXMLForObj(response,((WSReturnObject)ret).getValue(f[i]) , param.getName());
} }
} }
else{ else{
generateSOAPXMLForObj(response, ret, m.getOutput(0).getName()); generateSOAPXMLForObj(response, ret, m.getOutput(0).getName());
} }
} }
} }
else{ else{
throw new NoSuchMethodException("Unable to find method: "+e.getQName().getName()+"!"); throw new NoSuchMethodException("Unable to find method: "+e.getQName().getName()+"!");
} }
} }
} }
/** /**
* Generates a XML Element for a given Object. This method can * Generates a XML Element for a given Object. This method can
* handle return values as XML Elements, WSReturnObject and * handle return values as XML Elements, WSReturnObject and
* Java basic data types. * Java basic data types.
* *
* @param root is the parent Element * @param root is the parent Element
* @param obj is the object that is the return value * @param obj is the object that is the return value
* @param elementName is the name of the parent Element * @param elementName is the name of the parent Element
*/ */
protected static void generateSOAPXMLForObj(Element root, Object obj, String elementName) throws IllegalArgumentException, IllegalAccessException{ protected static void generateSOAPXMLForObj(Element root, Object obj, String elementName) throws IllegalArgumentException, IllegalAccessException{
if(obj == null) return; if(obj == null) return;
if(byte[].class.isAssignableFrom(obj.getClass())){ if(byte[].class.isAssignableFrom(obj.getClass())){
Element valueE = root.addElement( elementName ); Element valueE = root.addElement( elementName );
valueE.addAttribute("type", "xsd:"+ getSOAPClassName(obj.getClass())); valueE.addAttribute("type", "xsd:"+ getSOAPClassName(obj.getClass()));
String tmp = new sun.misc.BASE64Encoder().encode((byte[])obj); String tmp = new sun.misc.BASE64Encoder().encode((byte[])obj);
tmp = tmp.replaceAll("\\s", ""); tmp = tmp.replaceAll("\\s", "");
valueE.setText(tmp); valueE.setText(tmp);
} }
// return an array // return an array
else if(obj.getClass().isArray()){ else if(obj.getClass().isArray()){
Element array = root.addElement( (elementName.equals("element") ? "Array" : elementName) ); Element array = root.addElement( (elementName.equals("element") ? "Array" : elementName) );
String arrayType = "xsd:"+ getSOAPClassName(obj.getClass()); String arrayType = "xsd:"+ getSOAPClassName(obj.getClass());
arrayType = arrayType.replaceFirst("\\[\\]", "["+Array.getLength(obj)+"]"); arrayType = arrayType.replaceFirst("\\[\\]", "["+Array.getLength(obj)+"]");
array.addAttribute("type", "soap:Array"); array.addAttribute("type", "soap:Array");
array.addAttribute("soap:arrayType", arrayType); array.addAttribute("soap:arrayType", arrayType);
for(int i=0; i<Array.getLength(obj) ;i++){ for(int i=0; i<Array.getLength(obj) ;i++){
generateSOAPXMLForObj(array, Array.get(obj, i), "element"); generateSOAPXMLForObj(array, Array.get(obj, i), "element");
} }
} }
else{ else{
Element objectE = root.addElement( elementName ); Element objectE = root.addElement( elementName );
if(obj instanceof Element) if(obj instanceof Element)
objectE.add( (Element)obj ); objectE.add( (Element)obj );
else if(obj instanceof WSReturnObject){ else if(obj instanceof WSReturnObject){
Field[] fields = obj.getClass().getFields(); Field[] fields = obj.getClass().getFields();
for(int i=0; i<fields.length ;i++){ for(int i=0; i<fields.length ;i++){
WSValueName tmp = fields[i].getAnnotation( WSValueName.class ); WSValueName tmp = fields[i].getAnnotation( WSValueName.class );
String name; String name;
if(tmp != null) name = tmp.value(); if(tmp != null) name = tmp.value();
else name = "field"+i; else name = "field"+i;
generateSOAPXMLForObj(objectE, fields[i].get(obj), name); generateSOAPXMLForObj(objectE, fields[i].get(obj), name);
} }
} }
else { else {
objectE.addAttribute("type", "xsd:"+ getSOAPClassName(obj.getClass())); objectE.addAttribute("type", "xsd:"+ getSOAPClassName(obj.getClass()));
objectE.addText( ""+obj ); objectE.addText( ""+obj );
} }
} }
} }
protected static String getSOAPClassName(Class<?> c){ protected static String getSOAPClassName(Class<?> c){
Class<?> cTmp = getClass(c); Class<?> cTmp = getClass(c);
if(byte[].class.isAssignableFrom(c)){ if(byte[].class.isAssignableFrom(c)){
return "base64Binary"; return "base64Binary";
} }
else if( WSReturnObject.class.isAssignableFrom(cTmp) ){ else if( WSReturnObject.class.isAssignableFrom(cTmp) ){
return c.getSimpleName(); return c.getSimpleName();
} }
else{ else{
String ret = c.getSimpleName().toLowerCase(); String ret = c.getSimpleName().toLowerCase();
if(cTmp == Integer.class) ret = ret.replaceAll("integer", "int"); if(cTmp == Integer.class) ret = ret.replaceAll("integer", "int");
else if(cTmp == Character.class)ret = ret.replaceAll("character", "char"); else if(cTmp == Character.class)ret = ret.replaceAll("character", "char");
return ret; return ret;
} }
} }
protected static Class<?> getClass(Class<?> c){ protected static Class<?> getClass(Class<?> c){
if(c!=null && c.isArray()){ if(c!=null && c.isArray()){
return getClass(c.getComponentType()); return getClass(c.getComponentType());
} }
return c; return c;
} }
} }

2
src/zutil/osal/app/linux/ProcStat.java Normal file → Executable file
View file

@ -24,8 +24,8 @@
package zutil.osal.app.linux; package zutil.osal.app.linux;
import zutil.log.LogUtil;
import zutil.Timer; import zutil.Timer;
import zutil.log.LogUtil;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.FileReader; import java.io.FileReader;

2
src/zutil/osal/app/windows/TypePerf.java Normal file → Executable file
View file

@ -30,8 +30,6 @@ import zutil.parser.DataNode;
import java.io.IOException; import java.io.IOException;
import java.io.StringReader; import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;
/** /**
* Created by ezivkoc on 2015-07-30. * Created by ezivkoc on 2015-07-30.

2
src/zutil/parser/CSVParser.java Normal file → Executable file
View file

@ -24,8 +24,6 @@
package zutil.parser; package zutil.parser;
import zutil.struct.MutableInt;
import java.io.IOException; import java.io.IOException;
import java.io.Reader; import java.io.Reader;
import java.io.StringReader; import java.io.StringReader;

3
src/zutil/parser/Parser.java Normal file → Executable file
View file

@ -24,10 +24,7 @@
package zutil.parser; package zutil.parser;
import zutil.struct.MutableInt;
import java.io.IOException; import java.io.IOException;
import java.io.StringReader;
/** /**
* Created by Ziver * Created by Ziver

View file

@ -24,15 +24,15 @@
package zutil.parser.binary; package zutil.parser.binary;
import zutil.ByteUtil;
import zutil.converters.Converter;
import zutil.parser.binary.BinaryStruct.BinaryField;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import zutil.ByteUtil;
import zutil.converters.Converter;
import zutil.parser.binary.BinaryStruct.*;
/** /**
* Created by Ziver on 2016-01-28. * Created by Ziver on 2016-01-28.
*/ */

View file

@ -32,7 +32,9 @@ import zutil.parser.DataNode;
import javax.activation.UnsupportedDataTypeException; import javax.activation.UnsupportedDataTypeException;
import java.io.*; import java.io.*;
import java.lang.reflect.*; import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;

View file

@ -29,7 +29,6 @@ import zutil.ClassUtil;
import zutil.parser.Base64Encoder; import zutil.parser.Base64Encoder;
import zutil.parser.DataNode; import zutil.parser.DataNode;
import zutil.parser.DataNode.DataType; import zutil.parser.DataNode.DataType;
import static zutil.parser.json.JSONObjectInputStream.*;
import javax.activation.UnsupportedDataTypeException; import javax.activation.UnsupportedDataTypeException;
import java.io.*; import java.io.*;
@ -40,6 +39,9 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import static zutil.parser.json.JSONObjectInputStream.MD_CLASS;
import static zutil.parser.json.JSONObjectInputStream.MD_OBJECT_ID;
public class JSONObjectOutputStream extends OutputStream implements ObjectOutput, Closeable{ public class JSONObjectOutputStream extends OutputStream implements ObjectOutput, Closeable{
/** If the generated JSON should contain class def meta-data **/ /** If the generated JSON should contain class def meta-data **/
private boolean generateMetaData = true; private boolean generateMetaData = true;

1
src/zutil/parser/json/JSONWriter.java Normal file → Executable file
View file

@ -29,7 +29,6 @@ import zutil.parser.DataNode;
import zutil.parser.DataNode.DataType; import zutil.parser.DataNode.DataType;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.PrintStream;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.io.Writer; import java.io.Writer;
import java.util.Iterator; import java.util.Iterator;

108
src/zutil/parser/wsdl/WSDLHttpPage.java Normal file → Executable file
View file

@ -1,54 +1,54 @@
/* /*
* 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.parser.wsdl; package zutil.parser.wsdl;
import zutil.net.http.HttpHeaderParser; import zutil.net.http.HttpHeader;
import zutil.net.http.HttpPage; import zutil.net.http.HttpPage;
import zutil.net.http.HttpPrintStream; import zutil.net.http.HttpPrintStream;
import zutil.net.ws.WebServiceDef; import zutil.net.ws.WebServiceDef;
import java.io.IOException; import java.io.IOException;
import java.util.Map; import java.util.Map;
/** /**
* User: Ziver * User: Ziver
*/ */
public class WSDLHttpPage implements HttpPage { public class WSDLHttpPage implements HttpPage {
/** The WSDL document **/ /** The WSDL document **/
private WSDLWriter wsdl; private WSDLWriter wsdl;
public WSDLHttpPage( WebServiceDef wsDef ){ public WSDLHttpPage( WebServiceDef wsDef ){
wsdl = new WSDLWriter( wsDef ); wsdl = new WSDLWriter( wsDef );
} }
public void respond(HttpPrintStream out, public void respond(HttpPrintStream out,
HttpHeaderParser client_info, HttpHeader headers,
Map<String, Object> session, Map<String, Object> session,
Map<String, String> cookie, Map<String, String> cookie,
Map<String, String> request) throws IOException{ Map<String, String> request) throws IOException{
out.setHeader("Content-Type", "text/xml"); out.setHeader("Content-Type", "text/xml");
wsdl.write( out ); wsdl.write( out );
} }
} }

View file

@ -24,10 +24,7 @@
package zutil.struct; package zutil.struct;
import java.util.AbstractSet;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
/** /**
* This is a timed HashSet. Each entry has a limited time to live. * This is a timed HashSet. Each entry has a limited time to live.

View file

@ -28,7 +28,8 @@ import org.junit.Test;
import zutil.parser.binary.BinaryStruct; import zutil.parser.binary.BinaryStruct;
import zutil.parser.binary.BinaryStructParser; import zutil.parser.binary.BinaryStructParser;
import static junit.framework.TestCase.*; import static junit.framework.TestCase.assertEquals;
import static junit.framework.TestCase.assertFalse;
/** /**
* Created by Ziver on 2016-01-28. * Created by Ziver on 2016-01-28.

View file

@ -26,7 +26,7 @@ package zutil.test;
import org.junit.Test; import org.junit.Test;
import zutil.Encrypter; import zutil.Encrypter;
import zutil.Encrypter.*; import zutil.Encrypter.Algorithm;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;

View file

@ -24,7 +24,7 @@
package zutil.test; package zutil.test;
import zutil.net.http.HttpHeaderParser; import zutil.net.http.HttpHeader;
import zutil.net.http.HttpPage; import zutil.net.http.HttpPage;
import zutil.net.http.HttpPrintStream; import zutil.net.http.HttpPrintStream;
import zutil.net.http.HttpServer; import zutil.net.http.HttpServer;
@ -43,7 +43,7 @@ public class HTTPGuessTheNumber implements HttpPage{
} }
public void respond(HttpPrintStream out, public void respond(HttpPrintStream out,
HttpHeaderParser client_info, HttpHeader client_info,
Map<String, Object> session, Map<String, Object> session,
Map<String, String> cookie, Map<String, String> cookie,
Map<String, String> request) throws IOException { Map<String, String> request) throws IOException {

View file

@ -24,7 +24,7 @@
package zutil.test; package zutil.test;
import zutil.net.http.HttpHeaderParser; import zutil.net.http.HttpHeader;
import zutil.net.http.HttpPage; import zutil.net.http.HttpPage;
import zutil.net.http.HttpPrintStream; import zutil.net.http.HttpPrintStream;
import zutil.net.http.HttpServer; import zutil.net.http.HttpServer;
@ -42,7 +42,7 @@ public class HTTPUploaderTest implements HttpPage{
} }
public void respond(HttpPrintStream out, public void respond(HttpPrintStream out,
HttpHeaderParser client_info, HttpHeader client_info,
Map<String, Object> session, Map<String, Object> session,
Map<String, String> cookie, Map<String, String> cookie,
Map<String, String> request) throws IOException { Map<String, String> request) throws IOException {

View file

@ -24,19 +24,19 @@
package zutil.test; package zutil.test;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import java.util.Arrays;
import java.util.Collection;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.junit.runners.Parameterized; import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters; import org.junit.runners.Parameterized.Parameters;
import zutil.converters.NumberToWordsConverter; import zutil.converters.NumberToWordsConverter;
import java.util.Arrays;
import java.util.Collection;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
@RunWith(Parameterized.class) @RunWith(Parameterized.class)
public class NumberToWordsConverterTest { public class NumberToWordsConverterTest {

View file

@ -27,7 +27,6 @@ package zutil.test;
import org.dom4j.Document; import org.dom4j.Document;
import org.dom4j.io.OutputFormat; import org.dom4j.io.OutputFormat;
import org.dom4j.io.XMLWriter; import org.dom4j.io.XMLWriter;
import zutil.net.nio.message.type.SystemMessage;
import zutil.net.ws.WSInterface; import zutil.net.ws.WSInterface;
import zutil.net.ws.WSInterface.WSNamespace; import zutil.net.ws.WSInterface.WSNamespace;
import zutil.net.ws.WSReturnObject; import zutil.net.ws.WSReturnObject;

View file

@ -29,7 +29,6 @@ import zutil.net.ssdp.SSDPServer;
import zutil.net.ssdp.StandardSSDPInfo; import zutil.net.ssdp.StandardSSDPInfo;
import java.io.IOException; import java.io.IOException;
import java.util.HashMap;
import java.util.logging.Level; import java.util.logging.Level;
/** /**

View file

@ -28,7 +28,6 @@ import org.junit.Test;
import zutil.StringUtil; import zutil.StringUtil;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;

View file

@ -27,7 +27,7 @@ package zutil.test;
import org.junit.Test; import org.junit.Test;
import zutil.struct.TimedHashSet; import zutil.struct.TimedHashSet;
import static org.junit.Assert.*; import static org.junit.Assert.assertFalse;
/** /**
* Created by Ziver on 2015-11-20. * Created by Ziver on 2015-11-20.