Package org.xlightweb

Source Code of org.xlightweb.HttpUtils$ExtendedHttpRequestHeaderWrapper

/*
*  Copyright (c) xlightweb.org, 2008 - 2009. 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.IOException;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URLDecoder;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.Map.Entry;
import java.util.logging.Level;
import java.util.logging.Logger;


import org.xsocket.DataConverter;
import org.xsocket.Execution;
import org.xsocket.Resource;
import org.xsocket.connection.ConnectionUtils;
import org.xsocket.connection.IServer;
import org.xsocket.connection.IWriteCompletionHandler;


/**
* A HTTP utility class
*
* @author grro@xlightweb.org
*/
public final class HttpUtils {
 
  static final byte CR = 13;
  static final byte LF = 10;
  static final byte SPACE = 32;
  static final byte HTAB = 9;
 
  private static final Logger LOG = Logger.getLogger(HttpUtils.class.getName());
 
  @SuppressWarnings("unchecked")
  private static final Map<Class, Boolean> bodyDataExecutionModeCache = newMapCache(25);
 
  @SuppressWarnings("unchecked")
  private static final Map<Class, Boolean> bodyCompleteListenerExecutionModeCache = newMapCache(25);

  @SuppressWarnings("unchecked")
  private static final Map<Class, Boolean> requestTimeoutHandlerExecutionModeCache = newMapCache(25);

    @SuppressWarnings("unchecked")
    private static final Map<Class, CompletionHandlerInfo> completionHandlerInfoCache = ConnectionUtils.newMapCache(25);
 
  @SuppressWarnings("unchecked")
  private static final Map<Class, Boolean> bodyCloseListenerExecutionModeCache = newMapCache(25);

 
  @SuppressWarnings("unchecked")
  private static final Map<Class, RequestHandlerInfo> httpRequestHandlerInfoCache = HttpUtils.newMapCache(25);
  private static RequestHandlerInfo emptyHttpRequestHandlerInfo;

  @SuppressWarnings("unchecked")
  private static final Map<Class, PartHandlerInfo> partHandlerInfoCache = HttpUtils.newMapCache(25);
  private static PartHandlerInfo emptyPartHandlerInfo;
 
  @SuppressWarnings("unchecked")
  private static final Map<Class, ResponseHandlerInfo> httpResponseHandlerInfoCache = HttpUtils.newMapCache(25);
  private static ResponseHandlerInfo emptyResponseHandlerInfo;
 
  @SuppressWarnings("unchecked")
  private static final Map<Class, HttpConnectionHandlerInfo> httpConnectionHandlerInfoCache = HttpUtils.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<String, String> mimeTypeMap;

