Big HTTP header parsing refactoring
This commit is contained in:
parent
946953699f
commit
862bc7763e
50 changed files with 3114 additions and 3072 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -27,7 +27,6 @@ package zutil;
|
|||
import zutil.converters.Converter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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<Object> it = map.keySet().iterator();
|
||||
while(it.hasNext()){
|
||||
Object key = it.next();
|
||||
Object value = map.get(key);
|
||||
tmp.append(key);
|
||||
if (value != null) {
|
||||
if (value instanceof String)
|
||||
tmp.append(": \"").append(value).append("\"");
|
||||
else
|
||||
tmp.append(value);
|
||||
}
|
||||
else
|
||||
tmp.append("null");
|
||||
|
||||
if(it.hasNext())
|
||||
tmp.append(", ");
|
||||
}
|
||||
tmp.append('}');
|
||||
return tmp.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a BitSet to a Integer
|
||||
*
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -24,7 +24,6 @@
|
|||
|
||||
package zutil.db.bean;
|
||||
|
||||
import zutil.ClassUtil;
|
||||
import zutil.log.LogUtil;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,6 @@ package zutil.log;
|
|||
import zutil.io.file.FileUtil;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.util.Enumeration;
|
||||
import java.util.logging.*;
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
176
src/zutil/net/http/HttpHeader.java
Executable file
176
src/zutil/net/http/HttpHeader.java
Executable file
|
|
@ -0,0 +1,176 @@
|
|||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015 Ziver Koc
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package zutil.net.http;
|
||||
|
||||
import zutil.converters.Converter;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
|
||||
public class HttpHeader {
|
||||
// HTTP info
|
||||
private String type;
|
||||
private String url;
|
||||
private HashMap<String, String> urlAttributes;
|
||||
private float version;
|
||||
private int httpCode;
|
||||
|
||||
// Parameters
|
||||
private HashMap<String, String> headers;
|
||||
private HashMap<String, String> cookies;
|
||||
|
||||
|
||||
protected HttpHeader(){
|
||||
urlAttributes = new HashMap<String, String>();
|
||||
headers = new HashMap<String, String>();
|
||||
cookies = new HashMap<String, String>();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return the HTTP message type( ex. GET,POST...)
|
||||
*/
|
||||
public String getRequestType(){
|
||||
return type;
|
||||
}
|
||||
/**
|
||||
* @return the HTTP version of this header
|
||||
*/
|
||||
public float getHTTPVersion(){
|
||||
return version;
|
||||
}
|
||||
/**
|
||||
* @return the HTTP Return Code from a Server
|
||||
*/
|
||||
public int getHTTPCode(){
|
||||
return httpCode;
|
||||
}
|
||||
/**
|
||||
* @return the URL that the client sent the server
|
||||
*/
|
||||
public String getRequestURL(){
|
||||
return url;
|
||||
}
|
||||
/**
|
||||
* @return a Iterator with all defined url keys
|
||||
*/
|
||||
public Iterator<String> getURLAttributeKeys(){
|
||||
return urlAttributes.keySet().iterator();
|
||||
}
|
||||
/**
|
||||
* Returns the URL attribute value of the given name.
|
||||
*
|
||||
* returns null if there is no such attribute
|
||||
*/
|
||||
public String getURLAttribute(String name){
|
||||
return urlAttributes.get( name );
|
||||
}
|
||||
/**
|
||||
* @return a Iterator with all defined headers
|
||||
*/
|
||||
public Iterator<String> getHeaderKeys(){
|
||||
return headers.keySet().iterator();
|
||||
}
|
||||
/**
|
||||
* Returns the HTTP attribute value of the given name.
|
||||
*
|
||||
* returns null if there is no such attribute
|
||||
*/
|
||||
public String getHeader(String name){
|
||||
return headers.get( name.toUpperCase() );
|
||||
}
|
||||
/**
|
||||
* @return a Iterator with all defined cookies
|
||||
*/
|
||||
public Iterator<String> getCookieKeys(){
|
||||
return cookies.keySet().iterator();
|
||||
}
|
||||
/**
|
||||
* Returns the cookie value of the given name.
|
||||
*
|
||||
* returns null if there is no such attribute
|
||||
*/
|
||||
public String getCookie(String name){
|
||||
return cookies.get( name );
|
||||
}
|
||||
|
||||
|
||||
protected HashMap<String,String> getCookieMap(){
|
||||
return cookies;
|
||||
}
|
||||
protected HashMap<String,String> getUrlAttributeMap(){
|
||||
return urlAttributes;
|
||||
}
|
||||
|
||||
protected void setRequestType(String type){
|
||||
this.type = type.trim();
|
||||
}
|
||||
protected void setHTTPVersion(float version){
|
||||
this.version = version;
|
||||
}
|
||||
protected void setHTTPCode(int code){
|
||||
this.httpCode = code;
|
||||
}
|
||||
protected void setRequestURL(String url){
|
||||
this.url = url.trim().replaceAll("//", "/");
|
||||
}
|
||||
protected void putCookie(String key, String value){
|
||||
cookies.put(key.trim(), value.trim());
|
||||
}
|
||||
protected void putURLAttribute(String key, String value){
|
||||
urlAttributes.put(key.trim(), value.trim());
|
||||
}
|
||||
protected void putHeader(String key, String value){
|
||||
headers.put(key.trim(), value.trim());
|
||||
}
|
||||
|
||||
|
||||
|
||||
public String toString(){
|
||||
StringBuilder tmp = new StringBuilder();
|
||||
tmp.append("{Type: ").append(type);
|
||||
tmp.append(", HTTP_version: HTTP/").append(version);
|
||||
if(url == null)
|
||||
tmp.append(", URL: null");
|
||||
else
|
||||
tmp.append(", URL: \"").append(url).append('\"');
|
||||
|
||||
tmp.append(", URL_attr: ").append(toStringAttributes());
|
||||
tmp.append(", Headers: ").append(toStringHeaders());
|
||||
tmp.append(", Cookies: ").append(toStringCookies());
|
||||
|
||||
tmp.append('}');
|
||||
return tmp.toString();
|
||||
}
|
||||
public String toStringHeaders(){
|
||||
return Converter.toString(headers);
|
||||
}
|
||||
public String toStringCookies(){
|
||||
return Converter.toString(cookies);
|
||||
}
|
||||
public String toStringAttributes(){
|
||||
return Converter.toString(urlAttributes);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,297 +1,158 @@
|
|||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015 Ziver Koc
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package zutil.net.http;
|
||||
|
||||
import zutil.parser.URLDecoder;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Scanner;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class HttpHeaderParser {
|
||||
// Some Cached regex's
|
||||
private static final Pattern colonPattern = Pattern.compile(":");
|
||||
private static final Pattern equalPattern = Pattern.compile("=");
|
||||
private static final Pattern andPattern = Pattern.compile("&");
|
||||
private static final Pattern semiColonPattern = Pattern.compile(";");
|
||||
|
||||
// HTTP info
|
||||
private String type;
|
||||
private String url;
|
||||
private HashMap<String, String> url_attr;
|
||||
private float version;
|
||||
private int httpCode;
|
||||
|
||||
// Parameters
|
||||
private HashMap<String, String> headers;
|
||||
private HashMap<String, String> cookies;
|
||||
|
||||
/**
|
||||
* Parses the HTTP header information from a stream
|
||||
*
|
||||
* @param in is the stream
|
||||
* @throws IOException
|
||||
*/
|
||||
public HttpHeaderParser(BufferedReader in) throws IOException{
|
||||
url_attr = new HashMap<String, String>();
|
||||
headers = new HashMap<String, String>();
|
||||
cookies = new HashMap<String, String>();
|
||||
|
||||
String tmp = null;
|
||||
if( (tmp=in.readLine()) != null && !tmp.isEmpty() ){
|
||||
parseStatusLine( tmp );
|
||||
while( (tmp=in.readLine()) != null && !tmp.isEmpty() ){
|
||||
parseLine( tmp );
|
||||
}
|
||||
}
|
||||
parseCookies();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the HTTP header information from an String
|
||||
*
|
||||
* @param in is the string
|
||||
*/
|
||||
public HttpHeaderParser(String in){
|
||||
url_attr = new HashMap<String, String>();
|
||||
headers = new HashMap<String, String>();
|
||||
cookies = new HashMap<String, String>();
|
||||
|
||||
Scanner sc = new Scanner(in);
|
||||
sc.useDelimiter("\n");
|
||||
String tmp = null;
|
||||
if( sc.hasNext() && !(tmp=sc.next()).isEmpty() ){
|
||||
parseStatusLine( tmp );
|
||||
while( sc.hasNext() && !(tmp=sc.next()).isEmpty() ){
|
||||
parseLine( tmp );
|
||||
}
|
||||
}
|
||||
sc.close();
|
||||
parseCookies();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the first header line and ads the values to
|
||||
* the map and returns the file name and path
|
||||
*
|
||||
* @param line The header String
|
||||
* @return The path and file name as a String
|
||||
*/
|
||||
protected void parseStatusLine(String line){
|
||||
// Server Response
|
||||
if( line.startsWith("HTTP/") ){
|
||||
version = Float.parseFloat( line.substring( 5 , 8) );
|
||||
httpCode = Integer.parseInt( line.substring( 9, 12 ));
|
||||
}
|
||||
// Client Request
|
||||
else if(line.contains("HTTP/")){
|
||||
type = (line.substring(0, line.indexOf(" "))).trim();
|
||||
version = Float.parseFloat( line.substring(line.lastIndexOf("HTTP/")+5 , line.length()).trim() );
|
||||
line = (line.substring(type.length()+1, line.lastIndexOf("HTTP/"))).trim();
|
||||
|
||||
// parse URL and attributes
|
||||
int index = line.indexOf('?');
|
||||
if(index > -1){
|
||||
url = line.substring(0, index );
|
||||
line = line.substring( index+1, line.length());
|
||||
parseURLParameters(line, url_attr);
|
||||
}
|
||||
else{
|
||||
url = line;
|
||||
}
|
||||
|
||||
url = url.replaceAll("//", "/");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a String with variables from a get or post
|
||||
* that was sent from a client and puts the data into a HashMap
|
||||
*
|
||||
* @param attributes is the String containing all the attributes
|
||||
*/
|
||||
public static HashMap<String, String> parseURLParameters( String attributes ){
|
||||
HashMap<String, String> map = new HashMap<String, String>();
|
||||
parseURLParameters(attributes, map);
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a String with variables from a get or post
|
||||
* that was sent from a client and puts the data into a HashMap
|
||||
*
|
||||
* @param attributes is the String containing all the attributes
|
||||
* @param map is the HashMap to put all the values into
|
||||
*/
|
||||
public static void parseURLParameters(String attributes, HashMap<String, String> map){
|
||||
String[] tmp;
|
||||
attributes = URLDecoder.decode(attributes);
|
||||
// get the variables
|
||||
String[] data = andPattern.split( attributes );
|
||||
for(String element : data){
|
||||
tmp = equalPattern.split(element, 2);
|
||||
map.put(
|
||||
tmp[0].trim(), // Key
|
||||
(tmp.length>1 ? tmp[1] : "").trim()); //Value
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the rest of the header
|
||||
*
|
||||
* @param line is the next line in the header
|
||||
*/
|
||||
protected void parseLine(String line){
|
||||
String[] data = colonPattern.split( line, 2 );
|
||||
headers.put(
|
||||
data[0].trim().toUpperCase(), // Key
|
||||
(data.length>1 ? data[1] : "").trim()); //Value
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the attribute "Cookie" and returns a HashMap
|
||||
* with the values
|
||||
*
|
||||
* @return a HashMap with cookie values
|
||||
*/
|
||||
protected void parseCookies(){
|
||||
if( headers.containsKey("COOKIE") ){
|
||||
String[] tmp = semiColonPattern.split( headers.get("COOKIE") );
|
||||
String[] tmp2;
|
||||
for(String cookie : tmp){
|
||||
tmp2 = equalPattern.split(cookie, 2);
|
||||
cookies.put(
|
||||
tmp2[0].trim(), // Key
|
||||
(tmp2.length>1 ? tmp2[1] : "").trim()); //Value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the HTTP message type( ex. GET,POST...)
|
||||
*/
|
||||
public String getRequestType(){
|
||||
return type;
|
||||
}
|
||||
/**
|
||||
* @return the HTTP version of this header
|
||||
*/
|
||||
public float getHTTPVersion(){
|
||||
return version;
|
||||
}
|
||||
/**
|
||||
* @return the HTTP Return Code from a Server
|
||||
*/
|
||||
public float getHTTPCode(){
|
||||
return httpCode;
|
||||
}
|
||||
/**
|
||||
* @return the URL that the client sent the server
|
||||
*/
|
||||
public String getRequestURL(){
|
||||
return url;
|
||||
}
|
||||
/**
|
||||
* Returns the URL attribute value of the given name.
|
||||
*
|
||||
* returns null if there is no such attribute
|
||||
*/
|
||||
public String getURLAttribute(String name){
|
||||
return url_attr.get( name );
|
||||
}
|
||||
/**
|
||||
* Returns the HTTP attribute value of the given name.
|
||||
*
|
||||
* returns null if there is no such attribute
|
||||
*/
|
||||
public String getHeader(String name){
|
||||
return headers.get( name.toUpperCase() );
|
||||
}
|
||||
/**
|
||||
* Returns the cookie value of the given name.
|
||||
*
|
||||
* returns null if there is no such attribute
|
||||
*/
|
||||
public String getCookie(String name){
|
||||
return cookies.get( name );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return a map of the parsed cookies
|
||||
*/
|
||||
public HashMap<String, String> getCookies(){
|
||||
return cookies;
|
||||
}
|
||||
/**
|
||||
* @return a map of the parsed URL attributes
|
||||
*/
|
||||
public HashMap<String, String> getURLAttributes(){
|
||||
return url_attr;
|
||||
}
|
||||
/**
|
||||
* @return a map of the parsed headers
|
||||
*/
|
||||
public HashMap<String, String> getHeaders(){
|
||||
return headers;
|
||||
}
|
||||
|
||||
|
||||
public String toString(){
|
||||
StringBuilder tmp = new StringBuilder();
|
||||
tmp.append("{Type: ").append(type);
|
||||
tmp.append(", HTTP_version: HTTP/").append(version);
|
||||
if(url == null)
|
||||
tmp.append(", URL: null");
|
||||
else
|
||||
tmp.append(", URL: \"").append(url).append('\"');
|
||||
|
||||
tmp.append(", URL_attr: { ");
|
||||
for( String key : url_attr.keySet() ){
|
||||
tmp.append(key).append(": \"");
|
||||
tmp.append(url_attr.get(key)).append("\", ");
|
||||
}
|
||||
tmp.append('}');
|
||||
|
||||
tmp.append(", Headers: {");
|
||||
for( String key : headers.keySet() ){
|
||||
tmp.append(key).append(": \"");
|
||||
tmp.append( headers.get(key) ).append("\", ");
|
||||
}
|
||||
tmp.append('}');
|
||||
|
||||
tmp.append(", Cookies: {");
|
||||
for( String key : cookies.keySet() ){
|
||||
tmp.append(key).append(": \"");
|
||||
tmp.append( cookies.get(key) ).append("\", ");
|
||||
}
|
||||
tmp.append('}');
|
||||
|
||||
tmp.append('}');
|
||||
return tmp.toString();
|
||||
}
|
||||
}
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015 Ziver Koc
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package zutil.net.http;
|
||||
|
||||
import zutil.parser.URLDecoder;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class HttpHeaderParser {
|
||||
public static final String HEADER_COOKIE = "COOKIE";
|
||||
|
||||
private static final Pattern PATTERN_COLON = Pattern.compile(":");
|
||||
private static final Pattern PATTERN_EQUAL = Pattern.compile("=");
|
||||
private static final Pattern PATTERN_AND = Pattern.compile("&");
|
||||
private static final Pattern PATTERN_SEMICOLON = Pattern.compile(";");
|
||||
|
||||
|
||||
private BufferedReader in;
|
||||
|
||||
|
||||
/**
|
||||
* Parses the HTTP header information from a stream
|
||||
*
|
||||
* @param in is the stream
|
||||
*/
|
||||
public HttpHeaderParser(BufferedReader in){
|
||||
this.in = in;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the HTTP header information from a String
|
||||
*
|
||||
* @param in is the String
|
||||
*/
|
||||
public HttpHeaderParser(String in){
|
||||
this.in = new BufferedReader(new StringReader(in));
|
||||
}
|
||||
|
||||
|
||||
public HttpHeader read() throws IOException {
|
||||
HttpHeader header = null;
|
||||
String line = null;
|
||||
if( (line=in.readLine()) != null && !line.isEmpty() ){
|
||||
header = new HttpHeader();
|
||||
parseStatusLine(header, line);
|
||||
while( (line=in.readLine()) != null && !line.isEmpty() ){
|
||||
parseLine(header, line);
|
||||
}
|
||||
parseCookies(header);
|
||||
}
|
||||
return header;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parses the first header line and ads the values to
|
||||
* the map and returns the file name and path
|
||||
*
|
||||
* @param line The header String
|
||||
* @return The path and file name as a String
|
||||
*/
|
||||
protected static void parseStatusLine(HttpHeader header, String line){
|
||||
// Server Response
|
||||
if( line.startsWith("HTTP/") ){
|
||||
header.setHTTPVersion( Float.parseFloat( line.substring( 5 , 8)));
|
||||
header.setHTTPCode( Integer.parseInt( line.substring( 9, 12 )));
|
||||
}
|
||||
// Client Request
|
||||
else if(line.contains("HTTP/")){
|
||||
header.setRequestType( line.substring(0, line.indexOf(" ")));
|
||||
header.setHTTPVersion( Float.parseFloat( line.substring(line.lastIndexOf("HTTP/")+5 , line.length()).trim()));
|
||||
line = (line.substring(header.getRequestType().length()+1, line.lastIndexOf("HTTP/")));
|
||||
|
||||
// parse URL and attributes
|
||||
int index = line.indexOf('?');
|
||||
if(index > -1){
|
||||
header.setRequestURL( line.substring(0, index));
|
||||
line = line.substring( index+1, line.length());
|
||||
parseURLParameters(header, line);
|
||||
}
|
||||
else{
|
||||
header.setRequestURL(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the rest of the header
|
||||
*
|
||||
* @param line is the next line in the header
|
||||
*/
|
||||
protected void parseLine(HttpHeader header, String line){
|
||||
String[] data = PATTERN_COLON.split( line, 2 );
|
||||
header.putHeader(
|
||||
data[0].trim().toUpperCase(), // Key
|
||||
(data.length>1 ? data[1] : "").trim()); //Value
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the header "Cookie" and stores all cookies in the HttpHeader object
|
||||
*/
|
||||
protected void parseCookies(HttpHeader header){
|
||||
String cookieHeader = header.getHeader(HEADER_COOKIE);
|
||||
if(cookieHeader != null && !cookieHeader.isEmpty()){
|
||||
String[] tmp = PATTERN_SEMICOLON.split(cookieHeader);
|
||||
for(String cookie : tmp){
|
||||
String[] tmp2 = PATTERN_EQUAL.split(cookie, 2);
|
||||
header.putCookie(
|
||||
tmp2[0].trim(), // Key
|
||||
(tmp2.length>1 ? tmp2[1] : "").trim()); //Value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a String with variables from a get or post
|
||||
* that was sent from a client
|
||||
*
|
||||
* @param attributes is the String containing all the attributes
|
||||
*/
|
||||
protected static void parseURLParameters(HttpHeader header, String attributes){
|
||||
String[] tmp;
|
||||
attributes = URLDecoder.decode(attributes);
|
||||
// get the variables
|
||||
String[] data = PATTERN_AND.split( attributes );
|
||||
for(String element : data){
|
||||
tmp = PATTERN_EQUAL.split(element, 2);
|
||||
header.putURLAttribute(
|
||||
tmp[0].trim(), // Key
|
||||
(tmp.length>1 ? tmp[1] : "").trim()); //Value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
105
src/zutil/net/http/HttpPage.java
Normal file → Executable file
105
src/zutil/net/http/HttpPage.java
Normal file → Executable file
|
|
@ -1,52 +1,53 @@
|
|||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015 Ziver Koc
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package zutil.net.http;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* This is a interface for a ordinary page for the HttpServer
|
||||
*
|
||||
* @author Ziver
|
||||
*
|
||||
*/
|
||||
public interface HttpPage{
|
||||
/**
|
||||
* This method has to be implemented for every page.
|
||||
* This method is called when a client wants a response
|
||||
* from this specific page.
|
||||
* @param out is the PrintStream to the client
|
||||
* @param client_info is information about the client
|
||||
* @param session is session values for the client
|
||||
* @param cookie is cookie information from the client
|
||||
* @param request is POST and GET requests from the client
|
||||
*/
|
||||
public abstract void respond(HttpPrintStream out,
|
||||
HttpHeaderParser client_info,
|
||||
Map<String,Object> session,
|
||||
Map<String,String> cookie,
|
||||
Map<String,String> request) throws IOException;
|
||||
}
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015 Ziver Koc
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package zutil.net.http;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* This is a interface for a ordinary page for the HttpServer
|
||||
*
|
||||
* @author Ziver
|
||||
*
|
||||
*/
|
||||
public interface HttpPage{
|
||||
/**
|
||||
* This method has to be implemented for every page.
|
||||
* This method is called when a client wants a response
|
||||
* from this specific page.
|
||||
*
|
||||
* @param out is a output stream to the client
|
||||
* @param headers is the header received from the client
|
||||
* @param session is the session associated with the current client
|
||||
* @param cookie is cookie information from the client
|
||||
* @param request is POST and GET requests from the client
|
||||
*/
|
||||
public abstract void respond(HttpPrintStream out,
|
||||
HttpHeader headers,
|
||||
Map<String,Object> session,
|
||||
Map<String,String> cookie,
|
||||
Map<String,String> request) throws IOException;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -1,296 +1,298 @@
|
|||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015 Ziver Koc
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package zutil.net.http;
|
||||
|
||||
import zutil.StringUtil;
|
||||
import zutil.log.LogUtil;
|
||||
import zutil.net.threaded.ThreadedTCPNetworkServer;
|
||||
import zutil.net.threaded.ThreadedTCPNetworkServerThread;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.Socket;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
|
||||
/**
|
||||
* A simple web server that handles both cookies and
|
||||
* sessions for all the clients
|
||||
*
|
||||
* @author Ziver
|
||||
*/
|
||||
public class HttpServer extends ThreadedTCPNetworkServer{
|
||||
private static final Logger logger = LogUtil.getLogger();
|
||||
public static final String SERVER_VERSION = "Ziver HttpServer 1.0";
|
||||
public static final int COOKIE_TTL = 200;
|
||||
public static final int SESSION_TTL = 10*60*1000; // in milliseconds
|
||||
|
||||
private Map<String,HttpPage> pages;
|
||||
private HttpPage defaultPage;
|
||||
private Map<String,Map<String,Object>> sessions;
|
||||
private int nextSessionId;
|
||||
|
||||
/**
|
||||
* Creates a new instance of the sever
|
||||
*
|
||||
* @param port The port that the server should listen to
|
||||
*/
|
||||
public HttpServer(int port){
|
||||
this(port, null, null);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new instance of the sever
|
||||
*
|
||||
* @param port The port that the server should listen to
|
||||
* @param keyStore If this is not null then the server will use SSL connection with this keyStore file path
|
||||
* @param keyStorePass If this is not null then the server will use a SSL connection with the given certificate
|
||||
*/
|
||||
public HttpServer(int port, File keyStore, String keyStorePass){
|
||||
super( port, keyStore, keyStorePass );
|
||||
|
||||
pages = new ConcurrentHashMap<String,HttpPage>();
|
||||
sessions = new ConcurrentHashMap<String,Map<String,Object>>();
|
||||
nextSessionId = 0;
|
||||
|
||||
Timer timer = new Timer();
|
||||
timer.schedule(new GarbageCollector(), 10000, SESSION_TTL / 2);
|
||||
|
||||
logger.info("HTTP"+(keyStore==null?"":"S")+" Server ready!");
|
||||
}
|
||||
|
||||
/**
|
||||
* This class acts as an garbage collector that
|
||||
* removes old sessions from the session HashMap
|
||||
*
|
||||
* @author Ziver
|
||||
*/
|
||||
private class GarbageCollector extends TimerTask {
|
||||
public void run(){
|
||||
Object[] keys = sessions.keySet().toArray();
|
||||
for(Object key : keys){
|
||||
Map<String,Object> client_session = sessions.get(key);
|
||||
|
||||
// Check if session is still valid
|
||||
if((Long)client_session.get("ttl") < System.currentTimeMillis()){
|
||||
sessions.remove(key);
|
||||
logger.fine("Removing Session: "+key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a HttpPage to a specific URL
|
||||
*
|
||||
* @param name The URL or name of the page
|
||||
* @param page The page itself
|
||||
*/
|
||||
public void setPage(String name, HttpPage page){
|
||||
if(name.charAt(0) != '/')
|
||||
name = "/"+name;
|
||||
pages.put(name, page);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a default page that will be shown
|
||||
* if there is no other matching page,
|
||||
*
|
||||
* @param page The HttpPage that will be shown
|
||||
*/
|
||||
public void setDefaultPage(HttpPage page){
|
||||
defaultPage = page;
|
||||
}
|
||||
|
||||
protected ThreadedTCPNetworkServerThread getThreadInstance( Socket s ){
|
||||
try {
|
||||
return new HttpServerThread( s );
|
||||
} catch (IOException e) {
|
||||
logger.log(Level.SEVERE, "Could not start new Thread", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal class that handles all the requests
|
||||
*
|
||||
* @author Ziver
|
||||
*
|
||||
*/
|
||||
protected class HttpServerThread implements ThreadedTCPNetworkServerThread{
|
||||
private HttpPrintStream out;
|
||||
private BufferedReader in;
|
||||
private Socket socket;
|
||||
|
||||
public HttpServerThread(Socket socket) throws IOException{
|
||||
out = new HttpPrintStream(socket.getOutputStream());
|
||||
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
|
||||
this.socket = socket;
|
||||
}
|
||||
|
||||
public void run(){
|
||||
String tmp = null;
|
||||
|
||||
HashMap<String,String> cookie = new HashMap<String,String>();
|
||||
HashMap<String,String> request = new HashMap<String,String>();
|
||||
|
||||
//**************************** REQUEST *********************************
|
||||
try {
|
||||
long time = System.currentTimeMillis();
|
||||
HttpHeaderParser parser = new HttpHeaderParser(in);
|
||||
request = parser.getURLAttributes();
|
||||
cookie = parser.getCookies();
|
||||
|
||||
|
||||
//******* Read in the post data if available
|
||||
if( parser.getHeader("Content-Length")!=null ){
|
||||
// Reads the post data size
|
||||
tmp = parser.getHeader("Content-Length");
|
||||
int post_data_length = Integer.parseInt( tmp );
|
||||
// read the data
|
||||
StringBuffer tmpb = new StringBuffer();
|
||||
// read the data
|
||||
for(int i=0; i<post_data_length ;i++){
|
||||
tmpb.append((char)in.read());
|
||||
}
|
||||
|
||||
tmp = parser.getHeader("Content-Type");
|
||||
if( tmp.contains("application/x-www-form-urlencoded") ){
|
||||
// get the variables
|
||||
HttpHeaderParser.parseURLParameters( tmpb.toString(), request );
|
||||
}
|
||||
else if( tmp.contains("application/soap+xml" ) ||
|
||||
tmp.contains("text/xml") ||
|
||||
tmp.contains("text/plain") ){
|
||||
// save the variables
|
||||
request.put( "" , tmpb.toString() );
|
||||
}
|
||||
else if( tmp.contains("multipart/form-data") ){
|
||||
// TODO: File upload
|
||||
throw new Exception( "\"multipart-form-data\" Not implemented." );
|
||||
}
|
||||
}
|
||||
|
||||
//**************************** HANDLE REQUEST *********************************
|
||||
// Get the client session or create one
|
||||
Map<String, Object> client_session;
|
||||
long ttl_time = System.currentTimeMillis()+SESSION_TTL;
|
||||
if( cookie.containsKey("session_id") && sessions.containsKey(cookie.get("session_id")) ){
|
||||
client_session = sessions.get( cookie.get("session_id") );
|
||||
// Check if session is still valid
|
||||
if( (Long)client_session.get("ttl") < System.currentTimeMillis() ){
|
||||
int session_id = (Integer)client_session.get("session_id");
|
||||
client_session = Collections.synchronizedMap(new HashMap<String, Object>());
|
||||
client_session.put( "session_id", session_id);
|
||||
sessions.put( ""+session_id, client_session);
|
||||
}
|
||||
// renew the session TTL
|
||||
client_session.put("ttl", ttl_time);
|
||||
}
|
||||
else{
|
||||
client_session = Collections.synchronizedMap(new HashMap<String, Object>());
|
||||
client_session.put( "session_id", nextSessionId );
|
||||
client_session.put( "ttl", ttl_time );
|
||||
sessions.put( ""+nextSessionId, client_session );
|
||||
++nextSessionId;
|
||||
}
|
||||
|
||||
//**************************** RESPONSE ************************************
|
||||
out.setStatusCode(200);
|
||||
out.setHeader( "Server", SERVER_VERSION );
|
||||
out.setHeader( "Content-Type", "text/html" );
|
||||
out.setCookie( "session_id", ""+client_session.get("session_id") );
|
||||
|
||||
if( parser.getRequestURL() != null && pages.containsKey(parser.getRequestURL()) ){
|
||||
HttpPage page = pages.get(parser.getRequestURL());
|
||||
page.respond(out, parser, client_session, cookie, request);
|
||||
if(LogUtil.isLoggable(page.getClass(), Level.FINER))
|
||||
logRequest(parser, client_session, cookie, request, time);
|
||||
}
|
||||
else if( parser.getRequestURL() != null && defaultPage != null ){
|
||||
defaultPage.respond(out, parser, client_session, cookie, request);
|
||||
if(LogUtil.isLoggable(defaultPage.getClass(), Level.FINER))
|
||||
logRequest(parser, client_session, cookie, request, time);
|
||||
}
|
||||
else{
|
||||
out.setStatusCode( 404 );
|
||||
out.println( "404 Page Not Found: "+parser.getRequestURL() );
|
||||
logger.warning("Page not defined: " + parser.getRequestURL());
|
||||
}
|
||||
|
||||
//********************************************************************************
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "500 Internal Server Error", e);
|
||||
try {
|
||||
if (!out.isHeaderSent())
|
||||
out.setStatusCode(500);
|
||||
if (e.getMessage() != null)
|
||||
out.println("500 Internal Server Error: " + e.getMessage());
|
||||
else if (e.getCause() != null) {
|
||||
out.println("500 Internal Server Error: " + e.getCause().getMessage());
|
||||
} else {
|
||||
out.println("500 Internal Server Error: " + e);
|
||||
}
|
||||
}catch(IOException ioe){
|
||||
logger.log(Level.SEVERE, null, ioe);
|
||||
}
|
||||
}
|
||||
|
||||
try{
|
||||
out.close();
|
||||
in.close();
|
||||
socket.close();
|
||||
} catch( Exception e ) {
|
||||
logger.log(Level.WARNING, "Could not close connection", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected static void logRequest(HttpHeaderParser parser,
|
||||
Map<String,Object> client_session,
|
||||
Map<String,String> cookie,
|
||||
Map<String,String> request,
|
||||
long time){
|
||||
// Debug
|
||||
if(logger.isLoggable(Level.FINEST) ){
|
||||
logger.finer(
|
||||
"Received request: " + parser.getRequestURL()
|
||||
+ " (client_session: " + client_session
|
||||
+ ", cookie: " + cookie
|
||||
+ ", request: " + request + ")"
|
||||
+ ", time: "+ StringUtil.formatTimeToString(System.currentTimeMillis() - time));
|
||||
} else if(logger.isLoggable(Level.FINER)){
|
||||
logger.finer(
|
||||
"Received request: " + parser.getRequestURL()
|
||||
+ ", time: "+ StringUtil.formatTimeToString(System.currentTimeMillis() - time));
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015 Ziver Koc
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package zutil.net.http;
|
||||
|
||||
import zutil.StringUtil;
|
||||
import zutil.log.LogUtil;
|
||||
import zutil.net.threaded.ThreadedTCPNetworkServer;
|
||||
import zutil.net.threaded.ThreadedTCPNetworkServerThread;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.Socket;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
|
||||
/**
|
||||
* A simple web server that handles both cookies and
|
||||
* sessions for all the clients
|
||||
*
|
||||
* @author Ziver
|
||||
*/
|
||||
public class HttpServer extends ThreadedTCPNetworkServer{
|
||||
private static final Logger logger = LogUtil.getLogger();
|
||||
|
||||
public static final String SESSION_ID_KEY = "session_id";
|
||||
public static final String SESSION_TTL_KEY = "session_ttl";
|
||||
public static final String SERVER_VERSION = "Ziver HttpServer 1.1";
|
||||
public static final int SESSION_TTL = 10*60*1000; // in milliseconds
|
||||
|
||||
|
||||
private Map<String,HttpPage> pages;
|
||||
private HttpPage defaultPage;
|
||||
private Map<Integer,Map<String,Object>> sessions;
|
||||
private int nextSessionId;
|
||||
|
||||
/**
|
||||
* Creates a new instance of the sever
|
||||
*
|
||||
* @param port The port that the server should listen to
|
||||
*/
|
||||
public HttpServer(int port){
|
||||
this(port, null, null);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new instance of the sever
|
||||
*
|
||||
* @param port The port that the server should listen to
|
||||
* @param keyStore If this is not null then the server will use SSL connection with this keyStore file path
|
||||
* @param keyStorePass If this is not null then the server will use a SSL connection with the given certificate
|
||||
*/
|
||||
public HttpServer(int port, File keyStore, String keyStorePass){
|
||||
super( port, keyStore, keyStorePass );
|
||||
|
||||
pages = new ConcurrentHashMap<>();
|
||||
sessions = new ConcurrentHashMap<>();
|
||||
nextSessionId = 0;
|
||||
|
||||
Timer timer = new Timer();
|
||||
timer.schedule(new SessionGarbageCollector(), 10000, SESSION_TTL / 2);
|
||||
|
||||
logger.info("HTTP"+(keyStore==null?"":"S")+" Server ready!");
|
||||
}
|
||||
|
||||
/**
|
||||
* This class acts as an garbage collector that
|
||||
* removes old sessions from the session HashMap
|
||||
*
|
||||
* @author Ziver
|
||||
*/
|
||||
private class SessionGarbageCollector extends TimerTask {
|
||||
public void run(){
|
||||
Object[] keys = sessions.keySet().toArray();
|
||||
for(Object key : keys){
|
||||
Map<String,Object> session = sessions.get(key);
|
||||
|
||||
// Check if session is still valid
|
||||
if((Long)session.get(SESSION_TTL_KEY) < System.currentTimeMillis()){
|
||||
sessions.remove(key);
|
||||
logger.fine("Removing Session: "+key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a HttpPage to a specific URL
|
||||
*
|
||||
* @param name The URL or name of the page
|
||||
* @param page The page itself
|
||||
*/
|
||||
public void setPage(String name, HttpPage page){
|
||||
if(name.charAt(0) != '/')
|
||||
name = "/"+name;
|
||||
pages.put(name, page);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a default page that will be shown
|
||||
* if there is no other matching page,
|
||||
*
|
||||
* @param page The HttpPage that will be shown
|
||||
*/
|
||||
public void setDefaultPage(HttpPage page){
|
||||
defaultPage = page;
|
||||
}
|
||||
|
||||
protected ThreadedTCPNetworkServerThread getThreadInstance( Socket s ){
|
||||
try {
|
||||
return new HttpServerThread( s );
|
||||
} catch (IOException e) {
|
||||
logger.log(Level.SEVERE, "Could not start new Thread", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal class that handles all the requests
|
||||
*
|
||||
* @author Ziver
|
||||
*
|
||||
*/
|
||||
protected class HttpServerThread implements ThreadedTCPNetworkServerThread{
|
||||
private HttpPrintStream out;
|
||||
private BufferedReader in;
|
||||
private Socket socket;
|
||||
|
||||
public HttpServerThread(Socket socket) throws IOException{
|
||||
out = new HttpPrintStream(socket.getOutputStream());
|
||||
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
|
||||
this.socket = socket;
|
||||
}
|
||||
|
||||
public void run(){
|
||||
String tmp = null;
|
||||
HttpHeaderParser headerParser = new HttpHeaderParser(in);
|
||||
|
||||
//**************************** REQUEST *********************************
|
||||
try {
|
||||
long time = System.currentTimeMillis();
|
||||
HttpHeader header = headerParser.read();
|
||||
if(header == null){
|
||||
logger.finer("No header received");
|
||||
return;
|
||||
}
|
||||
|
||||
//******* Read in the post data if available
|
||||
if( header.getHeader("Content-Length") != null ){
|
||||
// Reads the post data size
|
||||
tmp = header.getHeader("Content-Length");
|
||||
int post_data_length = Integer.parseInt( tmp );
|
||||
// read the data
|
||||
StringBuilder tmpBuff = new StringBuilder();
|
||||
// read the data
|
||||
for(int i=0; i<post_data_length ;i++){
|
||||
tmpBuff.append((char)in.read());
|
||||
}
|
||||
|
||||
tmp = header.getHeader("Content-Type");
|
||||
if( tmp.contains("application/x-www-form-urlencoded") ){
|
||||
// get the variables
|
||||
HttpHeaderParser.parseURLParameters(header, tmpBuff.toString());
|
||||
}
|
||||
else if( tmp.contains("application/soap+xml" ) ||
|
||||
tmp.contains("text/xml") ||
|
||||
tmp.contains("text/plain") ){
|
||||
// save the variables
|
||||
header.putURLAttribute("" , tmpBuff.toString());
|
||||
}
|
||||
else if( tmp.contains("multipart/form-data") ){
|
||||
// TODO: File upload
|
||||
throw new UnsupportedOperationException("HTTP Content-Type 'multipart-form-data' not supported." );
|
||||
}
|
||||
}
|
||||
|
||||
//**************************** HANDLE REQUEST *********************************
|
||||
// Get the client session or create one
|
||||
Map<String, Object> session;
|
||||
long ttlTime = System.currentTimeMillis() + SESSION_TTL;
|
||||
String sessionCookie = header.getCookie(SESSION_ID_KEY);
|
||||
if( sessionCookie != null && sessions.containsKey(sessionCookie) &&
|
||||
(Long)sessions.get(sessionCookie).get(SESSION_TTL_KEY) < System.currentTimeMillis()){ // Check if session is still valid
|
||||
|
||||
session = sessions.get(sessionCookie);
|
||||
// renew the session TTL
|
||||
session.put(SESSION_TTL_KEY, ttlTime);
|
||||
}
|
||||
else{
|
||||
session = Collections.synchronizedMap(new HashMap<String, Object>());
|
||||
session.put(SESSION_ID_KEY, nextSessionId );
|
||||
session.put(SESSION_TTL_KEY, ttlTime );
|
||||
sessions.put(nextSessionId, session );
|
||||
++nextSessionId;
|
||||
}
|
||||
|
||||
//**************************** RESPONSE ************************************
|
||||
out.setStatusCode(200);
|
||||
out.setHeader("Server", SERVER_VERSION);
|
||||
out.setHeader("Content-Type", "text/html");
|
||||
out.setCookie(SESSION_ID_KEY, ""+session.get(SESSION_ID_KEY));
|
||||
|
||||
if( header.getRequestURL() != null && pages.containsKey(header.getRequestURL())){
|
||||
HttpPage page = pages.get(header.getRequestURL());
|
||||
page.respond(out, header, session, header.getCookieMap(), header.getUrlAttributeMap());
|
||||
if(LogUtil.isLoggable(page.getClass(), Level.FINER))
|
||||
logRequest(header, session, time);
|
||||
}
|
||||
else if( header.getRequestURL() != null && defaultPage != null ){
|
||||
defaultPage.respond(out, header, session, header.getCookieMap(), header.getUrlAttributeMap());
|
||||
if(LogUtil.isLoggable(defaultPage.getClass(), Level.FINER))
|
||||
logRequest(header, session, time);
|
||||
}
|
||||
else{
|
||||
out.setStatusCode(404);
|
||||
out.println("404 Page Not Found: "+header.getRequestURL());
|
||||
logger.warning("Page not defined: " + header.getRequestURL());
|
||||
}
|
||||
|
||||
//********************************************************************************
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "500 Internal Server Error", e);
|
||||
try {
|
||||
if (!out.isHeaderSent())
|
||||
out.setStatusCode(500);
|
||||
if (e.getMessage() != null)
|
||||
out.println("500 Internal Server Error: " + e.getMessage());
|
||||
else if (e.getCause() != null) {
|
||||
out.println("500 Internal Server Error: " + e.getCause().getMessage());
|
||||
} else {
|
||||
out.println("500 Internal Server Error: " + e);
|
||||
}
|
||||
}catch(IOException ioe){
|
||||
logger.log(Level.SEVERE, null, ioe);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
try{
|
||||
out.close();
|
||||
in.close();
|
||||
socket.close();
|
||||
} catch( Exception e ) {
|
||||
logger.log(Level.WARNING, "Could not close connection", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected static void logRequest(HttpHeader header,
|
||||
Map<String,Object> session,
|
||||
long time){
|
||||
// Debug
|
||||
if(logger.isLoggable(Level.FINEST) ){
|
||||
StringBuilder buff = new StringBuilder();
|
||||
buff.append("Received request: ").append(header.getRequestURL());
|
||||
buff.append(", (");
|
||||
buff.append("request: ").append(header.toStringAttributes());
|
||||
buff.append(", cookies: ").append(header.toStringCookies());
|
||||
buff.append(", session: ").append(session);
|
||||
buff.append(")");
|
||||
buff.append(", time: "+ StringUtil.formatTimeToString(System.currentTimeMillis() - time));
|
||||
|
||||
logger.finer(buff.toString());
|
||||
} else if(logger.isLoggable(Level.FINER)){
|
||||
logger.finer(
|
||||
"Received request: " + header.getRequestURL()
|
||||
+ ", time: "+ StringUtil.formatTimeToString(System.currentTimeMillis() - time));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
290
src/zutil/net/http/multipart/MultipartParser.java
Normal file → Executable file
290
src/zutil/net/http/multipart/MultipartParser.java
Normal file → Executable file
|
|
@ -1,145 +1,145 @@
|
|||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015 Ziver Koc
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package zutil.net.http.multipart;
|
||||
|
||||
import zutil.ProgressListener;
|
||||
import zutil.net.http.HttpHeaderParser;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Parses a multipart/form-data http request,
|
||||
* saves files to temporary location.
|
||||
*
|
||||
* http://www.ietf.org/rfc/rfc1867.txt
|
||||
*
|
||||
* @author Ziver
|
||||
*
|
||||
*/
|
||||
public class MultipartParser {
|
||||
/** This is the temporary directory for the received files */
|
||||
private File tempDir;
|
||||
/** This is the delimiter that will separate the fields */
|
||||
private String delimiter;
|
||||
/** The length of the HTTP Body */
|
||||
private long contentLength;
|
||||
/** This is the input stream */
|
||||
private BufferedReader in;
|
||||
|
||||
/** This is the listener that will listen on the progress */
|
||||
private ProgressListener<MultipartParser,MultipartField> listener;
|
||||
|
||||
|
||||
|
||||
public MultipartParser(BufferedReader in, HttpHeaderParser header){
|
||||
this.in = in;
|
||||
|
||||
String cotype = header.getHeader("Content-type");
|
||||
cotype = cotype.split(" *; *")[1];
|
||||
delimiter = cotype.split(" *= *")[1];
|
||||
|
||||
contentLength = Long.parseLong( header.getHeader("Content-Length") );
|
||||
}
|
||||
|
||||
public MultipartParser(BufferedReader in, HttpServletRequest req){
|
||||
this.in = in;
|
||||
|
||||
String cotype = req.getHeader("Content-type");
|
||||
cotype = cotype.split(" *; *")[1];
|
||||
delimiter = cotype.split(" *= *")[1];
|
||||
|
||||
contentLength = req.getContentLength();
|
||||
}
|
||||
|
||||
public MultipartParser(BufferedReader in, String delimiter, long length){
|
||||
this.in = in;
|
||||
this.delimiter = delimiter;
|
||||
this.contentLength = length;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param listener is the listener that will be called for progress
|
||||
*/
|
||||
public void setListener(ProgressListener<MultipartParser,MultipartField> listener){
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the HTTP Body and returns a list of fields
|
||||
*
|
||||
* @return A list of FormField
|
||||
*/
|
||||
public List<MultipartField> parse() throws IOException{
|
||||
ArrayList<MultipartField> list = new ArrayList<MultipartField>();
|
||||
parse(list, delimiter);
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
private void parse(List<MultipartField> list, String delimiter) throws IOException{
|
||||
// TODO:
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a temporary file in either the system
|
||||
* temporary folder or by the setTempDir() function
|
||||
*
|
||||
* @return the temporary file
|
||||
*/
|
||||
protected File createTempFile() throws IOException{
|
||||
if(tempDir != null)
|
||||
return File.createTempFile("upload", ".part", tempDir.getAbsoluteFile());
|
||||
else
|
||||
return File.createTempFile("upload", ".part");
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the initial delimiter
|
||||
*
|
||||
* @param delimiter is the new delimiter
|
||||
*/
|
||||
public void setDelimiter(String delimiter){
|
||||
this.delimiter = delimiter;
|
||||
}
|
||||
|
||||
public void setTempDir(File dir){
|
||||
if(!dir.isDirectory())
|
||||
throw new RuntimeException("\""+dir.getAbsolutePath()+"\" is not a directory!");
|
||||
if(!dir.canWrite())
|
||||
throw new RuntimeException("\""+dir.getAbsolutePath()+"\" is not writable!");
|
||||
tempDir = dir;
|
||||
}
|
||||
|
||||
public long getContentLength(){
|
||||
return contentLength;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015 Ziver Koc
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package zutil.net.http.multipart;
|
||||
|
||||
import zutil.ProgressListener;
|
||||
import zutil.net.http.HttpHeader;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Parses a multipart/form-data http request,
|
||||
* saves files to temporary location.
|
||||
*
|
||||
* http://www.ietf.org/rfc/rfc1867.txt
|
||||
*
|
||||
* @author Ziver
|
||||
*
|
||||
*/
|
||||
public class MultipartParser {
|
||||
/** This is the temporary directory for the received files */
|
||||
private File tempDir;
|
||||
/** This is the delimiter that will separate the fields */
|
||||
private String delimiter;
|
||||
/** The length of the HTTP Body */
|
||||
private long contentLength;
|
||||
/** This is the input stream */
|
||||
private BufferedReader in;
|
||||
|
||||
/** This is the listener that will listen on the progress */
|
||||
private ProgressListener<MultipartParser,MultipartField> listener;
|
||||
|
||||
|
||||
|
||||
public MultipartParser(BufferedReader in, HttpHeader header){
|
||||
this.in = in;
|
||||
|
||||
String cotype = header.getHeader("Content-type");
|
||||
cotype = cotype.split(" *; *")[1];
|
||||
delimiter = cotype.split(" *= *")[1];
|
||||
|
||||
contentLength = Long.parseLong( header.getHeader("Content-Length") );
|
||||
}
|
||||
|
||||
public MultipartParser(BufferedReader in, HttpServletRequest req){
|
||||
this.in = in;
|
||||
|
||||
String cotype = req.getHeader("Content-type");
|
||||
cotype = cotype.split(" *; *")[1];
|
||||
delimiter = cotype.split(" *= *")[1];
|
||||
|
||||
contentLength = req.getContentLength();
|
||||
}
|
||||
|
||||
public MultipartParser(BufferedReader in, String delimiter, long length){
|
||||
this.in = in;
|
||||
this.delimiter = delimiter;
|
||||
this.contentLength = length;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param listener is the listener that will be called for progress
|
||||
*/
|
||||
public void setListener(ProgressListener<MultipartParser,MultipartField> listener){
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the HTTP Body and returns a list of fields
|
||||
*
|
||||
* @return A list of FormField
|
||||
*/
|
||||
public List<MultipartField> parse() throws IOException{
|
||||
ArrayList<MultipartField> list = new ArrayList<MultipartField>();
|
||||
parse(list, delimiter);
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
private void parse(List<MultipartField> list, String delimiter) throws IOException{
|
||||
// TODO:
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a temporary file in either the system
|
||||
* temporary folder or by the setTempDir() function
|
||||
*
|
||||
* @return the temporary file
|
||||
*/
|
||||
protected File createTempFile() throws IOException{
|
||||
if(tempDir != null)
|
||||
return File.createTempFile("upload", ".part", tempDir.getAbsoluteFile());
|
||||
else
|
||||
return File.createTempFile("upload", ".part");
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the initial delimiter
|
||||
*
|
||||
* @param delimiter is the new delimiter
|
||||
*/
|
||||
public void setDelimiter(String delimiter){
|
||||
this.delimiter = delimiter;
|
||||
}
|
||||
|
||||
public void setTempDir(File dir){
|
||||
if(!dir.isDirectory())
|
||||
throw new RuntimeException("\""+dir.getAbsolutePath()+"\" is not a directory!");
|
||||
if(!dir.canWrite())
|
||||
throw new RuntimeException("\""+dir.getAbsolutePath()+"\" is not writable!");
|
||||
tempDir = dir;
|
||||
}
|
||||
|
||||
public long getContentLength(){
|
||||
return contentLength;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
328
src/zutil/net/http/pages/HttpFilePage.java
Normal file → Executable file
328
src/zutil/net/http/pages/HttpFilePage.java
Normal file → Executable file
|
|
@ -1,164 +1,164 @@
|
|||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015 Ziver Koc
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package zutil.net.http.pages;
|
||||
|
||||
import zutil.io.IOUtil;
|
||||
import zutil.io.file.FileUtil;
|
||||
import zutil.log.LogUtil;
|
||||
import zutil.net.http.HttpHeaderParser;
|
||||
import zutil.net.http.HttpPage;
|
||||
import zutil.net.http.HttpPrintStream;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* This Http Page will host static content from the server.
|
||||
*
|
||||
* Created by Ziver on 2015-03-30.
|
||||
*/
|
||||
public class HttpFilePage implements HttpPage{
|
||||
private static final Logger log = LogUtil.getLogger();
|
||||
|
||||
private File resource_root;
|
||||
private boolean showFolders;
|
||||
private boolean redirectToIndex;
|
||||
|
||||
/**
|
||||
* @param file a reference to a root directory or a file.
|
||||
*/
|
||||
public HttpFilePage(File file){
|
||||
this.resource_root = file;
|
||||
this.showFolders = true;
|
||||
this.redirectToIndex = true;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void respond(HttpPrintStream out,
|
||||
HttpHeaderParser client_info,
|
||||
Map<String, Object> session,
|
||||
Map<String, String> cookie,
|
||||
Map<String, String> request) throws IOException{
|
||||
|
||||
try {
|
||||
// Is the root only one file or a folder
|
||||
if (resource_root.isFile()) {
|
||||
deliverFile(resource_root, out);
|
||||
}
|
||||
else { // Resource root is a folder
|
||||
File file = new File(resource_root,
|
||||
client_info.getRequestURL());
|
||||
if(file.getCanonicalPath().startsWith(resource_root.getCanonicalPath())){
|
||||
if(file.isDirectory() && showFolders){
|
||||
File indexFile = new File(file, "index.html");
|
||||
// Redirect to index.html
|
||||
if(redirectToIndex && indexFile.isFile()) {
|
||||
deliverFile(indexFile, out);
|
||||
}
|
||||
// Show folder contents
|
||||
else if(showFolders){
|
||||
out.println("<HTML><BODY><H1>Directory: " + client_info.getRequestURL() + "</H1>");
|
||||
out.println("<HR><UL>");
|
||||
for (String f : file.list()) {
|
||||
String url = client_info.getRequestURL();
|
||||
out.println("<LI><A href='" +
|
||||
url + (url.charAt(url.length()-1)=='/'?"":"/")+ f
|
||||
+"'>" + f + "</A></LI>");
|
||||
}
|
||||
out.println("</UL><HR></BODY></HTML>");
|
||||
}
|
||||
else {
|
||||
throw new SecurityException("User not allowed to view folder: root=" + resource_root.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
else {
|
||||
deliverFile(file, out);
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw new SecurityException("File is outside of root directory: root=" + resource_root.getAbsolutePath() + " file=" + file.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
|
||||
}catch (FileNotFoundException e){
|
||||
if(!out.isHeaderSent())
|
||||
out.setStatusCode(404);
|
||||
log.log(Level.WARNING, e.getMessage());
|
||||
out.println("404 Page Not Found: " + client_info.getRequestURL());
|
||||
}catch (SecurityException e){
|
||||
if(!out.isHeaderSent())
|
||||
out.setStatusCode(404);
|
||||
log.log(Level.WARNING, e.getMessage());
|
||||
out.println("404 Page Not Found: " + client_info.getRequestURL() );
|
||||
}catch (IOException e){
|
||||
if(!out.isHeaderSent())
|
||||
out.setStatusCode(500);
|
||||
log.log(Level.WARNING, null, e);
|
||||
out.println("500 Internal Server Error: "+e.getMessage() );
|
||||
}
|
||||
}
|
||||
|
||||
private void deliverFile(File file, HttpPrintStream out) throws IOException {
|
||||
out.setHeader("Content-Type", getMIMEType(file));
|
||||
out.flush();
|
||||
|
||||
//InputStream in = new BufferedInputStream(new FileInputStream(file));
|
||||
InputStream in = new FileInputStream(file);
|
||||
IOUtil.copyStream(in, out);
|
||||
in.close();
|
||||
}
|
||||
|
||||
private String getMIMEType(File file){
|
||||
switch(FileUtil.getFileExtension(file)){
|
||||
case "css": return "text/css";
|
||||
case "cvs": return "text/csv";
|
||||
case "jpg": return "image/jpeg";
|
||||
case "js": return "application/javascript";
|
||||
case "png": return "image/png";
|
||||
case "htm":
|
||||
case "html": return "text/html";
|
||||
case "xml": return "text/xml";
|
||||
default: return "text/plain";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Enable or disable showing of folder contents
|
||||
*/
|
||||
public void showFolders(boolean enabled){
|
||||
this.showFolders = enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* If directory links should be redirected to index files
|
||||
*/
|
||||
public void redirectToIndexFile(boolean enabled){
|
||||
this.redirectToIndex = enabled;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015 Ziver Koc
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package zutil.net.http.pages;
|
||||
|
||||
import zutil.io.IOUtil;
|
||||
import zutil.io.file.FileUtil;
|
||||
import zutil.log.LogUtil;
|
||||
import zutil.net.http.HttpHeader;
|
||||
import zutil.net.http.HttpPage;
|
||||
import zutil.net.http.HttpPrintStream;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* This Http Page will host static content from the server.
|
||||
*
|
||||
* Created by Ziver on 2015-03-30.
|
||||
*/
|
||||
public class HttpFilePage implements HttpPage{
|
||||
private static final Logger log = LogUtil.getLogger();
|
||||
|
||||
private File resource_root;
|
||||
private boolean showFolders;
|
||||
private boolean redirectToIndex;
|
||||
|
||||
/**
|
||||
* @param file a reference to a root directory or a file.
|
||||
*/
|
||||
public HttpFilePage(File file){
|
||||
this.resource_root = file;
|
||||
this.showFolders = true;
|
||||
this.redirectToIndex = true;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void respond(HttpPrintStream out,
|
||||
HttpHeader headers,
|
||||
Map<String, Object> session,
|
||||
Map<String, String> cookie,
|
||||
Map<String, String> request) throws IOException{
|
||||
|
||||
try {
|
||||
// Is the root only one file or a folder
|
||||
if (resource_root.isFile()) {
|
||||
deliverFile(resource_root, out);
|
||||
}
|
||||
else { // Resource root is a folder
|
||||
File file = new File(resource_root,
|
||||
headers.getRequestURL());
|
||||
if(file.getCanonicalPath().startsWith(resource_root.getCanonicalPath())){
|
||||
if(file.isDirectory() && showFolders){
|
||||
File indexFile = new File(file, "index.html");
|
||||
// Redirect to index.html
|
||||
if(redirectToIndex && indexFile.isFile()) {
|
||||
deliverFile(indexFile, out);
|
||||
}
|
||||
// Show folder contents
|
||||
else if(showFolders){
|
||||
out.println("<HTML><BODY><H1>Directory: " + headers.getRequestURL() + "</H1>");
|
||||
out.println("<HR><UL>");
|
||||
for (String f : file.list()) {
|
||||
String url = headers.getRequestURL();
|
||||
out.println("<LI><A href='" +
|
||||
url + (url.charAt(url.length()-1)=='/'?"":"/")+ f
|
||||
+"'>" + f + "</A></LI>");
|
||||
}
|
||||
out.println("</UL><HR></BODY></HTML>");
|
||||
}
|
||||
else {
|
||||
throw new SecurityException("User not allowed to view folder: root=" + resource_root.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
else {
|
||||
deliverFile(file, out);
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw new SecurityException("File is outside of root directory: root=" + resource_root.getAbsolutePath() + " file=" + file.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
|
||||
}catch (FileNotFoundException e){
|
||||
if(!out.isHeaderSent())
|
||||
out.setStatusCode(404);
|
||||
log.log(Level.WARNING, e.getMessage());
|
||||
out.println("404 Page Not Found: " + headers.getRequestURL());
|
||||
}catch (SecurityException e){
|
||||
if(!out.isHeaderSent())
|
||||
out.setStatusCode(404);
|
||||
log.log(Level.WARNING, e.getMessage());
|
||||
out.println("404 Page Not Found: " + headers.getRequestURL() );
|
||||
}catch (IOException e){
|
||||
if(!out.isHeaderSent())
|
||||
out.setStatusCode(500);
|
||||
log.log(Level.WARNING, null, e);
|
||||
out.println("500 Internal Server Error: "+e.getMessage() );
|
||||
}
|
||||
}
|
||||
|
||||
private void deliverFile(File file, HttpPrintStream out) throws IOException {
|
||||
out.setHeader("Content-Type", getMIMEType(file));
|
||||
out.flush();
|
||||
|
||||
//InputStream in = new BufferedInputStream(new FileInputStream(file));
|
||||
InputStream in = new FileInputStream(file);
|
||||
IOUtil.copyStream(in, out);
|
||||
in.close();
|
||||
}
|
||||
|
||||
private String getMIMEType(File file){
|
||||
switch(FileUtil.getFileExtension(file)){
|
||||
case "css": return "text/css";
|
||||
case "cvs": return "text/csv";
|
||||
case "jpg": return "image/jpeg";
|
||||
case "js": return "application/javascript";
|
||||
case "png": return "image/png";
|
||||
case "htm":
|
||||
case "html": return "text/html";
|
||||
case "xml": return "text/xml";
|
||||
default: return "text/plain";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Enable or disable showing of folder contents
|
||||
*/
|
||||
public void showFolders(boolean enabled){
|
||||
this.showFolders = enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* If directory links should be redirected to index files
|
||||
*/
|
||||
public void redirectToIndexFile(boolean enabled){
|
||||
this.redirectToIndex = enabled;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<String, LinkedList<StandardSSDPInfo>> services_st;
|
||||
/** Map of all unique services received **/
|
||||
private HashMap<String, StandardSSDPInfo> 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<String,String> 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<StandardSSDPInfo> 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<String, LinkedList<StandardSSDPInfo>> services_st;
|
||||
/** Map of all unique services received **/
|
||||
private HashMap<String, StandardSSDPInfo> 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<String,String> 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<StandardSSDPInfo> 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<StandardSSDPInfo>());
|
||||
services_st.get(header.getHeader("ST")).add(service);
|
||||
}
|
||||
|
||||
service.setLocation(header.getHeader("LOCATION"));
|
||||
service.setST(st);
|
||||
service.setUSN(usn);
|
||||
service.setInetAddress(packet.getAddress());
|
||||
if (header.getHeader("Cache-Control") != null) {
|
||||
service.setExpirationTime(
|
||||
System.currentTimeMillis() + 1000 * getCacheTime(header.getHeader("Cache-Control")));
|
||||
}
|
||||
service.readHeaders(header);
|
||||
|
||||
if (listener != null && newService)
|
||||
listener.newService(service);
|
||||
} catch (IOException e){
|
||||
logger.log(Level.SEVERE, null, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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<StandardSSDPInfo>() );
|
||||
services_st.get( header.getHeader("ST") ).add( service );
|
||||
}
|
||||
|
||||
service.setLocation( header.getHeader("LOCATION") );
|
||||
service.setST( st );
|
||||
service.setUSN( usn );
|
||||
service.setInetAddress(packet.getAddress());
|
||||
if(header.getHeader("Cache-Control") != null) {
|
||||
service.setExpirationTime(
|
||||
System.currentTimeMillis() + 1000 * getCacheTime(header.getHeader("Cache-Control")));
|
||||
}
|
||||
service.readHeaders(header);
|
||||
|
||||
if(listener != null && newService)
|
||||
listener.newService(service);
|
||||
}
|
||||
|
||||
private long getCacheTime(String cache_control){
|
||||
long ret = 0;
|
||||
String[] tmp = cache_control.split(",");
|
||||
for( String element : tmp ){
|
||||
element = element.replaceAll("\\s", "").toLowerCase();
|
||||
if( element.startsWith("max-age=") ){
|
||||
ret = Long.parseLong( element.substring( "max-age=".length() ) );
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
public interface SSDPServiceListener{
|
||||
public void newService(StandardSSDPInfo service);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<String, SSDPServiceInfo> services;
|
||||
|
||||
|
||||
|
||||
public SSDPServer() throws IOException{
|
||||
super( null, SSDP_MULTICAST_ADDR, SSDP_PORT );
|
||||
super.setThread( this );
|
||||
|
||||
services = new HashMap<String, SSDPServiceInfo>();
|
||||
|
||||
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<String, SSDPServiceInfo> services;
|
||||
|
||||
|
||||
|
||||
public SSDPServer() throws IOException{
|
||||
super( null, SSDP_MULTICAST_ADDR, SSDP_PORT );
|
||||
super.setThread( this );
|
||||
|
||||
services = new HashMap<String, SSDPServiceInfo>();
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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<String, String> 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<String,String> 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<String, String> 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<String> 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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
232
src/zutil/net/upnp/UPnPMediaServer.java
Normal file → Executable file
232
src/zutil/net/upnp/UPnPMediaServer.java
Normal file → Executable file
|
|
@ -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<String, Object> session, Map<String, String> cookie,
|
||||
Map<String, String> request) throws IOException {
|
||||
|
||||
out.enableBuffering(true);
|
||||
out.setHeader("Content-Type", "text/xml");
|
||||
|
||||
out.println("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
|
||||
out.println("<root xmlns=\"urn:schemas-upnp-org:service:ContentDirectory:1\">");
|
||||
out.println(" <specVersion>");
|
||||
out.println(" <major>1</major>");
|
||||
out.println(" <minor>0</minor>");
|
||||
out.println(" </specVersion>");
|
||||
out.println(" <URLBase>"+url+"</URLBase>");//"+ssdp.getLocation()+"
|
||||
out.println(" <device>");
|
||||
out.println(" <deviceType>urn:schemas-upnp-org:device:MediaServer:1</deviceType>");
|
||||
out.println(" <friendlyName>ZupNP AV Media Server</friendlyName>");
|
||||
out.println(" <manufacturer>Ziver Koc</manufacturer>");
|
||||
out.println(" <manufacturerURL>http://ziver.koc.se</manufacturerURL>");
|
||||
out.println("");
|
||||
out.println(" <modelName>ZupNP Server</modelName>");
|
||||
out.println(" <modelDescription>UPnP AV Media Server</modelDescription>");
|
||||
out.println(" <modelNumber>0.1</modelNumber>");
|
||||
out.println(" <UDN>"+getUUID()+"</UDN>");
|
||||
out.println(" <serviceList>");
|
||||
out.println(" <service>");
|
||||
out.println(" <serviceType>urn:schemas-upnp-org:service:ConnectionManager:1</serviceType>");
|
||||
out.println(" <serviceId>urn:upnp-org:serviceId:CMGR_1-0</serviceId>");
|
||||
out.println(" <SCPDURL>CMGR_Control/GetServDesc</SCPDURL>");
|
||||
out.println(" <controlURL>CMGR_Control</controlURL>");
|
||||
out.println(" <eventSubURL>CMGR_Event</eventSubURL>");
|
||||
out.println(" </service>");
|
||||
out.println(" <service>");
|
||||
out.println(" <serviceType>urn:schemas-upnp-org:service:ContentDirectory:1</serviceType>");
|
||||
out.println(" <serviceId>urn:upnp-org:serviceId:CDS_1-0</serviceId>");
|
||||
out.println(" <SCPDURL>SCP/ContentDir</SCPDURL>");
|
||||
out.println(" <controlURL>Action/ContentDir</controlURL>");
|
||||
out.println(" <eventSubURL>Event/ContentDir</eventSubURL>");
|
||||
out.println(" </service>");
|
||||
out.println(" </serviceList>");
|
||||
out.println(" </device>");
|
||||
out.println("</root>");
|
||||
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<String, Object> session,
|
||||
Map<String, String> cookie,
|
||||
Map<String, String> request) throws IOException {
|
||||
|
||||
out.enableBuffering(true);
|
||||
out.setHeader("Content-Type", "text/xml");
|
||||
|
||||
out.println("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
|
||||
out.println("<root xmlns=\"urn:schemas-upnp-org:service:ContentDirectory:1\">");
|
||||
out.println(" <specVersion>");
|
||||
out.println(" <major>1</major>");
|
||||
out.println(" <minor>0</minor>");
|
||||
out.println(" </specVersion>");
|
||||
out.println(" <URLBase>"+url+"</URLBase>");//"+ssdp.getLocation()+"
|
||||
out.println(" <device>");
|
||||
out.println(" <deviceType>urn:schemas-upnp-org:device:MediaServer:1</deviceType>");
|
||||
out.println(" <friendlyName>ZupNP AV Media Server</friendlyName>");
|
||||
out.println(" <manufacturer>Ziver Koc</manufacturer>");
|
||||
out.println(" <manufacturerURL>http://ziver.koc.se</manufacturerURL>");
|
||||
out.println("");
|
||||
out.println(" <modelName>ZupNP Server</modelName>");
|
||||
out.println(" <modelDescription>UPnP AV Media Server</modelDescription>");
|
||||
out.println(" <modelNumber>0.1</modelNumber>");
|
||||
out.println(" <UDN>"+getUUID()+"</UDN>");
|
||||
out.println(" <serviceList>");
|
||||
out.println(" <service>");
|
||||
out.println(" <serviceType>urn:schemas-upnp-org:service:ConnectionManager:1</serviceType>");
|
||||
out.println(" <serviceId>urn:upnp-org:serviceId:CMGR_1-0</serviceId>");
|
||||
out.println(" <SCPDURL>CMGR_Control/GetServDesc</SCPDURL>");
|
||||
out.println(" <controlURL>CMGR_Control</controlURL>");
|
||||
out.println(" <eventSubURL>CMGR_Event</eventSubURL>");
|
||||
out.println(" </service>");
|
||||
out.println(" <service>");
|
||||
out.println(" <serviceType>urn:schemas-upnp-org:service:ContentDirectory:1</serviceType>");
|
||||
out.println(" <serviceId>urn:upnp-org:serviceId:CDS_1-0</serviceId>");
|
||||
out.println(" <SCPDURL>SCP/ContentDir</SCPDURL>");
|
||||
out.println(" <controlURL>Action/ContentDir</controlURL>");
|
||||
out.println(" <eventSubURL>Event/ContentDir</eventSubURL>");
|
||||
out.println(" </service>");
|
||||
out.println(" </serviceList>");
|
||||
out.println(" </device>");
|
||||
out.println("</root>");
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
1231
src/zutil/net/upnp/services/UPnPContentDirectory.java
Normal file → Executable file
1231
src/zutil/net/upnp/services/UPnPContentDirectory.java
Normal file → Executable file
File diff suppressed because it is too large
Load diff
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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<String, Object> session,
|
||||
Map<String, String> cookie,
|
||||
Map<String, String> request) throws IOException {
|
||||
try {
|
||||
out.println(
|
||||
execute(client_info.getRequestURL(), request));
|
||||
} catch (Throwable throwable) {
|
||||
throw new IOException(throwable);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private String execute(String targetMethod, Map<String, String> input) throws Throwable {
|
||||
if( wsDef.hasMethod(targetMethod) ){
|
||||
// Parse request
|
||||
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<String, String> input){
|
||||
Object[] inputParams = new Object[method.getInputCount()];
|
||||
|
||||
// Get the parameter values
|
||||
for(int i=0; i<method.getInputCount() ;i++){
|
||||
WSParameterDef param = method.getInput( i );
|
||||
if( input.containsKey(param.getName()) ){
|
||||
inputParams[i] = Converter.fromString(
|
||||
input.get(param.getName()),
|
||||
param.getParamClass());
|
||||
}
|
||||
}
|
||||
return inputParams;
|
||||
}
|
||||
|
||||
private void generateResponse(){
|
||||
|
||||
}
|
||||
}
|
||||
/*
|
||||
* 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.HttpHeader;
|
||||
import zutil.net.http.HttpPage;
|
||||
import zutil.net.http.HttpPrintStream;
|
||||
import zutil.net.ws.WSInterface;
|
||||
import zutil.net.ws.WSMethodDef;
|
||||
import zutil.net.ws.WSParameterDef;
|
||||
import zutil.net.ws.WebServiceDef;
|
||||
import zutil.parser.json.JSONObjectOutputStream;
|
||||
|
||||
import java.io.IOException;
|
||||
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,
|
||||
HttpHeader headers,
|
||||
Map<String, Object> session,
|
||||
Map<String, String> cookie,
|
||||
Map<String, String> request) throws IOException {
|
||||
try {
|
||||
out.println(
|
||||
execute(headers.getRequestURL(), request));
|
||||
} catch (Throwable throwable) {
|
||||
throw new IOException(throwable);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private String execute(String targetMethod, Map<String, String> input) throws Throwable {
|
||||
if( wsDef.hasMethod(targetMethod) ){
|
||||
// Parse request
|
||||
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<String, String> input){
|
||||
Object[] inputParams = new Object[method.getInputCount()];
|
||||
|
||||
// Get the parameter values
|
||||
for(int i=0; i<method.getInputCount() ;i++){
|
||||
WSParameterDef param = method.getInput( i );
|
||||
if( input.containsKey(param.getName()) ){
|
||||
inputParams[i] = Converter.fromString(
|
||||
input.get(param.getName()),
|
||||
param.getParamClass());
|
||||
}
|
||||
}
|
||||
return inputParams;
|
||||
}
|
||||
|
||||
private void generateResponse(){
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,11 +29,7 @@ import zutil.net.ws.WSClientFactory;
|
|||
import zutil.net.ws.WSInterface;
|
||||
import zutil.net.ws.WebServiceDef;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.net.URL;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,372 +1,372 @@
|
|||
/*
|
||||
* 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.HttpHeaderParser;
|
||||
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:
|
||||
* <br>-int
|
||||
* <br>-double
|
||||
* <br>-float
|
||||
* <br>-char
|
||||
* <br>-String
|
||||
* <br>-byte[]
|
||||
* <br>-And the Wrappers Classes except for Byte
|
||||
*
|
||||
* Output:
|
||||
* <br>-WSReturnObject
|
||||
* <br>-byte[]
|
||||
* <br>-int
|
||||
* <br>-double
|
||||
* <br>-float
|
||||
* <br>-char
|
||||
* <br>-String
|
||||
* <br>-Arrays of Output
|
||||
* <br>-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<String, Object> session,
|
||||
Map<String, String> cookie,
|
||||
Map<String, String> 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<Element> 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; i<m.getInputCount() ;i++){
|
||||
WSParameterDef param = m.getInput( i );
|
||||
if( e.element(param.getName()) != null ){
|
||||
params[i] = Converter.fromString(
|
||||
e.element(param.getName()).getTextTrim(),
|
||||
param.getParamClass());
|
||||
}
|
||||
}
|
||||
|
||||
// Invoke
|
||||
Object ret = m.invoke(obj, params);
|
||||
|
||||
// generate response XML
|
||||
if( m.getOutputCount()>0 ){
|
||||
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<m.getOutputCount() ;i++){
|
||||
WSParameterDef param = m.getOutput( i );
|
||||
generateSOAPXMLForObj(response,((WSReturnObject)ret).getValue(f[i]) , param.getName());
|
||||
}
|
||||
}
|
||||
else{
|
||||
generateSOAPXMLForObj(response, ret, m.getOutput(0).getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
else{
|
||||
throw new NoSuchMethodException("Unable to find method: "+e.getQName().getName()+"!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Generates a XML Element for a given Object. This method can
|
||||
* handle return values as XML Elements, WSReturnObject and
|
||||
* Java basic data types.
|
||||
*
|
||||
* @param root is the parent Element
|
||||
* @param obj is the object that is the return value
|
||||
* @param elementName is the name of the parent Element
|
||||
*/
|
||||
protected static void generateSOAPXMLForObj(Element root, Object obj, String elementName) throws IllegalArgumentException, IllegalAccessException{
|
||||
if(obj == null) return;
|
||||
if(byte[].class.isAssignableFrom(obj.getClass())){
|
||||
Element valueE = root.addElement( elementName );
|
||||
valueE.addAttribute("type", "xsd:"+ getSOAPClassName(obj.getClass()));
|
||||
String tmp = new sun.misc.BASE64Encoder().encode((byte[])obj);
|
||||
tmp = tmp.replaceAll("\\s", "");
|
||||
valueE.setText(tmp);
|
||||
}
|
||||
// return an array
|
||||
else if(obj.getClass().isArray()){
|
||||
Element array = root.addElement( (elementName.equals("element") ? "Array" : elementName) );
|
||||
String arrayType = "xsd:"+ getSOAPClassName(obj.getClass());
|
||||
arrayType = arrayType.replaceFirst("\\[\\]", "["+Array.getLength(obj)+"]");
|
||||
|
||||
array.addAttribute("type", "soap:Array");
|
||||
array.addAttribute("soap:arrayType", arrayType);
|
||||
for(int i=0; i<Array.getLength(obj) ;i++){
|
||||
generateSOAPXMLForObj(array, Array.get(obj, i), "element");
|
||||
}
|
||||
}
|
||||
else{
|
||||
Element objectE = root.addElement( elementName );
|
||||
if(obj instanceof Element)
|
||||
objectE.add( (Element)obj );
|
||||
else if(obj instanceof WSReturnObject){
|
||||
Field[] fields = obj.getClass().getFields();
|
||||
for(int i=0; i<fields.length ;i++){
|
||||
WSValueName tmp = fields[i].getAnnotation( WSValueName.class );
|
||||
String name;
|
||||
if(tmp != null) name = tmp.value();
|
||||
else name = "field"+i;
|
||||
generateSOAPXMLForObj(objectE, fields[i].get(obj), name);
|
||||
}
|
||||
}
|
||||
else {
|
||||
objectE.addAttribute("type", "xsd:"+ getSOAPClassName(obj.getClass()));
|
||||
objectE.addText( ""+obj );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected static String getSOAPClassName(Class<?> 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:
|
||||
* <br>-int
|
||||
* <br>-double
|
||||
* <br>-float
|
||||
* <br>-char
|
||||
* <br>-String
|
||||
* <br>-byte[]
|
||||
* <br>-And the Wrappers Classes except for Byte
|
||||
*
|
||||
* Output:
|
||||
* <br>-WSReturnObject
|
||||
* <br>-byte[]
|
||||
* <br>-int
|
||||
* <br>-double
|
||||
* <br>-float
|
||||
* <br>-char
|
||||
* <br>-String
|
||||
* <br>-Arrays of Output
|
||||
* <br>-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<String, Object> session,
|
||||
Map<String, String> cookie,
|
||||
Map<String, String> 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<Element> 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; i<m.getInputCount() ;i++){
|
||||
WSParameterDef param = m.getInput( i );
|
||||
if( e.element(param.getName()) != null ){
|
||||
params[i] = Converter.fromString(
|
||||
e.element(param.getName()).getTextTrim(),
|
||||
param.getParamClass());
|
||||
}
|
||||
}
|
||||
|
||||
// Invoke
|
||||
Object ret = m.invoke(obj, params);
|
||||
|
||||
// generate response XML
|
||||
if( m.getOutputCount()>0 ){
|
||||
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<m.getOutputCount() ;i++){
|
||||
WSParameterDef param = m.getOutput( i );
|
||||
generateSOAPXMLForObj(response,((WSReturnObject)ret).getValue(f[i]) , param.getName());
|
||||
}
|
||||
}
|
||||
else{
|
||||
generateSOAPXMLForObj(response, ret, m.getOutput(0).getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
else{
|
||||
throw new NoSuchMethodException("Unable to find method: "+e.getQName().getName()+"!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Generates a XML Element for a given Object. This method can
|
||||
* handle return values as XML Elements, WSReturnObject and
|
||||
* Java basic data types.
|
||||
*
|
||||
* @param root is the parent Element
|
||||
* @param obj is the object that is the return value
|
||||
* @param elementName is the name of the parent Element
|
||||
*/
|
||||
protected static void generateSOAPXMLForObj(Element root, Object obj, String elementName) throws IllegalArgumentException, IllegalAccessException{
|
||||
if(obj == null) return;
|
||||
if(byte[].class.isAssignableFrom(obj.getClass())){
|
||||
Element valueE = root.addElement( elementName );
|
||||
valueE.addAttribute("type", "xsd:"+ getSOAPClassName(obj.getClass()));
|
||||
String tmp = new sun.misc.BASE64Encoder().encode((byte[])obj);
|
||||
tmp = tmp.replaceAll("\\s", "");
|
||||
valueE.setText(tmp);
|
||||
}
|
||||
// return an array
|
||||
else if(obj.getClass().isArray()){
|
||||
Element array = root.addElement( (elementName.equals("element") ? "Array" : elementName) );
|
||||
String arrayType = "xsd:"+ getSOAPClassName(obj.getClass());
|
||||
arrayType = arrayType.replaceFirst("\\[\\]", "["+Array.getLength(obj)+"]");
|
||||
|
||||
array.addAttribute("type", "soap:Array");
|
||||
array.addAttribute("soap:arrayType", arrayType);
|
||||
for(int i=0; i<Array.getLength(obj) ;i++){
|
||||
generateSOAPXMLForObj(array, Array.get(obj, i), "element");
|
||||
}
|
||||
}
|
||||
else{
|
||||
Element objectE = root.addElement( elementName );
|
||||
if(obj instanceof Element)
|
||||
objectE.add( (Element)obj );
|
||||
else if(obj instanceof WSReturnObject){
|
||||
Field[] fields = obj.getClass().getFields();
|
||||
for(int i=0; i<fields.length ;i++){
|
||||
WSValueName tmp = fields[i].getAnnotation( WSValueName.class );
|
||||
String name;
|
||||
if(tmp != null) name = tmp.value();
|
||||
else name = "field"+i;
|
||||
generateSOAPXMLForObj(objectE, fields[i].get(obj), name);
|
||||
}
|
||||
}
|
||||
else {
|
||||
objectE.addAttribute("type", "xsd:"+ getSOAPClassName(obj.getClass()));
|
||||
objectE.addText( ""+obj );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected static String getSOAPClassName(Class<?> 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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
2
src/zutil/osal/app/linux/ProcStat.java
Normal file → Executable file
2
src/zutil/osal/app/linux/ProcStat.java
Normal file → Executable file
|
|
@ -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;
|
||||
|
|
|
|||
2
src/zutil/osal/app/windows/TypePerf.java
Normal file → Executable file
2
src/zutil/osal/app/windows/TypePerf.java
Normal file → Executable file
|
|
@ -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.
|
||||
|
|
|
|||
2
src/zutil/parser/CSVParser.java
Normal file → Executable file
2
src/zutil/parser/CSVParser.java
Normal file → Executable file
|
|
@ -24,8 +24,6 @@
|
|||
|
||||
package zutil.parser;
|
||||
|
||||
import zutil.struct.MutableInt;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.io.StringReader;
|
||||
|
|
|
|||
3
src/zutil/parser/Parser.java
Normal file → Executable file
3
src/zutil/parser/Parser.java
Normal file → Executable file
|
|
@ -24,10 +24,7 @@
|
|||
|
||||
package zutil.parser;
|
||||
|
||||
import zutil.struct.MutableInt;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
|
||||
/**
|
||||
* Created by Ziver
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
1
src/zutil/parser/json/JSONWriter.java
Normal file → Executable file
1
src/zutil/parser/json/JSONWriter.java
Normal file → Executable file
|
|
@ -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;
|
||||
|
|
|
|||
108
src/zutil/parser/wsdl/WSDLHttpPage.java
Normal file → Executable file
108
src/zutil/parser/wsdl/WSDLHttpPage.java
Normal file → Executable file
|
|
@ -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<String, Object> session,
|
||||
Map<String, String> cookie,
|
||||
Map<String, String> 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<String, Object> session,
|
||||
Map<String, String> cookie,
|
||||
Map<String, String> request) throws IOException{
|
||||
out.setHeader("Content-Type", "text/xml");
|
||||
wsdl.write( out );
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue