说明:本篇强调分析对象关系,而不是类关系,主要分析为何HttpProcessor还需要依赖上篇中的连接器?为何res/对象交由链接器创建且res/req本身也依赖连接器?
- org.apache.catalina.connector.http.HttpProcessor
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 package org.apache.catalina.connector.http; 2 3 import java.io.EOFException; 4 import java.io.IOException; 5 import java.io.InputStream; 6 import java.io.InterruptedIOException; 7 import java.io.OutputStream; 8 import java.net.Socket; 9 import java.util.ArrayList; 10 import java.util.Iterator; 11 import java.util.Locale; 12 import java.util.TreeMap; 13 import javax.servlet.ServletException; 14 import javax.servlet.http.Cookie; 15 import javax.servlet.http.HttpServletRequest; 16 import javax.servlet.http.HttpServletResponse; 17 import org.apache.catalina.Lifecycle; 18 import org.apache.catalina.LifecycleException; 19 import org.apache.catalina.LifecycleListener; 20 import org.apache.catalina.Logger; 21 import org.apache.catalina.connector.http.DefaultHeaders; 22 import org.apache.catalina.connector.http.HttpConnector; 23 import org.apache.catalina.connector.http.HttpHeader; 24 import org.apache.catalina.connector.http.HttpRequestImpl; 25 import org.apache.catalina.connector.http.HttpRequestLine; 26 import org.apache.catalina.connector.http.HttpResponseImpl; 27 import org.apache.catalina.connector.http.SocketInputStream; 28 import org.apache.catalina.util.FastHttpDateFormat; 29 import org.apache.catalina.util.LifecycleSupport; 30 import org.apache.catalina.util.RequestUtil; 31 import org.apache.catalina.util.ServerInfo; 32 import org.apache.catalina.util.StringManager; 33 import org.apache.catalina.util.StringParser; 34 35 final class HttpProcessor implements Lifecycle, Runnable { 36 private static final String SERVER_INFO = ServerInfo.getServerInfo() + " (HTTP/1.1 Connector)"; 37 private boolean available = false; 38 private HttpConnector connector = null; 39 private int debug = 0; 40 private int id = 0; 41 private LifecycleSupport lifecycle = new LifecycleSupport(this); 42 private static final String match = ";jsessionid="; 43 private static final char[] SESSION_ID = ";jsessionid=".toCharArray(); 44 private StringParser parser = new StringParser(); 45 private String proxyName = null; 46 private int proxyPort = 0; 47 private HttpRequestImpl request = null; 48 private HttpResponseImpl response = null; 49 private int serverPort = 0; 50 protected StringManager sm = StringManager.getManager("org.apache.catalina.connector.http"); 51 private Socket socket = null; 52 private boolean started = false; 53 private boolean stopped = false; 54 private Thread thread = null; 55 private String threadName = null; 56 private Object threadSync = new Object(); 57 private boolean keepAlive = false; 58 private boolean http11 = true; 59 private boolean sendAck = false; 60 private static final byte[] ack = (new String("HTTP/1.1 100 Continue\r\n\r\n")).getBytes(); 61 private static final byte[] CRLF = (new String("\r\n")).getBytes(); 62 private HttpRequestLine requestLine = new HttpRequestLine(); 63 private int status = 0; 64 65 public HttpProcessor(HttpConnector connector, int id) { 66 this.connector = connector; 67 this.debug = connector.getDebug(); 68 this.id = id; 69 this.proxyName = connector.getProxyName(); 70 this.proxyPort = connector.getProxyPort(); 71 this.request = (HttpRequestImpl) connector.createRequest(); 72 this.response = (HttpResponseImpl) connector.createResponse(); 73 this.serverPort = connector.getPort(); 74 this.threadName = "HttpProcessor[" + connector.getPort() + "][" + id + "]"; 75 } 76 77 public String toString() { 78 return this.threadName; 79 } 80 81 synchronized void assign(Socket socket) { 82 while (this.available) { 83 try { 84 this.wait(); 85 } catch (InterruptedException arg2) { 86 ; 87 } 88 } 89 90 this.socket = socket; 91 this.available = true; 92 this.notifyAll(); 93 if (this.debug >= 1 && socket != null) { 94 this.log(" An incoming request is being assigned"); 95 } 96 97 } 98 99 private synchronized Socket await() { 100 while (!this.available) { 101 try { 102 this.wait(); 103 } catch (InterruptedException arg1) { 104 ; 105 } 106 } 107 108 Socket socket = this.socket; 109 this.available = false; 110 this.notifyAll(); 111 if (this.debug >= 1 && socket != null) { 112 this.log(" The incoming request has been awaited"); 113 } 114 115 return socket; 116 } 117 118 private void log(String message) { 119 Logger logger = this.connector.getContainer().getLogger(); 120 if (logger != null) { 121 logger.log(this.threadName + " " + message); 122 } 123 124 } 125 126 private void log(String message, Throwable throwable) { 127 Logger logger = this.connector.getContainer().getLogger(); 128 if (logger != null) { 129 logger.log(this.threadName + " " + message, throwable); 130 } 131 132 } 133 134 private void parseAcceptLanguage(String value) { 135 TreeMap locales = new TreeMap(); 136 int white = value.indexOf(32); 137 if (white < 0) { 138 white = value.indexOf(9); 139 } 140 141 int keys; 142 int key; 143 if (white >= 0) { 144 StringBuffer length = new StringBuffer(); 145 keys = value.length(); 146 147 for (key = 0; key < keys; ++key) { 148 char list = value.charAt(key); 149 if (list != 32 && list != 9) { 150 length.append(list); 151 } 152 } 153 154 value = length.toString(); 155 } 156 157 this.parser.setString(value); 158 int arg18 = this.parser.getLength(); 159 160 while (true) { 161 keys = this.parser.getIndex(); 162 if (keys >= arg18) { 163 Iterator arg19 = locales.keySet().iterator(); 164 165 while (arg19.hasNext()) { 166 Double arg20 = (Double) arg19.next(); 167 ArrayList arg22 = (ArrayList) locales.get(arg20); 168 169 Locale locale; 170 for (Iterator arg23 = arg22.iterator(); arg23.hasNext(); this.request.addLocale(locale)) { 171 locale = (Locale) arg23.next(); 172 if (this.debug >= 1) { 173 this.log(" Adding locale \'" + locale + "\'"); 174 } 175 } 176 } 177 178 return; 179 } 180 181 key = this.parser.findChar(','); 182 String arg21 = this.parser.extract(keys, key).trim(); 183 this.parser.advance(); 184 double values = 1.0D; 185 int semi = arg21.indexOf(";q="); 186 if (semi >= 0) { 187 try { 188 values = Double.parseDouble(arg21.substring(semi + 3)); 189 } catch (NumberFormatException arg17) { 190 values = 0.0D; 191 } 192 193 arg21 = arg21.substring(0, semi); 194 } 195 196 if (values >= 5.0E-5D && !"*".equals(arg21)) { 197 String language = null; 198 String country = null; 199 String variant = null; 200 int dash = arg21.indexOf(45); 201 if (dash < 0) { 202 language = arg21; 203 country = ""; 204 variant = ""; 205 } else { 206 language = arg21.substring(0, dash); 207 country = arg21.substring(dash + 1); 208 int locale1 = country.indexOf(45); 209 if (locale1 > 0) { 210 String key1 = country.substring(0, locale1); 211 variant = country.substring(locale1 + 1); 212 country = key1; 213 } else { 214 variant = ""; 215 } 216 } 217 218 Locale arg24 = new Locale(language, country, variant); 219 Double arg25 = new Double(-values); 220 ArrayList values1 = (ArrayList) locales.get(arg25); 221 if (values1 == null) { 222 values1 = new ArrayList(); 223 locales.put(arg25, values1); 224 } 225 226 values1.add(arg24); 227 } 228 } 229 } 230 231 private void parseConnection(Socket socket) throws IOException, ServletException { 232 if (this.debug >= 2) { 233 this.log(" parseConnection: address=" + socket.getInetAddress() + ", port=" + this.connector.getPort()); 234 } 235 236 this.request.setInet(socket.getInetAddress()); 237 if (this.proxyPort != 0) { 238 this.request.setServerPort(this.proxyPort); 239 } else { 240 this.request.setServerPort(this.serverPort); 241 } 242 243 this.request.setSocket(socket); 244 } 245 246 private void parseHeaders(SocketInputStream input) throws IOException, ServletException { 247 while (true) { 248 HttpHeader header = this.request.allocateHeader(); 249 input.readHeader(header); 250 if (header.nameEnd == 0) { 251 if (header.valueEnd == 0) { 252 return; 253 } 254 255 throw new ServletException(this.sm.getString("httpProcessor.parseHeaders.colon")); 256 } 257 258 String value = new String(header.value, 0, header.valueEnd); 259 if (this.debug >= 1) { 260 this.log(" Header " + new String(header.name, 0, header.nameEnd) + " = " + value); 261 } 262 263 if (header.equals(DefaultHeaders.AUTHORIZATION_NAME)) { 264 this.request.setAuthorization(value); 265 } else if (header.equals(DefaultHeaders.ACCEPT_LANGUAGE_NAME)) { 266 this.parseAcceptLanguage(value); 267 } else { 268 int port; 269 if (header.equals(DefaultHeaders.COOKIE_NAME)) { 270 Cookie[] n = RequestUtil.parseCookieHeader(value); 271 272 for (port = 0; port < n.length; ++port) { 273 if (n[port].getName().equals("JSESSIONID") && !this.request.isRequestedSessionIdFromCookie()) { 274 this.request.setRequestedSessionId(n[port].getValue()); 275 this.request.setRequestedSessionCookie(true); 276 this.request.setRequestedSessionURL(false); 277 if (this.debug >= 1) { 278 this.log(" Requested cookie session id is " 279 + ((HttpServletRequest) this.request.getRequest()).getRequestedSessionId()); 280 } 281 } 282 283 if (this.debug >= 1) { 284 this.log(" Adding cookie " + n[port].getName() + "=" + n[port].getValue()); 285 } 286 287 this.request.addCookie(n[port]); 288 } 289 } else { 290 int arg9; 291 if (header.equals(DefaultHeaders.CONTENT_LENGTH_NAME)) { 292 boolean arg8 = true; 293 294 try { 295 arg9 = Integer.parseInt(value); 296 } catch (Exception arg7) { 297 throw new ServletException(this.sm.getString("httpProcessor.parseHeaders.contentLength")); 298 } 299 300 this.request.setContentLength(arg9); 301 } else if (header.equals(DefaultHeaders.CONTENT_TYPE_NAME)) { 302 this.request.setContentType(value); 303 } else if (header.equals(DefaultHeaders.HOST_NAME)) { 304 arg9 = value.indexOf(58); 305 if (arg9 < 0) { 306 if (this.connector.getScheme().equals("http")) { 307 this.request.setServerPort(80); 308 } else if (this.connector.getScheme().equals("https")) { 309 this.request.setServerPort(443); 310 } 311 312 if (this.proxyName != null) { 313 this.request.setServerName(this.proxyName); 314 } else { 315 this.request.setServerName(value); 316 } 317 } else { 318 if (this.proxyName != null) { 319 this.request.setServerName(this.proxyName); 320 } else { 321 this.request.setServerName(value.substring(0, arg9).trim()); 322 } 323 324 if (this.proxyPort != 0) { 325 this.request.setServerPort(this.proxyPort); 326 } else { 327 boolean arg10 = true; 328 329 try { 330 port = Integer.parseInt(value.substring(arg9 + 1).trim()); 331 } catch (Exception arg6) { 332 throw new ServletException( 333 this.sm.getString("httpProcessor.parseHeaders.portNumber")); 334 } 335 336 this.request.setServerPort(port); 337 } 338 } 339 } else if (header.equals(DefaultHeaders.CONNECTION_NAME)) { 340 if (header.valueEquals(DefaultHeaders.CONNECTION_CLOSE_VALUE)) { 341 this.keepAlive = false; 342 this.response.setHeader("Connection", "close"); 343 } 344 } else if (header.equals(DefaultHeaders.EXPECT_NAME)) { 345 if (!header.valueEquals(DefaultHeaders.EXPECT_100_VALUE)) { 346 throw new ServletException( 347 this.sm.getString("httpProcessor.parseHeaders.unknownExpectation")); 348 } 349 350 this.sendAck = true; 351 } else if (header.equals(DefaultHeaders.TRANSFER_ENCODING_NAME)) { 352 ; 353 } 354 } 355 } 356 357 this.request.nextHeader(); 358 } 359 } 360 361 private void parseRequest(SocketInputStream input, OutputStream output) throws IOException, ServletException { 362 input.readRequestLine(this.requestLine); 363 this.status = 1; 364 String method = new String(this.requestLine.method, 0, this.requestLine.methodEnd); 365 String uri = null; 366 String protocol = new String(this.requestLine.protocol, 0, this.requestLine.protocolEnd); 367 if (protocol.length() == 0) { 368 protocol = "HTTP/0.9"; 369 } 370 371 if (protocol.equals("HTTP/1.1")) { 372 this.http11 = true; 373 this.sendAck = false; 374 } else { 375 this.http11 = false; 376 this.sendAck = false; 377 this.keepAlive = false; 378 } 379 380 if (method.length() < 1) { 381 throw new ServletException(this.sm.getString("httpProcessor.parseRequest.method")); 382 } else if (this.requestLine.uriEnd < 1) { 383 throw new ServletException(this.sm.getString("httpProcessor.parseRequest.uri")); 384 } else { 385 int question = this.requestLine.indexOf("?"); 386 if (question >= 0) { 387 this.request.setQueryString( 388 new String(this.requestLine.uri, question + 1, this.requestLine.uriEnd - question - 1)); 389 if (this.debug >= 1) { 390 this.log(" Query string is " + ((HttpServletRequest) this.request.getRequest()).getQueryString()); 391 } 392 393 uri = new String(this.requestLine.uri, 0, question); 394 } else { 395 this.request.setQueryString((String) null); 396 uri = new String(this.requestLine.uri, 0, this.requestLine.uriEnd); 397 } 398 399 int semicolon; 400 if (!uri.startsWith("/")) { 401 semicolon = uri.indexOf("://"); 402 if (semicolon != -1) { 403 semicolon = uri.indexOf(47, semicolon + 3); 404 if (semicolon == -1) { 405 uri = ""; 406 } else { 407 uri = uri.substring(semicolon); 408 } 409 } 410 } 411 412 semicolon = uri.indexOf(";jsessionid="); 413 String normalizedUri; 414 if (semicolon >= 0) { 415 normalizedUri = uri.substring(semicolon + ";jsessionid=".length()); 416 int semicolon2 = normalizedUri.indexOf(59); 417 if (semicolon2 >= 0) { 418 this.request.setRequestedSessionId(normalizedUri.substring(0, semicolon2)); 419 normalizedUri = normalizedUri.substring(semicolon2); 420 } else { 421 this.request.setRequestedSessionId(normalizedUri); 422 normalizedUri = ""; 423 } 424 425 this.request.setRequestedSessionURL(true); 426 uri = uri.substring(0, semicolon) + normalizedUri; 427 if (this.debug >= 1) { 428 this.log(" Requested URL session id is " 429 + ((HttpServletRequest) this.request.getRequest()).getRequestedSessionId()); 430 } 431 } else { 432 this.request.setRequestedSessionId((String) null); 433 this.request.setRequestedSessionURL(false); 434 } 435 436 normalizedUri = this.normalize(uri); 437 if (this.debug >= 1) { 438 this.log("Normalized: \'" + uri + "\' to \'" + normalizedUri + "\'"); 439 } 440 441 this.request.setMethod(method); 442 this.request.setProtocol(protocol); 443 if (normalizedUri != null) { 444 this.request.setRequestURI(normalizedUri); 445 } else { 446 this.request.setRequestURI(uri); 447 } 448 449 this.request.setSecure(this.connector.getSecure()); 450 this.request.setScheme(this.connector.getScheme()); 451 if (normalizedUri == null) { 452 this.log(" Invalid request URI: \'" + uri + "\'"); 453 throw new ServletException("Invalid URI: " + uri + "\'"); 454 } else { 455 if (this.debug >= 1) { 456 this.log(" Request is \'" + method + "\' for \'" + uri + "\' with protocol \'" + protocol + "\'"); 457 } 458 459 } 460 } 461 } 462 463 protected String normalize(String path) { 464 if (path == null) { 465 return null; 466 } else { 467 String normalized = path; 468 if (path.startsWith("/%7E") || path.startsWith("/%7e")) { 469 normalized = "/~" + path.substring(4); 470 } 471 472 if (normalized.indexOf("%25") < 0 && normalized.indexOf("%2F") < 0 && normalized.indexOf("%2E") < 0 473 && normalized.indexOf("%5C") < 0 && normalized.indexOf("%2f") < 0 && normalized.indexOf("%2e") < 0 474 && normalized.indexOf("%5c") < 0) { 475 if (normalized.equals("/.")) { 476 return "/"; 477 } else { 478 if (normalized.indexOf(92) >= 0) { 479 normalized = normalized.replace('\\', '/'); 480 } 481 482 if (!normalized.startsWith("/")) { 483 normalized = "/" + normalized; 484 } 485 486 while (true) { 487 int index = normalized.indexOf("//"); 488 if (index < 0) { 489 while (true) { 490 index = normalized.indexOf("/./"); 491 if (index < 0) { 492 while (true) { 493 index = normalized.indexOf("/../"); 494 if (index < 0) { 495 return normalized.indexOf("/...") >= 0 ? null : normalized; 496 } 497 498 if (index == 0) { 499 return null; 500 } 501 502 int index2 = normalized.lastIndexOf(47, index - 1); 503 normalized = normalized.substring(0, index2) + normalized.substring(index + 3); 504 } 505 } 506 507 normalized = normalized.substring(0, index) + normalized.substring(index + 2); 508 } 509 } 510 511 normalized = normalized.substring(0, index) + normalized.substring(index + 1); 512 } 513 } 514 } else { 515 return null; 516 } 517 } 518 } 519 520 private void ackRequest(OutputStream output) throws IOException { 521 if (this.sendAck) { 522 output.write(ack); 523 } 524 525 } 526 527 private void process(Socket socket) { 528 boolean ok = true; 529 boolean finishResponse = true; 530 SocketInputStream input = null; 531 OutputStream output = null; 532 533 try { 534 input = new SocketInputStream(socket.getInputStream(), this.connector.getBufferSize()); 535 } catch (Exception arg26) { 536 this.log("process.create", arg26); 537 ok = false; 538 } 539 540 this.keepAlive = true; 541 542 while (!this.stopped && ok && this.keepAlive) { 543 finishResponse = true; 544 545 try { 546 this.request.setStream(input); 547 this.request.setResponse(this.response); 548 output = socket.getOutputStream(); 549 this.response.setStream(output); 550 this.response.setRequest(this.request); 551 ((HttpServletResponse) this.response.getResponse()).setHeader("Server", SERVER_INFO); 552 } catch (Exception arg25) { 553 this.log("process.create", arg25); 554 ok = false; 555 } 556 557 try { 558 if (ok) { 559 this.parseConnection(socket); 560 this.parseRequest(input, output); 561 if (!this.request.getRequest().getProtocol().startsWith("HTTP/0")) { 562 this.parseHeaders(input); 563 } 564 565 if (this.http11) { 566 this.ackRequest(output); 567 if (this.connector.isChunkingAllowed()) { 568 this.response.setAllowChunking(true); 569 } 570 } 571 } 572 } catch (EOFException arg27) { 573 ok = false; 574 finishResponse = false; 575 } catch (ServletException arg28) { 576 ok = false; 577 578 try { 579 ((HttpServletResponse) this.response.getResponse()).sendError(400); 580 } catch (Exception arg24) { 581 ; 582 } 583 } catch (InterruptedIOException arg29) { 584 InterruptedIOException e = arg29; 585 if (this.debug > 1) { 586 try { 587 this.log("process.parse", e); 588 ((HttpServletResponse) this.response.getResponse()).sendError(400); 589 } catch (Exception arg23) { 590 ; 591 } 592 } 593 594 ok = false; 595 } catch (Exception arg30) { 596 Exception f = arg30; 597 598 try { 599 this.log("process.parse", f); 600 ((HttpServletResponse) this.response.getResponse()).sendError(400); 601 } catch (Exception arg22) { 602 ; 603 } 604 605 ok = false; 606 } 607 608 try { 609 this.response.setHeader("Date", FastHttpDateFormat.getCurrentDate()); 610 if (ok) { 611 this.connector.getContainer().invoke(this.request, this.response); 612 } 613 } catch (ServletException arg19) { 614 this.log("process.invoke", arg19); 615 616 try { 617 ((HttpServletResponse) this.response.getResponse()).sendError(500); 618 } catch (Exception arg18) { 619 ; 620 } 621 622 ok = false; 623 } catch (InterruptedIOException arg20) { 624 ok = false; 625 } catch (Throwable arg21) { 626 this.log("process.invoke", arg21); 627 628 try { 629 ((HttpServletResponse) this.response.getResponse()).sendError(500); 630 } catch (Exception arg17) { 631 ; 632 } 633 634 ok = false; 635 } 636 637 if (finishResponse) { 638 try { 639 this.response.finishResponse(); 640 } catch (IOException arg15) { 641 ok = false; 642 } catch (Throwable arg16) { 643 this.log("process.invoke", arg16); 644 ok = false; 645 } 646 647 try { 648 this.request.finishRequest(); 649 } catch (IOException arg13) { 650 ok = false; 651 } catch (Throwable arg14) { 652 this.log("process.invoke", arg14); 653 ok = false; 654 } 655 656 try { 657 if (output != null) { 658 output.flush(); 659 } 660 } catch (IOException arg12) { 661 ok = false; 662 } 663 } 664 665 if ("close".equals(this.response.getHeader("Connection"))) { 666 this.keepAlive = false; 667 } 668 669 this.status = 0; 670 this.request.recycle(); 671 this.response.recycle(); 672 } 673 674 try { 675 this.shutdownInput(input); 676 socket.close(); 677 } catch (IOException arg10) { 678 ; 679 } catch (Throwable arg11) { 680 this.log("process.invoke", arg11); 681 } 682 683 socket = null; 684 } 685 686 protected void shutdownInput(InputStream input) { 687 try { 688 int e = input.available(); 689 if (e > 0) { 690 input.skip((long) e); 691 } 692 } catch (Throwable arg2) { 693 ; 694 } 695 696 } 697 698 public void run() { 699 while (!this.stopped) { 700 Socket socket = this.await(); 701 if (socket != null) { 702 try { 703 this.process(socket); 704 } catch (Throwable arg4) { 705 this.log("process.invoke", arg4); 706 } 707 708 this.connector.recycle(this); 709 } 710 } 711 712 Object socket1 = this.threadSync; 713 synchronized (socket1) { 714 this.threadSync.notifyAll(); 715 } 716 } 717 718 private void threadStart() { 719 this.log(this.sm.getString("httpProcessor.starting")); 720 this.thread = new Thread(this, this.threadName); 721 this.thread.setDaemon(true); 722 this.thread.start(); 723 if (this.debug >= 1) { 724 this.log(" Background thread has been started"); 725 } 726 727 } 728 729 private void threadStop() { 730 this.log(this.sm.getString("httpProcessor.stopping")); 731 this.stopped = true; 732 this.assign((Socket) null); 733 if (this.status != 0) { 734 Object arg0 = this.threadSync; 735 synchronized (arg0) { 736 try { 737 this.threadSync.wait(5000L); 738 } catch (InterruptedException arg3) { 739 ; 740 } 741 } 742 } 743 744 this.thread = null; 745 } 746 747 public void addLifecycleListener(LifecycleListener listener) { 748 this.lifecycle.addLifecycleListener(listener); 749 } 750 751 public LifecycleListener[] findLifecycleListeners() { 752 return this.lifecycle.findLifecycleListeners(); 753 } 754 755 public void removeLifecycleListener(LifecycleListener listener) { 756 this.lifecycle.removeLifecycleListener(listener); 757 } 758 759 public void start() throws LifecycleException { 760 if (this.started) { 761 throw new LifecycleException(this.sm.getString("httpProcessor.alreadyStarted")); 762 } else { 763 this.lifecycle.fireLifecycleEvent("start", (Object) null); 764 this.started = true; 765 this.threadStart(); 766 } 767 } 768 769 public void stop() throws LifecycleException { 770 if (!this.started) { 771 throw new LifecycleException(this.sm.getString("httpProcessor.notStarted")); 772 } else { 773 this.lifecycle.fireLifecycleEvent("stop", (Object) null); 774 this.started = false; 775 this.threadStop(); 776 } 777 } 778 }
分析:程序入口是上一篇笔记中的HttpProcessor的start()方法和assign()方法,当socket还没到来时,已经执行了该run方法,该方法如何知道已执行assign(),不关心,其实整 个分析过程一直屏蔽线程机制的实现。我更关心的是开篇的问题,还有HttpProcessor处理请求尤其是响应头的完整过程。
synchronized void assign(Socket socket) {//监听到连接到来时,即传递套接字
while (this.available) {
try {
this.wait();
} catch (InterruptedException arg2) {
;
}
}
this.socket = socket;
this.available = true;//存储套接字并更新状态
this.notifyAll();
if (this.debug >= 1 && socket != null) {
this.log(" An incoming request is being assigned");
}
}
public void start() throws LifecycleException {//启动线程,由此执行run()?
if (this.started) {
throw new LifecycleException(this.sm.getString("httpProcessor.alreadyStarted"));
} else {
this.lifecycle.fireLifecycleEvent("start", (Object) null);//通知生命周期监听器?
this.started = true;
this.threadStart();
}
}
public void run() {
while (!this.stopped) {
Socket socket = this.await();
if (socket != null) {
try {
this.process(socket);//解析处理任务由此执行
} catch (Throwable arg4) {
this.log("process.invoke", arg4);
}
this.connector.recycle(this);
}
}
Object socket1 = this.threadSync;
synchronized (socket1) {
this.threadSync.notifyAll();
}
}
以上代码其实都是为了实现多线程,下面从该类的process(socket)看程序是如何依据Http协议来处理socket的?
private void process(Socket socket) {
boolean ok = true;
boolean finishResponse = true;
SocketInputStream input = null;
OutputStream output = null; //输入输出流存储空间
try {
input = new SocketInputStream(socket.getInputStream(), this.connector.getBufferSize());//依赖连接器1
} catch (Exception arg26) {
this.log("process.create", arg26);
ok = false;
}
this.keepAlive = true;//Http1.1特性相关
while (!this.stopped && ok && this.keepAlive) {
finishResponse = true;
try {
this.request.setStream(input);//request
this.request.setResponse(this.response);//response两个对象在连接器创建processor对象时构造方法中就已经创建了。
output = socket.getOutputStream();
this.response.setStream(output);//response依赖输出流
this.response.setRequest(this.request);//已经解析过了吗?
((HttpServletResponse) this.response.getResponse()).setHeader("Server", SERVER_INFO);//给响应头填充server信息
} catch (Exception arg25) {
this.log("process.create", arg25);
ok = false;
}
try {
if (ok) {
this.parseConnection(socket);
this.parseRequest(input, output);
if (!this.request.getRequest().getProtocol().startsWith("HTTP/0")) {
this.parseHeaders(input);//诸多解析工作
}
if (this.http11) {
this.ackRequest(output);//Http1.1沟通是否可接受大请求数据
if (this.connector.isChunkingAllowed()) {//依赖connector2
this.response.setAllowChunking(true);
}
}
}
} catch (EOFException arg27) {
ok = false;
finishResponse = false;
} catch (ServletException arg28) {
ok = false;
try {
((HttpServletResponse) this.response.getResponse()).sendError(400);//填充状态码和状态描述,请求语法不合法,初步解析即可得
} catch (Exception arg24) {
;
}
} catch (InterruptedIOException arg29) {
InterruptedIOException e = arg29;
if (this.debug > 1) {
try {
this.log("process.parse", e);
((HttpServletResponse) this.response.getResponse()).sendError(400);
} catch (Exception arg23) {
;
}
}
ok = false;
} catch (Exception arg30) {
Exception f = arg30;
try {
this.log("process.parse", f);
((HttpServletResponse) this.response.getResponse()).sendError(400);
} catch (Exception arg22) {
;
}
ok = false;
}
try {
this.response.setHeader("Date", FastHttpDateFormat.getCurrentDate());//给response设置当前日期
if (ok) {
this.connector.getContainer().invoke(this.request, this.response); //这里是重点:通过连接器获取container容器
}
} catch (ServletException arg19) {
this.log("process.invoke", arg19);
try {
((HttpServletResponse) this.response.getResponse()).sendError(500);//内部出错时依然能返回500状态吗!!
} catch (Exception arg18) {
;
}
ok = false;
} catch (InterruptedIOException arg20) {
ok = false;
} catch (Throwable arg21) {
this.log("process.invoke", arg21);
try {
((HttpServletResponse) this.response.getResponse()).sendError(500);
} catch (Exception arg17) {
;
}
ok = false;
}
if (finishResponse) {
try {
this.response.finishResponse();//结束响应
} catch (IOException arg15) {
ok = false;
} catch (Throwable arg16) {
this.log("process.invoke", arg16);
ok = false;
}
try {
this.request.finishRequest();//结束请求
} catch (IOException arg13) {
ok = false;
} catch (Throwable arg14) {
this.log("process.invoke", arg14);
ok = false;
}
try {
if (output != null) {
output.flush();
}
} catch (IOException arg12) {
ok = false;
}
}
if ("close".equals(this.response.getHeader("Connection"))) {
this.keepAlive = false;//关闭持久链接
}
this.status = 0;
this.request.recycle();//clear该对象的属性值
this.response.recycle();//同上
}
try {
this.shutdownInput(input);//做了什么?
socket.close();
} catch (IOException arg10) {
;
} catch (Throwable arg11) {
this.log("process.invoke", arg11);
}
socket = null;
}
分析:以上的处理过程几乎一无所知:响应头全部存放在map中是如何发送给客户的,遍历出来吗?响应行、头、体是最后一并发送吗?响应行的完整状态判断如何做的?两个对象是传递给continer的?需要对绿色的方法进行逐一解答,最后得到答案!
private void parseConnection(Socket socket) throws IOException, ServletException {//给request对象填充连接信息
if (this.debug >= 2) {//打印日志
this.log(" parseConnection: address=" + socket.getInetAddress() + ", port=" + this.connector.getPort());
}
this.request.setInet(socket.getInetAddress());//填充host
if (this.proxyPort != 0) {
this.request.setServerPort(this.proxyPort);//填充端口号
} else {
this.request.setServerPort(this.serverPort);
}
this.request.setSocket(socket);//传递socket
}
结论:给request对象填充连接信息
private void parseHeaders(SocketInputStream input) throws IOException, ServletException {//从socket中解析出所有的请求头信息
while (true) {
HttpHeader header = this.request.allocateHeader();//关联HttpHeader,并调用其算法解析input中的头信息
input.readHeader(header);
if (header.nameEnd == 0) {
if (header.valueEnd == 0) {
return;
}
throw new ServletException(this.sm.getString("httpProcessor.parseHeaders.colon"));
}
String value = new String(header.value, 0, header.valueEnd);//请求行中的值
if (this.debug >= 1) {
this.log(" Header " + new String(header.name, 0, header.nameEnd) + " = " + value);
}
if (header.equals(DefaultHeaders.AUTHORIZATION_NAME)) {
this.request.setAuthorization(value);//判断并填充对应的request属性
} else if (header.equals(DefaultHeaders.ACCEPT_LANGUAGE_NAME)) {
this.parseAcceptLanguage(value);//填充属性
} else {
int port;
if (header.equals(DefaultHeaders.COOKIE_NAME)) {
Cookie[] n = RequestUtil.parseCookieHeader(value);
for (port = 0; port < n.length; ++port) {
if (n[port].getName().equals("JSESSIONID") && !this.request.isRequestedSessionIdFromCookie()) {
this.request.setRequestedSessionId(n[port].getValue());
this.request.setRequestedSessionCookie(true);
this.request.setRequestedSessionURL(false);
if (this.debug >= 1) {
this.log(" Requested cookie session id is "
+ ((HttpServletRequest) this.request.getRequest()).getRequestedSessionId());
}
}
if (this.debug >= 1) {
this.log(" Adding cookie " + n[port].getName() + "=" + n[port].getValue());
}
this.request.addCookie(n[port]);
}
} else {
int arg9;
if (header.equals(DefaultHeaders.CONTENT_LENGTH_NAME)) {
boolean arg8 = true;
try {
arg9 = Integer.parseInt(value);
} catch (Exception arg7) {
throw new ServletException(this.sm.getString("httpProcessor.parseHeaders.contentLength"));
}
this.request.setContentLength(arg9);
} else if (header.equals(DefaultHeaders.CONTENT_TYPE_NAME)) {
this.request.setContentType(value);
} else if (header.equals(DefaultHeaders.HOST_NAME)) {
arg9 = value.indexOf(58);
if (arg9 < 0) {
if (this.connector.getScheme().equals("http")) {
this.request.setServerPort(80);
} else if (this.connector.getScheme().equals("https")) {
this.request.setServerPort(443);
}
if (this.proxyName != null) {
this.request.setServerName(this.proxyName);
} else {
this.request.setServerName(value);
}
} else {
if (this.proxyName != null) {
this.request.setServerName(this.proxyName);
} else {
this.request.setServerName(value.substring(0, arg9).trim());
}
if (this.proxyPort != 0) {
this.request.setServerPort(this.proxyPort);
} else {
boolean arg10 = true;
try {
port = Integer.parseInt(value.substring(arg9 + 1).trim());
} catch (Exception arg6) {
throw new ServletException(
this.sm.getString("httpProcessor.parseHeaders.portNumber"));
}
this.request.setServerPort(port);
}
}
} else if (header.equals(DefaultHeaders.CONNECTION_NAME)) {
if (header.valueEquals(DefaultHeaders.CONNECTION_CLOSE_VALUE)) {
this.keepAlive = false;
this.response.setHeader("Connection", "close");
}
} else if (header.equals(DefaultHeaders.EXPECT_NAME)) {
if (!header.valueEquals(DefaultHeaders.EXPECT_100_VALUE)) {
throw new ServletException(
this.sm.getString("httpProcessor.parseHeaders.unknownExpectation"));
}
this.sendAck = true;
} else if (header.equals(DefaultHeaders.TRANSFER_ENCODING_NAME)) {//总之全部都能解析出来
;
}
}
}
this.request.nextHeader();
}
}
结论:从socket中解析出所有的请求头信息。
private void parseRequest(SocketInputStream input, OutputStream output) throws IOException, ServletException {
input.readRequestLine(this.requestLine);
this.status = 1;
String method = new String(this.requestLine.method, 0, this.requestLine.methodEnd);//请求行中的请求方式
String uri = null;
String protocol = new String(this.requestLine.protocol, 0, this.requestLine.protocolEnd);//协议
if (protocol.length() == 0) {
protocol = "HTTP/0.9";
}
if (protocol.equals("HTTP/1.1")) {
this.http11 = true;
this.sendAck = false;
} else {
this.http11 = false;
this.sendAck = false;
this.keepAlive = false;
}
if (method.length() < 1) {
throw new ServletException(this.sm.getString("httpProcessor.parseRequest.method"));
} else if (this.requestLine.uriEnd < 1) {
throw new ServletException(this.sm.getString("httpProcessor.parseRequest.uri"));
} else {//请求参数
int question = this.requestLine.indexOf("?");
if (question >= 0) {
this.request.setQueryString(
new String(this.requestLine.uri, question + 1, this.requestLine.uriEnd - question - 1));
if (this.debug >= 1) {
this.log(" Query string is " + ((HttpServletRequest) this.request.getRequest()).getQueryString());
}
uri = new String(this.requestLine.uri, 0, question);
} else {
this.request.setQueryString((String) null);
uri = new String(this.requestLine.uri, 0, this.requestLine.uriEnd);
}
int semicolon;
if (!uri.startsWith("/")) {
semicolon = uri.indexOf("://");
if (semicolon != -1) {
semicolon = uri.indexOf(47, semicolon + 3);
if (semicolon == -1) {
uri = "";
} else {
uri = uri.substring(semicolon);
}
}
}
semicolon = uri.indexOf(";jsessionid=");
String normalizedUri;
if (semicolon >= 0) {
normalizedUri = uri.substring(semicolon + ";jsessionid=".length());
int semicolon2 = normalizedUri.indexOf(59);
if (semicolon2 >= 0) {
this.request.setRequestedSessionId(normalizedUri.substring(0, semicolon2));
normalizedUri = normalizedUri.substring(semicolon2);
} else {
this.request.setRequestedSessionId(normalizedUri);
normalizedUri = "";
}
this.request.setRequestedSessionURL(true);
uri = uri.substring(0, semicolon) + normalizedUri;
if (this.debug >= 1) {
this.log(" Requested URL session id is "
+ ((HttpServletRequest) this.request.getRequest()).getRequestedSessionId());
}
} else {
this.request.setRequestedSessionId((String) null);
this.request.setRequestedSessionURL(false);
}
normalizedUri = this.normalize(uri);//美化uri
if (this.debug >= 1) {
this.log("Normalized: \'" + uri + "\' to \'" + normalizedUri + "\'");
}
this.request.setMethod(method);
this.request.setProtocol(protocol);
if (normalizedUri != null) {
this.request.setRequestURI(normalizedUri);
} else {
this.request.setRequestURI(uri);
}
this.request.setSecure(this.connector.getSecure());
this.request.setScheme(this.connector.getScheme());
if (normalizedUri == null) {
this.log(" Invalid request URI: \'" + uri + "\'");
throw new ServletException("Invalid URI: " + uri + "\'");
} else {
if (this.debug >= 1) {
this.log(" Request is \'" + method + "\' for \'" + uri + "\' with protocol \'" + protocol + "\'");
}
}
}
}
结论:解析请求头信息并填充。
总结:以上的流程大致是解析出已经创建好的req对象的全部属性,并填充响应对象部分行和头,然后把两个对象传递给container容器,结束res/req、回收并关闭socket。这样一个抽象的输入输出模型就结束了;回顾上一篇连接器的启动过程,位于生命周期的init程序中,该过程包含tart、run,主要流程为初始化serversocket,创建Httppro池,等待连接,创建HttpProccesor,执行process,回收HttpProcessor,而本篇的编程模型就位于执行process步骤中,就想放大镜一样,截取事物的某一区域放大,又是另一番天地,就像逻辑中的子概念,正是层次状态机,UML中可以用状态机图来刻画。
待续:学习进程依然要持续,往前追索,就是说有一个统一管理各个组件的程序,管理生命周期、创建启动连接器、创建container容器并传递容器到连接器,此推测来源于连接器的connector.getContainer,因为该关联类初始为null,并且没有在任何过程中有创建行为,因次肯定是被其他对象set填充的,虽然除了生命周期还有谁会控制连接器并给其填充container还不得而知(应该就是所谓的service),也就是说有一个统一管理各个组件的程序,一方面创建启动连接器,一方面创建container;往后追索则是container容器如何动态加载servet,且它还做了什么。代码切入点是处理器的connector.getContainer().invoke以及container类;扩展追索,完全阅读res/req本身,req自然是已经完全可以交给程序员使用了,但是res在process中只是做了500处理和几个响应头信息的填充,但是程序员发送响应时好多信息是由服务器自行填充并发回的,这些工作是在哪里做的?如果在扩展,就是重定向和jsper相关的疑问了!