An HTTP request sent by a browser contains a method, path, and headers. To represent this server-side, we define a class that parses the raw input stream.
package tomcat;
import java.io.IOException;
import java.io.InputStream;
public class IRequest {
private String url;
private String method;
public IRequest(InputStream inputStream) throws IOException {
byte[] buffer = new byte[1024];
int length = inputStream.read(buffer);
if (length > 0) {
String request = new String(buffer, 0, length);
System.out.println("Request Body:\r\n" + request);
String[] lines = request.split("\n");
if (lines.length > 0) {
String[] parts = lines[0].split("\\s");
if (parts.length >= 2) {
method = parts[0];
url = parts[1];
}
}
}
}
public String getUrl() { return url; }
public String getMethod() { return method; }
}
The response must be formatted as HTTP. We create a simple class that writes a HTTP response with HTML content.
package tomcat;
import java.io.IOException;
import java.io.OutputStream;
public class IResponse {
private OutputStream outputStream;
public IResponse(OutputStream outputStream) {
this.outputStream = outputStream;
}
public void write(String content) throws IOException {
StringBuilder sb = new StringBuilder();
sb.append("HTTP/1.1 200 OK\n");
sb.append("Content-Type: text/html\n");
sb.append("\r\n");
sb.append("<html><body>");
sb.append(content);
sb.append("</body></html>");
outputStream.write(sb.toString().getBytes());
outputStream.close();
}
}
Next, we define an abstract servlet with the service method dispatching to doGet or doPost.
package tomcat;
public abstract class IServlet {
public abstract void doGet(IRequest request, IResponse response);
public abstract void doPost(IRequest request, IResponse response);
public void service(IRequest request, IResponse response) {
if ("POST".equalsIgnoreCase(request.getMethod())) {
doPost(request, response);
} else if ("GET".equalsIgnoreCase(request.getMethod())) {
doGet(request, response);
}
}
}
A concrete implementation returns "hello world".
package tomcat.impl;
import tomcat.IRequest;
import tomcat.IResponse;
import tomcat.IServlet;
import java.io.IOException;
public class HelloServletImpl extends IServlet {
@Override
public void doGet(IRequest request, IResponse response) {
try {
response.write("hello world");
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void doPost(IRequest request, IResponse response) {
try {
response.write("hello post");
} catch (IOException e) {
e.printStackTrace();
}
}
}
We then map URLs to servlet classes.
package tomcat;
public class ServletMapping {
private String servletName;
private String url;
private Class<?> clazz;
public ServletMapping(String servletName, String url, Class<?> clazz) {
this.servletName = servletName;
this.url = url;
this.clazz = clazz;
}
public String getServletName() { return servletName; }
public String getUrl() { return url; }
public Class<?> getClazz() { return clazz; }
}
Configuration holds the list of mappings.
package tomcat;
import tomcat.impl.HelloServletImpl;
import java.util.ArrayList;
import java.util.List;
public class ServletMappingConfig {
public static List<ServletMapping> servletMappings = new ArrayList<>();
static {
servletMappings.add(new ServletMapping("hello", "/hello", HelloServletImpl.class));
servletMappings.add(new ServletMapping("hello", "/aa", HelloServletImpl.class));
}
}
Finally, the server listens on a port, accepts connections, and dispatches requests.
package tomcat;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
public class TomcatServer {
private int port;
private Map<String, Class<?>> urlServletMap = new HashMap<>();
public TomcatServer(int port) {
this.port = port;
}
public void start() {
initServletMapping();
try (ServerSocket serverSocket = new ServerSocket(port)) {
System.out.println("Tomcat running on port " + port);
while (true) {
try (Socket socket = serverSocket.accept()) {
InputStream is = socket.getInputStream();
OutputStream os = socket.getOutputStream();
IRequest request = new IRequest(is);
IResponse response = new IResponse(os);
dispatch(request, response);
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
private void initServletMapping() {
for (ServletMapping mapping : ServletMappingConfig.servletMappings) {
urlServletMap.put(mapping.getUrl(), mapping.getClazz());
}
}
private void dispatch(IRequest request, IResponse response) {
Class<?> clazz = urlServletMap.get(request.getUrl());
if (clazz != null) {
try {
IServlet servlet = (IServlet) clazz.getDeclaredConstructor().newInstance();
servlet.service(request, response);
} catch (Exception e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
new TomcatServer(8081).start();
}
}
This implementation handles GET and POST requests for two paths (/hello and /aa). The server reads the raw request, extracts the method and URL, looks up the mapping, and invokes the appropriate servlet method.