Package org.xlightweb

Source Code of org.xlightweb.HttpProtocolHandlerClientSide

/*
*  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.COLON;
import static org.xlightweb.HttpUtils.CR;
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.SLASH;
import static org.xlightweb.HttpUtils.SPACE;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.logging.Level;
import java.util.logging.Logger;


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



/**
* client side http protocol handler
*
* @author grro@xlightweb.org
*/
final class HttpProtocolHandlerClientSide extends AbstractHttpProtocolHandler {
   
    private static final Logger LOG = Logger.getLogger(HttpProtocolHandlerClientSide.class.getName());
   
 
  ByteBuffer[] parseHeader(AbstractHttpConnection httpConnection, ByteBuffer[] rawData) throws BadMessageException, IOException {
    HttpResponseHeader responseHeader = null;
   
    responseHeader = parse(httpConnection.getUnderlyingTcpConnection(), rawData);

    if (responseHeader != null) {
       
        if (LOG.isLoggable(Level.FINE)) {
            LOG.fine("[" + httpConnection.getId() + "] request header parsed (rawData: " + HttpUtils.computeRemaining(rawData) + ")");
        }
       
            setState(RECEIVING_BODY);

      httpConnection.setLastTimeHeaderReceivedMillis(System.currentTimeMillis());
      httpConnection.incCountMessageReceived();
       
      IMessageHeaderHandler messageHeaderHandler = httpConnection.getMessageHeaderHandler();
      if (messageHeaderHandler == null) {
        throw new IOException("no message handler set");
      }
     
      try {     
                int bodyType = getBodyType(httpConnection, responseHeader, (IHttpRequestHeader) messageHeaderHandler.getAssociatedHeader());
        switch (bodyType) {
         
          case BODY_TYPE_EMTPY:
            HttpResponse response = new HttpResponse(responseHeader);
           
            IMessageHandler messageHandler = messageHeaderHandler.onMessageHeaderReceived(response);
            setMessageHandler(messageHandler);
            reset();
           
            // next response? (-> pipelining)
            if (rawData != null) {
              return onData(httpConnection, rawData);
            }   
            break;

           
          case FULL_MESSAGE:
              AbstractNetworkBodyDataSource dataSource = new FullMessageBodyDataSource(httpConnection, responseHeader.getCharacterEncoding(), responseHeader.getContentLength());
              setBodyDataSource(dataSource);
           
            response = new HttpResponse(responseHeader, dataSource);

                      messageHandler = messageHeaderHandler.onMessageHeaderReceived(response);
                      setMessageHandler(messageHandler);
           
            response.getNonBlockingBody().setBodyDataReceiveTimeoutMillis(httpConnection.getBodyDataReceiveTimeoutMillis());
            rawData = parserBody(httpConnection, rawData);
            break;
           
          case BODY_TYPE_SIMPLE:
              dataSource = new SimpleMessageBodyDataSource(httpConnection, responseHeader.getCharacterEncoding());
                        setBodyDataSource(dataSource);
           
            response = new HttpResponse(responseHeader, dataSource);
           
            messageHandler = messageHeaderHandler.onMessageHeaderReceived(response);
                  setMessageHandler(messageHandler);
           
            response.getNonBlockingBody().setBodyDataReceiveTimeoutMillis(httpConnection.getBodyDataReceiveTimeoutMillis());
            rawData = parserBody(httpConnection, rawData);
            break;
           
          case BODY_TYPE_MULTIPART_BYTERANGE:             
              dataSource = new MultipartByteRangeMessageBodyDataSource(httpConnection, responseHeader);
              setBodyDataSource(dataSource);
           
            response = new HttpResponse(responseHeader, dataSource);

            messageHandler = messageHeaderHandler.onMessageHeaderReceived(response);
            setMessageHandler(messageHandler);
           
            response.getNonBlockingBody().setBodyDataReceiveTimeoutMillis(httpConnection.getBodyDataReceiveTimeoutMillis());
            rawData = parserBody(httpConnection, rawData);
            break;
           
           
          default// BODY_TYPE_CHUNKED
            dataSource = new FullMessageChunkedBodyDataSource(httpConnection, responseHeader);
              setBodyDataSource(dataSource);

            response = new HttpResponse(responseHeader, dataSource);
           
            messageHandler = messageHeaderHandler.onMessageHeaderReceived(response);
            setMessageHandler(messageHandler);
           
            response.getNonBlockingBody().setBodyDataReceiveTimeoutMillis(httpConnection.getBodyDataReceiveTimeoutMillis());
            rawData = parserBody(httpConnection, rawData);
           
            break;
        }       

     
      } catch (BadMessageException bme) {
        throw bme;       
      }
    }
   
    return rawData;
  }
 
 



  /**
   * {@inheritDoc}
     */
  @Override
  void onDisconnectInHeader(AbstractHttpConnection httpConnection, ByteBuffer[] rawData) throws IOException {
        ByteBuffer buffer = HttpUtils.merge(HttpUtils.copy(rawData));

        int maxReadSize = 8;
        if (buffer.remaining() < maxReadSize) {
            maxReadSize = buffer.remaining();
        }
       
        // looking for slash of protocol -> HTTP/
        for (int i = 0; i < maxReadSize; i++) {
            if (buffer.get() == SLASH) {
                int posSlash = buffer.position();
                   
                String protocolScheme = extractString(posSlash - 5, 4, buffer);
                if (protocolScheme.equalsIgnoreCase("HTTP")) {
                    throw new ProtocolException("connection " + httpConnection.getId() + " has been disconnected while receiving header. Already received: " + DataConverter.toString(HttpUtils.copy(rawData), AbstractHeader.DEFAULT_ENCODING));
                }
            }
        }
       
       
        // HTTP 0.9 response
        httpConnection.setLastTimeHeaderReceivedMillis(System.currentTimeMillis());
        httpConnection.incCountMessageReceived();
       
        HttpResponseHeader responseHeader = new HttpResponseHeader(200);
        responseHeader.setProtocolVersionSilence("0.9");
        responseHeader.setConnection("close");
       
        AbstractNetworkBodyDataSource dataSource = new SimpleMessageBodyDataSource(httpConnection, responseHeader.getCharacterEncoding());
        setBodyDataSource(dataSource);

        setState(RECEIVING_BODY);
       
        HttpResponse response = new HttpResponse(responseHeader, dataSource, true);

       
        IMessageHeaderHandler messageHeaderHandler = httpConnection.getMessageHeaderHandler(); // get it before disconnecting dataSource!
        if (messageHeaderHandler == null) {
            throw new IOException("no message handler set");
        }
       
        dataSource.parse(rawData);
        dataSource.onDisconnect();  
       
        IMessageHandler messageHandler = messageHeaderHandler.onMessageHeaderReceived(response);
        setMessageHandler(messageHandler)
  }

 
   
    @Override
    void onDisconnectInHeaderNothingReceived(AbstractHttpConnection httpConnection, ByteBuffer[] rawData) {
        if (httpConnection.getNumOpenTransactions() == 0) {
            // do nothing
        } else {
            super.onDisconnectInHeaderNothingReceived(httpConnection, rawData);
        }
    }
 
 
  private static int getBodyType(AbstractHttpConnection httpConnection, HttpResponseHeader responseHeader, IHttpRequestHeader requestHeader) {

      int status = responseHeader.getStatus();

    if ((status == 304) ||      // not modified
      (status == 204) ||      // no content 
      (status == 205) ||      // reset content
      (status == 100) ||      // continue
      (status == 101) ||      // Switching Protocols
      (requestHeader.getMethod().equals(IHttpMessage.HEAD_METHOD))) {
      return BODY_TYPE_EMTPY;
    }

    if (((status / 100) == 2) && requestHeader.getMethod().equals(IHttpMessage.CONNECT_METHOD)) {
        return BODY_TYPE_EMTPY;
      }
           
   
   
    // contains a non-zero Content-Length header  -> bound body
    if ((responseHeader.getContentLength() != -1)) {
      if (responseHeader.getContentLength() > 0) {
        return FULL_MESSAGE;
      }
      return BODY_TYPE_EMTPY;
    }
 
   
    //  transfer encoding header is set with chunked -> chunked body
    String transferEncoding = responseHeader.getTransferEncoding();
    if ((transferEncoding != null) && (transferEncoding.equalsIgnoreCase("chunked"))) {
      return BODY_TYPE_CHUNKED;
    }
   
 
     
        // is connection header set with close?
        if ((responseHeader.getConnection() != null) && (responseHeader.getConnection().equalsIgnoreCase("close"))) {
            httpConnection.setPersistent(false);
            return BODY_TYPE_SIMPLE;
        }
       
       
    // multipart/byteranges response?
    if ((responseHeader.getStatus() == 206) && (responseHeader.getContentType() != null) && (responseHeader.getContentType().toLowerCase().startsWith("multipart/byteranges"))) {
      return BODY_TYPE_MULTIPART_BYTERANGE;
    }
   
 
    // assume 0.9 response
    httpConnection.setPersistent(false);
    return BODY_TYPE_SIMPLE;
  }
 
 
  private static HttpResponseHeader parse(INonBlockingConnection connection, ByteBuffer[] rawData) throws 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...

