Package org.apache.cassandra.net.http

Source Code of org.apache.cassandra.net.http.HttpConnection

/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements.  See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership.  The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License.  You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/*
* This class accepts a client connection and parses http data from it.
*/

// TODO: shouldClose_ is not used correctly. It should be used to close the socket? When?

package org.apache.cassandra.net.http;

import java.util.*;
import java.net.*;
import java.io.*;
import java.nio.*;
import java.nio.channels.SelectionKey;
import java.nio.channels.SocketChannel;
import org.apache.cassandra.service.*;
import org.apache.cassandra.concurrent.SingleThreadedStage;
import org.apache.cassandra.concurrent.StageManager;
import org.apache.cassandra.db.Table;
import org.apache.cassandra.net.IVerbHandler;
import org.apache.cassandra.net.Message;
import org.apache.cassandra.net.MessagingService;
import org.apache.cassandra.net.SelectionKeyHandler;
import org.apache.cassandra.net.SelectorManager;
import org.apache.cassandra.service.StorageService;
import org.apache.cassandra.utils.LogUtil;
import org.apache.log4j.Logger;

/**
*
* @author kranganathan
*/
public class HttpConnection extends SelectionKeyHandler implements HttpStartLineParser.Callback, HttpHeaderParser.Callback
{
    private static Logger logger_ = Logger.getLogger(StorageService.class);
    public static final String httpRequestVerbHandler_ = "HTTP-REQUEST-VERB-HANDLER";
    public static final String httpStage_ = "HTTP-STAGE";

    /*
     * These are the callbacks into who ever intends
     * to listen on the client socket.
     */
    public interface HttpConnectionListener
    {
        public void onRequest(HttpRequest httpRequest);
        public void onResponse(HttpResponse httpResponse);
    }

    enum HttpMessageType
    {
        UNKNOWN,
        REQUEST,
        RESPONSE
    }

    enum ParseState
    {
        IN_NEW,
        IN_START,
        IN_HEADERS, IN_BODY
    }

    private ParseState parseState_ = ParseState.IN_NEW;
    private long parseStartTime_ = 0;
    private HttpMessageType currentMsgType_ = HttpMessageType.UNKNOWN;
    private int contentLength_ = 0;
    private List<ByteBuffer> bodyBuffers_ = new LinkedList<ByteBuffer>();
    private boolean shouldClose_ = false;
    private String defaultContentType_ = "text/html";
    private HttpRequest currentRequest_ = null;
    private HttpResponse currentResponse_ = null;
    private HttpStartLineParser startLineParser_ = new HttpStartLineParser(this);
    private HttpHeaderParser headerParser_ = new HttpHeaderParser(this);
    /* Selection Key associated with this HTTP Connection */
    private SelectionKey httpKey_;
    /* SocketChannel associated with this HTTP Connection */
    private SocketChannel httpChannel_;
    /* HTTPReader instance associated with this HTTP Connection */
    private HTTPReader httpReader_ = new HTTPReader();

    /*
     * This abstraction starts reading the data that comes in
     * on a HTTP request. It accumulates the bytes read into
     * a buffer and passes the buffer to the HTTP parser.
    */

    class HTTPReader implements Runnable
    {
        /* We read 256 bytes at a time from a HTTP connection */
        private static final int bufferSize_ = 256;

        /*
         * Read buffers from the input stream into the byte buffer.
         */
        public void run()
        {
            ByteBuffer readBuffer = ByteBuffer.allocate(HTTPReader.bufferSize_);
            try
            {
                int bytesRead = httpChannel_.read(readBuffer);
                readBuffer.flip();
                if ( readBuffer.remaining() > 0 )
                    HttpConnection.this.parse(readBuffer);
            }
            catch ( IOException ex )
            {
                logger_.warn(LogUtil.throwableToString(ex));
            }
        }
    }

    public static class HttpRequestMessage
    {
        private HttpRequest httpRequest_;
        private HttpConnection httpConnection_;

