您的位置:首页 > 运维架构 > Tomcat

Tomcat分析--Connector

2016-02-13 17:25 351 查看
Connector是使用了socket来进行连接,它接收请求并将他们封装成request和response对象来处理。Request和Response封装之后交给Container处理,Container就是servlet容器,处理之后再返回给Connector,最后Connector在使用socket将处理结果返回给客户端,这样请求就完成了。

Connector在创建时创建ProtocolHandler,然后在生命周期的相关方法中调用ProtocolHandler的相关生命周期方法。Connector的使用方法是通过Connector标签配置在conf/server.xml的文件中。

Connector的结构图如下



ProtocolHandler--用于处理请求,不同的protocolhandler代表不同的连接类型。protocolhander有一个抽象实现类AbstractProtocol, AbstractProtocol下有三种类型:Ajp,Http和Spdy.

     Endpoint--用于处理socket连接

     Processor--用于将endpoint接收的socket封装成request和response.

     Adapter--用于将request和response对象传给container.

 

其中Endpoint继承自AbstractEndpoint,它里面定义了Acceptor和AsyncTimeout两个内部类和一个Handler接口。

Acceptor用于监听请求

AsyncTimeout用于检查异步request的超时

Handler用于处理接收到的socket,再调用processor进去处理

结构如下:



来做一个简单的webserver吧

我们需要:

1.一个启动类Bootstrap

2. Connector(这个例子主要说它)

HttpConnector

        HttpProcessor

        HttpRequest

        HttpResponse

1. HttpProcessor

它要做的是创建一个HttpRequest,一个HttpResponse,解析Http请求的url和head,并给httpRequest对象。如果是要处理一个servlet就调用ServletProcessor就调用servlet的service方法.如果请求的是静态资源,就通过StaticResourceProcessor发送静态资源

public void process(Socket socket) {
SocketInputStream input = null;
OutputStream output = null;
try {
input = new SocketInputStream(socket.getInputStream(), 2048);
output = socket.getOutputStream();

// create HttpRequest object and parse
request = new HttpRequest(input);

// create HttpResponse object
response = new HttpResponse(output);
response.setRequest(request);

response.setHeader("Server", "Pyrmont Servlet Container");

parseRequest(input, output);
parseHeaders(input);

//check if this is a request for a servlet or a static resource
//a request for a servlet begins with "/servlet/"
if (request.getRequestURI().startsWith("/servlet/")) {
ServletProcessor processor = new ServletProcessor();
processor.process(request, response);
}
else {
StaticResourceProcessor processor = new StaticResourceProcessor();
processor.process(request, response);
}

// Close the socket
socket.close();
// no shutdown for this application
}
catch (Exception e) {
e.printStackTrace();
}
}

2. 创建HttpRequest对象

      用来 读取套接字的输入流,解析请求行,解析head,解析cookies,获取参数

解析uri

private void parseRequest(SocketInputStream input, OutputStream output)
throws IOException, ServletException {

// Parse the incoming request line
input.readRequestLine(requestLine);
String method =
new String(requestLine.method, 0, requestLine.methodEnd);
String uri = null;
String protocol = new String(requestLine.protocol, 0, requestLine.protocolEnd);

// Validate the incoming request line
if (method.length() < 1) {
throw new ServletException("Missing HTTP request method");
}
else if (requestLine.uriEnd < 1) {
throw new ServletException("Missing HTTP request URI");
}
// Parse any query parameters out of the request URI
int question = requestLine.indexOf("?");
if (question >= 0) {
request.setQueryString(new String(requestLine.uri, question + 1,
requestLine.uriEnd - question - 1));
uri = new String(requestLine.uri, 0, question);
}
else {
request.setQueryString(null);
uri = new String(requestLine.uri, 0, requestLine.uriEnd);
}

// Checking for an absolute URI (with the HTTP protocol)
if (!uri.startsWith("/")) {
int pos = uri.indexOf("://");
// Parsing out protocol and host name
if (pos != -1) {
pos = uri.indexOf('/', pos + 3);
if (pos == -1) {
uri = "";
}
else {
uri = uri.substring(pos);
}
}
}

// Parse any requested session ID out of the request URI
String match = ";jsessionid=";
int semicolon = uri.indexOf(match);
if (semicolon >= 0) {
String rest = uri.substring(semicolon + match.length());
int semicolon2 = rest.indexOf(';');
if (semicolon2 >= 0) {
request.setRequestedSessionId(rest.substring(0, semicolon2));
rest = rest.substring(semicolon2);
}
else {
request.setRequestedSessionId(rest);
rest = "";
}
request.setRequestedSessionURL(true);
uri = uri.substring(0, semicolon) + rest;
}
else {
request.setRequestedSessionId(null);
request.setRequestedSessionURL(false);
}

// Normalize URI (using String operations at the moment)
String normalizedUri = normalize(uri);

// Set the corresponding request properties
((HttpRequest) request).setMethod(method);
request.setProtocol(protocol);
if (normalizedUri != null) {
((HttpRequest) request).setRequestURI(normalizedUri);
}
else {
((HttpRequest) request).setRequestURI(uri);
}

if (normalizedUri == null) {
throw new ServletException("Invalid URI: " + uri + "'");
}
}

