一、实现Response
实现ConnectorUtils
获取资源主目录对应的路径
package connector;
import java.io.File;
/**
* @ClassName:
* @Description:
* @author: hszjj
* @date: 2019/11/26 12:54
*/
public class ConnectorUtils {
public static final String WEB_ROOT =
System.getProperty("user.dir") + File.separator + "webroot";
public static final String PROTOCOL = "HTTP/1.1";
public static final String CARRIAGE = "\r";
public static final String NEWLINE = "\n";
public static final String SPACE = " ";
public static String renderStatus(HttpStatus status){
StringBuilder sb=new StringBuilder(PROTOCOL)
.append(SPACE)
.append(status.getStatusCode())
.append(SPACE)
.append(status.getReason())
.append(CARRIAGE).append(NEWLINE)
.append(CARRIAGE).append(NEWLINE);
return sb.toString();
}
}
实现HttpStatus
获取网络状态
package connector;
/**
* @ClassName:
* @Description:
* @author: hszjj
* @date: 2019/11/26 13:07
*/
public enum HttpStatus {
SC_OK(200,"OK"),
SC_NOT_FOUND(404,"File Not Found");
private int statusCode;
private String reason;
HttpStatus(int Code,String reason){
this.statusCode=Code;
this.reason=reason;
}
public int getStatusCode() {
return statusCode;
}
public String getReason() {
return reason;
}
}
实现Response
package connector;
import java.io.*;
/**
* @ClassName:
* @Description:HTTP/1.1 200 OK
* @author: hszjj
* @date: 2019/11/26 12:49
*/
public class Response {
private static final int BUFFER_SIZE = 1024;
Request request;
OutputStream output;
public Response(OutputStream output) {
this.output = output;
}
public void setRequest(Request request) {
this.request = request;
}
public void sendStaticResource() throws IOException {
File file = new File(ConnectorUtils.WEB_ROOT, request.getRequestUri());
try {
write(file, HttpStatus.SC_OK);
} catch (IOException e) {
write(new File(ConnectorUtils.WEB_ROOT, "404.html"), HttpStatus.SC_NOT_FOUND);
}
}
private void write(File resource, HttpStatus status) throws IOException {
try (FileInputStream input = new FileInputStream(resource)) {
output.write(ConnectorUtils.renderStatus(status).getBytes());
byte[] buffer = new byte[BUFFER_SIZE];
int lenth = 0;
while ((lenth = input.read(buffer, 0, BUFFER_SIZE)) != -1) {
output.write(buffer, 0, lenth);
}
}
}
}
二、Response测试
TestUtils
工具类
package test.util;
import connector.Request;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
/**
* @ClassName:
* @Description:
* @author: hszjj
* @date: 2019/11/26 13:43
*/
public class TestUtils {
public static Request createRequest(String requestString){
InputStream input=new ByteArrayInputStream(requestString.getBytes());
Request request=new Request(input);
request.parse();
return request;
}
public static String readFileToString(String filename) throws IOException {
return new String(Files.readAllBytes(Paths.get(filename)));
}
}
ResponseTest
package test.connector;
import connector.ConnectorUtils;
import connector.Request;
import connector.Response;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import test.util.TestUtils;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
/**
* @ClassName:
* @Description:
* @author: hszjj
* @date: 2019/11/26 13:36
*/
public class ResponseTest {
private static final String VAliD_REQUEST = "GET /index.html HTTP/1.1";
private static final String INVAliD_REQUEST = "GET /notfound.html HTTP/1.1";
private static final String STATUS200 = "HTTP/1.1 200 OK\r\n\r\n";
private static final String STATUS404 = "HTTP/1.1 404 File Not Found\r\n\r\n";
@Test
public void givenValidRequest_thenReturnStaticResource() throws IOException {
Request request = TestUtils.createRequest(VAliD_REQUEST);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
Response response = new Response(outputStream);
response.setRequest(request);
response.sendStaticResource();
Assertions.assertEquals(
(STATUS200 + TestUtils.readFileToString(ConnectorUtils.WEB_ROOT + request.getRequestUri())),
outputStream.toString()
);
}
@Test
public void givenInvalidRequest_thenReturnError() throws IOException {
Request request = TestUtils.createRequest(INVAliD_REQUEST);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
Response response = new Response(outputStream);
response.setRequest(request);
response.sendStaticResource();
String resource=TestUtils.readFileToString(ConnectorUtils.WEB_ROOT +"/404.html");
Assertions.assertEquals(
(STATUS404 + resource),
outputStream.toString()
);
}
}
三、实现Connector和Processor
Processor
处理用户发送的请求,把请求对应的Response准备好
package processor;
import connector.Request;
import connector.Response;
import java.io.IOException;
/**
* @ClassName:
* @Description:
* @author: hszjj
* @date: 2019/11/26 14:15
*/
public class StaticProcessor {
public void process(Request request, Response response){
try {
response.sendStaticResource();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Connector
package connector;
import processor.ServletProcessor;
import processor.StaticProcessor;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class Connector implements Runnable {
private static final int DEFAULT_PORT=9999;
private ServerSocket server;
private int port;
private void shutDown(Closeable...closeables){
try {
for (Closeable shut:closeables){
if (shut!=null){
shut.close();
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
public Connector(int port){
this.port=port;
}
public Connector(){
this(DEFAULT_PORT);
}
public void start(){
new Thread(this).start();
}
@Override
public void run() {
try {
server=new ServerSocket(port);
System.out.println("服务器已启动,监听端口【"+port+"】");
while (true){
Socket socket=server.accept();
InputStream input=socket.getInputStream();
OutputStream output=socket.getOutputStream();
Request request=new Request(input);
request.parse();
Response response=new Response(output);
response.setRequest(request);
if (request.getRequestUri().startsWith("/servlet/")){
ServletProcessor processor=new ServletProcessor();
processor.process(request,response);
}else {
StaticProcessor processor = new StaticProcessor();
processor.process(request, response);
}
shutDown(socket);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
服务器主函数Bootstrap
import connector.Connector;
/**
* @ClassName:
* @Description:
* @author: hszjj
* @date: 2019/11/26 14:29
*/
public class Bootstrap {
public static void main(String[] args) {
Connector connector=new Connector();
connector.start();
}
}
四、测试服务器
启动客户端
package test;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
/**
* @ClassName:
* @Description:
* @author: hszjj
* @date: 2019/11/26 14:33
*/
public class TestClient {
public static void main(String[] args) {
try {
Socket socket=new Socket("localhost",9999);
OutputStream outputStream=socket.getOutputStream();
outputStream.write("GET /index.html HTTP/1.1".getBytes());
socket.shutdownOutput();
InputStream inputStream=socket.getInputStream();
byte[] buffer=new byte[2048];
int length=inputStream.read(buffer);
StringBuilder response=new StringBuilder();
for (int j=0;j<length;j++){
response.append((char)buffer[j]);
}
System.out.println(response.toString());
socket.shutdownInput();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
结果
效果查看
五、实现ServletRequest和ServletResponse
如图使Request继承ServletRequest接口和重写所有方法。同理,对Response做出同样的修改继承自ServletResponse。
其中,大部分函数都仅仅用默认修改即可,不过要对Response中的getWriter进行改写,方便动态资源写入进行便捷:
@Override
public PrintWriter getWriter() throws IOException {
//自动Flush
PrintWriter writer=new PrintWriter(output,true);
return writer;
}
六、实现Servlet
动态资源的Servletpublic class TimeServlet implements Servlet
主要的Service函数如下:
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
PrintWriter writer=servletResponse.getWriter();
writer.println(ConnectorUtils.renderStatus(HttpStatus.SC_OK));
writer.println("what time is it now?");
writer.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
}