打造自己的web容器(4)

本文介绍了一个简易Servlet容器的搭建过程,包括连接器的工作原理、HttpRequest和HttpResponse对象的解析及核心处理流程。
上一篇我们实现了一个极其简单的servlet容器,这一章,将在上一章的基础上进行扩充。我们以Tomcat的默认连接器为蓝本,逐步解剖连接器,虽然该连接器已不推荐使用,被性能更高的coyote所代替,但它仍是一个学习的好工具。

这一章,我们将完成如下任务:
1、解决上一篇的遗留问题
2、模拟Catalina的架构,重构我们的系统
3、实现HttpRequest的部分方法,包括:解析cookies,header,parameter等

这章的应用程序由三个模块组成:connector、startup和core,其中:
startup模块只有一个类,Bootstrap,用来启动应用的。
connector模块的类可以分为五组:
* 连接器和它的支撑类(HttpConnector和HttpProcessor)。
* 指代HTTP请求的类(HttpRequest)和它的辅助类。
* 指代HTTP响应的类(HttpResponse)和它的辅助类。
* Facade类(HttpRequestFacade和HttpResponseFacade)。
* Constant类
core模块由两个类组成:ServletProcessor和StaticResourceProcessor。


启动类:Bootstrap

public final class Bootstrap {
public static void main(String[] args) {
HttpConnector connector = new HttpConnector();
connector.start();
}
}



/**
* 连接器
* 1、等待HTTP请求
* @author <a href="mailto:cwq2006@163.com">chen wen quan</a>
*
*/
public class HttpConnector implements Runnable {

// shutdown command
private static final String SHUTDOWN_COMMAND = "/SHUTDOWN";

// the shutdown command received
private boolean shutdown = false;

private String scheme = "http";

public String getScheme() {

return scheme;
}

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);
}
// Loop waiting for a request
while (!shutdown) {
Socket socket = null;
try {
socket = serverSocket.accept();
}
catch (Exception e) {
continue;
}
// Hand this socket off to an HttpProcessor
HttpProcessor processor = new HttpProcessor();
processor.process(socket);
}
}

public void start() {
Thread thread = new Thread(this);
thread.start();
}
}


/**
* 连接器的支撑类,其主要作用为:
* 1 接受前来的HTTP请求的套接字
* 2 创建一个HttpRequest、HttpResponse对象
* 3 解析HTTP请求的第一行和头部,并放到HttpRequest对象
* 4 解析HttpRequest和HttpResponse对象到一个ServletProcessor或者 StaticResourceProcessor
* @author <a href="mailto:cwq2006@163.com">chen wen quan</a>
*
*/
public class HttpProcessor {

private HttpRequest request = null;

private HttpResponse response = null;

private String requestContent = null;

public void process(Socket socket) {

InputStream input = null;
OutputStream output = null;
try {
input = socket.getInputStream();
output = socket.getOutputStream();
// create HttpRequest object and parse
request = new HttpRequest(input);
// create HttpResponse object
response = new HttpResponse(output);
response.setRequest(request);
response.setHeader("Server", "Pyrmont Servlet Container");
parse(input);
parseRequest(input, output);
parseHeaders(input);
//check if this is a request for a servlet or a static resource
//a request for a servlet begins with "/servlet/"
if (request.getRequestURI().startsWith("/servlet/")) {
ServletProcessor processor = new ServletProcessor();
processor.process(request, response);
}
else {
StaticResourceProcessor processor = new StaticResourceProcessor();
processor.process(request, response);
}
// Close the socket
socket.close();
// no shutdown for this application
}
catch (Exception e) {
e.printStackTrace();
}
}

private void parseHeaders(InputStream input) {

// TODO Auto-generated method stub

}

public void parse(InputStream input) {

// Read a set of characters from the socket
StringBuffer request = new StringBuffer(2048);
int i;
byte[] buffer = new byte[2048];
try {
i = input.read(buffer);
}
catch (IOException e) {
e.printStackTrace();
i = -1;
}
for (int j = 0; j < i; j++) {
request.append((char) buffer[j]);
}
System.out.println(request.toString());
requestContent = request.toString();
}

private void parseRequest(InputStream input, OutputStream output) throws ServletException {

String requestHeaderFirstLine = requestContent.substring(0, requestContent.indexOf("\n"));
String headerInfo[] = requestHeaderFirstLine.split(" ");
String method = headerInfo[0];
String uri = headerInfo[1];
String protocol = headerInfo[2];
// Validate the incoming request line
if (method.length() < 1) {
throw new ServletException("Missing HTTP request method");
}
// Parse any query parameters out of the request URI
int question = uri.indexOf("?");
if (question >= 0) {
request.setQueryString(uri.substring(question - 1));
uri = uri.substring(0, question);
}
else {
request.setQueryString(null);
}
// Checking for an absolute URI (with the HTTP protocol)
if (!uri.startsWith("/")) {
int pos = uri.indexOf("://");
// Parsing out protocol and host name
if (pos != -1) {
pos = uri.indexOf('/', pos + 3);
if (pos == -1) {
uri = "";
}
else {
uri = uri.substring(pos);
}
}
}
// Parse any requested session ID out of the request URI
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);
}
// Normalize URI (using String operations at the moment)
String normalizedUri = normalize(uri);//检查URL是否合法
// Set the corresponding request properties
request.setMethod(method);
request.setProtocol(protocol);
if (normalizedUri != null) {
request.setRequestURI(normalizedUri);
}
else {
request.setRequestURI(uri);
}
if (normalizedUri == null) {
throw new ServletException("Invalid URI: " + uri + "'");
}

}