解析header
private void parseHeaders(SocketInputStream input)
throws IOException, ServletException {
while (true) {
HttpHeader header = new HttpHeader();;

// Read the next header
input.readHeader(header);
if (header.nameEnd == 0) {
if (header.valueEnd == 0) {
return;
}
else {
throw new ServletException
(sm.getString("httpProcessor.parseHeaders.colon"));
}
}

String name = new String(header.name, 0, header.nameEnd);
String value = new String(header.value, 0, header.valueEnd);
request.addHeader(name, value);
// do something for some headers, ignore others.
if (name.equals("cookie")) {
Cookie cookies[] = RequestUtil.parseCookieHeader(value);
for (int i = 0; i < cookies.length; i++) {
if (cookies[i].getName().equals("jsessionid")) {
// Override anything requested in the URL
if (!request.isRequestedSessionIdFromCookie()) {
// Accept only the first session id cookie
request.setRequestedSessionId(cookies[i].getValue());
request.setRequestedSessionCookie(true);
request.setRequestedSessionURL(false);
}
}
request.addCookie(cookies[i]);
}
}
else if (name.equals("content-length")) {
int n = -1;
try {
n = Integer.parseInt(value);
}
catch (Exception e) {
throw new ServletException(sm.getString("httpProcessor.parseHeaders.contentLength"));
}
request.setContentLength(n);
}
else if (name.equals("content-type")) {
request.setContentType(value);
}
} //end while
}

解析参数

protected void parseParameters() {
if (parsed)
return;
ParameterMap results = parameters;
if (results == null)
results = new ParameterMap();
results.setLocked(false);
String encoding = getCharacterEncoding();
if (encoding == null)
encoding = "ISO-8859-1";

// Parse any parameters specified in the query string
String queryString = getQueryString();

4000
try {
RequestUtil.parseParameters(results, queryString, encoding);
} catch (UnsupportedEncodingException e) {
;
}

// Parse any parameters specified in the input stream
String contentType = getContentType();
if (contentType == null)
contentType = "";
int semicolon = contentType.indexOf(';');
if (semicolon >= 0) {
contentType = contentType.substring(0, semicolon).trim();
} else {
contentType = contentType.trim();
}
if ("POST".equals(getMethod()) && (getContentLength() > 0)
&& "application/x-www-form-urlencoded".equals(contentType)) {
try {
int max = getContentLength();
int len = 0;
byte buf[] = new byte[getContentLength()];
ServletInputStream is = getInputStream();
while (len < max) {
int next = is.read(buf, len, max - len);
if (next < 0) {
break;
}
len += next;
}
is.close();
if (len < max) {
throw new RuntimeException("Content length mismatch");
}
RequestUtil.parseParameters(results, buf, encoding);
} catch (UnsupportedEncodingException ue) {
;
} catch (IOException e) {
throw new RuntimeException("Content read fail");
}
}

// Store the final results
results.setLocked(true);
parsed = true;
parameters = results;
}

3.创建HttpResponse对象

用于处理静态资源和servlet

处理servlet

public class ServletProcessor {

public void process(HttpRequest request, HttpResponse response) {

String uri = request.getRequestURI();
String servletName = uri.substring(uri.lastIndexOf("/") + 1);
URLClassLoader loader = null;
try {
// create a URLClassLoader
URL[] urls = new URL[1];
URLStreamHandler streamHandler = null;
File classPath = new File(Constants.WEB_ROOT);
String repository = (new URL("file", null, classPath.getCanonicalPath() + File.separator)).toString() ;
urls[0] = new URL(null, repository, streamHandler);
loader = new URLClassLoader(urls);
}
catch (IOException e) {
System.out.println(e.toString() );
}
Class myClass = null;
try {
myClass = loader.loadClass(servletName);
}
catch (ClassNotFoundException e) {
System.out.println(e.toString());
}

Servlet servlet = null;

try {
servlet = (Servlet) myClass.newInstance();
HttpRequestFacade requestFacade = new HttpRequestFacade(request);
HttpResponseFacade responseFacade = new HttpResponseFacade(response);
servlet.service(requestFacade, responseFacade);
((HttpResponse) response).finishResponse();
}
catch (Exception e) {
System.out.println(e.toString());
}
catch (Throwable e) {
System.out.println(e.toString());
}
}
}

处理静态资源
public void sendStaticResource() throws IOException {
byte[] bytes = new byte[BUFFER_SIZE];
FileInputStream fis = null;
try {
/* request.getUri has been replaced by request.getRequestURI */
File file = new File(Constants.WEB_ROOT, request.getRequestURI());
fis = new FileInputStream(file);
/*
* HTTP Response = Status-Line(( general-header | response-header |
* entity-header ) CRLF) CRLF [ message-body ] Status-Line =
* HTTP-Version SP Status-Code SP Reason-Phrase CRLF
*/
int ch = fis.read(bytes, 0, BUFFER_SIZE);
while (ch != -1) {
output.write(bytes, 0, ch);
ch = fis.read(bytes, 0, BUFFER_SIZE);
}
} catch (FileNotFoundException e) {
String errorMessage = "HTTP/1.1 404 File Not Found\r\n"
+ "Content-Type: text/html\r\n" + "Content-Length: 23\r\n"
+ "\r\n" + "<h1>File Not Found</h1>";
output.write(errorMessage.getBytes());
} finally {
if (fis != null)
fis.close();
}
}

参考:

1. how tomcat works

2. 看透spring mvc源代码分析与实践
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: