MiniCat Basic Introduction
We will hand-write a Mini version of Tomcat. What we implement is a server software that can receive HTTP requests from browser clients, process them through MiniCat, and return the processing results to the browser client.
- Provide service, accept requests (Socket communication)
- Request information encapsulated as Request object (Response object)
- Client requests resources, resources divided into static resources (HTML) and dynamic resources (Servlet)
- Return resources to client browser
Response Request Response Class
Defined a public class named Response, used to encapsulate logic for sending data to client (such as HTML, text, etc.).
public class Response {
private OutputStream outputStream;
public Response() {
}
public Response(OutputStream outputStream) {
this.outputStream = outputStream;
}
public void output(String content) throws IOException {
outputStream.write(content.getBytes());
}
public void outputHtml(String path) throws IOException {
String absoluteResourcePath = StaticResourceUtil.getAbsolutePath(path);
File file = new File(absoluteResourcePath);
if (file.exists() && file.isFile()) {
StaticResourceUtil.outputStaticResource(new FileInputStream(file), outputStream);
} else {
output(HttpProtocoUtil.getHttpHeader404());
}
}
}
OutputStream is the base class of Java I/O, used to output byte data to external (such as client’s socket). Its typical usage scenario is writing response content to browser in Web service.
Main responsibilities of Response class:
- Encapsulate output stream: Take OutputStream as the core resource of the class, used to write data to client
- Text output: Can directly output a string to client (such as returning HTTP response headers and body)
- HTML output: Read static HTML file from file system and write back to client
- 404 handling: Automatically return standard 404 response when file does not exist
Servlet Interface
public interface Servlet {
void init() throws Exception;
void destroy() throws Exception;
void service(Request request, Response response) throws Exception;
}
StaticResourceUtil Static Resources
public class StaticResourceUtil {
public static String getAbsolutePath(String path) {
String absolutePath = StaticResourceUtil.class.getResource("/").getPath();
return absolutePath.replace("\\\\", "/") + path;
}
public static void outputStaticResource(InputStream inputStream, OutputStream outputStream) throws IOException {
int count = 0;
while (count == 0) {
count = inputStream.available();
}
int resourceSize = count;
outputStream.write(HttpProtocoUtil.getHttpHeader200(resourceSize).getBytes());
long written = 0;
int byteSize = 1024;
byte[] bytes = new byte[byteSize];
while (written < resourceSize) {
if (written + byteSize > resourceSize) {
byteSize = (int) (resourceSize - written);
bytes = new byte[byteSize];
}
inputStream.read(bytes);
outputStream.write(bytes);
outputStream.flush();
written += byteSize;
}
}
}
Get absolute path of resource: getAbsolutePath gets the current class’s classpath root directory path, gets the actual path, finally concatenates path after classpath, returns absolute path of static resource.
Output static resource content: In outputStaticResource, inputStream.available() tries to get file length, but using while (count == 0) waiting for it to be non-zero is unstable. available() is not equivalent to “file size”. It is “currently readable bytes”, not suitable for estimating total resource length.
Overall summary:
- Class name: StaticResourceUtil, utility class
- Function: Get resource path, output file content to HTTP
- Highlight: Simulate Web Server sending HTML and other static files
- Key issues: Misuse of available(), not checking read byte count, frequent array allocation
- Optimization direction: Use readAllBytes() or while(read != -1) pattern instead of original reading logic
WzkServlet Implementation Class
This is the usage of previous content, established our own Servlet, then start the service!
public class WzkServlet extends HttpServlet {
@Override
public void doGet(Request request, Response response) {
String content = "<h1>WzkServlet GET Hello World!</h1>";
try {
response.output(HttpProtocoUtil.getHttpHeader200(content.getBytes().length) + content);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void doPost(Request request, Response response) {
String content = "<h1>WzkServlet POST Hello World!</h1>";
try {
response.output(HttpProtocoUtil.getHttpHeader200(content.getBytes().length) + content);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void init() throws Exception {
}
@Override
public void destroy() throws Exception {
}
}
Resource Files
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>wzk-icu</title>
</head>
<body>
<h1>
wzk-icu Test Page!
</h1>
</body>
</html>
web.xml
<?xml version="1.0" encoding="UTF-8" ?>
<web-app>
<servlet>
<servlet-name>wzkicu</servlet-name>
<servlet-class>icu.wzk.WzkServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>wzkicu</servlet-name>
<url-pattern>/wzkicu</url-pattern>
</servlet-mapping>
</web-app>
Startup Test
After starting the service, access port 8080. You can see the corresponding content output in the console, and the browser successfully displays the page.