本节列出开发Servlet服务器时需要注意的一些常见问题。
根据Servlet规范,getOutputStream()和getWriter()在一次HTTP处理中只能二选一,不能都调用,因此,HttpServletResponse内部会用callOutput记录调用状态:
getOutputStream()和getWriter();getOutputStream();getWriter()。违反调用规则会抛出IllegalStateException。
因为Web App可能不会调用getOutputStream()或getWriter(),而是直接设置Header后返回:
resp.setStatus(403);
此时,HttpConnector要调用cleanup(),如果发现没有发送Header,则需要立刻发送Header,否则浏览器无法收到响应。
此外,根据HttpConnector的实现方式,基于JDK的HttpExchange的OutputStream也需要关闭(但不一定会关闭对应的TCP连接)。
Servlet需要根据映射进行排序,遵循以下原则:
/auth/login排在/auth前;/auth/*排在*.do前。根据Servlet规范,/相当于/*,但还是有所不同,因为/表示默认的Servlet,即所有规则均不匹配时,最后匹配/。
如果一个Web App没有提供/映射,则Web Server可以自动提供一个默认的映射到/的Servlet。Jerrymouse和Tomcat类似,提供一个浏览文件的DefaultServlet。
处理静态文件时,将URL路径/path/to/file.doc转换为本地文件路径${webroot}/path/to/file.doc,然后根据扩展名设置正确的Content-Type,读取文件内容,发送即可。
需要注意的是,Servlet规范规定,不允许访问/WEB-INF/开头的URL,因此,遇到访问/WEB-INF/*的请求时,直接返回404错误码。
Servlet规范没有对Filter排序的要求,但我们在实现时还是按@WebFilter的filterName()进行排序,这样Web App可以根据名称调整Filter的顺序。
默认情况下,Jerrymouse Server采用线程池模式,要启用虚拟线程,可以加上配置项,以创建不同类型的ExecutorService:
ExecutorService executor = config.server.enableVirtualThread ?
Executors.newVirtualThreadPerTaskExecutor() :
new ThreadPoolExecutor(0, config.server.threadPoolSize, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>());