Package org.xlightweb

Source Code of org.xlightweb.HttpProtocolHandlerServerSide

/*
*  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 static org.xlightweb.HttpUtils.AND;
import static org.xlightweb.HttpUtils.CR;
import static org.xlightweb.HttpUtils.EQUALS;
import static org.xlightweb.HttpUtils.HTAB;
import static org.xlightweb.HttpUtils.LF;
import static org.xlightweb.HttpUtils.MAX_HEADER_SIZE;
import static org.xlightweb.HttpUtils.QUESTION_MARK;
import static org.xlightweb.HttpUtils.SLASH;
import static org.xlightweb.HttpUtils.SPACE;

import java.io.IOException;
import java.nio.ByteBuffer;

import org.xlightweb.AbstractHttpConnection.IMessageHandler;
import org.xlightweb.AbstractHttpConnection.IMessageHeaderHandler;
import org.xsocket.DataConverter;
import org.xsocket.connection.INonBlockingConnection;



/**
* server side http protocol handler
* @author grro@xlightweb.org
*/
final class HttpProtocolHandlerServerSide extends AbstractHttpProtocolHandler {
   
 
  ByteBuffer[] parseHeader(AbstractHttpConnection httpConnection, ByteBuffer[] rawData) throws BadMessageException, IOException {
    HttpRequestHeader requestHeader = parse(httpConnection.getUnderlyingTcpConnection(), rawData);
   
    if (requestHeader != null) {
        requestHeader.setBodyDefaultEncoding(httpConnection.getBodyDefaultEncoding());
       
      httpConnection.setLastTimeHeaderReceivedMillis(System.currentTimeMillis());
      httpConnection.incCountMessageReceived();

      IMessageHeaderHandler messageHeaderHandler = httpConnection.getMessageHeaderHandler();
      if (messageHeaderHandler == null) {
        throw new IOException("no message handler set");
      }
     
     
      HttpRequest request = null;
     
      switch (getBodyType(requestHeader)) {
       
        case BODY_TYPE_EMTPY:
          request = new HttpRequest(requestHeader);
          IMessageHandler messageHandler = messageHeaderHandler.onMessageHeaderReceived(request);
          messageHandler.onHeaderProcessed();
         
          messageHandler.onMessageReceived();
          httpConnection.onMessageCompleteReceived(requestHeader);
         
          rawData = HttpUtils.compact(rawData);
          reset();
         
          // next request? (-> pipelining)
          if (rawData != null) {
            return onData(httpConnection, rawData);
          }               
         
          break;
         
        case BODY_TYPE_FULL_MESSAGE:
          AbstractNetworkBodyDataSource dataSource = new FullMessageBodyDataSource(requestHeader, requestHeader.getContentLength(), httpConnection);
          setBodyDataSource(dataSource);
         
          request = new HttpRequest(requestHeader, dataSource);
          messageHandler = messageHeaderHandler.onMessageHeaderReceived(request);
          try {
            setMessageHandler(messageHandler);
           
            setState(RECEIVING_BODY);
           
            request.getNonBlockingBody().setBodyDataReceiveTimeoutMillisSilence(httpConnection.getBodyDataReceiveTimeoutMillis());
            rawData = parserBody(httpConnection, rawData);
          } finally {
            messageHandler.onHeaderProcessed();
          }
         
          return rawData;
         
         
                case BODY_TYPE_WEBSOCKET:
                    dataSource = new FullMessageBodyDataSource(requestHeader, 8, httpConnection);
                    setBodyDataSource(dataSource);
                   
                    request = new HttpRequest(requestHeader, dataSource);
                    request.removeHeader("Content-Length");
                    messageHandler = messageHeaderHandler.onMessageHeaderReceived(request);
                    try {
                        setMessageHandler(messageHandler);
                       
                        setState(RECEIVING_BODY);
                       
                        request.getNonBlockingBody().setBodyDataReceiveTimeoutMillisSilence(httpConnection.getBodyDataReceiveTimeoutMillis());
                        rawData = parserBody(httpConnection, rawData);
                    } finally {
                        messageHandler.onHeaderProcessed();
                    }
                   
                    return rawData;
         
         
        default// BODY_TYPE_CHUNKED
          dataSource = new FullMessageChunkedBodyDataSource(httpConnection, requestHeader);
            setBodyDataSource(dataSource);
               
            request = new HttpRequest(requestHeader, dataSource);

          messageHandler = messageHeaderHandler.onMessageHeaderReceived(request);
          try {
            setMessageHandler(messageHandler);
 
            setState(RECEIVING_BODY);
 
            request.getNonBlockingBody().setBodyDataReceiveTimeoutMillis(httpConnection.getBodyDataReceiveTimeoutMillis());
            rawData = parserBody(httpConnection, rawData);
          } finally {
            messageHandler.onHeaderProcessed();
          }
         
          return rawData;
      }           
     
    }
    return rawData;   
  }
 
 
 
