`
Tyrion
  • 浏览: 261057 次
  • 性别: Icon_minigender_1
  • 来自: 南京
社区版块
存档分类
最新评论

Tomcat7中一次请求处理的前世今生(二)Socket连接转换成内部请求对象

阅读更多

先抛开之前所看到的Tomcat源码不谈,Tomcat作为一个用Java实现的Web服务器,如果让你来实现,那么从何入手?

这里首先需要厘清的是Web服务器的概念,谷歌了一下,发现这条解释还算靠谱点,【在网络环境下可以向发出请求的浏览器提供文档的程序】。这里面重点有两条,1.网络环境下,2.能够给出响应。用Java写过网络通信程序的都知道,这里必然会用到Socket编程。我们自己要实现的服务器程序作为Socket编程里的服务端,浏览器作为Socket编程里的客户端。

要理解Tomcat原理,Socket编程这块的基本原理必须得了解,google一把一大堆,这里不再单独做介绍。下面给出一个服务器端最简单的响应客户端请求的伪代码示例:

			ServerSocket serverSocket  = new ServerSocket(8080, 1,
					InetAddress.getByName(“localhost”));
			Socket socket = null;
			InputStream is = null;
			OutputStream os = null;
			try {
				socket = serverSocket.accept();//1.监听到客户端的连接
				is = socket.getInputStream();
				os = socket.getOutputStream();
				Request request = Util.getRequest(is);//2.从输入流中读取数据,并根据Http协议转换成请求
				Response response = Util.service(request);//服务器内部根据请求信息给出响应信息
				os.writeResponse(response);//3.将响应信息写到输出流
			} catch (Exception e) {
				e.printStackTrace();
			} finally {//4.关闭输入输出流及连接
				if (is != null) {
					is.close();
				}
				if (os != null) {
					os.close();
				}
				socket.close();
			}

浏览器和Web服务器的一次交互过程分四步:连接、请求、响应、关闭。前一篇文章分析到的接收器线程,如前文开始截图里的http-bio-8080-Acceptor-0,这个线程的实现类org.apache.tomcat.util.net.JIoEndpoint.Acceptor,源码如下:

    // --------------------------------------------------- Acceptor Inner Class
    /**
     * The background thread that listens for incoming TCP/IP connections and
     * hands them off to an appropriate processor.
     */
    protected class Acceptor extends AbstractEndpoint.Acceptor {

        @Override
        public void run() {

            int errorDelay = 0;

            // Loop until we receive a shutdown command
            while (running) {

                // Loop if endpoint is paused
                while (paused && running) {
                    state = AcceptorState.PAUSED;
                    try {
                        Thread.sleep(50);
                    } catch (InterruptedException e) {
                        // Ignore
                    }
                }

                if (!running) {
                    break;
                }
                state = AcceptorState.RUNNING;

                try {
                    //if we have reached max connections, wait
                    countUpOrAwaitConnection();

                    Socket socket = null;
                    try {
                        // Accept the next incoming connection from the server
                        // socket
                        socket = serverSocketFactory.acceptSocket(serverSocket);
                    } catch (IOException ioe) {
                        countDownConnection();
                        // Introduce delay if necessary
                        errorDelay = handleExceptionWithDelay(errorDelay);
                        // re-throw
                        throw ioe;
                    }
                    // Successful accept, reset the error delay
                    errorDelay = 0;

                    // Configure the socket
                    if (running && !paused && setSocketOptions(socket)) {
                        // Hand this socket off to an appropriate processor
                        if (!processSocket(socket)) {
                            countDownConnection();
                            // Close socket right away
                            closeSocket(socket);
                        }
                    } else {
                        countDownConnection();
                        // Close socket right away
                        closeSocket(socket);
                    }
                } catch (IOException x) {
                    if (running) {
                        log.error(sm.getString("endpoint.accept.fail"), x);
                    }
                } catch (NullPointerException npe) {
                    if (running) {
                        log.error(sm.getString("endpoint.accept.fail"), npe);
                    }
                } catch (Throwable t) {
                    ExceptionUtils.handleThrowable(t);
                    log.error(sm.getString("endpoint.accept.fail"), t);
                }
            }
            state = AcceptorState.ENDED;
        }
    }

第39行做的事就是上面伪代码示例里的监听客户端连接,监听到连接后(即浏览器向服务器发起一次请求)在第53行由processSocket方法来处理这次接收到的Socket连接。processSocket方法源码如下:

    protected boolean processSocket(Socket socket) {
        // Process the request from this socket
        try {
            SocketWrapper<Socket> wrapper = new SocketWrapper<Socket>(socket);
            wrapper.setKeepAliveLeft(getMaxKeepAliveRequests());
            // During shutdown, executor may be null - avoid NPE
            if (!running) {
                return false;
            }
            getExecutor().execute(new SocketProcessor(wrapper));
        } catch (RejectedExecutionException x) {
            log.warn("Socket processing request was rejected for:"+socket,x);
            return false;
        } catch (Throwable t) {
            ExceptionUtils.handleThrowable(t);
            // This means we got an OOM or similar creating a thread, or that
            // the pool and its queue are full
            log.error(sm.getString("endpoint.process.fail"), t);
            return false;
        }
        return true;
    }

该方法中先把Socket包装成SocketWrapper,用以内部处理。重点是第10行:getExecutor().execute(new SocketProcessor(wrapper))。如果按照上面伪代码中的处理方式,在有并发请求的情况下,一个请求没有处理完成,服务器将无法立即响应另一个请求。这里做了一下改进,即在接收到一次Socket连接后另启一个线程处理该连接,使当前线程不阻塞。

 

下面跟着SocketProcessor这个线程来看看,一次Socket连接是如何在Tomcat7中被转成内部的Request的。看下该线程的run方法:

        @Override
        public void run() {
            boolean launch = false;
            synchronized (socket) {
                try {
                    SocketState state = SocketState.OPEN;

                    try {
                        // SSL handshake
                        serverSocketFactory.handshake(socket.getSocket());
                    } catch (Throwable t) {
                        ExceptionUtils.handleThrowable(t);
                        if (log.isDebugEnabled()) {
                            log.debug(sm.getString("endpoint.err.handshake"), t);
                        }
                        // Tell to close the socket
                        state = SocketState.CLOSED;
                    }

                    if ((state != SocketState.CLOSED)) {
                        if (status == null) {
                            state = handler.process(socket, SocketStatus.OPEN);
                        } else {
                            state = handler.process(socket,status);
                        }
                    }
                    if (state == SocketState.CLOSED) {
                        // Close socket
                        if (log.isTraceEnabled()) {
                            log.trace("Closing socket:"+socket);
                        }
                        countDownConnection();
                        try {
                            socket.getSocket().close();
                        } catch (IOException e) {
                            // Ignore
                        }
                    } else if (state == SocketState.OPEN ||
                            state == SocketState.UPGRADING  ||
                            state == SocketState.UPGRADED){
                        socket.setKeptAlive(true);
                        socket.access();
                        launch = true;
                    } else if (state == SocketState.LONG) {
                        socket.access();
                        waitingRequests.add(socket);
                    }
                } finally {
                    if (launch) {
                        try {
                            getExecutor().execute(new SocketProcessor(socket, SocketStatus.OPEN));
                        } catch (RejectedExecutionException x) {
                            log.warn("Socket reprocessing request was rejected for:"+socket,x);
                            try {
                                //unable to handle connection at this time
                                handler.process(socket, SocketStatus.DISCONNECT);
                            } finally {
                                countDownConnection();
                            }


                        } catch (NullPointerException npe) {
                            if (running) {
                                log.error(sm.getString("endpoint.launch.fail"),
                                        npe);
                            }
                        }
                    }
                }
            }
            socket = null;
            // Finish up this request
        }

    }

默认情况下会走到第22行,调用handler对象的process方法,这里handler对象实际上是Http11ConnectionHandler类的实例,该对象的初始化过程是在org.apache.coyote.http11.Http11Protocol对象的构造方法中:

    public Http11Protocol() {
        endpoint = new JIoEndpoint();
        cHandler = new Http11ConnectionHandler(this);
        ((JIoEndpoint) endpoint).setHandler(cHandler);
        setSoLinger(Constants.DEFAULT_CONNECTION_LINGER);
        setSoTimeout(Constants.DEFAULT_CONNECTION_TIMEOUT);
        setTcpNoDelay(Constants.DEFAULT_TCP_NO_DELAY);
    }

所以需要到org.apache.coyote.http11.Http11Protocol类的静态内部类Http11ConnectionHandler中找到process方法的定义,但当前定义里面没有,这个方法是在其父类org.apache.coyote.AbstractProtocol.AbstractConnectionHandler中定义的:

        public SocketState process(SocketWrapper<S> socket,
                SocketStatus status) {
            Processor<S> processor = connections.remove(socket.getSocket());

            if (status == SocketStatus.DISCONNECT && processor == null) {
                //nothing more to be done endpoint requested a close
                //and there are no object associated with this connection
                return SocketState.CLOSED;
            }

            socket.setAsync(false);

            try {
                if (processor == null) {
                    processor = recycledProcessors.poll();
                }
                if (processor == null) {
                    processor = createProcessor();
                }

                initSsl(socket, processor);

                SocketState state = SocketState.CLOSED;
                do {
                    if (status == SocketStatus.DISCONNECT &&
                            !processor.isComet()) {
                        // Do nothing here, just wait for it to get recycled
                        // Don't do this for Comet we need to generate an end
                        // event (see BZ 54022)
                    } else if (processor.isAsync() ||
                            state == SocketState.ASYNC_END) {
                        state = processor.asyncDispatch(status);
                    } else if (processor.isComet()) {
                        state = processor.event(status);
                    } else if (processor.isUpgrade()) {
                        state = processor.upgradeDispatch();
                    } else {
                        state = processor.process(socket);
                    }
    
                    if (state != SocketState.CLOSED && processor.isAsync()) {
                        state = processor.asyncPostProcess();
                    }

                    if (state == SocketState.UPGRADING) {
                        // Get the UpgradeInbound handler
                        UpgradeInbound inbound = processor.getUpgradeInbound();
                        // Release the Http11 processor to be re-used
                        release(socket, processor, false, false);
                        // Create the light-weight upgrade processor
                        processor = createUpgradeProcessor(socket, inbound);
                        inbound.onUpgradeComplete();
                    }
                } while (state == SocketState.ASYNC_END ||
                        state == SocketState.UPGRADING);

                if (state == SocketState.LONG) {
                    // In the middle of processing a request/response. Keep the
                    // socket associated with the processor. Exact requirements
                    // depend on type of long poll
                    longPoll(socket, processor);
                } else if (state == SocketState.OPEN) {
                    // In keep-alive but between requests. OK to recycle
                    // processor. Continue to poll for the next request.
                    release(socket, processor, false, true);
                } else if (state == SocketState.SENDFILE) {
                    // Sendfile in progress. If it fails, the socket will be
                    // closed. If it works, the socket will be re-added to the
                    // poller
                    release(socket, processor, false, false);
                } else if (state == SocketState.UPGRADED) {
                    // Need to keep the connection associated with the processor
                    longPoll(socket, processor);
                } else {
                    // Connection closed. OK to recycle the processor.
                    if (!(processor instanceof UpgradeProcessor)) {
                        release(socket, processor, true, false);
                    }
                }
                return state;
            } catch(java.net.SocketException e) {
                // SocketExceptions are normal
                getLog().debug(sm.getString(
                        "abstractConnectionHandler.socketexception.debug"), e);
            } catch (java.io.IOException e) {
                // IOExceptions are normal
                getLog().debug(sm.getString(
                        "abstractConnectionHandler.ioexception.debug"), e);
            }
            // Future developers: if you discover any other
            // rare-but-nonfatal exceptions, catch them here, and log as
            // above.
            catch (Throwable e) {
                ExceptionUtils.handleThrowable(e);
                // any other exception or error is odd. Here we log it
                // with "ERROR" level, so it will show up even on
                // less-than-verbose logs.
                getLog().error(
                        sm.getString("abstractConnectionHandler.error"), e);
            }
            // Don't try to add upgrade processors back into the pool
            if (!(processor instanceof UpgradeProcessor)) {
                release(socket, processor, true, false);
            }
            return SocketState.CLOSED;
        }

重点在第38行,调用processor的process方法处理socket。而processor对象在第18行通过createProcessor方法创建出来的,createProcessor方法在当前类里面是抽象方法,默认情况下调用的具体实现类在上面提到的Http11ConnectionHandler类中:

        @Override
        protected Http11Processor createProcessor() {
            Http11Processor processor = new Http11Processor(
                    proto.getMaxHttpHeaderSize(), (JIoEndpoint)proto.endpoint,
                    proto.getMaxTrailerSize());
            processor.setAdapter(proto.adapter);
            processor.setMaxKeepAliveRequests(proto.getMaxKeepAliveRequests());
            processor.setKeepAliveTimeout(proto.getKeepAliveTimeout());
            processor.setConnectionUploadTimeout(
                    proto.getConnectionUploadTimeout());
            processor.setDisableUploadTimeout(proto.getDisableUploadTimeout());
            processor.setCompressionMinSize(proto.getCompressionMinSize());
            processor.setCompression(proto.getCompression());
            processor.setNoCompressionUserAgents(proto.getNoCompressionUserAgents());
            processor.setCompressableMimeTypes(proto.getCompressableMimeTypes());
            processor.setRestrictedUserAgents(proto.getRestrictedUserAgents());
            processor.setSocketBuffer(proto.getSocketBuffer());
            processor.setMaxSavePostSize(proto.getMaxSavePostSize());
            processor.setServer(proto.getServer());
            processor.setDisableKeepAlivePercentage(
                    proto.getDisableKeepAlivePercentage());
            register(processor);
            return processor;
        }

此时的processor对象是Http11Processor类的实例,再看上一段提到的processor.process方法,最终会执行到Http11Processor类(因为该类中没有定义process方法)的父类org.apache.coyote.http11.AbstractHttp11Processor中的process方法。

 

为了方便理解,下面的时序图列出从Acceptor线程的run方法到AbstractHttp11Processor类的process方法的关键方法调用过程:

 

接下来分析org.apache.coyote.http11.AbstractHttp11Processor类的process方法:

    @Override
    public SocketState process(SocketWrapper<S> socketWrapper)
        throws IOException {
        RequestInfo rp = request.getRequestProcessor();
        rp.setStage(org.apache.coyote.Constants.STAGE_PARSE);

        // Setting up the I/O
        setSocketWrapper(socketWrapper);
        getInputBuffer().init(socketWrapper, endpoint);
        getOutputBuffer().init(socketWrapper, endpoint);

        // Flags
        error = false;
        keepAlive = true;
        comet = false;
        openSocket = false;
        sendfileInProgress = false;
        readComplete = true;
        if (endpoint.getUsePolling()) {
            keptAlive = false;
        } else {
            keptAlive = socketWrapper.isKeptAlive();
        }

        if (disableKeepAlive()) {
            socketWrapper.setKeepAliveLeft(0);
        }

        while (!error && keepAlive && !comet && !isAsync() &&
                upgradeInbound == null && !endpoint.isPaused()) {

            // Parsing the request header
            try {
                setRequestLineReadTimeout();

                if (!getInputBuffer().parseRequestLine(keptAlive)) {
                    if (handleIncompleteRequestLineRead()) {
                        break;
                    }
                }

                if (endpoint.isPaused()) {
                    // 503 - Service unavailable
                    response.setStatus(503);
                    error = true;
                } else {
                    // Make sure that connectors that are non-blocking during
                    // header processing (NIO) only set the start time the first
                    // time a request is processed.
                    if (request.getStartTime() < 0) {
                        request.setStartTime(System.currentTimeMillis());
                    }
                    keptAlive = true;
                    // Set this every time in case limit has been changed via JMX
                    request.getMimeHeaders().setLimit(endpoint.getMaxHeaderCount());
                    // Currently only NIO will ever return false here
                    if (!getInputBuffer().parseHeaders()) {
                        // We've read part of the request, don't recycle it
                        // instead associate it with the socket
                        openSocket = true;
                        readComplete = false;
                        break;
                    }
                    if (!disableUploadTimeout) {
                        setSocketTimeout(connectionUploadTimeout);
                    }
                }
            } catch (IOException e) {
                if (getLog().isDebugEnabled()) {
                    getLog().debug(
                            sm.getString("http11processor.header.parse"), e);
                }
                error = true;
                break;
            } catch (Throwable t) {
                ExceptionUtils.handleThrowable(t);
                UserDataHelper.Mode logMode = userDataHelper.getNextMode();
                if (logMode != null) {
                    String message = sm.getString(
                            "http11processor.header.parse");
                    switch (logMode) {
                        case INFO_THEN_DEBUG:
                            message += sm.getString(
                                    "http11processor.fallToDebug");
                            //$FALL-THROUGH$
                        case INFO:
                            getLog().info(message);
                            break;
                        case DEBUG:
                            getLog().debug(message);
                    }
                }
                // 400 - Bad Request
                response.setStatus(400);
                adapter.log(request, response, 0);
                error = true;
            }

            if (!error) {
                // Setting up filters, and parse some request headers
                rp.setStage(org.apache.coyote.Constants.STAGE_PREPARE);
                try {
                    prepareRequest();
                } catch (Throwable t) {
                    ExceptionUtils.handleThrowable(t);
                    if (getLog().isDebugEnabled()) {
                        getLog().debug(sm.getString(
                                "http11processor.request.prepare"), t);
                    }
                    // 400 - Internal Server Error
                    response.setStatus(400);
                    adapter.log(request, response, 0);
                    error = true;
                }
            }

            if (maxKeepAliveRequests == 1) {
                keepAlive = false;
            } else if (maxKeepAliveRequests > 0 &&
                    socketWrapper.decrementKeepAlive() <= 0) {
                keepAlive = false;
            }

            // Process the request in the adapter
            if (!error) {
                try {
                    rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE);
                    adapter.service(request, response);
                    // Handle when the response was committed before a serious
                    // error occurred.  Throwing a ServletException should both
                    // set the status to 500 and set the errorException.
                    // If we fail here, then the response is likely already
                    // committed, so we can't try and set headers.
                    if(keepAlive && !error) { // Avoid checking twice.
                        error = response.getErrorException() != null ||
                                (!isAsync() &&
                                statusDropsConnection(response.getStatus()));
                    }
                    setCometTimeouts(socketWrapper);
                } catch (InterruptedIOException e) {
                    error = true;
                } catch (HeadersTooLargeException e) {
                    error = true;
                    // The response should not have been committed but check it
                    // anyway to be safe
                    if (!response.isCommitted()) {
                        response.reset();
                        response.setStatus(500);
                        response.setHeader("Connection", "close");
                    }
                } catch (Throwable t) {
                    ExceptionUtils.handleThrowable(t);
                    getLog().error(sm.getString(
                            "http11processor.request.process"), t);
                    // 500 - Internal Server Error
                    response.setStatus(500);
                    adapter.log(request, response, 0);
                    error = true;
                }
            }

            // Finish the handling of the request
            rp.setStage(org.apache.coyote.Constants.STAGE_ENDINPUT);

            if (!isAsync() && !comet) {
                if (error) {
                    // If we know we are closing the connection, don't drain
                    // input. This way uploading a 100GB file doesn't tie up the
                    // thread if the servlet has rejected it.
                    getInputBuffer().setSwallowInput(false);
                }
                endRequest();
            }

            rp.setStage(org.apache.coyote.Constants.STAGE_ENDOUTPUT);

            // If there was an error, make sure the request is counted as
            // and error, and update the statistics counter
            if (error) {
                response.setStatus(500);
            }
            request.updateCounters();

            if (!isAsync() && !comet || error) {
                getInputBuffer().nextRequest();
                getOutputBuffer().nextRequest();
            }

            if (!disableUploadTimeout) {
                if(endpoint.getSoTimeout() > 0) {
                    setSocketTimeout(endpoint.getSoTimeout());
                } else {
                    setSocketTimeout(0);
                }
            }

            rp.setStage(org.apache.coyote.Constants.STAGE_KEEPALIVE);

            if (breakKeepAliveLoop(socketWrapper)) {
                break;
            }
        }

        rp.setStage(org.apache.coyote.Constants.STAGE_ENDED);

        if (error || endpoint.isPaused()) {
            return SocketState.CLOSED;
        } else if (isAsync() || comet) {
            return SocketState.LONG;
        } else if (isUpgrade()) {
            return SocketState.UPGRADING;
        } else {
            if (sendfileInProgress) {
                return SocketState.SENDFILE;
            } else {
                if (openSocket) {
                    if (readComplete) {
                        return SocketState.OPEN;
                    } else {
                        return SocketState.LONG;
                    }
                } else {
                    return SocketState.CLOSED;
                }
            }
        }
    }

从这个方法中可以清晰的看出解析请求的过程:第7到10行从Socket中获取输入输出流,第32到97行解析请求行和请求头,第99到115行校验和解析请求头中的属性,第125到160行调用适配器的service方法,第172行请求处理结束。

上面就是根据Http协议解析请求的总体流程。要理解上面提到的请求行、请求头等术语,需要熟悉Http协议,这里简单介绍下Http协议中的标准请求信息数据的格式:

请求信息包括以下三条

  • 请求行(request line)
    • 例如GET /images/logo.gif HTTP/1.1,表示从/images目录下请求logo.gif这个文件。
  • 请求头(request header),空行
    • 例如Accept-Language: en
  • 其他消息体

请求行和标题必须以<CR><LF>作为结尾。空行内必须只有<CR><LF>而无其他空格。在HTTP/1.1协议中,所有的请求头,除Host外,都是可选的。

 

请求行、请求头数据的格式具体看Http协议中的描述。所以在从输入流中读取到字节流数据之后必须按照请求行、请求头、消息体的顺序来解析。

 

这里以请求行数据的解析为例,在Http协议中该行内容格式为:

Request-Line   = Method SP Request-URI SP HTTP-Version CRLF

即请求类型、要访问的资源(URI)以及使用的HTTP版本,中间以特殊字符空格来分隔,以\r\n字符结尾。

 

在上面列出的AbstractHttp11Processor类的process代码中的第36行,会调用抽象方法getInputBuffer(),当前该抽象方法的具体实现在子类org.apache.coyote.http11.Http11Processor中,该方法返回的是该类的实例变量inputBuffer:

    protected AbstractInputBuffer<Socket> getInputBuffer() {
        return inputBuffer;
    }

该实例变量在Http11Processor的构造方法中会被初始化:

    public Http11Processor(int headerBufferSize, JIoEndpoint endpoint,
            int maxTrailerSize) {

        super(endpoint);
        
        inputBuffer = new InternalInputBuffer(request, headerBufferSize);
        request.setInputBuffer(inputBuffer);

        outputBuffer = new InternalOutputBuffer(response, headerBufferSize);
        response.setOutputBuffer(outputBuffer);

        initializeFilters(maxTrailerSize);
    }

所以AbstractHttp11Processor类的process方法的36行getInputBuffer().parseRequestLine()将会调用org.apache.coyote.http11.InternalInputBuffer类中的parseRequestLine方法:

    public boolean parseRequestLine(boolean useAvailableDataOnly)
    
        throws IOException {

        int start = 0;

        //
        // Skipping blank lines
        //

        byte chr = 0;
        do {

            // Read new bytes if needed
            if (pos >= lastValid) {
                if (!fill())
                    throw new EOFException(sm.getString("iib.eof.error"));
            }

            chr = buf[pos++];

        } while ((chr == Constants.CR) || (chr == Constants.LF));

        pos--;

        // Mark the current buffer position
        start = pos;

        //
        // Reading the method name
        // Method name is always US-ASCII
        //

        boolean space = false;

        while (!space) {

            // Read new bytes if needed
            if (pos >= lastValid) {
                if (!fill())
                    throw new EOFException(sm.getString("iib.eof.error"));
            }

            // Spec says no CR or LF in method name
            if (buf[pos] == Constants.CR || buf[pos] == Constants.LF) {
                throw new IllegalArgumentException(
                        sm.getString("iib.invalidmethod"));
            }
            // Spec says single SP but it also says be tolerant of HT
            if (buf[pos] == Constants.SP || buf[pos] == Constants.HT) {
                space = true;
                request.method().setBytes(buf, start, pos - start);
            }

            pos++;

        }

        
        // Spec says single SP but also says be tolerant of multiple and/or HT
        while (space) {
            // Read new bytes if needed
            if (pos >= lastValid) {
                if (!fill())
                    throw new EOFException(sm.getString("iib.eof.error"));
            }
            if (buf[pos] == Constants.SP || buf[pos] == Constants.HT) {
                pos++;
            } else {
                space = false;
            }
        }

        // Mark the current buffer position
        start = pos;
        int end = 0;
        int questionPos = -1;

        //
        // Reading the URI
        //

        boolean eol = false;

        while (!space) {

            // Read new bytes if needed
            if (pos >= lastValid) {
                if (!fill())
                    throw new EOFException(sm.getString("iib.eof.error"));
            }

            // Spec says single SP but it also says be tolerant of HT
            if (buf[pos] == Constants.SP || buf[pos] == Constants.HT) {
                space = true;
                end = pos;
            } else if ((buf[pos] == Constants.CR) 
                       || (buf[pos] == Constants.LF)) {
                // HTTP/0.9 style request
                eol = true;
                space = true;
                end = pos;
            } else if ((buf[pos] == Constants.QUESTION) 
                       && (questionPos == -1)) {
                questionPos = pos;
            }

            pos++;

        }

        request.unparsedURI().setBytes(buf, start, end - start);
        if (questionPos >= 0) {
            request.queryString().setBytes(buf, questionPos + 1, 
                                           end - questionPos - 1);
            request.requestURI().setBytes(buf, start, questionPos - start);
        } else {
            request.requestURI().setBytes(buf, start, end - start);
        }

        // Spec says single SP but also says be tolerant of multiple and/or HT
        while (space) {
            // Read new bytes if needed
            if (pos >= lastValid) {
                if (!fill())
                    throw new EOFException(sm.getString("iib.eof.error"));
            }
            if (buf[pos] == Constants.SP || buf[pos] == Constants.HT) {
                pos++;
            } else {
                space = false;
            }
        }

        // Mark the current buffer position
        start = pos;
        end = 0;

        //
        // Reading the protocol
        // Protocol is always US-ASCII
        //

        while (!eol) {

            // Read new bytes if needed
            if (pos >= lastValid) {
                if (!fill())
                    throw new EOFException(sm.getString("iib.eof.error"));
            }

            if (buf[pos] == Constants.CR) {
                end = pos;
            } else if (buf[pos] == Constants.LF) {
                if (end == 0)
                    end = pos;
                eol = true;
            }

            pos++;

        }

        if ((end - start) > 0) {
            request.protocol().setBytes(buf, start, end - start);
        } else {
            request.protocol().setString("");
        }
        
        return true;

    }

先看这个方法中第16行,调用了当前类的fill方法:

    protected boolean fill() throws IOException {
        return fill(true);
    }

里面调用了重载方法fill:

    protected boolean fill(boolean block) throws IOException {

        int nRead = 0;

        if (parsingHeader) {

            if (lastValid == buf.length) {
                throw new IllegalArgumentException
                    (sm.getString("iib.requestheadertoolarge.error"));
            }

            nRead = inputStream.read(buf, pos, buf.length - lastValid);
            if (nRead > 0) {
                lastValid = pos + nRead;
            }

        } else {

            if (buf.length - end < 4500) {
                // In this case, the request header was really large, so we allocate a 
                // brand new one; the old one will get GCed when subsequent requests
                // clear all references
                buf = new byte[buf.length];
                end = 0;
            }
            pos = end;
            lastValid = pos;
            nRead = inputStream.read(buf, pos, buf.length - lastValid);
            if (nRead > 0) {
                lastValid = pos + nRead;
            }

        }

        return (nRead > 0);

    }

在这里可以看到从输入流中读取数据到缓冲区buf。按照上面列出的请求行数据格式,从字符流中将会按顺序得到请求的类型(method)、请求的URI和Http版本。具体实现流程如下:

在org.apache.coyote.http11.InternalInputBuffer类中的parseRequestLine方法,第34到57行根据请求头协议的格式,从中取出表示请求方法的字节数据并设置到内置实例变量request。第60到72行解析method和uri之间的空格字节SP,第83到119行读取表示请求的URI的字节数据并放到request变量中。第122到133行解析uri和http协议版本之间的空格字节SP,第144到第168行读取表示请求的Http协议版本的字节数据并放到request变量中。

 

以上是根据Http协议解析请求行(request line)的代码实现部分,解析请求头的部分见InternalInputBuffer类的parseHeader方法,不再赘述。

 

至此可以看到在Tomcat中如何从一次Socket连接中取出请求的数据,将这些原始的字符流数据转换成初步可以理解的Tomcat内置对象org.apache.coyote.Request的。下一篇文章将会看到已经转换成内部变量的请求对象在Tomcat容器中的流转经过,如何一步一步将请求送到最终要执行的某个web应用中的某个servlet对象的service方法中的。

 

  • 大小: 61.4 KB
3
2
分享到:
评论
2 楼 myseason2009 2015-01-28  
默默点赞,膜拜博主
1 楼 hlssdlc 2014-12-09  
给个赞,慢慢消化中

相关推荐

    tomcat 分配请求之——socket获取请求

    Coyote会解析接收到的字节流,将其转换成请求和响应对象,然后再将这些对象传递给Servlet容器进行业务处理。 至于“工具”标签,可能是指开发者可以使用的一些辅助工具,例如Wireshark来捕获网络包,查看Socket通信...

    tomcat处理一个http请求的详细过程

    tomcat中server配置文件的结构,以及处理一个http请求的全过程

    迷你版的Tomcat,socket原理

    服务器端的Tomcat监听这个端口,一旦有连接请求,它就会创建一个Socket实例来处理这个连接。 在迷你版的Tomcat中,`ServerSocket`类用于监听客户端的连接请求。`ServerSocket`创建一个监听套接字,并在指定端口等待...

    解析Tomcat处理请求的类Connector<三>

    这两个对象是Coyote组件模型的一部分,它们不是Servlet API中的`HttpServletRequest`和`HttpServletResponse`,但可以在内部转换为Servlet API的接口,以便于Servlet容器处理。 `Connector`中的另一个重要概念是`...

    Tomcat请求处理UML序列图

    4. **CoyoteAdapter**:连接适配器,将接收到的HTTP请求转换为Servlet容器可以处理的对象。 5. **StandardEngine**:引擎组件,是Tomcat的核心组件之一,负责管理和调度其他容器组件。 6. **StandardPipeline**:...

    tomcat7安装使用及jvm连接数参数调优

    Tomcat7 的配置主要包括两个方面:一是配置 Tomcat7 的 JVM 参数,二是配置 Tomcat7 的连接数参数。 (一)配置 Tomcat7 的 JVM 参数 Tomcat7 的 JVM 参数配置主要是通过修改 catalina.sh 文件来实现的。具体来说...

    Tomcat怎样防止跨站请求伪造(CSRF) 1

    Tomcat 防止跨站请求伪造(CSRF)机制浅析 在 Web 应用开发中,跨站请求伪造(CSRF)是一种常见的安全威胁。跨站请求伪造攻击是指攻击者诱骗受信任用户访问恶意网站,从而使得恶意网站能以用户身份对受信任网站执行...

    tomcat7源码下载

    Tomcat7是一款广泛使用的开源Java Servlet容器,它实现了Java EE中的Web应用服务器标准,尤其是Servlet和JSP规范。源码下载是开发者深入理解其内部工作原理的重要途径,本篇文章将围绕Tomcat7源码进行详细探讨。 一...

    S19-再看tomcat架构与请求处理流程1

    1. **Endpoint 组件**:这是 Tomcat 的网络层面,负责处理连接请求。Endpoint 监听特定端口(如 `8080`),接收客户端通过 TCP/IP 协议发送的 HTTP 请求。它会创建线程来处理每个新的连接,并将接收到的数据传递给下...

    tomcat GET请求与POST请求

    在Web开发中,Tomcat作为一个流行的Java应用服务器,经常被用来托管Servlet和JSP应用程序。在HTTP协议中,GET和POST请求是两种最基本的方法,它们用于客户端(通常是浏览器)向服务器发送数据。理解这两者之间的差异...

    socket服务器 eclipse tomcat

    一个Socket服务器首先会创建一个ServerSocket,监听特定端口上的连接请求,当客户端连接时,ServerSocket会为每个客户端创建一个新的Socket实例来处理通信。 接下来,我们将在Eclipse中创建一个简单的Socket服务器...

    [线上问题] Nginx与Tomcat、Client之间请求的长连接配置不一致问题分析解决

    从提供的文件信息中,我们可以提取出有关于Nginx与Tomcat以及客户端之间请求的长连接配置不一致问题的分析解决知识点。同时,文件中提到了Linux系统中netstat命令的用法,我们可以从这部分内容中提取出与网络连接...

    3-5Tomcat响应请求源码与nio处理请求源码实现.mp4

    3-5Tomcat响应请求源码与nio处理请求源码实现.mp4

    Android系统单个应用的自动升级更新(通过http连接tomcat7)

    本教程将详细介绍如何构建一个能够通过HTTP连接到Tomcat7服务器,下载并安装更新的应用程序。 一、准备工作 在开始之前,确保你有以下环境: 1. Android Studio:这是Android开发的官方集成开发环境。 2. Tomcat7:...

    tomcat中多线程对于servlet处理的4篇资料

    接收到请求后,连接器将请求数据转换为内部格式,并传递给一个空闲的Coyote线程,由该线程调用Servlet进行处理。 10. **线程池的扩展性** Tomcat允许通过添加自定义的Executor来替换默认的线程池,这样可以根据...

    tomcat发送https请求配置

    Tomcat 是一个流行的 Web 服务器软件,用于部署基于 Java 的 Web 应用程序。为了确保数据传输的安全性,需要使用 HTTPS 协议来加密数据。在本文中,我们将介绍如何配置 Tomcat 来发送 HTTPS 请求,包括生成 JKS 文件...

    tomcat7源代码

    Tomcat7是一款广泛使用的开源Java Servlet容器,它实现了Java EE中的Web应用规范,包括Servlet、JSP和EL(Expression Language)等。本资源包含Tomcat7的源代码以及运行所需的jar包,旨在帮助开发者更深入地理解...

    TOMCAT原理详解及请求过程

    2. **Connector**接收请求,并将其转换成内部请求对象。 3. **Connector**将请求对象传递给对应的`Engine`。 4. `Engine`根据请求中的虚拟主机名选择合适的`Host`。 5. `Host`再根据请求中的上下文路径选择对应的`...

    Tomcat7性能优化

    本文将围绕“Tomcat7性能优化”这一主题,详细介绍如何通过优化配置提高Tomcat服务器的并发能力,并深入探讨服务器资源(如CPU、内存、硬盘等)对处理能力的影响。 #### 二、理解服务器资源的重要性 在优化Tomcat...

    tomcat 7 及其源码

    Apache Tomcat 7 是一个广泛使用的开源Java Servlet容器,它实现了Java EE的Web应用程序部分,特别是Servlet和JSP规范。这个版本7.0.42是Tomcat 7的一个稳定版本,提供了对Java Servlet 3.0和JavaServer Pages (JSP)...

Global site tag (gtag.js) - Google Analytics