Tomcat中的连接器是一个独立的模块,可以被插入到servlet容器中,而且还有很多连接器可以使用。例如Coyote,mod_jk,mod_jk2,mod_webapp等。Tomcat中使用的连接器必须满足以下条件:
1.实现org.apache.catalina.Connector接口
2.负责创建实现了org.apache.catalina.Request接口的request对象
3.负责创建实现了org.apache.catalina.Response接口的response对象
Tomcat的连接器等待引入的HTTP请求,创建request对象和response对象,然后调用org.apache.catalina.Container接口的invoke()方法,将request对象和response对象传给servlet容器。
- package com.whatsmars.tomcat.connector;
- /**
- * Created by shenhongxi on 16/4/11.
- */
- public final class Bootstrap {
- public static void main(String[] args) {
- HttpConnector connector = new HttpConnector();
- Container container = new SimpleContainer();
- connector.setContainer(container);
- connector.setBufferSize(2048);
- connector.start();
- }
- }
- package com.whatsmars.tomcat.connector;
- import java.io.IOException;
- import java.net.InetAddress;
- import java.net.ServerSocket;
- import java.net.Socket;
- /**
- * Created by shenhongxi on 16/4/11.
- */
- public class HttpConnector implements Runnable {
- private Container container;
- boolean stopped;
- private String scheme = "http";
- private int bufferSize;
- public void run() {
- ServerSocket serverSocket = null;
- int port = 8080;
- try {
- serverSocket = new ServerSocket(port, 1, InetAddress.getByName("127.0.0.1"));
- } catch (IOException e) {
- e.printStackTrace();
- System.exit(1);
- }
- while (!stopped) {
- // Accept the next incoming connection from the server socket
- Socket socket = null;
- try {
- socket = serverSocket.accept();
- } catch (Exception e) {
- continue;
- }
- // Hand this socket off to an HttpProcessor
- HttpProcessor processor = new HttpProcessor(this);
- processor.process(socket);
- }
- }
- public void start() {
- new Thread(this).start();
- }
- public String getScheme() {
- return scheme;
- }
- public int getBufferSize() {
- return bufferSize;
- }
- public void setBufferSize(int bufferSize) {
- this.bufferSize = bufferSize;
- }
- public Container getContainer() {
- return container;
- }
- public void setContainer(Container container) {
- this.container = container;
- }
- }
- package com.whatsmars.tomcat.connector;
- import org.apache.catalina.util.Enumerator;
- import org.apache.catalina.util.ParameterMap;
- import org.apache.catalina.util.RequestUtil;
- import javax.servlet.*;
- import javax.servlet.http.*;
- import java.io.BufferedReader;
- import java.io.IOException;
- import java.io.UnsupportedEncodingException;
- import java.security.Principal;
- import java.util.*;
- /**
- * Created by shenhongxi on 16/4/11.
- */
- public class HttpRequest implements HttpServletRequest {
- private String requestURI;
- private int contentLength;
- private String contentType;
- private String queryString;
- private String method;
- private String protocol;
- protected Map headers = new HashMap();
- protected SocketInputStream input;
- /**
- * The parsed parameters for this request. This is populated only if
- * parameter information is requested via one of the
- * <code>getParameter()</code> family of method calls. The key is the
- * parameter name, while the value is a String array of values for this
- * parameter.
- * <p>
- * <strong>IMPLEMENTATION NOTE</strong> - Once the parameters for a
- * particular request are parsed and stored here, they are not modified.
- * Therefore, application level access to the parameters need not be
- * synchronized.
- */
- protected ParameterMap parameters = null; // extends LinkedHashMap has a boolean var 'locked'
- /**
- * Have the parameters for this request been parsed yet?
- */
- protected boolean parsed = false;
- public HttpRequest(SocketInputStream input) {
- this.input = input;
- }
- public void addHeader(String name, String value) {
- name = name.toLowerCase();
- synchronized (headers) {
- ArrayList values = (ArrayList) headers.get(name);
- if (values == null) {
- values = new ArrayList();
- headers.put(name, values);
- }
- values.add(value);
- }
- }
- public String getParameter(String name) {
- parseParameters();
- String values[] = (String[]) parameters.get(name);
- if (values != null)
- return (values[0]);
- else
- return (null);
- }
- public Map getParameterMap() {
- parseParameters();
- return (this.parameters);
- }
- public Enumeration getParameterNames() {
- parseParameters();
- return (new Enumerator(parameters.keySet()));
- }
- public String[] getParameterValues(String name) {
- parseParameters();
- String values[] = (String[]) parameters.get(name);
- if (values != null)
- return (values);
- else
- return null;
- }
- /**
- * Parse the parameters of this request, if it has not already occurred.
- * If parameters are present in both the query string and the request
- * content, they are merged.
- */
- 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();
- try {
- RequestUtil.parseParameters(results, queryString, encoding);
- } catch (Exception 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;
- }
- public void setRequestURI(String requestURI) {
- this.requestURI = requestURI;
- }
- public void setContentLength(int contentLength) {
- this.contentLength = contentLength;
- }
- public void setContentType(String contentType) {
- this.contentType = contentType;
- }
- public void setQueryString(String queryString) {
- this.queryString = queryString;
- }
- public void setMethod(String method) {
- this.method = method;
- }
- public void setProtocol(String protocol) {
- this.protocol = protocol;
- }
- // ... other methods
- }
- package com.whatsmars.tomcat.connector;
- import javax.servlet.ServletOutputStream;
- import javax.servlet.http.Cookie;
- import javax.servlet.http.HttpServletResponse;
- import java.io.IOException;
- import java.io.OutputStream;
- import java.io.PrintWriter;
- import java.util.Collection;
- import java.util.Locale;
- /**
- * Created by shenhongxi on 16/4/11.
- */
- public class HttpResponse implements HttpServletResponse {
- OutputStream output;
- HttpRequest request;
- PrintWriter writer;
- public HttpResponse(OutputStream output) {
- this.output = output;
- }
- public void setRequest(HttpRequest request) {
- this.request = request;
- }
- /**
- * call this method to send headers and response to the output
- */
- public void finishResponse() {
- // sendHeaders();
- // Flush and close the appropriate output mechanism
- if (writer != null) {
- writer.flush();
- writer.close();
- }
- }
- public void addCookie(Cookie cookie) {
- }
- public boolean containsHeader(String name) {
- return false;
- }
- public String encodeURL(String url) {
- return null;
- }
- public String encodeRedirectURL(String url) {
- return null;
- }
- public String encodeUrl(String url) {
- return null;
- }
- public String encodeRedirectUrl(String url) {
- return null;
- }
- public void sendError(int sc, String msg) throws IOException {
- }
- public void sendError(int sc) throws IOException {
- }
- public void sendRedirect(String location) throws IOException {
- }
- // ... other methods
- }
- package com.whatsmars.tomcat.connector;
- import com.whatsmars.tomcat.servlet.StaticResourceProcessor;
- import javax.servlet.ServletException;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import java.io.IOException;
- import java.io.OutputStream;
- import java.net.Socket;
- /**
- * Created by shenhongxi on 16/4/11.
- */
- public class HttpProcessor {
- private HttpConnector connector;
- private HttpRequest request;
- private HttpResponse response;
- private HttpRequestLine requestLine = new HttpRequestLine();
- public HttpProcessor(HttpConnector connector) {
- this.connector = connector;
- }
- public void process(Socket socket) {
- SocketInputStream input = null;
- OutputStream output = null;
- try {
- input = new SocketInputStream(socket.getInputStream(), connector.getBufferSize()); // 1.读取套接字的输入流
- output = socket.getOutputStream();
- // create HttpRequest object and parse
- request = new HttpRequest(input);
- response = new HttpResponse(output);
- response.setRequest(request);
- response.setHeader("Server", "Mars Servlet Container");
- parseRequest(input, output); // 解析请求行,即HTTP请求的第一行内容
- parseHeaders(input); // 解析请求头
- if (request.getRequestURI().startsWith("/servlet/")) {
- connector.getContainer().invoke((HttpServletRequest) request, (HttpServletResponse) response);
- } else {
- StaticResourceProcessor processor = new StaticResourceProcessor();
- //processor.process(request, response);
- }
- socket.close();
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- private void parseHeaders(SocketInputStream input) throws IOException, ServletException{
- while (true) { // 一行一行解析完header
- HttpHeader header = new HttpHeader();
- // Read the next header
- input.readHeader(header);
- if (header.nameEnd == 0) {
- if (header.valueEnd == 0) {
- return;
- } else {
- throw new ServletException("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")) {
- // ...
- // request.addCookie(cookies[i]);
- } else if (name.equals("content-length")) {
- int n = -1;
- try {
- n = Integer.parseInt(value);
- } catch (Exception e) {
- throw new ServletException("httpProcessor.parseHeaders.contentLength");
- }
- request.setContentLength(n);
- } else if (name.equals("content-type")) {
- request.setContentType(value);
- }
- }
- }
- private void parseRequest(SocketInputStream input, OutputStream output) throws IOException, ServletException {
- 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);
- }
- String normalizedUri = normalize(uri);
- ((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 + "'");
- }
- }
- // Return a context-relative path, beginning with a "/"
- protected String normalize(String path) {
- if (path == null) return null;
- String normalized = path;
- // ...
- return path;
- }
- }
- package com.whatsmars.tomcat.connector;
- /**
- * Created by shenhongxi on 16/4/13.
- */
- public final class HttpHeader {
- public static final int INITIAL_NAME_SIZE = 32;
- public static final int INITIAL_VALUE_SIZE = 64;
- public static final int MAX_NAME_SIZE = 128;
- public static final int MAX_VALUE_SIZE = 4096;
- public char[] name;
- public int nameEnd;
- public char[] value;
- public int valueEnd;
- protected int hashCode = 0;
- public HttpHeader() {
- this(new char[INITIAL_NAME_SIZE], 0, new char[INITIAL_VALUE_SIZE], 0);
- }
- public HttpHeader(char[] name, int nameEnd, char[] value, int valueEnd) {
- this.name = name;
- this.nameEnd = nameEnd;
- this.value = value;
- this.valueEnd = valueEnd;
- }
- public HttpHeader(String name, String value) {
- this.name = name.toLowerCase().toCharArray();
- this.nameEnd = name.length();
- this.value = value.toCharArray();
- this.valueEnd = value.length();
- }
- /**
- * Release all object references, and initialize instance variables, in
- * preparation for reuse of this object.
- */
- public void recycle() {
- nameEnd = 0;
- valueEnd = 0;
- hashCode = 0;
- }
- }
- package com.whatsmars.tomcat.connector;
- /**
- * Created by shenhongxi on 16/4/13.
- */
- public final class HttpRequestLine {
- public static final int INITIAL_METHOD_SIZE = 8;
- public static final int INITIAL_URI_SIZE = 64;
- public static final int INITIAL_PROTOCOL_SIZE = 8;
- public static final int MAX_METHOD_SIZE = 1024;
- public static final int MAX_URI_SIZE = 32768;
- public static final int MAX_PROTOCOL_SIZE = 1024;
- public char[] method;
- public int methodEnd;
- public char[] uri;
- public int uriEnd;
- public char[] protocol;
- public int protocolEnd;
- public HttpRequestLine() {
- this(new char[INITIAL_METHOD_SIZE], 0, new char[INITIAL_URI_SIZE], 0,
- new char[INITIAL_PROTOCOL_SIZE], 0);
- }
- public HttpRequestLine(char[] method, int methodEnd,
- char[] uri, int uriEnd,
- char[] protocol, int protocolEnd) {
- this.method = method;
- this.methodEnd = methodEnd;
- this.uri = uri;
- this.uriEnd = uriEnd;
- this.protocol = protocol;
- this.protocolEnd = protocolEnd;
- }
- public int indexOf(String str) {
- // ...
- return -1;
- }
- /**
- * Release all object references, and initialize instance variables, in
- * preparation for reuse of this object.
- */
- public void recycle() {
- methodEnd = 0;
- uriEnd = 0;
- protocolEnd = 0;
- }
- }
- package com.whatsmars.tomcat.connector;
- import java.io.IOException;
- import java.io.InputStream;
- /**
- * Created by shenhongxi on 16/4/11.
- * Extends InputStream to be more efficient reading lines during HTTP header processing.
- */
- public class SocketInputStream extends InputStream {
- /**
- * Underlying input stream.
- */
- private InputStream input;
- /**
- * Internal buffer.
- */
- protected byte[] buf;
- /**
- * Last valid byte.
- */
- protected int count;
- /**
- * Position in the buffer.
- */
- protected int pos;
- public SocketInputStream(InputStream input, int bufferSize) {
- this.input = input;
- this.buf = new byte[bufferSize];
- }
- // input => buf => HttpRequestLine
- public void readRequestLine(HttpRequestLine requestLine) throws IOException {
- // Recycling check
- if (requestLine.methodEnd != 0)
- requestLine.recycle();
- // Checking for a blank line
- // Reading the method name
- // Reading URI
- // Reading protocol
- }
- // input => buf => HttpHeader
- public void readHeader(HttpHeader header) throws IOException {
- // Recycling check
- if (header.nameEnd != 0)
- header.recycle();
- // Checking for a blank line
- // Reading the header name
- // Reading the header value (which can be spanned over multiple lines)
- }
- @Override
- public int read() throws IOException {
- if (pos >= count) {
- fill();
- if (pos >= count)
- return -1;
- }
- return buf[pos++] & 0xff;
- }
- /**
- * Fill the internal buffer using data from the undelying input stream.
- */
- protected void fill()
- throws IOException {
- pos = 0;
- count = 0;
- int nRead = input.read(buf, 0, buf.length);
- if (nRead > 0) {
- count = nRead;
- }
- }
- }
- package com.whatsmars.tomcat.connector;
- import com.whatsmars.tomcat.servlet.Constants;
- import javax.servlet.Servlet;
- import javax.servlet.ServletException;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import java.io.File;
- import java.io.IOException;
- import java.net.URL;
- import java.net.URLClassLoader;
- import java.net.URLStreamHandler;
- /**
- * Created by shenhongxi on 16/4/14.
- */
- public class SimpleContainer implements Container {
- public void invoke(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
- String servletName = ( (HttpServletRequest) request).getRequestURI();
- servletName = servletName.substring(servletName.lastIndexOf("/") + 1);
- URLClassLoader loader = null;
- try {
- 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();
- servlet.service((HttpServletRequest) request, (HttpServletResponse) response);
- }
- catch (Exception e) {
- System.out.println(e.toString());
- }
- catch (Throwable e) {
- System.out.println(e.toString());
- }
- }
- }
- package com.whatsmars.tomcat.connector;
- import javax.servlet.ServletException;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import java.io.IOException;
- /**
- * Created by shenhongxi on 16/4/14.
- */
- public interface Container {
- public void invoke(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException;
- }
最后,关于HttpProcessor池,连接器线程和处理器线程的等待与唤醒,请参考多线程知识自行脑补。
原文链接:[http://wely.iteye.com/blog/2290576]