        HttpResponseHeader responseHeader = new HttpResponseHeader();

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

        boolean isMerged = false;
        ByteBuffer buffer;
        if (rawData.length == 1) {
            buffer = rawData[0];
        } else {
            if (LOG.isLoggable(Level.FINE)) {
                LOG.fine("merging buffers");
            }
            buffer = HttpUtils.merge(rawData);
            isMerged = true;
        }
       
       
        int position = buffer.position();
        int limit = buffer.limit();
       
       
        try {
   
            // looking for slash of protocol -> HTTP/
            while (true) {
                if (buffer.get() == SLASH) {
                    int posSlash = buffer.position();
                   
                    if (LOG.isLoggable(Level.FINE)) {
                        if (posSlash != 5) {
                            LOG.fine("leading data " + DataConverter.toString(HttpUtils.copy(buffer)));
                        }
                    }
                   
                    String protocolScheme = extractString(posSlash - 5, 4, buffer);
                    responseHeader.setProtocolSchemeSilence(protocolScheme);
                   
                    String protocolVersion = extractString(posSlash, 3, buffer);
                    responseHeader.setProtocolVersionSilence(protocolVersion);
                   
                    buffer.position(posSlash + 4);
                    break;
                }
            }
   
           
            // looking for first char of status  -> HTTP/1.1 2
            int posStatusCode;
            while (true) {
                if (buffer.get() > 47) {
                    posStatusCode = buffer.position();
                   
                    int statusCode = extractInt(posStatusCode - 1, 3, buffer);
                    responseHeader.setStatus(statusCode);
                   
                    buffer.position(posStatusCode + 3);
   
                    break;
                }
            }

           
            // looking for LF -> HTTP/1.1 200 OK
            int posLF;
            while (true) {
                if (buffer.get() == LF) {
                    posLF = buffer.position();
                   
                    String reason = extractStringWithoutTailingCR(posStatusCode + 3, posLF - (posStatusCode + 4), buffer);
                    responseHeader.setReason(reason);
   
                    buffer.position(posLF + 1);
                    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) {
                                responseHeader.addHeader(name, value);
                            }
                           
                            if (isMerged) {
                                updateMergedBuffers(buffer, position, rawData);
                            }

                            return responseHeader;
                        }
                       
                    } else if (firstCharOfLine == LF) {
                        if (name != null) {
                            responseHeader.addHeader(name, value);
                        }
                       
                        if (isMerged) {
                            updateMergedBuffers(buffer, position, rawData);
                        }

                        return responseHeader;
                       
                    // 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) {
                        responseHeader.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 (Exception e) {
            if (LOG.isLoggable(Level.FINE)) {
                LOG.fine("header parsing exception: " + e.toString());
            }
           
            buffer.position(position);
            buffer.limit(limit);
           
            if (buffer.remaining() > MAX_HEADER_SIZE) {
                throw new BadMessageException("max header size reached");
            } else {
                return null;
            }
        }
  }
}

TOP

Related Classes of org.xlightweb.HttpProtocolHandlerClientSide

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.