From 862bc7763e98919f16f68c2556aabc275bb6d75b Mon Sep 17 00:00:00 2001 From: Ziver Koc Date: Fri, 19 Feb 2016 20:28:26 +0100 Subject: [PATCH] Big HTTP header parsing refactoring --- src/zutil/ClassUtil.java | 4 - src/zutil/StringUtil.java | 1 - src/zutil/converters/Converter.java | 31 + src/zutil/db/DBConnection.java | 1 - src/zutil/db/DBUpgradeHandler.java | 2 - src/zutil/db/bean/DBBeanConfig.java | 1 - src/zutil/db/handler/ListSQLResult.java | 1 - src/zutil/io/file/FileUtil.java | 2 - src/zutil/log/InputStreamLogger.java | 1 - src/zutil/log/LogUtil.java | 1 - src/zutil/log/OutputStreamLogger.java | 1 - src/zutil/log/StreamLogger.java | 7 - src/zutil/net/dns/DNSPacket.java | 1 - src/zutil/net/http/HttpHeader.java | 176 +++ src/zutil/net/http/HttpHeaderParser.java | 455 +++--- src/zutil/net/http/HttpPage.java | 105 +- src/zutil/net/http/HttpPrintStream.java | 24 +- src/zutil/net/http/HttpServer.java | 594 ++++---- .../net/http/multipart/MultipartParser.java | 290 ++-- src/zutil/net/http/pages/HttpFilePage.java | 328 ++--- src/zutil/net/ssdp/SSDPClient.java | 488 +++---- src/zutil/net/ssdp/SSDPCustomInfo.java | 76 +- src/zutil/net/ssdp/SSDPServer.java | 670 ++++----- src/zutil/net/ssdp/SSDPServiceInfo.java | 2 - src/zutil/net/ssdp/StandardSSDPInfo.java | 325 ++--- src/zutil/net/upnp/UPnPMediaServer.java | 232 ++-- .../upnp/services/UPnPContentDirectory.java | 1231 +++++++++-------- src/zutil/net/ws/WSClientFactory.java | 1 - src/zutil/net/ws/rest/RestHttpPage.java | 218 +-- src/zutil/net/ws/soap/SOAPClientFactory.java | 4 - src/zutil/net/ws/soap/SOAPHttpPage.java | 744 +++++----- src/zutil/osal/app/linux/ProcStat.java | 2 +- src/zutil/osal/app/windows/TypePerf.java | 2 - src/zutil/parser/CSVParser.java | 2 - src/zutil/parser/Parser.java | 3 - .../parser/binary/BinaryStructParser.java | 8 +- .../parser/json/JSONObjectInputStream.java | 4 +- .../parser/json/JSONObjectOutputStream.java | 4 +- src/zutil/parser/json/JSONWriter.java | 1 - src/zutil/parser/wsdl/WSDLHttpPage.java | 108 +- src/zutil/struct/TimedHashSet.java | 3 - test/zutil/test/BinaryStructTest.java | 3 +- test/zutil/test/EncrypterTest.java | 2 +- test/zutil/test/HTTPGuessTheNumber.java | 4 +- test/zutil/test/HTTPUploaderTest.java | 4 +- .../test/NumberToWordsConverterTest.java | 14 +- test/zutil/test/SOAPTest.java | 1 - test/zutil/test/SSDPServerTest.java | 1 - test/zutil/test/StringUtilTest.java | 1 - test/zutil/test/TimedHashSetTest.java | 2 +- 50 files changed, 3114 insertions(+), 3072 deletions(-) create mode 100755 src/zutil/net/http/HttpHeader.java mode change 100644 => 100755 src/zutil/net/http/HttpPage.java mode change 100644 => 100755 src/zutil/net/http/multipart/MultipartParser.java mode change 100644 => 100755 src/zutil/net/http/pages/HttpFilePage.java mode change 100644 => 100755 src/zutil/net/upnp/UPnPMediaServer.java mode change 100644 => 100755 src/zutil/net/upnp/services/UPnPContentDirectory.java mode change 100644 => 100755 src/zutil/osal/app/linux/ProcStat.java mode change 100644 => 100755 src/zutil/osal/app/windows/TypePerf.java mode change 100644 => 100755 src/zutil/parser/CSVParser.java mode change 100644 => 100755 src/zutil/parser/Parser.java mode change 100644 => 100755 src/zutil/parser/json/JSONWriter.java mode change 100644 => 100755 src/zutil/parser/wsdl/WSDLHttpPage.java diff --git a/src/zutil/ClassUtil.java b/src/zutil/ClassUtil.java index 725f22c..81f769e 100755 --- a/src/zutil/ClassUtil.java +++ b/src/zutil/ClassUtil.java @@ -27,11 +27,7 @@ package zutil; import java.lang.reflect.Field; import java.lang.reflect.ParameterizedType; 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.Objects; /** * This class include some utility functions for classes diff --git a/src/zutil/StringUtil.java b/src/zutil/StringUtil.java index 6fd1d72..791be0d 100755 --- a/src/zutil/StringUtil.java +++ b/src/zutil/StringUtil.java @@ -27,7 +27,6 @@ package zutil; import zutil.converters.Converter; import java.util.ArrayList; -import java.util.Collection; import java.util.Iterator; /** diff --git a/src/zutil/converters/Converter.java b/src/zutil/converters/Converter.java index ce61d5e..f58ae0c 100755 --- a/src/zutil/converters/Converter.java +++ b/src/zutil/converters/Converter.java @@ -29,6 +29,8 @@ import zutil.parser.Base64Decoder; import java.io.*; import java.util.BitSet; +import java.util.Iterator; +import java.util.Map; public class Converter { private Converter(){} @@ -265,6 +267,35 @@ public class Converter { 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 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 * diff --git a/src/zutil/db/DBConnection.java b/src/zutil/db/DBConnection.java index f606673..9f6da08 100755 --- a/src/zutil/db/DBConnection.java +++ b/src/zutil/db/DBConnection.java @@ -31,7 +31,6 @@ import javax.naming.InitialContext; import javax.naming.NamingException; import javax.sql.DataSource; import java.io.Closeable; -import java.math.BigInteger; import java.sql.*; import java.util.logging.Level; import java.util.logging.Logger; diff --git a/src/zutil/db/DBUpgradeHandler.java b/src/zutil/db/DBUpgradeHandler.java index d27c3fb..f07f3e8 100755 --- a/src/zutil/db/DBUpgradeHandler.java +++ b/src/zutil/db/DBUpgradeHandler.java @@ -25,7 +25,6 @@ package zutil.db; import zutil.StringUtil; -import zutil.db.handler.ListSQLResult; import zutil.db.handler.SimpleSQLResult; import zutil.log.LogUtil; @@ -37,7 +36,6 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; -import java.util.logging.Level; import java.util.logging.Logger; /** diff --git a/src/zutil/db/bean/DBBeanConfig.java b/src/zutil/db/bean/DBBeanConfig.java index 5c093b0..024608b 100755 --- a/src/zutil/db/bean/DBBeanConfig.java +++ b/src/zutil/db/bean/DBBeanConfig.java @@ -24,7 +24,6 @@ package zutil.db.bean; -import zutil.ClassUtil; import zutil.log.LogUtil; import java.lang.reflect.Field; diff --git a/src/zutil/db/handler/ListSQLResult.java b/src/zutil/db/handler/ListSQLResult.java index c0aec6c..727a113 100755 --- a/src/zutil/db/handler/ListSQLResult.java +++ b/src/zutil/db/handler/ListSQLResult.java @@ -31,7 +31,6 @@ import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; import java.util.List; -import java.util.Properties; /** * Adds the result of the query to a List. diff --git a/src/zutil/io/file/FileUtil.java b/src/zutil/io/file/FileUtil.java index 1c0c2af..658d83d 100755 --- a/src/zutil/io/file/FileUtil.java +++ b/src/zutil/io/file/FileUtil.java @@ -31,8 +31,6 @@ import zutil.log.LogUtil; import java.io.*; import java.net.URISyntaxException; import java.net.URL; -import java.util.ArrayList; -import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.logging.Logger; diff --git a/src/zutil/log/InputStreamLogger.java b/src/zutil/log/InputStreamLogger.java index 45584e6..9628a6c 100755 --- a/src/zutil/log/InputStreamLogger.java +++ b/src/zutil/log/InputStreamLogger.java @@ -26,7 +26,6 @@ package zutil.log; import java.io.IOException; import java.io.InputStream; -import java.io.UnsupportedEncodingException; import java.util.logging.Level; import java.util.logging.Logger; diff --git a/src/zutil/log/LogUtil.java b/src/zutil/log/LogUtil.java index bf2ccb6..9c083b4 100755 --- a/src/zutil/log/LogUtil.java +++ b/src/zutil/log/LogUtil.java @@ -27,7 +27,6 @@ package zutil.log; import zutil.io.file.FileUtil; import java.io.FileInputStream; -import java.util.Enumeration; import java.util.logging.*; /** diff --git a/src/zutil/log/OutputStreamLogger.java b/src/zutil/log/OutputStreamLogger.java index 29f8aa6..6eb7756 100755 --- a/src/zutil/log/OutputStreamLogger.java +++ b/src/zutil/log/OutputStreamLogger.java @@ -25,7 +25,6 @@ package zutil.log; import java.io.IOException; -import java.io.InputStream; import java.io.OutputStream; import java.util.logging.Level; import java.util.logging.Logger; diff --git a/src/zutil/log/StreamLogger.java b/src/zutil/log/StreamLogger.java index b9c593e..e9e6731 100755 --- a/src/zutil/log/StreamLogger.java +++ b/src/zutil/log/StreamLogger.java @@ -24,13 +24,6 @@ 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. */ diff --git a/src/zutil/net/dns/DNSPacket.java b/src/zutil/net/dns/DNSPacket.java index 85ffdfc..2cef175 100755 --- a/src/zutil/net/dns/DNSPacket.java +++ b/src/zutil/net/dns/DNSPacket.java @@ -25,7 +25,6 @@ package zutil.net.dns; import zutil.parser.binary.BinaryStruct; -import zutil.parser.binary.BinaryStruct.*; /** * Created by Ziver on 2016-02-09. diff --git a/src/zutil/net/http/HttpHeader.java b/src/zutil/net/http/HttpHeader.java new file mode 100755 index 0000000..cbf6efc --- /dev/null +++ b/src/zutil/net/http/HttpHeader.java @@ -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 urlAttributes; + private float version; + private int httpCode; + + // Parameters + private HashMap headers; + private HashMap cookies; + + + protected HttpHeader(){ + urlAttributes = new HashMap(); + headers = new HashMap(); + cookies = new HashMap(); + } + + + /** + * @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 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 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 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 getCookieMap(){ + return cookies; + } + protected HashMap 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); + } +} diff --git a/src/zutil/net/http/HttpHeaderParser.java b/src/zutil/net/http/HttpHeaderParser.java index 7e58628..82c8a19 100755 --- a/src/zutil/net/http/HttpHeaderParser.java +++ b/src/zutil/net/http/HttpHeaderParser.java @@ -1,297 +1,158 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2015 Ziver Koc - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -package zutil.net.http; - -import zutil.parser.URLDecoder; - -import java.io.BufferedReader; -import java.io.IOException; -import java.util.HashMap; -import java.util.Scanner; -import java.util.regex.Pattern; - -public class HttpHeaderParser { - // Some Cached regex's - private static final Pattern colonPattern = Pattern.compile(":"); - private static final Pattern equalPattern = Pattern.compile("="); - private static final Pattern andPattern = Pattern.compile("&"); - private static final Pattern semiColonPattern = Pattern.compile(";"); - - // HTTP info - private String type; - private String url; - private HashMap url_attr; - private float version; - private int httpCode; - - // Parameters - private HashMap headers; - private HashMap cookies; - - /** - * Parses the HTTP header information from a stream - * - * @param in is the stream - * @throws IOException - */ - public HttpHeaderParser(BufferedReader in) throws IOException{ - url_attr = new HashMap(); - headers = new HashMap(); - cookies = new HashMap(); - - String tmp = null; - if( (tmp=in.readLine()) != null && !tmp.isEmpty() ){ - parseStatusLine( tmp ); - while( (tmp=in.readLine()) != null && !tmp.isEmpty() ){ - parseLine( tmp ); - } - } - parseCookies(); - } - - /** - * Parses the HTTP header information from an String - * - * @param in is the string - */ - public HttpHeaderParser(String in){ - url_attr = new HashMap(); - headers = new HashMap(); - cookies = new HashMap(); - - Scanner sc = new Scanner(in); - sc.useDelimiter("\n"); - String tmp = null; - if( sc.hasNext() && !(tmp=sc.next()).isEmpty() ){ - parseStatusLine( tmp ); - while( sc.hasNext() && !(tmp=sc.next()).isEmpty() ){ - parseLine( tmp ); - } - } - sc.close(); - parseCookies(); - } - - /** - * Parses the first header line and ads the values to - * the map and returns the file name and path - * - * @param line The header String - * @return The path and file name as a String - */ - protected void parseStatusLine(String line){ - // Server Response - if( line.startsWith("HTTP/") ){ - version = Float.parseFloat( line.substring( 5 , 8) ); - httpCode = Integer.parseInt( line.substring( 9, 12 )); - } - // Client Request - else if(line.contains("HTTP/")){ - type = (line.substring(0, line.indexOf(" "))).trim(); - version = Float.parseFloat( line.substring(line.lastIndexOf("HTTP/")+5 , line.length()).trim() ); - line = (line.substring(type.length()+1, line.lastIndexOf("HTTP/"))).trim(); - - // parse URL and attributes - int index = line.indexOf('?'); - if(index > -1){ - url = line.substring(0, index ); - line = line.substring( index+1, line.length()); - parseURLParameters(line, url_attr); - } - else{ - url = line; - } - - url = url.replaceAll("//", "/"); - } - } - - /** - * Parses a String with variables from a get or post - * that was sent from a client and puts the data into a HashMap - * - * @param attributes is the String containing all the attributes - */ - public static HashMap parseURLParameters( String attributes ){ - HashMap map = new HashMap(); - parseURLParameters(attributes, map); - return map; - } - - /** - * Parses a String with variables from a get or post - * that was sent from a client and puts the data into a HashMap - * - * @param attributes is the String containing all the attributes - * @param map is the HashMap to put all the values into - */ - public static void parseURLParameters(String attributes, HashMap map){ - String[] tmp; - attributes = URLDecoder.decode(attributes); - // get the variables - String[] data = andPattern.split( attributes ); - for(String element : data){ - tmp = equalPattern.split(element, 2); - map.put( - tmp[0].trim(), // Key - (tmp.length>1 ? tmp[1] : "").trim()); //Value - } - } - - /** - * Parses the rest of the header - * - * @param line is the next line in the header - */ - protected void parseLine(String line){ - String[] data = colonPattern.split( line, 2 ); - headers.put( - data[0].trim().toUpperCase(), // Key - (data.length>1 ? data[1] : "").trim()); //Value - } - - /** - * Parses the attribute "Cookie" and returns a HashMap - * with the values - * - * @return a HashMap with cookie values - */ - protected void parseCookies(){ - if( headers.containsKey("COOKIE") ){ - String[] tmp = semiColonPattern.split( headers.get("COOKIE") ); - String[] tmp2; - for(String cookie : tmp){ - tmp2 = equalPattern.split(cookie, 2); - cookies.put( - tmp2[0].trim(), // Key - (tmp2.length>1 ? tmp2[1] : "").trim()); //Value - } - } - } - - /** - * @return the HTTP message type( ex. GET,POST...) - */ - public String getRequestType(){ - return type; - } - /** - * @return the HTTP version of this header - */ - public float getHTTPVersion(){ - return version; - } - /** - * @return the HTTP Return Code from a Server - */ - public float getHTTPCode(){ - return httpCode; - } - /** - * @return the URL that the client sent the server - */ - public String getRequestURL(){ - return url; - } - /** - * Returns the URL attribute value of the given name. - * - * returns null if there is no such attribute - */ - public String getURLAttribute(String name){ - return url_attr.get( name ); - } - /** - * Returns the HTTP attribute value of the given name. - * - * returns null if there is no such attribute - */ - public String getHeader(String name){ - return headers.get( name.toUpperCase() ); - } - /** - * Returns the cookie value of the given name. - * - * returns null if there is no such attribute - */ - public String getCookie(String name){ - return cookies.get( name ); - } - - - /** - * @return a map of the parsed cookies - */ - public HashMap getCookies(){ - return cookies; - } - /** - * @return a map of the parsed URL attributes - */ - public HashMap getURLAttributes(){ - return url_attr; - } - /** - * @return a map of the parsed headers - */ - public HashMap getHeaders(){ - return headers; - } - - - public String toString(){ - StringBuilder tmp = new StringBuilder(); - tmp.append("{Type: ").append(type); - tmp.append(", HTTP_version: HTTP/").append(version); - if(url == null) - tmp.append(", URL: null"); - else - tmp.append(", URL: \"").append(url).append('\"'); - - tmp.append(", URL_attr: { "); - for( String key : url_attr.keySet() ){ - tmp.append(key).append(": \""); - tmp.append(url_attr.get(key)).append("\", "); - } - tmp.append('}'); - - tmp.append(", Headers: {"); - for( String key : headers.keySet() ){ - tmp.append(key).append(": \""); - tmp.append( headers.get(key) ).append("\", "); - } - tmp.append('}'); - - tmp.append(", Cookies: {"); - for( String key : cookies.keySet() ){ - tmp.append(key).append(": \""); - tmp.append( cookies.get(key) ).append("\", "); - } - tmp.append('}'); - - tmp.append('}'); - return tmp.toString(); - } -} +/* + * The MIT License (MIT) + * + * Copyright (c) 2015 Ziver Koc + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package zutil.net.http; + +import zutil.parser.URLDecoder; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.StringReader; +import java.util.regex.Pattern; + +public class HttpHeaderParser { + public static final String HEADER_COOKIE = "COOKIE"; + + private static final Pattern PATTERN_COLON = Pattern.compile(":"); + private static final Pattern PATTERN_EQUAL = Pattern.compile("="); + private static final Pattern PATTERN_AND = Pattern.compile("&"); + private static final Pattern PATTERN_SEMICOLON = Pattern.compile(";"); + + + private BufferedReader in; + + + /** + * Parses the HTTP header information from a stream + * + * @param in is the stream + */ + public HttpHeaderParser(BufferedReader in){ + this.in = in; + } + + /** + * Parses the HTTP header information from a String + * + * @param in is the String + */ + public HttpHeaderParser(String in){ + this.in = new BufferedReader(new StringReader(in)); + } + + + public HttpHeader read() throws IOException { + HttpHeader header = null; + String line = null; + if( (line=in.readLine()) != null && !line.isEmpty() ){ + header = new HttpHeader(); + parseStatusLine(header, line); + while( (line=in.readLine()) != null && !line.isEmpty() ){ + parseLine(header, line); + } + parseCookies(header); + } + return header; + } + + + /** + * Parses the first header line and ads the values to + * the map and returns the file name and path + * + * @param line The header String + * @return The path and file name as a String + */ + protected static void parseStatusLine(HttpHeader header, String line){ + // Server Response + if( line.startsWith("HTTP/") ){ + header.setHTTPVersion( Float.parseFloat( line.substring( 5 , 8))); + header.setHTTPCode( Integer.parseInt( line.substring( 9, 12 ))); + } + // Client Request + else if(line.contains("HTTP/")){ + header.setRequestType( line.substring(0, line.indexOf(" "))); + header.setHTTPVersion( Float.parseFloat( line.substring(line.lastIndexOf("HTTP/")+5 , line.length()).trim())); + line = (line.substring(header.getRequestType().length()+1, line.lastIndexOf("HTTP/"))); + + // parse URL and attributes + int index = line.indexOf('?'); + if(index > -1){ + header.setRequestURL( line.substring(0, index)); + line = line.substring( index+1, line.length()); + parseURLParameters(header, line); + } + else{ + header.setRequestURL(line); + } + } + } + + /** + * Parses the rest of the header + * + * @param line is the next line in the header + */ + protected void parseLine(HttpHeader header, String line){ + String[] data = PATTERN_COLON.split( line, 2 ); + header.putHeader( + data[0].trim().toUpperCase(), // Key + (data.length>1 ? data[1] : "").trim()); //Value + } + + /** + * Parses the header "Cookie" and stores all cookies in the HttpHeader object + */ + protected void parseCookies(HttpHeader header){ + String cookieHeader = header.getHeader(HEADER_COOKIE); + if(cookieHeader != null && !cookieHeader.isEmpty()){ + String[] tmp = PATTERN_SEMICOLON.split(cookieHeader); + for(String cookie : tmp){ + String[] tmp2 = PATTERN_EQUAL.split(cookie, 2); + header.putCookie( + tmp2[0].trim(), // Key + (tmp2.length>1 ? tmp2[1] : "").trim()); //Value + } + } + } + + /** + * Parses a String with variables from a get or post + * that was sent from a client + * + * @param attributes is the String containing all the attributes + */ + protected static void parseURLParameters(HttpHeader header, String attributes){ + String[] tmp; + attributes = URLDecoder.decode(attributes); + // get the variables + String[] data = PATTERN_AND.split( attributes ); + for(String element : data){ + tmp = PATTERN_EQUAL.split(element, 2); + header.putURLAttribute( + tmp[0].trim(), // Key + (tmp.length>1 ? tmp[1] : "").trim()); //Value + } + } +} diff --git a/src/zutil/net/http/HttpPage.java b/src/zutil/net/http/HttpPage.java old mode 100644 new mode 100755 index 97dcb6e..84f7286 --- a/src/zutil/net/http/HttpPage.java +++ b/src/zutil/net/http/HttpPage.java @@ -1,52 +1,53 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2015 Ziver Koc - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -package zutil.net.http; - -import java.io.IOException; -import java.util.Map; - -/** - * This is a interface for a ordinary page for the HttpServer - * - * @author Ziver - * - */ -public interface HttpPage{ - /** - * This method has to be implemented for every page. - * This method is called when a client wants a response - * from this specific page. - * @param out is the PrintStream to the client - * @param client_info is information about the client - * @param session is session values for the client - * @param cookie is cookie information from the client - * @param request is POST and GET requests from the client - */ - public abstract void respond(HttpPrintStream out, - HttpHeaderParser client_info, - Map session, - Map cookie, - Map request) throws IOException; -} +/* + * The MIT License (MIT) + * + * Copyright (c) 2015 Ziver Koc + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package zutil.net.http; + +import java.io.IOException; +import java.util.Map; + +/** + * This is a interface for a ordinary page for the HttpServer + * + * @author Ziver + * + */ +public interface HttpPage{ + /** + * This method has to be implemented for every page. + * This method is called when a client wants a response + * from this specific page. + * + * @param out is a output stream to the client + * @param headers is the header received from the client + * @param session is the session associated with the current client + * @param cookie is cookie information from the client + * @param request is POST and GET requests from the client + */ + public abstract void respond(HttpPrintStream out, + HttpHeader headers, + Map session, + Map cookie, + Map request) throws IOException; +} diff --git a/src/zutil/net/http/HttpPrintStream.java b/src/zutil/net/http/HttpPrintStream.java index dd865d0..83e68b4 100755 --- a/src/zutil/net/http/HttpPrintStream.java +++ b/src/zutil/net/http/HttpPrintStream.java @@ -24,6 +24,8 @@ package zutil.net.http; +import zutil.converters.Converter; + import java.io.IOException; import java.io.OutputStream; import java.io.PrintStream; @@ -66,7 +68,7 @@ public class HttpPrintStream extends OutputStream{ * Creates an new instance of HttpPrintStream with * message type of RESPONSE and buffering disabled. * - * @param out is the OutputStream to send the message + * @param out is the OutputStream where the data will be written to */ public HttpPrintStream(OutputStream out) { this( out, HttpMessageType.RESPONSE ); @@ -75,7 +77,7 @@ public class HttpPrintStream extends OutputStream{ * Creates an new instance of HttpPrintStream with * message type buffering disabled. * - * @param out is the OutputStream to send the message + * @param out is the OutputStream where the data will be written to * @param type is the type of message */ public HttpPrintStream(OutputStream out, HttpMessageType type) { @@ -107,7 +109,7 @@ public class HttpPrintStream extends OutputStream{ * * @param key is the name of the cookie * @param value is the value of the cookie - * @throws IOException Throws exception if the header has already been sent + * @throws IOException if the header has already been sent */ public void setCookie(String key, String value) throws IOException{ if(cookies == null) @@ -120,7 +122,7 @@ public class HttpPrintStream extends OutputStream{ * * @param key is the header name * @param value is the value of the header - * @throws IOException Throws exception if the header has already been sent + * @throws IOException if the header has already been sent */ public void setHeader(String key, String value) throws IOException{ if(headers == null) @@ -325,24 +327,16 @@ public class HttpPrintStream extends OutputStream{ str.append(", req_url: null"); else str.append(", req_url: \"").append(req_url).append('\"'); - } else { + } else if (message_type == HttpMessageType.RESPONSE){ str.append(", status_code: ").append(res_status_code); str.append(", status_str: ").append(getStatusString(res_status_code)); } if (headers != null) { - str.append(", Headers: {"); - for (String key : headers.keySet()) { - str.append(key).append(": \"").append(headers.get(key)).append("\", "); - } - str.append('}'); + str.append(", Headers: ").append(Converter.toString(headers)); } if (cookies != null) { - str.append(", Cookies: {"); - for (String key : cookies.keySet()) { - str.append(key).append(": \"").append(cookies.get(key)).append("\", "); - } - str.append('}'); + str.append(", Cookies: ").append(Converter.toString(cookies)); } } else diff --git a/src/zutil/net/http/HttpServer.java b/src/zutil/net/http/HttpServer.java index ba90c5f..ab58876 100755 --- a/src/zutil/net/http/HttpServer.java +++ b/src/zutil/net/http/HttpServer.java @@ -1,296 +1,298 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2015 Ziver Koc - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -package zutil.net.http; - -import zutil.StringUtil; -import zutil.log.LogUtil; -import zutil.net.threaded.ThreadedTCPNetworkServer; -import zutil.net.threaded.ThreadedTCPNetworkServerThread; - -import java.io.BufferedReader; -import java.io.File; -import java.io.IOException; -import java.io.InputStreamReader; -import java.net.Socket; -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; -import java.util.logging.Level; -import java.util.logging.Logger; - - -/** - * A simple web server that handles both cookies and - * sessions for all the clients - * - * @author Ziver - */ -public class HttpServer extends ThreadedTCPNetworkServer{ - private static final Logger logger = LogUtil.getLogger(); - public static final String SERVER_VERSION = "Ziver HttpServer 1.0"; - public static final int COOKIE_TTL = 200; - public static final int SESSION_TTL = 10*60*1000; // in milliseconds - - private Map pages; - private HttpPage defaultPage; - private Map> sessions; - private int nextSessionId; - - /** - * Creates a new instance of the sever - * - * @param port The port that the server should listen to - */ - public HttpServer(int port){ - this(port, null, null); - } - - - /** - * Creates a new instance of the sever - * - * @param port The port that the server should listen to - * @param keyStore If this is not null then the server will use SSL connection with this keyStore file path - * @param keyStorePass If this is not null then the server will use a SSL connection with the given certificate - */ - public HttpServer(int port, File keyStore, String keyStorePass){ - super( port, keyStore, keyStorePass ); - - pages = new ConcurrentHashMap(); - sessions = new ConcurrentHashMap>(); - nextSessionId = 0; - - Timer timer = new Timer(); - timer.schedule(new GarbageCollector(), 10000, SESSION_TTL / 2); - - logger.info("HTTP"+(keyStore==null?"":"S")+" Server ready!"); - } - - /** - * This class acts as an garbage collector that - * removes old sessions from the session HashMap - * - * @author Ziver - */ - private class GarbageCollector extends TimerTask { - public void run(){ - Object[] keys = sessions.keySet().toArray(); - for(Object key : keys){ - Map client_session = sessions.get(key); - - // Check if session is still valid - if((Long)client_session.get("ttl") < System.currentTimeMillis()){ - sessions.remove(key); - logger.fine("Removing Session: "+key); - } - } - } - } - - /** - * Add a HttpPage to a specific URL - * - * @param name The URL or name of the page - * @param page The page itself - */ - public void setPage(String name, HttpPage page){ - if(name.charAt(0) != '/') - name = "/"+name; - pages.put(name, page); - } - - /** - * This is a default page that will be shown - * if there is no other matching page, - * - * @param page The HttpPage that will be shown - */ - public void setDefaultPage(HttpPage page){ - defaultPage = page; - } - - protected ThreadedTCPNetworkServerThread getThreadInstance( Socket s ){ - try { - return new HttpServerThread( s ); - } catch (IOException e) { - logger.log(Level.SEVERE, "Could not start new Thread", e); - } - return null; - } - - /** - * Internal class that handles all the requests - * - * @author Ziver - * - */ - protected class HttpServerThread implements ThreadedTCPNetworkServerThread{ - private HttpPrintStream out; - private BufferedReader in; - private Socket socket; - - public HttpServerThread(Socket socket) throws IOException{ - out = new HttpPrintStream(socket.getOutputStream()); - in = new BufferedReader(new InputStreamReader(socket.getInputStream())); - this.socket = socket; - } - - public void run(){ - String tmp = null; - - HashMap cookie = new HashMap(); - HashMap request = new HashMap(); - - //**************************** REQUEST ********************************* - try { - long time = System.currentTimeMillis(); - HttpHeaderParser parser = new HttpHeaderParser(in); - request = parser.getURLAttributes(); - cookie = parser.getCookies(); - - - //******* Read in the post data if available - if( parser.getHeader("Content-Length")!=null ){ - // Reads the post data size - tmp = parser.getHeader("Content-Length"); - int post_data_length = Integer.parseInt( tmp ); - // read the data - StringBuffer tmpb = new StringBuffer(); - // read the data - for(int i=0; i client_session; - long ttl_time = System.currentTimeMillis()+SESSION_TTL; - if( cookie.containsKey("session_id") && sessions.containsKey(cookie.get("session_id")) ){ - client_session = sessions.get( cookie.get("session_id") ); - // Check if session is still valid - if( (Long)client_session.get("ttl") < System.currentTimeMillis() ){ - int session_id = (Integer)client_session.get("session_id"); - client_session = Collections.synchronizedMap(new HashMap()); - client_session.put( "session_id", session_id); - sessions.put( ""+session_id, client_session); - } - // renew the session TTL - client_session.put("ttl", ttl_time); - } - else{ - client_session = Collections.synchronizedMap(new HashMap()); - client_session.put( "session_id", nextSessionId ); - client_session.put( "ttl", ttl_time ); - sessions.put( ""+nextSessionId, client_session ); - ++nextSessionId; - } - - //**************************** RESPONSE ************************************ - out.setStatusCode(200); - out.setHeader( "Server", SERVER_VERSION ); - out.setHeader( "Content-Type", "text/html" ); - out.setCookie( "session_id", ""+client_session.get("session_id") ); - - if( parser.getRequestURL() != null && pages.containsKey(parser.getRequestURL()) ){ - HttpPage page = pages.get(parser.getRequestURL()); - page.respond(out, parser, client_session, cookie, request); - if(LogUtil.isLoggable(page.getClass(), Level.FINER)) - logRequest(parser, client_session, cookie, request, time); - } - else if( parser.getRequestURL() != null && defaultPage != null ){ - defaultPage.respond(out, parser, client_session, cookie, request); - if(LogUtil.isLoggable(defaultPage.getClass(), Level.FINER)) - logRequest(parser, client_session, cookie, request, time); - } - else{ - out.setStatusCode( 404 ); - out.println( "404 Page Not Found: "+parser.getRequestURL() ); - logger.warning("Page not defined: " + parser.getRequestURL()); - } - - //******************************************************************************** - } catch (Exception e) { - logger.log(Level.SEVERE, "500 Internal Server Error", e); - try { - if (!out.isHeaderSent()) - out.setStatusCode(500); - if (e.getMessage() != null) - out.println("500 Internal Server Error: " + e.getMessage()); - else if (e.getCause() != null) { - out.println("500 Internal Server Error: " + e.getCause().getMessage()); - } else { - out.println("500 Internal Server Error: " + e); - } - }catch(IOException ioe){ - logger.log(Level.SEVERE, null, ioe); - } - } - - try{ - out.close(); - in.close(); - socket.close(); - } catch( Exception e ) { - logger.log(Level.WARNING, "Could not close connection", e); - } - } - } - - protected static void logRequest(HttpHeaderParser parser, - Map client_session, - Map cookie, - Map request, - long time){ - // Debug - if(logger.isLoggable(Level.FINEST) ){ - logger.finer( - "Received request: " + parser.getRequestURL() - + " (client_session: " + client_session - + ", cookie: " + cookie - + ", request: " + request + ")" - + ", time: "+ StringUtil.formatTimeToString(System.currentTimeMillis() - time)); - } else if(logger.isLoggable(Level.FINER)){ - logger.finer( - "Received request: " + parser.getRequestURL() - + ", time: "+ StringUtil.formatTimeToString(System.currentTimeMillis() - time)); - } - } -} +/* + * The MIT License (MIT) + * + * Copyright (c) 2015 Ziver Koc + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package zutil.net.http; + +import zutil.StringUtil; +import zutil.log.LogUtil; +import zutil.net.threaded.ThreadedTCPNetworkServer; +import zutil.net.threaded.ThreadedTCPNetworkServerThread; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.Socket; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.logging.Level; +import java.util.logging.Logger; + + +/** + * A simple web server that handles both cookies and + * sessions for all the clients + * + * @author Ziver + */ +public class HttpServer extends ThreadedTCPNetworkServer{ + private static final Logger logger = LogUtil.getLogger(); + + public static final String SESSION_ID_KEY = "session_id"; + public static final String SESSION_TTL_KEY = "session_ttl"; + public static final String SERVER_VERSION = "Ziver HttpServer 1.1"; + public static final int SESSION_TTL = 10*60*1000; // in milliseconds + + + private Map pages; + private HttpPage defaultPage; + private Map> sessions; + private int nextSessionId; + + /** + * Creates a new instance of the sever + * + * @param port The port that the server should listen to + */ + public HttpServer(int port){ + this(port, null, null); + } + + + /** + * Creates a new instance of the sever + * + * @param port The port that the server should listen to + * @param keyStore If this is not null then the server will use SSL connection with this keyStore file path + * @param keyStorePass If this is not null then the server will use a SSL connection with the given certificate + */ + public HttpServer(int port, File keyStore, String keyStorePass){ + super( port, keyStore, keyStorePass ); + + pages = new ConcurrentHashMap<>(); + sessions = new ConcurrentHashMap<>(); + nextSessionId = 0; + + Timer timer = new Timer(); + timer.schedule(new SessionGarbageCollector(), 10000, SESSION_TTL / 2); + + logger.info("HTTP"+(keyStore==null?"":"S")+" Server ready!"); + } + + /** + * This class acts as an garbage collector that + * removes old sessions from the session HashMap + * + * @author Ziver + */ + private class SessionGarbageCollector extends TimerTask { + public void run(){ + Object[] keys = sessions.keySet().toArray(); + for(Object key : keys){ + Map session = sessions.get(key); + + // Check if session is still valid + if((Long)session.get(SESSION_TTL_KEY) < System.currentTimeMillis()){ + sessions.remove(key); + logger.fine("Removing Session: "+key); + } + } + } + } + + /** + * Add a HttpPage to a specific URL + * + * @param name The URL or name of the page + * @param page The page itself + */ + public void setPage(String name, HttpPage page){ + if(name.charAt(0) != '/') + name = "/"+name; + pages.put(name, page); + } + + /** + * This is a default page that will be shown + * if there is no other matching page, + * + * @param page The HttpPage that will be shown + */ + public void setDefaultPage(HttpPage page){ + defaultPage = page; + } + + protected ThreadedTCPNetworkServerThread getThreadInstance( Socket s ){ + try { + return new HttpServerThread( s ); + } catch (IOException e) { + logger.log(Level.SEVERE, "Could not start new Thread", e); + } + return null; + } + + /** + * Internal class that handles all the requests + * + * @author Ziver + * + */ + protected class HttpServerThread implements ThreadedTCPNetworkServerThread{ + private HttpPrintStream out; + private BufferedReader in; + private Socket socket; + + public HttpServerThread(Socket socket) throws IOException{ + out = new HttpPrintStream(socket.getOutputStream()); + in = new BufferedReader(new InputStreamReader(socket.getInputStream())); + this.socket = socket; + } + + public void run(){ + String tmp = null; + HttpHeaderParser headerParser = new HttpHeaderParser(in); + + //**************************** REQUEST ********************************* + try { + long time = System.currentTimeMillis(); + HttpHeader header = headerParser.read(); + if(header == null){ + logger.finer("No header received"); + return; + } + + //******* Read in the post data if available + if( header.getHeader("Content-Length") != null ){ + // Reads the post data size + tmp = header.getHeader("Content-Length"); + int post_data_length = Integer.parseInt( tmp ); + // read the data + StringBuilder tmpBuff = new StringBuilder(); + // read the data + for(int i=0; i session; + long ttlTime = System.currentTimeMillis() + SESSION_TTL; + String sessionCookie = header.getCookie(SESSION_ID_KEY); + if( sessionCookie != null && sessions.containsKey(sessionCookie) && + (Long)sessions.get(sessionCookie).get(SESSION_TTL_KEY) < System.currentTimeMillis()){ // Check if session is still valid + + session = sessions.get(sessionCookie); + // renew the session TTL + session.put(SESSION_TTL_KEY, ttlTime); + } + else{ + session = Collections.synchronizedMap(new HashMap()); + session.put(SESSION_ID_KEY, nextSessionId ); + session.put(SESSION_TTL_KEY, ttlTime ); + sessions.put(nextSessionId, session ); + ++nextSessionId; + } + + //**************************** RESPONSE ************************************ + out.setStatusCode(200); + out.setHeader("Server", SERVER_VERSION); + out.setHeader("Content-Type", "text/html"); + out.setCookie(SESSION_ID_KEY, ""+session.get(SESSION_ID_KEY)); + + if( header.getRequestURL() != null && pages.containsKey(header.getRequestURL())){ + HttpPage page = pages.get(header.getRequestURL()); + page.respond(out, header, session, header.getCookieMap(), header.getUrlAttributeMap()); + if(LogUtil.isLoggable(page.getClass(), Level.FINER)) + logRequest(header, session, time); + } + else if( header.getRequestURL() != null && defaultPage != null ){ + defaultPage.respond(out, header, session, header.getCookieMap(), header.getUrlAttributeMap()); + if(LogUtil.isLoggable(defaultPage.getClass(), Level.FINER)) + logRequest(header, session, time); + } + else{ + out.setStatusCode(404); + out.println("404 Page Not Found: "+header.getRequestURL()); + logger.warning("Page not defined: " + header.getRequestURL()); + } + + //******************************************************************************** + } catch (Exception e) { + logger.log(Level.SEVERE, "500 Internal Server Error", e); + try { + if (!out.isHeaderSent()) + out.setStatusCode(500); + if (e.getMessage() != null) + out.println("500 Internal Server Error: " + e.getMessage()); + else if (e.getCause() != null) { + out.println("500 Internal Server Error: " + e.getCause().getMessage()); + } else { + out.println("500 Internal Server Error: " + e); + } + }catch(IOException ioe){ + logger.log(Level.SEVERE, null, ioe); + } + } + finally { + try{ + out.close(); + in.close(); + socket.close(); + } catch( Exception e ) { + logger.log(Level.WARNING, "Could not close connection", e); + } + } + } + } + + + protected static void logRequest(HttpHeader header, + Map session, + long time){ + // Debug + if(logger.isLoggable(Level.FINEST) ){ + StringBuilder buff = new StringBuilder(); + buff.append("Received request: ").append(header.getRequestURL()); + buff.append(", ("); + buff.append("request: ").append(header.toStringAttributes()); + buff.append(", cookies: ").append(header.toStringCookies()); + buff.append(", session: ").append(session); + buff.append(")"); + buff.append(", time: "+ StringUtil.formatTimeToString(System.currentTimeMillis() - time)); + + logger.finer(buff.toString()); + } else if(logger.isLoggable(Level.FINER)){ + logger.finer( + "Received request: " + header.getRequestURL() + + ", time: "+ StringUtil.formatTimeToString(System.currentTimeMillis() - time)); + } + } +} diff --git a/src/zutil/net/http/multipart/MultipartParser.java b/src/zutil/net/http/multipart/MultipartParser.java old mode 100644 new mode 100755 index 9ad1e10..caabd74 --- a/src/zutil/net/http/multipart/MultipartParser.java +++ b/src/zutil/net/http/multipart/MultipartParser.java @@ -1,145 +1,145 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2015 Ziver Koc - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -package zutil.net.http.multipart; - -import zutil.ProgressListener; -import zutil.net.http.HttpHeaderParser; - -import javax.servlet.http.HttpServletRequest; -import java.io.BufferedReader; -import java.io.File; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -/** - * Parses a multipart/form-data http request, - * saves files to temporary location. - * - * http://www.ietf.org/rfc/rfc1867.txt - * - * @author Ziver - * - */ -public class MultipartParser { - /** This is the temporary directory for the received files */ - private File tempDir; - /** This is the delimiter that will separate the fields */ - private String delimiter; - /** The length of the HTTP Body */ - private long contentLength; - /** This is the input stream */ - private BufferedReader in; - - /** This is the listener that will listen on the progress */ - private ProgressListener listener; - - - - public MultipartParser(BufferedReader in, HttpHeaderParser header){ - this.in = in; - - String cotype = header.getHeader("Content-type"); - cotype = cotype.split(" *; *")[1]; - delimiter = cotype.split(" *= *")[1]; - - contentLength = Long.parseLong( header.getHeader("Content-Length") ); - } - - public MultipartParser(BufferedReader in, HttpServletRequest req){ - this.in = in; - - String cotype = req.getHeader("Content-type"); - cotype = cotype.split(" *; *")[1]; - delimiter = cotype.split(" *= *")[1]; - - contentLength = req.getContentLength(); - } - - public MultipartParser(BufferedReader in, String delimiter, long length){ - this.in = in; - this.delimiter = delimiter; - this.contentLength = length; - } - - - /** - * @param listener is the listener that will be called for progress - */ - public void setListener(ProgressListener listener){ - this.listener = listener; - } - - /** - * Parses the HTTP Body and returns a list of fields - * - * @return A list of FormField - */ - public List parse() throws IOException{ - ArrayList list = new ArrayList(); - parse(list, delimiter); - return list; - } - - - private void parse(List list, String delimiter) throws IOException{ - // TODO: - } - - - /** - * Creates a temporary file in either the system - * temporary folder or by the setTempDir() function - * - * @return the temporary file - */ - protected File createTempFile() throws IOException{ - if(tempDir != null) - return File.createTempFile("upload", ".part", tempDir.getAbsoluteFile()); - else - return File.createTempFile("upload", ".part"); - } - - /** - * Sets the initial delimiter - * - * @param delimiter is the new delimiter - */ - public void setDelimiter(String delimiter){ - this.delimiter = delimiter; - } - - public void setTempDir(File dir){ - if(!dir.isDirectory()) - throw new RuntimeException("\""+dir.getAbsolutePath()+"\" is not a directory!"); - if(!dir.canWrite()) - throw new RuntimeException("\""+dir.getAbsolutePath()+"\" is not writable!"); - tempDir = dir; - } - - public long getContentLength(){ - return contentLength; - } -} +/* + * The MIT License (MIT) + * + * Copyright (c) 2015 Ziver Koc + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package zutil.net.http.multipart; + +import zutil.ProgressListener; +import zutil.net.http.HttpHeader; + +import javax.servlet.http.HttpServletRequest; +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +/** + * Parses a multipart/form-data http request, + * saves files to temporary location. + * + * http://www.ietf.org/rfc/rfc1867.txt + * + * @author Ziver + * + */ +public class MultipartParser { + /** This is the temporary directory for the received files */ + private File tempDir; + /** This is the delimiter that will separate the fields */ + private String delimiter; + /** The length of the HTTP Body */ + private long contentLength; + /** This is the input stream */ + private BufferedReader in; + + /** This is the listener that will listen on the progress */ + private ProgressListener listener; + + + + public MultipartParser(BufferedReader in, HttpHeader header){ + this.in = in; + + String cotype = header.getHeader("Content-type"); + cotype = cotype.split(" *; *")[1]; + delimiter = cotype.split(" *= *")[1]; + + contentLength = Long.parseLong( header.getHeader("Content-Length") ); + } + + public MultipartParser(BufferedReader in, HttpServletRequest req){ + this.in = in; + + String cotype = req.getHeader("Content-type"); + cotype = cotype.split(" *; *")[1]; + delimiter = cotype.split(" *= *")[1]; + + contentLength = req.getContentLength(); + } + + public MultipartParser(BufferedReader in, String delimiter, long length){ + this.in = in; + this.delimiter = delimiter; + this.contentLength = length; + } + + + /** + * @param listener is the listener that will be called for progress + */ + public void setListener(ProgressListener listener){ + this.listener = listener; + } + + /** + * Parses the HTTP Body and returns a list of fields + * + * @return A list of FormField + */ + public List parse() throws IOException{ + ArrayList list = new ArrayList(); + parse(list, delimiter); + return list; + } + + + private void parse(List list, String delimiter) throws IOException{ + // TODO: + } + + + /** + * Creates a temporary file in either the system + * temporary folder or by the setTempDir() function + * + * @return the temporary file + */ + protected File createTempFile() throws IOException{ + if(tempDir != null) + return File.createTempFile("upload", ".part", tempDir.getAbsoluteFile()); + else + return File.createTempFile("upload", ".part"); + } + + /** + * Sets the initial delimiter + * + * @param delimiter is the new delimiter + */ + public void setDelimiter(String delimiter){ + this.delimiter = delimiter; + } + + public void setTempDir(File dir){ + if(!dir.isDirectory()) + throw new RuntimeException("\""+dir.getAbsolutePath()+"\" is not a directory!"); + if(!dir.canWrite()) + throw new RuntimeException("\""+dir.getAbsolutePath()+"\" is not writable!"); + tempDir = dir; + } + + public long getContentLength(){ + return contentLength; + } +} diff --git a/src/zutil/net/http/pages/HttpFilePage.java b/src/zutil/net/http/pages/HttpFilePage.java old mode 100644 new mode 100755 index c8a337b..b7922b6 --- a/src/zutil/net/http/pages/HttpFilePage.java +++ b/src/zutil/net/http/pages/HttpFilePage.java @@ -1,164 +1,164 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2015 Ziver Koc - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -package zutil.net.http.pages; - -import zutil.io.IOUtil; -import zutil.io.file.FileUtil; -import zutil.log.LogUtil; -import zutil.net.http.HttpHeaderParser; -import zutil.net.http.HttpPage; -import zutil.net.http.HttpPrintStream; - -import java.io.*; -import java.util.Map; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * This Http Page will host static content from the server. - * - * Created by Ziver on 2015-03-30. - */ -public class HttpFilePage implements HttpPage{ - private static final Logger log = LogUtil.getLogger(); - - private File resource_root; - private boolean showFolders; - private boolean redirectToIndex; - - /** - * @param file a reference to a root directory or a file. - */ - public HttpFilePage(File file){ - this.resource_root = file; - this.showFolders = true; - this.redirectToIndex = true; - } - - - @Override - public void respond(HttpPrintStream out, - HttpHeaderParser client_info, - Map session, - Map cookie, - Map request) throws IOException{ - - try { - // Is the root only one file or a folder - if (resource_root.isFile()) { - deliverFile(resource_root, out); - } - else { // Resource root is a folder - File file = new File(resource_root, - client_info.getRequestURL()); - if(file.getCanonicalPath().startsWith(resource_root.getCanonicalPath())){ - if(file.isDirectory() && showFolders){ - File indexFile = new File(file, "index.html"); - // Redirect to index.html - if(redirectToIndex && indexFile.isFile()) { - deliverFile(indexFile, out); - } - // Show folder contents - else if(showFolders){ - out.println("

Directory: " + client_info.getRequestURL() + "

"); - out.println("
    "); - for (String f : file.list()) { - String url = client_info.getRequestURL(); - out.println("
  • " + f + "
  • "); - } - out.println("

"); - } - else { - throw new SecurityException("User not allowed to view folder: root=" + resource_root.getAbsolutePath()); - } - } - else { - deliverFile(file, out); - } - } - else { - throw new SecurityException("File is outside of root directory: root=" + resource_root.getAbsolutePath() + " file=" + file.getAbsolutePath()); - } - } - - }catch (FileNotFoundException e){ - if(!out.isHeaderSent()) - out.setStatusCode(404); - log.log(Level.WARNING, e.getMessage()); - out.println("404 Page Not Found: " + client_info.getRequestURL()); - }catch (SecurityException e){ - if(!out.isHeaderSent()) - out.setStatusCode(404); - log.log(Level.WARNING, e.getMessage()); - out.println("404 Page Not Found: " + client_info.getRequestURL() ); - }catch (IOException e){ - if(!out.isHeaderSent()) - out.setStatusCode(500); - log.log(Level.WARNING, null, e); - out.println("500 Internal Server Error: "+e.getMessage() ); - } - } - - private void deliverFile(File file, HttpPrintStream out) throws IOException { - out.setHeader("Content-Type", getMIMEType(file)); - out.flush(); - - //InputStream in = new BufferedInputStream(new FileInputStream(file)); - InputStream in = new FileInputStream(file); - IOUtil.copyStream(in, out); - in.close(); - } - - private String getMIMEType(File file){ - switch(FileUtil.getFileExtension(file)){ - case "css": return "text/css"; - case "cvs": return "text/csv"; - case "jpg": return "image/jpeg"; - case "js": return "application/javascript"; - case "png": return "image/png"; - case "htm": - case "html": return "text/html"; - case "xml": return "text/xml"; - default: return "text/plain"; - } - } - - - /** - * Enable or disable showing of folder contents - */ - public void showFolders(boolean enabled){ - this.showFolders = enabled; - } - - /** - * If directory links should be redirected to index files - */ - public void redirectToIndexFile(boolean enabled){ - this.redirectToIndex = enabled; - } -} +/* + * The MIT License (MIT) + * + * Copyright (c) 2015 Ziver Koc + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package zutil.net.http.pages; + +import zutil.io.IOUtil; +import zutil.io.file.FileUtil; +import zutil.log.LogUtil; +import zutil.net.http.HttpHeader; +import zutil.net.http.HttpPage; +import zutil.net.http.HttpPrintStream; + +import java.io.*; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * This Http Page will host static content from the server. + * + * Created by Ziver on 2015-03-30. + */ +public class HttpFilePage implements HttpPage{ + private static final Logger log = LogUtil.getLogger(); + + private File resource_root; + private boolean showFolders; + private boolean redirectToIndex; + + /** + * @param file a reference to a root directory or a file. + */ + public HttpFilePage(File file){ + this.resource_root = file; + this.showFolders = true; + this.redirectToIndex = true; + } + + + @Override + public void respond(HttpPrintStream out, + HttpHeader headers, + Map session, + Map cookie, + Map request) throws IOException{ + + try { + // Is the root only one file or a folder + if (resource_root.isFile()) { + deliverFile(resource_root, out); + } + else { // Resource root is a folder + File file = new File(resource_root, + headers.getRequestURL()); + if(file.getCanonicalPath().startsWith(resource_root.getCanonicalPath())){ + if(file.isDirectory() && showFolders){ + File indexFile = new File(file, "index.html"); + // Redirect to index.html + if(redirectToIndex && indexFile.isFile()) { + deliverFile(indexFile, out); + } + // Show folder contents + else if(showFolders){ + out.println("

Directory: " + headers.getRequestURL() + "

"); + out.println("
    "); + for (String f : file.list()) { + String url = headers.getRequestURL(); + out.println("
  • " + f + "
  • "); + } + out.println("

"); + } + else { + throw new SecurityException("User not allowed to view folder: root=" + resource_root.getAbsolutePath()); + } + } + else { + deliverFile(file, out); + } + } + else { + throw new SecurityException("File is outside of root directory: root=" + resource_root.getAbsolutePath() + " file=" + file.getAbsolutePath()); + } + } + + }catch (FileNotFoundException e){ + if(!out.isHeaderSent()) + out.setStatusCode(404); + log.log(Level.WARNING, e.getMessage()); + out.println("404 Page Not Found: " + headers.getRequestURL()); + }catch (SecurityException e){ + if(!out.isHeaderSent()) + out.setStatusCode(404); + log.log(Level.WARNING, e.getMessage()); + out.println("404 Page Not Found: " + headers.getRequestURL() ); + }catch (IOException e){ + if(!out.isHeaderSent()) + out.setStatusCode(500); + log.log(Level.WARNING, null, e); + out.println("500 Internal Server Error: "+e.getMessage() ); + } + } + + private void deliverFile(File file, HttpPrintStream out) throws IOException { + out.setHeader("Content-Type", getMIMEType(file)); + out.flush(); + + //InputStream in = new BufferedInputStream(new FileInputStream(file)); + InputStream in = new FileInputStream(file); + IOUtil.copyStream(in, out); + in.close(); + } + + private String getMIMEType(File file){ + switch(FileUtil.getFileExtension(file)){ + case "css": return "text/css"; + case "cvs": return "text/csv"; + case "jpg": return "image/jpeg"; + case "js": return "application/javascript"; + case "png": return "image/png"; + case "htm": + case "html": return "text/html"; + case "xml": return "text/xml"; + default: return "text/plain"; + } + } + + + /** + * Enable or disable showing of folder contents + */ + public void showFolders(boolean enabled){ + this.showFolders = enabled; + } + + /** + * If directory links should be redirected to index files + */ + public void redirectToIndexFile(boolean enabled){ + this.redirectToIndex = enabled; + } +} diff --git a/src/zutil/net/ssdp/SSDPClient.java b/src/zutil/net/ssdp/SSDPClient.java index 1a44214..03626fc 100755 --- a/src/zutil/net/ssdp/SSDPClient.java +++ b/src/zutil/net/ssdp/SSDPClient.java @@ -1,243 +1,249 @@ -/* - * 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.ssdp; - -import zutil.io.StringOutputStream; -import zutil.log.LogUtil; -import zutil.net.http.HttpHeaderParser; -import zutil.net.http.HttpPrintStream; -import zutil.net.threaded.ThreadedUDPNetwork; -import zutil.net.threaded.ThreadedUDPNetworkThread; - -import java.io.IOException; -import java.net.DatagramPacket; -import java.net.InetAddress; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * An SSDP client class that will request - * service information. - * - * @author Ziver - */ -public class SSDPClient extends ThreadedUDPNetwork implements ThreadedUDPNetworkThread{ - private static final Logger logger = LogUtil.getLogger(); - /** Mapping of search targets and list of associated services **/ - private HashMap> services_st; - /** Map of all unique services received **/ - private HashMap services_usn; - - private SSDPServiceListener listener; - - - /** - * Creates new instance of this class. An UDP - * listening socket at the SSDP port. - * - * @throws IOException - */ - public SSDPClient() throws IOException{ - super(null); - super.setThread(this); - - services_st = new HashMap<>(); - services_usn = new HashMap<>(); - } - - /** - * Sends an request for an service - * - * @param searchTarget is the SearchTarget of the service - * - * ***** REQUEST: - * M-SEARCH * HTTP/1.1 - * Host: 239.255.255.250:reservedSSDPport - * Man: "ssdp:discover" - * ST: ge:fridge - * MX: 3 - * - */ - public void requestService(String searchTarget){ - requestService(searchTarget, null); - } - public void requestService(String searchTarget, HashMap headers){ - try { - // Generate an SSDP discover message - StringOutputStream msg = new StringOutputStream(); - HttpPrintStream http = new HttpPrintStream( msg, HttpPrintStream.HttpMessageType.REQUEST ); - http.setRequestType("M-SEARCH"); - http.setRequestURL("*"); - http.setHeader("Host", SSDPServer.SSDP_MULTICAST_ADDR +":"+ SSDPServer.SSDP_PORT ); - http.setHeader("ST", searchTarget ); - http.setHeader("Man", "\"ssdp:discover\"" ); - http.setHeader("MX", "3" ); - if(headers != null) { - for (String key : headers.keySet()) { - http.setHeader(key, headers.get(key)); - } - } - logger.log(Level.FINEST, "Sending Multicast: "+ http); - http.flush(); - - byte[] data = msg.toString().getBytes(); - DatagramPacket packet = new DatagramPacket( - data, data.length, - InetAddress.getByName( SSDPServer.SSDP_MULTICAST_ADDR ), - SSDPServer.SSDP_PORT ); - super.send( packet ); - http.close(); - } catch (Exception e) { - e.printStackTrace(); - } - } - - /** - * Set a listener that will be notified when new services are detected - */ - public void setListener(SSDPServiceListener listener){ - this.listener = listener; - } - - /** - * Returns a list of received services by - * the given search target. - * - * @param searchTarget is the search target - * @return a list of received services - */ - public LinkedList getServices(String searchTarget){ - if(services_st.get(searchTarget) == null) - return new LinkedList<>(); - return services_st.get(searchTarget); - } - - /** - * Returns the amount of services in the search target - * - * @param searchTarget is the search target - * @return the amount of services cached - */ - public int getServicesCount(String searchTarget){ - if(services_st.containsKey(searchTarget)){ - return services_st.get(searchTarget).size(); - } - return 0; - } - - /** - * 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 - */ - public StandardSSDPInfo getService(String usn){ - return services_usn.get( usn ); - } - - /** - * Clears all the received information of the services - */ - public void clearServices(){ - services_usn.clear(); - services_st.clear(); - } - /** - * Clears all services matching the search target - */ - public void clearServices(String st){ - if(services_st.get(st) != null) { - for (StandardSSDPInfo service : services_st.get(st)) { - services_usn.remove(service.getUSN()); +/* + * 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.ssdp; + +import zutil.io.StringOutputStream; +import zutil.log.LogUtil; +import zutil.net.http.HttpHeader; +import zutil.net.http.HttpHeaderParser; +import zutil.net.http.HttpPrintStream; +import zutil.net.threaded.ThreadedUDPNetwork; +import zutil.net.threaded.ThreadedUDPNetworkThread; + +import java.io.IOException; +import java.net.DatagramPacket; +import java.net.InetAddress; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * An SSDP client class that will request + * service information. + * + * @author Ziver + */ +public class SSDPClient extends ThreadedUDPNetwork implements ThreadedUDPNetworkThread{ + private static final Logger logger = LogUtil.getLogger(); + /** Mapping of search targets and list of associated services **/ + private HashMap> services_st; + /** Map of all unique services received **/ + private HashMap services_usn; + + private SSDPServiceListener listener; + + + /** + * Creates new instance of this class. An UDP + * listening socket at the SSDP port. + * + * @throws IOException + */ + public SSDPClient() throws IOException{ + super(null); + super.setThread(this); + + services_st = new HashMap<>(); + services_usn = new HashMap<>(); + } + + /** + * Sends an request for an service + * + * @param searchTarget is the SearchTarget of the service + * + * ***** REQUEST: + * M-SEARCH * HTTP/1.1 + * Host: 239.255.255.250:reservedSSDPport + * Man: "ssdp:discover" + * ST: ge:fridge + * MX: 3 + * + */ + public void requestService(String searchTarget){ + requestService(searchTarget, null); + } + public void requestService(String searchTarget, HashMap headers){ + try { + // Generate an SSDP discover message + StringOutputStream msg = new StringOutputStream(); + HttpPrintStream http = new HttpPrintStream( msg, HttpPrintStream.HttpMessageType.REQUEST ); + http.setRequestType("M-SEARCH"); + http.setRequestURL("*"); + http.setHeader("Host", SSDPServer.SSDP_MULTICAST_ADDR +":"+ SSDPServer.SSDP_PORT ); + http.setHeader("ST", searchTarget ); + http.setHeader("Man", "\"ssdp:discover\"" ); + http.setHeader("MX", "3" ); + if(headers != null) { + for (String key : headers.keySet()) { + http.setHeader(key, headers.get(key)); + } + } + logger.log(Level.FINEST, "Sending Multicast: "+ http); + http.flush(); + + byte[] data = msg.toString().getBytes(); + DatagramPacket packet = new DatagramPacket( + data, data.length, + InetAddress.getByName( SSDPServer.SSDP_MULTICAST_ADDR ), + SSDPServer.SSDP_PORT ); + super.send( packet ); + http.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * Set a listener that will be notified when new services are detected + */ + public void setListener(SSDPServiceListener listener){ + this.listener = listener; + } + + /** + * Returns a list of received services by + * the given search target. + * + * @param searchTarget is the search target + * @return a list of received services + */ + public LinkedList getServices(String searchTarget){ + if(services_st.get(searchTarget) == null) + return new LinkedList<>(); + return services_st.get(searchTarget); + } + + /** + * Returns the amount of services in the search target + * + * @param searchTarget is the search target + * @return the amount of services cached + */ + public int getServicesCount(String searchTarget){ + if(services_st.containsKey(searchTarget)){ + return services_st.get(searchTarget).size(); + } + return 0; + } + + /** + * 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 + */ + public StandardSSDPInfo getService(String usn){ + return services_usn.get( usn ); + } + + /** + * Clears all the received information of the services + */ + public void clearServices(){ + services_usn.clear(); + services_st.clear(); + } + /** + * Clears all services matching the search target + */ + public void clearServices(String st){ + if(services_st.get(st) != null) { + 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()); + 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); } - } - - /** - * 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) { - String msg = new String(packet.getData(), packet.getOffset(), packet.getLength()); - HttpHeaderParser header = new HttpHeaderParser( msg ); - 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() ); - 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); - } -} + } + + 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); + } +} diff --git a/src/zutil/net/ssdp/SSDPCustomInfo.java b/src/zutil/net/ssdp/SSDPCustomInfo.java index e907dc7..e74be91 100755 --- a/src/zutil/net/ssdp/SSDPCustomInfo.java +++ b/src/zutil/net/ssdp/SSDPCustomInfo.java @@ -1,38 +1,38 @@ -/* - * 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.ssdp; - -import zutil.net.http.HttpHeaderParser; -import zutil.net.http.HttpPrintStream; - -/** - * Created by Ziver on 2014-11-07. - */ -public interface SSDPCustomInfo extends SSDPServiceInfo{ - - public void readHeaders(HttpHeaderParser http); - - public void writeHeaders(HttpPrintStream http); -} +/* + * 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.ssdp; + +import zutil.net.http.HttpHeader; +import zutil.net.http.HttpPrintStream; + +/** + * Created by Ziver on 2014-11-07. + */ +public interface SSDPCustomInfo extends SSDPServiceInfo{ + + public void readHeaders(HttpHeader http); + + public void writeHeaders(HttpPrintStream http); +} diff --git a/src/zutil/net/ssdp/SSDPServer.java b/src/zutil/net/ssdp/SSDPServer.java index 31f765f..883646c 100755 --- a/src/zutil/net/ssdp/SSDPServer.java +++ b/src/zutil/net/ssdp/SSDPServer.java @@ -1,334 +1,336 @@ -/* - * 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.ssdp; - -import zutil.StringUtil; -import zutil.io.StringOutputStream; -import zutil.log.LogUtil; -import zutil.net.http.HttpHeaderParser; -import zutil.net.http.HttpPrintStream; -import zutil.net.threaded.ThreadedUDPNetwork; -import zutil.net.threaded.ThreadedUDPNetworkThread; - -import java.io.IOException; -import java.net.DatagramPacket; -import java.net.InetAddress; -import java.util.HashMap; -import java.util.Timer; -import java.util.TimerTask; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * A Server class that announces an service by the SSDP - * protocol specified at: - * 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 - * - * ********* Message clarification: - * ****** Incoming: - * ST: Search Target, this is object of the discovery request, (e.g., ssdp:all, etc.) - * HOST: This is the SSDP multicast address - * 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 - * SERVER: informational - * LOCATION: This is the URL to request the QueryEndpointsInterface endpoint - * USN: advertisement UUID - * CACHE-CONTROL: max-age = seconds until advertisement expires - * 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 static final String SERVER_INFO = "Zutil SSDP Java Server"; - public static final int DEFAULT_CACHE_TIME = 60*30; // 30 min - public static final int BUFFER_SIZE = 512; - 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; - private NotifyTimer notifyTimer = null; - /** HashMap that contains services as < SearchTargetName, SSDPServiceInfo > */ - private HashMap services; - - - - public SSDPServer() throws IOException{ - super( null, SSDP_MULTICAST_ADDR, SSDP_PORT ); - super.setThread( this ); - - services = new HashMap(); - - setChacheTime( DEFAULT_CACHE_TIME ); - enableNotify( true ); - } - - /** - * Adds an service that will be announced. - * - * @param service add a new service to be announced - */ - 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. - * - * @param searchTarget is the ST value in SSDP - */ - public void removeService(String searchTarget){ - sendByeBye( searchTarget ); - services.remove( searchTarget ); - } - - /** - * Sets the cache time that will be sent to - * 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 - */ - public void setChacheTime(int time){ - cache_time = time; - if( isNotifyEnabled() ){ - enableNotify(false); - enableNotify(true); - } - } - - /** - * Enable or disable notification messages to clients - * every cache_time/2 seconds - */ - public void enableNotify(boolean enable){ - if( enable && notifyTimer==null ){ - notifyTimer = new NotifyTimer(); - Timer timer = new Timer(); - timer.schedule(new NotifyTimer(), 0, cache_time*1000/2); - }else if( !enable && notifyTimer!=null ){ - notifyTimer.cancel(); - notifyTimer = null; - } - } - /** - * @return if notification messages is enabled - */ - public boolean isNotifyEnabled(){ - return notifyTimer != null; - } - - /** - * Handles the incoming packets like this: - * - * ***** REQUEST: - * M-SEARCH * HTTP/1.1 - * Host: 239.255.255.250:reservedSSDPport - * Man: "ssdp:discover" - * ST: ge:fridge - * MX: 3 - * - * ***** 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 header = new HttpHeaderParser( msg ); - - // ******* Respond - // Check that the message is an ssdp discovery message - if( header.getRequestType() != null && header.getRequestType().equalsIgnoreCase("M-SEARCH") ){ - String man = header.getHeader("Man"); - if(man != null) - man = StringUtil.trim(man, '\"'); - String st = header.getHeader("ST"); - // Check that its the correct URL and that its an ssdp:discover message - if( header.getRequestURL().equals("*") && "ssdp:discover".equalsIgnoreCase(man) ){ - // Check if the requested service exists - if( services.containsKey( st ) ){ - logger.log(Level.FINEST, "Received Multicast(from: "+packet.getAddress()+"): "+ header); - - // Generate the SSDP response - StringOutputStream response = new StringOutputStream(); - HttpPrintStream http = new HttpPrintStream( response ); - http.setStatusCode(200); - http.setHeader("Location", services.get(st).getLocation() ); - http.setHeader("USN", services.get(st).getUSN() ); - http.setHeader("Server", SERVER_INFO ); - http.setHeader("ST", st ); - http.setHeader("EXT", "" ); - http.setHeader("Cache-Control", "max-age = "+ cache_time ); - if(services.get(st) instanceof SSDPCustomInfo) - ((SSDPCustomInfo)services.get(st)).writeHeaders(http); - logger.log(Level.FINEST, "Sending Response: "+ http); - http.flush(); - - String strData = response.toString(); - byte[] data = strData.getBytes(); - packet = new DatagramPacket( - data, data.length, - packet.getAddress(), - packet.getPort()); - network.send( packet ); - http.close(); - } - } - } - } 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. - * - * @author Ziver - */ - private class NotifyTimer extends TimerTask { - public void run(){ - sendNotify(); - } - } - /** - * Sends keep-alive messages to update the cache of the clients - */ - 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 - * - * ********** Message ex: - * NOTIFY * HTTP/1.1 - * Host: 239.255.255.250:reservedSSDPport - * NT: blenderassociation:blender - * NTS: ssdp:alive - * USN: someunique:idscheme3 - * Location: http://localhost:80 - * Cache-Control: max-age = 7393 - */ - public void sendNotify(String searchTarget){ - try { - SSDPServiceInfo service = services.get(searchTarget); - // Generate the SSDP response - StringOutputStream msg = new StringOutputStream(); - HttpPrintStream http = new HttpPrintStream( msg, HttpPrintStream.HttpMessageType.REQUEST ); - http.setRequestType("NOTIFY"); - http.setRequestURL("*"); - http.setHeader("Server", SERVER_INFO ); - http.setHeader("Host", SSDP_MULTICAST_ADDR+":"+SSDP_PORT ); - http.setHeader("NT", searchTarget ); - http.setHeader("NTS", "ssdp:alive" ); - http.setHeader("Location", service.getLocation() ); - http.setHeader("Cache-Control", "max-age = "+cache_time ); - http.setHeader("USN", service.getUSN() ); - if(service instanceof SSDPCustomInfo) - ((SSDPCustomInfo) service).writeHeaders(http); - logger.log(Level.FINEST, "Sending Notification: " + http); - http.flush(); - - byte[] data = msg.toString().getBytes(); - DatagramPacket packet = new DatagramPacket( - data, data.length, - InetAddress.getByName( SSDP_MULTICAST_ADDR ), - SSDP_PORT ); - super.send( packet ); - http.close(); - - } catch (Exception e) { - logger.log(Level.SEVERE, null, e); - } - } - - - /** - * Shutdown message is sent to the clients that all - * the service is shutting down. - */ - 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 - * - * ********** Message ex: - * NOTIFY * HTTP/1.1 - * Host: 239.255.255.250:reservedSSDPport - * NT: someunique:idscheme3 - * NTS: ssdp:byebye - * USN: someunique:idscheme3 - */ - public void sendByeBye(String searchTarget){ - try { - // Generate the SSDP response - StringOutputStream msg = new StringOutputStream(); - HttpPrintStream http = new HttpPrintStream( msg, HttpPrintStream.HttpMessageType.REQUEST ); - http.setRequestType("NOTIFY"); - http.setRequestURL("*"); - http.setHeader("Server", SERVER_INFO ); - http.setHeader("Host", SSDP_MULTICAST_ADDR+":"+SSDP_PORT ); - http.setHeader("NT", searchTarget ); - http.setHeader("NTS", "ssdp:byebye" ); - http.setHeader("USN", services.get(searchTarget).getUSN() ); - logger.log(Level.FINEST, "Sending ByeBye: " + http); - http.flush(); - - byte[] data = msg.toString().getBytes(); - DatagramPacket packet = new DatagramPacket( - data, data.length, - InetAddress.getByName( SSDP_MULTICAST_ADDR ), - SSDP_PORT ); - super.send( packet ); - http.close(); - - } catch (Exception e) { - logger.log(Level.SEVERE, null, e); - } - } -} +/* + * 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.ssdp; + +import zutil.StringUtil; +import zutil.io.StringOutputStream; +import zutil.log.LogUtil; +import zutil.net.http.HttpHeader; +import zutil.net.http.HttpHeaderParser; +import zutil.net.http.HttpPrintStream; +import zutil.net.threaded.ThreadedUDPNetwork; +import zutil.net.threaded.ThreadedUDPNetworkThread; + +import java.io.IOException; +import java.net.DatagramPacket; +import java.net.InetAddress; +import java.util.HashMap; +import java.util.Timer; +import java.util.TimerTask; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * A Server class that announces an service by the SSDP + * protocol specified at: + * 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 + * + * ********* Message clarification: + * ****** Incoming: + * ST: Search Target, this is object of the discovery request, (e.g., ssdp:all, etc.) + * HOST: This is the SSDP multicast address + * 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 + * SERVER: informational + * LOCATION: This is the URL to request the QueryEndpointsInterface endpoint + * USN: advertisement UUID + * CACHE-CONTROL: max-age = seconds until advertisement expires + * 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 static final String SERVER_INFO = "Zutil SSDP Java Server"; + public static final int DEFAULT_CACHE_TIME = 60*30; // 30 min + public static final int BUFFER_SIZE = 512; + 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; + private NotifyTimer notifyTimer = null; + /** HashMap that contains services as < SearchTargetName, SSDPServiceInfo > */ + private HashMap services; + + + + public SSDPServer() throws IOException{ + super( null, SSDP_MULTICAST_ADDR, SSDP_PORT ); + super.setThread( this ); + + services = new HashMap(); + + setChacheTime( DEFAULT_CACHE_TIME ); + enableNotify( true ); + } + + /** + * Adds an service that will be announced. + * + * @param service add a new service to be announced + */ + 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. + * + * @param searchTarget is the ST value in SSDP + */ + public void removeService(String searchTarget){ + sendByeBye( searchTarget ); + services.remove( searchTarget ); + } + + /** + * Sets the cache time that will be sent to + * 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 + */ + public void setChacheTime(int time){ + cache_time = time; + if( isNotifyEnabled() ){ + enableNotify(false); + enableNotify(true); + } + } + + /** + * Enable or disable notification messages to clients + * every cache_time/2 seconds + */ + public void enableNotify(boolean enable){ + if( enable && notifyTimer==null ){ + notifyTimer = new NotifyTimer(); + Timer timer = new Timer(); + timer.schedule(new NotifyTimer(), 0, cache_time*1000/2); + }else if( !enable && notifyTimer!=null ){ + notifyTimer.cancel(); + notifyTimer = null; + } + } + /** + * @return if notification messages is enabled + */ + public boolean isNotifyEnabled(){ + return notifyTimer != null; + } + + /** + * Handles the incoming packets like this: + * + * ***** REQUEST: + * M-SEARCH * HTTP/1.1 + * Host: 239.255.255.250:reservedSSDPport + * Man: "ssdp:discover" + * ST: ge:fridge + * MX: 3 + * + * ***** 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(); + + // ******* Respond + // Check that the message is an ssdp discovery message + if( header.getRequestType() != null && header.getRequestType().equalsIgnoreCase("M-SEARCH") ){ + String man = header.getHeader("Man"); + if(man != null) + man = StringUtil.trim(man, '\"'); + String st = header.getHeader("ST"); + // Check that its the correct URL and that its an ssdp:discover message + if( header.getRequestURL().equals("*") && "ssdp:discover".equalsIgnoreCase(man) ){ + // Check if the requested service exists + if( services.containsKey( st ) ){ + logger.log(Level.FINEST, "Received Multicast(from: "+packet.getAddress()+"): "+ header); + + // Generate the SSDP response + StringOutputStream response = new StringOutputStream(); + HttpPrintStream http = new HttpPrintStream( response ); + http.setStatusCode(200); + http.setHeader("Location", services.get(st).getLocation() ); + http.setHeader("USN", services.get(st).getUSN() ); + http.setHeader("Server", SERVER_INFO ); + http.setHeader("ST", st ); + http.setHeader("EXT", "" ); + http.setHeader("Cache-Control", "max-age = "+ cache_time ); + if(services.get(st) instanceof SSDPCustomInfo) + ((SSDPCustomInfo)services.get(st)).writeHeaders(http); + logger.log(Level.FINEST, "Sending Response: "+ http); + http.flush(); + + String strData = response.toString(); + byte[] data = strData.getBytes(); + packet = new DatagramPacket( + data, data.length, + packet.getAddress(), + packet.getPort()); + network.send( packet ); + http.close(); + } + } + } + } 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. + * + * @author Ziver + */ + private class NotifyTimer extends TimerTask { + public void run(){ + sendNotify(); + } + } + /** + * Sends keep-alive messages to update the cache of the clients + */ + 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 + * + * ********** Message ex: + * NOTIFY * HTTP/1.1 + * Host: 239.255.255.250:reservedSSDPport + * NT: blenderassociation:blender + * NTS: ssdp:alive + * USN: someunique:idscheme3 + * Location: http://localhost:80 + * Cache-Control: max-age = 7393 + */ + public void sendNotify(String searchTarget){ + try { + SSDPServiceInfo service = services.get(searchTarget); + // Generate the SSDP response + StringOutputStream msg = new StringOutputStream(); + HttpPrintStream http = new HttpPrintStream( msg, HttpPrintStream.HttpMessageType.REQUEST ); + http.setRequestType("NOTIFY"); + http.setRequestURL("*"); + http.setHeader("Server", SERVER_INFO ); + http.setHeader("Host", SSDP_MULTICAST_ADDR+":"+SSDP_PORT ); + http.setHeader("NT", searchTarget ); + http.setHeader("NTS", "ssdp:alive" ); + http.setHeader("Location", service.getLocation() ); + http.setHeader("Cache-Control", "max-age = "+cache_time ); + http.setHeader("USN", service.getUSN() ); + if(service instanceof SSDPCustomInfo) + ((SSDPCustomInfo) service).writeHeaders(http); + logger.log(Level.FINEST, "Sending Notification: " + http); + http.flush(); + + byte[] data = msg.toString().getBytes(); + DatagramPacket packet = new DatagramPacket( + data, data.length, + InetAddress.getByName( SSDP_MULTICAST_ADDR ), + SSDP_PORT ); + super.send( packet ); + http.close(); + + } catch (Exception e) { + logger.log(Level.SEVERE, null, e); + } + } + + + /** + * Shutdown message is sent to the clients that all + * the service is shutting down. + */ + 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 + * + * ********** Message ex: + * NOTIFY * HTTP/1.1 + * Host: 239.255.255.250:reservedSSDPport + * NT: someunique:idscheme3 + * NTS: ssdp:byebye + * USN: someunique:idscheme3 + */ + public void sendByeBye(String searchTarget){ + try { + // Generate the SSDP response + StringOutputStream msg = new StringOutputStream(); + HttpPrintStream http = new HttpPrintStream( msg, HttpPrintStream.HttpMessageType.REQUEST ); + http.setRequestType("NOTIFY"); + http.setRequestURL("*"); + http.setHeader("Server", SERVER_INFO ); + http.setHeader("Host", SSDP_MULTICAST_ADDR+":"+SSDP_PORT ); + http.setHeader("NT", searchTarget ); + http.setHeader("NTS", "ssdp:byebye" ); + http.setHeader("USN", services.get(searchTarget).getUSN() ); + logger.log(Level.FINEST, "Sending ByeBye: " + http); + http.flush(); + + byte[] data = msg.toString().getBytes(); + DatagramPacket packet = new DatagramPacket( + data, data.length, + InetAddress.getByName( SSDP_MULTICAST_ADDR ), + SSDP_PORT ); + super.send( packet ); + http.close(); + + } catch (Exception e) { + logger.log(Level.SEVERE, null, e); + } + } +} diff --git a/src/zutil/net/ssdp/SSDPServiceInfo.java b/src/zutil/net/ssdp/SSDPServiceInfo.java index 6a8203b..4131957 100755 --- a/src/zutil/net/ssdp/SSDPServiceInfo.java +++ b/src/zutil/net/ssdp/SSDPServiceInfo.java @@ -24,8 +24,6 @@ package zutil.net.ssdp; -import java.net.InetAddress; - /** * This class contains information about a service from * or through the SSDP protocol diff --git a/src/zutil/net/ssdp/StandardSSDPInfo.java b/src/zutil/net/ssdp/StandardSSDPInfo.java index 090caeb..644a85d 100755 --- a/src/zutil/net/ssdp/StandardSSDPInfo.java +++ b/src/zutil/net/ssdp/StandardSSDPInfo.java @@ -1,161 +1,164 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2015 Ziver Koc - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -package zutil.net.ssdp; - -import zutil.net.http.HttpHeaderParser; -import zutil.net.http.HttpPrintStream; - -import java.io.IOException; -import java.net.InetAddress; -import java.util.Date; -import java.util.HashMap; -import java.util.UUID; - -/** - * This class contains information about a service from - * or through the SSDP protocol - * - * @author Ziver - */ -public class StandardSSDPInfo implements SSDPServiceInfo, SSDPCustomInfo{ - private String location; - private String st; - private String usn; - private long expiration_time; - // All header parameters - private HashMap headers = new HashMap<>(); - private InetAddress inetAddress; - - /** - * @param l is the value to set the Location variable - */ - public void setLocation(String l) { - location = l; - } - - /** - * @param st is the value to set the SearchTarget variable - */ - public void setST(String st) { - this.st = st; - } - - /** - * @param usn is the value to set the USN variable - */ - protected void setUSN(String usn) { - this.usn = usn; - } - - /** - * @param time sets the expiration time of values in this object - */ - protected void setExpirationTime(long time) { - expiration_time = time; - } - - /** - * @return The URL to the Service, e.g. "http://192.168.0.1:80/index.html" - */ - public String getLocation(){ - return location; - } - - /** - * @return the Search Target, e.g. "upnp:rootdevice" - */ - public String getSearchTarget(){ - return st; - } - - /** - * @return the expiration time for the values in this object - */ - public long getExpirationTime(){ - return expiration_time; - } - - /** - * @return the USN value, e.g. "uuid:abcdefgh-7dec-11d0-a765-00a0c91e6bf6 " - */ - public String getUSN(){ - if( usn==null ) - usn = genUSN(); - return usn+"::"+st; - } - - /** - * @return only the USN UUID String - */ - public String getUUID(){ - if( usn==null ) - usn = genUSN(); - return usn; - } - - /** - * Generates an unique USN for the service - * - * @return an unique string that corresponds to the service - */ - 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 void setHeader(String key, String value) { - headers.put(key, value); - } - public String getHeader(String header){ - return headers.get(header); - } - @Override - public void writeHeaders(HttpPrintStream http) { - try { - for (String key : headers.keySet()) - http.setHeader(key, headers.get(key)); - }catch(IOException e){ - e.printStackTrace(); - } - } - @Override - public void readHeaders(HttpHeaderParser http) { - HashMap httpHeaders = http.getHeaders(); - for (String key : httpHeaders.keySet()) - headers.put(key, httpHeaders.get(key)); - } - - public InetAddress getInetAddress(){ - return inetAddress; - } - - protected void setInetAddress(InetAddress inetAddress) { - this.inetAddress = inetAddress; - } -} +/* + * 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.ssdp; + +import zutil.net.http.HttpHeader; +import zutil.net.http.HttpPrintStream; + +import java.io.IOException; +import java.net.InetAddress; +import java.util.Date; +import java.util.HashMap; +import java.util.Iterator; +import java.util.UUID; + +/** + * This class contains information about a service from + * or through the SSDP protocol + * + * @author Ziver + */ +public class StandardSSDPInfo implements SSDPServiceInfo, SSDPCustomInfo{ + private String location; + private String st; + private String usn; + private long expiration_time; + // All header parameters + private HashMap headers = new HashMap<>(); + private InetAddress inetAddress; + + /** + * @param l is the value to set the Location variable + */ + public void setLocation(String l) { + location = l; + } + + /** + * @param st is the value to set the SearchTarget variable + */ + public void setST(String st) { + this.st = st; + } + + /** + * @param usn is the value to set the USN variable + */ + protected void setUSN(String usn) { + this.usn = usn; + } + + /** + * @param time sets the expiration time of values in this object + */ + protected void setExpirationTime(long time) { + expiration_time = time; + } + + /** + * @return The URL to the Service, e.g. "http://192.168.0.1:80/index.html" + */ + public String getLocation(){ + return location; + } + + /** + * @return the Search Target, e.g. "upnp:rootdevice" + */ + public String getSearchTarget(){ + return st; + } + + /** + * @return the expiration time for the values in this object + */ + public long getExpirationTime(){ + return expiration_time; + } + + /** + * @return the USN value, e.g. "uuid:abcdefgh-7dec-11d0-a765-00a0c91e6bf6 " + */ + public String getUSN(){ + if( usn==null ) + usn = genUSN(); + return usn+"::"+st; + } + + /** + * @return only the USN UUID String + */ + public String getUUID(){ + if( usn==null ) + usn = genUSN(); + return usn; + } + + /** + * Generates an unique USN for the service + * + * @return an unique string that corresponds to the service + */ + 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 void setHeader(String key, String value) { + headers.put(key, value); + } + public String getHeader(String header){ + return headers.get(header); + } + @Override + public void writeHeaders(HttpPrintStream http) { + try { + for (String key : headers.keySet()) + http.setHeader(key, headers.get(key)); + }catch(IOException e){ + e.printStackTrace(); + } + } + @Override + public void readHeaders(HttpHeader header) { + Iterator it = header.getHeaderKeys(); + while (it.hasNext()) { + String key = it.next(); + headers.put(key, header.getHeader(key)); + } + } + + public InetAddress getInetAddress(){ + return inetAddress; + } + + protected void setInetAddress(InetAddress inetAddress) { + this.inetAddress = inetAddress; + } +} diff --git a/src/zutil/net/upnp/UPnPMediaServer.java b/src/zutil/net/upnp/UPnPMediaServer.java old mode 100644 new mode 100755 index 32f5b2b..02be448 --- a/src/zutil/net/upnp/UPnPMediaServer.java +++ b/src/zutil/net/upnp/UPnPMediaServer.java @@ -1,115 +1,117 @@ -/* - * 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.upnp; - -import zutil.net.http.HttpHeaderParser; -import zutil.net.http.HttpPrintStream; - -import java.io.IOException; -import java.util.Map; -import java.util.UUID; - -/** - * This class is a UPnP AV Media Server that handles all the - * other UPnP services - * - * @author Ziver - */ -public class UPnPMediaServer extends UPnPRootDevice{ - public static final String RELATIVE_URL = "upnp/rootdev"; - - private String url; - private String uuid; - - public UPnPMediaServer(String location){ - url = location; - } - - public void respond(HttpPrintStream out, HttpHeaderParser clientInfo, - Map session, Map cookie, - Map request) throws IOException { - - out.enableBuffering(true); - out.setHeader("Content-Type", "text/xml"); - - out.println(""); - out.println(""); - out.println(" "); - out.println(" 1"); - out.println(" 0"); - out.println(" "); - out.println(" "+url+"");//"+ssdp.getLocation()+" - out.println(" "); - out.println(" urn:schemas-upnp-org:device:MediaServer:1"); - out.println(" ZupNP AV Media Server"); - out.println(" Ziver Koc"); - out.println(" http://ziver.koc.se"); - out.println(""); - out.println(" ZupNP Server"); - out.println(" UPnP AV Media Server"); - out.println(" 0.1"); - out.println(" "+getUUID()+""); - out.println(" "); - out.println(" "); - out.println(" urn:schemas-upnp-org:service:ConnectionManager:1"); - out.println(" urn:upnp-org:serviceId:CMGR_1-0"); - out.println(" CMGR_Control/GetServDesc"); - out.println(" CMGR_Control"); - out.println(" CMGR_Event"); - out.println(" "); - out.println(" "); - out.println(" urn:schemas-upnp-org:service:ContentDirectory:1"); - out.println(" urn:upnp-org:serviceId:CDS_1-0"); - out.println(" SCP/ContentDir"); - out.println(" Action/ContentDir"); - out.println(" Event/ContentDir"); - out.println(" "); - out.println(" "); - out.println(" "); - out.println(""); - out.flush(); - } - - - public long getExpirationTime() { - return 60*30; // 30min - } - public String getLocation() { - return url+"RootDesc"; - } - public String getSearchTarget() { - return "upnp:rootdevice"; - } - public String getUSN() { - return getUUID()+"::upnp:rootdevice"; - } - public String getUUID() { - if(uuid==null){ - uuid = "uuid:"+UUID.nameUUIDFromBytes( this.getClass().toString().getBytes() ); //(url+Math.random()).getBytes() - } - return uuid; - } - -} +/* + * 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.upnp; + +import zutil.net.http.HttpHeader; +import zutil.net.http.HttpPrintStream; + +import java.io.IOException; +import java.util.Map; +import java.util.UUID; + +/** + * This class is a UPnP AV Media Server that handles all the + * other UPnP services + * + * @author Ziver + */ +public class UPnPMediaServer extends UPnPRootDevice{ + public static final String RELATIVE_URL = "upnp/rootdev"; + + private String url; + private String uuid; + + public UPnPMediaServer(String location){ + url = location; + } + + public void respond(HttpPrintStream out, + HttpHeader headers, + Map session, + Map cookie, + Map request) throws IOException { + + out.enableBuffering(true); + out.setHeader("Content-Type", "text/xml"); + + out.println(""); + out.println(""); + out.println(" "); + out.println(" 1"); + out.println(" 0"); + out.println(" "); + out.println(" "+url+"");//"+ssdp.getLocation()+" + out.println(" "); + out.println(" urn:schemas-upnp-org:device:MediaServer:1"); + out.println(" ZupNP AV Media Server"); + out.println(" Ziver Koc"); + out.println(" http://ziver.koc.se"); + out.println(""); + out.println(" ZupNP Server"); + out.println(" UPnP AV Media Server"); + out.println(" 0.1"); + out.println(" "+getUUID()+""); + out.println(" "); + out.println(" "); + out.println(" urn:schemas-upnp-org:service:ConnectionManager:1"); + out.println(" urn:upnp-org:serviceId:CMGR_1-0"); + out.println(" CMGR_Control/GetServDesc"); + out.println(" CMGR_Control"); + out.println(" CMGR_Event"); + out.println(" "); + out.println(" "); + out.println(" urn:schemas-upnp-org:service:ContentDirectory:1"); + out.println(" urn:upnp-org:serviceId:CDS_1-0"); + out.println(" SCP/ContentDir"); + out.println(" Action/ContentDir"); + out.println(" Event/ContentDir"); + out.println(" "); + out.println(" "); + out.println(" "); + out.println(""); + out.flush(); + } + + + public long getExpirationTime() { + return 60*30; // 30min + } + public String getLocation() { + return url+"RootDesc"; + } + public String getSearchTarget() { + return "upnp:rootdevice"; + } + public String getUSN() { + return getUUID()+"::upnp:rootdevice"; + } + public String getUUID() { + if(uuid==null){ + uuid = "uuid:"+UUID.nameUUIDFromBytes( this.getClass().toString().getBytes() ); //(url+Math.random()).getBytes() + } + return uuid; + } + +} diff --git a/src/zutil/net/upnp/services/UPnPContentDirectory.java b/src/zutil/net/upnp/services/UPnPContentDirectory.java old mode 100644 new mode 100755 index 9cabd19..863d9c4 --- a/src/zutil/net/upnp/services/UPnPContentDirectory.java +++ b/src/zutil/net/upnp/services/UPnPContentDirectory.java @@ -1,615 +1,616 @@ -/* - * 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.upnp.services; - -import org.dom4j.DocumentException; -import zutil.io.file.FileUtil; -import zutil.net.http.HttpHeaderParser; -import zutil.net.http.HttpPage; -import zutil.net.http.HttpPrintStream; -import zutil.net.upnp.UPnPService; -import zutil.net.ws.WSInterface; -import zutil.net.ws.WSReturnObject; - -import java.io.File; -import java.io.IOException; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; - -/** - * Information about a UPNP Service - * - * @author Ziver - */ -public class UPnPContentDirectory implements UPnPService, HttpPage, WSInterface { - public static final int VERSION = 1; - - private static List file_list; - - public UPnPContentDirectory(){} - - public UPnPContentDirectory(File dir){ - file_list = FileUtil.search(dir, new LinkedList(), true, Integer.MAX_VALUE); - } - - /** - * This action returns the searching capabilities - * that are supported by the device. - * - */ - @WSReturnName("SortCaps") - public String GetSearchCapabilities(){ - // "dc:title,res@size" - return ""; - } - - /** - * Returns the CSV list of meta-data tags that can - * be used in sortCriteria - * - */ - @WSReturnName("SortCaps") - public String GetSortCapabilities(){ - return "dc:title"; - } - - /** - * This action returns the current value of state variable - * SystemUpdateID. It can be used by clients that want to - * 'poll' for any changes in the Content Directory - * (as opposed to subscribing to events). - * - */ - @WSReturnName("Id") - public int GetSystemUpdateID(){ - return 0; - } - - /** - * This action allows the caller to incrementally browse - * the native hierarchy of the Content Directory objects - * exposed by the Content Directory Service, including - * information listing the classes of objects available - * in any particular object container. - * @throws DocumentException - * - */ - //@WSNameSpace("urn:schemas-upnp-org:service:ContentDirectory:1") - public BrowseRetObj Browse( - @WSParamName("ObjectID") String ObjectID, - @WSParamName("BrowseFlag") String BrowseFlag, - @WSParamName("Filter") String Filter, - @WSParamName("StartingIndex") int StartingIndex, - @WSParamName("RequestedCount") int RequestedCount, - @WSParamName("SortCriteria") String SortCriteria) throws DocumentException{ - - BrowseRetObj ret = new BrowseRetObj(); - if( BrowseFlag.equals("BrowseMetadata") ){ - - } - else if( BrowseFlag.equals("BrowseDirectChildren") ){ - StringBuffer xml = new StringBuffer(); - xml.append( "" ); - List tmp = FileUtil.search( file_list.get(Integer.parseInt(ObjectID)), new LinkedList(), false ); - for(File file : tmp){ - xml.append(" "); - - xml.append(" "+file.getName()+" "); - if( file.isDirectory() ) - xml.append(" object.container.storageFolder "); - else - xml.append(" object.container "); - xml.append(" "+(int)(file.length()/1000)+" "); - xml.append(" "); - - ret.NumberReturned++; - ret.TotalMatches++; - } - xml.append( "" ); - - ret.Result = xml.toString(); - //Document document = DocumentHelper.parseText( xml.toString() ); - //ret.Result = document.getRootElement(); - } - return ret; - } - public class BrowseRetObj extends WSReturnObject{ - @WSValueName("Result") - public String Result; - @WSValueName("NumberReturned") - public int NumberReturned; - @WSValueName("TotalMatches") - public int TotalMatches; - @WSValueName("UpdateID") - public int UpdateID; - } - - - @WSDisabled - public void respond(HttpPrintStream out, HttpHeaderParser clientInfo, - Map session, Map cookie, - Map request) throws IOException { - - out.enableBuffering(true); - out.setHeader("Content-Type", "text/xml"); - - out.println(""); - out.println(""); - out.println(" "); - out.println(" "); - out.println(" "); - out.println(" TransferIDs"); - out.println(" yes"); - out.println(" string"); - out.println(" "); - out.println(" "); - out.println(" A_ARG_TYPE_ObjectID"); - out.println(" no"); - out.println(" string"); - out.println(" "); - out.println(" "); - out.println(" A_ARG_TYPE_Result"); - out.println(" no"); - out.println(" string"); - out.println(" "); - out.println(" "); - out.println(" "); - out.println(" A_ARG_TYPE_SearchCriteria"); - out.println(" no"); - out.println(" string"); - out.println(" "); - out.println(" "); - out.println(" A_ARG_TYPE_BrowseFlag"); - out.println(" no"); - out.println(" string"); - out.println(" "); - out.println(" BrowseMetadata"); - out.println(" BrowseDirectChildren"); - out.println(" "); - out.println(" "); - out.println(" "); - out.println(" A_ARG_TYPE_Filter"); - out.println(" no"); - out.println(" string"); - out.println(" "); - out.println(" "); - out.println(" A_ARG_TYPE_SortCriteria"); - out.println(" no"); - out.println(" string"); - out.println(" "); - out.println(" "); - out.println(" A_ARG_TYPE_Index"); - out.println(" no"); - out.println(" ui4"); - out.println(" "); - out.println(" "); - out.println(" A_ARG_TYPE_Count"); - out.println(" no"); - out.println(" ui4"); - out.println(" "); - out.println(" "); - out.println(" A_ARG_TYPE_UpdateID"); - out.println(" no"); - out.println(" ui4"); - out.println(" "); - out.println(" "); - out.println(" "); - out.println(" A_ARG_TYPE_TransferID"); - out.println(" no"); - out.println(" ui4"); - out.println(" "); - out.println(" "); - out.println(" "); - out.println(" A_ARG_TYPE_TransferStatus"); - out.println(" no"); - out.println(" string"); - out.println(" "); - out.println(" COMPLETED"); - out.println(" ERROR"); - out.println(" IN_PROGRESS"); - out.println(" STOPPED"); - out.println(" "); - out.println(" "); - out.println(" "); - out.println(" "); - out.println(" A_ARG_TYPE_TransferLength"); - out.println(" no"); - out.println(" string"); - out.println(" "); - out.println(" "); - out.println(" "); - out.println(" A_ARG_TYPE_TransferTotal"); - out.println(" no"); - out.println(" string"); - out.println(" "); - out.println(" "); - out.println(" "); - out.println(" A_ARG_TYPE_TagValueList"); - out.println(" no"); - out.println(" string"); - out.println(" "); - out.println(" "); - out.println(" "); - out.println(" A_ARG_TYPE_URI"); - out.println(" no"); - out.println(" uri"); - out.println(" "); - out.println(" "); - out.println(" SearchCapabilities"); - out.println(" no"); - out.println(" string"); - out.println(" "); - out.println(" "); - out.println(" SortCapabilities"); - out.println(" no"); - out.println(" string"); - out.println(" "); - out.println(" "); - out.println(" SystemUpdateID"); - out.println(" yes"); - out.println(" ui4"); - out.println(" "); - out.println(" "); - out.println(" "); - out.println(" ContainerUpdateIDs"); - out.println(" yes"); - out.println(" string"); - out.println(" "); - out.println(" "); - - - out.println(" "); - out.println(" "); - out.println(" GetSearchCapabilities"); - out.println(" "); - out.println(" "); - out.println(" SearchCaps"); - out.println(" out"); - out.println(" SearchCapabilities"); - out.println(" "); - out.println(" "); - out.println(" "); - out.println(" "); - out.println(" GetSortCapabilities"); - out.println(" "); - out.println(" "); - out.println(" SortCaps"); - out.println(" out"); - out.println(" SortCapabilities"); - out.println(" "); - out.println(" "); - out.println(" "); - out.println(" "); - out.println(" GetSystemUpdateID"); - out.println(" "); - out.println(" "); - out.println(" Id"); - out.println(" out"); - out.println(" SystemUpdateID"); - out.println(" "); - out.println(" "); - out.println(" "); - - out.println(" "); - out.println(" Browse"); - out.println(" "); - out.println(" "); - out.println(" ObjectID"); - out.println(" in"); - out.println(" A_ARG_TYPE_ObjectID"); - out.println(" "); - out.println(" "); - out.println(" BrowseFlag"); - out.println(" in"); - out.println(" A_ARG_TYPE_BrowseFlag"); - out.println(" "); - out.println(" "); - out.println(" Filter"); - out.println(" in"); - out.println(" A_ARG_TYPE_Filter"); - out.println(" "); - out.println(" "); - out.println(" StartingIndex"); - out.println(" in"); - out.println(" A_ARG_TYPE_Index"); - out.println(" "); - out.println(" "); - out.println(" RequestedCount"); - out.println(" in"); - out.println(" A_ARG_TYPE_Count"); - out.println(" "); - out.println(" "); - out.println(" SortCriteria"); - out.println(" in"); - out.println(" A_ARG_TYPE_SortCriteria"); - out.println(" "); - out.println(" "); - out.println(" Result"); - out.println(" out"); - out.println(" A_ARG_TYPE_Result"); - out.println(" "); - out.println(" "); - out.println(" NumberReturned"); - out.println(" out"); - out.println(" A_ARG_TYPE_Count"); - out.println(" "); - out.println(" "); - out.println(" TotalMatches"); - out.println(" out"); - out.println(" A_ARG_TYPE_Count"); - out.println(" "); - out.println(" "); - out.println(" UpdateID"); - out.println(" out"); - out.println(" A_ARG_TYPE_UpdateID"); - out.println(" "); - out.println(" "); - out.println(" "); - - /*out.println(" "); - out.println(" "); - out.println(" Search"); - out.println(" "); - out.println(" "); - out.println(" ContainerID"); - out.println(" in"); - out.println(" A_ARG_TYPE_ObjectID"); - out.println(" "); - out.println(" "); - out.println(" SearchCriteria"); - out.println(" in"); - out.println(" A_ARG_TYPE_SearchCriteria "); - out.println(" "); - out.println(" "); - out.println(" Filter"); - out.println(" in"); - out.println(" A_ARG_TYPE_Filter"); - out.println(" "); - out.println(" "); - out.println(" StartingIndex"); - out.println(" in"); - out.println(" A_ARG_TYPE_Index"); - out.println(" "); - out.println(" "); - out.println(" RequestedCount"); - out.println(" in"); - out.println(" A_ARG_TYPE_Count"); - out.println(" "); - out.println(" "); - out.println(" SortCriteria"); - out.println(" in"); - out.println(" A_ARG_TYPE_SortCriteria"); - out.println(" "); - out.println(" "); - out.println(" Result"); - out.println(" out"); - out.println(" A_ARG_TYPE_Result"); - out.println(" "); - out.println(" "); - out.println(" NumberReturned"); - out.println(" out"); - out.println(" A_ARG_TYPE_Count"); - out.println(" "); - out.println(" "); - out.println(" TotalMatches"); - out.println(" out"); - out.println(" A_ARG_TYPE_Count"); - out.println(" "); - out.println(" "); - out.println(" UpdateID"); - out.println(" out"); - out.println(" A_ARG_TYPE_UpdateID"); - out.println(" "); - out.println(" "); - out.println(" ");*/ - - /*out.println(" "); - out.println(" "); - out.println(" CreateObject"); - out.println(" "); - out.println(" "); - out.println(" ContainerID"); - out.println(" in"); - out.println(" A_ARG_TYPE_ObjectID"); - out.println(" "); - out.println(" "); - out.println(" Elements"); - out.println(" in"); - out.println(" A_ARG_TYPE_Result"); - out.println(" "); - out.println(" "); - out.println(" ObjectID"); - out.println(" out"); - out.println(" A_ARG_TYPE_ObjectID"); - out.println(" "); - out.println(" "); - out.println(" Result"); - out.println(" out"); - out.println(" A_ARG_TYPE_Result"); - out.println(" "); - out.println(" "); - out.println(" ");*/ - - /*out.println(" "); - out.println(" "); - out.println(" DestroyObject"); - out.println(" "); - out.println(" "); - out.println(" ObjectID"); - out.println(" in"); - out.println(" A_ARG_TYPE_ObjectID"); - out.println(" "); - out.println(" "); - out.println(" ");*/ - - /*out.println(" "); - out.println(" "); - out.println(" UpdateObject"); - out.println(" "); - out.println(" "); - out.println(" ObjectID"); - out.println(" in"); - out.println(" A_ARG_TYPE_ObjectID"); - out.println(" "); - out.println(" "); - out.println(" CurrentTagValue"); - out.println(" in"); - out.println(" A_ARG_TYPE_TagValueList "); - out.println(" "); - out.println(" "); - out.println(" NewTagValue"); - out.println(" in"); - out.println(" A_ARG_TYPE_TagValueList "); - out.println(" "); - out.println(" "); - out.println(" ");*/ - - /*out.println(" "); - out.println(" "); - out.println(" ImportResource"); - out.println(" "); - out.println(" "); - out.println(" SourceURI"); - out.println(" in"); - out.println(" A_ARG_TYPE_URI"); - out.println(" "); - out.println(" "); - out.println(" DestinationURI"); - out.println(" in"); - out.println(" A_ARG_TYPE_URI"); - out.println(" "); - out.println(" "); - out.println(" TransferID"); - out.println(" out"); - out.println(" A_ARG_TYPE_TransferID "); - out.println(" "); - out.println(" "); - out.println(" ");*/ - - /*out.println(" "); - out.println(" "); - out.println(" ExportResource"); - out.println(" "); - out.println(" "); - out.println(" SourceURI"); - out.println(" in"); - out.println(" A_ARG_TYPE_URI"); - out.println(" "); - out.println(" "); - out.println(" DestinationURI"); - out.println(" in"); - out.println(" A_ARG_TYPE_URI"); - out.println(" "); - out.println(" "); - out.println(" TransferID"); - out.println(" out"); - out.println(" A_ARG_TYPE_TransferID "); - out.println(" "); - out.println(" "); - out.println(" ");*/ - - /*out.println(" "); - out.println(" "); - out.println(" StopTransferResource"); - out.println(" "); - out.println(" "); - out.println(" TransferID"); - out.println(" in"); - out.println(" A_ARG_TYPE_TransferID "); - out.println(" "); - out.println(" "); - out.println(" ");*/ - - /*out.println(" "); - out.println(" "); - out.println(" GetTransferProgress"); - out.println(" "); - out.println(" "); - out.println(" TransferID"); - out.println(" in"); - out.println(" A_ARG_TYPE_TransferID "); - out.println(" "); - out.println(" "); - out.println(" TransferStatus"); - out.println(" out"); - out.println(" A_ARG_TYPE_TransferStatus "); - out.println(" "); - out.println(" "); - out.println(" TransferLength"); - out.println(" out"); - out.println(" A_ARG_TYPE_TransferLength "); - out.println(" "); - out.println(" "); - out.println(" TransferTotal"); - out.println(" out"); - out.println(" A_ARG_TYPE_TransferTotal"); - out.println(" "); - out.println(" "); - out.println(" ");*/ - - /*out.println(" "); - out.println(" "); - out.println(" DeleteResource"); - out.println(" "); - out.println(" "); - out.println(" ResourceURI"); - out.println(" in"); - out.println(" A_ARG_TYPE_URI"); - out.println(" "); - out.println(" "); - out.println(" ");*/ - - /*out.println(" "); - out.println(" "); - out.println(" CreateReference"); - out.println(" "); - out.println(" "); - out.println(" ContainerID"); - out.println(" in"); - out.println(" A_ARG_TYPE_ObjectID"); - out.println(" "); - out.println(" "); - out.println(" ObjectID"); - out.println(" in"); - out.println(" A_ARG_TYPE_ObjectID"); - out.println(" "); - out.println(" "); - out.println(" NewID"); - out.println(" out"); - out.println(" A_ARG_TYPE_ObjectID"); - out.println(" "); - out.println(" "); - out.println(" ");*/ - - out.println(" "); - out.println(""); - } -} +/* + * 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.upnp.services; + +import org.dom4j.DocumentException; +import zutil.io.file.FileUtil; +import zutil.net.http.HttpHeader; +import zutil.net.http.HttpPage; +import zutil.net.http.HttpPrintStream; +import zutil.net.upnp.UPnPService; +import zutil.net.ws.WSInterface; +import zutil.net.ws.WSReturnObject; + +import java.io.File; +import java.io.IOException; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +/** + * Information about a UPNP Service + * + * @author Ziver + */ +public class UPnPContentDirectory implements UPnPService, HttpPage, WSInterface { + public static final int VERSION = 1; + + private static List file_list; + + public UPnPContentDirectory(){} + + public UPnPContentDirectory(File dir){ + file_list = FileUtil.search(dir, new LinkedList(), true, Integer.MAX_VALUE); + } + + /** + * This action returns the searching capabilities + * that are supported by the device. + * + */ + @WSReturnName("SortCaps") + public String GetSearchCapabilities(){ + // "dc:title,res@size" + return ""; + } + + /** + * Returns the CSV list of meta-data tags that can + * be used in sortCriteria + * + */ + @WSReturnName("SortCaps") + public String GetSortCapabilities(){ + return "dc:title"; + } + + /** + * This action returns the current value of state variable + * SystemUpdateID. It can be used by clients that want to + * 'poll' for any changes in the Content Directory + * (as opposed to subscribing to events). + * + */ + @WSReturnName("Id") + public int GetSystemUpdateID(){ + return 0; + } + + /** + * This action allows the caller to incrementally browse + * the native hierarchy of the Content Directory objects + * exposed by the Content Directory Service, including + * information listing the classes of objects available + * in any particular object container. + * @throws DocumentException + * + */ + //@WSNameSpace("urn:schemas-upnp-org:service:ContentDirectory:1") + public BrowseRetObj Browse( + @WSParamName("ObjectID") String ObjectID, + @WSParamName("BrowseFlag") String BrowseFlag, + @WSParamName("Filter") String Filter, + @WSParamName("StartingIndex") int StartingIndex, + @WSParamName("RequestedCount") int RequestedCount, + @WSParamName("SortCriteria") String SortCriteria) throws DocumentException{ + + BrowseRetObj ret = new BrowseRetObj(); + if( BrowseFlag.equals("BrowseMetadata") ){ + + } + else if( BrowseFlag.equals("BrowseDirectChildren") ){ + StringBuffer xml = new StringBuffer(); + xml.append( "" ); + List tmp = FileUtil.search( file_list.get(Integer.parseInt(ObjectID)), new LinkedList(), false ); + for(File file : tmp){ + xml.append(" "); + + xml.append(" "+file.getName()+" "); + if( file.isDirectory() ) + xml.append(" object.container.storageFolder "); + else + xml.append(" object.container "); + xml.append(" "+(int)(file.length()/1000)+" "); + xml.append(" "); + + ret.NumberReturned++; + ret.TotalMatches++; + } + xml.append( "" ); + + ret.Result = xml.toString(); + //Document document = DocumentHelper.parseText( xml.toString() ); + //ret.Result = document.getRootElement(); + } + return ret; + } + public class BrowseRetObj extends WSReturnObject{ + @WSValueName("Result") + public String Result; + @WSValueName("NumberReturned") + public int NumberReturned; + @WSValueName("TotalMatches") + public int TotalMatches; + @WSValueName("UpdateID") + public int UpdateID; + } + + + @WSDisabled + public void respond(HttpPrintStream out, HttpHeader headers, + Map session, + Map cookie, + Map request) throws IOException { + + out.enableBuffering(true); + out.setHeader("Content-Type", "text/xml"); + + out.println(""); + out.println(""); + out.println(" "); + out.println(" "); + out.println(" "); + out.println(" TransferIDs"); + out.println(" yes"); + out.println(" string"); + out.println(" "); + out.println(" "); + out.println(" A_ARG_TYPE_ObjectID"); + out.println(" no"); + out.println(" string"); + out.println(" "); + out.println(" "); + out.println(" A_ARG_TYPE_Result"); + out.println(" no"); + out.println(" string"); + out.println(" "); + out.println(" "); + out.println(" "); + out.println(" A_ARG_TYPE_SearchCriteria"); + out.println(" no"); + out.println(" string"); + out.println(" "); + out.println(" "); + out.println(" A_ARG_TYPE_BrowseFlag"); + out.println(" no"); + out.println(" string"); + out.println(" "); + out.println(" BrowseMetadata"); + out.println(" BrowseDirectChildren"); + out.println(" "); + out.println(" "); + out.println(" "); + out.println(" A_ARG_TYPE_Filter"); + out.println(" no"); + out.println(" string"); + out.println(" "); + out.println(" "); + out.println(" A_ARG_TYPE_SortCriteria"); + out.println(" no"); + out.println(" string"); + out.println(" "); + out.println(" "); + out.println(" A_ARG_TYPE_Index"); + out.println(" no"); + out.println(" ui4"); + out.println(" "); + out.println(" "); + out.println(" A_ARG_TYPE_Count"); + out.println(" no"); + out.println(" ui4"); + out.println(" "); + out.println(" "); + out.println(" A_ARG_TYPE_UpdateID"); + out.println(" no"); + out.println(" ui4"); + out.println(" "); + out.println(" "); + out.println(" "); + out.println(" A_ARG_TYPE_TransferID"); + out.println(" no"); + out.println(" ui4"); + out.println(" "); + out.println(" "); + out.println(" "); + out.println(" A_ARG_TYPE_TransferStatus"); + out.println(" no"); + out.println(" string"); + out.println(" "); + out.println(" COMPLETED"); + out.println(" ERROR"); + out.println(" IN_PROGRESS"); + out.println(" STOPPED"); + out.println(" "); + out.println(" "); + out.println(" "); + out.println(" "); + out.println(" A_ARG_TYPE_TransferLength"); + out.println(" no"); + out.println(" string"); + out.println(" "); + out.println(" "); + out.println(" "); + out.println(" A_ARG_TYPE_TransferTotal"); + out.println(" no"); + out.println(" string"); + out.println(" "); + out.println(" "); + out.println(" "); + out.println(" A_ARG_TYPE_TagValueList"); + out.println(" no"); + out.println(" string"); + out.println(" "); + out.println(" "); + out.println(" "); + out.println(" A_ARG_TYPE_URI"); + out.println(" no"); + out.println(" uri"); + out.println(" "); + out.println(" "); + out.println(" SearchCapabilities"); + out.println(" no"); + out.println(" string"); + out.println(" "); + out.println(" "); + out.println(" SortCapabilities"); + out.println(" no"); + out.println(" string"); + out.println(" "); + out.println(" "); + out.println(" SystemUpdateID"); + out.println(" yes"); + out.println(" ui4"); + out.println(" "); + out.println(" "); + out.println(" "); + out.println(" ContainerUpdateIDs"); + out.println(" yes"); + out.println(" string"); + out.println(" "); + out.println(" "); + + + out.println(" "); + out.println(" "); + out.println(" GetSearchCapabilities"); + out.println(" "); + out.println(" "); + out.println(" SearchCaps"); + out.println(" out"); + out.println(" SearchCapabilities"); + out.println(" "); + out.println(" "); + out.println(" "); + out.println(" "); + out.println(" GetSortCapabilities"); + out.println(" "); + out.println(" "); + out.println(" SortCaps"); + out.println(" out"); + out.println(" SortCapabilities"); + out.println(" "); + out.println(" "); + out.println(" "); + out.println(" "); + out.println(" GetSystemUpdateID"); + out.println(" "); + out.println(" "); + out.println(" Id"); + out.println(" out"); + out.println(" SystemUpdateID"); + out.println(" "); + out.println(" "); + out.println(" "); + + out.println(" "); + out.println(" Browse"); + out.println(" "); + out.println(" "); + out.println(" ObjectID"); + out.println(" in"); + out.println(" A_ARG_TYPE_ObjectID"); + out.println(" "); + out.println(" "); + out.println(" BrowseFlag"); + out.println(" in"); + out.println(" A_ARG_TYPE_BrowseFlag"); + out.println(" "); + out.println(" "); + out.println(" Filter"); + out.println(" in"); + out.println(" A_ARG_TYPE_Filter"); + out.println(" "); + out.println(" "); + out.println(" StartingIndex"); + out.println(" in"); + out.println(" A_ARG_TYPE_Index"); + out.println(" "); + out.println(" "); + out.println(" RequestedCount"); + out.println(" in"); + out.println(" A_ARG_TYPE_Count"); + out.println(" "); + out.println(" "); + out.println(" SortCriteria"); + out.println(" in"); + out.println(" A_ARG_TYPE_SortCriteria"); + out.println(" "); + out.println(" "); + out.println(" Result"); + out.println(" out"); + out.println(" A_ARG_TYPE_Result"); + out.println(" "); + out.println(" "); + out.println(" NumberReturned"); + out.println(" out"); + out.println(" A_ARG_TYPE_Count"); + out.println(" "); + out.println(" "); + out.println(" TotalMatches"); + out.println(" out"); + out.println(" A_ARG_TYPE_Count"); + out.println(" "); + out.println(" "); + out.println(" UpdateID"); + out.println(" out"); + out.println(" A_ARG_TYPE_UpdateID"); + out.println(" "); + out.println(" "); + out.println(" "); + + /*out.println(" "); + out.println(" "); + out.println(" Search"); + out.println(" "); + out.println(" "); + out.println(" ContainerID"); + out.println(" in"); + out.println(" A_ARG_TYPE_ObjectID"); + out.println(" "); + out.println(" "); + out.println(" SearchCriteria"); + out.println(" in"); + out.println(" A_ARG_TYPE_SearchCriteria "); + out.println(" "); + out.println(" "); + out.println(" Filter"); + out.println(" in"); + out.println(" A_ARG_TYPE_Filter"); + out.println(" "); + out.println(" "); + out.println(" StartingIndex"); + out.println(" in"); + out.println(" A_ARG_TYPE_Index"); + out.println(" "); + out.println(" "); + out.println(" RequestedCount"); + out.println(" in"); + out.println(" A_ARG_TYPE_Count"); + out.println(" "); + out.println(" "); + out.println(" SortCriteria"); + out.println(" in"); + out.println(" A_ARG_TYPE_SortCriteria"); + out.println(" "); + out.println(" "); + out.println(" Result"); + out.println(" out"); + out.println(" A_ARG_TYPE_Result"); + out.println(" "); + out.println(" "); + out.println(" NumberReturned"); + out.println(" out"); + out.println(" A_ARG_TYPE_Count"); + out.println(" "); + out.println(" "); + out.println(" TotalMatches"); + out.println(" out"); + out.println(" A_ARG_TYPE_Count"); + out.println(" "); + out.println(" "); + out.println(" UpdateID"); + out.println(" out"); + out.println(" A_ARG_TYPE_UpdateID"); + out.println(" "); + out.println(" "); + out.println(" ");*/ + + /*out.println(" "); + out.println(" "); + out.println(" CreateObject"); + out.println(" "); + out.println(" "); + out.println(" ContainerID"); + out.println(" in"); + out.println(" A_ARG_TYPE_ObjectID"); + out.println(" "); + out.println(" "); + out.println(" Elements"); + out.println(" in"); + out.println(" A_ARG_TYPE_Result"); + out.println(" "); + out.println(" "); + out.println(" ObjectID"); + out.println(" out"); + out.println(" A_ARG_TYPE_ObjectID"); + out.println(" "); + out.println(" "); + out.println(" Result"); + out.println(" out"); + out.println(" A_ARG_TYPE_Result"); + out.println(" "); + out.println(" "); + out.println(" ");*/ + + /*out.println(" "); + out.println(" "); + out.println(" DestroyObject"); + out.println(" "); + out.println(" "); + out.println(" ObjectID"); + out.println(" in"); + out.println(" A_ARG_TYPE_ObjectID"); + out.println(" "); + out.println(" "); + out.println(" ");*/ + + /*out.println(" "); + out.println(" "); + out.println(" UpdateObject"); + out.println(" "); + out.println(" "); + out.println(" ObjectID"); + out.println(" in"); + out.println(" A_ARG_TYPE_ObjectID"); + out.println(" "); + out.println(" "); + out.println(" CurrentTagValue"); + out.println(" in"); + out.println(" A_ARG_TYPE_TagValueList "); + out.println(" "); + out.println(" "); + out.println(" NewTagValue"); + out.println(" in"); + out.println(" A_ARG_TYPE_TagValueList "); + out.println(" "); + out.println(" "); + out.println(" ");*/ + + /*out.println(" "); + out.println(" "); + out.println(" ImportResource"); + out.println(" "); + out.println(" "); + out.println(" SourceURI"); + out.println(" in"); + out.println(" A_ARG_TYPE_URI"); + out.println(" "); + out.println(" "); + out.println(" DestinationURI"); + out.println(" in"); + out.println(" A_ARG_TYPE_URI"); + out.println(" "); + out.println(" "); + out.println(" TransferID"); + out.println(" out"); + out.println(" A_ARG_TYPE_TransferID "); + out.println(" "); + out.println(" "); + out.println(" ");*/ + + /*out.println(" "); + out.println(" "); + out.println(" ExportResource"); + out.println(" "); + out.println(" "); + out.println(" SourceURI"); + out.println(" in"); + out.println(" A_ARG_TYPE_URI"); + out.println(" "); + out.println(" "); + out.println(" DestinationURI"); + out.println(" in"); + out.println(" A_ARG_TYPE_URI"); + out.println(" "); + out.println(" "); + out.println(" TransferID"); + out.println(" out"); + out.println(" A_ARG_TYPE_TransferID "); + out.println(" "); + out.println(" "); + out.println(" ");*/ + + /*out.println(" "); + out.println(" "); + out.println(" StopTransferResource"); + out.println(" "); + out.println(" "); + out.println(" TransferID"); + out.println(" in"); + out.println(" A_ARG_TYPE_TransferID "); + out.println(" "); + out.println(" "); + out.println(" ");*/ + + /*out.println(" "); + out.println(" "); + out.println(" GetTransferProgress"); + out.println(" "); + out.println(" "); + out.println(" TransferID"); + out.println(" in"); + out.println(" A_ARG_TYPE_TransferID "); + out.println(" "); + out.println(" "); + out.println(" TransferStatus"); + out.println(" out"); + out.println(" A_ARG_TYPE_TransferStatus "); + out.println(" "); + out.println(" "); + out.println(" TransferLength"); + out.println(" out"); + out.println(" A_ARG_TYPE_TransferLength "); + out.println(" "); + out.println(" "); + out.println(" TransferTotal"); + out.println(" out"); + out.println(" A_ARG_TYPE_TransferTotal"); + out.println(" "); + out.println(" "); + out.println(" ");*/ + + /*out.println(" "); + out.println(" "); + out.println(" DeleteResource"); + out.println(" "); + out.println(" "); + out.println(" ResourceURI"); + out.println(" in"); + out.println(" A_ARG_TYPE_URI"); + out.println(" "); + out.println(" "); + out.println(" ");*/ + + /*out.println(" "); + out.println(" "); + out.println(" CreateReference"); + out.println(" "); + out.println(" "); + out.println(" ContainerID"); + out.println(" in"); + out.println(" A_ARG_TYPE_ObjectID"); + out.println(" "); + out.println(" "); + out.println(" ObjectID"); + out.println(" in"); + out.println(" A_ARG_TYPE_ObjectID"); + out.println(" "); + out.println(" "); + out.println(" NewID"); + out.println(" out"); + out.println(" A_ARG_TYPE_ObjectID"); + out.println(" "); + out.println(" "); + out.println(" ");*/ + + out.println(" "); + out.println(""); + } +} diff --git a/src/zutil/net/ws/WSClientFactory.java b/src/zutil/net/ws/WSClientFactory.java index ba6afcf..0b90a0b 100755 --- a/src/zutil/net/ws/WSClientFactory.java +++ b/src/zutil/net/ws/WSClientFactory.java @@ -25,7 +25,6 @@ package zutil.net.ws; import zutil.log.LogUtil; -import zutil.net.ws.soap.SOAPClientInvocationHandler; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationHandler; diff --git a/src/zutil/net/ws/rest/RestHttpPage.java b/src/zutil/net/ws/rest/RestHttpPage.java index db0c676..506ed74 100755 --- a/src/zutil/net/ws/rest/RestHttpPage.java +++ b/src/zutil/net/ws/rest/RestHttpPage.java @@ -1,108 +1,110 @@ -/* - * 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.ws.rest; - -import zutil.converters.Converter; -import zutil.io.StringOutputStream; -import zutil.net.http.HttpHeaderParser; -import zutil.net.http.HttpPage; -import zutil.net.http.HttpPrintStream; -import zutil.net.ws.*; -import zutil.parser.json.JSONObjectOutputStream; - -import java.io.IOException; -import java.lang.reflect.Field; -import java.util.Map; - -/** - * User: Ziver - */ -public class RestHttpPage implements HttpPage { - - /** The object that the functions will be invoked from **/ - 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()); - } - - - @Override - public void respond(HttpPrintStream out, - HttpHeaderParser client_info, - Map session, - Map cookie, - Map request) throws IOException { - try { - out.println( - execute(client_info.getRequestURL(), request)); - } catch (Throwable throwable) { - throw new IOException(throwable); - } - } - - - private String execute(String targetMethod, Map input) throws Throwable { - if( wsDef.hasMethod(targetMethod) ){ - // Parse request - WSMethodDef m = wsDef.getMethod(targetMethod); - Object[] params = prepareInputParams(m, input); - - // Invoke - Object ret = m.invoke(ws, params); - - // Generate Response - StringOutputStream dummyOut = new StringOutputStream(); - JSONObjectOutputStream out = new JSONObjectOutputStream(dummyOut); - out.writeObject(ret); - out.close(); - return dummyOut.toString(); - } - return "{error: \"Unknown target: "+targetMethod+"\"}"; - } - - private Object[] prepareInputParams(WSMethodDef method, Map input){ - Object[] inputParams = new Object[method.getInputCount()]; - - // Get the parameter values - for(int i=0; i session, + Map cookie, + Map request) throws IOException { + try { + out.println( + execute(headers.getRequestURL(), request)); + } catch (Throwable throwable) { + throw new IOException(throwable); + } + } + + + private String execute(String targetMethod, Map input) throws Throwable { + if( wsDef.hasMethod(targetMethod) ){ + // Parse request + WSMethodDef m = wsDef.getMethod(targetMethod); + Object[] params = prepareInputParams(m, input); + + // Invoke + Object ret = m.invoke(ws, params); + + // Generate Response + StringOutputStream dummyOut = new StringOutputStream(); + JSONObjectOutputStream out = new JSONObjectOutputStream(dummyOut); + out.writeObject(ret); + out.close(); + return dummyOut.toString(); + } + return "{error: \"Unknown target: "+targetMethod+"\"}"; + } + + private Object[] prepareInputParams(WSMethodDef method, Map input){ + Object[] inputParams = new Object[method.getInputCount()]; + + // Get the parameter values + for(int i=0; i-int - *
-double - *
-float - *
-char - *
-String - *
-byte[] - *
-And the Wrappers Classes except for Byte - * - * Output: - *
-WSReturnObject - *
-byte[] - *
-int - *
-double - *
-float - *
-char - *
-String - *
-Arrays of Output - *
-And the Wrappers Classes except for Byte - * - * @author Ziver - */ -public class SOAPHttpPage implements HttpPage{ - private static final Logger logger = LogUtil.getLogger(); - - /** The object that the functions will be invoked from **/ - private WebServiceDef wsDef; - /** This instance of the web service class is used if session is disabled **/ - private WSInterface ws; - /** Session enabled **/ - private boolean session_enabled; - - public SOAPHttpPage( WebServiceDef wsDef ){ - this.wsDef = wsDef; - this.session_enabled = false; - } - - /** - * Enables session support, if enabled then a new instance - * of the SOAPInterface will be created, if disabled then - * only the given object will be used as an static interface - * - * @param enabled is if session should be enabled - */ - public void enableSession(boolean enabled){ - this.session_enabled = enabled; - } - - /** - * Sets the web service object to the specified one. - * Only used when session is disabled - */ - public void setObject(WSInterface obj) { - this.ws = obj; - } - - - public void respond(HttpPrintStream out, - HttpHeaderParser client_info, - Map session, - Map cookie, - Map request) { - - try { - out.setHeader("Content-Type", "text/xml"); - out.flush(); - - WSInterface obj = null; - if(session_enabled){ - if( session.containsKey("SOAPInterface")) - obj = (WSInterface)session.get("SOAPInterface"); - else{ - obj = wsDef.newInstance(); - session.put("SOAPInterface", obj); - } - } - else{ - if( ws == null ) - ws = wsDef.newInstance(); - obj = ws; - } - - Document document = genSOAPResponse( request.get(""), obj); - - OutputFormat format = OutputFormat.createPrettyPrint(); - XMLWriter writer = new XMLWriter( out, format ); - writer.write( document ); - - - // DEBUG - if( logger.isLoggable(Level.FINEST) ){ - System.out.println("********** Request"); - System.out.println(request); - System.out.println("********** Response"); - writer = new XMLWriter( System.out, format ); - writer.write( document ); - } - } catch (Exception e) { - logger.log(Level.WARNING, "Unhandled request", e); - } - } - - /** - * Generates a soap response for the given XML - * - * @param xml is the XML request - * @return a Document with the response - */ - public Document genSOAPResponse(String xml){ - try { - WSInterface obj = null; - if( ws == null ) - ws = wsDef.newInstance(); - obj = ws; - - return genSOAPResponse(xml, obj ); - } catch (Exception e) { - logger.log(Level.WARNING, "Exception in SOAP generation", e); - } - return null; - } - - protected Document genSOAPResponse(String xml, WSInterface obj){ - Document document = DocumentHelper.createDocument(); - Element envelope = document.addElement("soap:Envelope"); - try { - envelope.addNamespace("soap", "http://schemas.xmlsoap.org/soap/envelope/"); - envelope.addNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance"); - envelope.addNamespace("xsd", "http://www.w3.org/2001/XMLSchema"); - - Element body = envelope.addElement( "soap:Body" ); - try{ - Element request = getXMLRoot(xml); - if(request == null) return document; - // Header - if( request.element("Header") != null){ - Element header = envelope.addElement( "soap:Header" ); - prepareInvoke( obj, request.element("Header"), header ); - } - - // Body - if( request.element("Body") != null){ - prepareInvoke( obj, request.element("Body"), body ); - } - }catch(Throwable e){ - body.clearContent(); - Element fault = body.addElement("soap:Fault"); - // The fault source - if(e instanceof SOAPException || e instanceof SAXException || e instanceof DocumentException) - fault.addElement("faultcode").setText( "soap:Client" ); - else - fault.addElement("faultcode").setText( "soap:Server" ); - // The fault message - if( e.getMessage() == null || e.getMessage().isEmpty()) - fault.addElement("faultstring").setText( ""+e.getClass().getSimpleName() ); - else - fault.addElement("faultstring").setText( ""+e.getMessage() ); - logger.log(Level.WARNING, "Caught exception from SOAP Class", e); - } - } catch (Exception e) { - logger.log(Level.WARNING, "Exception in SOAP generation", e); - } - - return document; - } - - /** - * Converts an String XML to an Element - * - * @param xml is the string XML - * @return the XML root Element - */ - protected static Element getXMLRoot(String xml) throws DocumentException { - if(xml != null && !xml.isEmpty()){ - Document document = DocumentHelper.parseText(xml); - return document.getRootElement(); - } - return null; - } - - /** - * 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 requestRoot is the Element where the children lies - * @param responseRoot is the root element of the response - */ - @SuppressWarnings("unchecked") - private void prepareInvoke(WSInterface obj, Element requestRoot, Element responseRoot) throws Throwable{ - Iterator it = requestRoot.elementIterator(); - while( it.hasNext() ){ - Element e = it.next(); - if( wsDef.hasMethod( e.getQName().getName()) ){ - WSMethodDef m = wsDef.getMethod( e.getQName().getName() ); - Object[] params = new Object[ m.getInputCount() ]; - - // Get the parameter values - for(int i=0; i0 ){ - Element response = responseRoot.addElement(""); - response.addNamespace("m", m.getNamespace() ); - response.setName("m:"+m.getName()+"Response"); - - if( ret instanceof WSReturnObject ){ - Field[] f = ret.getClass().getFields(); - for(int i=0; i c){ - Class cTmp = getClass(c); - if(byte[].class.isAssignableFrom(c)){ - return "base64Binary"; - } - else if( WSReturnObject.class.isAssignableFrom(cTmp) ){ - return c.getSimpleName(); - } - else{ - String ret = c.getSimpleName().toLowerCase(); - - if(cTmp == Integer.class) ret = ret.replaceAll("integer", "int"); - else if(cTmp == Character.class)ret = ret.replaceAll("character", "char"); - - return ret; - } - } - - protected static Class getClass(Class c){ - if(c!=null && c.isArray()){ - return getClass(c.getComponentType()); - } - return c; - } -} - +/* + * 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.ws.soap; + +import org.dom4j.Document; +import org.dom4j.DocumentException; +import org.dom4j.DocumentHelper; +import org.dom4j.Element; +import org.dom4j.io.OutputFormat; +import org.dom4j.io.XMLWriter; +import org.xml.sax.SAXException; +import zutil.converters.Converter; +import zutil.log.LogUtil; +import zutil.net.http.HttpHeader; +import zutil.net.http.HttpPage; +import zutil.net.http.HttpPrintStream; +import zutil.net.ws.*; +import zutil.net.ws.WSReturnObject.WSValueName; + +import java.lang.reflect.Array; +import java.lang.reflect.Field; +import java.util.Iterator; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * This is an HTTPPage for the HTTPServer that + * handles soap messages. + * + * TODO: Header should be variables not methods + * TODO: Read WSReturnObjects as input parameter + * TODO: Ability to have multiple arrays of same WSReturnObject + * + * Features: + * Input: + *
-int + *
-double + *
-float + *
-char + *
-String + *
-byte[] + *
-And the Wrappers Classes except for Byte + * + * Output: + *
-WSReturnObject + *
-byte[] + *
-int + *
-double + *
-float + *
-char + *
-String + *
-Arrays of Output + *
-And the Wrappers Classes except for Byte + * + * @author Ziver + */ +public class SOAPHttpPage implements HttpPage{ + private static final Logger logger = LogUtil.getLogger(); + + /** The object that the functions will be invoked from **/ + private WebServiceDef wsDef; + /** This instance of the web service class is used if session is disabled **/ + private WSInterface ws; + /** Session enabled **/ + private boolean session_enabled; + + public SOAPHttpPage( WebServiceDef wsDef ){ + this.wsDef = wsDef; + this.session_enabled = false; + } + + /** + * Enables session support, if enabled then a new instance + * of the SOAPInterface will be created, if disabled then + * only the given object will be used as an static interface + * + * @param enabled is if session should be enabled + */ + public void enableSession(boolean enabled){ + this.session_enabled = enabled; + } + + /** + * Sets the web service object to the specified one. + * Only used when session is disabled + */ + public void setObject(WSInterface obj) { + this.ws = obj; + } + + + public void respond(HttpPrintStream out, + HttpHeader headers, + Map session, + Map cookie, + Map request) { + + try { + out.setHeader("Content-Type", "text/xml"); + out.flush(); + + WSInterface obj = null; + if(session_enabled){ + if( session.containsKey("SOAPInterface")) + obj = (WSInterface)session.get("SOAPInterface"); + else{ + obj = wsDef.newInstance(); + session.put("SOAPInterface", obj); + } + } + else{ + if( ws == null ) + ws = wsDef.newInstance(); + obj = ws; + } + + Document document = genSOAPResponse( request.get(""), obj); + + OutputFormat format = OutputFormat.createPrettyPrint(); + XMLWriter writer = new XMLWriter( out, format ); + writer.write( document ); + + + // DEBUG + if( logger.isLoggable(Level.FINEST) ){ + System.out.println("********** Request"); + System.out.println(request); + System.out.println("********** Response"); + writer = new XMLWriter( System.out, format ); + writer.write( document ); + } + } catch (Exception e) { + logger.log(Level.WARNING, "Unhandled request", e); + } + } + + /** + * Generates a soap response for the given XML + * + * @param xml is the XML request + * @return a Document with the response + */ + public Document genSOAPResponse(String xml){ + try { + WSInterface obj = null; + if( ws == null ) + ws = wsDef.newInstance(); + obj = ws; + + return genSOAPResponse(xml, obj ); + } catch (Exception e) { + logger.log(Level.WARNING, "Exception in SOAP generation", e); + } + return null; + } + + protected Document genSOAPResponse(String xml, WSInterface obj){ + Document document = DocumentHelper.createDocument(); + Element envelope = document.addElement("soap:Envelope"); + try { + envelope.addNamespace("soap", "http://schemas.xmlsoap.org/soap/envelope/"); + envelope.addNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance"); + envelope.addNamespace("xsd", "http://www.w3.org/2001/XMLSchema"); + + Element body = envelope.addElement( "soap:Body" ); + try{ + Element request = getXMLRoot(xml); + if(request == null) return document; + // Header + if( request.element("Header") != null){ + Element header = envelope.addElement( "soap:Header" ); + prepareInvoke( obj, request.element("Header"), header ); + } + + // Body + if( request.element("Body") != null){ + prepareInvoke( obj, request.element("Body"), body ); + } + }catch(Throwable e){ + body.clearContent(); + Element fault = body.addElement("soap:Fault"); + // The fault source + if(e instanceof SOAPException || e instanceof SAXException || e instanceof DocumentException) + fault.addElement("faultcode").setText( "soap:Client" ); + else + fault.addElement("faultcode").setText( "soap:Server" ); + // The fault message + if( e.getMessage() == null || e.getMessage().isEmpty()) + fault.addElement("faultstring").setText( ""+e.getClass().getSimpleName() ); + else + fault.addElement("faultstring").setText( ""+e.getMessage() ); + logger.log(Level.WARNING, "Caught exception from SOAP Class", e); + } + } catch (Exception e) { + logger.log(Level.WARNING, "Exception in SOAP generation", e); + } + + return document; + } + + /** + * Converts an String XML to an Element + * + * @param xml is the string XML + * @return the XML root Element + */ + protected static Element getXMLRoot(String xml) throws DocumentException { + if(xml != null && !xml.isEmpty()){ + Document document = DocumentHelper.parseText(xml); + return document.getRootElement(); + } + return null; + } + + /** + * 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 requestRoot is the Element where the children lies + * @param responseRoot is the root element of the response + */ + @SuppressWarnings("unchecked") + private void prepareInvoke(WSInterface obj, Element requestRoot, Element responseRoot) throws Throwable{ + Iterator it = requestRoot.elementIterator(); + while( it.hasNext() ){ + Element e = it.next(); + if( wsDef.hasMethod( e.getQName().getName()) ){ + WSMethodDef m = wsDef.getMethod( e.getQName().getName() ); + Object[] params = new Object[ m.getInputCount() ]; + + // Get the parameter values + for(int i=0; i0 ){ + Element response = responseRoot.addElement(""); + response.addNamespace("m", m.getNamespace() ); + response.setName("m:"+m.getName()+"Response"); + + if( ret instanceof WSReturnObject ){ + Field[] f = ret.getClass().getFields(); + for(int i=0; i c){ + Class cTmp = getClass(c); + if(byte[].class.isAssignableFrom(c)){ + return "base64Binary"; + } + else if( WSReturnObject.class.isAssignableFrom(cTmp) ){ + return c.getSimpleName(); + } + else{ + String ret = c.getSimpleName().toLowerCase(); + + if(cTmp == Integer.class) ret = ret.replaceAll("integer", "int"); + else if(cTmp == Character.class)ret = ret.replaceAll("character", "char"); + + return ret; + } + } + + protected static Class getClass(Class c){ + if(c!=null && c.isArray()){ + return getClass(c.getComponentType()); + } + return c; + } +} + diff --git a/src/zutil/osal/app/linux/ProcStat.java b/src/zutil/osal/app/linux/ProcStat.java old mode 100644 new mode 100755 index 7be5747..7dda6bc --- a/src/zutil/osal/app/linux/ProcStat.java +++ b/src/zutil/osal/app/linux/ProcStat.java @@ -24,8 +24,8 @@ package zutil.osal.app.linux; -import zutil.log.LogUtil; import zutil.Timer; +import zutil.log.LogUtil; import java.io.BufferedReader; import java.io.FileReader; diff --git a/src/zutil/osal/app/windows/TypePerf.java b/src/zutil/osal/app/windows/TypePerf.java old mode 100644 new mode 100755 index e85bf06..cdb79f8 --- a/src/zutil/osal/app/windows/TypePerf.java +++ b/src/zutil/osal/app/windows/TypePerf.java @@ -30,8 +30,6 @@ import zutil.parser.DataNode; import java.io.IOException; import java.io.StringReader; -import java.util.ArrayList; -import java.util.List; /** * Created by ezivkoc on 2015-07-30. diff --git a/src/zutil/parser/CSVParser.java b/src/zutil/parser/CSVParser.java old mode 100644 new mode 100755 index 2dc4730..b5366a0 --- a/src/zutil/parser/CSVParser.java +++ b/src/zutil/parser/CSVParser.java @@ -24,8 +24,6 @@ package zutil.parser; -import zutil.struct.MutableInt; - import java.io.IOException; import java.io.Reader; import java.io.StringReader; diff --git a/src/zutil/parser/Parser.java b/src/zutil/parser/Parser.java old mode 100644 new mode 100755 index 76f9475..3dba20b --- a/src/zutil/parser/Parser.java +++ b/src/zutil/parser/Parser.java @@ -24,10 +24,7 @@ package zutil.parser; -import zutil.struct.MutableInt; - import java.io.IOException; -import java.io.StringReader; /** * Created by Ziver diff --git a/src/zutil/parser/binary/BinaryStructParser.java b/src/zutil/parser/binary/BinaryStructParser.java index 1eafdc0..549b7ba 100755 --- a/src/zutil/parser/binary/BinaryStructParser.java +++ b/src/zutil/parser/binary/BinaryStructParser.java @@ -24,15 +24,15 @@ package zutil.parser.binary; +import zutil.ByteUtil; +import zutil.converters.Converter; +import zutil.parser.binary.BinaryStruct.BinaryField; + import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Collections; import java.util.List; -import zutil.ByteUtil; -import zutil.converters.Converter; -import zutil.parser.binary.BinaryStruct.*; - /** * Created by Ziver on 2016-01-28. */ diff --git a/src/zutil/parser/json/JSONObjectInputStream.java b/src/zutil/parser/json/JSONObjectInputStream.java index 6114457..f56a73b 100755 --- a/src/zutil/parser/json/JSONObjectInputStream.java +++ b/src/zutil/parser/json/JSONObjectInputStream.java @@ -32,7 +32,9 @@ import zutil.parser.DataNode; import javax.activation.UnsupportedDataTypeException; 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.Iterator; import java.util.List; diff --git a/src/zutil/parser/json/JSONObjectOutputStream.java b/src/zutil/parser/json/JSONObjectOutputStream.java index 127de0f..b6d2bd6 100755 --- a/src/zutil/parser/json/JSONObjectOutputStream.java +++ b/src/zutil/parser/json/JSONObjectOutputStream.java @@ -29,7 +29,6 @@ import zutil.ClassUtil; import zutil.parser.Base64Encoder; import zutil.parser.DataNode; import zutil.parser.DataNode.DataType; -import static zutil.parser.json.JSONObjectInputStream.*; import javax.activation.UnsupportedDataTypeException; import java.io.*; @@ -40,6 +39,9 @@ import java.util.HashMap; import java.util.List; 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{ /** If the generated JSON should contain class def meta-data **/ private boolean generateMetaData = true; diff --git a/src/zutil/parser/json/JSONWriter.java b/src/zutil/parser/json/JSONWriter.java old mode 100644 new mode 100755 index 4c5e9be..6e00f98 --- a/src/zutil/parser/json/JSONWriter.java +++ b/src/zutil/parser/json/JSONWriter.java @@ -29,7 +29,6 @@ import zutil.parser.DataNode; import zutil.parser.DataNode.DataType; import java.io.OutputStream; -import java.io.PrintStream; import java.io.PrintWriter; import java.io.Writer; import java.util.Iterator; diff --git a/src/zutil/parser/wsdl/WSDLHttpPage.java b/src/zutil/parser/wsdl/WSDLHttpPage.java old mode 100644 new mode 100755 index 92c5b02..bc77dd1 --- a/src/zutil/parser/wsdl/WSDLHttpPage.java +++ b/src/zutil/parser/wsdl/WSDLHttpPage.java @@ -1,54 +1,54 @@ -/* - * 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.parser.wsdl; - -import zutil.net.http.HttpHeaderParser; -import zutil.net.http.HttpPage; -import zutil.net.http.HttpPrintStream; -import zutil.net.ws.WebServiceDef; - -import java.io.IOException; -import java.util.Map; - -/** - * User: Ziver - */ -public class WSDLHttpPage implements HttpPage { - /** The WSDL document **/ - private WSDLWriter wsdl; - - public WSDLHttpPage( WebServiceDef wsDef ){ - wsdl = new WSDLWriter( wsDef ); - } - - public void respond(HttpPrintStream out, - HttpHeaderParser client_info, - Map session, - Map cookie, - Map request) throws IOException{ - out.setHeader("Content-Type", "text/xml"); - wsdl.write( out ); - } -} +/* + * 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.parser.wsdl; + +import zutil.net.http.HttpHeader; +import zutil.net.http.HttpPage; +import zutil.net.http.HttpPrintStream; +import zutil.net.ws.WebServiceDef; + +import java.io.IOException; +import java.util.Map; + +/** + * User: Ziver + */ +public class WSDLHttpPage implements HttpPage { + /** The WSDL document **/ + private WSDLWriter wsdl; + + public WSDLHttpPage( WebServiceDef wsDef ){ + wsdl = new WSDLWriter( wsDef ); + } + + public void respond(HttpPrintStream out, + HttpHeader headers, + Map session, + Map cookie, + Map request) throws IOException{ + out.setHeader("Content-Type", "text/xml"); + wsdl.write( out ); + } +} diff --git a/src/zutil/struct/TimedHashSet.java b/src/zutil/struct/TimedHashSet.java index c02cb1f..bb249c1 100755 --- a/src/zutil/struct/TimedHashSet.java +++ b/src/zutil/struct/TimedHashSet.java @@ -24,10 +24,7 @@ package zutil.struct; -import java.util.AbstractSet; 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. diff --git a/test/zutil/test/BinaryStructTest.java b/test/zutil/test/BinaryStructTest.java index ddf96f7..aaa8e86 100755 --- a/test/zutil/test/BinaryStructTest.java +++ b/test/zutil/test/BinaryStructTest.java @@ -28,7 +28,8 @@ import org.junit.Test; import zutil.parser.binary.BinaryStruct; 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. diff --git a/test/zutil/test/EncrypterTest.java b/test/zutil/test/EncrypterTest.java index bca258a..ea985be 100755 --- a/test/zutil/test/EncrypterTest.java +++ b/test/zutil/test/EncrypterTest.java @@ -26,7 +26,7 @@ package zutil.test; import org.junit.Test; import zutil.Encrypter; -import zutil.Encrypter.*; +import zutil.Encrypter.Algorithm; import static org.junit.Assert.assertEquals; diff --git a/test/zutil/test/HTTPGuessTheNumber.java b/test/zutil/test/HTTPGuessTheNumber.java index 44ca99f..73b824f 100755 --- a/test/zutil/test/HTTPGuessTheNumber.java +++ b/test/zutil/test/HTTPGuessTheNumber.java @@ -24,7 +24,7 @@ package zutil.test; -import zutil.net.http.HttpHeaderParser; +import zutil.net.http.HttpHeader; import zutil.net.http.HttpPage; import zutil.net.http.HttpPrintStream; import zutil.net.http.HttpServer; @@ -43,7 +43,7 @@ public class HTTPGuessTheNumber implements HttpPage{ } public void respond(HttpPrintStream out, - HttpHeaderParser client_info, + HttpHeader client_info, Map session, Map cookie, Map request) throws IOException { diff --git a/test/zutil/test/HTTPUploaderTest.java b/test/zutil/test/HTTPUploaderTest.java index 2e5c158..f032bb1 100755 --- a/test/zutil/test/HTTPUploaderTest.java +++ b/test/zutil/test/HTTPUploaderTest.java @@ -24,7 +24,7 @@ package zutil.test; -import zutil.net.http.HttpHeaderParser; +import zutil.net.http.HttpHeader; import zutil.net.http.HttpPage; import zutil.net.http.HttpPrintStream; import zutil.net.http.HttpServer; @@ -42,7 +42,7 @@ public class HTTPUploaderTest implements HttpPage{ } public void respond(HttpPrintStream out, - HttpHeaderParser client_info, + HttpHeader client_info, Map session, Map cookie, Map request) throws IOException { diff --git a/test/zutil/test/NumberToWordsConverterTest.java b/test/zutil/test/NumberToWordsConverterTest.java index 3c2825b..24617c2 100755 --- a/test/zutil/test/NumberToWordsConverterTest.java +++ b/test/zutil/test/NumberToWordsConverterTest.java @@ -24,19 +24,19 @@ 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.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; 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) public class NumberToWordsConverterTest { diff --git a/test/zutil/test/SOAPTest.java b/test/zutil/test/SOAPTest.java index f9349a7..9ac9717 100755 --- a/test/zutil/test/SOAPTest.java +++ b/test/zutil/test/SOAPTest.java @@ -27,7 +27,6 @@ package zutil.test; import org.dom4j.Document; import org.dom4j.io.OutputFormat; import org.dom4j.io.XMLWriter; -import zutil.net.nio.message.type.SystemMessage; import zutil.net.ws.WSInterface; import zutil.net.ws.WSInterface.WSNamespace; import zutil.net.ws.WSReturnObject; diff --git a/test/zutil/test/SSDPServerTest.java b/test/zutil/test/SSDPServerTest.java index 1b8e18b..3978285 100755 --- a/test/zutil/test/SSDPServerTest.java +++ b/test/zutil/test/SSDPServerTest.java @@ -29,7 +29,6 @@ import zutil.net.ssdp.SSDPServer; import zutil.net.ssdp.StandardSSDPInfo; import java.io.IOException; -import java.util.HashMap; import java.util.logging.Level; /** diff --git a/test/zutil/test/StringUtilTest.java b/test/zutil/test/StringUtilTest.java index 618e8d7..4fba0e3 100755 --- a/test/zutil/test/StringUtilTest.java +++ b/test/zutil/test/StringUtilTest.java @@ -28,7 +28,6 @@ import org.junit.Test; import zutil.StringUtil; import java.util.Arrays; -import java.util.Collection; import static org.junit.Assert.assertEquals; diff --git a/test/zutil/test/TimedHashSetTest.java b/test/zutil/test/TimedHashSetTest.java index 85b39d8..ecdd143 100755 --- a/test/zutil/test/TimedHashSetTest.java +++ b/test/zutil/test/TimedHashSetTest.java @@ -27,7 +27,7 @@ package zutil.test; import org.junit.Test; import zutil.struct.TimedHashSet; -import static org.junit.Assert.*; +import static org.junit.Assert.assertFalse; /** * Created by Ziver on 2015-11-20.