public class HttpResponse implements HttpServletResponse,Response {
private static final Logger LOGGER = LoggerFactory.getLogger(HttpResponse.class);
private OutputStream outputStream;
private HttpRequest request;
private PrintWriter writer;
public void accessStaticResources() throws IOException {
//根据请求URI找到用户对应请求的资源文件
File staticResource = new File(SimpleContainer.WEB_PROJECT_ROOT + request.getRequestURI());
//资源存在
if (staticResource.exists() && staticResource.isFile()) {
outputStream.write(responseToByte(HttpStatusEnum.OK));
writeFile(staticResource);
//资源不存在
} else {
staticResource = new File(SimpleContainer.WEB_PROJECT_ROOT + "/404.html");
outputStream.write(responseToByte(HttpStatusEnum.NOT_FOUND));
writeFile(staticResource);
}
}
/**
* 将读取到的资源文件输出
*
* @param file 读取到的文件
* @throws IOException IOException
*/
private void writeFile(File file) throws IOException {
try (FileInputStream fis = new FileInputStream(file)) {
byte[] cache = ArrayUtil.generatorCache();
int read;
while ((read = fis.read(cache, 0, ArrayUtil.BUFFER_SIZE)) != -1) {
outputStream.write(cache, 0, read);
}
}
}
/**
* 将请求行 请求头转换为byte数组
*
* @param status 响应http状态
* @return 响应头byte数组
*/
public byte[] responseToByte(HttpStatusEnum status) {
return new StringBuilder().append(HttpVersionConstant.HTTP_1_1).append(" ")
.append(status.getStatus()).append(" ")
.append(status.getDesc()).append("\r\n\r\n")
.toString().getBytes();
}
@Override
public PrintWriter getWriter() throws IOException {
if (writer != null) {
return writer;
}
return (writer = new PrintWriter(outputStream));
}
package me.geoffrey.tomcat.server.http.process;
import me.geoffrey.tomcat.server.connector.HttpConnector;
import me.geoffrey.tomcat.server.constant.HttpConstant;
import me.geoffrey.tomcat.server.enums.HTTPHeaderEnum;
import me.geoffrey.tomcat.server.http.carrier.HttpRequest;
import me.geoffrey.tomcat.server.http.carrier.HttpResponse;
import me.geoffrey.tomcat.server.util.RequestUtil;
import me.geoffrey.tomcat.server.util.StringUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.LinkedList;
import java.util.Optional;
import java.util.Queue;
import java.util.stream.Stream;
import static java.util.stream.Collectors.toCollection;
/**
* @author Geoffrey.Yip
* @time 2017/12/31 17:40
* @description Http处理器
*/
public class HttpProcess implements Runnable {
private static final Logger LOGGER = LoggerFactory.getLogger(HttpProcess.class);
/**
* Servlet资源请求起始字符串
**/
private static final String SERVLET_URI_START_WITH = "/servlet/";
private int id;
private HttpConnector httpConnector;
private boolean available;
/**
* The background thread.
*/
private Thread thread;
/**
* HttpRequest
**/
private HttpRequest request;
/**
* HttpResponse
**/
private HttpResponse response;
private String threadName;
private Socket socket;
private boolean stopped;
public HttpProcess(HttpConnector httpConnector, int id) {
this.httpConnector = httpConnector;
this.id = id;
request = (HttpRequest) httpConnector.createRequest();
response = (HttpResponse) httpConnector.createResponse();
threadName = "HttpProcessor[" + httpConnector.getServerPort() + "][" + id + "]";
}
/**
* 执行用户请求
*
* @param socket 请求socket
*/
public void process(Socket socket) {
try (InputStream input = socket.getInputStream();
OutputStream output = socket.getOutputStream()) {
//初始化request以及response
request = new HttpRequest(input);
response = new HttpResponse(output, request);
//解析request请求和请求头
this.parseRequest(input);
this.parseHeaders(input);
//调用对应的处理器处理
if (request.getRequestURI().startsWith(SERVLET_URI_START_WITH)) {
httpConnector.getContainer().invoke(request, response);
} else {
new StaticResourceProcess().process(request, response);
}
} catch (ServletException e) {
LOGGER.info("Catch ServletException from Socket process :", e);
} catch (IOException e){
} finally {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 解析请求行和校验URI安全性
*
* @param input socket输入流
* @throws IOException 流错误
* @throws ServletException 读取到的请求行有误
*/
private void parseRequest(InputStream input) throws IOException, ServletException {
StringBuilder temp = new StringBuilder();
int cache;
while ((cache = input.read()) != -1) {
//请求行读取完毕
if (HttpConstant.CARRIAGE_RETURN == cache && HttpConstant.LINE_FEED == input.read()) {
break;
}
temp.append((char) cache);
}
String[] requestLineArray = temp.toString().split(" ");
if (requestLineArray.length < 3) {
throw new ServletException("HTTP request line is not standard!");
}
// 填充request的URI和方法信息
request.setMethod(requestLineArray[0]);
request.setProtocol(requestLineArray[2]);
String uri = requestLineArray[1];
int question = uri.indexOf("?");
if (question >= 0) {
request.setQueryString(uri.substring(question + 1, uri.length()));
uri = uri.substring(0, question);
}
// 如果URI是绝对路径则替换成相对路径
if (!uri.startsWith("/")) {
//获取 http:// 中://的索引
int pos = uri.indexOf("://");
if (pos != -1) {
//获取相对路径的第一个/索引
pos = uri.indexOf('/', pos + 3);
if (pos == -1) {
uri = "";
} else {
//直接根据索引截取到URI
uri = uri.substring(pos);
}
}
}
// 解析查询字符串是否携带jsessionid,如果有则设置sessionid信息
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);
}
//校验URI有没有不符合规范或者不正常的地方,修正
String normalizedUri = RequestUtil.normalize(uri);
if (normalizedUri == null) {
throw new ServletException("Invalid URI: " + uri + "'");
}
request.setRequestURI(normalizedUri);
}
/**
* 解析HTTP请求头
*
* @param input socket输入流
* @throws IOException 读取出错
*/
private void parseHeaders(InputStream input) throws IOException {
StringBuilder sb = new StringBuilder();
int cache;
while (input.available() > 0 && (cache = input.read()) > -1) {
sb.append((char) cache);
}
//使用\r\n分割请求头
Queue<String> headers = Stream.of(sb.toString().split("\r\n")).collect(toCollection(LinkedList::new));
//获取一个请求头
while (!headers.isEmpty()) {
String headerString = headers.poll();
//读取到空行则说明请求头已读取完毕
if (StringUtil.isBlank(headerString)) {
break;
}
//分割请求头的key和value
String[] headerKeyValue = headerString.split(": ");
request.addHeader(headerKeyValue[0], headerKeyValue[1]);
}
//如果在读取到空行后还有数据,说明是POST请求的表单参数
if (!headers.isEmpty()) {
request.setPostParams(headers.poll());
}
//设置请求参数
String contentLength = request.getHeader(HTTPHeaderEnum.CONTENT_LENGTH.getDesc());
if (contentLength != null) {
request.setContentLength(Integer.parseInt(contentLength));
}
request.setContentType(request.getHeader(HTTPHeaderEnum.CONTENT_TYPE.getDesc()));
request.setCharacterEncoding(RequestUtil.parseCharacterEncoding(request.getHeader(request.getContentType())));
Cookie[] cookies = parseCookieHeader(request.getHeader(HTTPHeaderEnum.COOKIE.getDesc()));
Optional.ofNullable(cookies).ifPresent(cookie ->
Stream.of(cookie).forEach(c -> request.addCookie(c))
);
//如果sessionid不是从cookie中获取的,则优先使用cookie中的sessionid
if (!request.isRequestedSessionIdFromCookie() && cookies != null) {
Stream.of(cookies)
.filter(cookie -> "jsessionid".equals(cookie.getName()))
.findFirst().
ifPresent(cookie -> {
//设置cookie的值
request.setRequestedSessionId(cookie.getValue());
request.setRequestedSessionCookie(true);
request.setRequestedSessionURL(false);
});
}
}
/**
* 将cookie字符串解析成cookie数组
*
* @param cookieListString 请求头中的cookie字符串
* @return cookie数组
*/
private Cookie[] parseCookieHeader(String cookieListString) {
if (StringUtil.isBlank(cookieListString)) {
return null;
}
return Stream.of(cookieListString.split("; "))
.map(cookieStr -> {
String[] cookieArray = cookieStr.split("=");
return new Cookie(StringUtil.urlDecode(cookieArray[0]), StringUtil.urlDecode(cookieArray[1]));
}).toArray(Cookie[]::new);
}
@Override
public void run() {
while (!stopped) {
Socket socket = await();
process(socket);
httpConnector.recycle(this);
}
}
private Socket await(){
while(!available){
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
available = false;
Socket socket = this.socket;
notifyAll();
return socket;
}
public void start() {
thread = new Thread(this, threadName);
thread.setDaemon(true);
thread.start();
}
public void assign(Socket socket) {
while (available) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.socket = socket;
available = true;
notifyAll();
}
}