精华内容
下载资源
问答
  • 声明:源码版本为Tomcat 6.0.35前面的文章中介绍了Tomcat初始化的过程,本文将会介绍TomcatHTTP请求的处理的整体流程,更细节的。在上一篇文章中,介绍到JIoEndpoint 中的内部类Acceptor用来接受Socket请求,并...

    声明:源码版本为Tomcat 6.0.35

    前面的文章中介绍了Tomcat初始化的过程,本文将会介绍Tomcat对HTTP请求的处理的整体流程,更细节的。

    在上一篇文章中,介绍到JIoEndpoint 中的内部类Acceptor用来接受Socket请求,并调用processSocket方法来进行请求的处理,所以会从本文这个方法开始进行讲解。

    在以上的代码中,首先会判断是否在server.xml配置了进程池,如果配置了的话,将会使用该线程池进行请求的处理,如果没有配置的话将会使用JIoEndpoint中自己实现的线程池WorkerStack来进行请求的处理,我们将会介绍WorkerStack的请求处理方式。

    在以上的代码中,最终返回了一个Worker的实例,有其来进行请求的处理,在这里,我们再看一下createWorkerThread方法,该方法会生成或者在线程池中取到一个线程。

    到此,线程已经获取了,接下来,最关键的是调用线程实现Worker的run方法:

    这里跟请求处理密切相关的是handler.process(socket)这一句代码,此处handle对应的类是Http11Protocol中的内部类Http11ConnectionHandler,在此后的处理中,会有一些请求的预处理,我们用一个时序图来表示一下:

    e08667638dca86db832c39cbc241ecd2.png

    在这个过程中,会对原始的socket进行一些处理,到CoyoteAdapter时,接受的参数已经是org.apache.coyote.Request和org.apache.coyote.Response了,但是要注意的是,此时这两个类并不是我们常用的HttpServletRequest和HttpServletResponse的实现类,而是Tomcat内部的数据结构,存储了和输入、输出相关的信息。值得注意的是,在CoyoteAdapter的service方法中,会调用名为postParseRequest的方法,在这个方法中,会解析请求,调用Mapper的Map方法来确定该请求该由哪个Engine、Host和Context来处理。

    在以上的信息处理完毕后,在CoyoteAdapter的service方法中,会调用这样一个方法:

    这个方法会涉及到Tomcat组件中的Container实现类的重要组成结构,即每个容器类组件都有一个pipeline属性,这个属性控制请求的处理过程,在pipeline上可以添加Valve,进而可以控制请求的处理流程。可以用下面的图来表示,请求是如何流动的:

    56bdf90c37b8c685664289d0daf3a8d1.png

    可以将请求想象成水的流动,请求需要在各个组件之间流动,中间经过若干的水管和水阀,等所有的水阀走完,请求也就处理完了,而每个组件都会有一个默认的水阀(以Standard作为类的前缀)来进行请求的处理,如果业务需要的话,可以自定义Valve,将其安装到容器中。

    后面的处理过程就比较类似了,会按照解析出来的Engine、Host、Context的顺序来进行处理。这里用了两张算不上标准的时序图来描述这一过程:

    1c14c9b9590ea7c572998d1c48a15c97.png

    6838457d7ab83ae675df44e545502438.png

    在以上的流程中,会一层层地调用各个容器组件的Valve的invoke方法,其中StandardWrapperValve这个标准阀门将会调用StandardWrapper的allocate方法来获取真正要执行的Servlet(在Tomcat中所有的请求最终都会映射到一个Servlet,静态资源和JSP也是如此),并按照请求的地址来构建过滤器链,按照顺序执行各个过滤器并最终调用目标Servlet的service方法,来完成业务的真正处理。

    以上的处理过程中,涉及到很多重要的代码,后续的文章会择期要者进行解析,如:

    Mapper中的internalMapWrapper方法(用来匹配对应的Servlet)

    ApplicationFilterFactory的createFilterChain方法(用来创建该请求的过滤器链)

    ApplicationFilterChain的internalDoFilter方法(用来执行过滤器方法以及最后的Servlet)

    Http11Processor中的process方法、prepareRequest方法以及prepareResponse方法(用来处理HTTP请求相关的协议、参数等信息)

    至此,我们简单了解一个请求的处理流程。

    展开全文
  • Tomcat解析请求头错误   今天上班遇到个坑,前端网页发起了个GET的请求.然后后台程序就开始报错 报错信息为:The valid characters are defined in RFC 7230 上网百度都说在catalina.properties中加上以下...

    Tomcat解析请求头错误

      今天上班遇到个坑,前端网页发起了个GET的请求.然后后台程序就开始报错 报错信息为:The valid characters are defined in RFC 7230

    上网百度都说在catalina.properties中加上以下两行就可以解决

    tomcat.util.http.parser.HttpParser.requestTargetAllow=|{}
    org.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH=true
    

    但是实际发现并没有用。最开始我怀疑是idea中配置tomcat问题导致的配置无效,再反复添加删除tomcat后,在tomcat的实例中发现配置是生效了的。
    于是继续百度,发现标题为:终极解决办法的,该方法指出了除了配置catalina.xml以外还要配置server.xml这个文件.需要在节点中,添加2个属性(可以接收任意特殊字符的组合,自行配置):

    relaxedPathChars="|{}[],\"
    relaxedQueryChars="|{}[],\"
    
    

      我自己的博客网站服务器还有几个月就快过期了,会陆续搬一些之前写的博客过来.以后回归csdn写博客了.如果写的文章能够满足你的需要.希望能够点个赞

    展开全文
  • 前面的文章中介绍了Tomcat初始化的过程,本文将会介绍TomcatHTTP请求的处理的整体流程,更细节的。 在上一篇文章中,介绍到JIoEndpoint 中的内部类Acceptor用来接受Socket请求,并调用processSocket方法来进行...

    声明:源码版本为Tomcat 6.0.35

    前面的文章中介绍了Tomcat初始化的过程,本文将会介绍Tomcat对HTTP请求的处理的整体流程,更细节的。

    在上一篇文章中,介绍到JIoEndpoint 中的内部类Acceptor用来接受Socket请求,并调用processSocket方法来进行请求的处理,所以会从本文这个方法开始进行讲解。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    protected boolean processSocket(Socket socket) {
            try {
                if (executor == null) {
                    getWorkerThread().assign(socket);
                else {
                    executor.execute(new SocketProcessor(socket));
                }
            catch (Throwable t) {
                //……此处略去若干代码
            }
            return true;
        }

    在以上的代码中,首先会判断是否在server.xml配置了进程池,如果配置了的话,将会使用该线程池进行请求的处理,如果没有配置的话将会使用JIoEndpoint中自己实现的线程池WorkerStack来进行请求的处理,我们将会介绍WorkerStack的请求处理方式。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    protected Worker getWorkerThread() {
            // Allocate a new worker thread
            synchronized (workers) {
                Worker workerThread;
                while ((workerThread = createWorkerThread()) == null) {
                    try {
                        workers.wait();
                    catch (InterruptedException e) {
                        // Ignore
                    }
                }
                return workerThread;
            }
        }

     

    在以上的代码中,最终返回了一个Worker的实例,有其来进行请求的处理,在这里,我们再看一下createWorkerThread方法,该方法会生成或者在线程池中取到一个线程。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    protected Worker createWorkerThread() {
            synchronized (workers) {
                if (workers.size() > 0) {
                //如果线程池中有空闲的线程,取一个
                    curThreadsBusy++;
                    return workers.pop();
                }
                if ((maxThreads > 0) && (curThreads < maxThreads)) {
                    //如果还没有超过最大线程数,会新建一个线程
                    curThreadsBusy++;
                    return (newWorkerThread());
                else {
                    if (maxThreads < 0) {
                        curThreadsBusy++;
                        return (newWorkerThread());
                    else {
                        return (null);
                    }
                }
            }
        }

     

    到此,线程已经获取了,接下来,最关键的是调用线程实现Worker的run方法:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    public void run() {
                // Process requests until we receive a shutdown signal
                while (running) {
                    // Wait for the next socket to be assigned
                    Socket socket = await();
                    if (socket == null)
                        continue;
                       if (!setSocketOptions(socket) || !handler.process(socket)) {
                        try {
                            socket.close();
                        catch (IOException e) {
                        }
                    }
                    socket = null;
                    recycleWorkerThread(this);
                }
            }

     

    这里跟请求处理密切相关的是handler.process(socket)这一句代码,此处handle对应的类是Http11Protocol中的内部类Http11ConnectionHandler,在此后的处理中,会有一些请求的预处理,我们用一个时序图来表示一下:


    在这个过程中,会对原始的socket进行一些处理,到CoyoteAdapter时,接受的参数已经是org.apache.coyote.Request和org.apache.coyote.Response了,但是要注意的是,此时这两个类并不是我们常用的HttpServletRequest和HttpServletResponse的实现类,而是Tomcat内部的数据结构,存储了和输入、输出相关的信息。值得注意的是,在CoyoteAdapter的service方法中,会调用名为postParseRequest的方法,在这个方法中,会解析请求,调用Mapper的Map方法来确定该请求该由哪个Engine、Host和Context来处理。

    在以上的信息处理完毕后,在CoyoteAdapter的service方法中,会调用这样一个方法:

    1
    connector.getContainer().getPipeline().getFirst().invoke(request, response);

    这个方法会涉及到Tomcat组件中的Container实现类的重要组成结构,即每个容器类组件都有一个pipeline属性,这个属性控制请求的处理过程,在pipeline上可以添加Valve,进而可以控制请求的处理流程。可以用下面的图来表示,请求是如何流动的:

    可以将请求想象成水的流动,请求需要在各个组件之间流动,中间经过若干的水管和水阀,等所有的水阀走完,请求也就处理完了,而每个组件都会有一个默认的水阀(以Standard作为类的前缀)来进行请求的处理,如果业务需要的话,可以自定义Valve,将其安装到容器中。

    后面的处理过程就比较类似了,会按照解析出来的Engine、Host、Context的顺序来进行处理。这里用了两张算不上标准的时序图来描述这一过程:


    在以上的流程中,会一层层地调用各个容器组件的Valve的invoke方法,其中StandardWrapperValve这个标准阀门将会调用StandardWrapper的allocate方法来获取真正要执行的Servlet(在Tomcat中所有的请求最终都会映射到一个Servlet,静态资源和JSP也是如此),并按照请求的地址来构建过滤器链,按照顺序执行各个过滤器并最终调用目标Servlet的service方法,来完成业务的真正处理。

    以上的处理过程中,涉及到很多重要的代码,后续的文章会择期要者进行解析,如:

    • Mapper中的internalMapWrapper方法(用来匹配对应的Servlet)

    • ApplicationFilterFactory的createFilterChain方法(用来创建该请求的过滤器链)

    • ApplicationFilterChain的internalDoFilter方法(用来执行过滤器方法以及最后的Servlet)

    • Http11Processor中的process方法、prepareRequest方法以及prepareResponse方法(用来处理HTTP请求相关的协议、参数等信息)

    至此,我们简单了解一个请求的处理流程。


    转载于:https://www.cnblogs.com/pig66/p/10421035.html

    展开全文
  • 我们都知道,一个http请求过程一般为浏览器发送请求->建立socket连接->通过socket读取数据->解析http数据->调用后台服务完成响应,而对于Tomcat来说,着重点在建立socket连接->通过socket读取数据-&...


    我们都知道,一个http请求过程一般为浏览器发送请求->建立socket连接->通过socket读取数据->解析http数据->调用后台服务完成响应,而对于Tomcat来说,着重点在建立socket连接->通过socket读取数据->解析http数据->调用filter->调用servlet处理并返回响应,本文将详细介绍Tomcat请求过程。


    前言

    当Tomcat启动时,会启动一组线程用于不同阶段的请求处理过程。

    在这里插入图片描述

    从图中可以看出,根据不同协议分别启动了Acceptor和Poller线程,先介绍一下这两个线程的作用,这里只对http协议的请求过程进行分析。

    • Acceptor:用于接收新连接,并将新连接添加到Poller事件队列中。
    • Poller:用于监听Socket事件,当Socket可读或者可写时,调用线程池处理Socket请求。

    建立Socket连接

    Acceptor#run

    public void run() {
    
        int errorDelay = 0;
    
        // Loop until we receive a shutdown command
        while (endpoint.isRunning()) {
    
            try {
               //...
    
                U socket = null;
                // 监听socket负责接收新连接
                socket = endpoint.serverSocketAccept();
            
                // Successful accept, reset the error delay
                errorDelay = 0;
    
                // Configure the socket
                if (endpoint.isRunning() && !endpoint.isPaused()) {
                    // setSocketOptions() will hand the socket off to
                    // an appropriate processor if successful
                    //处理接受到的socket对象并发布事件
                    if (!endpoint.setSocketOptions(socket)) {
                        endpoint.closeSocket(socket);
                    }
                } else {
                    endpoint.destroySocket(socket);
                }
            } catch (Throwable t) {
                //...
            }
        }
        state = AcceptorState.ENDED;
    }
    
    1. Acceptor 在启动后会阻塞在 endpoint.serverSocketAccept(); 方法处,当有新连接到达时,该方法返回一个 SocketChannel。这里的endpointNioEndpoint
    protected SocketChannel serverSocketAccept() throws Exception {
        return serverSock.accept();
    }
    
    1. endpoint.setSocketOptions(socket)会将socket封装到NioChannel中,并注册到Poller。
    protected boolean setSocketOptions(SocketChannel socket) {
        // Process the connection
        // Disable blocking, polling will be used
        socket.configureBlocking(false);
        Socket sock = socket.socket();
        socketProperties.setProperties(sock);
    
        NioChannel channel = null;
        if (nioChannels != null) {
            channel = nioChannels.pop();
        }
        if (channel == null) {
            SocketBufferHandler bufhandler = new SocketBufferHandler(
                socketProperties.getAppReadBufSize(),
                socketProperties.getAppWriteBufSize(),
                socketProperties.getDirectBuffer());
            if (isSSLEnabled()) {
                channel = new SecureNioChannel(socket, bufhandler, selectorPool, this);
            } else {
                channel = new NioChannel(socket, bufhandler);
            }
        } else {
            channel.setIOChannel(socket);
            channel.reset();
        }
        //获取Poller并将socket注册到Poller当中
        getPoller0().register(channel);
        //...
        return true;
    }
    

    发布PollerEvent事件

    NioEndpoint 维护了一个 Poller 数组,使用时,从中取出一个。如果当前Poller线程只有一个时,默认返回pollers[0],否则当一个连接分配给 pollers[index] 时,下一个连接就会分配给 pollers[(index+1)%pollers.length]

    public Poller getPoller0() {
        if (pollerThreadCount == 1) {
            return pollers[0];
        } else {
            int idx = Math.abs(pollerRotater.incrementAndGet()) % pollers.length;
            return pollers[idx];
        }
    }
    

    然后将 Socket 添加到该PollerPollerEvent队列中。

    public void register(final NioChannel socket) {
        socket.setPoller(this);
        NioSocketWrapper socketWrapper = new NioSocketWrapper(socket, NioEndpoint.this);
        socket.setSocketWrapper(socketWrapper);
        socketWrapper.setPoller(this);
        socketWrapper.setReadTimeout(getConnectionTimeout());
        socketWrapper.setWriteTimeout(getConnectionTimeout());
        socketWrapper.setKeepAliveLeft(NioEndpoint.this.getMaxKeepAliveRequests());
        socketWrapper.setSecure(isSSLEnabled());
        socketWrapper.interestOps(SelectionKey.OP_READ);//this is what OP_REGISTER turns into.
        PollerEvent r = null;
        if (eventCache != null) {
            r = eventCache.pop();
        }
        if (r == null) {
            r = new PollerEvent(socket, socketWrapper, OP_REGISTER);
        } else {
            r.reset(socket, socketWrapper, OP_REGISTER);
        }
        // 添加到PollerEvent队列当中
        addEvent(r);
    }
    
    private void addEvent(PollerEvent event) {
        // 添加到PollerEvent队列当中
        events.offer(event);
        if (wakeupCounter.incrementAndGet() == 0) {
            selector.wakeup();
        }
    }
    

    处理PollerEvent事件

    Poller线程会消费PollerEvent队列

    Poller#run

    public void run() {
    	//..
        if (!close) {
            //这里会处理PollerEvent事件
            hasEvents = events();
        }
        //...
        Iterator<SelectionKey> iterator =
            keyCount > 0 ? selector.selectedKeys().iterator() : null;
        while (iterator != null && iterator.hasNext()) {
            SelectionKey sk = iterator.next();
            NioSocketWrapper socketWrapper = (NioSocketWrapper) sk.attachment();
            if (socketWrapper == null) {
                iterator.remove();
            } else {
                iterator.remove();
                //处理socket请求
                processKey(sk, socketWrapper);
            }
        }
    }
    
    1. events()处理PollerEvent事件并将socket注册到selector当中,省略了部分代码,Poller和PollerEvent都是NioEndpoint的内部类。
    public class Poller implements Runnable {
        public boolean events() {
            boolean result = false;
            PollerEvent pe = null;
            for (int i = 0, size = events.size(); i < size && (pe = events.poll()) != null; i++ ) {
                result = true;
                //调用PollerEvent的run
                pe.run();
                pe.reset();
                if (running && !paused && eventCache != null) {
                    eventCache.push(pe);
                }
    
            }
    
            return result;
        }
    }    
        
    public static class PollerEvent implements Runnable {
         public void run() {
                if (interestOps == OP_REGISTER) {
                    //将Socket注册到channel中
                    socket.getIOChannel().register(socket.getPoller().getSelector(), SelectionKey.OP_READ, socketWrapper);
                   
                } 
            }
    }
    
    1. processKey(sk, socketWrapper)处理socket数据。

    Poller#processKey

    protected void processKey(SelectionKey sk, NioSocketWrapper attachment) {
        if (sk.isReadable()) {
            if (!processSocket(attachment, SocketEvent.OPEN_READ, true)) {
                closeSocket = true;
            }
        }
    	//...
        if (!closeSocket && sk.isWritable()) {
            if (!processSocket(attachment, SocketEvent.OPEN_WRITE, true)) {
                closeSocket = true;
            }
        }
    }
    

    当Socket可读或者可写时,处理Socket数据。

    读取Socket数据

    AbstractEndpoint#processSocket

    public boolean processSocket(SocketWrapperBase<S> socketWrapper,
                                 SocketEvent event, boolean dispatch) {
        if (socketWrapper == null) {
            return false;
        }
        SocketProcessorBase<S> sc = null;
        if (processorCache != null) {
            sc = processorCache.pop();
        }
        // 1.创建socket数据处理器
        if (sc == null) {
            
            sc = createSocketProcessor(socketWrapper, event);
        } else {
            sc.reset(socketWrapper, event);
        }
        //2.启动线程处理socket数据,
        Executor executor = getExecutor();
        if (dispatch && executor != null) {
            executor.execute(sc);
        } else {
            sc.run();
        }
        
        return true;
    }
    
    1. 创建socket数据处理器

    **NioEndpoint#createSocketProcessor **

    protected SocketProcessorBase<NioChannel> createSocketProcessor(
        SocketWrapperBase<NioChannel> socketWrapper, SocketEvent event) {
        return new SocketProcessor(socketWrapper, event);
    }
    
    1. 启动线程处理socket数据。如果配置了线程池会启动线程池来执行。
    protected class SocketProcessor extends SocketProcessorBase<NioChannel> {
        protected void doRun() {
            NioChannel socket = socketWrapper.getSocket();
            SelectionKey key = socket.getIOChannel().keyFor(socket.getPoller().getSelector());
    
            int handshake = -1;
    
            if (handshake == 0) {
                SocketState state = SocketState.OPEN;
                // Process the request from this socket
                // 处理socket数据
                if (event == null) {
                    state = getHandler().process(socketWrapper, SocketEvent.OPEN_READ);
                } else {
                    state = getHandler().process(socketWrapper, event);
                }
                if (state == SocketState.CLOSED) {
                    close(socket, key);
                }
            } else if (handshake == -1 ) {
                close(socket, key);
            } else if (handshake == SelectionKey.OP_READ){
                socketWrapper.registerReadInterest();
            } else if (handshake == SelectionKey.OP_WRITE){
                socketWrapper.registerWriteInterest();
            }
                
        }
    }
    

    这里的handler为ConnectionHandler,来源于Http11NioProtocol构造方法。

    public class Http11NioProtocol extends AbstractHttp11JsseProtocol<NioChannel> {
    
        public Http11NioProtocol() {
            super(new NioEndpoint());
        }
    }
    
    public abstract class AbstractHttp11Protocol<S> extends AbstractProtocol<S> {
        public AbstractHttp11Protocol(AbstractEndpoint<S,?> endpoint) {
            super(endpoint);
            setConnectionTimeout(Constants.DEFAULT_CONNECTION_TIMEOUT);
            ConnectionHandler<S> cHandler = new ConnectionHandler<>(this);
            setHandler(cHandler);
            getEndpoint().setHandler(cHandler);
        } 
    }
    

    那么接下来聚焦于它的process方法。

    ConnectionHandler#process

    public SocketState process(SocketWrapperBase<S> wrapper, SocketEvent status) {
        //...
        S socket = wrapper.getSocket();
        Processor processor = connections.get(socket);
        //....
        if (processor == null) {
            //创建processor
            processor = getProtocol().createProcessor();
            register(processor);
        }
        SocketState state = SocketState.CLOSED;
        do {
            //处理socket
            state = processor.process(wrapper, status);
            //...省略了处理完成之后清理socket的操作
        } while ( state == SocketState.UPGRADING);
        // Make sure socket/processor is removed from the list of current
        // connections
        connections.remove(socket);
        release(processor);
        return SocketState.CLOSED;
    }
    

    默认一个新连接的情况下,会调用processor.process(wrapper, status)代码,而processor的实例一般是在getProtocol().createProcessor()创建的,而protocol则:

    public ConnectionHandler(AbstractProtocol<S> proto) {
        this.proto = proto;
    }
    
    protected AbstractProtocol<S> getProtocol() {
        return proto;
    }
    

    AbstractProtocol有两个实现类,AbstractHttp11ProtocolAbstractAjpProtocol,这里是Http请求,所以获取到的protocol为AbstractHttp11Protocol。而AbstractHttp11Protocol创建了Http11Processor实例。

    protected Processor createProcessor() {
        Http11Processor processor = new Http11Processor(this, adapter);
        return processor;
    }
    

    由于Http11Processor没有实现process方法,所以实际调用了其父类的父类AbstractProcessorLight的process方法。

    public abstract class AbstractProcessorLight implements Processor {
    
        @Override
        public SocketState process(SocketWrapperBase<?> socketWrapper, SocketEvent status)
                throws IOException {
    		
            SocketState state = SocketState.CLOSED;
            Iterator<DispatchType> dispatches = null;
            do {
                //...
                if (status == SocketEvent.OPEN_READ){
                     //
                    state = service(socketWrapper);
                } else {
                    state = SocketState.CLOSED;
                }
            } while (state == SocketState.ASYNC_END ||
                    dispatches != null && state != SocketState.CLOSED);
    
            return state;
        }
    }
    

    根据不同的SocketEvent做不同操作,这里的事件是OPEN_READ,所以调用service方法解析http数据。

    解析Http数据

    Http11Processor#service

    public SocketState service(SocketWrapperBase<?> socketWrapper)
        throws IOException {
        RequestInfo rp = request.getRequestProcessor();
        rp.setStage(org.apache.coyote.Constants.STAGE_PARSE);
    
        // Setting up the I/O
        setSocketWrapper(socketWrapper);
        inputBuffer.init(socketWrapper);
        outputBuffer.init(socketWrapper);
    
        // Flags
        while (!getErrorState().isError() && keepAlive && !isAsync() && upgradeToken == null &&
               sendfileState == SendfileState.DONE && !protocol.isPaused()) {
    
            // Parsing the request header
            // 解析requestLine
            if (!inputBuffer.parseRequestLine(keptAlive, protocol.getConnectionTimeout(),
                                              protocol.getKeepAliveTimeout())) {
                if (inputBuffer.getParsingRequestLinePhase() == -1) {
                    return SocketState.UPGRADING;
                } else if (handleIncompleteRequestLineRead()) {
                    break;
                }
            }
    
            if (protocol.isPaused()) {
                response.setStatus(503);
                setErrorState(ErrorState.CLOSE_CLEAN, null);
            } else {
                keptAlive = true;
                request.getMimeHeaders().setLimit(protocol.getMaxHeaderCount());
                //解析header
                if (!inputBuffer.parseHeaders()) {
                    openSocket = true;
                    readComplete = false;
                    break;
                }
                if (!protocol.getDisableUploadTimeout()) {
                    socketWrapper.setReadTimeout(protocol.getConnectionUploadTimeout());
                }
            }
            //...
            //指定request body的读取filter
            prepareRequest();
            //...
            // Process the request in the adapter
            if (getErrorState().isIoAllowed()) {
                rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE);
                //处理request
                getAdapter().service(request, response);
    
                if(keepAlive && !getErrorState().isError() && !isAsync() &&
                   statusDropsConnection(response.getStatus())) {
                    setErrorState(ErrorState.CLOSE_CLEAN, null);
                }
            }
    
            // Finish the handling of the request
            //...
        }
    }
    
    1. 解析请求header。
    2. 指定request body的读取filter。
    3. 匹配servlet。

    这里主要针对匹配servlet作详细解释。

    CoyoteAdapter#service

    public void service(org.apache.coyote.Request req, org.apache.coyote.Response res)
        throws Exception {
        //...
        // 解析request并匹配servlet
        postParseSuccess = postParseRequest(req, request, res, response);
        if (postParseSuccess) {
            request.setAsyncSupported(
                connector.getService().getContainer().getPipeline().isAsyncSupported());
            // 调用匹配成功的servlet
            connector.getService().getContainer().getPipeline().getFirst().invoke(
                request, response);
        }
        //...
    }
    
    1. 解析request并匹配servlet
    2. 调用匹配成功的servlet

    匹配servlet

    protected boolean postParseRequest(org.apache.coyote.Request req, Request request,
                                       org.apache.coyote.Response res, Response response) throws IOException, ServletException {
        //...
        connector.getService().getMapper().map(serverName, decodedURI,
                        version, request.getMappingData());
        //..
    }
    

    这里只关注如何匹配,省略了一些有关seesion和cookie的处理。

    Mapper在StandardService启动时MapperListener 的start方法初始化。MapperListener启动这里放一张截图方便理解。

    在这里插入图片描述

    Mapper#map

    public void map(MessageBytes host, MessageBytes uri, String version,
                    MappingData mappingData) throws IOException {
    
        if (host.isNull()) {
            String defaultHostName = this.defaultHostName;
            if (defaultHostName == null) {
                return;
            }
            host.getCharChunk().append(defaultHostName);
        }
        host.toChars();
        uri.toChars();
        //匹配context(即war包)名称,设置到mappingData中,匹配成功后匹配context的child wrapper名称,设置到mappingData中。
        internalMap(host.getCharChunk(), uri.getCharChunk(), version, mappingData); 
    }
    
    private final void internalMap(CharChunk host, CharChunk uri,
                String version, MappingData mappingData) throws IOException {
    
            if (mappingData.host != null) {
                throw new AssertionError();
            }
    		//...
            ContextList contextList = mappedHost.contextList;
            MappedContext[] contexts = contextList.contexts;
            int pos = find(contexts, uri);
            if (pos == -1) {
                return;
            }
    
            int lastSlash = -1;
            int uriEnd = uri.getEnd();
            int length = -1;
            boolean found = false;
            MappedContext context = null;
            while (pos >= 0) {
                context = contexts[pos];
                //匹配Context
                if (uri.startsWith(context.name)) {
                    length = context.name.length();
                    if (uri.getLength() == length) {
                        found = true;
                        break;
                    } else if (uri.startsWithIgnoreCase("/", length)) {
                        found = true;
                        break;
                    }
                }
                if (lastSlash == -1) {
                    lastSlash = nthSlash(uri, contextList.nesting + 1);
                } else {
                    lastSlash = lastSlash(uri);
                }
                uri.setEnd(lastSlash);
                pos = find(contexts, uri);
            }
            uri.setEnd(uriEnd);
    
            if (!found) {
                if (contexts[0].name.equals("")) {
                    context = contexts[0];
                } else {
                    context = null;
                }
            }
            if (context == null) {
                return;
            }
    
            mappingData.contextPath.setString(context.name);
    		//匹配contextversion
            ContextVersion contextVersion = null;
            ContextVersion[] contextVersions = context.versions;
            final int versionCount = contextVersions.length;
            if (versionCount > 1) {
                Context[] contextObjects = new Context[contextVersions.length];
                for (int i = 0; i < contextObjects.length; i++) {
                    contextObjects[i] = contextVersions[i].object;
                }
                mappingData.contexts = contextObjects;
                if (version != null) {
                    contextVersion = exactFind(contextVersions, version);
                }
            }
            if (contextVersion == null) {
                // Return the latest version
                // The versions array is known to contain at least one element
                contextVersion = contextVersions[versionCount - 1];
            }
            mappingData.context = contextVersion.object;
            mappingData.contextSlashCount = contextVersion.slashCount;
    
            // Wrapper mapping 匹配wrapper
            if (!contextVersion.isPaused()) {
                internalMapWrapper(contextVersion, uri, mappingData);
            }
    
        }
    

    这里主要分为两步进行匹配。

    1. 首先匹配context.name,匹配成功后设置到mappingData.contextPath
    2. 匹配wrapper.name,匹配成功后设置到mappingData.wrapperPath
    private final void internalMapWrapper(ContextVersion contextVersion,
                                          CharChunk path,
                                          MappingData mappingData) throws IOException {
    
        //...
    
        // Rule 1 -- Exact Match 匹配真实的wrapper,即项目里自己写的
        MappedWrapper[] exactWrappers = contextVersion.exactWrappers;
        internalMapExactWrapper(exactWrappers, path, mappingData);
    
        // Rule 2 -- Prefix Match 通配匹配
        boolean checkJspWelcomeFiles = false;
        MappedWrapper[] wildcardWrappers = contextVersion.wildcardWrappers;
        if (mappingData.wrapper == null) {
            internalMapWildcardWrapper(wildcardWrappers, contextVersion.nesting,
                                       path, mappingData);
            if (mappingData.wrapper != null && mappingData.jspWildCard) {
                char[] buf = path.getBuffer();
                if (buf[pathEnd - 1] == '/') {
                    mappingData.wrapper = null;
                    checkJspWelcomeFiles = true;
                } else {
                    mappingData.wrapperPath.setChars(buf, path.getStart(),
                                                     path.getLength());
                    mappingData.pathInfo.recycle();
                }
            }
        }
    	//...
    
        // Rule 3 -- Extension Match 扩展匹配
        MappedWrapper[] extensionWrappers = contextVersion.extensionWrappers;
        if (mappingData.wrapper == null && !checkJspWelcomeFiles) {
            internalMapExtensionWrapper(extensionWrappers, path, mappingData,
                                        true);
        }
    
        // Rule 4 -- Welcome resources processing for servlets
        if (mappingData.wrapper == null) {
            boolean checkWelcomeFiles = checkJspWelcomeFiles;
            if (!checkWelcomeFiles) {
                char[] buf = path.getBuffer();
                checkWelcomeFiles = (buf[pathEnd - 1] == '/');
            }
            if (checkWelcomeFiles) {
                for (int i = 0; (i < contextVersion.welcomeResources.length)
                     && (mappingData.wrapper == null); i++) {
                    path.setOffset(pathOffset);
                    path.setEnd(pathEnd);
                    path.append(contextVersion.welcomeResources[i], 0,
                                contextVersion.welcomeResources[i].length());
                    path.setOffset(servletPath);
    
                    // Rule 4a -- Welcome resources processing for exact macth
                    internalMapExactWrapper(exactWrappers, path, mappingData);
    
                    // Rule 4b -- Welcome resources processing for prefix match
                    if (mappingData.wrapper == null) {
                        internalMapWildcardWrapper
                            (wildcardWrappers, contextVersion.nesting,
                             path, mappingData);
                    }
    
                    // Rule 4c -- Welcome resources processing
                    //            for physical folder
                    if (mappingData.wrapper == null
                        && contextVersion.resources != null) {
                        String pathStr = path.toString();
                        WebResource file =
                            contextVersion.resources.getResource(pathStr);
                        if (file != null && file.isFile()) {
                            internalMapExtensionWrapper(extensionWrappers, path,
                                                        mappingData, true);
                            if (mappingData.wrapper == null
                                && contextVersion.defaultWrapper != null) {
                                mappingData.wrapper =
                                    contextVersion.defaultWrapper.object;
                                mappingData.requestPath.setChars
                                    (path.getBuffer(), path.getStart(),
                                     path.getLength());
                                mappingData.wrapperPath.setChars
                                    (path.getBuffer(), path.getStart(),
                                     path.getLength());
                                mappingData.requestPath.setString(pathStr);
                                mappingData.wrapperPath.setString(pathStr);
                            }
                        }
                    }
                }
    
                path.setOffset(servletPath);
                path.setEnd(pathEnd);
            }
    
        }
    
        //...
        // Rule 7 -- Default servlet
        if (mappingData.wrapper == null && !checkJspWelcomeFiles) {
            if (contextVersion.defaultWrapper != null) {
                mappingData.wrapper = contextVersion.defaultWrapper.object;
                mappingData.requestPath.setChars
                    (path.getBuffer(), path.getStart(), path.getLength());
                mappingData.wrapperPath.setChars
                    (path.getBuffer(), path.getStart(), path.getLength());
                mappingData.matchType = MappingMatch.DEFAULT;
            }
            // Redirection to a folder
            char[] buf = path.getBuffer();
            if (contextVersion.resources != null && buf[pathEnd -1 ] != '/') {
                String pathStr = path.toString();
                // Note: Check redirect first to save unnecessary getResource()
                //       call. See BZ 62968.
                if (contextVersion.object.getMapperDirectoryRedirectEnabled()) {
                    WebResource file;
                    // Handle context root
                    if (pathStr.length() == 0) {
                        file = contextVersion.resources.getResource("/");
                    } else {
                        file = contextVersion.resources.getResource(pathStr);
                    }
                    if (file != null && file.isDirectory()) {
                        // Note: this mutates the path: do not do any processing
                        // after this (since we set the redirectPath, there
                        // shouldn't be any)
                        path.setOffset(pathOffset);
                        path.append('/');
                        mappingData.redirectPath.setChars
                            (path.getBuffer(), path.getStart(), path.getLength());
                    } else {
                        mappingData.requestPath.setString(pathStr);
                        mappingData.wrapperPath.setString(pathStr);
                    }
                } else {
                    mappingData.requestPath.setString(pathStr);
                    mappingData.wrapperPath.setString(pathStr);
                }
            }
        }
    
        path.setOffset(pathOffset);
        path.setEnd(pathEnd);
    }
    

    匹配规则:

    1. 匹配exact servlet,即项目里真实的servlet。
    2. 通用匹配,
    3. 扩展匹配,tomcat容器web.xml默认的servlet
    4. reources匹配,即欢迎页面等
    5. 匹配未配置的扩展名映射,如index.jsf,index.do。
    6. default servlet的匹配。

    这里重点看一下匹配exact servlet

    private final void internalMapExactWrapper
        (MappedWrapper[] wrappers, CharChunk path, MappingData mappingData) {
        //根据path寻找wrapper
        MappedWrapper wrapper = exactFind(wrappers, path);
        if (wrapper != null) {
            mappingData.requestPath.setString(wrapper.name);
            mappingData.wrapper = wrapper.object;
            if (path.equals("/")) {//根目录
                // Special handling for Context Root mapped servlet
                mappingData.pathInfo.setString("/");
                mappingData.wrapperPath.setString("");
                // This seems wrong but it is what the spec says...
                mappingData.contextPath.setString("");
                mappingData.matchType = MappingMatch.CONTEXT_ROOT;
            } else {
                mappingData.wrapperPath.setString(wrapper.name);
                mappingData.matchType = MappingMatch.EXACT;
            }
        }
    }
    

    匹配成功后,将wrapper.namematchType设置到mappingData中以备调用servlet时使用。这里放一张截图方便理解。

    在这里插入图片描述

    到这里postParseRequest就完成了。

    调用servlet

    CoyoteAdapter#service

    connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);
    

    这里通过调用每个容器的pipeline的Valve对象,实现了对Servlet的调用,每个容器都有一个或者多个Valve,这些Valve来源于两个地方,一个是conf/server.xml中配置的Valve,另外一个就是每一个容器的构造其中自己初始化的Valve

    这里调用了StandardEngineValve.invoke,然后继续往下调用。

    public final void invoke(Request request, Response response)
        throws IOException, ServletException {
    
        // Select the Host to be used for this Request
        Host host = request.getHost();
        if (host == null) {
            return;
        }
        if (request.isAsyncSupported()) {
            request.setAsyncSupported(host.getPipeline().isAsyncSupported());
        }
    
        // Ask this Host to process this request
        host.getPipeline().getFirst().invoke(request, response);
    }
    

    通过debug代码,可以得到一个完整的调用链。

    ->org.apache.catalina.core.StandardEngineValve#invoke
    -->org.apache.catalina.valves.AbstractAccessLogValve#invoke
    --->org.apache.catalina.valves.ErrorReportValve#invoke
    ---->org.apache.catalina.core.StandardHostValve#invoke
    ----->org.apache.catalina.authenticator.AuthenticatorBase#invoke
    ------>org.apache.catalina.core.StandardContextValve#invoke
    ------->org.apache.catalina.core.StandardWrapperValve#invoke
    

    因为StandardWrapperValve中最终调用了servlet,所以重点看下。

    final class StandardWrapperValve extends ValveBase {
        
         public final void invoke(Request request, Response response) throws IOException, ServletException {
             //...
             if (!unavailable) {
                 //实例化servlet
                 servlet = wrapper.allocate();
             }
             //...
             //create filter调用链
             ApplicationFilterChain filterChain =
                    ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);
             //...
             //调用filter
             filterChain.doFilter(request.getRequest(),
                                        response.getResponse());
         }
    }
    
    1. 实例化servlet。
    2. 创建filter调用链。
    3. 调用filter

    创建filter调用链

    ApplicationFilterFactory#createFilterChain

    public static ApplicationFilterChain createFilterChain(ServletRequest request,
                                                           Wrapper wrapper, Servlet servlet) {
    
        // If there is no servlet to execute, return null
        if (servlet == null)
            return null;
    
        // Create and initialize a filter chain object
        ApplicationFilterChain filterChain = null;
        if (request instanceof Request) {
            Request req = (Request) request;
            if (Globals.IS_SECURITY_ENABLED) {
                filterChain = new ApplicationFilterChain();
            } else {
                filterChain = (ApplicationFilterChain) req.getFilterChain();
                if (filterChain == null) {
                    filterChain = new ApplicationFilterChain();
                    req.setFilterChain(filterChain);
                }
            }
        } else {
            // Request dispatcher in use
            filterChain = new ApplicationFilterChain();
        }
    
        filterChain.setServlet(servlet);
        filterChain.setServletSupportsAsync(wrapper.isAsyncSupported());
    
        // 获取启动时初始化的filter
        StandardContext context = (StandardContext) wrapper.getParent();
        FilterMap filterMaps[] = context.findFilterMaps();
    
        // If there are no filter mappings, we are done
        // 如果没有filter 直接返回
        if ((filterMaps == null) || (filterMaps.length == 0))
            return filterChain;
    
        // Acquire the information we will need to match filter mappings
        DispatcherType dispatcher =
            (DispatcherType) request.getAttribute(Globals.DISPATCHER_TYPE_ATTR);
    
        String requestPath = null;
        Object attribute = request.getAttribute(Globals.DISPATCHER_REQUEST_PATH_ATTR);
        if (attribute != null){
            requestPath = attribute.toString();
        }
    
        String servletName = wrapper.getName();
        //校验requestPath是否匹配filter的urlPatterns
        // Add the relevant path-mapped filters to this filter chain
        for (int i = 0; i < filterMaps.length; i++) {
            if (!matchDispatcher(filterMaps[i] ,dispatcher)) {
                continue;
            }
            if (!matchFiltersURL(filterMaps[i], requestPath))
                continue;
            ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
                context.findFilterConfig(filterMaps[i].getFilterName());
            if (filterConfig == null) {
                // FIXME - log configuration problem
                continue;
            }
            filterChain.addFilter(filterConfig);
        }
        //校验servlet name是否匹配filter的servletNames
        // Add filters that match on servlet name second
        for (int i = 0; i < filterMaps.length; i++) {
            if (!matchDispatcher(filterMaps[i] ,dispatcher)) {
                continue;
            }
            if (!matchFiltersServlet(filterMaps[i], servletName))
                continue;
            ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
                context.findFilterConfig(filterMaps[i].getFilterName());
            if (filterConfig == null) {
                // FIXME - log configuration problem
                continue;
            }
            filterChain.addFilter(filterConfig);
        }
    
        // Return the completed filter chain
        return filterChain;
    }
    
    • 首先创建并初始化一个filterChain对象。
    • 循环调用matchFiltersURL(filterMaps[i], requestPath)匹配requestPath与filter的urlPatterns,requestPath即servlet路径,匹配成功后将当前filter添加到filterChain
    private static boolean matchFiltersURL(FilterMap filterMap, String requestPath) {
    
        // Check the specific "*" special URL pattern, which also matches
        // named dispatches
        if (filterMap.getMatchAllUrlPatterns())
            return true;
        if (requestPath == null)
            return false;
        // Match on context relative request path
        String[] testPaths = filterMap.getURLPatterns();
    
        for (int i = 0; i < testPaths.length; i++) {
            if (matchFiltersURL(testPaths[i], requestPath)) {
                return true;
            }
        }
        // No match
        return false;
    
    }
    
    private static boolean matchFiltersURL(String testPath, String requestPath) {
    
        if (testPath == null)
            return false;
    
        // Case 1 - Exact Match
        if (testPath.equals(requestPath))
            return true;
    
        // Case 2 - Path Match ("/.../*")
        if (testPath.equals("/*"))
            return true;
        if (testPath.endsWith("/*")) {
            if (testPath.regionMatches(0, requestPath, 0,
                                       testPath.length() - 2)) {
                if (requestPath.length() == (testPath.length() - 2)) {
                    return true;
                } else if ('/' == requestPath.charAt(testPath.length() - 2)) {
                    return true;
                }
            }
            return false;
        }
    
        // Case 3 - Extension Match
        if (testPath.startsWith("*.")) {
            int slash = requestPath.lastIndexOf('/');
            int period = requestPath.lastIndexOf('.');
            if ((slash >= 0) && (period > slash)
                && (period != requestPath.length() - 1)
                && ((requestPath.length() - period)
                    == (testPath.length() - 1))) {
                return testPath.regionMatches(2, requestPath, period + 1,
                                              testPath.length() - 2);
            }
        }
    
        // Case 4 - "Default" Match
        return false; // NOTE - Not relevant for selecting filters
    
    }
    
    • 循环调用matchFiltersServlet(filterMaps[i], servletName)匹配servletName与filter的servletNames,匹配成功后将当前filter添加到filterChain
      private static boolean matchFiltersServlet(FilterMap filterMap,
                                            String servletName) {
    
            if (servletName == null) {
                return false;
            }
            // Check the specific "*" special servlet name
            else if (filterMap.getMatchAllServletNames()) {
                return true;
            } else {
                String[] servletNames = filterMap.getServletNames();
                for (int i = 0; i < servletNames.length; i++) {
                    if (servletName.equals(servletNames[i])) {
                        return true;
                    }
                }
                return false;
            }
    
        }
    

    到这里创建filter调用链就结束了,开始调用filter。

    调用Filter

    ApplicationFilterChain#doFilter

    public void doFilter(ServletRequest request, ServletResponse response)
        throws IOException, ServletException {
        //...
        internalDoFilter(request,response);
    }
    
    private void internalDoFilter(ServletRequest request,
                                  ServletResponse response)
        throws IOException, ServletException {
    
        // n=filter.length ,addFilter时初始化。
        if (pos < n) {
            ApplicationFilterConfig filterConfig = filters[pos++];
            Filter filter = filterConfig.getFilter();
    		//调用filter
            filter.doFilter(request, response, this);
          
            return;
        }
    	//...
        //filter调用完成之后调用servlet的service方法。
        servlet.service(request, response);
       
    }
    

    调用servlet

    最后会调用HttpServlet的service方法来区分调用GET/POST等方法。

    protected void service(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException {
    
        String method = req.getMethod();
    
        if (method.equals(METHOD_GET)) {
            long lastModified = getLastModified(req);
            if (lastModified == -1) {
                doGet(req, resp);
            } else {
                long ifModifiedSince;
                try {
                    ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
                } catch (IllegalArgumentException iae) {
                    ifModifiedSince = -1;
                }
                if (ifModifiedSince < (lastModified / 1000 * 1000)) {
                    // If the servlet mod time is later, call doGet()
                    // Round down to the nearest second for a proper compare
                    // A ifModifiedSince of -1 will always be less
                    maybeSetLastModified(resp, lastModified);
                    doGet(req, resp);
                } else {
                    resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
                }
            }
    
        } else if (method.equals(METHOD_HEAD)) {
            long lastModified = getLastModified(req);
            maybeSetLastModified(resp, lastModified);
            doHead(req, resp);
    
        } else if (method.equals(METHOD_POST)) {
            doPost(req, resp);
    
        } else if (method.equals(METHOD_PUT)) {
            doPut(req, resp);
    
        } else if (method.equals(METHOD_DELETE)) {
            doDelete(req, resp);
    
        } else if (method.equals(METHOD_OPTIONS)) {
            doOptions(req,resp);
    
        } else if (method.equals(METHOD_TRACE)) {
            doTrace(req,resp);
    
        } else {
            //
            // Note that this means NO servlet supports whatever
            // method was requested, anywhere on this server.
            //
    
            String errMsg = lStrings.getString("http.method_not_implemented");
            Object[] errArgs = new Object[1];
            errArgs[0] = method;
            errMsg = MessageFormat.format(errMsg, errArgs);
    
            resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
        }
    }
    

    以上。

    总结

    通过上面的分析,其实我们已经清楚了当一个请求过来以后,Tomcat是如何处理的。我们再来做一个总体的总结:

    1. 用户浏览器发送请求,请求会发送到对应的Connector监听的Socket端口。
    2. Connector从Socket流中获取数据,然后根据Http协议将其解析为Request和Reponse对象。
    3. 找到Request对象对应的Host,Context,Wrapper。
    4. 调用filter链。
    5. 调用最终的Servelt的service进行处理。

    最后放一张整体的调用链。

    在这里插入图片描述

    参考:

    https://github.com/c-rainstorm/blog/blob/master/tomcat/%E8%B0%88%E8%B0%88Tomcat%E8%AF%B7%E6%B1%82%E5%A4%84%E7%90%86%E6%B5%81%E7%A8%8B.md

    https://blog.csdn.net/jiaomingliang/article/details/47414657

    https://www.jianshu.com/p/d8a2bc7d3c21

    展开全文
  • [http-nio-8080-exec-6] org.apache.coyote.http11.Http11Processor.service 解析 HTTP 请求 header 错误注意:HTTP请求解析错误的进一步发生将记录在DEBUG级别。 java.lang.IllegalArgumentException: 在方法名称...
  • 在连接器对请求信息进行解析, 封装为Request和Response, 然后通过适配器CoyoteAdapter的service()方法调用引擎中管道pipeline的第一个Valve, 开始对请求进行处理; CoyoteAdapter#service实现: @Override public ...
  • 在开始本文之前,咋们首先来看看一个Http请求处理的过程,一般情况下是浏览器发送http请求-&gt;建立Socket连接-&gt;通过Socket读取数据-&gt;根据http协议解析数据-&gt;调用后台服务完成响应,详细的...
  • 手写tomcat,实现静态网站部署
  • Tomcat 解析

    2020-12-06 15:42:39
    用于接收socket连接,交处理器解析http请求,调用容器的invoke方法 2. Container Engine: Host:代表一个虚拟主机,一个Engine可以有多个Host Context:代表一个应用程序,一个Host可以用多个Context Wrapper...
  • 1、tomcat 的javaweb项目 前端请求带后缀".s "(如:localhost/user/list.s),返回json 报406  解决方案:web.xml中定义了文件扩展名到tomcat MIME类型的对应关系 web.xml 增加如下配置 s application/json ...
  • 一、启动线程接收客户端请求 在NioEndpoint的startInternal()方法中启动Acceptor线程, 开始接收客户端请求 NioEndpoint#startInternal()方法实现: @Override public void startInternal() throws Exception { ...
  • tomcat原理解析(五):http请求处理

    千次阅读 2017-05-18 14:29:29
    一概述 二http请求处理 三总结
  • 书中第三章的程序比较庞大,实现了简单的请求解析请求头部解析等。鉴于程序比较复杂,所以我自己实现了一个更简易的版本。本文的程序主要是为了讲解web服务器解析请求的过程到底做了什么。 程序 启动类 我们...
  • 一、启动8080端口监听http请求 二、启动http监听代码解析 NioEndpoint的方法startInternal启动socket的监听,监听http请求 @Override public void startInternal() throws Exception { if (!running) { ...
  • 其中最重要的组件就是Connector和Container,其中Connector负责监听客户端的请求并把他封装成容器统一的Request和Response对象。 Tomcat的连接器大致分两种: HTTP HTTP连接器是Tomcat默认设置的并且可用的,该...
  • tomcat解析

    2018-09-20 17:21:00
    tomat是一个servlet容器,来处理http请求。在平时的使用中我们都会再浏览器中输入http地址来访问服务资源,比如格式http://host[":"port][abs_path]。从浏览器到服务端的一次请求都遵循http协议,在网络上其实走仍然...
  • Tomcat源码解析系列2015-08-11 09:25 248人阅读 评论(0) 收藏 举报 ...在开始本文之前,咋们首先来看看一个Http请求处理的过程,一般情况下是浏览器发送http请求->建立Socket连接->通过Socke
  • Tomcat Tomcat的组织结构 由Server.xml的结构看Tomcat的体系结构 Tomca的两大组件:Connecter和Container Connecter组件 Container组件 Tomcat 中其它组件 Tomcat Server处理一个HTTP请求的过程 Tomcat ...
  • 一、前言     介绍网Catalina中Web应用的加载过程,这篇博客再来介绍...Tomcat通过org.apache.tomcat.util.http.Mapper维护请求链接与Host、Context、Wrapper等Container映射。同时,通过org....
  • Tomcat Tomcat是一个JSP/Servlet容器。其作为Servlet容器,有三种工作模式:独立的Servlet容器、进程内的Servlet容器和进程外的Servlet容器。 Tomcat的组织结构 Tomcat是一个基于组件的服务器,它的构成组件都是...
  • 24-Mar-2018 14:11:20.564 INFO [http-nio-8080-exec-3] org.apache.coyote.http11.Http11Processor.service Error parsing HTTP request header Note: further occurrences of HTTP header parsing errors will be ...
  • Tomcat架构图架构分析处理流程1.由用户发来的信息通过Http协议2....解析完毕将请求的交给Engine由Engine进行实质的请求的处理5.请求处理完毕后在由Engine将处理的返回给Connector由Connector将结果返回给浏览器...
  • tomcat服务器值HTTP请求

    2015-01-31 15:29:04
    当你在Web浏览器中输入一个URL时,浏览器将根据你的要求创建并发送请求,该请求包含所输入的URL以及一些与浏览器本身相关的信息。当服务器收到这个请求时将返回一个...HTTP请求 HTTP请求的格式如下所示:

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 869
精华内容 347
关键字:

tomcat解析http请求