package jakarta.servlet.http; import jakarta.servlet.ServletResponse; import jakarta.servlet.ServletRequest; import java.text.MessageFormat; import jakarta.servlet.ServletOutputStream; import java.util.Enumeration; import java.lang.reflect.Method; import jakarta.servlet.DispatcherType; import java.io.IOException; import jakarta.servlet.ServletException; import java.util.ResourceBundle; import jakarta.servlet.GenericServlet; public abstract class HttpServlet extends GenericServlet { private static final long serialVersionUID = 1L; private static final String METHOD_DELETE = "DELETE"; private static final String METHOD_HEAD = "HEAD"; private static final String METHOD_GET = "GET"; private static final String METHOD_OPTIONS = "OPTIONS"; private static final String METHOD_POST = "POST"; private static final String METHOD_PUT = "PUT"; private static final String METHOD_TRACE = "TRACE"; private static final String HEADER_IFMODSINCE = "If-Modified-Since"; private static final String HEADER_LASTMOD = "Last-Modified"; private static final String LSTRING_FILE = "jakarta.servlet.http.LocalStrings"; private static final ResourceBundle lStrings; private final Object cachedAllowHeaderValueLock; private volatile String cachedAllowHeaderValue; public HttpServlet() { this.cachedAllowHeaderValueLock = new Object(); this.cachedAllowHeaderValue = null; } protected void doGet(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException { final String msg = HttpServlet.lStrings.getString("http.method_get_not_supported"); this.sendMethodNotAllowed(req, resp, msg); } protected long getLastModified(final HttpServletRequest req) { return -1L; } protected void doHead(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException { if (DispatcherType.INCLUDE.equals((Object)req.getDispatcherType())) { this.doGet(req, resp); } else { final NoBodyResponse response = new NoBodyResponse(resp); this.doGet(req, (HttpServletResponse)response); response.setContentLength(); } } protected void doPost(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException { final String msg = HttpServlet.lStrings.getString("http.method_post_not_supported"); this.sendMethodNotAllowed(req, resp, msg); } protected void doPut(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException { final String msg = HttpServlet.lStrings.getString("http.method_put_not_supported"); this.sendMethodNotAllowed(req, resp, msg); } protected void doDelete(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException { final String msg = HttpServlet.lStrings.getString("http.method_delete_not_supported"); this.sendMethodNotAllowed(req, resp, msg); } private void sendMethodNotAllowed(final HttpServletRequest req, final HttpServletResponse resp, final String msg) throws IOException { final String protocol = req.getProtocol(); if (protocol.length() == 0 || protocol.endsWith("0.9") || protocol.endsWith("1.0")) { resp.sendError(400, msg); } else { resp.sendError(405, msg); } } private String getCachedAllowHeaderValue() { if (this.cachedAllowHeaderValue == null) { synchronized (this.cachedAllowHeaderValueLock) { if (this.cachedAllowHeaderValue == null) { final Method[] methods = getAllDeclaredMethods(this.getClass()); boolean allowGet = false; boolean allowHead = false; boolean allowPost = false; boolean allowPut = false; boolean allowDelete = false; for (final Method method : methods) { final String name = method.getName(); switch (name) { case "doGet": { allowGet = true; allowHead = true; break; } case "doPost": { allowPost = true; break; } case "doPut": { allowPut = true; break; } case "doDelete": { allowDelete = true; break; } } } final StringBuilder allow = new StringBuilder(); if (allowGet) { allow.append("GET"); allow.append(", "); } if (allowHead) { allow.append("HEAD"); allow.append(", "); } if (allowPost) { allow.append("POST"); allow.append(", "); } if (allowPut) { allow.append("PUT"); allow.append(", "); } if (allowDelete) { allow.append("DELETE"); allow.append(", "); } allow.append("OPTIONS"); this.cachedAllowHeaderValue = allow.toString(); } } } return this.cachedAllowHeaderValue; } private static Method[] getAllDeclaredMethods(final Class c) { if (c.equals(HttpServlet.class)) { return null; } final Method[] parentMethods = getAllDeclaredMethods(c.getSuperclass()); Method[] thisMethods = c.getDeclaredMethods(); if (parentMethods != null && parentMethods.length > 0) { final Method[] allMethods = new Method[parentMethods.length + thisMethods.length]; System.arraycopy(parentMethods, 0, allMethods, 0, parentMethods.length); System.arraycopy(thisMethods, 0, allMethods, parentMethods.length, thisMethods.length); thisMethods = allMethods; } return thisMethods; } protected void doOptions(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException { String allow = this.getCachedAllowHeaderValue(); if (HttpServlet.TomcatHack.getAllowTrace(req)) { if (allow.length() == 0) { allow = "TRACE"; } else { allow = allow + ", " + "TRACE"; } } resp.setHeader("Allow", allow); } protected void doTrace(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException { final String CRLF = "\r\n"; final StringBuilder buffer = new StringBuilder("TRACE ").append(req.getRequestURI()).append(" ").append(req.getProtocol()); final Enumeration reqHeaderEnum = (Enumeration)req.getHeaderNames(); while (reqHeaderEnum.hasMoreElements()) { final String headerName = reqHeaderEnum.nextElement(); buffer.append(CRLF).append(headerName).append(": ").append(req.getHeader(headerName)); } buffer.append(CRLF); final int responseLength = buffer.length(); resp.setContentType("message/http"); resp.setContentLength(responseLength); final ServletOutputStream out = resp.getOutputStream(); out.print(buffer.toString()); out.close(); } protected void service(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException { final String method = req.getMethod(); if (method.equals("GET")) { final long lastModified = this.getLastModified(req); if (lastModified == -1L) { this.doGet(req, resp); } else { long ifModifiedSince; try { ifModifiedSince = req.getDateHeader("If-Modified-Since"); } catch (IllegalArgumentException iae) { ifModifiedSince = -1L; } if (ifModifiedSince < lastModified / 1000L * 1000L) { this.maybeSetLastModified(resp, lastModified); this.doGet(req, resp); } else { resp.setStatus(304); } } } else if (method.equals("HEAD")) { final long lastModified = this.getLastModified(req); this.maybeSetLastModified(resp, lastModified); this.doHead(req, resp); } else if (method.equals("POST")) { this.doPost(req, resp); } else if (method.equals("PUT")) { this.doPut(req, resp); } else if (method.equals("DELETE")) { this.doDelete(req, resp); } else if (method.equals("OPTIONS")) { this.doOptions(req, resp); } else if (method.equals("TRACE")) { this.doTrace(req, resp); } else { String errMsg = HttpServlet.lStrings.getString("http.method_not_implemented"); final Object[] errArgs = { method }; errMsg = MessageFormat.format(errMsg, errArgs); resp.sendError(501, errMsg); } } private void maybeSetLastModified(final HttpServletResponse resp, final long lastModified) { if (resp.containsHeader("Last-Modified")) { return; } if (lastModified >= 0L) { resp.setDateHeader("Last-Modified", lastModified); } } public void service(final ServletRequest req, final ServletResponse res) throws ServletException, IOException { HttpServletRequest request; HttpServletResponse response; try { request = (HttpServletRequest)req; response = (HttpServletResponse)res; } catch (ClassCastException e) { throw new ServletException(HttpServlet.lStrings.getString("http.non_http")); } this.service(request, response); } static { lStrings = ResourceBundle.getBundle("jakarta.servlet.http.LocalStrings"); } }