add static file server capability
This commit is contained in:
parent
f4cfd6313d
commit
ad460a5111
37 changed files with 452 additions and 129 deletions
|
@ -64,14 +64,14 @@ subprojects {
|
||||||
}
|
}
|
||||||
|
|
||||||
test {
|
test {
|
||||||
if (JavaVersion.current() == JavaVersion.VERSION_1_8) {
|
systemProperty 'java.util.logging.config.file', 'src/test/resources/logging.properties'
|
||||||
jvmArgs "-javaagent:" + configurations.alpnagent.asPath
|
|
||||||
}
|
|
||||||
testLogging {
|
testLogging {
|
||||||
// set this to 'true' to show lot of debug output
|
|
||||||
showStandardStreams = false
|
showStandardStreams = false
|
||||||
exceptionFormat = 'full'
|
exceptionFormat = 'full'
|
||||||
}
|
}
|
||||||
|
if (JavaVersion.current() == JavaVersion.VERSION_1_8) {
|
||||||
|
jvmArgs "-javaagent:" + configurations.alpnagent.asPath
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
clean {
|
clean {
|
||||||
|
|
|
@ -1,17 +1,16 @@
|
||||||
group = org.xbib
|
group = org.xbib
|
||||||
name = netty-http
|
name = netty-http
|
||||||
version = 4.1.34.0
|
version = 4.1.35.0
|
||||||
|
|
||||||
# main packages
|
# main packages
|
||||||
netty.version = 4.1.34.Final
|
netty.version = 4.1.35.Final
|
||||||
tcnative.version = 2.0.22.Final
|
tcnative.version = 2.0.22.Final
|
||||||
bouncycastle.version = 1.61
|
bouncycastle.version = 1.61
|
||||||
alpnagent.version = 2.0.9
|
alpnagent.version = 2.0.9
|
||||||
xbib-net-url.version = 1.2.1
|
xbib-net-url.version = 1.2.2
|
||||||
|
|
||||||
# test packages
|
# test packages
|
||||||
junit.version = 4.12
|
junit.version = 4.12
|
||||||
# 1.0.1
|
|
||||||
conscrypt.version = 2.0.0
|
conscrypt.version = 2.0.0
|
||||||
jackson.version = 2.8.11.1
|
jackson.version = 2.8.11.1
|
||||||
wagon.version = 3.0.0
|
wagon.version = 3.0.0
|
||||||
|
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
4
gradle/wrapper/gradle-wrapper.properties
vendored
4
gradle/wrapper/gradle-wrapper.properties
vendored
|
@ -1,6 +1,6 @@
|
||||||
#Thu Mar 14 13:01:02 CET 2019
|
#Mon Apr 22 17:45:04 CEST 2019
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-5.2.1-all.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-5.3-all.zip
|
||||||
|
|
18
gradlew
vendored
18
gradlew
vendored
|
@ -1,5 +1,21 @@
|
||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
#
|
||||||
|
# Copyright 2015 the original author or authors.
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
#
|
||||||
|
|
||||||
##############################################################################
|
##############################################################################
|
||||||
##
|
##
|
||||||
## Gradle start up script for UN*X
|
## Gradle start up script for UN*X
|
||||||
|
@ -28,7 +44,7 @@ APP_NAME="Gradle"
|
||||||
APP_BASE_NAME=`basename "$0"`
|
APP_BASE_NAME=`basename "$0"`
|
||||||
|
|
||||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
DEFAULT_JVM_OPTS='"-Xmx64m"'
|
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||||
|
|
||||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
MAX_FD="maximum"
|
MAX_FD="maximum"
|
||||||
|
|
18
gradlew.bat
vendored
18
gradlew.bat
vendored
|
@ -1,3 +1,19 @@
|
||||||
|
@rem
|
||||||
|
@rem Copyright 2015 the original author or authors.
|
||||||
|
@rem
|
||||||
|
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
@rem you may not use this file except in compliance with the License.
|
||||||
|
@rem You may obtain a copy of the License at
|
||||||
|
@rem
|
||||||
|
@rem http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
@rem
|
||||||
|
@rem Unless required by applicable law or agreed to in writing, software
|
||||||
|
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
@rem See the License for the specific language governing permissions and
|
||||||
|
@rem limitations under the License.
|
||||||
|
@rem
|
||||||
|
|
||||||
@if "%DEBUG%" == "" @echo off
|
@if "%DEBUG%" == "" @echo off
|
||||||
@rem ##########################################################################
|
@rem ##########################################################################
|
||||||
@rem
|
@rem
|
||||||
|
@ -14,7 +30,7 @@ set APP_BASE_NAME=%~n0
|
||||||
set APP_HOME=%DIRNAME%
|
set APP_HOME=%DIRNAME%
|
||||||
|
|
||||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
set DEFAULT_JVM_OPTS="-Xmx64m"
|
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||||
|
|
||||||
@rem Find java.exe
|
@rem Find java.exe
|
||||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||||
|
|
|
@ -80,6 +80,10 @@ public class HttpTransport extends BaseTransport {
|
||||||
logger.log(Level.WARNING, "throwable not null for response " + fullHttpResponse, throwable);
|
logger.log(Level.WARNING, "throwable not null for response " + fullHttpResponse, throwable);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (requests.isEmpty()) {
|
||||||
|
logger.log(Level.WARNING, "no request present, can not handle response " + fullHttpResponse);
|
||||||
|
return;
|
||||||
|
}
|
||||||
// streamID is expected to be null, last request on memory is expected to be current, remove request from memory
|
// streamID is expected to be null, last request on memory is expected to be current, remove request from memory
|
||||||
Request request = requests.remove(requests.lastKey());
|
Request request = requests.remove(requests.lastKey());
|
||||||
if (request != null) {
|
if (request != null) {
|
||||||
|
|
5
netty-http-client/src/test/resources/logging.properties
Normal file
5
netty-http-client/src/test/resources/logging.properties
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
handlers = java.util.logging.ConsoleHandler
|
||||||
|
.level = FINE
|
||||||
|
java.util.logging.ConsoleHandler.level = FINE
|
||||||
|
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
|
||||||
|
java.util.logging.SimpleFormatter.format = %1$tFT%1$tT.%1$tL%1$tz [%4$-11s] [%3$s] %5$s %6$s%n
|
|
@ -138,7 +138,7 @@ public final class Server {
|
||||||
DomainNameMappingBuilder<SslContext> mappingBuilder = new DomainNameMappingBuilder<>(sslContext);
|
DomainNameMappingBuilder<SslContext> mappingBuilder = new DomainNameMappingBuilder<>(sslContext);
|
||||||
for (VirtualServer virtualServer : serverConfig.getVirtualServers()) {
|
for (VirtualServer virtualServer : serverConfig.getVirtualServers()) {
|
||||||
String name = virtualServer.getName();
|
String name = virtualServer.getName();
|
||||||
mappingBuilder.add( name == null ? "*" : name, sslContext);
|
mappingBuilder.add(name == null ? "*" : name, sslContext);
|
||||||
}
|
}
|
||||||
domainNameMapping = mappingBuilder.build();
|
domainNameMapping = mappingBuilder.build();
|
||||||
}
|
}
|
||||||
|
|
|
@ -224,7 +224,7 @@ public class ServerBuilder {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ServerBuilder addVirtualHost(VirtualServer virtualServer) {
|
public ServerBuilder addVirtualServer(VirtualServer virtualServer) {
|
||||||
this.serverConfig.addVirtualServer(virtualServer);
|
this.serverConfig.addVirtualServer(virtualServer);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
package org.xbib.netty.http.server.context;
|
||||||
|
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import io.netty.buffer.Unpooled;
|
||||||
|
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||||
|
import org.xbib.netty.http.server.transport.ServerRequest;
|
||||||
|
import org.xbib.netty.http.server.transport.ServerResponse;
|
||||||
|
import org.xbib.netty.http.server.util.MimeTypeUtils;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.nio.MappedByteBuffer;
|
||||||
|
import java.nio.channels.FileChannel;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
|
||||||
|
public class ClasspathContextHandler implements ContextHandler {
|
||||||
|
|
||||||
|
private final ClassLoader classLoader;
|
||||||
|
|
||||||
|
private final String prefix;
|
||||||
|
|
||||||
|
public ClasspathContextHandler(ClassLoader classLoader, String prefix) {
|
||||||
|
this.classLoader = classLoader;
|
||||||
|
this.prefix = prefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void serve(ServerRequest serverRequest, ServerResponse serverResponse) throws IOException {
|
||||||
|
String contextPath = serverRequest.getContextPath();
|
||||||
|
URL url = classLoader.getResource(prefix + contextPath);
|
||||||
|
if (url != null) {
|
||||||
|
try {
|
||||||
|
Path path = Paths.get(url.toURI());
|
||||||
|
FileChannel fileChannel = (FileChannel) Files.newByteChannel(path);
|
||||||
|
MappedByteBuffer mappedByteBuffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fileChannel.size());
|
||||||
|
ByteBuf byteBuf = Unpooled.wrappedBuffer(mappedByteBuffer);
|
||||||
|
try {
|
||||||
|
String contentType = MimeTypeUtils.guessFromPath(contextPath, false);
|
||||||
|
serverResponse.write(HttpResponseStatus.OK, contentType, byteBuf);
|
||||||
|
} finally {
|
||||||
|
byteBuf.release();
|
||||||
|
}
|
||||||
|
} catch (URISyntaxException e) {
|
||||||
|
serverResponse.write(HttpResponseStatus.INTERNAL_SERVER_ERROR);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
serverResponse.write(HttpResponseStatus.NOT_FOUND);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
package org.xbib.netty.http.server.context;
|
package org.xbib.netty.http.server.context;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -8,12 +8,13 @@ import java.util.Map;
|
||||||
*/
|
*/
|
||||||
public class ContextInfo {
|
public class ContextInfo {
|
||||||
|
|
||||||
private final Map<String, ContextHandler> handlers = new HashMap<>(2);
|
|
||||||
|
|
||||||
private final VirtualServer virtualServer;
|
private final VirtualServer virtualServer;
|
||||||
|
|
||||||
|
private final Map<String, ContextHandler> methodHandlerMap;
|
||||||
|
|
||||||
public ContextInfo(VirtualServer virtualServer) {
|
public ContextInfo(VirtualServer virtualServer) {
|
||||||
this.virtualServer = virtualServer;
|
this.virtualServer = virtualServer;
|
||||||
|
this.methodHandlerMap = new LinkedHashMap<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -21,8 +22,8 @@ public class ContextInfo {
|
||||||
*
|
*
|
||||||
* @return the map of supported HTTP methods and their corresponding handlers
|
* @return the map of supported HTTP methods and their corresponding handlers
|
||||||
*/
|
*/
|
||||||
public Map<String, ContextHandler> getHandlers() {
|
public Map<String, ContextHandler> getMethodHandlerMap() {
|
||||||
return handlers;
|
return methodHandlerMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -33,11 +34,13 @@ public class ContextInfo {
|
||||||
*/
|
*/
|
||||||
public void addHandler(ContextHandler handler, String... methods) {
|
public void addHandler(ContextHandler handler, String... methods) {
|
||||||
if (methods.length == 0) {
|
if (methods.length == 0) {
|
||||||
methods = new String[]{"GET"};
|
methodHandlerMap.put("GET", handler);
|
||||||
}
|
virtualServer.getMethods().add("GET");
|
||||||
for (String method : methods) {
|
} else {
|
||||||
handlers.put(method, handler);
|
for (String method : methods) {
|
||||||
virtualServer.getMethods().add(method);
|
methodHandlerMap.put(method, handler);
|
||||||
|
virtualServer.getMethods().add(method);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package org.xbib.netty.http.server.context;
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.buffer.ByteBufAllocator;
|
import io.netty.buffer.ByteBufAllocator;
|
||||||
|
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||||
import org.xbib.netty.http.server.transport.ServerRequest;
|
import org.xbib.netty.http.server.transport.ServerRequest;
|
||||||
import org.xbib.netty.http.server.transport.ServerResponse;
|
import org.xbib.netty.http.server.transport.ServerResponse;
|
||||||
|
|
||||||
|
@ -28,11 +29,10 @@ public class DirectoryContextHandler implements ContextHandler {
|
||||||
String uri = serverRequest.getRequest().uri();
|
String uri = serverRequest.getRequest().uri();
|
||||||
Path p = path.resolve(uri);
|
Path p = path.resolve(uri);
|
||||||
ByteBuf byteBuf = read(allocator, p);
|
ByteBuf byteBuf = read(allocator, p);
|
||||||
serverResponse.write(200, "application/octet-stream", byteBuf);
|
serverResponse.write(HttpResponseStatus.OK, "application/octet-stream", byteBuf);
|
||||||
byteBuf.release();
|
byteBuf.release();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static ByteBuf read(ByteBufAllocator allocator, Path path)
|
public static ByteBuf read(ByteBufAllocator allocator, Path path)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
try (SeekableByteChannel sbc = Files.newByteChannel(path);
|
try (SeekableByteChannel sbc = Files.newByteChannel(path);
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
package org.xbib.netty.http.server.context;
|
||||||
|
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import io.netty.buffer.Unpooled;
|
||||||
|
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||||
|
import org.xbib.netty.http.server.transport.ServerRequest;
|
||||||
|
import org.xbib.netty.http.server.transport.ServerResponse;
|
||||||
|
import org.xbib.netty.http.server.util.MimeTypeUtils;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.MappedByteBuffer;
|
||||||
|
import java.nio.channels.FileChannel;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
|
||||||
|
public class NioContextHandler implements ContextHandler {
|
||||||
|
|
||||||
|
private final Path prefix;
|
||||||
|
|
||||||
|
public NioContextHandler(Path prefix) {
|
||||||
|
this.prefix = prefix;
|
||||||
|
if (!Files.exists(prefix) || !Files.isDirectory(prefix)) {
|
||||||
|
throw new IllegalArgumentException("prefix: " + prefix + " (not a directory");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void serve(ServerRequest serverRequest, ServerResponse serverResponse) throws IOException {
|
||||||
|
String requestPath = serverRequest.getRequestPath();
|
||||||
|
Path path = prefix.resolve(requestPath.substring(1)); // starts always with '/'
|
||||||
|
if (Files.exists(path) && Files.isReadable(path)) {
|
||||||
|
try (FileChannel fileChannel = (FileChannel) Files.newByteChannel(path)) {
|
||||||
|
MappedByteBuffer mappedByteBuffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fileChannel.size());
|
||||||
|
ByteBuf byteBuf = Unpooled.wrappedBuffer(mappedByteBuffer);
|
||||||
|
String contentType = MimeTypeUtils.guessFromPath(requestPath, false);
|
||||||
|
serverResponse.write(HttpResponseStatus.OK, contentType, byteBuf);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
serverResponse.write(HttpResponseStatus.NOT_FOUND);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -24,6 +24,10 @@ public class VirtualServer {
|
||||||
|
|
||||||
private volatile boolean allowGeneratedIndex;
|
private volatile boolean allowGeneratedIndex;
|
||||||
|
|
||||||
|
public VirtualServer() {
|
||||||
|
this(null);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a VirtualServer with the given name.
|
* Constructs a VirtualServer with the given name.
|
||||||
*
|
*
|
||||||
|
@ -94,25 +98,6 @@ public class VirtualServer {
|
||||||
return methods;
|
return methods;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the context handler for the given path.
|
|
||||||
* If a context is not found for the given path, the search is repeated for
|
|
||||||
* its parent path, and so on until a base context is found. If neither the
|
|
||||||
* given path nor any of its parents has a context, an empty context is returned.
|
|
||||||
*
|
|
||||||
* @param path the context's path
|
|
||||||
* @return the context info for the given path, or an empty context if none exists
|
|
||||||
*/
|
|
||||||
public ContextInfo getContext(String path) {
|
|
||||||
path = trimRight(path, '/'); // remove trailing slash
|
|
||||||
ContextInfo info = null;
|
|
||||||
while (info == null && path != null) {
|
|
||||||
info = contexts.get(path);
|
|
||||||
path = getParentPath(path);
|
|
||||||
}
|
|
||||||
return info != null ? info : emptyContext;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a context and its corresponding context handler to this server.
|
* Adds a context and its corresponding context handler to this server.
|
||||||
* Paths are normalized by removing trailing slashes (except the root).
|
* Paths are normalized by removing trailing slashes (except the root).
|
||||||
|
@ -122,15 +107,16 @@ public class VirtualServer {
|
||||||
* @param methods the HTTP methods supported by the context handler (default is "GET")
|
* @param methods the HTTP methods supported by the context handler (default is "GET")
|
||||||
* @throws IllegalArgumentException if path is malformed
|
* @throws IllegalArgumentException if path is malformed
|
||||||
*/
|
*/
|
||||||
public void addContext(String path, ContextHandler handler, String... methods) {
|
public VirtualServer addContext(String path, ContextHandler handler, String... methods) {
|
||||||
if (path == null || !path.startsWith("/") && !path.equals("*")) {
|
if (path == null || !path.startsWith("/") && !path.equals("*")) {
|
||||||
throw new IllegalArgumentException("invalid path: " + path);
|
throw new IllegalArgumentException("invalid path: " + path);
|
||||||
}
|
}
|
||||||
path = trimRight(path, '/');
|
String s = trimRight(path, '/');
|
||||||
ContextInfo info = new ContextInfo(this);
|
ContextInfo info = new ContextInfo(this);
|
||||||
ContextInfo existing = contexts.putIfAbsent(path, info);
|
ContextInfo existing = contexts.putIfAbsent(s, info);
|
||||||
info = existing != null ? existing : info;
|
info = existing != null ? existing : info;
|
||||||
info.addHandler(handler, methods);
|
info.addHandler(handler, methods);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -141,17 +127,37 @@ public class VirtualServer {
|
||||||
* @throws IllegalArgumentException if a Context-annotated
|
* @throws IllegalArgumentException if a Context-annotated
|
||||||
* method has an {@link Context invalid signature}
|
* method has an {@link Context invalid signature}
|
||||||
*/
|
*/
|
||||||
public void addContexts(Object o) throws IllegalArgumentException {
|
public VirtualServer addContexts(Object o) throws IllegalArgumentException {
|
||||||
for (Class<?> c = o.getClass(); c != null; c = c.getSuperclass()) {
|
for (Class<?> c = o.getClass(); c != null; c = c.getSuperclass()) {
|
||||||
// add to contexts those with @Context annotation
|
|
||||||
for (Method m : c.getDeclaredMethods()) {
|
for (Method m : c.getDeclaredMethods()) {
|
||||||
Context context = m.getAnnotation(Context.class);
|
Context context = m.getAnnotation(Context.class);
|
||||||
if (context != null) {
|
if (context != null) {
|
||||||
//m.setAccessible(true); // allow access to private method
|
|
||||||
addContext(context.value(), new MethodContextHandler(m, o), context.methods());
|
addContext(context.value(), new MethodContextHandler(m, o), context.methods());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the context handler for the given path.
|
||||||
|
* If a context is not found for the given path, the search is repeated for
|
||||||
|
* its parent path, and so on until a base context is found. If neither the
|
||||||
|
* given path nor any of its parents has a context, an empty context is returned.
|
||||||
|
*
|
||||||
|
* @param path the context's path
|
||||||
|
* @return the context info for the given path, or an empty context if none exists
|
||||||
|
*/
|
||||||
|
public ContextPath getContextPath(String path) {
|
||||||
|
String s = trimRight(path, '/');
|
||||||
|
ContextInfo info = null;
|
||||||
|
String hook = null;
|
||||||
|
while (info == null && s != null) {
|
||||||
|
hook = s;
|
||||||
|
info = contexts.get(s);
|
||||||
|
s = getParentPath(s);
|
||||||
|
}
|
||||||
|
return new ContextPath(hook, info != null ? info : emptyContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -179,9 +185,29 @@ public class VirtualServer {
|
||||||
* or null if given path is the root path
|
* or null if given path is the root path
|
||||||
*/
|
*/
|
||||||
private static String getParentPath(String path) {
|
private static String getParentPath(String path) {
|
||||||
path = trimRight(path, '/'); // remove trailing slash
|
String s = trimRight(path, '/'); // remove trailing slash
|
||||||
int slash = path.lastIndexOf('/');
|
int slash = s.lastIndexOf('/');
|
||||||
return slash == -1 ? null : path.substring(0, slash);
|
return slash == -1 ? null : s.substring(0, slash);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ContextPath {
|
||||||
|
|
||||||
|
private final String hook;
|
||||||
|
|
||||||
|
private final ContextInfo contextInfo;
|
||||||
|
|
||||||
|
ContextPath(String hook, ContextInfo contextInfo) {
|
||||||
|
this.hook = hook;
|
||||||
|
this.contextInfo = contextInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getHook() {
|
||||||
|
return hook;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ContextInfo getContextInfo() {
|
||||||
|
return contextInfo;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ package org.xbib.netty.http.server.transport;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.handler.codec.http.HttpHeaderNames;
|
import io.netty.handler.codec.http.HttpHeaderNames;
|
||||||
import io.netty.handler.codec.http.HttpHeaders;
|
import io.netty.handler.codec.http.HttpHeaders;
|
||||||
|
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||||
import io.netty.handler.codec.http.HttpVersion;
|
import io.netty.handler.codec.http.HttpVersion;
|
||||||
import org.xbib.netty.http.server.Server;
|
import org.xbib.netty.http.server.Server;
|
||||||
import org.xbib.netty.http.server.context.ContextHandler;
|
import org.xbib.netty.http.server.context.ContextHandler;
|
||||||
|
@ -33,7 +34,7 @@ abstract class BaseServerTransport implements ServerTransport {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void exceptionReceived(ChannelHandlerContext ctx, Throwable throwable) throws IOException {
|
public void exceptionReceived(ChannelHandlerContext ctx, Throwable throwable) {
|
||||||
logger.log(Level.WARNING, throwable.getMessage(), throwable);
|
logger.log(Level.WARNING, throwable.getMessage(), throwable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,7 +55,7 @@ abstract class BaseServerTransport implements ServerTransport {
|
||||||
case 2:
|
case 2:
|
||||||
if (!reqHeaders.contains(HttpHeaderNames.HOST)) {
|
if (!reqHeaders.contains(HttpHeaderNames.HOST)) {
|
||||||
// RFC2616#14.23: missing Host header gets 400
|
// RFC2616#14.23: missing Host header gets 400
|
||||||
serverResponse.writeError(400, "missing 'Host' header");
|
serverResponse.writeError(HttpResponseStatus.BAD_REQUEST, "missing 'Host' header");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// return a continue response before reading body
|
// return a continue response before reading body
|
||||||
|
@ -65,13 +66,13 @@ abstract class BaseServerTransport implements ServerTransport {
|
||||||
//tempResp.sendHeaders(100);
|
//tempResp.sendHeaders(100);
|
||||||
} else {
|
} else {
|
||||||
// RFC2616#14.20: if unknown expect, send 417
|
// RFC2616#14.20: if unknown expect, send 417
|
||||||
serverResponse.writeError(417);
|
serverResponse.writeError(HttpResponseStatus.EXPECTATION_FAILED);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
serverResponse.writeError(400, "Unknown version: " + version);
|
serverResponse.writeError(HttpResponseStatus.BAD_REQUEST, "Unknown version: " + version);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
@ -88,12 +89,14 @@ abstract class BaseServerTransport implements ServerTransport {
|
||||||
String method = serverRequest.getRequest().method().name();
|
String method = serverRequest.getRequest().method().name();
|
||||||
String path = serverRequest.getRequest().uri();
|
String path = serverRequest.getRequest().uri();
|
||||||
VirtualServer virtualServer = serverRequest.getVirtualServer();
|
VirtualServer virtualServer = serverRequest.getVirtualServer();
|
||||||
Map<String, ContextHandler> handlers = virtualServer.getContext(path).getHandlers();
|
VirtualServer.ContextPath contextPath = virtualServer.getContextPath(path);
|
||||||
|
serverRequest.setContextPath(contextPath.getHook());
|
||||||
|
Map<String, ContextHandler> methodHandlerMap = contextPath.getContextInfo().getMethodHandlerMap();
|
||||||
// RFC 2616#5.1.1 - GET and HEAD must be supported
|
// RFC 2616#5.1.1 - GET and HEAD must be supported
|
||||||
if (method.equals("GET") || method.equals("HEAD") || handlers.containsKey(method)) {
|
if (method.equals("GET") || method.equals("HEAD") || methodHandlerMap.containsKey(method)) {
|
||||||
ContextHandler handler = virtualServer.getContext(path).getHandlers().get(method);
|
ContextHandler handler = methodHandlerMap.get(method);
|
||||||
if (handler == null) {
|
if (handler == null) {
|
||||||
serverResponse.writeError(404);
|
serverResponse.writeError(HttpResponseStatus.NOT_FOUND);
|
||||||
} else {
|
} else {
|
||||||
handler.serve(serverRequest, serverResponse);
|
handler.serve(serverRequest, serverResponse);
|
||||||
}
|
}
|
||||||
|
@ -101,15 +104,15 @@ abstract class BaseServerTransport implements ServerTransport {
|
||||||
Set<String> methods = new LinkedHashSet<>(METHODS);
|
Set<String> methods = new LinkedHashSet<>(METHODS);
|
||||||
// "*" is a special server-wide (no-context) request supported by OPTIONS
|
// "*" is a special server-wide (no-context) request supported by OPTIONS
|
||||||
boolean isServerOptions = path.equals("*") && method.equals("OPTIONS");
|
boolean isServerOptions = path.equals("*") && method.equals("OPTIONS");
|
||||||
methods.addAll(isServerOptions ? virtualServer.getMethods() : handlers.keySet());
|
methods.addAll(isServerOptions ? virtualServer.getMethods() : methodHandlerMap.keySet());
|
||||||
serverResponse.setHeader(HttpHeaderNames.ALLOW, String.join(", ", methods));
|
serverResponse.setHeader(HttpHeaderNames.ALLOW, String.join(", ", methods));
|
||||||
if (method.equals("OPTIONS")) { // default OPTIONS handler
|
if (method.equals("OPTIONS")) { // default OPTIONS handler
|
||||||
serverResponse.setHeader(HttpHeaderNames.CONTENT_LENGTH, "0"); // RFC2616#9.2
|
serverResponse.setHeader(HttpHeaderNames.CONTENT_LENGTH, "0"); // RFC2616#9.2
|
||||||
serverResponse.write(200);
|
serverResponse.write(HttpResponseStatus.OK);
|
||||||
} else if (virtualServer.getMethods().contains(method)) {
|
} else if (virtualServer.getMethods().contains(method)) {
|
||||||
serverResponse.write(405); // supported by server, but not this context (nor built-in)
|
serverResponse.write(HttpResponseStatus.METHOD_NOT_ALLOWED); // supported by server, but not this context (nor built-in)
|
||||||
} else {
|
} else {
|
||||||
serverResponse.writeError(501); // unsupported method
|
serverResponse.writeError(HttpResponseStatus.NOT_IMPLEMENTED); // unsupported method
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,7 +41,7 @@ public class Http2ServerResponse implements ServerResponse {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(String text) {
|
public void write(String text) {
|
||||||
write(200, "text/plain; charset=utf-8", text);
|
write(HttpResponseStatus.OK, "text/plain; charset=utf-8", text);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -50,8 +50,8 @@ public class Http2ServerResponse implements ServerResponse {
|
||||||
* @param status the response status
|
* @param status the response status
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void writeError(int status) {
|
public void writeError(HttpResponseStatus status) {
|
||||||
writeError(status, status < 400 ? ":)" : "sorry it didn't work out :(");
|
writeError(status, status.code() < 400 ? ":)" : "sorry it didn't work out :(");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -64,32 +64,32 @@ public class Http2ServerResponse implements ServerResponse {
|
||||||
* @param text the text body (sent as text/html)
|
* @param text the text body (sent as text/html)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void writeError(int status, String text) {
|
public void writeError(HttpResponseStatus status, String text) {
|
||||||
write(status, "text/html; charset=utf-8",
|
write(status, "text/html; charset=utf-8",
|
||||||
String.format("<!DOCTYPE html>%n<html>%n<head><title>%d %s</title></head>%n" +
|
String.format("<!DOCTYPE html>%n<html>%n<head><title>%d %s</title></head>%n" +
|
||||||
"<body><h1>%d %s</h1>%n<p>%s</p>%n</body></html>",
|
"<body><h1>%d %s</h1>%n<p>%s</p>%n</body></html>",
|
||||||
status, HttpResponseStatus.valueOf(status).reasonPhrase(),
|
status.code(), status.reasonPhrase(),
|
||||||
status, HttpResponseStatus.valueOf(status).reasonPhrase(),
|
status.code(), status.reasonPhrase(),
|
||||||
escapeHTML(text)));
|
escapeHTML(text)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(int status) {
|
public void write(HttpResponseStatus status) {
|
||||||
write(status, null, (ByteBuf) null);
|
write(status, null, (ByteBuf) null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(int status, String contentType, String text) {
|
public void write(HttpResponseStatus status, String contentType, String text) {
|
||||||
write(status, contentType, ByteBufUtil.writeUtf8(ctx.alloc(), text));
|
write(status, contentType, ByteBufUtil.writeUtf8(ctx.alloc(), text));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(int status, String contentType, String text, Charset charset) {
|
public void write(HttpResponseStatus status, String contentType, String text, Charset charset) {
|
||||||
write(status, contentType, ByteBufUtil.encodeString(ctx.alloc(), CharBuffer.allocate(text.length()).append(text), charset));
|
write(status, contentType, ByteBufUtil.encodeString(ctx.alloc(), CharBuffer.allocate(text.length()).append(text), charset));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(int status, String contentType, ByteBuf byteBuf) {
|
public void write(HttpResponseStatus status, String contentType, ByteBuf byteBuf) {
|
||||||
if (byteBuf != null) {
|
if (byteBuf != null) {
|
||||||
CharSequence s = headers.get(HttpHeaderNames.CONTENT_TYPE);
|
CharSequence s = headers.get(HttpHeaderNames.CONTENT_TYPE);
|
||||||
if (s == null) {
|
if (s == null) {
|
||||||
|
@ -120,7 +120,7 @@ public class Http2ServerResponse implements ServerResponse {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Http2Headers http2Headers = new DefaultHttp2Headers()
|
Http2Headers http2Headers = new DefaultHttp2Headers()
|
||||||
.status(HttpResponseStatus.valueOf(status).codeAsText())
|
.status(status.codeAsText())
|
||||||
.add(headers);
|
.add(headers);
|
||||||
ctx.channel().write(new DefaultHttp2HeadersFrame(http2Headers,byteBuf == null));
|
ctx.channel().write(new DefaultHttp2HeadersFrame(http2Headers,byteBuf == null));
|
||||||
if (byteBuf != null) {
|
if (byteBuf != null) {
|
||||||
|
|
|
@ -3,6 +3,7 @@ package org.xbib.netty.http.server.transport;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.handler.codec.http.FullHttpRequest;
|
import io.netty.handler.codec.http.FullHttpRequest;
|
||||||
import io.netty.handler.codec.http.HttpHeaderNames;
|
import io.netty.handler.codec.http.HttpHeaderNames;
|
||||||
|
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||||
import io.netty.handler.codec.http2.Http2Settings;
|
import io.netty.handler.codec.http2.Http2Settings;
|
||||||
import io.netty.handler.codec.http2.HttpConversionUtil;
|
import io.netty.handler.codec.http2.HttpConversionUtil;
|
||||||
import org.xbib.netty.http.common.HttpAddress;
|
import org.xbib.netty.http.common.HttpAddress;
|
||||||
|
@ -36,6 +37,8 @@ public class Http2ServerTransport extends BaseServerTransport {
|
||||||
ServerResponse serverResponse = new Http2ServerResponse(serverRequest, ctx);
|
ServerResponse serverResponse = new Http2ServerResponse(serverRequest, ctx);
|
||||||
if (acceptRequest(serverRequest, serverResponse)) {
|
if (acceptRequest(serverRequest, serverResponse)) {
|
||||||
handle(serverRequest, serverResponse);
|
handle(serverRequest, serverResponse);
|
||||||
|
} else {
|
||||||
|
serverResponse.write(HttpResponseStatus.NOT_ACCEPTABLE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,12 +21,9 @@ import java.nio.charset.Charset;
|
||||||
import java.time.ZoneOffset;
|
import java.time.ZoneOffset;
|
||||||
import java.time.ZonedDateTime;
|
import java.time.ZonedDateTime;
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
public class HttpServerResponse implements ServerResponse {
|
public class HttpServerResponse implements ServerResponse {
|
||||||
|
|
||||||
private static final Logger logger = Logger.getLogger(HttpServerResponse.class.getName());
|
|
||||||
|
|
||||||
private final ServerRequest serverRequest;
|
private final ServerRequest serverRequest;
|
||||||
|
|
||||||
private final ChannelHandlerContext ctx;
|
private final ChannelHandlerContext ctx;
|
||||||
|
@ -49,7 +46,7 @@ public class HttpServerResponse implements ServerResponse {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(String text) {
|
public void write(String text) {
|
||||||
write(200, "text/plain; charset=utf-8", text);
|
write(HttpResponseStatus.OK, "text/plain; charset=utf-8", text);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -58,8 +55,8 @@ public class HttpServerResponse implements ServerResponse {
|
||||||
* @param status the response status
|
* @param status the response status
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void writeError(int status) {
|
public void writeError(HttpResponseStatus status) {
|
||||||
writeError(status, status < 400 ? ":)" : "sorry it didn't work out :(");
|
writeError(status, status.code() < 400 ? ":)" : "sorry it didn't work out :(");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -72,32 +69,32 @@ public class HttpServerResponse implements ServerResponse {
|
||||||
* @param text the text body (sent as text/html)
|
* @param text the text body (sent as text/html)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void writeError(int status, String text) {
|
public void writeError(HttpResponseStatus status, String text) {
|
||||||
write(status, "text/html; charset=utf-8",
|
write(status, "text/html; charset=utf-8",
|
||||||
String.format("<!DOCTYPE html>%n<html>%n<head><title>%d %s</title></head>%n" +
|
String.format("<!DOCTYPE html>%n<html>%n<head><title>%d %s</title></head>%n" +
|
||||||
"<body><h1>%d %s</h1>%n<p>%s</p>%n</body></html>",
|
"<body><h1>%d %s</h1>%n<p>%s</p>%n</body></html>",
|
||||||
status, HttpResponseStatus.valueOf(status).reasonPhrase(),
|
status.code(), status.reasonPhrase(),
|
||||||
status, HttpResponseStatus.valueOf(status).reasonPhrase(),
|
status.code(), status.reasonPhrase(),
|
||||||
escapeHTML(text)));
|
escapeHTML(text)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(int status) {
|
public void write(HttpResponseStatus status) {
|
||||||
write(status, null, (ByteBuf) null);
|
write(status, null, (ByteBuf) null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(int status, String contentType, String text) {
|
public void write(HttpResponseStatus status, String contentType, String text) {
|
||||||
write(status, contentType, ByteBufUtil.writeUtf8(ctx.alloc(), text));
|
write(status, contentType, ByteBufUtil.writeUtf8(ctx.alloc(), text));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(int status, String contentType, String text, Charset charset) {
|
public void write(HttpResponseStatus status, String contentType, String text, Charset charset) {
|
||||||
write(status, contentType, ByteBufUtil.encodeString(ctx.alloc(), CharBuffer.allocate(text.length()).append(text), charset));
|
write(status, contentType, ByteBufUtil.encodeString(ctx.alloc(), CharBuffer.allocate(text.length()).append(text), charset));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(int status, String contentType, ByteBuf byteBuf) {
|
public void write(HttpResponseStatus status, String contentType, ByteBuf byteBuf) {
|
||||||
if (byteBuf != null) {
|
if (byteBuf != null) {
|
||||||
CharSequence s = headers.get(HttpHeaderNames.CONTENT_TYPE);
|
CharSequence s = headers.get(HttpHeaderNames.CONTENT_TYPE);
|
||||||
if (s == null) {
|
if (s == null) {
|
||||||
|
@ -123,9 +120,9 @@ public class HttpServerResponse implements ServerResponse {
|
||||||
}
|
}
|
||||||
FullHttpResponse fullHttpResponse = byteBuf != null ?
|
FullHttpResponse fullHttpResponse = byteBuf != null ?
|
||||||
new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,
|
new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,
|
||||||
HttpResponseStatus.valueOf(status), byteBuf, headers, trailingHeaders) :
|
status, byteBuf, headers, trailingHeaders) :
|
||||||
new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,
|
new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,
|
||||||
HttpResponseStatus.valueOf(status), Unpooled.EMPTY_BUFFER, headers, trailingHeaders);
|
status, Unpooled.EMPTY_BUFFER, headers, trailingHeaders);
|
||||||
if (serverRequest != null && serverRequest.getSequenceId() != null) {
|
if (serverRequest != null && serverRequest.getSequenceId() != null) {
|
||||||
HttpPipelinedResponse httpPipelinedResponse = new HttpPipelinedResponse(fullHttpResponse,
|
HttpPipelinedResponse httpPipelinedResponse = new HttpPipelinedResponse(fullHttpResponse,
|
||||||
ctx.channel().newPromise(), serverRequest.getSequenceId());
|
ctx.channel().newPromise(), serverRequest.getSequenceId());
|
||||||
|
@ -176,7 +173,7 @@ public class HttpServerResponse implements ServerResponse {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (ref != null) {
|
if (ref != null) {
|
||||||
es.append(s.substring(start, i)).append(ref);
|
es.append(s, start, i).append(ref);
|
||||||
start = i + 1;
|
start = i + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ package org.xbib.netty.http.server.transport;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.handler.codec.http.FullHttpRequest;
|
import io.netty.handler.codec.http.FullHttpRequest;
|
||||||
import io.netty.handler.codec.http.HttpHeaderNames;
|
import io.netty.handler.codec.http.HttpHeaderNames;
|
||||||
|
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||||
import io.netty.handler.codec.http2.Http2Settings;
|
import io.netty.handler.codec.http2.Http2Settings;
|
||||||
import org.xbib.netty.http.common.HttpAddress;
|
import org.xbib.netty.http.common.HttpAddress;
|
||||||
import org.xbib.netty.http.server.Server;
|
import org.xbib.netty.http.server.Server;
|
||||||
|
@ -35,6 +36,8 @@ public class HttpServerTransport extends BaseServerTransport {
|
||||||
ServerResponse serverResponse = new HttpServerResponse(serverRequest, ctx);
|
ServerResponse serverResponse = new HttpServerResponse(serverRequest, ctx);
|
||||||
if (acceptRequest(serverRequest, serverResponse)) {
|
if (acceptRequest(serverRequest, serverResponse)) {
|
||||||
handle(serverRequest, serverResponse);
|
handle(serverRequest, serverResponse);
|
||||||
|
} else {
|
||||||
|
serverResponse.write(HttpResponseStatus.NOT_ACCEPTABLE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,8 @@ public class ServerRequest {
|
||||||
|
|
||||||
private final Integer requestId;
|
private final Integer requestId;
|
||||||
|
|
||||||
|
private String contextPath;
|
||||||
|
|
||||||
public ServerRequest(VirtualServer virtualServer, HttpAddress httpAddress,
|
public ServerRequest(VirtualServer virtualServer, HttpAddress httpAddress,
|
||||||
FullHttpRequest httpRequest, Integer sequenceId, Integer streamId, Integer requestId) {
|
FullHttpRequest httpRequest, Integer sequenceId, Integer streamId, Integer requestId) {
|
||||||
this.virtualServer = virtualServer;
|
this.virtualServer = virtualServer;
|
||||||
|
@ -35,6 +37,18 @@ public class ServerRequest {
|
||||||
return virtualServer;
|
return virtualServer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setContextPath(String contextPath) {
|
||||||
|
this.contextPath = contextPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getContextPath() {
|
||||||
|
return contextPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRequestPath() {
|
||||||
|
return contextPath != null ? httpRequest.uri().substring(contextPath.length()) : httpRequest.uri();
|
||||||
|
}
|
||||||
|
|
||||||
public HttpAddress getHttpAddress() {
|
public HttpAddress getHttpAddress() {
|
||||||
return httpAddress;
|
return httpAddress;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package org.xbib.netty.http.server.transport;
|
package org.xbib.netty.http.server.transport;
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||||
import io.netty.util.AsciiString;
|
import io.netty.util.AsciiString;
|
||||||
|
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
|
@ -14,16 +15,16 @@ public interface ServerResponse {
|
||||||
|
|
||||||
void write(String text);
|
void write(String text);
|
||||||
|
|
||||||
void writeError(int status);
|
void writeError(HttpResponseStatus status);
|
||||||
|
|
||||||
void writeError(int status, String text);
|
void writeError(HttpResponseStatus status, String text);
|
||||||
|
|
||||||
void write(int status);
|
void write(HttpResponseStatus status);
|
||||||
|
|
||||||
void write(int status, String contentType, String text);
|
void write(HttpResponseStatus status, String contentType, String text);
|
||||||
|
|
||||||
void write(int status, String contentType, String text, Charset charset);
|
void write(HttpResponseStatus status, String contentType, String text, Charset charset);
|
||||||
|
|
||||||
void write(int status, String contentType, ByteBuf byteBuf);
|
void write(HttpResponseStatus status, String contentType, ByteBuf byteBuf);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,81 @@
|
||||||
|
package org.xbib.netty.http.server.util;
|
||||||
|
|
||||||
|
import java.net.URLConnection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static java.util.Objects.requireNonNull;
|
||||||
|
|
||||||
|
public class MimeTypeUtils {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A map from extension to MIME types, which is queried before
|
||||||
|
* {@link URLConnection#guessContentTypeFromName(String)}, so that
|
||||||
|
* important extensions are always mapped to the right MIME types.
|
||||||
|
*/
|
||||||
|
private static final Map<String, String> EXTENSION_TO_MEDIA_TYPE;
|
||||||
|
|
||||||
|
static {
|
||||||
|
Map<String, String> map = new HashMap<>();
|
||||||
|
// Text files
|
||||||
|
add(map, "text/css", "css");
|
||||||
|
add(map, "text/html", "html", "htm");
|
||||||
|
add(map, "text/plain", "txt");
|
||||||
|
|
||||||
|
// Image files
|
||||||
|
add(map, "image/gif", "gif");
|
||||||
|
add(map, "image/jpeg", "jpeg", "jpg");
|
||||||
|
add(map, "image/png", "png");
|
||||||
|
add(map, "image/svg+xml", "svg", "svgz");
|
||||||
|
add(map, "image/x-icon", "ico");
|
||||||
|
|
||||||
|
// Font files
|
||||||
|
add(map, "application/x-font-ttf", "ttc", "ttf");
|
||||||
|
add(map, "application/font-woff", "woff");
|
||||||
|
add(map, "application/font-woff2", "woff2");
|
||||||
|
add(map, "application/vnd.ms-fontobject", "eot");
|
||||||
|
add(map, "font/opentype", "otf");
|
||||||
|
|
||||||
|
// JavaScript, XML, etc
|
||||||
|
add(map, "application/javascript", "js", "map");
|
||||||
|
add(map, "application/json", "json");
|
||||||
|
add(map, "application/pdf", "pdf");
|
||||||
|
add(map, "application/xhtml+xml", "xhtml", "xhtm");
|
||||||
|
add(map, "application/xml", "xml", "xsd");
|
||||||
|
add(map, "application/xml-dtd", "dtd");
|
||||||
|
|
||||||
|
EXTENSION_TO_MEDIA_TYPE = Collections.unmodifiableMap(map);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void add(Map<String, String> extensionToMediaType,
|
||||||
|
String mediaType, String... extensions) {
|
||||||
|
for (String s : extensions) {
|
||||||
|
extensionToMediaType.put(s, mediaType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String guessFromPath(String path, boolean preCompressed) {
|
||||||
|
requireNonNull(path, "path");
|
||||||
|
String s = path;
|
||||||
|
// If the path is for a precompressed file, it will have an additional extension indicating the
|
||||||
|
// encoding, which we don't want to use when determining content type.
|
||||||
|
if (preCompressed) {
|
||||||
|
s = s.substring(0, s.lastIndexOf('.'));
|
||||||
|
}
|
||||||
|
int dotIdx = s.lastIndexOf('.');
|
||||||
|
int slashIdx = s.lastIndexOf('/');
|
||||||
|
if (dotIdx < 0 || slashIdx > dotIdx) {
|
||||||
|
// No extension
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String extension = s.substring(dotIdx + 1).toLowerCase(Locale.ROOT);
|
||||||
|
String mediaType = EXTENSION_TO_MEDIA_TYPE.get(extension);
|
||||||
|
if (mediaType != null) {
|
||||||
|
return mediaType;
|
||||||
|
}
|
||||||
|
String guessedContentType = URLConnection.guessContentTypeFromName(path);
|
||||||
|
return guessedContentType != null ? guessedContentType : "application/octet-stream";
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,8 +1,8 @@
|
||||||
package org.xbib.netty.http.server.test;
|
package org.xbib.netty.http.server.test;
|
||||||
|
|
||||||
|
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||||
import io.netty.handler.codec.http.HttpVersion;
|
import io.netty.handler.codec.http.HttpVersion;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.xbib.TestBase;
|
|
||||||
import org.xbib.netty.http.client.Client;
|
import org.xbib.netty.http.client.Client;
|
||||||
import org.xbib.netty.http.client.Request;
|
import org.xbib.netty.http.client.Request;
|
||||||
import org.xbib.netty.http.client.listener.ResponseListener;
|
import org.xbib.netty.http.client.listener.ResponseListener;
|
||||||
|
@ -30,7 +30,7 @@ public class CleartextHttp1Test extends TestBase {
|
||||||
Server server = Server.builder()
|
Server server = Server.builder()
|
||||||
.bind(httpAddress).build();
|
.bind(httpAddress).build();
|
||||||
server.getDefaultVirtualServer().addContext("/", (request, response) ->
|
server.getDefaultVirtualServer().addContext("/", (request, response) ->
|
||||||
response.write(200, "text/plain", request.getRequest().content().retain()));
|
response.write(HttpResponseStatus.OK, "text/plain", request.getRequest().content().retain()));
|
||||||
server.accept();
|
server.accept();
|
||||||
Client client = Client.builder()
|
Client client = Client.builder()
|
||||||
.build();
|
.build();
|
||||||
|
@ -63,7 +63,7 @@ public class CleartextHttp1Test extends TestBase {
|
||||||
//.enableDebug()
|
//.enableDebug()
|
||||||
.bind(httpAddress).build();
|
.bind(httpAddress).build();
|
||||||
server.getDefaultVirtualServer().addContext("/", (request, response) -> {
|
server.getDefaultVirtualServer().addContext("/", (request, response) -> {
|
||||||
response.write(200, "text/plain", request.getRequest().content().retain());
|
response.write(HttpResponseStatus.OK, "text/plain", request.getRequest().content().retain());
|
||||||
});
|
});
|
||||||
server.accept();
|
server.accept();
|
||||||
Client client = Client.builder()
|
Client client = Client.builder()
|
||||||
|
@ -109,7 +109,7 @@ public class CleartextHttp1Test extends TestBase {
|
||||||
//.enableDebug()
|
//.enableDebug()
|
||||||
.bind(httpAddress).build();
|
.bind(httpAddress).build();
|
||||||
server.getDefaultVirtualServer().addContext("/", (request, response) -> {
|
server.getDefaultVirtualServer().addContext("/", (request, response) -> {
|
||||||
response.write(200, "text/plain", request.getRequest().content().retain());
|
response.write(HttpResponseStatus.OK, "text/plain", request.getRequest().content().retain());
|
||||||
});
|
});
|
||||||
server.accept();
|
server.accept();
|
||||||
Client client = Client.builder()
|
Client client = Client.builder()
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package org.xbib.netty.http.server.test;
|
package org.xbib.netty.http.server.test;
|
||||||
|
|
||||||
|
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.xbib.TestBase;
|
|
||||||
import org.xbib.netty.http.client.Client;
|
import org.xbib.netty.http.client.Client;
|
||||||
import org.xbib.netty.http.client.Request;
|
import org.xbib.netty.http.client.Request;
|
||||||
import org.xbib.netty.http.client.listener.ResponseListener;
|
import org.xbib.netty.http.client.listener.ResponseListener;
|
||||||
|
@ -31,7 +31,7 @@ public class CleartextHttp2Test extends TestBase {
|
||||||
.bind(httpAddress)
|
.bind(httpAddress)
|
||||||
.build();
|
.build();
|
||||||
server.getDefaultVirtualServer().addContext("/", (request, response) ->
|
server.getDefaultVirtualServer().addContext("/", (request, response) ->
|
||||||
response.write(200, "text/plain", request.getRequest().content().retain()));
|
response.write(HttpResponseStatus.OK, "text/plain", request.getRequest().content().retain()));
|
||||||
server.accept();
|
server.accept();
|
||||||
Client client = Client.builder()
|
Client client = Client.builder()
|
||||||
.build();
|
.build();
|
||||||
|
@ -71,7 +71,7 @@ public class CleartextHttp2Test extends TestBase {
|
||||||
Server server = Server.builder()
|
Server server = Server.builder()
|
||||||
.bind(httpAddress).build();
|
.bind(httpAddress).build();
|
||||||
server.getDefaultVirtualServer().addContext("/", (request, response) ->
|
server.getDefaultVirtualServer().addContext("/", (request, response) ->
|
||||||
response.write(200, "text/plain", request.getRequest().content().retain()));
|
response.write(HttpResponseStatus.OK, "text/plain", request.getRequest().content().retain()));
|
||||||
//server.getDefaultVirtualServer().addContext("/", (request, response) ->
|
//server.getDefaultVirtualServer().addContext("/", (request, response) ->
|
||||||
// response.write(request.getRequest().content().toString(StandardCharsets.UTF_8)));
|
// response.write(request.getRequest().content().toString(StandardCharsets.UTF_8)));
|
||||||
server.accept();
|
server.accept();
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
package org.xbib.netty.http.server.test;
|
package org.xbib.netty.http.server.test;
|
||||||
|
|
||||||
|
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||||
import io.netty.handler.codec.http.HttpVersion;
|
import io.netty.handler.codec.http.HttpVersion;
|
||||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||||
import org.junit.Ignore;
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.xbib.TestBase;
|
|
||||||
import org.xbib.netty.http.client.Client;
|
import org.xbib.netty.http.client.Client;
|
||||||
import org.xbib.netty.http.client.Request;
|
import org.xbib.netty.http.client.Request;
|
||||||
import org.xbib.netty.http.client.listener.ResponseListener;
|
import org.xbib.netty.http.client.listener.ResponseListener;
|
||||||
|
@ -53,7 +52,7 @@ public class SecureHttp1Test extends TestBase {
|
||||||
};
|
};
|
||||||
try {
|
try {
|
||||||
server.getDefaultVirtualServer().addContext("/", (request, response) ->
|
server.getDefaultVirtualServer().addContext("/", (request, response) ->
|
||||||
response.write(200, "text/plain", request.getRequest().content().retain()));
|
response.write(HttpResponseStatus.OK, "text/plain", request.getRequest().content().retain()));
|
||||||
server.accept();
|
server.accept();
|
||||||
Request request = Request.get().setVersion(HttpVersion.HTTP_1_1)
|
Request request = Request.get().setVersion(HttpVersion.HTTP_1_1)
|
||||||
.url(server.getServerConfig().getAddress().base())
|
.url(server.getServerConfig().getAddress().base())
|
||||||
|
@ -77,7 +76,7 @@ public class SecureHttp1Test extends TestBase {
|
||||||
.setSelfCert()
|
.setSelfCert()
|
||||||
.bind(httpAddress).build();
|
.bind(httpAddress).build();
|
||||||
server.getDefaultVirtualServer().addContext("/", (request, response) ->
|
server.getDefaultVirtualServer().addContext("/", (request, response) ->
|
||||||
response.write(200, "text/plain", request.getRequest().content().retain()));
|
response.write(HttpResponseStatus.OK, "text/plain", request.getRequest().content().retain()));
|
||||||
server.accept();
|
server.accept();
|
||||||
Client client = Client.builder()
|
Client client = Client.builder()
|
||||||
.setJdkSslProvider()
|
.setJdkSslProvider()
|
||||||
|
@ -125,7 +124,7 @@ public class SecureHttp1Test extends TestBase {
|
||||||
.bind(httpAddress)
|
.bind(httpAddress)
|
||||||
.build();
|
.build();
|
||||||
server.getDefaultVirtualServer().addContext("/", (request, response) ->
|
server.getDefaultVirtualServer().addContext("/", (request, response) ->
|
||||||
response.write(200, "text/plain", request.getRequest().content().retain())
|
response.write(HttpResponseStatus.OK, "text/plain", request.getRequest().content().retain())
|
||||||
);
|
);
|
||||||
server.accept();
|
server.accept();
|
||||||
Client client = Client.builder()
|
Client client = Client.builder()
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
package org.xbib.netty.http.server.test;
|
package org.xbib.netty.http.server.test;
|
||||||
|
|
||||||
|
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||||
import org.junit.Ignore;
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.xbib.TestBase;
|
|
||||||
import org.xbib.netty.http.client.Client;
|
import org.xbib.netty.http.client.Client;
|
||||||
import org.xbib.netty.http.client.Request;
|
import org.xbib.netty.http.client.Request;
|
||||||
import org.xbib.netty.http.client.listener.ResponseListener;
|
import org.xbib.netty.http.client.listener.ResponseListener;
|
||||||
|
@ -42,7 +41,7 @@ public class SecureHttp2Test extends TestBase {
|
||||||
.bind(httpAddress)
|
.bind(httpAddress)
|
||||||
.build();
|
.build();
|
||||||
server.getDefaultVirtualServer().addContext("/", (request, response) ->
|
server.getDefaultVirtualServer().addContext("/", (request, response) ->
|
||||||
response.write(200, "text/plain", request.getRequest().content().retain()));
|
response.write(HttpResponseStatus.OK, "text/plain", request.getRequest().content().retain()));
|
||||||
server.accept();
|
server.accept();
|
||||||
Client client = Client.builder()
|
Client client = Client.builder()
|
||||||
.setJdkSslProvider()
|
.setJdkSslProvider()
|
||||||
|
@ -86,7 +85,7 @@ public class SecureHttp2Test extends TestBase {
|
||||||
.bind(httpAddress)
|
.bind(httpAddress)
|
||||||
.build();
|
.build();
|
||||||
server.getDefaultVirtualServer().addContext("/", (request, response) ->
|
server.getDefaultVirtualServer().addContext("/", (request, response) ->
|
||||||
response.write(200, "text/plain", request.getRequest().content().retain()));
|
response.write(HttpResponseStatus.OK, "text/plain", request.getRequest().content().retain()));
|
||||||
server.accept();
|
server.accept();
|
||||||
Client client = Client.builder()
|
Client client = Client.builder()
|
||||||
.setJdkSslProvider()
|
.setJdkSslProvider()
|
||||||
|
@ -138,7 +137,7 @@ public class SecureHttp2Test extends TestBase {
|
||||||
.bind(httpAddress)
|
.bind(httpAddress)
|
||||||
.build();
|
.build();
|
||||||
server.getDefaultVirtualServer().addContext("/", (request, response) ->
|
server.getDefaultVirtualServer().addContext("/", (request, response) ->
|
||||||
response.write(200, "text/plain", request.getRequest().content().retain())
|
response.write(HttpResponseStatus.OK, "text/plain", request.getRequest().content().retain())
|
||||||
);
|
);
|
||||||
server.accept();
|
server.accept();
|
||||||
Client client = Client.builder()
|
Client client = Client.builder()
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
package org.xbib.netty.http.server.test;
|
||||||
|
|
||||||
|
import io.netty.handler.codec.http.HttpVersion;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.xbib.netty.http.client.Client;
|
||||||
|
import org.xbib.netty.http.client.Request;
|
||||||
|
import org.xbib.netty.http.server.Server;
|
||||||
|
import org.xbib.netty.http.server.context.NioContextHandler;
|
||||||
|
import org.xbib.netty.http.server.context.VirtualServer;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
public class StaticFileServerTest {
|
||||||
|
|
||||||
|
private static final Logger logger = Logger.getLogger(StaticFileServerTest.class.getName());
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testStaticFileServer() throws Exception {
|
||||||
|
Path vartmp = Paths.get("/var/tmp/");
|
||||||
|
Server server = Server.builder()
|
||||||
|
.addVirtualServer(new VirtualServer().addContext("/static", new NioContextHandler(vartmp)))
|
||||||
|
.build();
|
||||||
|
Client client = Client.builder()
|
||||||
|
.build();
|
||||||
|
final AtomicBoolean success = new AtomicBoolean(false);
|
||||||
|
try {
|
||||||
|
Files.write(vartmp.resolve("test.txt"), "Hello Jörg".getBytes(StandardCharsets.UTF_8));
|
||||||
|
server.accept();
|
||||||
|
Request request = Request.get().setVersion(HttpVersion.HTTP_1_1)
|
||||||
|
.url(server.getServerConfig().getAddress().base().resolve("/static/test.txt"))
|
||||||
|
.build()
|
||||||
|
.setResponseListener(r -> {
|
||||||
|
assertEquals("Hello Jörg", r.content().toString(StandardCharsets.UTF_8));
|
||||||
|
success.set(true);
|
||||||
|
});
|
||||||
|
logger.log(Level.INFO, request.toString());
|
||||||
|
client.execute(request).get();
|
||||||
|
logger.log(Level.INFO, "request complete");
|
||||||
|
} finally {
|
||||||
|
server.shutdownGracefully();
|
||||||
|
client.shutdownGracefully();
|
||||||
|
Files.delete(vartmp.resolve("test.txt"));
|
||||||
|
}
|
||||||
|
assertTrue(success.get());
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package org.xbib;
|
package org.xbib.netty.http.server.test;
|
||||||
|
|
||||||
import java.util.logging.ConsoleHandler;
|
import java.util.logging.ConsoleHandler;
|
||||||
import java.util.logging.Handler;
|
import java.util.logging.Handler;
|
|
@ -3,7 +3,6 @@ package org.xbib.netty.http.server.test;
|
||||||
import io.netty.buffer.UnpooledByteBufAllocator;
|
import io.netty.buffer.UnpooledByteBufAllocator;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.xbib.TestBase;
|
|
||||||
import org.xbib.netty.http.server.Server;
|
import org.xbib.netty.http.server.Server;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package org.xbib.netty.http.hacks;
|
package org.xbib.netty.http.server.test.hacks;
|
||||||
|
|
||||||
import io.netty.bootstrap.Bootstrap;
|
import io.netty.bootstrap.Bootstrap;
|
||||||
import io.netty.bootstrap.ServerBootstrap;
|
import io.netty.bootstrap.ServerBootstrap;
|
||||||
|
@ -31,7 +31,7 @@ import io.netty.handler.logging.LogLevel;
|
||||||
import io.netty.handler.logging.LoggingHandler;
|
import io.netty.handler.logging.LoggingHandler;
|
||||||
import org.junit.Ignore;
|
import org.junit.Ignore;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.xbib.TestBase;
|
import org.xbib.netty.http.server.test.TestBase;
|
||||||
|
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
|
@ -1,4 +1,4 @@
|
||||||
package org.xbib.netty.http.hacks;
|
package org.xbib.netty.http.server.test.hacks;
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.buffer.ByteBufUtil;
|
import io.netty.buffer.ByteBufUtil;
|
||||||
|
@ -24,7 +24,7 @@ import org.junit.Test;
|
||||||
import org.xbib.netty.http.server.handler.http.HttpPipelinedRequest;
|
import org.xbib.netty.http.server.handler.http.HttpPipelinedRequest;
|
||||||
import org.xbib.netty.http.server.handler.http.HttpPipelinedResponse;
|
import org.xbib.netty.http.server.handler.http.HttpPipelinedResponse;
|
||||||
import org.xbib.netty.http.server.handler.http.HttpPipeliningHandler;
|
import org.xbib.netty.http.server.handler.http.HttpPipeliningHandler;
|
||||||
import org.xbib.TestBase;
|
import org.xbib.netty.http.server.test.TestBase;
|
||||||
|
|
||||||
import java.nio.channels.ClosedChannelException;
|
import java.nio.channels.ClosedChannelException;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
|
@ -1,4 +1,4 @@
|
||||||
package org.xbib.netty.http.hacks;
|
package org.xbib.netty.http.server.test.hacks;
|
||||||
|
|
||||||
import io.netty.bootstrap.Bootstrap;
|
import io.netty.bootstrap.Bootstrap;
|
||||||
import io.netty.bootstrap.ServerBootstrap;
|
import io.netty.bootstrap.ServerBootstrap;
|
||||||
|
@ -43,7 +43,7 @@ import io.netty.handler.logging.LoggingHandler;
|
||||||
import io.netty.util.AsciiString;
|
import io.netty.util.AsciiString;
|
||||||
import org.junit.Ignore;
|
import org.junit.Ignore;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.xbib.TestBase;
|
import org.xbib.netty.http.server.test.TestBase;
|
||||||
|
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
|
@ -1,4 +1,4 @@
|
||||||
package org.xbib.netty.http.hacks;
|
package org.xbib.netty.http.server.test.hacks;
|
||||||
|
|
||||||
import io.netty.bootstrap.Bootstrap;
|
import io.netty.bootstrap.Bootstrap;
|
||||||
import io.netty.bootstrap.ServerBootstrap;
|
import io.netty.bootstrap.ServerBootstrap;
|
||||||
|
@ -29,7 +29,7 @@ import io.netty.handler.codec.http2.HttpToHttp2ConnectionHandlerBuilder;
|
||||||
import io.netty.handler.codec.http2.InboundHttp2ToHttpAdapterBuilder;
|
import io.netty.handler.codec.http2.InboundHttp2ToHttpAdapterBuilder;
|
||||||
import org.junit.Ignore;
|
import org.junit.Ignore;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.xbib.TestBase;
|
import org.xbib.netty.http.server.test.TestBase;
|
||||||
|
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
|
@ -1,4 +1,4 @@
|
||||||
package org.xbib.netty.http.hacks;
|
package org.xbib.netty.http.server.test.hacks;
|
||||||
|
|
||||||
import io.netty.bootstrap.Bootstrap;
|
import io.netty.bootstrap.Bootstrap;
|
||||||
import io.netty.bootstrap.ServerBootstrap;
|
import io.netty.bootstrap.ServerBootstrap;
|
||||||
|
@ -37,7 +37,7 @@ import io.netty.handler.codec.http2.Http2StreamFrameToHttpObjectCodec;
|
||||||
import io.netty.util.AsciiString;
|
import io.netty.util.AsciiString;
|
||||||
import org.junit.Ignore;
|
import org.junit.Ignore;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.xbib.TestBase;
|
import org.xbib.netty.http.server.test.TestBase;
|
||||||
|
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
|
@ -1,4 +1,4 @@
|
||||||
/**
|
/**
|
||||||
* Hacking Netty for showing server functions.
|
* Hacking Netty for showing server functions.
|
||||||
*/
|
*/
|
||||||
package org.xbib.netty.http.hacks;
|
package org.xbib.netty.http.server.test.hacks;
|
5
netty-http-server/src/test/resources/logging.properties
Normal file
5
netty-http-server/src/test/resources/logging.properties
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
handlers = java.util.logging.ConsoleHandler
|
||||||
|
.level = FINE
|
||||||
|
java.util.logging.ConsoleHandler.level = FINE
|
||||||
|
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
|
||||||
|
java.util.logging.SimpleFormatter.format = %1$tFT%1$tT.%1$tL%1$tz [%4$-11s] [%3$s] %5$s %6$s%n
|
Loading…
Reference in a new issue