org.xlightweb.HttpUtils Maven / Gradle / Ivy
/*
* Copyright (c) xlightweb.org, 2008 - 2010. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Please refer to the LGPL license at: http://www.gnu.org/copyleft/lesser.txt
* The latest copy of this software may be found on http://www.xlightweb.org/
*/
package org.xlightweb;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.io.RandomAccessFile;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.SocketTimeoutException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.TimeZone;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import org.xlightweb.AbstractHttpConnection.IMultimodeExecutor;
import org.xsocket.DataConverter;
import org.xsocket.Execution;
import org.xsocket.ILifeCycle;
import org.xsocket.MaxReadSizeExceededException;
import org.xsocket.Resource;
import org.xsocket.SerializedTaskQueue;
import org.xsocket.connection.ConnectionUtils;
import org.xsocket.connection.IDataHandler;
import org.xsocket.connection.IDisconnectHandler;
import org.xsocket.connection.INonBlockingConnection;
import org.xsocket.connection.IServer;
import org.xsocket.connection.IWriteCompletionHandler;
import org.xsocket.connection.NonBlockingConnection;
import org.xsocket.connection.NonBlockingConnectionPool;
import org.xsocket.connection.IConnection.FlushMode;
/**
* A HTTP utility class
*
* @author [email protected]
*/
public final class HttpUtils {
static final int MAX_HEADER_SIZE = 8192;
static final byte CR = 13;
static final byte LF = 10;
static final byte SPACE = 32;
static final byte HTAB = 9;
static final byte AND = 38;
static final byte SLASH = 47;
static final byte COLON = 58;
static final byte EQUALS = 61;
static final byte QUESTION_MARK = 63;
private static final Logger LOG = Logger.getLogger(HttpUtils.class.getName());
private static final Bom BOM_UTF_8 = new Bom("UTF-8", new byte[] {(byte) 0xEF, (byte) 0xBB, (byte) 0xBF});
private static final Bom BOM_UTF_32BE = new Bom("UTF-32BE", new byte[] {(byte) 0x00, (byte) 0x00, (byte) 0xFE, (byte) 0xFF});
private static final Bom BOM_UTF_32LE = new Bom("UTF-32LE", new byte[] {(byte) 0xFF, (byte) 0xFE, (byte) 0x00, (byte) 0x00});
private static final Bom BOM_UTF_16BE = new Bom("UTF-16BE", new byte[] {(byte) 0xFE, (byte) 0xFF});
private static final Bom BOM_UTF_16LE = new Bom("UTF-16LE", new byte[] {(byte) 0xFF, (byte) 0xFE});
private static final int BOM_MAX_LENGTH = 4;
private static final Bom[] BOMS = new Bom[] { BOM_UTF_8, BOM_UTF_32BE, BOM_UTF_32LE, BOM_UTF_16LE, BOM_UTF_16BE };
private static final Timer TIMER = new Timer("xHttpTimer", true);
private static final Executor DEFAULT_WORKERPOOL = Executors.newCachedThreadPool(new XLightwebThreadFactory());
private static final String DATE_TIME_PATTERN_1 = "yyyy-MM-dd'T'HH:mm:ss";
private static final String DATE_TIME_PATTERN_2 = "yyyyMMdd'T'HHmmssz";
private static final String DATE_TIME_PATTERN_3 = "yyyy-MM-dd'T'HH:mm:ss.S";
private static final String RFC1123_TIME_PATTERN = "EEE, dd MMM yyyy HH:mm:ss zzz";
private static final String RFC1036_TIME_PATTERN = "EEEE, dd-MMM-yy HH:mm:ss zzz";
@SuppressWarnings("unchecked")
private static final Map bodyDataExecutionModeCache = ConnectionUtils.newMapCache(25);
@SuppressWarnings("unchecked")
private static final Map bodyCompleteListenerExecutionModeCache = ConnectionUtils.newMapCache(25);
@SuppressWarnings("unchecked")
private static final Map requestTimeoutHandlerExecutionModeCache = ConnectionUtils.newMapCache(25);
@SuppressWarnings("unchecked")
private static final Map completionHandlerInfoCache = ConnectionUtils.newMapCache(25);
@SuppressWarnings("unchecked")
private static final Map bodyCloseListenerExecutionModeCache = ConnectionUtils.newMapCache(25);
@SuppressWarnings("unchecked")
private static final Map mappingCache = ConnectionUtils.newMapCache(25);
@SuppressWarnings("unchecked")
private static final Map bodyDataHandlerExecutionModeCache = ConnectionUtils.newMapCache(25);
@SuppressWarnings("unchecked")
private static final Map bodyCompeleteListenerModeCache = ConnectionUtils.newMapCache(25);
@SuppressWarnings("unchecked")
private static final Map httpRequestHandlerInfoCache = ConnectionUtils.newMapCache(25);
private static RequestHandlerInfo emptyHttpRequestHandlerInfo;
@SuppressWarnings("unchecked")
private static final Map partHandlerInfoCache = ConnectionUtils.newMapCache(25);
private static PartHandlerInfo emptyPartHandlerInfo;
@SuppressWarnings("unchecked")
private static final Map httpResponseHandlerInfoCache = ConnectionUtils.newMapCache(25);
private static ResponseHandlerInfo emptyResponseHandlerInfo;
@SuppressWarnings("unchecked")
private static final Map httpConnectionHandlerInfoCache = ConnectionUtils.newMapCache(25);
private static final HttpConnectionHandlerInfo EMPTY_HTTP_CONNECTION_HANDLER_INFO = new HttpConnectionHandlerInfo(null);
private static String implementationVersion;
private static String implementationDate;
private static String xSocketImplementationVersion;
private static Map mimeTypeMap;
private static boolean showDetailedError;
private static Boolean isConnectHandlerWarningIsSuppressed = null;
static {
showDetailedError = Boolean.parseBoolean(System.getProperty(IHttpExchange.SHOW_DETAILED_ERROR_KEY, IHttpExchange.SHOW_DETAILED_ERROR_DEFAULT));
}
private static final char[] ISO_8859_1_Array = new char[256];
static {
for (int i = 0; i < ISO_8859_1_Array.length; i++) {
try {
ISO_8859_1_Array[i] = new String(new byte[] { (byte) i }, "ISO-8859-1").charAt(0);
} catch (UnsupportedEncodingException use) {
throw new RuntimeException(use);
}
}
}
private static final byte base64[] = {
(byte) 'A', (byte) 'B', (byte) 'C', (byte) 'D',
(byte) 'E', (byte) 'F', (byte) 'G', (byte) 'H',
(byte) 'I', (byte) 'J', (byte) 'K', (byte) 'L',
(byte) 'M', (byte) 'N', (byte) 'O', (byte) 'P',
(byte) 'Q', (byte) 'R', (byte) 'S', (byte) 'T',
(byte) 'U', (byte) 'V', (byte) 'W', (byte) 'X',
(byte) 'Y', (byte) 'Z', (byte) 'a', (byte) 'b',
(byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f',
(byte) 'g', (byte) 'h', (byte) 'i', (byte) 'j',
(byte) 'k', (byte) 'l', (byte) 'm', (byte) 'n',
(byte) 'o', (byte) 'p', (byte) 'q', (byte) 'r',
(byte) 's', (byte) 't', (byte) 'u', (byte) 'v',
(byte) 'w', (byte) 'x', (byte) 'y', (byte) 'z',
(byte) '0', (byte) '1', (byte) '2', (byte) '3',
(byte) '4', (byte) '5', (byte) '6', (byte) '7',
(byte) '8', (byte) '9', (byte) '+', (byte) '/',
(byte) '='
};
private HttpUtils() { }
/**
* returns a ISO_8859_1 byte array
*
* @return a ISO_8859_1 byte array
*/
static char[] getISO_8859_1_Array() {
return ISO_8859_1_Array;
}
/**
* returns true, if detailed error messages should been shown. See {@link IHttpExchange#SHOW_DETAILED_ERROR_KEY}
*
* @return true, if detailed error messages should been shown
*/
public static boolean isShowDetailedError() {
return showDetailedError;
}
/**
* clears the internal caches
*
*
This is a xSocket internal method and subject to change
*/
public static void clearCaches() {
bodyDataExecutionModeCache.clear();
mappingCache.clear();
bodyCompleteListenerExecutionModeCache.clear();;
requestTimeoutHandlerExecutionModeCache.clear();
completionHandlerInfoCache.clear();
bodyCloseListenerExecutionModeCache.clear();
httpRequestHandlerInfoCache.clear();
partHandlerInfoCache.clear();
httpResponseHandlerInfoCache.clear();
httpConnectionHandlerInfoCache.clear();
}
/**
* returns the reason text
*
* @param statusCode the status code
* @return the reason text
*/
public static String getReason(int statusCode) {
switch (statusCode) {
case 100:
return "Continue";
case 101:
return "Switching Protocols";
case 200:
return "OK";
case 201:
return "Created";
case 202:
return "Accepted";
case 203:
return "Non-Authoritative Information";
case 204:
return "No Content";
case 205:
return "Reset Content";
case 206:
return "Partial Content";
case 300:
return "Multiple Choices";
case 301:
return "Moved Permanently";
case 302:
return "Not Found";
case 303:
return "See other";
case 304:
return "Not Modifed";
case 305:
return "Use Proxy";
case 307:
return "Temporary Redirect";
case 400:
return "Bad Request";
case 401:
return "Unauthorized";
case 402:
return "Payment Required";
case 403:
return "Forbidden";
case 404:
return "Not found";
case 405:
return "Method Not Allowed";
case 406:
return "Not Acceptable";
case 407:
return "Proxy Authentication Required";
case 408:
return "Request Timeout";
case 409:
return "Conflict";
case 410:
return "Gone";
case 411:
return "Length Required";
case 412:
return "Precondition Failed";
case 413:
return "Request Entity Too Large";
case 414:
return "Request-URI Too Long";
case 415:
return "Unsupported Media Type";
case 416:
return "Requested Range Not Satisfiable";
case 417:
return "Expectation Failed";
case 500:
return "Internal Server Error";
case 501:
return "Not Implemented";
case 502:
return "Bad Gateway";
case 503:
return "Service Unavailable";
case 504:
return "Gateway Timeout";
case 505:
return "HTTP Version Not Supported";
default:
return " ";
}
}
/**
* encodes the given byte array
*
* @param bytes byte array to encode
* @return the encoded byte array
* @throws IOException if an exception occurs
*/
public static byte[] encodeBase64(byte bytes[]) throws IOException {
ByteArrayInputStream in = new ByteArrayInputStream(bytes);
ByteArrayOutputStream out = new ByteArrayOutputStream();
byte buffer[] = new byte[1024];
int got = -1;
int off = 0;
int count = 0;
while ((got = in.read(buffer, off, 1024 - off)) > 0) {
if (got >= 3) {
got += off;
off = 0;
while (off + 3 <= got) {
int c1 = (buffer[off] & 0xfc) >> 2;
int c2 = ((buffer[off]&0x3) << 4) | ((buffer[off+1]&0xf0) >>> 4);
int c3 = ((buffer[off+1] & 0x0f) << 2) | ((buffer[off+2] & 0xc0) >>> 6);
int c4 = buffer[off+2] & 0x3f;
switch (count) {
case 73:
out.write(base64[c1]);
out.write(base64[c2]);
out.write(base64[c3]);
out.write ('\n') ;
out.write(base64[c4]);
count = 1 ;
break ;
case 74:
out.write(base64[c1]);
out.write(base64[c2]);
out.write ('\n') ;
out.write(base64[c3]);
out.write(base64[c4]) ;
count = 2 ;
break ;
case 75:
out.write(base64[c1]);
out.write ('\n') ;
out.write(base64[c2]);
out.write(base64[c3]);
out.write(base64[c4]) ;
count = 3 ;
break ;
case 76:
out.write('\n') ;
out.write(base64[c1]);
out.write(base64[c2]);
out.write(base64[c3]);
out.write(base64[c4]);
count = 4;
break;
default:
out.write(base64[c1]);
out.write(base64[c2]);
out.write(base64[c3]);
out.write(base64[c4]);
count += 4;
break;
}
off += 3;
}
for ( int i = 0 ; i < 3 ;i++) {
buffer[i] = (i < got-off) ? buffer[off+i] : ((byte) 0);
}
off = got-off ;
} else {
off += got;
}
}
switch (off) {
case 1:
out.write(base64[(buffer[0] & 0xfc) >> 2]);
out.write(base64[((buffer[0]&0x3) << 4) | ((buffer[1]&0xf0) >>> 4)]);
out.write('=');
out.write('=');
break ;
case 2:
out.write(base64[(buffer[0] & 0xfc) >> 2]);
out.write(base64[((buffer[0]&0x3) << 4) | ((buffer[1]&0xf0) >>> 4)]);
out.write(base64[((buffer[1] & 0x0f) << 2) | ((buffer[2] & 0xc0) >>> 6)]);
out.write('=');
}
return out.toByteArray();
}
/**
* decodes the given byte array
*
* @param bytes byte array to decode
* @return the decoded byte array
* @throws IOException if an exception occurs
*/
public static byte[] decodeBase64(byte bytes[]) throws IOException {
ByteArrayInputStream in = new ByteArrayInputStream(bytes);
ByteArrayOutputStream out = new ByteArrayOutputStream();
byte buffer[] = new byte[1024];
byte chunk[] = new byte[4];
int got = -1;
int ready = 0;
fill : while ((got = in.read(buffer)) > 0) {
int skiped = 0;
while (skiped < got) {
while (ready < 4) {
if (skiped >= got) {
continue fill;
}
int ch = buffer[skiped++];
if ((ch >= 'A') && (ch <= 'Z')) {
ch = ch - 'A';
} else if ((ch >= 'a') && (ch <= 'z')) {
ch = ch - 'a' + 26;
} else if ((ch >= '0') && (ch <= '9')) {
ch = ch - '0' + 52;
} else {
switch (ch) {
case '=':
ch = 65;
break;
case '+':
ch = 62;
break;
case '/':
ch = 63;
break;
default:
ch = -1;
}
}
if (ch >= 0) {
chunk[ready++] = (byte) ch;
}
}
if (chunk[2] == 65) {
out.write(((chunk[0] & 0x3f) << 2) | ((chunk[1] & 0x30) >>> 4));
out.flush();
return out.toByteArray();
} else if (chunk[3] == 65) {
out.write(((chunk[0] & 0x3f) << 2) | ((chunk[1] & 0x30) >>> 4));
out.write(((chunk[1] & 0x0f) << 4) | ((chunk[2] & 0x3c) >>> 2));
out.flush();
return out.toByteArray();
} else {
out.write(((chunk[0] & 0x3f) << 2) | ((chunk[1] & 0x30) >>> 4));
out.write(((chunk[1] & 0x0f) << 4) | ((chunk[2] & 0x3c) >>> 2));
out.write(((chunk[2] & 0x03) << 6) | (chunk[3] & 0x3f));
}
ready = 0;
}
}
if (ready != 0) {
throw new IOException("invalid length");
}
out.flush();
return out.toByteArray();
}
/**
* validate, based on a leading int length field. The length field will be removed
*
* @param connection the connection
* @return the length
* @throws IOException if an exception occurs
* @throws BufferUnderflowException if not enough data is available
*/
public static int validateSufficientDatasizeByIntLengthField(NonBlockingBodyDataSource stream) throws IOException, BufferUnderflowException {
return validateSufficientDatasizeByIntLengthField(stream, true) ;
}
/**
* validate, based on a leading int length field, that enough data (getNumberOfAvailableBytes() >= length) is available. If not,
* an BufferUnderflowException will been thrown.
*
* @param connection the connection
* @param removeLengthField true, if length field should be removed
* @return the length
* @throws IOException if an exception occurs
* @throws BufferUnderflowException if not enough data is available
*/
public static int validateSufficientDatasizeByIntLengthField(NonBlockingBodyDataSource stream, boolean removeLengthField) throws IOException, BufferUnderflowException {
stream.resetToReadMark();
stream.markReadPosition();
// check if enough data is available
int length = stream.readInt();
if (stream.available() < length) {
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("insufficient data. require " + length + " got " + stream.available());
}
throw new BufferUnderflowException();
} else {
// ...yes, remove mark
if (!removeLengthField) {
stream.resetToReadMark();
}
stream.removeReadMark();
return length;
}
}
/**
* get the mime type file to extension map
*
* @return the mime type file to extension map
*/
public synchronized static Map getMimeTypeMapping() {
if (mimeTypeMap == null) {
Map map = new HashMap();
mimeTypeMap = Collections.unmodifiableMap(map);
InputStreamReader isr = null;
LineNumberReader lnr = null;
try {
isr = new InputStreamReader(HttpUtils.class.getResourceAsStream("/org/xlightweb/mime.types"));
if (isr != null) {
lnr = new LineNumberReader(isr);
String line = null;
while (true) {
line = lnr.readLine();
if (line != null) {
line = line.trim();
if (!line.startsWith("#")) {
StringTokenizer st = new StringTokenizer(line);
if (st.hasMoreTokens()) {
String mimeType = st.nextToken();
while (st.hasMoreTokens()) {
String extension = st.nextToken();
map.put(extension, mimeType);
if (LOG.isLoggable(Level.FINER)) {
LOG.finer("mapping " + extension + " -> " + mimeType + " added");
}
}
} else {
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("line " + line + "ignored");
}
}
}
} else {
break;
}
}
lnr.close();
}
} catch (Exception ioe) {
// eat and log exception
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("could not read mime.types. reason: " + ioe.toString());
}
} finally {
try {
if (lnr != null) {
lnr.close();
}
if (isr != null) {
isr.close();
}
} catch (IOException ioe) {
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("exception occured by closing mime.types file stream " + ioe.toString());
}
}
}
}
return mimeTypeMap;
}
/**
* injects a server field
*
* @param handler the handler
* @param server the server to inject
*/
static void injectServerField(Object handler, IServer server) {
Field[] fields = handler.getClass().getDeclaredFields();
for (Field field : fields) {
if (field.isAnnotationPresent(Resource.class)) {
Resource res = field.getAnnotation(Resource.class);
if ((field.getType() == IServer.class) || (res.type() == IServer.class)) {
field.setAccessible(true);
try {
field.set(handler, server);
} catch (IllegalAccessException iae) {
LOG.warning("could not set HandlerContext for attribute " + field.getName() + ". Reason " + DataConverter.toString(iae));
}
}
}
}
}
static boolean hasFormUrlencodedContentType(IHttpMessage message) {
if (!message.hasBody()) {
return false;
}
return hasContentType(message.getPartHeader(), "application/x-www-form-urlencoded");
}
/**
* returns if the content type of the header match with the given one
*
* @param header the header
* @param contentType the content type
* @return true, if the content type of the header match with the given one
*/
public static boolean hasContentType(IHeader header, String contentType) {
return (header.getContentType() != null) && (parseMediaType(header.getContentType()).equalsIgnoreCase(contentType));
}
/**
* returns the encoding type of content typed FORM URL encoded message
*
* @param message the message
* @return the encoding type
*/
static String getContentTypedFormUrlencodedEncodingType(IHttpMessage message) {
String contentType = message.getContentType();
if ((contentType != null) && (contentType.startsWith("application/x-www-form-urlencoded"))) {
String[] parts = contentType.split(";");
if (parts.length > 1) {
for(int i = 1; i < parts.length; i++) {
String[] kvp = parts[i].split("=");
if (kvp[0].trim().toUpperCase().equals("CHARSET")) {
return kvp[1].trim();
}
}
}
}
return IHttpMessage.DEFAULT_ENCODING;
}
/**
* creates a FORM URL encoded body message wrapper
*
* @param request the request
* @return a FORM URL encoded body message wrapper
* @throws IOException if an exception occurs
*/
static IHttpRequest newFormEncodedRequestWrapper(IHttpRequest request) throws IOException {
if (request instanceof HttpRequestWrapper) {
return request;
}
return new HttpRequestWrapper(new FormEncodedRequestHeaderWrapper(request), request);
}
static String[] retrieveMappings(IWebHandler handler) {
String[] mappings = mappingCache.get(handler.getClass());
if (mappings == null) {
Mapping mappingAnnotation = handler.getClass().getAnnotation(Mapping.class);
if (mappingAnnotation != null) {
mappings = mappingAnnotation.value();
mappingCache.put(handler.getClass(), mappings);
}
}
return mappings;
}
static CompletionHandlerInfo getCompletionHandlerInfo(IWriteCompletionHandler handler) {
CompletionHandlerInfo completionHandlerInfo = completionHandlerInfoCache.get(handler.getClass());
if (completionHandlerInfo == null) {
completionHandlerInfo = new CompletionHandlerInfo(handler);
completionHandlerInfoCache.put(handler.getClass(), completionHandlerInfo);
}
return completionHandlerInfo;
}
/**
* returns the part handler info
*
* @param partHandler the part handler
* @return the part handler info
*/
@SuppressWarnings("unchecked")
static PartHandlerInfo getPartHandlerInfo(IPartHandler partHandler) {
if (partHandler == null) {
if (emptyPartHandlerInfo == null) {
emptyPartHandlerInfo = new PartHandlerInfo(null);
}
return emptyPartHandlerInfo;
}
PartHandlerInfo partHandlerInfo = partHandlerInfoCache.get(partHandler.getClass());
if (partHandlerInfo == null) {
partHandlerInfo = new PartHandlerInfo((Class) partHandler.getClass());
partHandlerInfoCache.put(partHandler.getClass(), partHandlerInfo);
}
return partHandlerInfo;
}
static String getRequestURLWithoutQueryParams(IHttpRequestHeader header) {
String url = header.getRequestUrl().toString();
int idx = url.indexOf("?");
if (idx != -1) {
url = url.substring(0, idx);
}
return url;
}
/**
* returns the response handler info
*
* @param httpResponseHandler the response handler
* @return the response handler info
*/
@SuppressWarnings("unchecked")
private static ResponseHandlerInfo getHttpResponseHandlerInfo(IHttpResponseHandler httpResponseHandler) {
if (httpResponseHandler == null) {
if (emptyResponseHandlerInfo == null) {
emptyResponseHandlerInfo = new ResponseHandlerInfo(null);
}
return emptyResponseHandlerInfo;
}
ResponseHandlerInfo httpResponseHandlerInfo = httpResponseHandlerInfoCache.get(httpResponseHandler.getClass());
if (httpResponseHandlerInfo == null) {
httpResponseHandlerInfo = new ResponseHandlerInfo((Class) httpResponseHandler.getClass());
httpResponseHandlerInfoCache.put(httpResponseHandler.getClass(), httpResponseHandlerInfo);
}
return httpResponseHandlerInfo;
}
/**
* returns the connection handler info
*
* @param httpConnectionHandler the connection handler
* @return the connection handler info
*/
@SuppressWarnings("unchecked")
static HttpConnectionHandlerInfo getHttpConnectionHandlerInfo(IHttpConnectionHandler httpConnectionHandler) {
if (httpConnectionHandler == null) {
return EMPTY_HTTP_CONNECTION_HANDLER_INFO;
}
HttpConnectionHandlerInfo httpConnectionHandlerInfo = httpConnectionHandlerInfoCache.get(httpConnectionHandler.getClass());
if (httpConnectionHandlerInfo == null) {
httpConnectionHandlerInfo = new HttpConnectionHandlerInfo((Class) httpConnectionHandler.getClass());
httpConnectionHandlerInfoCache.put(httpConnectionHandler.getClass(), httpConnectionHandlerInfo);
}
return httpConnectionHandlerInfo;
}
/**
* returns true, if the response is bodyless per definition
* @param header the status
* @return true, if the response is bodyless per definition
*/
public static boolean isBodylessStatus(int status) {
if ((status == 304) || // not modified
(status == 204) || // no content
(status == 205) || // reset content
(status == 100) || // continue
(status == 101)) { // Switching Protocols
return true;
} else {
return false;
}
}
static void addConnectionAttribute(IHttpResponseHeader header, IHttpConnection con) {
if ((header.getAttribute("org.xlightweb.client.connection") == null) && (con != null)) {
header.setAttribute("org.xlightweb.client.connection", con);
}
}
static IHttpConnection getConnectionFromAttribute(IHttpResponseHeader header) {
return (IHttpConnection) header.getAttribute("org.xlightweb.client.connection");
}
static Integer getExecutionMode(IBodyDataHandler bodyDataHandler) {
Integer executionMode = bodyDataHandlerExecutionModeCache.get(bodyDataHandler.getClass());
if (executionMode == null) {
if (IUnsynchronized.class.isAssignableFrom(bodyDataHandler.getClass())) {
executionMode = -1;
} else if (bodyDataHandler.getClass().getName().equals("org.xlightweb.client.DuplicatingBodyForwarder")) {
executionMode = -1;
} else {
executionMode = IBodyDataHandler.DEFAULT_EXECUTION_MODE;
Execution execution = bodyDataHandler.getClass().getAnnotation(Execution.class);
if (execution != null) {
executionMode = execution.value();
}
try {
Method meth = bodyDataHandler.getClass().getMethod("onData", new Class[] { NonBlockingBodyDataSource.class });
execution = meth.getAnnotation(Execution.class);
if (execution != null) {
executionMode = execution.value();
}
} catch (NoSuchMethodException nsme) {
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("shouldn't occure because body handler has to have such a method " + nsme.toString());
}
}
}
bodyDataHandlerExecutionModeCache.put(bodyDataHandler.getClass(), executionMode);
}
return executionMode;
}
/**
* This is a xSocket internal method and subject to change
*/
@SuppressWarnings("unchecked")
static RequestHandlerInfo getRequestHandlerInfo(IHttpRequestHandler requestHandler) {
if (requestHandler == null) {
if (emptyHttpRequestHandlerInfo == null) {
emptyHttpRequestHandlerInfo = new RequestHandlerInfo(null);
}
return emptyHttpRequestHandlerInfo;
}
RequestHandlerInfo requestHandlerInfo = httpRequestHandlerInfoCache.get(requestHandler.getClass());
// info found in cache?
if (requestHandlerInfo != null) {
return requestHandlerInfo;
// .. no, it is not in cache
} else {
// create a new one
requestHandlerInfo = new RequestHandlerInfo((Class) requestHandler.getClass());
httpRequestHandlerInfoCache.put(requestHandler.getClass(), requestHandlerInfo);
}
return requestHandlerInfo;
}
/**
* This is a xSocket internal method and subject to change
*/
static ResponseHandlerInfo getResponseHandlerInfo(IHttpResponseHandler responseHandler) {
return getHttpResponseHandlerInfo(responseHandler);
}
public static boolean isAfter(String dateString, long referenceTime) {
referenceTime = (referenceTime / 1000) * 1000; // ignore millis
return DataConverter.toDate(DataConverter.toFormatedRFC822Date(referenceTime)).after(DataConverter.toDate(dateString));
}
static boolean isContentTypeSupportsCharset(String contentType) {
contentType = contentType.toLowerCase();
return (contentType.startsWith("text") || contentType.startsWith("message/rfc822"));
}
/**
* returns the given media type string without parameters
*
* @param mediaType the media type string
* @return the media type string without paramaters
*/
public static String removeMediaTypeParameters(String mediaType) {
int idx = mediaType.indexOf(";");
if (idx != -1) {
return mediaType.substring(0, idx).trim();
} else {
return mediaType;
}
}
static String parseMediaTypeParameter(String mediaType, String parameterName, boolean isRemoveSurroundingQuote, String dflt) {
if (mediaType != null) {
String[] parts = mediaType.split(";");
if (parts.length > 1) {
for (String part : parts) {
part = part.trim();
if (part.toLowerCase().startsWith(parameterName.toLowerCase() + "=")) {
String value= part.substring((parameterName.toLowerCase() + "=").length(), part.length());
value = value.trim();
if (isRemoveSurroundingQuote && value.startsWith("\"") && value.endsWith("\"") && value.length() > 1) {
value = value.substring(1, value.length() - 1);
value = value.trim();
}
return value;
}
}
}
}
return dflt;
}
static String parseMediaType(String mediaType) {
if (mediaType == null) {
return null;
}
String[] parts = mediaType.split(";");
return parts[0].trim();
}
/**
* parse extracts the encoding parameter of the content type
* @param contentType the content type
* @return the encoding or
*/
public static String parseEncoding(String contentType) {
return parseMediaTypeParameter(contentType, "charset", true, null);
}
/**
* parse extracts the encoding parameter of the content type
* @param contentType the content type
* @return the encoding or
*/
public static String parseEncodingWithDefault(String contentType, String dflt) {
if (contentType == null) {
return dflt;
}
String encoding = parseMediaTypeParameter(contentType, "charset", true, null);
if (encoding == null) {
String mediaType = parseMediaType(contentType);
if (mediaType.equalsIgnoreCase("text/xml")) {
encoding = "us-ascii";
} else if (mediaType.equalsIgnoreCase("application/json") || mediaType.equalsIgnoreCase("text/event-stream")) {
encoding = "utf-8";
} else {
if (dflt == null) {
encoding = IHttpMessage.DEFAULT_ENCODING;
} else {
encoding = dflt;
}
}
}
return encoding;
}
static String addEncodingIfNotPresent(String contentType) {
if (contentType == null) {
return null;
}
String encoding = parseMediaTypeParameter(contentType, "charset", true, null);
if (encoding == null) {
String mediaType = parseMediaType(contentType);
if (mediaType.equalsIgnoreCase("text/xml")) {
return contentType + "; charset=US-ASCII";
} else if (mediaType.equalsIgnoreCase("application/json") || mediaType.equalsIgnoreCase("text/event-stream")) {
return contentType + "; charset=utf-8";
} else {
return contentType + "; charset=" + IHttpMessage.DEFAULT_ENCODING;
}
} else {
return contentType;
}
}
static BodyCompeleteListenerInfo getBodybodyCompeleteListenerInfo(IBodyCompleteListener completeListener) {
BodyCompeleteListenerInfo completionListenerInfo = bodyCompeleteListenerModeCache.get(completeListener.getClass());
if (completionListenerInfo == null) {
completionListenerInfo = new BodyCompeleteListenerInfo(completeListener.getClass());
bodyCompeleteListenerModeCache.put(completeListener.getClass(), completionListenerInfo);
}
return completionListenerInfo;
}
/**
* Copies a request. Only complete received message are supported
*
* @param request the request
* @return the copy
* @throws IOException if an exception occurs
*/
public static IHttpRequest copy(IHttpRequest request) throws IOException {
if (request.hasBody()) {
if (!request.getNonBlockingBody().isCompleteReceived()) {
throw new IOException("copy is only supported for complete received messages (hint: uses @InvokeOn(InvokeOn.MESSAGE_RECEIVED) annotation)");
}
return new HttpRequest(request.getRequestHeader().copy(), request.getNonBlockingBody().copyContent());
} else {
return new HttpRequest(request.getRequestHeader().copy());
}
}
/**
* Copies a response. Only complete received message are supported
*
* @param response the response
* @return the copy
* @throws IOException if an exception occurs
*/
public static IHttpResponse copy(IHttpResponse response) throws IOException {
if (response.hasBody()) {
if (!response.getNonBlockingBody().isCompleteReceived()) {
throw new IOException("copy is only supported for complete received messages (hint: uses @InvokeOn(InvokeOn.MESSAGE_RECEIVED) annotation)");
}
return new HttpResponse(response.getResponseHeader().copy(), response.getNonBlockingBody().copyContent());
} else {
return new HttpResponse(response.getResponseHeader().copy());
}
}
static boolean isTextMimeType(String mimeType) {
mimeType = mimeType.toLowerCase();
mimeType = mimeType.trim();
if (mimeType.startsWith("text")) {
return true;
}
if (mimeType.startsWith("message/rfc822")) {
return true;
}
return false;
}
static int[] computeFromRangePosition(String range, int length) throws BadMessageException {
int[] positions = new int[2];
int idx = range.indexOf("-");
if (idx == -1) {
throw new BadMessageException("invalid range header entry " + range);
}
String from = range.substring(0, idx).trim();
String to = range.substring(idx + 1, range.length()).trim();
if (from.length() > 0) {
positions[0] = Integer.parseInt(from);
if (to.length() > 0) {
// e.g. 500-1000
positions[1] = Integer.parseInt(to);
} else {
// e.g. 500-
positions[1] = length - 1;
}
} else {
if (to.length() > 0) {
// e.g. -3000
positions[0] = length - Integer.parseInt(to);
positions[1] = length -1;
} else {
throw new BadMessageException("invalid range header entry " + range);
}
}
if ((positions[0] < 0) || (positions[1] < 0) ||
(positions[0] >= length) || (positions[1] >= length)) {
throw new BadMessageException("invalid range header entry " + range);
}
return positions;
}
static String detectEncoding(File file) {
try {
FileInputStream fis = new FileInputStream(file);
try {
byte[] data = new byte[BOM_MAX_LENGTH];
fis.read(data);
return detectEncoding(data);
} finally {
if (fis != null) {
fis.close();
}
}
} catch (IOException ioe) {
return null;
}
}
static String detectEncoding(byte[] data) throws BufferUnderflowException {
if ((data != null) && (data.length > 0)) {
for (Bom bom : BOMS) {
if (bom.match(data)) {
return bom.getEncoding();
}
}
}
if ((data == null) || (data.length < BOM_MAX_LENGTH)) {
throw new BufferUnderflowException();
}
return null;
}
static boolean startsWithUTF8BOM(ByteBuffer buffer) throws BufferUnderflowException {
return BOM_UTF_8.match(buffer);
}
static boolean startsWithUTF16BEBOM(ByteBuffer buffer) throws BufferUnderflowException {
return BOM_UTF_16BE.match(buffer);
}
static boolean startsWithUTF16LEBOM(ByteBuffer buffer) throws BufferUnderflowException {
return BOM_UTF_16LE.match(buffer);
}
static boolean startsWithUTF32BEBOM(ByteBuffer buffer) throws BufferUnderflowException {
return BOM_UTF_32BE.match(buffer);
}
static boolean startsWithUTF32LEBOM(ByteBuffer buffer) throws BufferUnderflowException {
return BOM_UTF_32LE.match(buffer);
}
static int computeRemaining(ByteBuffer[] bufs) {
int remaining = 0;
if (bufs == null) {
return 0;
}
for (ByteBuffer byteBuffer : bufs) {
if (byteBuffer != null) {
remaining += byteBuffer.remaining();
}
}
return remaining;
}
static boolean isEmpty(ByteBuffer buffer) {
if (buffer == null) {
return true;
}
return (buffer.remaining() < 1);
}
static boolean isEmpty(ByteBuffer[] buffer) {
if (buffer == null) {
return true;
}
for (ByteBuffer byteBuffer : buffer) {
if (byteBuffer != null) {
if (byteBuffer.hasRemaining()) {
return false;
}
}
}
return true;
}
static ByteBuffer[] compact(ByteBuffer[] buffers) {
if (buffers == null) {
return null;
}
// just one buffer?
if (buffers.length == 1) {
if (buffers[0] == null) {
return null;
} else {
if (buffers[0].remaining() == 0) {
return null;
} else {
return buffers;
}
}
}
// more than one buffer
ByteBuffer[] result = null;
for (ByteBuffer buffer : buffers) {
if (buffer != null) {
result = merge(result, buffer);
}
}
return result;
}
private static ByteBuffer[] merge(ByteBuffer[] buffers, ByteBuffer tailBuffer) {
if (tailBuffer.remaining() == 0) {
return buffers;
}
if ((buffers == null) || (buffers.length == 0)) {
return new ByteBuffer[] { tailBuffer };
} else {
ByteBuffer[] result = new ByteBuffer[buffers.length + 1];
System.arraycopy(buffers, 0, result, 0, buffers.length);
result[buffers.length] = tailBuffer;
return result;
}
}
static ByteBuffer[] merge(ByteBuffer[] buffers, ByteBuffer[] tailBuffers) {
if ((buffers == null) || (buffers.length == 0)) {
return tailBuffers;
} else {
ByteBuffer[] result = new ByteBuffer[buffers.length + tailBuffers.length];
System.arraycopy(buffers, 0, result, 0, buffers.length);
System.arraycopy(tailBuffers, 0, result, buffers.length, tailBuffers.length);
return result;
}
}
static ByteBuffer duplicateAndMerge(ByteBuffer[] buffers) {
if (buffers.length == 0) {
return ByteBuffer.allocate(0);
} else if (buffers.length == 1) {
return buffers[0].duplicate();
} else {
int size = 0;
for (ByteBuffer byteBuffer : buffers) {
if (byteBuffer != null) {
size += byteBuffer.remaining();
}
}
ByteBuffer buffer = ByteBuffer.allocate(size);
for (ByteBuffer byteBuffer : buffers) {
if (byteBuffer != null) {
int pos = byteBuffer.position();
int limit = byteBuffer.limit();
buffer.put(byteBuffer);
byteBuffer.position(pos);
byteBuffer.limit(limit);
}
}
buffer.flip();
return buffer;
}
}
static ByteBuffer merge(ByteBuffer buffer, ByteBuffer[] tailBuffers) {
if ((buffer == null) || (buffer.remaining() == 0)) {
return merge(tailBuffers);
}
int size = buffer.remaining();
for (ByteBuffer byteBuffer : tailBuffers) {
if (byteBuffer != null) {
size += byteBuffer.remaining();
}
}
ByteBuffer result = ByteBuffer.allocate(size);
result.put(buffer);
for (ByteBuffer byteBuffer : tailBuffers) {
if (byteBuffer != null) {
int pos = byteBuffer.position();
int limit = byteBuffer.limit();
result.put(byteBuffer);
byteBuffer.position(pos);
byteBuffer.limit(limit);
}
}
result.flip();
return result;
}
static ByteBuffer merge(ByteBuffer[] buffers) {
if ((buffers == null) || (buffers.length == 0)) {
return ByteBuffer.allocate(0);
} else if (buffers.length == 1) {
return buffers[0];
} else {
int size = 0;
for (ByteBuffer byteBuffer : buffers) {
if (byteBuffer != null) {
size += byteBuffer.remaining();
}
}
ByteBuffer buffer = ByteBuffer.allocate(size);
for (ByteBuffer byteBuffer : buffers) {
if (byteBuffer != null) {
int pos = byteBuffer.position();
int limit = byteBuffer.limit();
buffer.put(byteBuffer);
byteBuffer.position(pos);
byteBuffer.limit(limit);
}
}
buffer.flip();
return buffer;
}
}
static ByteBuffer merge(ByteBuffer buffer, ByteBuffer tailBuffer) {
if ((buffer == null) || (buffer.remaining() == 0)) {
return tailBuffer;
}
if ((tailBuffer == null) || (tailBuffer.remaining() == 0)) {
return buffer;
}
ByteBuffer result = ByteBuffer.allocate(buffer.remaining() + tailBuffer.remaining());
result.put(buffer);
result.put(tailBuffer);
result.flip();
return result;
}
static byte[] merge(byte[] bytes, byte[] tailBytes) {
byte[] b = new byte[bytes.length + tailBytes.length];
System.arraycopy(bytes, 0, b, 0, bytes.length);
System.arraycopy(tailBytes, 0, b, bytes.length, tailBytes.length);
return b;
}
/**
* copies the given buffer
*
* @param buffer the buffer to copy
* @return the copy
*/
static ByteBuffer copy(ByteBuffer buffer) {
if (buffer == null) {
return null;
}
return ByteBuffer.wrap(DataConverter.toBytes(buffer));
}
/**
* wraps a exception with an io exception
*
* @param t the exception
* @return the ioe exception
*/
public static IOException toIOException(Throwable t) {
if (t instanceof IOException) {
return (IOException) t;
} else {
IOException ioe = new IOException(t.getClass().getName() + ": " + t.toString());
ioe.setStackTrace(t.getStackTrace());
return ioe;
}
}
/**
* @deprecated
*/
public static BlockingBodyDataSource compress(BlockingBodyDataSource dataSource) throws IOException {
byte[] compressedData = compress(dataSource.readBytes());
InMemoryBodyDataSource compressedDataSource = new InMemoryBodyDataSource(dataSource.getHeader(), compressedData);
return new BlockingBodyDataSource(compressedDataSource);
}
/**
* compresses a body (using the GZIP compression)
*
* @param dataSource the body to compress
* @return the compressed body
* @throws IOException if an excption occurs
*/
public static BodyDataSource compress(BodyDataSource dataSource) throws IOException {
byte[] compressedData = compress(dataSource.readBytes());
InMemoryBodyDataSource compressedDataSource = new InMemoryBodyDataSource(dataSource.getHeader(), compressedData);
return new BodyDataSource(compressedDataSource);
}
/**
* compresses a byte array (using the GZIP compression)
*
* @param dataSource the byte array to compress
* @return the compressed byte array
* @throws IOException if an excption occurs
*/
public static byte[] compress(byte[] data) throws IOException {
ByteArrayOutputStream bos = new ByteArrayOutputStream(data.length);
GZIPOutputStream out = new GZIPOutputStream(bos);
out.write(data);
out.close();
return bos.toByteArray();
}
static ByteBuffer[] readFile(File file) throws IOException {
RandomAccessFile raf = new RandomAccessFile(file, "r");
FileChannel fc = raf.getChannel();
ByteBuffer buffer = ByteBuffer.allocate((int) fc.size());
fc.read(buffer);
fc.close();
raf.close();
buffer.flip();
return new ByteBuffer[] { buffer };
}
/**
* @deprecated
*
*/
public static BlockingBodyDataSource decompress(BlockingBodyDataSource dataSource) throws IOException {
byte[] decompressedData = decompress(dataSource.readBytes());
InMemoryBodyDataSource decompressedDataSource = new InMemoryBodyDataSource(dataSource.getHeader(), decompressedData);
return new BlockingBodyDataSource(decompressedDataSource);
}
/**
* decompresses a body (using the GZIP decompression)
*
* @param dataSource the body to decompress
* @return the decompressed body
* @throws IOException if an excption occurs
*/
public static BodyDataSource decompress(BodyDataSource dataSource) throws IOException {
byte[] decompressedData = decompress(dataSource.readBytes());
InMemoryBodyDataSource decompressedDataSource = new InMemoryBodyDataSource(dataSource.getHeader(), decompressedData);
return new BodyDataSource(decompressedDataSource);
}
/**
* decompresses a byte array (using the GZIP decompression)
*
* @param compressedData
* @return the deconmpressed byte array
* @throws IOException
*/
public static byte[] decompress(byte[] compressedData) throws IOException {
ByteArrayInputStream bis = new ByteArrayInputStream(compressedData);
GZIPInputStream in = new GZIPInputStream(bis);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
byte[] transferBuffer = new byte[4096];
int read = 0;
do {
read = in.read(transferBuffer);
if (read != -1) {
bos.write(transferBuffer, 0, read);
}
} while (read != -1);
in.close();
in.close();
bos.close();
return bos.toByteArray();
}
static boolean isCompressable(File file) {
return isCompressableMimeType(resolveContentTypeByFileExtension(file));
}
public static boolean isCompressableMimeType(String mimeType) {
if (mimeType == null) {
return false;
} else {
mimeType = parseMediaType(mimeType.toLowerCase());
if (mimeType.startsWith("text/") && !mimeType.startsWith("text/event-stream")) {
return true;
}
if (mimeType.startsWith("application/")) {
if (mimeType.startsWith("application/json") ||
mimeType.startsWith("application/x-www-form-urlencoded") ||
mimeType.startsWith("application/x-javascript")) {
return true;
}
if (mimeType.endsWith("+xml")) {
return true;
}
}
return false;
}
}
/**
* schedule a time task
*
* @param task the timer task
* @param delay the delay
* @param period the period
*/
static void schedule(TimerTask task, long delay, long period) {
TIMER.schedule(task, delay, period);
}
/**
* schedule a time task
*
* @param task the timer task
* @param delay the delay
*/
static void schedule(TimerTask task, long delay) {
TIMER.schedule(task, delay);
}
static long computeRemainingTime(long start, int receiveTimeoutSec) {
return (start + ((long) receiveTimeoutSec * 1000)) - System.currentTimeMillis();
}
static boolean isPrintable(IHttpMessage message) {
if (!message.hasBody()) {
return true;
}
String contentType = message.getContentType();
if (contentType == null) {
return true;
}
contentType = contentType.toLowerCase();
if (contentType.startsWith("text/") ||
contentType.startsWith("application/json") ||
contentType.startsWith("application/x-www-form-urlencoded") ||
contentType.startsWith("application/x-javascript")) {
return true;
}
contentType = contentType.split(",")[0].trim();
if (contentType.startsWith("application/") && contentType.endsWith("+xml")) {
return true;
}
return false;
}
/**
* set the caching expires headers of a response
*
* @param header the header
* @param expireSec the expire time or 0 to set no-cache headers
*/
public static void setExpireHeaders(IHttpResponseHeader header, int expireSec) {
if (expireSec > 0) {
header.setHeader("Expires", toRFC1123DateString(new Date(System.currentTimeMillis() + (expireSec * 1000L))));
header.setHeader("Cache-Control", "public, max-age=" + expireSec);
} else {
header.setHeader("Expires", "Fri, 01 Jan 1990 00:00:00 GMT");
header.setHeader("Cache-Control", "no-cache, no-store, max-age=0, must-revalidate");
header.setHeader("Pragma", "no-cache");
}
}
/**
* set a last modified header of the response
*
* @param header the header
* @param timeMillis the last modified time in millis
*/
public static void setLastModifiedHeader(IHttpResponseHeader header, long timeMillis) {
header.setHeader("Last-Modified", DataConverter.toFormatedRFC822Date(timeMillis));
}
public static String toDateTimeString(Date date) {
SimpleDateFormat formatter = new SimpleDateFormat(DATE_TIME_PATTERN_1);
formatter.setTimeZone(TimeZone.getTimeZone("UTC"));
return formatter.format(date);
}
public static Date parseDateTimeString(String dateTimeString) throws java.text.ParseException {
try {
SimpleDateFormat formatter = new SimpleDateFormat(DATE_TIME_PATTERN_1);
formatter.setTimeZone(TimeZone.getTimeZone("UTC"));
return formatter.parse(dateTimeString);
} catch (java.text.ParseException pe) {
try {
SimpleDateFormat formatter = new SimpleDateFormat(DATE_TIME_PATTERN_2);
formatter.setTimeZone(TimeZone.getTimeZone("UTC"));
return formatter.parse(dateTimeString);
} catch (java.text.ParseException pe2) {
SimpleDateFormat formatter = new SimpleDateFormat(DATE_TIME_PATTERN_3);
formatter.setTimeZone(TimeZone.getTimeZone("UTC"));
return formatter.parse(dateTimeString);
}
}
}
static boolean isAcceptEncodingGzip(IHttpRequest request) {
String acceptEncoding = request.getHeader("Accept-Encoding");
if (acceptEncoding != null ) {
for (String part : acceptEncoding.split(",")) {
part = part.toLowerCase().trim();
if (part.startsWith("gzip")) {
return true;
}
}
}
return false;
}
/**
* computes a HMAC-SHA1 Signature
*
* @param data the data
* @param key the key
* @return the signature
* @throws IOException if an error occurs
*/
public static byte[] computeRFC2104HMAC(byte[] data, byte[] key) throws IOException {
try {
SecretKeySpec signingKey = new SecretKeySpec(key, "HmacSHA1");
Mac mac = Mac.getInstance("HmacSHA1");
mac.init(signingKey);
byte[] rawHmac = mac.doFinal(data);
return encodeBase64(rawHmac);
} catch (NoSuchAlgorithmException nsae) {
throw new IOException("required algorithm HmacSHA1 is not supported by environment: " + nsae.toString());
} catch (InvalidKeyException ke) {
throw new IOException("error occured by initializing mac: " + ke.toString());
}
}
public static String toRFC1123DateString(Date date) {
SimpleDateFormat formatter = new SimpleDateFormat(RFC1123_TIME_PATTERN, Locale.US);
formatter.setTimeZone(TimeZone.getTimeZone("GMT"));
String dateString = formatter.format(date);
return dateString;
}
public static Date parseHttpDateString(String dateString) {
try {
SimpleDateFormat dateParser = new SimpleDateFormat(RFC1123_TIME_PATTERN, Locale.US);
dateParser.setTimeZone(TimeZone.getTimeZone("GMT"));
return dateParser.parse(dateString);
} catch (ParseException pe) {
try {
SimpleDateFormat dateParser = new SimpleDateFormat(RFC1036_TIME_PATTERN, Locale.US);
dateParser.setTimeZone(TimeZone.getTimeZone("GMT"));
return dateParser.parse(dateString);
} catch (ParseException pe2) {
return null;
}
}
}
/**
* (deep) copy of the byte buffer array
*
* @param buffers the byte buffer array
* @return the copy
*/
static ByteBuffer[] copy(ByteBuffer[] buffers) {
if (buffers == null) {
return null;
}
ByteBuffer[] copy = new ByteBuffer[buffers.length];
for (int i = 0; i < copy.length; i++) {
copy[i] = copy(buffers[i]);
}
return copy;
}
static Executor getDefaultWorkerPool() {
return DEFAULT_WORKERPOOL;
}
static IMultimodeExecutor newMultimodeExecutor() {
return newMultimodeExecutor(DEFAULT_WORKERPOOL);
}
static IMultimodeExecutor newMultimodeExecutor(Executor workerpool) {
return new MultimodeExecutor(workerpool);
}
private static final class MultimodeExecutor implements IMultimodeExecutor {
private final SerializedTaskQueue taskQueue = new SerializedTaskQueue();
private Executor workerpool = null;
public MultimodeExecutor(Executor workerpool) {
this.workerpool = workerpool;
}
public void processMultithreaded(Runnable task) {
taskQueue.performMultiThreaded(task, workerpool);
}
public void processNonthreaded(Runnable task) {
taskQueue.performNonThreaded(task, workerpool);
}
public Executor getWorkerpool() {
return workerpool;
}
}
/**
* returns true if the header contains a Expect: 100-continue header
*
* @param header the request header
* @return true if the header contains a Expect: 100-continue header
*/
public static boolean isContainExpect100ContinueHeader(IHttpMessageHeader header) {
Boolean isExpectContinue = (Boolean) header.getAttribute("org.xlightweb.containsExpect-100-Continue-header");
if (isExpectContinue == null) {
isExpectContinue = isContainExpect100ContinueHeader((IHeader) header);
header.setAttribute("org.xlightweb.containsExpect-100-Continue-header", isExpectContinue);
}
return isExpectContinue;
}
static boolean isContainExpect100ContinueHeader(IHeader header) {
return ((header.getHeader("Expect") != null)) && (header.getHeader("Expect").equalsIgnoreCase("100-Continue"));
}
public static boolean isContainsExpect100ContinueHeader(IHttpRequest request) {
return isContainExpect100ContinueHeader(request.getRequestHeader());
}
public static String removeSurroundingSlashes(String path) {
path = path.trim();
if (path.startsWith("/")) {
path = path.substring(1, path.length());
}
if (path.endsWith("/")) {
path = path.substring(0, path.length() -1);
}
return path;
}
/**
* returns true if the location is absolute
*
* @param location the location
* @return true if the location is absolute
*/
static boolean isAbsoluteURL(String location) {
String locationLowerCase = location.toLowerCase();
return ((locationLowerCase.startsWith("http://") || locationLowerCase.startsWith("https://")));
}
/**
* returns the execution mode for request timeout handler
*
* @param requestTimeoutHandler the request timeout handler
* @return the execution mode
*/
static boolean isRequestTimeoutHandlerMultithreaded(Class clazz) {
Boolean isMultithreaded = requestTimeoutHandlerExecutionModeCache.get(clazz);
if (isMultithreaded == null) {
int mode = IHttpRequestTimeoutHandler.DEFAULT_EXECUTION_MODE;
Execution execution = clazz.getAnnotation(Execution.class);
if (execution != null) {
mode = execution.value();
}
try {
Method meth = clazz.getMethod("onRequestTimeout", new Class[] { IHttpConnection.class });
execution = meth.getAnnotation(Execution.class);
if (execution != null) {
mode = execution.value();
}
} catch (NoSuchMethodException nsme) {
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("shouldn't occure because request timeout handlerr has to have such a method " + nsme.toString());
}
}
isMultithreaded = (mode == Execution.MULTITHREADED);
bodyCompleteListenerExecutionModeCache.put(clazz, isMultithreaded);
}
return isMultithreaded;
}
static Map> parseParamters(String txt, String encoding) {
Map> result = new HashMap>();
try {
String[] params = txt.split("&");
for (String param : params) {
String[] kv = param.split("=");
if (kv.length > 1) {
String name = URLDecoder.decode(kv[0], encoding);
List values = result.get(name);
if (values == null) {
values = new ArrayList();
result.put(name, values);
}
values.add(URLDecoder.decode(kv[1], encoding));
}
}
return result;
} catch (UnsupportedEncodingException use) {
throw new RuntimeException(use.toString());
}
}
static Map> parseMatrixParamters(String uri, String encoding) {
Map> result = new HashMap>();
try {
String[] parts = uri.split(";");
for (String part : parts) {
String[] kv = part.split("=");
if (kv.length > 1) {
String name = URLDecoder.decode(kv[0], encoding);
List values = result.get(name);
if (values == null) {
values = new ArrayList();
result.put(name, values);
}
values.add(URLDecoder.decode(kv[1], encoding));
}
}
return result;
} catch (UnsupportedEncodingException use) {
throw new RuntimeException(use.toString());
}
}
static String removeMatrixParamter(String uri, String name) {
if (uri == null) {
return null;
}
int idx = uri.indexOf(";" + name);
if (idx == -1) {
return uri;
} else {
int end = uri.indexOf(";", idx + 1);
if (end == -1) {
return uri.substring(0, idx);
} else {
return uri.substring(0, idx) + uri.substring(end, uri.length());
}
}
}
static String addMatrixParamter(String uri, String name, String value, String encoding) {
try {
if (uri == null) {
uri = "";
}
uri += ";" + URLEncoder.encode(name, encoding) + "=" + URLEncoder.encode(value, encoding);
return uri;
} catch (UnsupportedEncodingException use) {
throw new RuntimeException(use.toString());
}
}
/**
* returns the content type based on the file extension
*
* @param file the file
*/
public static final String resolveContentTypeByFileExtension(File file) {
String mimeType = null;
String name = file.getName();
int pos = name.lastIndexOf(".");
if (pos != -1) {
String extension = name.substring(pos + 1, name.length());
mimeType = HttpUtils.getMimeTypeMapping().get(extension);
if (mimeType != null) {
if (isTextMimeType(mimeType)) {
String encoding = HttpUtils.detectEncoding(file);
if (encoding != null) {
mimeType = mimeType + "; charset=" + encoding;
}
}
}
}
return mimeType;
}
static int convertMillisToSec(long millis) {
if (millis > Integer.MAX_VALUE) {
return Integer.MAX_VALUE;
} else if (millis >= 1000) {
return ((int) millis) / 1000;
} else if (millis == 0) {
return 0;
} else {
return 1;
}
}
/**
* get the implementation version
*
* @return the implementation version
*/
public static String getImplementationVersion() {
if (implementationVersion == null) {
readVersionFile();
}
return implementationVersion;
}
/**
* get the xSocket implementation version
*
* @return the xSocket implementation version
*/
static String getXSocketImplementationVersion() {
if (xSocketImplementationVersion == null) {
readVersionFile();
}
return xSocketImplementationVersion;
}
/**
*
This is a xSocket internal method and subject to change
*/
public static long parseLong(String longString, long dflt) {
if (longString == null) {
return dflt;
}
try {
return Long.parseLong(longString);
} catch (NumberFormatException nfe) {
return dflt;
}
}
/**
* get the implementation date
*
* @return the implementation date
*/
public static String getImplementationDate() {
if (implementationDate== null) {
readVersionFile();
}
return implementationDate;
}
private static void readVersionFile() {
implementationVersion = "";
implementationDate = "";
InputStreamReader isr = null;
LineNumberReader lnr = null;
try {
isr = new InputStreamReader(HttpUtils.class.getResourceAsStream("/org/xlightweb/version.txt"));
if (isr != null) {
lnr = new LineNumberReader(isr);
String line = null;
do {
line = lnr.readLine();
if (line != null) {
if (line.startsWith("Implementation-Version=")) {
implementationVersion = line.substring("Implementation-Version=".length(), line.length()).trim();
} else if (line.startsWith("Implementation-Date=")) {
implementationDate = line.substring("Implementation-Date=".length(), line.length()).trim();
} else if (line.startsWith("Dependency.xSocket.Implementation-Version=")) {
xSocketImplementationVersion = line.substring("Dependency.xSocket.Implementation-Version=".length(), line.length()).trim();
}
}
} while (line != null);
lnr.close();
}
} catch (Exception ioe) {
implementationDate = "";
implementationVersion = "";
xSocketImplementationVersion = "";
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("could not read version file. reason: " + ioe.toString());
}
} finally {
try {
if (lnr != null) {
lnr.close();
}
if (isr != null) {
isr.close();
}
} catch (IOException ioe) {
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("exception occured by closing version.txt file stream " + ioe.toString());
}
}
}
}
/**
* returns if handler is multi threaded
*
*
This is a xLightweb internal method and subject to change
*
* @param clazz the handler class
* @param dflt the default value
* @return true, if multi threaded
*/
static boolean isHandlerMultithreaded(Class extends Object> clazz, boolean dflt) {
Execution execution = clazz.getAnnotation(Execution.class);
if (execution != null) {
if(execution.value() == Execution.NONTHREADED) {
return false;
} else {
return true;
}
} else {
return dflt;
}
}
/**
* returns if the handler method is multi threaded
*
*
This is a xSocket internal method and subject to change
*
* @param clazz the handler class
* @param methodname the method name
* @param dflt the default value
* @param paramClass the method parameter classes
* @return true, if multi threaded
*/
@SuppressWarnings("unchecked")
public static boolean isMethodMultithreaded(Class clazz, String methodname, boolean dflt, Class... paramClass) {
try {
Method meth = clazz.getMethod(methodname, paramClass);
Execution execution = meth.getAnnotation(Execution.class);
if (execution != null) {
if(execution.value() == Execution.NONTHREADED) {
return false;
} else {
return true;
}
} else {
return dflt;
}
} catch (NoSuchMethodException nsme) {
return dflt;
}
}
static boolean isSynchronizedOnSession(Class extends Object> clazz, boolean dflt) {
SynchronizedOn synchronizedOn = clazz.getAnnotation(SynchronizedOn.class);
if (synchronizedOn != null) {
if(synchronizedOn.value() == SynchronizedOn.SESSION) {
return true;
} else {
return false;
}
} else {
return dflt;
}
}
/**
* returns if the connect handler warning is suppressed
*
*
This is a xSocket internal method and subject to change
*
* @return true, if the connect handler warning is suppressed
*/
public static boolean isConnectHandlerWarningIsSuppressed() {
if (isConnectHandlerWarningIsSuppressed == null) {
isConnectHandlerWarningIsSuppressed = Boolean.parseBoolean(System.getProperty("org.xlightweb.httpConnectHandler.suppresswarning", "false"));
}
return isConnectHandlerWarningIsSuppressed;
}
@SuppressWarnings("unchecked")
static boolean isSynchronizedOnSession(Class clazz, String methodname, boolean dflt, Class... paramClass) {
try {
Method meth = clazz.getMethod(methodname, paramClass);
SynchronizedOn synchronizedOn = meth.getAnnotation(SynchronizedOn.class);
if (synchronizedOn != null) {
if(synchronizedOn.value() == SynchronizedOn.SESSION) {
return true;
} else {
return false;
}
} else {
return dflt;
}
} catch (NoSuchMethodException nsme) {
return dflt;
}
}
static boolean isInvokeOnMessageReceived(Class extends Object> clazz, boolean dflt) {
InvokeOn invokeOn = clazz.getAnnotation(InvokeOn.class);
if (invokeOn != null) {
if(invokeOn.value() == InvokeOn.MESSAGE_RECEIVED) {
return true;
} else {
return false;
}
} else {
return dflt;
}
}
@SuppressWarnings("unchecked")
static boolean isInvokeOnMessageReceived(Class clazz, String methodname, boolean dflt, Class... paramClass) {
try {
Method meth = clazz.getMethod(methodname, paramClass);
InvokeOn invokeOn = meth.getAnnotation(InvokeOn.class);
if (invokeOn != null) {
if(invokeOn.value() == InvokeOn.MESSAGE_RECEIVED) {
return true;
} else {
return false;
}
} else {
return dflt;
}
} catch (NoSuchMethodException nsme) {
return dflt;
}
}
@SuppressWarnings("unchecked")
static boolean isContinueHandler(Class extends Object> clazz, String methodname, Class... params) {
try {
Supports100Continue handles100Continue = clazz.getAnnotation(Supports100Continue.class);
if (handles100Continue != null) {
return true;
} else {
Method meth = clazz.getMethod(methodname, params);
handles100Continue = meth.getAnnotation(Supports100Continue.class);
if (handles100Continue != null) {
return true;
}
}
} catch (NoSuchMethodException ignore) { }
return false;
}
static boolean isGzipEncoded(IHttpMessageHeader responseHeader) {
return (responseHeader.getHeader("Content-Encoding") != null) &&
(responseHeader.getHeader("Content-Encoding").toLowerCase().indexOf("gzip") != -1);
}
public static boolean isAcceptEncdoingGzip(IHttpRequestHeader requestHeader) {
return (requestHeader.getHeader("Accept-Encoding") != null) && (requestHeader.getHeader("Accept-Encoding").toLowerCase().indexOf("gzip") != -1);
}
/**
* establishs a tcp tunnel
* @param httpConnection the http connection
* @param target the target address (e.g. www.gmx.com, www.gmx.com:443)
* @throws IOException if an exception occurs
*/
public static void establishTcpTunnel(IHttpConnection httpConnection, String target) throws IOException {
String targetHost = null;
int targetPort = 443;
int idx = target.lastIndexOf(":");
if (idx == -1) {
targetHost = target;
} else {
targetHost = target.substring(0, idx);
targetPort = Integer.parseInt(target.substring(idx + 1, target.length()));
}
establishTcpTunnel(httpConnection, targetHost, targetPort);
}
/**
* establishs a tcp tunnel
* @param httpConnection the http connection
* @param targetHost the target host
* @param targetPort the target port
* @throws IOException if an exception occurs
*/
public static void establishTcpTunnel(IHttpConnection httpConnection, String targetHost, int targetPort) throws IOException {
INonBlockingConnection forwardCon = new NonBlockingConnection(targetHost, targetPort);
INonBlockingConnection tcpCon = httpConnection.getUnderlyingTcpConnection();
forwardCon.setAttachment(tcpCon);
tcpCon.setAttachment(forwardCon);
forwardCon.setFlushmode(FlushMode.ASYNC);
forwardCon.setAutoflush(false);
tcpCon.setFlushmode(FlushMode.ASYNC);
tcpCon.setAutoflush(false);
forwardCon.setHandler(new TcpProxyHandler());
tcpCon.setHandler(new TcpProxyHandler());
}
private static class TcpProxyHandler implements IDataHandler, IDisconnectHandler {
public boolean onDisconnect(INonBlockingConnection connection) throws IOException {
INonBlockingConnection reverseConnection = (INonBlockingConnection) connection.getAttachment();
if (reverseConnection != null) {
connection.setAttachment(null);
NonBlockingConnectionPool.destroy(reverseConnection);
}
return true;
}
public boolean onData(INonBlockingConnection connection) throws IOException, BufferUnderflowException, MaxReadSizeExceededException {
INonBlockingConnection forwardConnection = (INonBlockingConnection) connection.getAttachment();
int available = connection.available();
if (available > 0) {
ByteBuffer[] data = connection.readByteBufferByLength(connection.available());
forwardConnection.write(data);
forwardConnection.flush();
} else if (available == -1) {
NonBlockingConnectionPool.destroy(connection);
}
return true;
}
}
static class HttpConnectionHandlerInfo {
private boolean isConnectHandler = false;
private boolean isConnectHandlerMultithreaded = true;
private boolean isDisconnectHandler = false;
private boolean isDisconnectHandlerMultithreaded = true;
@SuppressWarnings("unchecked")
public HttpConnectionHandlerInfo(Class clazz) {
if (clazz == null) {
return;
}
if (IHttpConnectHandler.class.isAssignableFrom(clazz)) {
isConnectHandler = true;
isConnectHandlerMultithreaded = isOnConnectMultithreaded(clazz);
}
if (IHttpDisconnectHandler.class.isAssignableFrom(clazz)) {
isDisconnectHandler = true;
isDisconnectHandlerMultithreaded = isOnDisconnectMultithreaded(clazz);
}
}
static boolean isOnConnectMultithreaded(Class serverHandlerClass) {
int mode = IHttpRequestHandler.DEFAULT_EXECUTION_MODE;
Execution execution = serverHandlerClass.getAnnotation(Execution.class);
if (execution != null) {
mode = execution.value();
return (mode == Execution.MULTITHREADED);
}
try {
Method meth = serverHandlerClass.getMethod("onConnect", new Class[] { IHttpConnection.class });
execution = meth.getAnnotation(Execution.class);
if (execution != null) {
mode = execution.value();
}
} catch (NoSuchMethodException nsme) {
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("shouldn't occure because body handler has to have such a method " + nsme.toString());
}
}
return (mode == Execution.MULTITHREADED);
}
static boolean isOnDisconnectMultithreaded(Class serverHandlerClass) {
int mode = IHttpRequestHandler.DEFAULT_EXECUTION_MODE;
Execution execution = serverHandlerClass.getAnnotation(Execution.class);
if (execution != null) {
mode = execution.value();
return (mode == Execution.MULTITHREADED);
}
try {
Method meth = serverHandlerClass.getMethod("onDisconnect", new Class[] { IHttpConnection.class });
execution = meth.getAnnotation(Execution.class);
if (execution != null) {
mode = execution.value();
}
} catch (NoSuchMethodException nsme) {
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("shouldn't occure because body handler has to have such a method " + nsme.toString());
}
}
return (mode == Execution.MULTITHREADED);
}
public boolean isConnectHandler() {
return isConnectHandler;
}
public boolean isConnectHandlerMultithreaded() {
return isConnectHandlerMultithreaded;
}
public boolean isDisconnectHandler() {
return isDisconnectHandler;
}
public boolean isDisconnectHandlerMultithreaded() {
return isDisconnectHandlerMultithreaded;
}
}
private static final class FormEncodedRequestHeaderWrapper extends HttpRequestHeaderWrapper {
private static final Boolean NULL_BOOLEAN = null;
private final IHttpRequest request;
private final Map> bodyParamsMap = new HashMap>();
private boolean isBodyParsed = false;
FormEncodedRequestHeaderWrapper(IHttpRequest request) throws IOException {
super(request.getRequestHeader());
this.request = request;
}
public Integer getIntParameter(String name) {
String s = getParameter(name);
if (s != null) {
return Integer.parseInt(s);
} else {
return null;
}
}
public int getIntParameter(String name, int defaultVal) {
String s = getParameter(name);
if (s != null) {
try {
return Integer.parseInt(s);
} catch (Exception e) {
return defaultVal;
}
} else {
return defaultVal;
}
}
public Long getLongParameter(String name) {
String s = getParameter(name);
if (s != null) {
return Long.parseLong(s);
} else {
return null;
}
}
public long getLongParameter(String name, long defaultVal) {
String s = getParameter(name);
if (s != null) {
try {
return Long.parseLong(s);
} catch (Exception e) {
return defaultVal;
}
} else {
return defaultVal;
}
}
public Double getDoubleParameter(String name) {
String s = getParameter(name);
if (s != null) {
return Double.parseDouble(s);
} else {
return null;
}
}
public double getDoubleParameter(String name, double defaultVal) {
String s = getParameter(name);
if (s != null) {
try {
return Double.parseDouble(s);
} catch (Exception e) {
return defaultVal;
}
} else {
return defaultVal;
}
}
public Float getFloatParameter(String name) {
String s = getParameter(name);
if (s != null) {
return Float.parseFloat(s);
} else {
return null;
}
}
public float getFloatParameter(String name, float defaultVal) {
String s = getParameter(name);
if (s != null) {
try {
return Float.parseFloat(s);
} catch (Exception e) {
return defaultVal;
}
} else {
return defaultVal;
}
}
public Boolean getBooleanParameter(String name) {
String s = getParameter(name);
if (s != null) {
return Boolean.parseBoolean(s);
} else {
return NULL_BOOLEAN;
}
}
public boolean getBooleanParameter(String name, boolean defaultVal) {
String s = getParameter(name);
if (s != null) {
try {
return Boolean.parseBoolean(s);
} catch (Exception e) {
return defaultVal;
}
} else {
return defaultVal;
}
}
private void parseBodyIfNecessary() {
if (!isBodyParsed) {
isBodyParsed = true;
try {
if (HttpUtils.hasFormUrlencodedContentType(request) && request.hasBody()) {
bodyParamsMap.putAll(parseParamters(request.getNonBlockingBody().toString(), HttpUtils.getContentTypedFormUrlencodedEncodingType(request)));
} else {
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("warning request does not contain a FORM-URLENCODED body: " + request);
}
}
} catch (IOException ioe) {
throw new RuntimeException(ioe);
}
}
}
public String getParameter(String name) {
parseBodyIfNecessary();
if (bodyParamsMap.containsKey(name)) {
return bodyParamsMap.get(name).get(0);
}
return getWrappedRequestHeader().getParameter(name);
}
public String[] getParameterValues(String name) {
parseBodyIfNecessary();
ArrayList result = new ArrayList();
if (bodyParamsMap.containsKey(name)) {
result.addAll(bodyParamsMap.get(name));
}
String[] v = getWrappedRequestHeader().getParameterValues(name);
result.addAll(Arrays.asList(v));
return result.toArray(new String[result.size()]);
}
public void setParameter(String parameterName, String parameterValue) {
parseBodyIfNecessary();
if (bodyParamsMap.containsKey(parameterName)) {
throw new RuntimeException("parameter is contained in body and can not be modified");
}
getWrappedRequestHeader().setParameter(parameterName, parameterValue);
}
public Set getParameterNameSet() {
parseBodyIfNecessary();
Set result = new HashSet();
result.addAll(getWrappedRequestHeader().getParameterNameSet());
result.addAll(bodyParamsMap.keySet());
return result;
}
@SuppressWarnings("unchecked")
public Enumeration getParameterNames() {
return Collections.enumeration(getParameterNameSet());
}
}
static final class CompletionHandlerInfo {
private boolean isUnsynchronized = false;
private boolean isOnWrittenMultithreaded = false;
private boolean isOnExceptionMultithreaded = false;
public CompletionHandlerInfo(IWriteCompletionHandler handler) {
boolean isHandlerMultithreaded = isHandlerMultithreaded(handler.getClass(), true);
isOnWrittenMultithreaded = isMethodMultithreaded(handler.getClass(), "onWritten", isHandlerMultithreaded, int.class);
isOnExceptionMultithreaded = isMethodMultithreaded(handler.getClass(), "onException", isHandlerMultithreaded, IOException.class);
isUnsynchronized = (handler instanceof IUnsynchronized);
}
public boolean isUnsynchronized() {
return isUnsynchronized;
}
public boolean isOnWrittenMultithreaded() {
return isOnWrittenMultithreaded;
}
public boolean isOnExceptionMutlithreaded() {
return isOnExceptionMultithreaded;
}
}
static final class ResponseHandlerInfo {
private static final List SYSTEM_HANDLERS = Arrays.asList(new String[] { "org.xlightweb.client.CookieHandler$ResponseHandler",
"org.xlightweb.client.RedirectHandler$BodyRedirectResponseHandler",
"org.xlightweb.client.RedirectHandler$BodylessRedirectResponseHandler",
"org.xlightweb.client.RetryHandler$BodyRetryResponseHandler",
"org.xlightweb.client.RetryHandler$BodylessRetryResponseHandler"});
private boolean isResponseHandler = false;
private boolean isUnsynchronized = false;
private boolean isResponseHandlerInvokeOnMessageReceived = false;
private boolean isResponseHandlerMultithreaded = true;
private boolean isResponseExeptionHandlerMultithreaded = true;
private boolean isSocketTimeoutHandler = false;
private boolean isSocketTimeoutHandlerMultithreaded = true;
private boolean isContinueHandler = true;
@SuppressWarnings("unchecked")
ResponseHandlerInfo(Class clazz) {
if (clazz == null) {
return;
}
isContinueHandler = HttpUtils.isContinueHandler(clazz, "onResponse", IHttpResponse.class);
if (IHttpResponseHandler.class.isAssignableFrom(clazz)) {
isResponseHandler = true;
isResponseHandlerMultithreaded = isOnResponseMultithreaded((Class) clazz);
isResponseHandlerInvokeOnMessageReceived = isOnResponseInvokeOnMessageReceived((Class) clazz);
isResponseExeptionHandlerMultithreaded = isOnResponseExceptionMultithreaded((Class) clazz);
}
isSocketTimeoutHandlerMultithreaded = isResponseExeptionHandlerMultithreaded;
if (IHttpSocketTimeoutHandler.class.isAssignableFrom(clazz)) {
isSocketTimeoutHandler = true;
isSocketTimeoutHandlerMultithreaded = isOnResponseTimeoutMultithreaded((Class) clazz);
}
isUnsynchronized = IUnsynchronized.class.isAssignableFrom(clazz) || SYSTEM_HANDLERS.contains(clazz.getName());
}
static boolean isOnResponseMultithreaded(Class handlerClass) {
boolean isMultithreaded = HttpUtils.isHandlerMultithreaded(handlerClass, (IHttpResponseHandler.DEFAULT_EXECUTION_MODE == Execution.MULTITHREADED));
return HttpUtils.isMethodMultithreaded(handlerClass, "onResponse", isMultithreaded, IHttpResponse.class);
}
static boolean isOnResponseExceptionMultithreaded(Class handlerClass) {
boolean isMultithreaded = HttpUtils.isHandlerMultithreaded(handlerClass, (IHttpResponseHandler.DEFAULT_EXECUTION_MODE == Execution.MULTITHREADED));
return HttpUtils.isMethodMultithreaded(handlerClass, "onException", isMultithreaded, IOException.class);
}
static boolean isOnResponseTimeoutMultithreaded(Class handlerClass) {
boolean isMultithreaded = HttpUtils.isHandlerMultithreaded(handlerClass, (IHttpResponseHandler.DEFAULT_EXECUTION_MODE == Execution.MULTITHREADED));
return HttpUtils.isMethodMultithreaded(handlerClass, "onException", isMultithreaded, SocketTimeoutException.class);
}
static boolean isOnResponseInvokeOnMessageReceived(Class handlerClass) {
boolean invokeOnMessageReceived = HttpUtils.isInvokeOnMessageReceived(handlerClass, (IHttpResponseHandler.DEFAULT_INVOKE_ON_MODE == InvokeOn.MESSAGE_RECEIVED));
return HttpUtils.isInvokeOnMessageReceived(handlerClass, "onResponse", invokeOnMessageReceived, IHttpResponse.class);
}
public boolean isResponseHandler() {
return isResponseHandler;
}
public boolean isResponseHandlerInvokeOnMessageReceived() {
return isResponseHandlerInvokeOnMessageReceived;
}
public boolean isResponseHandlerMultithreaded() {
return isResponseHandlerMultithreaded;
}
public boolean isSocketTimeoutHandler() {
return isSocketTimeoutHandler;
}
public boolean isResponseExeptionHandlerMultithreaded() {
return isResponseExeptionHandlerMultithreaded;
}
public boolean isContinueHandler() {
return isContinueHandler;
}
public boolean isSocketTimeoutHandlerMultithreaded() {
return isSocketTimeoutHandlerMultithreaded;
}
public boolean isUnsynchronized() {
return isUnsynchronized;
}
}
static final class BodyCompeleteListenerInfo {
private final boolean isUnsynchronized;
private final boolean isMultithreaded;
@SuppressWarnings("unchecked")
BodyCompeleteListenerInfo(Class clazz) {
if (clazz != null) {
boolean isMultiThreaded = HttpUtils.isHandlerMultithreaded(clazz, true);
isUnsynchronized = IUnsynchronized.class.isAssignableFrom(clazz);
isMultithreaded = HttpUtils.isMethodMultithreaded(clazz, "onComplete", isMultiThreaded);
} else {
isUnsynchronized = true;
isMultithreaded = false;
}
}
public boolean isMultithreaded() {
return isMultithreaded;
}
public boolean isUnsynchronized() {
return isUnsynchronized;
}
}
static final class RequestHandlerInfo {
private static final Logger LOG = Logger.getLogger(RequestHandlerInfo.class.getName());
private static final List SYSTEM_HANDLERS = Arrays.asList(new String[] { "org.xlightweb.client.CookieHandler",
"org.xlightweb.client.CacheHandler",
"org.xlightweb.client.RedirectHandler",
"org.xlightweb.client.ProxyHandler",
"org.xlightweb.client.RetryHandler"});
private String clazzName;
private boolean isUnsynchronized = false;
private boolean isRequestHandlerSynchronizedOnSession = false;
private boolean isRequestHandlerInvokeOnMessageReceived = false;
private boolean isRequestHandlerMultithreaded = true;
private boolean isLifeCycle = false;
private boolean isRequestTimoutHandler = false;
private boolean isRequestTimoutHandlerMultithreaded = true;
private boolean isContinueHandler = true;
@SuppressWarnings("unchecked")
RequestHandlerInfo(Class clazz) {
if (clazz == null) {
return;
}
clazzName = clazz.getName();
isLifeCycle = ILifeCycle.class.isAssignableFrom(clazz);
isContinueHandler = HttpUtils.isContinueHandler(clazz, "onRequest", IHttpExchange.class);
if (IHttpRequestHandler.class.isAssignableFrom(clazz)) {
isRequestHandlerMultithreaded = isOnRequestMultithreaded((Class) clazz);
isRequestHandlerInvokeOnMessageReceived = isOnRequestInvokeOnMessageReceived((Class) clazz);
isRequestHandlerSynchronizedOnSession = isOnSession((Class) clazz);
if (isRequestHandlerSynchronizedOnSession && !isRequestHandlerMultithreaded) {
LOG.warning("request handler " + clazz.getName() + " is annotated as session scope and non-threaded. " +
"Session scope have to be multi-threaded. Updating execution mode");
isRequestHandlerMultithreaded = true;
}
}
if (IHttpRequestTimeoutHandler.class.isAssignableFrom(clazz)) {
isRequestTimoutHandler = true;
isRequestTimoutHandlerMultithreaded = HttpUtils.isRequestTimeoutHandlerMultithreaded((Class) clazz);
}
isUnsynchronized = IUnsynchronized.class.isAssignableFrom(clazz) || SYSTEM_HANDLERS.contains(clazz.getName());
}
static boolean isOnRequestMultithreaded(Class serverHandlerClass) {
boolean isMultithreaded = HttpUtils.isHandlerMultithreaded(serverHandlerClass, (IHttpRequestHandler.DEFAULT_EXECUTION_MODE == Execution.MULTITHREADED));
return HttpUtils.isMethodMultithreaded(serverHandlerClass, "onRequest", isMultithreaded, IHttpExchange.class);
}
static boolean isOnRequestInvokeOnMessageReceived(Class handlerClass) {
boolean invokeOnMessageReceived = HttpUtils.isInvokeOnMessageReceived(handlerClass, (IHttpRequestHandler.DEFAULT_INVOKE_ON_MODE == InvokeOn.MESSAGE_RECEIVED));
return HttpUtils.isInvokeOnMessageReceived(handlerClass, "onRequest", invokeOnMessageReceived, IHttpExchange.class);
}
static boolean isOnSession(Class handlerClass) {
boolean isSynchronizedOnSession = HttpUtils.isSynchronizedOnSession(handlerClass, (IHttpRequestHandler.DEFAULT_SYNCHRONIZED_ON_MODE == SynchronizedOn.SESSION));
return HttpUtils.isSynchronizedOnSession(handlerClass, "onRequest", isSynchronizedOnSession, IHttpExchange.class);
}
public boolean isLifeCycle() {
return isLifeCycle;
}
public boolean isRequestHandlerInvokeOnMessageReceived() {
return isRequestHandlerInvokeOnMessageReceived;
}
public boolean isRequestHandlerSynchronizedOnSession() {
return isRequestHandlerSynchronizedOnSession;
}
public boolean isRequestHandlerMultithreaded() {
return isRequestHandlerMultithreaded;
}
public boolean isRequestTimeoutHandler() {
return isRequestTimoutHandler;
}
public boolean isRequestTimeoutHandlerMultithreaded() {
return isRequestTimoutHandlerMultithreaded;
}
public boolean isContinueHandler() {
return isContinueHandler;
}
public boolean isUnsynchronized() {
return isUnsynchronized;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder(super.toString() + " (");
sb.append("classname=" + clazzName + " isLifeCycle=" + isLifeCycle + " " +
"isUnsynchronized=" + isUnsynchronized + " " +
"isContinueHandler=" + isContinueHandler + " " +
"isRequestHandlerMultithreaded=" + isRequestHandlerMultithreaded + " " +
"isRequestHandlerInvokeOnMessageReceived=" + isRequestHandlerInvokeOnMessageReceived + " " +
"isRequestHandlerSynchronizedOnSession=" + isRequestHandlerSynchronizedOnSession + " " +
"isRequestTimoutHandler=" + isRequestTimoutHandler + " " +
"isRequestTimoutHandlerMultithreaded=" + isRequestTimoutHandlerMultithreaded + ")");
return sb.toString();
}
}
private static class XLightwebThreadFactory implements ThreadFactory {
private static final AtomicInteger poolNumber = new AtomicInteger(1);
private final AtomicInteger threadNumber = new AtomicInteger(1);
private final String namePrefix;
XLightwebThreadFactory() {
namePrefix = "xLightwebPool-" + poolNumber.getAndIncrement() + "-thread-";
}
public Thread newThread(Runnable r) {
Thread t = new Thread(r, namePrefix + threadNumber.getAndIncrement());
if (!t.isDaemon()) {
t.setDaemon(true);
}
if (t.getPriority() != Thread.NORM_PRIORITY) {
t.setPriority(Thread.NORM_PRIORITY);
}
return t;
}
}
private static final class Bom {
private String encoding;
private byte[] bom;
public Bom(String encoding, byte[] bom) {
this.encoding = encoding;
this.bom = bom;
}
public String getEncoding() {
return encoding;
}
public boolean match(byte[] data) {
for (int i = 0; i < bom.length; i++) {
if (bom[i] != data[i]) {
return false;
}
}
return true;
}
public boolean match(ByteBuffer buffer) {
if (buffer.remaining() < bom.length) {
return false;
}
int pos = buffer.position();
try {
for (int i = 0; i < bom.length; i++) {
if (bom[i] != buffer.get()) {
return false;
}
}
return true;
} finally {
buffer.position(pos);
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy