All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.xlightweb.HttpUtils Maven / Gradle / Ivy

Go to download

xLightweb is a lightweight, high performance, scalable web network library

There is a newer version: 2.13.2
Show newest version
/*
 *  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 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 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 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 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