private String normalize(String uri) {

if (uri == null)
return null;

// Create a place for the normalized path
String normalized = uri;

if (normalized.equals("/."))
return "/";

// Add a leading "/" if necessary
if (!normalized.startsWith("/"))
normalized = "/" + normalized;

// Resolve occurrences of "//" in the normalized path
while (true) {
int index = normalized.indexOf("//");
if (index < 0)
break;
normalized = normalized.substring(0, index) + normalized.substring(index + 1);
}

// Resolve occurrences of "/./" in the normalized path
while (true) {
int index = normalized.indexOf("/./");
if (index < 0)
break;
normalized = normalized.substring(0, index) + normalized.substring(index + 2);
}

// Resolve occurrences of "/../" in the normalized path
while (true) {
int index = normalized.indexOf("/../");
if (index < 0)
break;
if (index == 0)
return (null); // Trying to go outside our context
int index2 = normalized.lastIndexOf('/', index - 1);
normalized = normalized.substring(0, index2) + normalized.substring(index + 3);
}

// Return the normalized path that we have completed
return (normalized);
}
}


public class RequestUtil {

/**
* 解析cookie
* Cookie: userName=budi; password=pwd;
* @param header
* @return
*/
public static Cookie[] parseCookieHeader(String header) {

if ((header == null) || (header.length() < 1))
return (new Cookie[0]);
List cookies = new ArrayList();
while (header.length() > 0) {
int semicolon = header.indexOf(';');
if (semicolon < 0)
semicolon = header.length();
if (semicolon == 0)
break;
String token = header.substring(0, semicolon);
if (semicolon < header.length())
header = header.substring(semicolon + 1);
else
header = "";
try {
int equals = token.indexOf('=');
if (equals > 0) {
String name = token.substring(0, equals).trim();
String value = token.substring(equals + 1).trim();
cookies.add(new Cookie(name, value));
}
}
catch (Throwable e) {
;
}
}
return ((Cookie[]) cookies.toArray(new Cookie[cookies.size()]));
}

/**
* 解析请求参数
* @param map
* @param data 由 "userName=cwq&password=pwd".getBytes() 得
* @param encoding 字符编码
* @return
*/
public static void parseParameters(Map map, byte[] data, String encoding) throws UnsupportedEncodingException {

if (data != null && data.length > 0) {
int ix = 0;
int ox = 0;
String key = null;
String value = null;
while (ix < data.length) {
byte c = data[ix++];
switch ((char) c) {
case '&':
value = new String(data, 0, ox, encoding);
if (key != null) {
putMapEntry(map, key, value);
key = null;
}
ox = 0;
break;
case '=':
if (key == null) {
key = new String(data, 0, ox, encoding);
ox = 0;
}
else {
data[ox++] = c;
}
break;
case '+':
data[ox++] = (byte) ' ';
break;
case '%':
data[ox++] = (byte) ((convertHexDigit(data[ix++]) << 4) + convertHexDigit(data[ix++]));
break;
default:
data[ox++] = c;
}
}
//The last value does not end in '&'. So save it now.
if (key != null) {
value = new String(data, 0, ox, encoding);
putMapEntry(map, key, value);
}
}
}

private static void putMapEntry(Map map, String name, String value) {

String[] newValues = null;
String[] oldValues = (String[]) map.get(name);
if (oldValues == null) {
newValues = new String[1];
newValues[0] = value;
}
else {
newValues = new String[oldValues.length + 1];
System.arraycopy(oldValues, 0, newValues, 0, oldValues.length);
newValues[oldValues.length] = value;
}
map.put(name, newValues);
}

private static byte convertHexDigit( byte b ) {
if ((b >= '0') && (b <= '9')) return (byte)(b - '0');
if ((b >= 'a') && (b <= 'f')) return (byte)(b - 'a' + 10);
if ((b >= 'A') && (b <= 'F')) return (byte)(b - 'A' + 10);
return 0;
}
}
内容概要:本文介绍了ENVI Deep Learning V1.0的操作教程,重点讲解了如何利用ENVI软件进行深度学习模型的训练与应用,以实现遥感图像中特定目标(如集装箱)的自动提取。教程涵盖了从数据准备、标签图像创建、模型初始化与训练,到执行分类及结果优化的完整流程,并介绍了精度评价与通过ENVI Modeler实现一键化建模的方法。系统基于TensorFlow框架,采用ENVINet5(U-Net变体)架构,支持通过点、线、面ROI或分类图生成标签数据,适用于多/高光谱影像的单一类别特征提取。; 适合人群:具备遥感图像处理基础,熟悉ENVI软件操作,从事地理信息、测绘、环境监测等相关领域的技术人员或研究人员,尤其是希望将深度学习技术应用于遥感目标识别的初学者与实践者。; 使用场景及目标:①在遥感影像中自动识别和提取特定地物目标(如车辆、建筑、道路、集装箱等);②掌握ENVI环境下深度学习模型的训练流程与关键参数设置(如Patch Size、Epochs、Class Weight等);③通过模型调优与结果反馈提升分类精度,实现高效自动化信息提取。; 阅读建议:建议结合实际遥感项目边学边练,重点关注标签数据制作、模型参数配置与结果后处理环节,充分利用ENVI Modeler进行自动化建模与参数优化,同时注意软硬件环境(特别是NVIDIA GPU)的配置要求以保障训练效率。
内容概要:本文系统阐述了企业新闻发稿在生成式引擎优化(GEO)时代下的全渠道策略与效果评估体系,涵盖当前企业传播面临的预算、资源、内容与效果评估四大挑战,并深入分析2025年新闻发稿行业五大趋势,包括AI驱动的智能化转型、精准化传播、首发内容价值提升、内容资产化及数据可视化。文章重点解析央媒、地方官媒、综合门户和自媒体四类媒体资源的特性、传播优势与发稿策略,提出基于内容适配性、时间节奏、话题设计的策略制定方法,并构建涵盖品牌价值、销售转化与GEO优化的多维评估框架。此外,结合“传声港”工具实操指南,提供AI智能投放、效果监测、自媒体管理与舆情应对的全流程解决方案,并针对科技、消费、B2B、区域品牌四大行业推出定制化发稿方案。; 适合人群:企业市场/公关负责人、品牌传播管理者、数字营销从业者及中小企业决策者,具备一定媒体传播经验并希望提升发稿效率与ROI的专业人士。; 使用场景及目标:①制定科学的新闻发稿策略,实现从“流量思维”向“价值思维”转型;②构建央媒定调、门户扩散、自媒体互动的立体化传播矩阵;③利用AI工具实现精准投放与GEO优化,提升品牌在AI搜索中的权威性与可见性;④通过数据驱动评估体系量化品牌影响力与销售转化效果。; 阅读建议:建议结合文中提供的实操清单、案例分析与工具指南进行系统学习,重点关注媒体适配性策略与GEO评估指标,在实际发稿中分阶段试点“AI+全渠道”组合策略,并定期复盘优化,以实现品牌传播的长期复利效应。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值