        HttpRequestMessage(HttpRequest httpRequest, HttpConnection httpConnection)
        {
            httpRequest_ = httpRequest;
            httpConnection_ = httpConnection;
        }

        public HttpRequest getHttpRequest()
        {
            return httpRequest_;
        }

        public HttpConnection getHttpConnection()
        {
            return httpConnection_;
        }
    }

    /*
     *  Read called on the Selector thread. This is called
     *  when there is some HTTP request that needs to be
     *  processed.
    */
    public void read(SelectionKey key)
    {
        if ( httpKey_ == null )
        {
            httpKey_ = key;
            httpChannel_ = (SocketChannel)key.channel();
        }
        /* deregister interest for read */
        key.interestOps( key.interestOps() & ( ~SelectionKey.OP_READ ) );
        /* Add a task to process the HTTP request */
        MessagingService.getReadExecutor().execute(httpReader_);
    }

    public void modifyKeyForRead(SelectionKey key)
    {
        key.interestOps( httpKey_.interestOps() | SelectionKey.OP_READ );
    }

    private void resetParserState()
    {
        startLineParser_.resetParserState();
        headerParser_.resetParserState();
        parseState_ = ParseState.IN_NEW;
        contentLength_ = 0;
        bodyBuffers_ = new LinkedList<ByteBuffer>();
        currentMsgType_ = HttpMessageType.UNKNOWN;
        currentRequest_ = null;
        currentResponse_ = null;
    }

    public void close()
    {       
        logger_.info("Closing HTTP socket ...");
        if ( httpKey_ != null )
            SelectorManager.getSelectorManager().cancel(httpKey_);
    }

    /*
     * Process the HTTP commands sent from the client. Reads
     * the socket and parses the HTTP request.
    */
    public void parse(ByteBuffer bb)
    {
        try
        {
            logger_.debug("Processing http requests from socket ...");
            switch (parseState_)
            {
                case IN_NEW:
                    parseState_ = ParseState.IN_START;
                    parseStartTime_ = System.currentTimeMillis();

                // fall through
                case IN_START:
                    if (startLineParser_.onMoreBytesNew(bb) == false)
                    {
                        break;
                    }
                    else
                    {
                        /* Already done through the callback */
                        parseState_ = ParseState.IN_HEADERS;
                    }

                // fall through
                case IN_HEADERS:
                    if (headerParser_.onMoreBytesNew(bb) == false)
                    {

                        break; // need more bytes
                    }
                    else
                    {
                        String len;
                        if (currentMsgType_ == HttpMessageType.REQUEST)
                        {
                            len = currentRequest_.getHeader(HttpProtocolConstants.CONTENT_LENGTH);

                            // find if we should close method
                            if (currentRequest_.getVersion().equalsIgnoreCase("HTTP/1.1"))
                            {
                                /*
                                 * Scan all of the headers for close messages
                                 */
                                String val = currentRequest_.getHeader(HttpProtocolConstants.CONNECTION);

                                if (val != null && val.equalsIgnoreCase(HttpProtocolConstants.CLOSE))
                                {
                                    shouldClose_ = true;
                                }
                            } else if (currentRequest_.getVersion().equalsIgnoreCase("HTTP/1.0"))
                            {
                                /* By default no keep-alive */
                                shouldClose_ = true;

                                /*
                                 * Scan all of the headers for keep-alive
                                 * messages
                                 */
                                String val = currentRequest_.getHeader(HttpProtocolConstants.CONNECTION);

                                if (val != null && val.equalsIgnoreCase(HttpProtocolConstants.KEEP_ALIVE))
                                {
                                    shouldClose_ = false;
                                }
                            } else
                            {
                                /* Assume 0.9 */
                                shouldClose_ = true;
                            }
                        }
                        else if (currentMsgType_ == HttpMessageType.RESPONSE)
                        {
                            len = currentResponse_.getHeader(HttpProtocolConstants.CONTENT_LENGTH);

                        // TODO: pay attention to keep-alive and
                        // close headers
                        }
                        else
                        {
                            logger_.warn("in HttpConnection::processInput_() Message type is not set");
                            return;
                        }

                        if (len != null)
                        {
                            try
                            {
                                if(len == null || len.equals(""))
                                    contentLength_ = 0;
                                else
                                    contentLength_ = Integer.parseInt(len);
                            }
                            catch (NumberFormatException ex)
                            {
                                throw new HttpParsingException();
                            }
                        }
                        parseState_ = ParseState.IN_BODY;
                    }

                // fall through
                case IN_BODY:
                    boolean done = false;

                    if (contentLength_ > 0)
                    {
                        if (bb.remaining() > contentLength_)
                        {
                            int newLimit = bb.position() + contentLength_;
                            bodyBuffers_.add(((ByteBuffer) bb.duplicate().limit(newLimit)).slice());
                            bb.position(newLimit);
                            contentLength_ = 0;
                        }
                        else
                        {
                            contentLength_ -= bb.remaining();
                            bodyBuffers_.add(bb.duplicate());
                            bb.position(bb.limit());
                        }
                    }

                if (contentLength_ == 0)
                {
                    done = true;
                }

                if (done)
                {
                    if (currentMsgType_ == HttpMessageType.REQUEST)
                    {
                        //currentRequest_.setParseTime(env_.getCurrentTime() - parseStartTime_);
                        currentRequest_.setBody(bodyBuffers_);

                        if (currentRequest_.getHeader("Content-Type") == null)
                        {
                            currentRequest_.addHeader("Content-Type", defaultContentType_);
                        }

                        handleRequest(currentRequest_);
                    }
                    else if (currentMsgType_ == HttpMessageType.RESPONSE)
                    {
                        logger_.info("Holy shit! We are not supposed to be here - ever !!!");
                    }
                    else
                    {
                        logger_.error("Http message type is still" +
                                " unset after we finish parsing the body?");
                    }

                    resetParserState();
                }
            }

        }
        catch (final Throwable e)
        {
            logger_.warn(LogUtil.throwableToString(e));
            //close();
        }
        finally
        {
            SelectorManager.getSelectorManager().modifyKeyForRead(httpKey_);
        }
    }

