Package org.xlightweb

Source Code of org.xlightweb.HttpProtocolHandlerServerSide

/*
*  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 static org.xlightweb.HttpUtils.AND;
import static org.xlightweb.HttpUtils.COLON;
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) {
      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.onMessageReceived();
         
          rawData = HttpUtils.compact(rawData);
          reset();
         
          // next request? (-> pipelining)
          if (rawData != null) {
            return onData(httpConnection, rawData);
          }               
         
          break;
         
        case FULL_MESSAGE:
          AbstractNetworkBodyDataSource dataSource = new FullMessageBodyDataSource(httpConnection, requestHeader.getCharacterEncoding(), requestHeader.getContentLength());
          setBodyDataSource(dataSource);
         
          request = new HttpRequest(requestHeader, dataSource);
          messageHandler = messageHeaderHandler.onMessageHeaderReceived(request);
          setMessageHandler(messageHandler);
         
          setState(RECEIVING_BODY);
         
          request.getNonBlockingBody().setBodyDataReceiveTimeoutMillis(httpConnection.getBodyDataReceiveTimeoutMillis());
          return parserBody(httpConnection, rawData);
         
        default// BODY_TYPE_CHUNKED
          dataSource = new FullMessageChunkedBodyDataSource(httpConnection, requestHeader);
            setBodyDataSource(dataSource);
               
            request = new HttpRequest(requestHeader, dataSource);

          messageHandler = messageHeaderHandler.onMessageHeaderReceived(request);
          setMessageHandler(messageHandler);

          setState(RECEIVING_BODY);

          request.getNonBlockingBody().setBodyDataReceiveTimeoutMillis(httpConnection.getBodyDataReceiveTimeoutMillis());
          return parserBody(httpConnection, rawData);
      }           
     
    }
    return rawData;   
  }
 
 
 
  private static int getBodyType(HttpRequestHeader requestHeader) throws BadMessageException {
   
    String method = requestHeader.getMethod();

 
    // GET request?
    if (method.equals(IHttpMessage.GET_METHOD)) {
      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 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;
        }

        boolean isMerged = false;
        ByteBuffer buffer;
        if (rawData.length == 1) {
            buffer = rawData[0];
        } else {
            buffer = HttpUtils.merge(rawData);
            isMerged = true;
        }
       
       
        int position = buffer.position();
        int limit = buffer.limit();
       
        try {
           
            // looking for first char of method -> P
            int posStart;
            while (true) {
                byte b = buffer.get();
                if (b > SPACE) {
                    posStart = buffer.position();
                    break;
                   
                } else if ((b != CR) && (b != LF)) {
                    if (HttpUtils.isShowDetailedError()) {
                        buffer.position(position);
                        buffer.limit(limit);
                        throw new BadMessageException("bad request (by parsing method name): " + DataConverter.toString(HttpUtils.copy(buffer)));
                    } else {
                        throw new BadMessageException("bad request");
                    }
                }
            }
   

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

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

           

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

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


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

                        parserQueryParams(buffer, 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 = buffer.get();
                        if (b == LF) {
                            throw new BadMessageException("simple request messages are not supported");
                        } else {
                            buffer.position(buffer.position() - 1);
                        }
                    }
                }
            }


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

            outer : while (true) {
               
                // is first char not printable?
                byte firstCharOfLine = buffer.get();
                if (firstCharOfLine < 33) {
                    if (firstCharOfLine == CR) {
                        byte secondCharOfLine = buffer.get();
                        if (secondCharOfLine == LF) {
                            if (name != null) {
                                requestHeader.addHeader(name, value);
                            }
                           
                            if (isMerged) {
                                updateMergedBuffers(buffer, position, rawData);
                            }
                            return requestHeader;
                        }
                       
                    } else if (firstCharOfLine == LF) {
                        if (name != null) {
                            requestHeader.addHeader(name, value);
                        }
                       
                        if (isMerged) {
                            updateMergedBuffers(buffer, position, rawData);
                        }
                        return requestHeader;
                       
                       
                    // Folding
                    } else if ((firstCharOfLine == SPACE) || (firstCharOfLine == HTAB)) {
                       
                        // read first non WSP char
                        int pos = 0;
                        while (true) {
                            byte c = buffer.get();
                            if ((c != SPACE) && (c != HTAB)) {
                                pos = buffer.position();
                                break;
                            }
                        }
                       
                        // looking for LF
                        while (true) {
                            if (buffer.get() == LF) {
                                posLF = buffer.position();
                                String v2 = extractStringWithoutTailingCR(pos - 1, posLF - pos, buffer);
                                if (value == null) {
                                    value = v2;
                                } else {
                                    value = value + " " + v2;
                                }
                                value = value.trim();
                               
                                buffer.position(posLF);
                                continue outer;
                            }
                        }
                    }
                   
                } else {
                    if (name != null) {
                        requestHeader.addHeader(name, value);
                    }
                }
               
               
                // looking for colon -> Content-Length:
                int posColon;
                while (true) {
                    if (buffer.get() == COLON) {
                        posColon = buffer.position();
                        name = extractString(posLF, posColon - (posLF + 1), buffer);
                        buffer.position(posColon + 1);
                        break;
                    }
                }
               
                // looking for LF  -> Content-Length: 143
                while (true) {
                    if (buffer.get() == LF) {
                        posLF = buffer.position();
                        value = extractStringWithoutTailingCR(posColon + 1, posLF - (posColon + 2), buffer);
                        value = value.trim();
                       
                        buffer.position(posLF);
                        break;
                    }
                }
            }

        } catch (BadMessageException bme) {
            buffer.position(position);
            buffer.limit(limit);
            throw bme;
           
        } catch (Exception e) {
            buffer.position(position);
            buffer.limit(limit);
           
            if (buffer.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.