  private static boolean showDetailedError;

  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;
  }



 
  /**
   * returns the reason text
   *
   * @param statusCode the status code
   * @return the reason text
   */
  public static String getReason(int statusCode) {
    switch (statusCode) {
    case 200:
      return "OK";
     
    case 201:
      return "Created";
     
    case 304:
      return "Not Modifed";

    case 401:
      return "Unauthorized";

    case 404:
      return "Not found";
     
    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();
  }
 

   
 
  /**
   * 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<String, String> getMimeTypeMapping() {
   
    if (mimeTypeMap == null) {
     
      Map<String, String> map = new HashMap<String, String>();
      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 " + iae.toString());
          }
        }
      }
    }
  }





  static boolean isContentTypeFormUrlencoded(IHttpMessage message) {
   
    if (!message.hasBody()) {
      return false;
    }
   
    String contentType = message.getContentType();
    if ((contentType != null) && (contentType.startsWith("application/x-www-form-urlencoded"))) {
      return true;
    }
   
    return false;
  }
 
 
 
  /**
   * 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 "UTF-8";
  }
 


 
  /**
   * 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 ExtendedHttpRequestHeaderWrapper(request), request);
  }
 
 

  /**
   * returns the request handler info
   *
   * @param httpRequestHandler the request handler
   * @return the request handler info
   */
  @SuppressWarnings("unchecked")
  static RequestHandlerInfo getHttpRequestHandlerInfo(IHttpRequestHandler httpRequestHandler) {
    if (httpRequestHandler == null) {
      if (emptyHttpRequestHandlerInfo == null) {
        emptyHttpRequestHandlerInfo = new RequestHandlerInfo(null);
      }
      return emptyHttpRequestHandlerInfo;
    }

    RequestHandlerInfo httpRequestHandlerInfo = httpRequestHandlerInfoCache.get(httpRequestHandler.getClass());

    if (httpRequestHandlerInfo == null) {
      httpRequestHandlerInfo = new RequestHandlerInfo((Class<IHttpRequestHandler>) httpRequestHandler.getClass());
      httpRequestHandlerInfoCache.put(httpRequestHandler.getClass(), httpRequestHandlerInfo);
    }

    return httpRequestHandlerInfo;
  }


   
    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<IPartHandler>) partHandler.getClass());
            partHandlerInfoCache.put(partHandler.getClass(), partHandlerInfo);
        }

        return partHandlerInfo;
    }

 
  /**
   * returns the response handler info
   *
   * @param httpResponseHandler  the response handler
   * @return the response handler info
   */
  @SuppressWarnings("unchecked")
  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<IHttpResponseHandler>) 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<IHttpConnectionHandler>) httpConnectionHandler.getClass());
      httpConnectionHandlerInfoCache.put(httpConnectionHandler.getClass(), httpConnectionHandlerInfo);
    }

    return httpConnectionHandlerInfo;
  }

 


  /**
   * <b>This is a xSocket internal method and subject to change</b>
   */
  static RequestHandlerInfo getRequestHandlerInfo(IHttpRequestHandler requestHandler) {
    return getHttpRequestHandlerInfo(requestHandler);
  }

 
  /**
   * <b>This is a xSocket internal method and subject to change</b>
   */
  static ResponseHandlerInfo getResponseHandlerInfo(IHttpResponseHandler responseHandler) {
    return getHttpResponseHandlerInfo(responseHandler);
  }

 
 
  /**
   * returns true if the body handler is mutlithreaded
   *
   * @param bodyHandler  the body handler
   * @return true if the body handler is multithreaded
   */
  static boolean isMutlithreaded(IBodyDataHandler bodyHandler) {
    Boolean isMutlithreaded = bodyDataExecutionModeCache.get(bodyHandler.getClass());
   
    if (isMutlithreaded == null) {
      int mode = IBodyDataHandler.DEFAULT_EXECUTION_MODE;
     
      Execution execution = bodyHandler.getClass().getAnnotation(Execution.class);
      if (execution != null) {
        mode = execution.value();
      }
     
      try {
        Method meth = bodyHandler.getClass().getMethod("onData", new Class[] { NonBlockingBodyDataSource.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());
        }
      }
     
      isMutlithreaded = (mode == Execution.MULTITHREADED);
     
      bodyDataExecutionModeCache.put(bodyHandler.getClass(), isMutlithreaded);
    }

    return isMutlithreaded;
  }

 
 
  static String parseEncoding(String contentType) {

    // contains charset encoding?
        int pos = contentType.indexOf(';');
        if (pos > 0) {
          int pos2 = contentType.indexOf("charset=", pos + 1);
          if (pos2 > 0) {
            String encoding = contentType.substring(pos2 + "charset=".length(), contentType.length()).trim();
            if(encoding.indexOf(';') != -1) {
                encoding = encoding.substring(0, encoding.indexOf(';'));
            }
            return encoding;
          }
        }
       
        return null;
  }

 
  static String parseEncoding(String contentType, String dflt) {
      String encoding = parseEncoding(contentType);
     
      if (encoding == null) {
          return dflt;
      } else {
          return encoding;
      }
    }
 
 

  static boolean isBodyCompleteListenerMutlithreaded(IBodyCompleteListener completeListener) {
    Boolean isMutlithreaded = bodyCompleteListenerExecutionModeCache.get(completeListener.getClass());
    
    if (isMutlithreaded == null) {
      int mode = IBodyDataHandler.DEFAULT_EXECUTION_MODE;
     
      Execution execution = completeListener.getClass().getAnnotation(Execution.class);
      if (execution != null) {
        mode = execution.value();
      }
     
      try {
        Method meth = completeListener.getClass().getMethod("onComplete", new 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());
        }
      }
     
      isMutlithreaded = (mode == Execution.MULTITHREADED);
     
      bodyCompleteListenerExecutionModeCache.put(completeListener.getClass(), isMutlithreaded);
    }

    return isMutlithreaded;
  }
 
 
 
  /**
   * 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().isComplete()) {
        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().isComplete()) {
        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 int computeRemaining(ByteBuffer[] bufs) {
    int remaining = 0;
   
    for (ByteBuffer byteBuffer : bufs) {
      remaining += byteBuffer.remaining();
    }
   
    return remaining;
  }

 
  /**
   * 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));
  }
 
 
  /**
   * (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;
  }
 
 
  /**
   * returns the execution mode for request timeout handler
   *
   * @param requestTimeoutHandler  the request timeout handler
   * @return the execution mode
   */
  static boolean isRequestTimeoutHandlerMultithreaded(Class<IHttpRequestTimeoutHandler> 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;
  }
 
 
  /**
   * returns the execution mode for the given body complete listener
   *
   * @param bodyHandler  the body handler
   * @return the execution mode
   */
  static boolean isBodyCloseListenerMutlithreaded(IBodyCloseListener closeListener) {
    Boolean isMutlithreaded = bodyCloseListenerExecutionModeCache.get(closeListener.getClass());
    
    if (isMutlithreaded == null) {
      int mode = IBodyDataHandler.DEFAULT_EXECUTION_MODE;
     
      Execution execution = closeListener.getClass().getAnnotation(Execution.class);
      if (execution != null) {
        mode = execution.value();
      }
     
      try {
        Method meth = closeListener.getClass().getMethod("onClose", new 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());
        }
      }
     
      isMutlithreaded = (mode == Execution.MULTITHREADED);
     
      bodyCloseListenerExecutionModeCache.put(closeListener.getClass(), isMutlithreaded);
    }

    return isMutlithreaded;
  }

 
  static Map<String, List<String>> parseParamters(String txt, String encoding) {
    Map<String, List<String>> result = new HashMap<String, List<String>>();
   
    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<String> values = result.get(name);
          if (values == null) {
            values = new ArrayList<String>();
            result.put(name, values);
          }
       
          values.add(URLDecoder.decode(kv[1], encoding));
        }
      }
     
      return result;
   
    } catch (UnsupportedEncodingException use) {
      throw new RuntimeException(use.toString());
    }
  }
 
 
  /**
   * 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;
    }

 
  /**
   * get the implementation date
   *
   * @return the implementation date
   */
  public static String getImplementationDate() {
   
    if (implementationDate== null) {
      readVersionFile();
    }
   
    return implementationDate;
  }

 
  private static void readVersionFile() {
   
    implementationVersion = "<unknown>";
    implementationDate = "<unknown>";
     
    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 = "<unknown>";
            implementationVersion  = "<unknown>";
            xSocketImplementationVersion  = "<unknown>";
                   
      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
   *
   * <br/><br/><b>This is a xSocket internal method and subject to change</b>
   *
   * @param clazz   the handler class
   * @param dflt    the default value
   * @return true, if multi threaded
   */
    public 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
     *
   * <br/><br/><b>This is a xSocket internal method and subject to change</b>
     *
     * @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;
      }
  }
 
 
  
  @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;
        }
  }
   
  
  /**
   * returns a new map cache
   *
   * @param <T>   the key type
   * @param <E>   the value type
   * @param maxSize the max size
   * @return the cache
   */
  public static <T, E>  Map<T, E> newMapCache(int maxSize) {
    return Collections.synchronizedMap(new Cache<T, E>(maxSize));
  }
 

  private static final class Cache<T, E> extends LinkedHashMap<T, E> {
   
    private static final long serialVersionUID = 4513864504007457500L;
   
    private int maxSize = 0;
   
    Cache(int maxSize) {
      this.maxSize = maxSize;
    }
   

    @Override
    protected boolean removeEldestEntry(Entry<T, E> eldest) {
      return size() > maxSize;
   
  }
 
 
 

  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<IHttpRequestHandler> 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<IHttpRequestHandler> 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 ExtendedHttpRequestHeaderWrapper extends HttpRequestHeaderWrapper {
   
    private static final Boolean NULL_BOOLEAN = null;
   
    private Map<String, List<String>> bodyParamsMap = new HashMap<String, List<String>>();
   
   
    ExtendedHttpRequestHeaderWrapper(IHttpRequest request) throws IOException {
      super(request.getRequestHeader());     
     
      if (HttpUtils.isContentTypeFormUrlencoded(request) && request.hasBody()) {
        bodyParamsMap.putAll(parseParamters(request.getNonBlockingBody().toString(), HttpUtils.getContentTypedFormUrlencodedEncodingType(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;
      }
    }
       
   
    public String getParameter(String name) {
      if (bodyParamsMap.containsKey(name)) {
        return bodyParamsMap.get(name).get(0);
      }
     
      return getWrappedRequestHeader().getParameter(name);
    }
   
   
    public String[] getParameterValues(String name) {
      ArrayList<String> result = new ArrayList<String>();
     
      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) {
      if (bodyParamsMap.containsKey(parameterName)) {
        throw new RuntimeException("parameter is contained in body and can not be modified");
      }
     
      getWrappedRequestHeader().setParameter(parameterName, parameterValue);
    }

   
    public Set<String> getParameterNameSet() {
      Set<String> result = new HashSet<String>();
     
      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 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);
        }


        public boolean isOnWrittenMultithreaded() {
            return isOnWrittenMultithreaded;
        }

        public boolean isOnExceptionMutlithreaded() {
            return isOnExceptionMultithreaded;
        }
    } 
}
TOP

Related Classes of org.xlightweb.HttpUtils$ExtendedHttpRequestHeaderWrapper

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.