Помимо исходного кода Tomcat, который вы видели ранее, Tomcat представляет собой веб-сервер, реализованный на Java.Если вас попросят реализовать его, с чего вы начнете?
Первое, что здесь нужно уточнить, это понятие веб-сервера, я погуглил и обнаружил, что это объяснение вполне достоверно, [программа, которая может предоставлять документы запрашивающему браузеру в сетевом окружении]. Есть два ключевых момента: 1. В сетевом окружении, 2. Возможность ответить. Любой, кто написал программу сетевого взаимодействия на Java, знает, что здесь обязательно будет использоваться программирование на основе сокетов. Серверная программа, которую мы хотим реализовать, используется в качестве сервера в программировании для сокетов, а браузер используется в качестве клиента в программировании для сокетов.
Чтобы понять принцип работы 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();
}
Взаимодействие между браузером и веб-сервером делится на четыре этапа: подключение, запрос, ответ и закрытие.предыдущий постАнализируемый поток-приемник, такой как http-bio-8080-Acceptor-0 на скриншоте в начале предыдущей статьи, класс реализации этого потокаorg.apache.tomcat.util.net.JIoEndpoint.Acceptor
, исходный код выглядит следующим образом:
1 // --------------------------------------------------- Acceptor Inner Class
2 /**
3 * The background thread that listens for incoming TCP/IP connections and
4 * hands them off to an appropriate processor.
5 */
6 protected class Acceptor extends AbstractEndpoint.Acceptor {
7
8 @Override
9 public void run() {
10
11 int errorDelay = 0;
12
13 // Loop until we receive a shutdown command
14 while (running) {
15
16 // Loop if endpoint is paused
17 while (paused && running) {
18 state = AcceptorState.PAUSED;
19 try {
20 Thread.sleep(50);
21 } catch (InterruptedException e) {
22 // Ignore
23 }
24 }
25
26 if (!running) {
27 break;
28 }
29 state = AcceptorState.RUNNING;
30
31 try {
32 //if we have reached max connections, wait
33 countUpOrAwaitConnection();
34
35 Socket socket = null;
36 try {
37 // Accept the next incoming connection from the server
38 // socket
39 socket = serverSocketFactory.acceptSocket(serverSocket);
40 } catch (IOException ioe) {
41 countDownConnection();
42 // Introduce delay if necessary
43 errorDelay = handleExceptionWithDelay(errorDelay);
44 // re-throw
45 throw ioe;
46 }
47 // Successful accept, reset the error delay
48 errorDelay = 0;
49
50 // Configure the socket
51 if (running && !paused && setSocketOptions(socket)) {
52 // Hand this socket off to an appropriate processor
53 if (!processSocket(socket)) {
54 countDownConnection();
55 // Close socket right away
56 closeSocket(socket);
57 }
58 } else {
59 countDownConnection();
60 // Close socket right away
61 closeSocket(socket);
62 }
63 } catch (IOException x) {
64 if (running) {
65 log.error(sm.getString("endpoint.accept.fail"), x);
66 }
67 } catch (NullPointerException npe) {
68 if (running) {
69 log.error(sm.getString("endpoint.accept.fail"), npe);
70 }
71 } catch (Throwable t) {
72 ExceptionUtils.handleThrowable(t);
73 log.error(sm.getString("endpoint.accept.fail"), t);
74 }
75 }
76 state = AcceptorState.ENDED;
77 }
78 }
Что делает строка 39, так это отслеживает клиентское соединение в приведенном выше примере псевдокода.После прослушивания соединения (то есть браузер инициирует запрос к серверу) метод processSocket обрабатывает полученное соединение Socket в строке 53. Исходный код метода processSocket выглядит следующим образом:
1 protected boolean processSocket(Socket socket) {
2 // Process the request from this socket
3 try {
4 SocketWrapper<Socket> wrapper = new SocketWrapper<Socket>(socket);
5 wrapper.setKeepAliveLeft(getMaxKeepAliveRequests());
6 // During shutdown, executor may be null - avoid NPE
7 if (!running) {
8 return false;
9 }
10 getExecutor().execute(new SocketProcessor(wrapper));
11 } catch (RejectedExecutionException x) {
12 log.warn("Socket processing request was rejected for:"+socket,x);
13 return false;
14 } catch (Throwable t) {
15 ExceptionUtils.handleThrowable(t);
16 // This means we got an OOM or similar creating a thread, or that
17 // the pool and its queue are full
18 log.error(sm.getString("endpoint.process.fail"), t);
19 return false;
20 }
21 return true;
22 }
В этом методе Socket сначала помещается в SocketWrapper для внутренней обработки. Фокус находится на строке 10:getExecutor().execute(new SocketProcessor(wrapper))
. Если в соответствии с методом обработки в приведенном выше псевдокоде, в случае параллельных запросов, если один запрос не будет обработан, сервер не сможет немедленно ответить на другой запрос. Здесь сделано улучшение, то есть после получения Socket-соединения запускается другой поток для обработки соединения, чтобы текущий поток не блокировался.
Давайте проследим за потоком SocketProcessor, чтобы увидеть, как соединение Socket преобразуется во внутренний запрос в Tomcat 7. Взгляните на метод запуска этого потока:
1 @Override
2 public void run() {
3 boolean launch = false;
4 synchronized (socket) {
5 try {
6 SocketState state = SocketState.OPEN;
7
8 try {
9 // SSL handshake
10 serverSocketFactory.handshake(socket.getSocket());
11 } catch (Throwable t) {
12 ExceptionUtils.handleThrowable(t);
13 if (log.isDebugEnabled()) {
14 log.debug(sm.getString("endpoint.err.handshake"), t);
15 }
16 // Tell to close the socket
17 state = SocketState.CLOSED;
18 }
19
20 if ((state != SocketState.CLOSED)) {
21 if (status == null) {
22 state = handler.process(socket, SocketStatus.OPEN);
23 } else {
24 state = handler.process(socket,status);
25 }
26 }
27 if (state == SocketState.CLOSED) {
28 // Close socket
29 if (log.isTraceEnabled()) {
30 log.trace("Closing socket:"+socket);
31 }
32 countDownConnection();
33 try {
34 socket.getSocket().close();
35 } catch (IOException e) {
36 // Ignore
37 }
38 } else if (state == SocketState.OPEN ||
39 state == SocketState.UPGRADING ||
40 state == SocketState.UPGRADED){
41 socket.setKeptAlive(true);
42 socket.access();
43 launch = true;
44 } else if (state == SocketState.LONG) {
45 socket.access();
46 waitingRequests.add(socket);
47 }
48 } finally {
49 if (launch) {
50 try {
51 getExecutor().execute(new SocketProcessor(socket, SocketStatus.OPEN));
52 } catch (RejectedExecutionException x) {
53 log.warn("Socket reprocessing request was rejected for:"+socket,x);
54 try {
55 //unable to handle connection at this time
56 handler.process(socket, SocketStatus.DISCONNECT);
57 } finally {
58 countDownConnection();
59 }
60
61
62 } catch (NullPointerException npe) {
63 if (running) {
64 log.error(sm.getString("endpoint.launch.fail"),
65 npe);
66 }
67 }
68 }
69 }
70 }
71 socket = null;
72 // Finish up this request
73 }
74
75 }
По умолчанию он перейдет к строке 22 и вызовет метод процесса объекта-обработчика, где объект-обработчик фактически является экземпляром класса 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 класса, но не в текущем определении.Этот метод находится в его родительском классе.org.apache.coyote.AbstractProtocol.AbstractConnectionHandler
определено в:
1 public SocketState process(SocketWrapper<S> socket,
2 SocketStatus status) {
3 Processor<S> processor = connections.remove(socket.getSocket());
4
5 if (status == SocketStatus.DISCONNECT && processor == null) {
6 //nothing more to be done endpoint requested a close
7 //and there are no object associated with this connection
8 return SocketState.CLOSED;
9 }
10
11 socket.setAsync(false);
12
13 try {
14 if (processor == null) {
15 processor = recycledProcessors.poll();
16 }
17 if (processor == null) {
18 processor = createProcessor();
19 }
20
21 initSsl(socket, processor);
22
23 SocketState state = SocketState.CLOSED;
24 do {
25 if (status == SocketStatus.DISCONNECT &&
26 !processor.isComet()) {
27 // Do nothing here, just wait for it to get recycled
28 // Don't do this for Comet we need to generate an end
29 // event (see BZ 54022)
30 } else if (processor.isAsync() ||
31 state == SocketState.ASYNC_END) {
32 state = processor.asyncDispatch(status);
33 } else if (processor.isComet()) {
34 state = processor.event(status);
35 } else if (processor.isUpgrade()) {
36 state = processor.upgradeDispatch();
37 } else {
38 state = processor.process(socket);
39 }
40
41 if (state != SocketState.CLOSED && processor.isAsync()) {
42 state = processor.asyncPostProcess();
43 }
44
45 if (state == SocketState.UPGRADING) {
46 // Get the UpgradeInbound handler
47 UpgradeInbound inbound = processor.getUpgradeInbound();
48 // Release the Http11 processor to be re-used
49 release(socket, processor, false, false);
50 // Create the light-weight upgrade processor
51 processor = createUpgradeProcessor(socket, inbound);
52 inbound.onUpgradeComplete();
53 }
54 } while (state == SocketState.ASYNC_END ||
55 state == SocketState.UPGRADING);
56
57 if (state == SocketState.LONG) {
58 // In the middle of processing a request/response. Keep the
59 // socket associated with the processor. Exact requirements
60 // depend on type of long poll
61 longPoll(socket, processor);
62 } else if (state == SocketState.OPEN) {
63 // In keep-alive but between requests. OK to recycle
64 // processor. Continue to poll for the next request.
65 release(socket, processor, false, true);
66 } else if (state == SocketState.SENDFILE) {
67 // Sendfile in progress. If it fails, the socket will be
68 // closed. If it works, the socket will be re-added to the
69 // poller
70 release(socket, processor, false, false);
71 } else if (state == SocketState.UPGRADED) {
72 // Need to keep the connection associated with the processor
73 longPoll(socket, processor);
74 } else {
75 // Connection closed. OK to recycle the processor.
76 if (!(processor instanceof UpgradeProcessor)) {
77 release(socket, processor, true, false);
78 }
79 }
80 return state;
81 } catch(java.net.SocketException e) {
82 // SocketExceptions are normal
83 getLog().debug(sm.getString(
84 "abstractConnectionHandler.socketexception.debug"), e);
85 } catch (java.io.IOException e) {
86 // IOExceptions are normal
87 getLog().debug(sm.getString(
88 "abstractConnectionHandler.ioexception.debug"), e);
89 }
90 // Future developers: if you discover any other
91 // rare-but-nonfatal exceptions, catch them here, and log as
92 // above.
93 catch (Throwable e) {
94 ExceptionUtils.handleThrowable(e);
95 // any other exception or error is odd. Here we log it
96 // with "ERROR" level, so it will show up even on
97 // less-than-verbose logs.
98 getLog().error(
99 sm.getString("abstractConnectionHandler.error"), e);
100 }
101 // Don't try to add upgrade processors back into the pool
102 if (!(processor instanceof UpgradeProcessor)) {
103 release(socket, processor, true, false);
104 }
105 return SocketState.CLOSED;
106 }
Фокус находится на строке 38, вызывающей метод процесса процессора для обработки сокета. Объект процессора создается методом createProcessor в строке 18. Метод createProcessor является абстрактным методом в текущем классе.По умолчанию вызывается конкретный класс реализации в упомянутом выше классе Http11ConnectionHandler:
1 @Override
2 protected Http11Processor createProcessor() {
3 Http11Processor processor = new Http11Processor(
4 proto.getMaxHttpHeaderSize(), (JIoEndpoint)proto.endpoint,
5 proto.getMaxTrailerSize());
6 processor.setAdapter(proto.adapter);
7 processor.setMaxKeepAliveRequests(proto.getMaxKeepAliveRequests());
8 processor.setKeepAliveTimeout(proto.getKeepAliveTimeout());
9 processor.setConnectionUploadTimeout(
10 proto.getConnectionUploadTimeout());
11 processor.setDisableUploadTimeout(proto.getDisableUploadTimeout());
12 processor.setCompressionMinSize(proto.getCompressionMinSize());
13 processor.setCompression(proto.getCompression());
14 processor.setNoCompressionUserAgents(proto.getNoCompressionUserAgents());
15 processor.setCompressableMimeTypes(proto.getCompressableMimeTypes());
16 processor.setRestrictedUserAgents(proto.getRestrictedUserAgents());
17 processor.setSocketBuffer(proto.getSocketBuffer());
18 processor.setMaxSavePostSize(proto.getMaxSavePostSize());
19 processor.setServer(proto.getServer());
20 processor.setDisableKeepAlivePercentage(
21 proto.getDisableKeepAlivePercentage());
22 register(processor);
23 return processor;
24 }
В настоящее время объект процессора является экземпляром класса Http11Processor.Если вы посмотрите на метод processing.process, упомянутый в предыдущем абзаце, он в конечном итоге будет выполнен в родительском классе класса Http11Processor (поскольку метод процесса не является определено в этом классе).org.apache.coyote.http11.AbstractHttp11Processor
метод обработки в .
Для простоты понимания на следующей диаграмме последовательности показан процесс вызова ключевого метода от метода run потока Acceptor до метода процесса класса AbstractHttp11Processor:
Затем проанализируйте метод процесса класса org.apache.coyote.http11.AbstractHttp11Processor:
1 @Override
2 public SocketState process(SocketWrapper<S> socketWrapper)
3 throws IOException {
4 RequestInfo rp = request.getRequestProcessor();
5 rp.setStage(org.apache.coyote.Constants.STAGE_PARSE);
6
7 // Setting up the I/O
8 setSocketWrapper(socketWrapper);
9 getInputBuffer().init(socketWrapper, endpoint);
10 getOutputBuffer().init(socketWrapper, endpoint);
11
12 // Flags
13 error = false;
14 keepAlive = true;
15 comet = false;
16 openSocket = false;
17 sendfileInProgress = false;
18 readComplete = true;
19 if (endpoint.getUsePolling()) {
20 keptAlive = false;
21 } else {
22 keptAlive = socketWrapper.isKeptAlive();
23 }
24
25 if (disableKeepAlive()) {
26 socketWrapper.setKeepAliveLeft(0);
27 }
28
29 while (!error && keepAlive && !comet && !isAsync() &&
30 upgradeInbound == null && !endpoint.isPaused()) {
31
32 // Parsing the request header
33 try {
34 setRequestLineReadTimeout();
35
36 if (!getInputBuffer().parseRequestLine(keptAlive)) {
37 if (handleIncompleteRequestLineRead()) {
38 break;
39 }
40 }
41
42 if (endpoint.isPaused()) {
43 // 503 - Service unavailable
44 response.setStatus(503);
45 error = true;
46 } else {
47 // Make sure that connectors that are non-blocking during
48 // header processing (NIO) only set the start time the first
49 // time a request is processed.
50 if (request.getStartTime() < 0) {
51 request.setStartTime(System.currentTimeMillis());
52 }
53 keptAlive = true;
54 // Set this every time in case limit has been changed via JMX
55 request.getMimeHeaders().setLimit(endpoint.getMaxHeaderCount());
56 // Currently only NIO will ever return false here
57 if (!getInputBuffer().parseHeaders()) {
58 // We've read part of the request, don't recycle it
59 // instead associate it with the socket
60 openSocket = true;
61 readComplete = false;
62 break;
63 }
64 if (!disableUploadTimeout) {
65 setSocketTimeout(connectionUploadTimeout);
66 }
67 }
68 } catch (IOException e) {
69 if (getLog().isDebugEnabled()) {
70 getLog().debug(
71 sm.getString("http11processor.header.parse"), e);
72 }
73 error = true;
74 break;
75 } catch (Throwable t) {
76 ExceptionUtils.handleThrowable(t);
77 UserDataHelper.Mode logMode = userDataHelper.getNextMode();
78 if (logMode != null) {
79 String message = sm.getString(
80 "http11processor.header.parse");
81 switch (logMode) {
82 case INFO_THEN_DEBUG:
83 message += sm.getString(
84 "http11processor.fallToDebug");
85 //$FALL-THROUGH$
86 case INFO:
87 getLog().info(message);
88 break;
89 case DEBUG:
90 getLog().debug(message);
91 }
92 }
93 // 400 - Bad Request
94 response.setStatus(400);
95 adapter.log(request, response, 0);
96 error = true;
97 }
98
99 if (!error) {
100 // Setting up filters, and parse some request headers
101 rp.setStage(org.apache.coyote.Constants.STAGE_PREPARE);
102 try {
103 prepareRequest();
104 } catch (Throwable t) {
105 ExceptionUtils.handleThrowable(t);
106 if (getLog().isDebugEnabled()) {
107 getLog().debug(sm.getString(
108 "http11processor.request.prepare"), t);
109 }
110 // 400 - Internal Server Error
111 response.setStatus(400);
112 adapter.log(request, response, 0);
113 error = true;
114 }
115 }
116
117 if (maxKeepAliveRequests == 1) {
118 keepAlive = false;
119 } else if (maxKeepAliveRequests > 0 &&
120 socketWrapper.decrementKeepAlive() <= 0) {
121 keepAlive = false;
122 }
123
124 // Process the request in the adapter
125 if (!error) {
126 try {
127 rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE);
128 adapter.service(request, response);
129 // Handle when the response was committed before a serious
130 // error occurred. Throwing a ServletException should both
131 // set the status to 500 and set the errorException.
132 // If we fail here, then the response is likely already
133 // committed, so we can't try and set headers.
134 if(keepAlive && !error) { // Avoid checking twice.
135 error = response.getErrorException() != null ||
136 (!isAsync() &&
137 statusDropsConnection(response.getStatus()));
138 }
139 setCometTimeouts(socketWrapper);
140 } catch (InterruptedIOException e) {
141 error = true;
142 } catch (HeadersTooLargeException e) {
143 error = true;
144 // The response should not have been committed but check it
145 // anyway to be safe
146 if (!response.isCommitted()) {
147 response.reset();
148 response.setStatus(500);
149 response.setHeader("Connection", "close");
150 }
151 } catch (Throwable t) {
152 ExceptionUtils.handleThrowable(t);
153 getLog().error(sm.getString(
154 "http11processor.request.process"), t);
155 // 500 - Internal Server Error
156 response.setStatus(500);
157 adapter.log(request, response, 0);
158 error = true;
159 }
160 }
161
162 // Finish the handling of the request
163 rp.setStage(org.apache.coyote.Constants.STAGE_ENDINPUT);
164
165 if (!isAsync() && !comet) {
166 if (error) {
167 // If we know we are closing the connection, don't drain
168 // input. This way uploading a 100GB file doesn't tie up the
169 // thread if the servlet has rejected it.
170 getInputBuffer().setSwallowInput(false);
171 }
172 endRequest();
173 }
174
175 rp.setStage(org.apache.coyote.Constants.STAGE_ENDOUTPUT);
176
177 // If there was an error, make sure the request is counted as
178 // and error, and update the statistics counter
179 if (error) {
180 response.setStatus(500);
181 }
182 request.updateCounters();
183
184 if (!isAsync() && !comet || error) {
185 getInputBuffer().nextRequest();
186 getOutputBuffer().nextRequest();
187 }
188
189 if (!disableUploadTimeout) {
190 if(endpoint.getSoTimeout() > 0) {
191 setSocketTimeout(endpoint.getSoTimeout());
192 } else {
193 setSocketTimeout(0);
194 }
195 }
196
197 rp.setStage(org.apache.coyote.Constants.STAGE_KEEPALIVE);
198
199 if (breakKeepAliveLoop(socketWrapper)) {
200 break;
201 }
202 }
203
204 rp.setStage(org.apache.coyote.Constants.STAGE_ENDED);
205
206 if (error || endpoint.isPaused()) {
207 return SocketState.CLOSED;
208 } else if (isAsync() || comet) {
209 return SocketState.LONG;
210 } else if (isUpgrade()) {
211 return SocketState.UPGRADING;
212 } else {
213 if (sendfileInProgress) {
214 return SocketState.SENDFILE;
215 } else {
216 if (openSocket) {
217 if (readComplete) {
218 return SocketState.OPEN;
219 } else {
220 return SocketState.LONG;
221 }
222 } else {
223 return SocketState.CLOSED;
224 }
225 }
226 }
227 }
Из этого метода хорошо виден процесс анализа запроса: строки с 7 по 10 получают входной и выходной потоки из сокета, строки с 32 по 97 анализируют строку запроса и заголовок запроса, а строки с 99 по 115 проверяют и анализируют запрашивать свойства заголовка в , строки 125–160 вызывают метод службы адаптера, а строка 172 запрашивает окончание обработки.
Выше приведен общий процесс разбора запросов по протоколу Http. Чтобы понять такие термины, как строка запроса и заголовок запроса, упомянутые выше, вы должны быть знакомы с протоколом Http.Вот краткое введение в формат стандартных информационных данных запроса в протоколе Http:
Запрашиваемая информация включает в себя следующие три элемента
- строка запроса
Например, GET /images/logo.gif HTTP/1.1 означает запрос файла logo.gif из каталога /images.
- заголовок запроса, пустая строка
Например, 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
конец персонажа.
В строке 36 кода процесса класса AbstractHttp11Processor, указанного выше, вызывается абстрактный метод 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);
}
Таким образом, строка 36 метода обработки класса AbstractHttp11Processor getInputBuffer().parseRequestLine() будет вызыватьorg.apache.coyote.http11.InternalInputBuffer
Метод parseRequestLine в классе:
1 public boolean parseRequestLine(boolean useAvailableDataOnly)
2
3 throws IOException {
4
5 int start = 0;
6
7 //
8 // Skipping blank lines
9 //
10
11 byte chr = 0;
12 do {
13
14 // Read new bytes if needed
15 if (pos >= lastValid) {
16 if (!fill())
17 throw new EOFException(sm.getString("iib.eof.error"));
18 }
19
20 chr = buf[pos++];
21
22 } while ((chr == Constants.CR) || (chr == Constants.LF));
23
24 pos--;
25
26 // Mark the current buffer position
27 start = pos;
28
29 //
30 // Reading the method name
31 // Method name is always US-ASCII
32 //
33
34 boolean space = false;
35
36 while (!space) {
37
38 // Read new bytes if needed
39 if (pos >= lastValid) {
40 if (!fill())
41 throw new EOFException(sm.getString("iib.eof.error"));
42 }
43
44 // Spec says no CR or LF in method name
45 if (buf[pos] == Constants.CR || buf[pos] == Constants.LF) {
46 throw new IllegalArgumentException(
47 sm.getString("iib.invalidmethod"));
48 }
49 // Spec says single SP but it also says be tolerant of HT
50 if (buf[pos] == Constants.SP || buf[pos] == Constants.HT) {
51 space = true;
52 request.method().setBytes(buf, start, pos - start);
53 }
54
55 pos++;
56
57 }
58
59
60 // Spec says single SP but also says be tolerant of multiple and/or HT
61 while (space) {
62 // Read new bytes if needed
63 if (pos >= lastValid) {
64 if (!fill())
65 throw new EOFException(sm.getString("iib.eof.error"));
66 }
67 if (buf[pos] == Constants.SP || buf[pos] == Constants.HT) {
68 pos++;
69 } else {
70 space = false;
71 }
72 }
73
74 // Mark the current buffer position
75 start = pos;
76 int end = 0;
77 int questionPos = -1;
78
79 //
80 // Reading the URI
81 //
82
83 boolean eol = false;
84
85 while (!space) {
86
87 // Read new bytes if needed
88 if (pos >= lastValid) {
89 if (!fill())
90 throw new EOFException(sm.getString("iib.eof.error"));
91 }
92
93 // Spec says single SP but it also says be tolerant of HT
94 if (buf[pos] == Constants.SP || buf[pos] == Constants.HT) {
95 space = true;
96 end = pos;
97 } else if ((buf[pos] == Constants.CR)
98 || (buf[pos] == Constants.LF)) {
99 // HTTP/0.9 style request
100 eol = true;
101 space = true;
102 end = pos;
103 } else if ((buf[pos] == Constants.QUESTION)
104 && (questionPos == -1)) {
105 questionPos = pos;
106 }
107
108 pos++;
109
110 }
111
112 request.unparsedURI().setBytes(buf, start, end - start);
113 if (questionPos >= 0) {
114 request.queryString().setBytes(buf, questionPos + 1,
115 end - questionPos - 1);
116 request.requestURI().setBytes(buf, start, questionPos - start);
117 } else {
118 request.requestURI().setBytes(buf, start, end - start);
119 }
120
121 // Spec says single SP but also says be tolerant of multiple and/or HT
122 while (space) {
123 // Read new bytes if needed
124 if (pos >= lastValid) {
125 if (!fill())
126 throw new EOFException(sm.getString("iib.eof.error"));
127 }
128 if (buf[pos] == Constants.SP || buf[pos] == Constants.HT) {
129 pos++;
130 } else {
131 space = false;
132 }
133 }
134
135 // Mark the current buffer position
136 start = pos;
137 end = 0;
138
139 //
140 // Reading the protocol
141 // Protocol is always US-ASCII
142 //
143
144 while (!eol) {
145
146 // Read new bytes if needed
147 if (pos >= lastValid) {
148 if (!fill())
149 throw new EOFException(sm.getString("iib.eof.error"));
150 }
151
152 if (buf[pos] == Constants.CR) {
153 end = pos;
154 } else if (buf[pos] == Constants.LF) {
155 if (end == 0)
156 end = pos;
157 eol = true;
158 }
159
160 pos++;
161
162 }
163
164 if ((end - start) > 0) {
165 request.protocol().setBytes(buf, start, end - start);
166 } else {
167 request.protocol().setString("");
168 }
169
170 return true;
171
172 }
Сначала посмотрите на строку 16 в этом методе, которая вызывает метод заполнения текущего класса:
protected boolean fill() throws IOException {
return fill(true);
}
Он вызывает перегруженный метод fill :
1 protected boolean fill(boolean block) throws IOException {
2
3 int nRead = 0;
4
5 if (parsingHeader) {
6
7 if (lastValid == buf.length) {
8 throw new IllegalArgumentException
9 (sm.getString("iib.requestheadertoolarge.error"));
10 }
11
12 nRead = inputStream.read(buf, pos, buf.length - lastValid);
13 if (nRead > 0) {
14 lastValid = pos + nRead;
15 }
16
17 } else {
18
19 if (buf.length - end < 4500) {
20 // In this case, the request header was really large, so we allocate a
21 // brand new one; the old one will get GCed when subsequent requests
22 // clear all references
23 buf = new byte[buf.length];
24 end = 0;
25 }
26 pos = end;
27 lastValid = pos;
28 nRead = inputStream.read(buf, pos, buf.length - lastValid);
29 if (nRead > 0) {
30 lastValid = pos + nRead;
31 }
32
33 }
34
35 return (nRead > 0);
36
37 } }
Здесь вы можете увидеть чтение данных из входного потока в буфер buf. В соответствии с форматом данных строки запроса, указанным выше, тип запроса (метод), URI запроса и версия Http будут получены по порядку из потока символов. Конкретный процесс реализации выглядит следующим образом:
существуетorg.apache.coyote.http11.InternalInputBuffer
Метод parseRequestLine в классе, строки с 34 по 57, в соответствии с форматом протокола заголовка запроса, берет из него байтовые данные, представляющие метод запроса, и устанавливает их во встроенную переменную экземпляра request. Строки с 60 по 72 анализируют пробельный байт SP между методом и uri, а строки с 83 по 119 считывают байтовые данные, представляющие запрошенный URI, и помещают их в переменную запроса. Строки с 122 по 133 анализируют пробельный байт SP между uri и версией протокола http, а строки с 144 по 168 считывают байтовые данные, представляющие запрошенную версию протокола HTTP, и помещают их в переменную запроса.
Выше приведена часть реализации кода парсинга строки запроса (строки запроса) по протоколу Http.Для части парсинга заголовка запроса смотрите метод parseHeader класса InternalInputBuffer, повторять их не буду.
До сих пор вы видели, как извлекать запрошенные данные из соединения Socket в Tomcat и преобразовывать эти исходные данные символьного потока во встроенные объекты Tomcat, которые можно изначально понять.org.apache.coyote.Request
из.