Skip to content

Commit

Permalink
HTTP @requestmethod annotation
Browse files Browse the repository at this point in the history
  • Loading branch information
apangin committed Dec 26, 2018
1 parent 8c5c084 commit b252e30
Show file tree
Hide file tree
Showing 4 changed files with 123 additions and 30 deletions.
56 changes: 27 additions & 29 deletions src/one/nio/http/HttpServer.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,19 +28,19 @@
import java.util.Map;

public class HttpServer extends Server {
private final Map<String, RequestHandler> defaultHandlers = new HashMap<>();
private final Map<String, Map<String, RequestHandler>> handlersByAlias = new HashMap<>();
private final Map<String, Map<String, RequestHandler>> handlersByHost = new HashMap<>();
private final PathMapper defaultMapper = new PathMapper();
private final Map<String, PathMapper> mappersByAlias = new HashMap<>();
private final Map<String, PathMapper> mappersByHost = new HashMap<>();

public HttpServer(HttpServerConfig config, Object... routers) throws IOException {
super(config);

if (config.virtualHosts != null) {
for (Map.Entry<String, String[]> virtualHost : config.virtualHosts.entrySet()) {
Map<String, RequestHandler> handlers = new HashMap<>();
handlersByAlias.put(virtualHost.getKey(), handlers);
PathMapper mapper = new PathMapper();
mappersByAlias.put(virtualHost.getKey(), mapper);
for (String host : virtualHost.getValue()) {
handlersByHost.put(host.toLowerCase(), handlers);
mappersByHost.put(host.toLowerCase(), mapper);
}
}
}
Expand All @@ -59,7 +59,7 @@ public HttpSession createSession(Socket socket) throws RejectedSessionException
public void handleRequest(Request request, HttpSession session) throws IOException {
RequestHandler handler = findHandlerByHost(request);
if (handler == null) {
handler = defaultHandlers.get(request.getPath());
handler = defaultMapper.find(request.getPath(), request.getMethod());
}

if (handler != null) {
Expand All @@ -74,24 +74,6 @@ public void handleDefault(Request request, HttpSession session) throws IOExcepti
session.sendResponse(response);
}

private RequestHandler findHandlerByHost(Request request) {
if (handlersByHost.isEmpty()) {
return null;
}

String host = request.getHost();
if (host == null) {
return null;
}

Map<String, RequestHandler> handlers = handlersByHost.get(host.toLowerCase());
if (handlers == null) {
return null;
}

return handlers.get(request.getPath());
}

public void addRequestHandlers(Object router) {
ArrayList<Class> supers = new ArrayList<>(4);
for (Class<?> cls = router.getClass(); cls != Object.class; cls = cls.getSuperclass()) {
Expand All @@ -111,24 +93,40 @@ public void addRequestHandlers(Object router) {
continue;
}

RequestMethod requestMethod = m.getAnnotation(RequestMethod.class);
int[] methods = requestMethod == null ? null : requestMethod.value();

RequestHandler requestHandler = generator.generateFor(m, router);
for (String path : annotation.value()) {
if (!path.startsWith("/")) {
throw new IllegalArgumentException("Path '" + path + "' is not absolute");
}

if (aliases == null || aliases.length == 0) {
defaultHandlers.put(path, requestHandler);
defaultMapper.add(path, methods, requestHandler);
} else {
for (String alias : aliases) {
Map<String, RequestHandler> handlers = handlersByAlias.get(alias);
if (handlers != null) {
handlers.put(path, requestHandler);
PathMapper mapper = mappersByAlias.get(alias);
if (mapper != null) {
mapper.add(path, methods, requestHandler);
}
}
}
}
}
}
}

protected RequestHandler findHandlerByHost(Request request) {
if (!mappersByHost.isEmpty()) {
String host = request.getHost();
if (host != null) {
PathMapper mapper = mappersByHost.get(host.toLowerCase());
if (mapper != null) {
return mapper.find(request.getPath(), request.getMethod());
}
}
}
return null;
}
}
61 changes: 61 additions & 0 deletions src/one/nio/http/PathMapper.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* Copyright 2018 Odnoklassniki Ltd, Mail.Ru Group
*
* Licensed 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 one.nio.http;

import java.util.Arrays;
import java.util.HashMap;

/**
* Finds a RequestHandler by the given @Path and HTTP method.
* Uses an embedded HashMap for performance reasons.
*/
public class PathMapper extends HashMap<String, RequestHandler[]> {

// Add a new mapping
public void add(String path, int[] methods, RequestHandler handler) {
RequestHandler[] handlersByMethod = super.computeIfAbsent(path, p -> new RequestHandler[1]);
if (methods == null) {
handlersByMethod[0] = handler;
} else {
for (int method : methods) {
if (method <= 0 || method >= Request.NUMBER_OF_METHODS) {
throw new IllegalArgumentException("Invalid RequestMethod " + method + " for path " + path);
}
if (method >= handlersByMethod.length) {
handlersByMethod = Arrays.copyOf(handlersByMethod, method + 1);
super.put(path, handlersByMethod);
}
handlersByMethod[method] = handler;
}
}
}

// Return an existing handler for this HTTP request or null if not found
public RequestHandler find(String path, int method) {
RequestHandler[] handlersByMethod = super.get(path);
if (handlersByMethod == null) {
return null;
}

// First, try to find a handler with @RequestMethod annotation
if (method > 0 && method < handlersByMethod.length && handlersByMethod[method] != null) {
return handlersByMethod[method];
}
// Otherwise return the universal handler for all methods
return handlersByMethod[0];
}
}
28 changes: 28 additions & 0 deletions src/one/nio/http/RequestMethod.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Copyright 2018 Odnoklassniki Ltd, Mail.Ru Group
*
* Licensed 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 one.nio.http;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface RequestMethod {
int[] value(); // METHOD_GET, METHOD_POST etc.
}
8 changes: 7 additions & 1 deletion test/one/nio/http/HttpMethodTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ public void post() throws Exception {
@Test
public void put() throws Exception {
assertEquals(
Integer.toString(Request.METHOD_PUT),
"echoMethodOnPut",
client.put(ENDPOINT).getBodyUtf8());
}

Expand Down Expand Up @@ -158,5 +158,11 @@ public static class TestServer extends HttpServer {
public Response echoMethod(Request request) throws IOException {
return Response.ok(Integer.toString(request.getMethod()));
}

@Path(ENDPOINT)
@RequestMethod(Request.METHOD_PUT)
public Response echoMethodOnPut(Request request) {
return Response.ok("echoMethodOnPut");
}
}
}

0 comments on commit b252e30

Please sign in to comment.