BinCat V3-实现Servlet3.x API
添加Servlet3.x
依赖:
创建com.anbai.sec.server.servlet.BinCatRequest
类并继承javax.servlet.http.HttpServletRequest
,然后需要实现HttpServletRequest
接口方法,作为一个非标准的Servlet容器
我们自然是没必要严格的是实现里面的所有方法,选择几个方法实现一下就行了。
注意:示例以下中省去了解析协议Servlet
接口的代码,完整代码请参考:com.anbai.sec.server.servlet
包下的完整实现代码。
BinCatRequest.java示例代码片段:
package com.anbai.sec.server.servlet;
import org.javaweb.utils.StringUtils;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
import java.net.Socket;
import java.net.URLDecoder;
import java.security.Principal;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Logger;
/**
* BinCat 请求解析实现对象,解析Http请求协议和参数
*/
public class BinCatRequest implements HttpServletRequest {
// 客户端Socket连接对象
private final Socket clientSocket;
// Socket输入流对象
private final InputStream socketInputStream;
// Http请求头对象
private Map<String, String> headerMap;
// Http请求参数对象
private Map<String, String[]> parameterMap;
// Http请求attribute对象
private final Map<String, Object> attributeMap = new ConcurrentHashMap<String, Object>();
// Http请求Cookie对象
private Cookie[] cookie;
// Http请求Cookie对象
private final Map<String, String> cookieMap = new ConcurrentHashMap<String, String>();
// Http请求Session对象
private final Map<String, BinCatSession> sessionMap = new ConcurrentHashMap<String, BinCatSession>();
// Http请求方法类型
private String requestMethod;
// Http请求URL
private String requestURL;
// Http请求QueryString
private String queryString;
// Http请求协议版本信息
private String httpVersion;
// 是否已经解析过Http请求参数,防止多次解析请求参数
private volatile boolean parsedParameter = false;
// Http请求内容长度
private int contentLength;
// Http请求内容类型
private String contentType;
// 存储Session的ID名称
private static final String SESSION_ID_NAME = "JSESSIONID";
// Http请求主机名
private String host;
// Http请求主机端口
private int port;
private static final Logger LOG = Logger.getLogger("info");
public BinCatRequest(Socket clientSocket) throws IOException {
this.clientSocket = clientSocket;
this.socketInputStream = clientSocket.getInputStream();
// 解析Http协议
parse();
}
/**
* 解析Http请求协议,不解析Body部分
*
* @throws IOException
*/
private void parse() throws IOException {
// 此处省略Http请求协议解析、参数解析等内容...
/**
* 解析Http请求参数
* @throws IOException Http协议解析异常
*/
private synchronized void parseParameter() {
// 此处省略Http请求协议解析、参数解析等内容...
}
// 此处省略HttpServletRequest接口中的大部分方法,仅保留几个示例方法...
public String getHeader(String name) {
return this.headerMap.get(name);
}
public ServletInputStream getInputStream() throws IOException {
return new ServletInputStream() {
@Override
public int read() throws IOException {
return socketInputStream.read();
}
};
}
public String getParameter(String name) {
if (!parsedParameter) {
this.parseParameter();
}
if (parameterMap.containsKey(name)) {
return this.parameterMap.get(name)[0];
}
return null;
}
public String getRemoteAddr() {
return clientSocket.getInetAddress().getHostAddress();
}
public void setAttribute(String name, Object o) {
attributeMap.put(name, o);
}
}
HttpServletResponse实现
BinCatResponse.java示例代码片段:
package com.anbai.sec.server.servlet;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.URLEncoder;
import java.util.*;
public class BinCatResponse implements HttpServletResponse {
private final Socket socket;
private final Map<String, String> header;
private final ByteArrayOutputStream out;
private int status = 404;
private String statusMessage = "Not Found";
private String charset = "UTF-8";
private int contentLength = 0;
private String contentType = "text/html; charset=UTF-8";
private String location;
public BinCatResponse(Socket socket, Map<String, String> header, ByteArrayOutputStream out) {
this.socket = socket;
this.header = header;
this.out = out;
}
// 此处省略HttpServletResponse接口中的大部分方法,仅保留几个示例方法...
public void setHeader(String name, String value) {
this.header.put(name, value);
}
public String getHeader(String name) {
return header.get(name);
}
public PrintWriter getWriter() throws IOException {
return new PrintWriter(out);
}
BinCatSession.java示例代码片段:
Servlet类注册
Servlet3.0
支持web.xml
和注解两种方式配置,但不管是通过那种方式都需要知道Servlet
的处理类和映射的URL
地址,这里为了方法理解我将解析web.xml
和扫描@WebServlet
注解的步骤省略了,直接改成了手动配置一个Servlet映射类对象。
// 初始化Servlet映射类对象
final Set<Class<? extends HttpServlet>> servletList = new HashSet<Class<? extends HttpServlet>>();
// 手动注册Servlet类
servletList.add(CMDServlet.class);
当接收到浏览器请求时候我们需要根据请求的URL地址来动态调用Servlet类相关的代码。
调用Servlet类处理Http请求代码片段:
// 处理Http请求URL
for (Class<? extends HttpServlet> clazz : servletList) {
WebServlet webServlet = clazz.getAnnotation(WebServlet.class);
String[] urlPatterns = webServlet.urlPatterns();
for (String urlPattern : urlPatterns) {
try {
// 检测请求的URL地址和Servlet的地址是否匹配
if (Pattern.compile(urlPattern).matcher(uri).find()) {
// 修改状态码
response.setStatus(200, "OK");
// 创建Servlet类实例
HttpServlet httpServlet = clazz.newInstance();
// 调用Servlet请求处理方法
httpServlet.service(request, response);
break;
}
} catch (IOException e) {
// 修改状态码
response.setStatus(500, "Internal Server Error");
e.printStackTrace();
}
}
}
V3
简单的封装了BinCatRequest
、BinCatResponse
、BinCatSession
,还是先了部分的Servlet API
从而实现了一个最初级的Servlet容器
。
V3处理流程:
- 创建服务端
Socket
连接(ServerSocket
)。 - 手动注册
Servlet
类。 - 创建用于处理请求的
BinCatRequest
对象。 BinCatRequest
解析请求协议、请求头、请求参数、Cookie
等。- 创建用于处理请求的
BinCatResponse
对象。 - 解析
Servlet
类的@WebServlet
注解,反射调用Servlet
类方法处理Http请求。 - 输出响应信息以及
Servlet
处理结果。 - 关闭
Socket
连接。
BinCatServerV3实现代码:
Servlet功能测试
为了验证BinCat
是否真的具备了Servlet
处理能力,我们写两个测试用例:TestServlet
和CMDServlet
。
TestServlet示例代码:
package com.anbai.sec.server.test.servlet;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
@WebServlet(name = "TestServlet", urlPatterns = "/TestServlet/")
public class TestServlet extends HttpServlet {
@Override
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
doPost(request, response);
}
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {
OutputStream out = response.getOutputStream();
out.write(("Hello....<br/>Request Method:" + request.getMethod() + "<br/>Class:" + this.getClass()).getBytes());
}
}
CMDServlet示例代码:
package com.anbai.sec.server.test.servlet;
import org.javaweb.utils.IOUtils;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
@WebServlet(name = "CMDServlet", urlPatterns = "/CMD/")
public class CMDServlet extends HttpServlet {
@Override
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
doPost(request, response);
}
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {
String cmd = request.getParameter("cmd");
byte[] bytes = IOUtils.toByteArray(Runtime.getRuntime().exec(cmd).getInputStream());
OutputStream out = response.getOutputStream();
out.write(bytes);
out.flush();
out.close();
}
浏览器请求:
使用curl
发送POST请求:,服务器可以正常接收POST参数,处理结果如图:
请求一个错误服务:
至此,我们已经实现了一个非常初级的Servlet容器
了。