手写WebServer

基于Java BIO,手写一个Web Server。Github 地址: https://github.com/xiaxinyu/summer_projects/tree/master/project-web-server

一、启动

1、启动类

package com.web.server.boot;

import java.util.Map;

import com.web.server.core.Servlet;
import com.web.server.core.WebServer;
import com.web.server.core.configuration.ConfigurationFactory;

public class Bootstrap {
	public static void main(String[] args) {
		Map configuration = ConfigurationFactory.getConfiguration();
		new WebServer().start(configuration);
	}
}

2、配置解析类

package com.web.server.core.configuration;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.web.server.core.Servlet;
import com.web.server.exception.WebServerException;

public class ConfigurationFactory {
	private final static Logger logger = LoggerFactory.getLogger(ConfigurationFactory.class);
	private static Properties properties = new Properties();

	public static Map getConfiguration() {
		Map configuration = null;
		InputStream inputStream = null;
		try {
			String basePath = ConfigurationFactory.class.getResource("/").getPath();
			inputStream = new FileInputStream(basePath + "web.properties");
			properties.load(inputStream);

			Set keys = properties.keySet();
			if (keys != null && !keys.isEmpty()) {
				configuration = new ConcurrentHashMap<>();
				for (Object k : keys) {
					String key = k.toString();
					if (key.endsWith(".url")) {
						String servletName = key.replaceAll("\\.url$", "") + ".className";
						String url = properties.getProperty(key);
						String className = properties.getProperty(servletName);
						configuration.put(url, (Servlet) Class.forName(className).newInstance());
					}
				}
			}
		} catch (Exception e) {
			throw new WebServerException("Parse web configuration has error.");
		} finally {
			if (inputStream != null) {
				try {
					inputStream.close();
				} catch (IOException e) {
					logger.error("Startring server has error", e);
				}
			}
		}
		return configuration;
	}
} 
  

二、WebSever 核心服务器类

package com.web.server.core;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class WebServer {
	private final static Logger logger = LoggerFactory.getLogger(WebServer.class);
	public static final int PORT = 8080;
	public static final int BACK_LOG = 50;

	public void start(Map configuration) {
		ServerSocket server = null;
		try {
			server = new ServerSocket();
			server.bind(new InetSocketAddress(PORT), BACK_LOG);
			logger.info("Start server, listen port {}", PORT);
			while (true) {
				new ConnectionProcessor(server.accept(), configuration).start();
			}
		} catch (IOException e) {
			logger.error("Startring server has error", e);
			System.exit(1);
		} finally {
			if (server != null) {
				try {
					server.close();
				} catch (IOException e) {
					logger.error("Startring server has error", e);
				}
			}
		}
	}
}

三、Connection 核心服务器类

package com.web.server.core;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.web.server.core.enumeration.HttpStatus;

public class ConnectionProcessor extends Thread {
	private final static Logger logger = LoggerFactory.getLogger(ConnectionProcessor.class);

	private Socket client;
	private Map servletMapping;

	public ConnectionProcessor(Socket client, Map servletMapping) {
		this.client = client;
		this.servletMapping = servletMapping;
	}

	@Override
	public void run() {
		logger.info("Start-New connection:" + client.getInetAddress() + ":" + client.getPort());
		process(client);
	}

	private void process(Socket client) {
		InputStream is = null;
		OutputStream os = null;

		try {
			is = client.getInputStream();
			os = client.getOutputStream();

			Request request = new Request(is);
			logger.info(request.toString());

			Response response = new Response(os);
			Servlet servlet = servletMapping.get(request.getUrl());
			if (null != servlet) {
				servlet.service(request, response);
			} else {
				logger.warn("Can't found {}", request.getUrl());
				response.setStatus(HttpStatus.S_404);
			}

			response.flush();
			os.flush();
		} catch (Exception e) {
			logger.error("Process client request has error.", e);
		} finally {
			try {
				if (is != null) {
					is.close();
				}
				if (os != null) {
					os.close();
				}
				if (client != null) {
					client.close();
				}
			} catch (IOException e) {
				logger.error("Process client request has error.", e);
			}
		}
	}
}

四、模版模式Servlet处理类

package com.web.server.core;

import com.web.server.core.enumeration.MethodType;
import com.web.server.exception.NotSupportMethodTypeException;

public abstract class Servlet {
	public void service(Request request, Response response) throws Exception {
		if (MethodType.GET == request.getMethod()) {
			doGet(request, response);
		} else if (MethodType.POST == request.getMethod()) {
			doPost(request, response);
		} else {
			throw new NotSupportMethodTypeException(request.getMethod() + " can't be support.");
		}
	}

	public abstract void doGet(Request request, Response response) throws Exception;

	public abstract void doPost(Request request, Response response) throws Exception;
}

 

五、请求处理类

package com.web.server.core;

import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.web.server.core.enumeration.MethodType;
import com.web.server.exception.HttpProtocolParsingException;

public class Request {
	private final static Logger logger = LoggerFactory.getLogger(Request.class);
	public static final int SMALL_BUF_SIZE = 1024;

	private String[] httpProtocolLines;
	private String httpProtocol;
	private String url;
	private MethodType method;
	private Map parameter = new HashMap<>();

	public Request(InputStream is) {
		try {
			this.httpProtocol = getHttpProtocol(is);
			parseHttpProtocol(this.httpProtocol);
		} catch (IOException e) {
			logger.error("Parse http protocol has error.", e);
		}
	}

	private String getHttpProtocol(InputStream is) throws IOException {
		String httpProtocol = null;
		byte[] buffer = new byte[SMALL_BUF_SIZE];
		int len = 0;
		if ((len = is.read(buffer)) > 0) {
			httpProtocol = new String(buffer, 0, len);
		}
		return httpProtocol;
	}

	private void parseHttpProtocol(String httpProtocol) {
		httpProtocolLines = httpProtocol.split("\\n");
		if (httpProtocol.length() > 0) {
			parseBaseProtocolLine(httpProtocolLines[0]);
		} else {
			throw new HttpProtocolParsingException("Http protocol has error.");
		}
	}

	private void parseBaseProtocolLine(String protocolLine) {
		String[] segments = protocolLine.split("\\s");

		// Parse method
		MethodType methodType = MethodType.of(segments[0]);
		if (null != methodType) {
			this.method = methodType;
		} else {
			throw new HttpProtocolParsingException("Can't recongnize method type.");
		}

		String[] items = segments[1].split("\\?");
		this.url = items[0];

		if (items.length <= 1) {
			return;
		}

		// Parse parameters
		String[] params = items[1].split("\\&");
		if (params != null && params.length > 0) {
			for (String param : params) {
				String[] pairs = param.split("\\=");
				this.parameter.put(pairs[0], pairs[1]);
			}
		}
	}

	public String getUrl() {
		return this.url;
	}

	public MethodType getMethod() {
		return this.method;
	}

	public String getHttpProtocol() {
		return httpProtocol;
	}

	public Map getParameter() {
		return this.parameter;
	}

	@Override
	public String toString() {
		return "Request [url=" + url + ", method=" + method + "]";
	}
}

六、响应处理类

package com.web.server.core;

import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.lang3.StringUtils;

import com.web.server.core.enumeration.HttpStatus;
import com.web.server.core.enumeration.HttpStatusText;
import com.web.server.utils.ResponseHelper;


public class Response {
	private OutputStream os;
	private HttpStatus status = HttpStatus.S_200;
	private List responseTexts = new ArrayList<>();

	public Response(OutputStream os) {
		this.os = os;
	}

	public HttpStatus getStatus() {
		return status;
	}

	public void setStatus(HttpStatus status) {
		this.status = status;
	}

	public void write(String outContext) {
		responseTexts.add(outContext);
	}

	public void flush() throws IOException {
		if (HttpStatus.S_200 == status) {
			String responseText = ResponseHelper.fmtResponseText(responseTexts);
			if (StringUtils.isBlank(responseText)) {
				responseText = HttpStatusText.of(HttpStatus.S_200).getText();
			}
			os.write(ResponseHelper.fmtResponse(status, responseText).getBytes());
		} else {
			os.write(ResponseHelper.fmtResponse(status, HttpStatusText.of(status).getText()).getBytes());
		}
		responseTexts.clear();
	}
}

 

你可能感兴趣的:(IT)