  private static int getBodyType(HttpRequestHeader requestHeader) throws BadMessageException {
   
    String method = requestHeader.getMethod();

 
    // GET request?
    if (method.equals(IHttpMessage.GET_METHOD)) {
        if (requestHeader.getHeader("Sec-WebSocket-Key1") != null) {
            return BODY_TYPE_WEBSOCKET;
        } else {
            return BODY_TYPE_EMTPY;
        }
    }

   
    // POST request?
    if (method.equals(IHttpMessage.POST_METHOD)) {
      return getBodyTypeByHeader(requestHeader);
    }
   
   
    // other bodyless request?
    if (method.equals(IHttpMessage.CONNECT_METHOD) ||
      method.equals(IHttpMessage.HEAD_METHOD) ||
        method.equals(IHttpMessage.TRACE_METHOD)||
        method.equals(IHttpMessage.DELETE_METHOD)||
        method.equals(IHttpMessage.OPTIONS_METHOD)) {
     
      return BODY_TYPE_EMTPY;
    }
       
    // ... no, determine if body by header
    return getBodyTypeByHeader(requestHeader);
  }
 
 
  private static int getBodyTypeByHeader(HttpRequestHeader requestHeader) throws BadMessageException {
   
    // contains a non-zero Content-Length header  -> bound body
    if ((requestHeader.getContentLength() != -1)) {
      if (requestHeader.getContentLength() > 0) {
        return BODY_TYPE_FULL_MESSAGE;
      }
      return BODY_TYPE_EMTPY;
    }
   
    //  transfer encoding header is set with chunked -> chunked body
    String transferEncoding = requestHeader.getTransferEncoding();
    if ((transferEncoding != null) && (transferEncoding.equalsIgnoreCase("chunked"))) {
      return BODY_TYPE_CHUNKED;
    }
   
    throw new BadMessageException(requestHeader.toString());
  }
 
 

