TinyHttpHandler.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one
  3.  * or more contributor license agreements. See the NOTICE file
  4.  * distributed with this work for additional information
  5.  * regarding copyright ownership. The ASF licenses this file
  6.  * to you under the Apache License, Version 2.0 (the
  7.  * "License"); you may not use this file except in compliance
  8.  * with the License. You may obtain a copy of the License at
  9.  *
  10.  *   http://www.apache.org/licenses/LICENSE-2.0
  11.  *
  12.  * Unless required by applicable law or agreed to in writing,
  13.  * software distributed under the License is distributed on an
  14.  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  15.  * KIND, either express or implied. See the License for the
  16.  * specific language governing permissions and limitations
  17.  * under the License.
  18.  */
  19. package net.morimekta.tiny.server.http;

  20. import com.sun.net.httpserver.HttpExchange;
  21. import com.sun.net.httpserver.HttpHandler;
  22. import org.slf4j.Logger;
  23. import org.slf4j.LoggerFactory;

  24. import java.io.IOException;
  25. import java.lang.reflect.Method;
  26. import java.util.Set;
  27. import java.util.TreeSet;

  28. /**
  29.  * Class simplifying the HttpHandler in a similar fashion to the jakarta
  30.  * <code>HttpServlet</code> class. Just implement the matching method to
  31.  * the HTTP method you need served.
  32.  */
  33. public abstract class TinyHttpHandler implements HttpHandler {
  34.     private static final Logger LOGGER = LoggerFactory.getLogger(TinyHttpHandler.class);

  35.     @Override
  36.     public void handle(HttpExchange exchange) throws IOException {
  37.         try {
  38.             exchange.getResponseHeaders().set("Server", "tiny-server");
  39.             switch (exchange.getRequestMethod()) {
  40.                 case "GET":
  41.                     doGet(exchange);
  42.                     break;
  43.                 case "POST":
  44.                     doPost(exchange);
  45.                     break;
  46.                 case "HEAD":
  47.                     doHead(exchange);
  48.                     break;
  49.                 case "OPTIONS":
  50.                     doOptions(exchange);
  51.                     break;
  52.                 case "TRACE":
  53.                     doTrace(exchange);
  54.                     break;
  55.                 case "PUT":
  56.                     doPut(exchange);
  57.                     break;
  58.                 case "DELETE":
  59.                     doDelete(exchange);
  60.                     break;
  61.                 default: {
  62.                     LOGGER.warn("Unknown request method: {} {} HTTP/1.1",
  63.                                 exchange.getRequestMethod(),
  64.                                 exchange.getRequestURI().getPath());
  65.                     exchange.sendResponseHeaders(TinyHttpStatus.SC_METHOD_NOT_ALLOWED, 0);
  66.                 }
  67.             }
  68.         } catch (Exception e) {
  69.             if (exchange.getResponseCode() <= 0) {
  70.                 LOGGER.warn("Exception handling: {} {} HTTP/1.1",
  71.                             exchange.getRequestMethod(),
  72.                             exchange.getRequestURI().getPath(),
  73.                             e);
  74.                 exchange.sendResponseHeaders(TinyHttpStatus.SC_INTERNAL, 0);
  75.             } else {
  76.                 LOGGER.error("Exception handling: {} {} HTTP/1.1",
  77.                              exchange.getRequestMethod(),
  78.                              exchange.getRequestURI().getPath(),
  79.                              e);
  80.             }
  81.         } finally {
  82.             exchange.close();
  83.         }
  84.     }

  85.     /**
  86.      * Handle a GET request.
  87.      *
  88.      * @param exchange The HTTP exchange.
  89.      * @throws IOException If failed to handle the request.
  90.      */
  91.     protected void doGet(HttpExchange exchange) throws IOException {
  92.         exchange.sendResponseHeaders(TinyHttpStatus.SC_METHOD_NOT_ALLOWED, 0);
  93.     }

  94.     /**
  95.      * Handle a POST request.
  96.      *
  97.      * @param exchange The HTTP exchange.
  98.      * @throws IOException If failed to handle the request.
  99.      */
  100.     protected void doPost(HttpExchange exchange) throws IOException {
  101.         exchange.sendResponseHeaders(TinyHttpStatus.SC_METHOD_NOT_ALLOWED, 0);
  102.     }

  103.     /**
  104.      * Handle a HEAD request.
  105.      *
  106.      * @param exchange The HTTP exchange.
  107.      * @throws IOException If failed to handle the request.
  108.      */
  109.     protected void doHead(HttpExchange exchange) throws IOException {
  110.         exchange.sendResponseHeaders(TinyHttpStatus.SC_METHOD_NOT_ALLOWED, -1);
  111.     }

  112.     /**
  113.      * Handle an OPTIONS request.
  114.      *
  115.      * @param exchange The HTTP exchange.
  116.      * @throws IOException If failed to handle the request.
  117.      */
  118.     protected void doOptions(HttpExchange exchange) throws IOException {
  119.         Set<String> methods = new TreeSet<>();
  120.         Set<String> corsMethods = new TreeSet<>();
  121.         for (Method method : getClass().getDeclaredMethods()) {
  122.             switch (method.getName()) {
  123.                 case "doGet":
  124.                     methods.add("GET");
  125.                     corsMethods.add("GET");
  126.                     break;
  127.                 case "doPost":
  128.                     methods.add("POST");
  129.                     break;
  130.                 case "doHead":
  131.                     methods.add("HEAD");
  132.                     break;
  133.                 case "doTrace":
  134.                     methods.add("TRACE");
  135.                     break;
  136.                 case "doPut":
  137.                     methods.add("PUT");
  138.                     break;
  139.                 case "doDelete":
  140.                     methods.add("DELETE");
  141.                     break;
  142.                 default:
  143.                     break;
  144.             }
  145.         }
  146.         if (!methods.isEmpty()) {
  147.             exchange.getResponseHeaders().set("Allow", String.join(", ", methods));
  148.         }
  149.         if (!corsMethods.isEmpty()) {
  150.             exchange.getResponseHeaders().set("Access-Control-Allow-Methods", String.join(", ", corsMethods));
  151.         }
  152.         if (methods.isEmpty() && corsMethods.isEmpty()) {
  153.             // ... nothing implemented?
  154.             exchange.sendResponseHeaders(TinyHttpStatus.SC_INTERNAL, 0);
  155.         } else {
  156.             exchange.sendResponseHeaders(TinyHttpStatus.SC_NO_CONTENT, -1);
  157.         }
  158.     }

  159.     /**
  160.      * Handle a TRACE request.
  161.      *
  162.      * @param exchange The HTTP exchange.
  163.      * @throws IOException If failed to handle the request.
  164.      */
  165.     protected void doTrace(HttpExchange exchange) throws IOException {
  166.         exchange.sendResponseHeaders(TinyHttpStatus.SC_METHOD_NOT_ALLOWED, 0);
  167.     }

  168.     /**
  169.      * Handle a PUT request.
  170.      *
  171.      * @param exchange The HTTP exchange.
  172.      * @throws IOException If failed to handle the request.
  173.      */
  174.     protected void doPut(HttpExchange exchange) throws IOException {
  175.         exchange.sendResponseHeaders(TinyHttpStatus.SC_METHOD_NOT_ALLOWED, 0);
  176.     }

  177.     /**
  178.      * Handle a DELETE request.
  179.      *
  180.      * @param exchange The HTTP exchange.
  181.      * @throws IOException If failed to handle the request.
  182.      */
  183.     protected void doDelete(HttpExchange exchange) throws IOException {
  184.         exchange.sendResponseHeaders(TinyHttpStatus.SC_METHOD_NOT_ALLOWED, 0);
  185.     }
  186. }