    public void write(ByteBuffer buffer)
    {
        /*
         * TODO: Make this a non blocking write.
        */
        try
        {
            while ( buffer.remaining() > 0 )
            {
                httpChannel_.write(buffer);
            }
            close();
        }
        catch ( IOException ex )
        {
            logger_.warn(LogUtil.throwableToString(ex));
        }
    }

    private void handleRequest(HttpRequest request)
    {
        HttpConnection.HttpRequestMessage httpRequestMessage = new HttpConnection.HttpRequestMessage(request, this);
        Message httpMessage = new Message(null, HttpConnection.httpStage_, HttpConnection.httpRequestVerbHandler_, new Object[]{httpRequestMessage});
        MessagingService.receive(httpMessage);
    }

    // HttpStartLineParser.Callback interface implementation
    public void onStartLine(String method, String path, String query, String version)
    {
        logger_.debug("Startline method=" + method + " path=" + path + " query=" + query + " version=" + version);

        if (method.startsWith("HTTP"))
        {
                // response
                currentMsgType_ = HttpMessageType.RESPONSE;
                currentResponse_ = new HttpResponse();
                currentResponse_.setStartLine(method, path, version);
        }
        else
        {
                // request
                currentMsgType_ = HttpMessageType.REQUEST;
                currentRequest_ = new HttpRequest();
                currentRequest_.setStartLine(method, path, query, version);
        }
    }

    // HttpHeaderParser.Callback interface implementation
    public void onHeader(String name, String value)
    {
        if (currentMsgType_ == HttpMessageType.REQUEST)
        {
                currentRequest_.addHeader(name, value);
        }
        else if (currentMsgType_ == HttpMessageType.RESPONSE)
        {
                currentResponse_.addHeader(name, value);
        }
        else
        {
            logger_.warn("Unknown message type -- HttpConnection::onHeader()");
        }

        logger_.debug(name + " : " + value);
    }
}


TOP

Related Classes of org.apache.cassandra.net.http.HttpConnection

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.