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 {
|
||||
if (JavaVersion.current() == JavaVersion.VERSION_1_8) {
|
||||
jvmArgs "-javaagent:" + configurations.alpnagent.asPath
|
||||
}
|
||||
systemProperty 'java.util.logging.config.file', 'src/test/resources/logging.properties'
|
||||
testLogging {
|
||||
// set this to 'true' to show lot of debug output
|
||||
showStandardStreams = false
|
||||
exceptionFormat = 'full'
|
||||
}
|
||||
if (JavaVersion.current() == JavaVersion.VERSION_1_8) {
|
||||
jvmArgs "-javaagent:" + configurations.alpnagent.asPath
|
||||
}
|
||||
}
|
||||
|
||||
clean {
|
||||
|
|
|
@ -1,17 +1,16 @@
|
|||
group = org.xbib
|
||||
name = netty-http
|
||||
version = 4.1.34.0
|
||||
version = 4.1.35.0
|
||||
|
||||
# main packages
|
||||
netty.version = 4.1.34.Final
|
||||
netty.version = 4.1.35.Final
|
||||
tcnative.version = 2.0.22.Final
|
||||
bouncycastle.version = 1.61
|
||||
alpnagent.version = 2.0.9
|
||||
xbib-net-url.version = 1.2.1
|
||||
xbib-net-url.version = 1.2.2
|
||||
|
||||
# test packages
|
||||
junit.version = 4.12
|
||||
# 1.0.1
|
||||
conscrypt.version = 2.0.0
|
||||
jackson.version = 2.8.11.1
|
||||
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
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
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
|
||||
|
||||
#
|
||||
# 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
|
||||
|
@ -28,7 +44,7 @@ APP_NAME="Gradle"
|
|||
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.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m"'
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
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
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
|
@ -14,7 +30,7 @@ set APP_BASE_NAME=%~n0
|
|||
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.
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m"
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
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);
|
||||
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
|
||||
Request request = requests.remove(requests.lastKey());
|
||||
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
|
|
@ -224,7 +224,7 @@ public class ServerBuilder {
|
|||
return this;
|
||||
}
|
||||
|
||||
public ServerBuilder addVirtualHost(VirtualServer virtualServer) {
|
||||
public ServerBuilder addVirtualServer(VirtualServer virtualServer) {
|
||||
this.serverConfig.addVirtualServer(virtualServer);
|
||||
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;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
|
@ -8,12 +8,13 @@ import java.util.Map;
|
|||
*/
|
||||
public class ContextInfo {
|
||||
|
||||
private final Map<String, ContextHandler> handlers = new HashMap<>(2);
|
||||
|
||||
private final VirtualServer virtualServer;
|
||||
|
||||
private final Map<String, ContextHandler> methodHandlerMap;
|
||||
|
||||
public ContextInfo(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
|
||||
*/
|
||||
public Map<String, ContextHandler> getHandlers() {
|
||||
return handlers;
|
||||
public Map<String, ContextHandler> getMethodHandlerMap() {
|
||||
return methodHandlerMap;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -33,11 +34,13 @@ public class ContextInfo {
|
|||
*/
|
||||
public void addHandler(ContextHandler handler, String... methods) {
|
||||
if (methods.length == 0) {
|
||||
methods = new String[]{"GET"};
|
||||
}
|
||||
methodHandlerMap.put("GET", handler);
|
||||
virtualServer.getMethods().add("GET");
|
||||
} else {
|
||||
for (String method : methods) {
|
||||
handlers.put(method, handler);
|
||||
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.ByteBufAllocator;
|
||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||
import org.xbib.netty.http.server.transport.ServerRequest;
|
||||
import org.xbib.netty.http.server.transport.ServerResponse;
|
||||
|
||||
|
@ -28,11 +29,10 @@ public class DirectoryContextHandler implements ContextHandler {
|
|||
String uri = serverRequest.getRequest().uri();
|
||||
Path p = path.resolve(uri);
|
||||
ByteBuf byteBuf = read(allocator, p);
|
||||
serverResponse.write(200, "application/octet-stream", byteBuf);
|
||||
serverResponse.write(HttpResponseStatus.OK, "application/octet-stream", byteBuf);
|
||||
byteBuf.release();
|
||||
}
|
||||
|
||||
|
||||
public static ByteBuf read(ByteBufAllocator allocator, Path path)
|
||||
throws IOException {
|
||||
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;
|
||||
|
||||
public VirtualServer() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a VirtualServer with the given name.
|
||||
*
|
||||
|
@ -94,25 +98,6 @@ public class VirtualServer {
|
|||
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.
|
||||
* 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")
|
||||
* @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("*")) {
|
||||
throw new IllegalArgumentException("invalid path: " + path);
|
||||
}
|
||||
path = trimRight(path, '/');
|
||||
String s = trimRight(path, '/');
|
||||
ContextInfo info = new ContextInfo(this);
|
||||
ContextInfo existing = contexts.putIfAbsent(path, info);
|
||||
ContextInfo existing = contexts.putIfAbsent(s, info);
|
||||
info = existing != null ? existing : info;
|
||||
info.addHandler(handler, methods);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -141,17 +127,37 @@ public class VirtualServer {
|
|||
* @throws IllegalArgumentException if a Context-annotated
|
||||
* 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()) {
|
||||
// add to contexts those with @Context annotation
|
||||
for (Method m : c.getDeclaredMethods()) {
|
||||
Context context = m.getAnnotation(Context.class);
|
||||
if (context != null) {
|
||||
//m.setAccessible(true); // allow access to private method
|
||||
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
|
||||
*/
|
||||
private static String getParentPath(String path) {
|
||||
path = trimRight(path, '/'); // remove trailing slash
|
||||
int slash = path.lastIndexOf('/');
|
||||
return slash == -1 ? null : path.substring(0, slash);
|
||||
String s = trimRight(path, '/'); // remove trailing slash
|
||||
int slash = s.lastIndexOf('/');
|
||||
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.handler.codec.http.HttpHeaderNames;
|
||||
import io.netty.handler.codec.http.HttpHeaders;
|
||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||
import io.netty.handler.codec.http.HttpVersion;
|
||||
import org.xbib.netty.http.server.Server;
|
||||
import org.xbib.netty.http.server.context.ContextHandler;
|
||||
|
@ -33,7 +34,7 @@ abstract class BaseServerTransport implements ServerTransport {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void exceptionReceived(ChannelHandlerContext ctx, Throwable throwable) throws IOException {
|
||||
public void exceptionReceived(ChannelHandlerContext ctx, Throwable throwable) {
|
||||
logger.log(Level.WARNING, throwable.getMessage(), throwable);
|
||||
}
|
||||
|
||||
|
@ -54,7 +55,7 @@ abstract class BaseServerTransport implements ServerTransport {
|
|||
case 2:
|
||||
if (!reqHeaders.contains(HttpHeaderNames.HOST)) {
|
||||
// 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 a continue response before reading body
|
||||
|
@ -65,13 +66,13 @@ abstract class BaseServerTransport implements ServerTransport {
|
|||
//tempResp.sendHeaders(100);
|
||||
} else {
|
||||
// RFC2616#14.20: if unknown expect, send 417
|
||||
serverResponse.writeError(417);
|
||||
serverResponse.writeError(HttpResponseStatus.EXPECTATION_FAILED);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
serverResponse.writeError(400, "Unknown version: " + version);
|
||||
serverResponse.writeError(HttpResponseStatus.BAD_REQUEST, "Unknown version: " + version);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
@ -88,12 +89,14 @@ abstract class BaseServerTransport implements ServerTransport {
|
|||
String method = serverRequest.getRequest().method().name();
|
||||
String path = serverRequest.getRequest().uri();
|
||||
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
|
||||
if (method.equals("GET") || method.equals("HEAD") || handlers.containsKey(method)) {
|
||||
ContextHandler handler = virtualServer.getContext(path).getHandlers().get(method);
|
||||
if (method.equals("GET") || method.equals("HEAD") || methodHandlerMap.containsKey(method)) {
|
||||
ContextHandler handler = methodHandlerMap.get(method);
|
||||
if (handler == null) {
|
||||
serverResponse.writeError(404);
|
||||
serverResponse.writeError(HttpResponseStatus.NOT_FOUND);
|
||||
} else {
|
||||
handler.serve(serverRequest, serverResponse);
|
||||
}
|
||||
|
@ -101,15 +104,15 @@ abstract class BaseServerTransport implements ServerTransport {
|
|||
Set<String> methods = new LinkedHashSet<>(METHODS);
|
||||
// "*" is a special server-wide (no-context) request supported by 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));
|
||||
if (method.equals("OPTIONS")) { // default OPTIONS handler
|
||||
serverResponse.setHeader(HttpHeaderNames.CONTENT_LENGTH, "0"); // RFC2616#9.2
|
||||
serverResponse.write(200);
|
||||
serverResponse.write(HttpResponseStatus.OK);
|
||||
} 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 {
|
||||
serverResponse.writeError(501); // unsupported method
|
||||
serverResponse.writeError(HttpResponseStatus.NOT_IMPLEMENTED); // unsupported method
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,7 +41,7 @@ public class Http2ServerResponse implements ServerResponse {
|
|||
|
||||
@Override
|
||||
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
|
||||
*/
|
||||
@Override
|
||||
public void writeError(int status) {
|
||||
writeError(status, status < 400 ? ":)" : "sorry it didn't work out :(");
|
||||
public void writeError(HttpResponseStatus status) {
|
||||
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)
|
||||
*/
|
||||
@Override
|
||||
public void writeError(int status, String text) {
|
||||
public void writeError(HttpResponseStatus status, String text) {
|
||||
write(status, "text/html; charset=utf-8",
|
||||
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>",
|
||||
status, HttpResponseStatus.valueOf(status).reasonPhrase(),
|
||||
status, HttpResponseStatus.valueOf(status).reasonPhrase(),
|
||||
status.code(), status.reasonPhrase(),
|
||||
status.code(), status.reasonPhrase(),
|
||||
escapeHTML(text)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(int status) {
|
||||
public void write(HttpResponseStatus status) {
|
||||
write(status, null, (ByteBuf) null);
|
||||
}
|
||||
|
||||
@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));
|
||||
}
|
||||
|
||||
@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));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(int status, String contentType, ByteBuf byteBuf) {
|
||||
public void write(HttpResponseStatus status, String contentType, ByteBuf byteBuf) {
|
||||
if (byteBuf != null) {
|
||||
CharSequence s = headers.get(HttpHeaderNames.CONTENT_TYPE);
|
||||
if (s == null) {
|
||||
|
@ -120,7 +120,7 @@ public class Http2ServerResponse implements ServerResponse {
|
|||
}
|
||||
}
|
||||
Http2Headers http2Headers = new DefaultHttp2Headers()
|
||||
.status(HttpResponseStatus.valueOf(status).codeAsText())
|
||||
.status(status.codeAsText())
|
||||
.add(headers);
|
||||
ctx.channel().write(new DefaultHttp2HeadersFrame(http2Headers,byteBuf == null));
|
||||
if (byteBuf != null) {
|
||||
|
|
|
@ -3,6 +3,7 @@ package org.xbib.netty.http.server.transport;
|
|||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.http.FullHttpRequest;
|
||||
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.HttpConversionUtil;
|
||||
import org.xbib.netty.http.common.HttpAddress;
|
||||
|
@ -36,6 +37,8 @@ public class Http2ServerTransport extends BaseServerTransport {
|
|||
ServerResponse serverResponse = new Http2ServerResponse(serverRequest, ctx);
|
||||
if (acceptRequest(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.ZonedDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class HttpServerResponse implements ServerResponse {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(HttpServerResponse.class.getName());
|
||||
|
||||
private final ServerRequest serverRequest;
|
||||
|
||||
private final ChannelHandlerContext ctx;
|
||||
|
@ -49,7 +46,7 @@ public class HttpServerResponse implements ServerResponse {
|
|||
|
||||
@Override
|
||||
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
|
||||
*/
|
||||
@Override
|
||||
public void writeError(int status) {
|
||||
writeError(status, status < 400 ? ":)" : "sorry it didn't work out :(");
|
||||
public void writeError(HttpResponseStatus status) {
|
||||
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)
|
||||
*/
|
||||
@Override
|
||||
public void writeError(int status, String text) {
|
||||
public void writeError(HttpResponseStatus status, String text) {
|
||||
write(status, "text/html; charset=utf-8",
|
||||
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>",
|
||||
status, HttpResponseStatus.valueOf(status).reasonPhrase(),
|
||||
status, HttpResponseStatus.valueOf(status).reasonPhrase(),
|
||||
status.code(), status.reasonPhrase(),
|
||||
status.code(), status.reasonPhrase(),
|
||||
escapeHTML(text)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(int status) {
|
||||
public void write(HttpResponseStatus status) {
|
||||
write(status, null, (ByteBuf) null);
|
||||
}
|
||||
|
||||
@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));
|
||||
}
|
||||
|
||||
@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));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(int status, String contentType, ByteBuf byteBuf) {
|
||||
public void write(HttpResponseStatus status, String contentType, ByteBuf byteBuf) {
|
||||
if (byteBuf != null) {
|
||||
CharSequence s = headers.get(HttpHeaderNames.CONTENT_TYPE);
|
||||
if (s == null) {
|
||||
|
@ -123,9 +120,9 @@ public class HttpServerResponse implements ServerResponse {
|
|||
}
|
||||
FullHttpResponse fullHttpResponse = byteBuf != null ?
|
||||
new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,
|
||||
HttpResponseStatus.valueOf(status), byteBuf, headers, trailingHeaders) :
|
||||
status, byteBuf, headers, trailingHeaders) :
|
||||
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) {
|
||||
HttpPipelinedResponse httpPipelinedResponse = new HttpPipelinedResponse(fullHttpResponse,
|
||||
ctx.channel().newPromise(), serverRequest.getSequenceId());
|
||||
|
@ -176,7 +173,7 @@ public class HttpServerResponse implements ServerResponse {
|
|||
break;
|
||||
}
|
||||
if (ref != null) {
|
||||
es.append(s.substring(start, i)).append(ref);
|
||||
es.append(s, start, i).append(ref);
|
||||
start = i + 1;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package org.xbib.netty.http.server.transport;
|
|||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.http.FullHttpRequest;
|
||||
import io.netty.handler.codec.http.HttpHeaderNames;
|
||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||
import io.netty.handler.codec.http2.Http2Settings;
|
||||
import org.xbib.netty.http.common.HttpAddress;
|
||||
import org.xbib.netty.http.server.Server;
|
||||
|
@ -35,6 +36,8 @@ public class HttpServerTransport extends BaseServerTransport {
|
|||
ServerResponse serverResponse = new HttpServerResponse(serverRequest, ctx);
|
||||
if (acceptRequest(serverRequest, serverResponse)) {
|
||||
handle(serverRequest, serverResponse);
|
||||
} else {
|
||||
serverResponse.write(HttpResponseStatus.NOT_ACCEPTABLE);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -21,6 +21,8 @@ public class ServerRequest {
|
|||
|
||||
private final Integer requestId;
|
||||
|
||||
private String contextPath;
|
||||
|
||||
public ServerRequest(VirtualServer virtualServer, HttpAddress httpAddress,
|
||||
FullHttpRequest httpRequest, Integer sequenceId, Integer streamId, Integer requestId) {
|
||||
this.virtualServer = virtualServer;
|
||||
|
@ -35,6 +37,18 @@ public class ServerRequest {
|
|||
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() {
|
||||
return httpAddress;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package org.xbib.netty.http.server.transport;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||
import io.netty.util.AsciiString;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
|
@ -14,16 +15,16 @@ public interface ServerResponse {
|
|||
|
||||
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;
|
||||
|
||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||
import io.netty.handler.codec.http.HttpVersion;
|
||||
import org.junit.Test;
|
||||
import org.xbib.TestBase;
|
||||
import org.xbib.netty.http.client.Client;
|
||||
import org.xbib.netty.http.client.Request;
|
||||
import org.xbib.netty.http.client.listener.ResponseListener;
|
||||
|
@ -30,7 +30,7 @@ public class CleartextHttp1Test extends TestBase {
|
|||
Server server = Server.builder()
|
||||
.bind(httpAddress).build();
|
||||
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();
|
||||
Client client = Client.builder()
|
||||
.build();
|
||||
|
@ -63,7 +63,7 @@ public class CleartextHttp1Test extends TestBase {
|
|||
//.enableDebug()
|
||||
.bind(httpAddress).build();
|
||||
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();
|
||||
Client client = Client.builder()
|
||||
|
@ -109,7 +109,7 @@ public class CleartextHttp1Test extends TestBase {
|
|||
//.enableDebug()
|
||||
.bind(httpAddress).build();
|
||||
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();
|
||||
Client client = Client.builder()
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package org.xbib.netty.http.server.test;
|
||||
|
||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||
import org.junit.Test;
|
||||
import org.xbib.TestBase;
|
||||
import org.xbib.netty.http.client.Client;
|
||||
import org.xbib.netty.http.client.Request;
|
||||
import org.xbib.netty.http.client.listener.ResponseListener;
|
||||
|
@ -31,7 +31,7 @@ public class CleartextHttp2Test extends TestBase {
|
|||
.bind(httpAddress)
|
||||
.build();
|
||||
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();
|
||||
Client client = Client.builder()
|
||||
.build();
|
||||
|
@ -71,7 +71,7 @@ public class CleartextHttp2Test extends TestBase {
|
|||
Server server = Server.builder()
|
||||
.bind(httpAddress).build();
|
||||
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) ->
|
||||
// response.write(request.getRequest().content().toString(StandardCharsets.UTF_8)));
|
||||
server.accept();
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
package org.xbib.netty.http.server.test;
|
||||
|
||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||
import io.netty.handler.codec.http.HttpVersion;
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.xbib.TestBase;
|
||||
import org.xbib.netty.http.client.Client;
|
||||
import org.xbib.netty.http.client.Request;
|
||||
import org.xbib.netty.http.client.listener.ResponseListener;
|
||||
|
@ -53,7 +52,7 @@ public class SecureHttp1Test extends TestBase {
|
|||
};
|
||||
try {
|
||||
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();
|
||||
Request request = Request.get().setVersion(HttpVersion.HTTP_1_1)
|
||||
.url(server.getServerConfig().getAddress().base())
|
||||
|
@ -77,7 +76,7 @@ public class SecureHttp1Test extends TestBase {
|
|||
.setSelfCert()
|
||||
.bind(httpAddress).build();
|
||||
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();
|
||||
Client client = Client.builder()
|
||||
.setJdkSslProvider()
|
||||
|
@ -125,7 +124,7 @@ public class SecureHttp1Test extends TestBase {
|
|||
.bind(httpAddress)
|
||||
.build();
|
||||
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();
|
||||
Client client = Client.builder()
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
package org.xbib.netty.http.server.test;
|
||||
|
||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.xbib.TestBase;
|
||||
import org.xbib.netty.http.client.Client;
|
||||
import org.xbib.netty.http.client.Request;
|
||||
import org.xbib.netty.http.client.listener.ResponseListener;
|
||||
|
@ -42,7 +41,7 @@ public class SecureHttp2Test extends TestBase {
|
|||
.bind(httpAddress)
|
||||
.build();
|
||||
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();
|
||||
Client client = Client.builder()
|
||||
.setJdkSslProvider()
|
||||
|
@ -86,7 +85,7 @@ public class SecureHttp2Test extends TestBase {
|
|||
.bind(httpAddress)
|
||||
.build();
|
||||
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();
|
||||
Client client = Client.builder()
|
||||
.setJdkSslProvider()
|
||||
|
@ -138,7 +137,7 @@ public class SecureHttp2Test extends TestBase {
|
|||
.bind(httpAddress)
|
||||
.build();
|
||||
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();
|
||||
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.Handler;
|
|
@ -3,7 +3,6 @@ package org.xbib.netty.http.server.test;
|
|||
import io.netty.buffer.UnpooledByteBufAllocator;
|
||||
import org.junit.After;
|
||||
import org.junit.Test;
|
||||
import org.xbib.TestBase;
|
||||
import org.xbib.netty.http.server.Server;
|
||||
|
||||
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.ServerBootstrap;
|
||||
|
@ -31,7 +31,7 @@ import io.netty.handler.logging.LogLevel;
|
|||
import io.netty.handler.logging.LoggingHandler;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.xbib.TestBase;
|
||||
import org.xbib.netty.http.server.test.TestBase;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
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.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.HttpPipelinedResponse;
|
||||
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.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.ServerBootstrap;
|
||||
|
@ -43,7 +43,7 @@ import io.netty.handler.logging.LoggingHandler;
|
|||
import io.netty.util.AsciiString;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.xbib.TestBase;
|
||||
import org.xbib.netty.http.server.test.TestBase;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
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.ServerBootstrap;
|
||||
|
@ -29,7 +29,7 @@ import io.netty.handler.codec.http2.HttpToHttp2ConnectionHandlerBuilder;
|
|||
import io.netty.handler.codec.http2.InboundHttp2ToHttpAdapterBuilder;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.xbib.TestBase;
|
||||
import org.xbib.netty.http.server.test.TestBase;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
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.ServerBootstrap;
|
||||
|
@ -37,7 +37,7 @@ import io.netty.handler.codec.http2.Http2StreamFrameToHttpObjectCodec;
|
|||
import io.netty.util.AsciiString;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.xbib.TestBase;
|
||||
import org.xbib.netty.http.server.test.TestBase;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.concurrent.CompletableFuture;
|
|
@ -1,4 +1,4 @@
|
|||
/**
|
||||
* 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