    private static HttpRequestHeader parse(INonBlockingConnection connection, ByteBuffer[] rawData) throws BadMessageException, IOException {

        // filter CR:
        // RFC 2616 (19.3 Tolerant Applications)
        // ... The line terminator for message-header fields is the sequence CRLF.
        // However, we recommend that applications, when parsing such headers,
        // recognize a single LF as a line terminator and ignore the leading CR...

        HttpRequestHeader requestHeader = new HttpRequestHeader(connection);

       
        if (rawData == null) {
            return null;
        }

        ByteBuffer workBuffer = HttpUtils.duplicateAndMerge(rawData);
        int savePosition = workBuffer.position();
        int saveLimit = workBuffer.limit();
       
       
        try {
           
            // looking for first char of method -> P
            int posStart;
            while (true) {
                byte b = workBuffer.get();
                if (b > SPACE) {
                    posStart = workBuffer.position();
                    break;
                   
                } else if ((b != CR) && (b != LF)) {
                    if (HttpUtils.isShowDetailedError()) {
                        workBuffer.position(savePosition);
                        workBuffer.limit(saveLimit);
                        throw new BadMessageException("bad request (by parsing method name): " + DataConverter.toString(HttpUtils.copy(workBuffer)));
                    } else {
                        throw new BadMessageException("bad request");
                    }
                }
            }
   

            // looking for last char of method -> POST
            while (true) {
                if (workBuffer.get() == SPACE) {
                    int posEnd = workBuffer.position();
                   
                    String method = extractString(posStart - 1, posEnd - posStart, workBuffer);
                    requestHeader.setMethodSilence(method);
                    workBuffer.position(posEnd);
                    break;
                }
            }

           
            // looking for first char of path -> POST /
            while (true) {
                if (workBuffer.get() > SPACE) {
                    posStart = workBuffer.position();
                    break;
                }
            }

           

            // looking for last char of path or question mark -> POST /test/resource
            while (true) {
                byte b = workBuffer.get();
               
                if (b < 64) {  // performance optimization: check only char equals or lower than '?'               
                    int posEnd = workBuffer.position();

                    if (b == SPACE) {
                        String path = extractString(posStart - 1, posEnd - posStart, workBuffer);
                        requestHeader.setPathSilence(path);
                        workBuffer.position(posEnd);
                        break;


                    } else if (b == QUESTION_MARK) {
                        String path = extractString(posStart - 1, posEnd - posStart, workBuffer);
                        requestHeader.setPathSilence(path);
                        workBuffer.position(posEnd);

                        parserQueryParams(workBuffer, posEnd, requestHeader);
                        break;
                       
                       
                    // printable char
                    } else if (b > SPACE) {
                        continue// optimization: printable char: continue
                       
                       
                    // HTTP 0.9 request handling
                    } else if (b == LF) {
                        throw new BadMessageException("simple request messages are not supported");
                       
                    } else if (b == CR) {
                        b = workBuffer.get();
                        if (b == LF) {
                            throw new BadMessageException("simple request messages are not supported");
                        } else {
                            workBuffer.position(workBuffer.position() - 1);
                        }
                    }
                }
            }


            // looking for slash  -> POST /test/resource HTTP/1.1
            while (true) {
                byte b = workBuffer.get();
                if (b < 48) {  // performance optimization: check only char equals or lower than '/'
                    if (b == SLASH) {
                        int posSlash = workBuffer.position();
                       
                        String protocolScheme = extractString(posSlash - 5, 4, workBuffer);
                        requestHeader.setProtocolSchemeSilence(protocolScheme);
                       
                        String protocolVersion = extractString(posSlash, 3, workBuffer);
                        requestHeader.setProtocolVersionSilence(protocolVersion);
                       
                        workBuffer.position(posSlash + 3);
                        break;
                       
                    } else if (b >= SPACE) {
                        continue;
                       
                    } else if (b != HTAB) {
                        if (HttpUtils.isShowDetailedError()) {
                            workBuffer.position(savePosition);
                            workBuffer.limit(saveLimit);
                            throw new BadMessageException("bad request (by parsing protocol): " + DataConverter.toString(HttpUtils.copy(workBuffer)));
                        } else {
                            throw new BadMessageException("bad message");
                        }
                           
                    }
                }
            }
           
           
            // looking for LF  -> POST /test/resource HTTP/1.1
            while (true) {
                if (workBuffer.get() == LF) {
                    break;
                }
            }

            // parse header lines
            parseHeaderLines(workBuffer, requestHeader);

            skipPositions(workBuffer.position() - savePosition, rawData);

            return requestHeader;


        } catch (BadMessageException bme) {
            throw bme;
           
        } catch (Exception e) {
            workBuffer.position(savePosition);
            workBuffer.limit(saveLimit);
           
            if (workBuffer.remaining() > MAX_HEADER_SIZE) {
                throw new BadMessageException("max header size reached");
            } else {
                return null;
            }
        }
    }
   
   
    private static void parserQueryParams(ByteBuffer buffer, int pos, HttpRequestHeader requestHeader) throws BadMessageException, IOException {
       
        String paramName = null;
        String paramValue = "";
       
        boolean isQueryParameterRead = false;
       
        while (true) {
            byte b = buffer.get();
           
            if (b < 62) { // performance optimization: check only char equals or lower than '='                

                // looking for equals -> POST /test/resource?param1=
                if (b == EQUALS) {
                    isQueryParameterRead = true;
                    paramName = extractString(pos, (buffer.position() - 1- pos, buffer);
                    pos = buffer.position() + 1;
                   
                    // parse value
                    while (true) {
                        b = buffer.get();

                        if (b < 39) { // performance optimization: check only char equals or lower than '&'
                           
                            // looking for & -> POST /test/resource?param1=value1&
                            if (b == AND) {
                                paramValue = extractString(pos, (buffer.position() - 1- pos, buffer);
                                pos = buffer.position() + 1;
                                buffer.position(pos);

                                requestHeader.addRawQueryParameterSilence(paramName, paramValue);
                                paramName = null;
                                paramValue = "";
                                break;
                               
                            // or for space -> POST /test/resource?param1=value1                               
                            } else if (b == SPACE) {
                                paramValue = extractString(pos, (buffer.position() - 1- pos, buffer);
                                buffer.position(buffer.position() + 1);
                               
                                requestHeader.addRawQueryParameterSilence(paramName, paramValue);
                                return;
                               
                            // printable char
                            } else if (b > SPACE) {
                                continue// optimization: printable char: continue
                               
                               
                            // HTTP 0.9 request handling
                            } else if (b == LF) {
                                throw new BadMessageException("simple request messages are not supported");
                               
                            } else if (b == CR) {
                                b = buffer.get();
                                if (b == LF) {
                                    throw new BadMessageException("simple request messages are not supported");
                                } else {
                                    buffer.position(buffer.position() - 1);
                                }
                            }
                        }
                    }
                   
                } else if (b == SPACE) {
                    if (!isQueryParameterRead) {
                        String queryString = extractString(pos, (buffer.position() - 1- pos, buffer);
                        requestHeader.setQueryString(queryString);
                    }
                    buffer.position(buffer.position() + 1);
                   
                    return;
                   
                // printable char
                } else if (b > SPACE) {
                    continue;
                   
                   
                // HTTP 0.9 request handling
                } else if (b == LF) {
                    throw new BadMessageException("simple request messages are not supported");
                   
                } else if (b == CR) {
                    b = buffer.get();
                    if (b == LF) {
                        throw new BadMessageException("simple request messages are not supported");
                    } else {
                        buffer.position(buffer.position() - 1);
                    }
                }
            }
        }
    }
   
    @Override
    void onDisconnectInHeaderNothingReceived(AbstractHttpConnection httpConnection, ByteBuffer[] rawData) {
        // do nothing
    }
}

TOP

Related Classes of org.xlightweb.HttpProtocolHandlerServerSide

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.