TinyHttpHandler.java
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package net.morimekta.tiny.server.http;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Set;
import java.util.TreeSet;
/**
* Class simplifying the HttpHandler in a similar fashion to the jakarta
* <code>HttpServlet</code> class. Just implement the matching method to
* the HTTP method you need served.
*/
public abstract class TinyHttpHandler implements HttpHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(TinyHttpHandler.class);
@Override
public void handle(HttpExchange exchange) throws IOException {
try {
exchange.getResponseHeaders().set("Server", "tiny-server");
switch (exchange.getRequestMethod()) {
case "GET":
doGet(exchange);
break;
case "POST":
doPost(exchange);
break;
case "HEAD":
doHead(exchange);
break;
case "OPTIONS":
doOptions(exchange);
break;
case "TRACE":
doTrace(exchange);
break;
case "PUT":
doPut(exchange);
break;
case "DELETE":
doDelete(exchange);
break;
default: {
LOGGER.warn("Unknown request method: {} {} HTTP/1.1",
exchange.getRequestMethod(),
exchange.getRequestURI().getPath());
exchange.sendResponseHeaders(TinyHttpStatus.SC_METHOD_NOT_ALLOWED, 0);
}
}
} catch (Exception e) {
if (exchange.getResponseCode() <= 0) {
LOGGER.warn("Exception handling: {} {} HTTP/1.1",
exchange.getRequestMethod(),
exchange.getRequestURI().getPath(),
e);
exchange.sendResponseHeaders(TinyHttpStatus.SC_INTERNAL, 0);
} else {
LOGGER.error("Exception handling: {} {} HTTP/1.1",
exchange.getRequestMethod(),
exchange.getRequestURI().getPath(),
e);
}
} finally {
exchange.close();
}
}
/**
* Handle a GET request.
*
* @param exchange The HTTP exchange.
* @throws IOException If failed to handle the request.
*/
protected void doGet(HttpExchange exchange) throws IOException {
exchange.sendResponseHeaders(TinyHttpStatus.SC_METHOD_NOT_ALLOWED, 0);
}
/**
* Handle a POST request.
*
* @param exchange The HTTP exchange.
* @throws IOException If failed to handle the request.
*/
protected void doPost(HttpExchange exchange) throws IOException {
exchange.sendResponseHeaders(TinyHttpStatus.SC_METHOD_NOT_ALLOWED, 0);
}
/**
* Handle a HEAD request.
*
* @param exchange The HTTP exchange.
* @throws IOException If failed to handle the request.
*/
protected void doHead(HttpExchange exchange) throws IOException {
exchange.sendResponseHeaders(TinyHttpStatus.SC_METHOD_NOT_ALLOWED, -1);
}
/**
* Handle an OPTIONS request.
*
* @param exchange The HTTP exchange.
* @throws IOException If failed to handle the request.
*/
protected void doOptions(HttpExchange exchange) throws IOException {
Set<String> methods = new TreeSet<>();
Set<String> corsMethods = new TreeSet<>();
for (Method method : getClass().getDeclaredMethods()) {
switch (method.getName()) {
case "doGet":
methods.add("GET");
corsMethods.add("GET");
break;
case "doPost":
methods.add("POST");
break;
case "doHead":
methods.add("HEAD");
break;
case "doTrace":
methods.add("TRACE");
break;
case "doPut":
methods.add("PUT");
break;
case "doDelete":
methods.add("DELETE");
break;
default:
break;
}
}
if (!methods.isEmpty()) {
exchange.getResponseHeaders().set("Allow", String.join(", ", methods));
}
if (!corsMethods.isEmpty()) {
exchange.getResponseHeaders().set("Access-Control-Allow-Methods", String.join(", ", corsMethods));
}
if (methods.isEmpty() && corsMethods.isEmpty()) {
// ... nothing implemented?
exchange.sendResponseHeaders(TinyHttpStatus.SC_INTERNAL, 0);
} else {
exchange.sendResponseHeaders(TinyHttpStatus.SC_NO_CONTENT, -1);
}
}
/**
* Handle a TRACE request.
*
* @param exchange The HTTP exchange.
* @throws IOException If failed to handle the request.
*/
protected void doTrace(HttpExchange exchange) throws IOException {
exchange.sendResponseHeaders(TinyHttpStatus.SC_METHOD_NOT_ALLOWED, 0);
}
/**
* Handle a PUT request.
*
* @param exchange The HTTP exchange.
* @throws IOException If failed to handle the request.
*/
protected void doPut(HttpExchange exchange) throws IOException {
exchange.sendResponseHeaders(TinyHttpStatus.SC_METHOD_NOT_ALLOWED, 0);
}
/**
* Handle a DELETE request.
*
* @param exchange The HTTP exchange.
* @throws IOException If failed to handle the request.
*/
protected void doDelete(HttpExchange exchange) throws IOException {
exchange.sendResponseHeaders(TinyHttpStatus.SC_METHOD_NOT_ALLOWED, 0);
}
}