fix resolving endpoints, fix Unpooled.buffer(o) in flush, update to Netty 4.1.41

This commit is contained in:
Jörg Prante 2019-09-13 15:25:29 +02:00
parent 5c14695785
commit 833e502a7c
28 changed files with 931 additions and 357 deletions

View file

@ -1,13 +1,13 @@
group = org.xbib
name = netty-http
version = 4.1.39.2
version = 4.1.41.0
# netty
netty.version = 4.1.39.Final
netty.version = 4.1.41.Final
tcnative.version = 2.0.25.Final
# for netty-http-common
xbib-net-url.version = 2.0.0
xbib-net-url.version = 2.0.1
# for netty-http-server
bouncycastle.version = 1.62

View file

@ -172,8 +172,8 @@ public class Request {
@Override
public String toString() {
return "Request[url='" + url +
"',version=" + httpVersion +
return "Request[url=" + url +
",version=" + httpVersion +
",method=" + httpMethod +
",headers=" + headers.entries() +
",content=" + (content != null && content.readableBytes() >= 16 ?

View file

@ -102,7 +102,7 @@ abstract class BaseTransport implements Transport {
flow.close();
}
channels.clear();
requests.clear();
// do not clear requests
}
@Override

View file

@ -1,6 +1,7 @@
package org.xbib.netty.http.client.test.htt2push;
import io.netty.handler.codec.http.HttpMethod;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.xbib.netty.http.client.Client;
@ -11,6 +12,7 @@ import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
@Disabled // /http2-push.io "connection refused"
@ExtendWith(NettyHttpTestExtension.class)
class Http2PushTest {

View file

@ -0,0 +1,6 @@
package org.xbib.netty.http.common;
public enum HttpMethod {
GET, HEAD, POST, PUT, DELETE, CONNECT, OPTIONS, TRACE, PATCH
}

View file

@ -1,4 +1,4 @@
package org.xbib.netty.http.server.util;
package org.xbib.netty.http.common.net;
/**
* The network classes.

View file

@ -1,4 +1,4 @@
package org.xbib.netty.http.server.util;
package org.xbib.netty.http.common.net;
/**
* The TCP/IP network protocol versions.

View file

@ -0,0 +1,39 @@
package org.xbib.netty.http.common.util;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
@SuppressWarnings("serial")
public class LimitedConcurrentHashMap<K, V> extends ConcurrentHashMap<K, V> {
private final Semaphore semaphore;
public LimitedConcurrentHashMap(int limit) {
super(16, 0.75f);
this.semaphore = new Semaphore(limit);
}
@Override
public V put(K key, V value) {
try {
if (semaphore.tryAcquire(1L, TimeUnit.SECONDS)) {
return super.put(key, value);
}
} catch (InterruptedException e) {
throw new IllegalArgumentException("size limit exceeded");
}
throw new IllegalArgumentException("size limit exceeded");
}
@Override
public V remove(Object key) {
V v;
try {
v = super.remove(key);
} finally {
semaphore.release();
}
return v;
}
}

View file

@ -3,11 +3,11 @@ package org.xbib.netty.http.common.util;
import java.util.LinkedHashMap;
@SuppressWarnings("serial")
public class LimitedMap<K, V> extends LinkedHashMap<K, V> {
public class LimitedLinkedHashMap<K, V> extends LinkedHashMap<K, V> {
private final int limit;
public LimitedMap(int limit) {
public LimitedLinkedHashMap(int limit) {
super(16, 0.75f, true);
this.limit = limit;
}

View file

@ -24,6 +24,7 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
/**
@ -99,10 +100,25 @@ public class Domain {
return aliases;
}
/**
* Handle server requests.
* @param serverRequest the server request
* @param serverResponse the server response
* @throws IOException if handling server request fails
*/
public void handle(ServerRequest serverRequest, ServerResponse serverResponse) throws IOException {
if (httpEndpointResolvers != null && !httpEndpointResolvers.isEmpty()) {
if (httpEndpointResolvers != null) {
boolean found = false;
for (HttpEndpointResolver httpEndpointResolver : httpEndpointResolvers) {
httpEndpointResolver.handle(serverRequest, serverResponse);
List<HttpEndpoint> matchingEndpoints = httpEndpointResolver.matchingEndpointsFor(serverRequest);
if (matchingEndpoints != null && !matchingEndpoints.isEmpty()) {
httpEndpointResolver.handle(matchingEndpoints, serverRequest, serverResponse);
found = true;
break;
}
}
if (!found) {
ServerResponse.write(serverResponse, HttpResponseStatus.NOT_IMPLEMENTED);
}
} else {
ServerResponse.write(serverResponse, HttpResponseStatus.NOT_IMPLEMENTED);
@ -143,6 +159,8 @@ public class Domain {
private String keyPassword;
Builder(HttpAddress httpAddress, String serverName) {
Objects.requireNonNull(httpAddress);
Objects.requireNonNull(serverName);
this.httpAddress = httpAddress;
this.serverName = serverName;
this.aliases = new LinkedHashSet<>();
@ -154,31 +172,37 @@ public class Domain {
}
public Builder setTrustManagerFactory(TrustManagerFactory trustManagerFactory) {
Objects.requireNonNull(trustManagerFactory);
this.trustManagerFactory = trustManagerFactory;
return this;
}
public Builder setTrustManagerKeyStore(KeyStore trustManagerKeyStore) {
Objects.requireNonNull(trustManagerKeyStore);
this.trustManagerKeyStore = trustManagerKeyStore;
return this;
}
public Builder setSslContextProvider(Provider sslContextProvider) {
Objects.requireNonNull(sslContextProvider);
this.sslContextProvider = sslContextProvider;
return this;
}
public Builder setSslProvider(SslProvider sslProvider) {
Objects.requireNonNull(sslProvider);
this.sslProvider = sslProvider;
return this;
}
public Builder setCiphers(Iterable<String> ciphers) {
Objects.requireNonNull(ciphers);
this.ciphers = ciphers;
return this;
}
public Builder setCipherSuiteFilter(CipherSuiteFilter cipherSuiteFilter) {
Objects.requireNonNull(cipherSuiteFilter);
this.cipherSuiteFilter = cipherSuiteFilter;
return this;
}
@ -196,21 +220,26 @@ public class Domain {
}
public Builder setKeyCertChainInputStream(InputStream keyCertChainInputStream) {
Objects.requireNonNull(keyCertChainInputStream);
this.keyCertChainInputStream = keyCertChainInputStream;
return this;
}
public Builder setKeyInputStream(InputStream keyInputStream) {
Objects.requireNonNull(keyInputStream);
this.keyInputStream = keyInputStream;
return this;
}
public Builder setKeyPassword(String keyPassword) {
// null in keyPassword allowed, it means no password
this.keyPassword = keyPassword;
return this;
}
public Builder setKeyCert(InputStream keyCertChainInputStream, InputStream keyInputStream) {
Objects.requireNonNull(keyCertChainInputStream);
Objects.requireNonNull(keyInputStream);
setKeyCertChainInputStream(keyCertChainInputStream);
setKeyInputStream(keyInputStream);
return this;
@ -218,6 +247,9 @@ public class Domain {
public Builder setKeyCert(InputStream keyCertChainInputStream, InputStream keyInputStream,
String keyPassword) {
Objects.requireNonNull(keyCertChainInputStream);
Objects.requireNonNull(keyInputStream);
Objects.requireNonNull(keyPassword);
setKeyCertChainInputStream(keyCertChainInputStream);
setKeyInputStream(keyInputStream);
setKeyPassword(keyPassword);
@ -239,31 +271,56 @@ public class Domain {
* @return this builder
*/
public Builder addAlias(String alias) {
Objects.requireNonNull(alias);
aliases.add(alias);
return this;
}
public Builder addEndpointResolver(HttpEndpointResolver httpEndpointResolver) {
Objects.requireNonNull(httpEndpointResolver);
this.httpEndpointResolvers.add(httpEndpointResolver);
return this;
}
public Builder singleEndpoint(String path, Service service) {
Objects.requireNonNull(path);
Objects.requireNonNull(service);
addEndpointResolver(HttpEndpointResolver.builder()
.addEndpoint(HttpEndpoint.builder().setPath(path).addFilter(service).build()).build());
.addEndpoint(HttpEndpoint.builder()
.setPath(path)
.build())
.setDispatcher((endpoint, req, resp) -> service.handle(req, resp))
.build());
return this;
}
public Builder singleEndpoint(String prefix, String path, Service service) {
Objects.requireNonNull(prefix);
Objects.requireNonNull(path);
Objects.requireNonNull(service);
addEndpointResolver(HttpEndpointResolver.builder()
.addEndpoint(HttpEndpoint.builder().setPrefix(prefix).setPath(path).addFilter(service).build()).build());
.addEndpoint(HttpEndpoint.builder()
.setPrefix(prefix)
.setPath(path)
.build())
.setDispatcher((endpoint, req, resp) -> service.handle(req, resp))
.build());
return this;
}
public Builder singleEndpoint(String prefix, String path, Service service, String... methods) {
public Builder singleEndpoint(String prefix, String path, Service service,
String... methods) {
Objects.requireNonNull(prefix);
Objects.requireNonNull(path);
Objects.requireNonNull(service);
addEndpointResolver(HttpEndpointResolver.builder()
.addEndpoint(HttpEndpoint.builder().setPrefix(prefix).setPath(path).addFilter(service)
.setMethods(Arrays.asList(methods)).build()).build());
.addEndpoint(HttpEndpoint.builder()
.setPrefix(prefix)
.setPath(path)
.setMethods(Arrays.asList(methods))
.build())
.setDispatcher((endpoint, req, resp) -> service.handle(req, resp))
.build());
return this;
}

View file

@ -55,5 +55,4 @@ public interface ServerRequest {
InetSocketAddress getLocalAddress();
InetSocketAddress getRemoteAddress();
}

View file

@ -4,33 +4,40 @@ import org.xbib.net.Pair;
import org.xbib.net.QueryParameters;
import org.xbib.net.path.PathMatcher;
import org.xbib.net.path.PathNormalizer;
import org.xbib.netty.http.common.HttpMethod;
import org.xbib.netty.http.server.ServerRequest;
import org.xbib.netty.http.server.ServerResponse;
import org.xbib.netty.http.server.endpoint.service.Service;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.EnumSet;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
public class HttpEndpoint implements Endpoint<HttpEndpointDescriptor> {
private static final PathMatcher pathMatcher = new PathMatcher();
public static final EnumSet<HttpMethod> DEFAULT_METHODS =
EnumSet.of(HttpMethod.GET, HttpMethod.HEAD);
private static final List<String> DEFAULT_METHODS = Arrays.asList("GET", "HEAD");
private static final PathMatcher pathMatcher = new PathMatcher();
private final String prefix;
private final String path;
private final List<String> methods;
private final EnumSet<HttpMethod> methods;
private final List<String> contentTypes;
private final List<Service> filters;
private HttpEndpoint(String prefix, String path, List<String> methods, List<String> contentTypes, List<Service> filters) {
private HttpEndpoint(String prefix, String path,
EnumSet<HttpMethod> methods,
List<String> contentTypes,
List<Service> filters) {
this.prefix = PathNormalizer.normalize(prefix);
this.path = PathNormalizer.normalize(path);
this.methods = methods;
@ -80,6 +87,7 @@ public class HttpEndpoint implements Endpoint<HttpEndpointDescriptor> {
}
}
@Override
public void handle(ServerRequest serverRequest, ServerResponse serverResponse) throws IOException {
serverRequest.setContext(pathMatcher.tokenizePath(getPrefix()));
for (Service service : filters) {
@ -92,7 +100,7 @@ public class HttpEndpoint implements Endpoint<HttpEndpointDescriptor> {
@Override
public String toString() {
return "Endpoint[prefix=" + prefix + ",path=" + path + ",methods=" + methods + ",contentTypes=" + contentTypes + " --> " + filters +"]";
return "Endpoint[prefix=" + prefix + ",path=" + path + ",methods=" + methods + ",contentTypes=" + contentTypes + ",filters=" + filters +"]";
}
static class EndpointPathComparator implements Comparator<HttpEndpoint> {
@ -115,7 +123,7 @@ public class HttpEndpoint implements Endpoint<HttpEndpointDescriptor> {
private String path;
private List<String> methods;
private EnumSet<HttpMethod> methods;
private List<String> contentTypes;
@ -124,55 +132,62 @@ public class HttpEndpoint implements Endpoint<HttpEndpointDescriptor> {
Builder() {
this.prefix = "/";
this.path = "/**";
this.methods = new ArrayList<>();
this.methods = DEFAULT_METHODS;
this.contentTypes = new ArrayList<>();
this.filters = new ArrayList<>();
}
public Builder setPrefix(String prefix) {
Objects.requireNonNull(prefix);
this.prefix = prefix;
return this;
}
public Builder setPath(String path) {
Objects.requireNonNull(path);
this.path = path;
return this;
}
public Builder setMethods(List<String> methods) {
public Builder setMethods(EnumSet<HttpMethod> methods) {
Objects.requireNonNull(methods);
this.methods = methods;
return this;
}
public Builder addMethod(String method) {
methods.add(method);
public Builder setMethods(List<String> methods) {
Objects.requireNonNull(methods);
this.methods = methods.stream()
.map(HttpMethod::valueOf)
.collect(Collectors.toCollection(() -> EnumSet.noneOf(HttpMethod.class)));
return this;
}
public Builder setContentTypes(List<String> contentTypes) {
Objects.requireNonNull(contentTypes);
this.contentTypes = contentTypes;
return this;
}
public Builder addContentType(String contentType) {
Objects.requireNonNull(contentType);
this.contentTypes.add(contentType);
return this;
}
public Builder setFilters(List<Service> filters) {
Objects.requireNonNull(filters);
this.filters = filters;
return this;
}
public Builder addFilter(Service filter) {
Objects.requireNonNull(filter);
this.filters.add(filter);
return this;
}
public HttpEndpoint build() {
if (methods.isEmpty()) {
methods = DEFAULT_METHODS;
}
return new HttpEndpoint(prefix, path, methods, contentTypes, filters);
}
}

View file

@ -1,5 +1,6 @@
package org.xbib.netty.http.server.endpoint;
import org.xbib.netty.http.common.HttpMethod;
import org.xbib.netty.http.server.transport.HttpServerRequest;
import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_TYPE;
@ -8,13 +9,13 @@ public class HttpEndpointDescriptor implements EndpointDescriptor, Comparable<Ht
private final String path;
private final String method;
private final HttpMethod method;
private final String contentType;
public HttpEndpointDescriptor(HttpServerRequest serverRequest) {
this.path = extractPath(serverRequest.getRequestURI());
this.method = serverRequest.getMethod().name();
this.method = Enum.valueOf(HttpMethod.class, serverRequest.getMethod().name());
this.contentType = serverRequest.getHeaders().get(CONTENT_TYPE);
}
@ -22,7 +23,7 @@ public class HttpEndpointDescriptor implements EndpointDescriptor, Comparable<Ht
return path;
}
public String getMethod() {
public HttpMethod getMethod() {
return method;
}

View file

@ -1,7 +1,6 @@
package org.xbib.netty.http.server.endpoint;
import io.netty.handler.codec.http.HttpResponseStatus;
import org.xbib.netty.http.common.util.LimitedMap;
import org.xbib.netty.http.common.util.LimitedConcurrentHashMap;
import org.xbib.netty.http.server.ServerRequest;
import org.xbib.netty.http.server.ServerResponse;
import org.xbib.netty.http.server.annotation.Endpoint;
@ -13,6 +12,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
@ -21,79 +21,60 @@ public class HttpEndpointResolver {
private static final Logger logger = Logger.getLogger(HttpEndpointResolver.class.getName());
private final HttpEndpoint defaultEndpoint;
private static final int DEFAULT_LIMIT = 1024;
private final List<HttpEndpoint> endpoints;
private final EndpointDispatcher endpointDispatcher;
private final LimitedMap<HttpEndpointDescriptor, List<HttpEndpoint>> endpointDescriptors;
private final Map<HttpEndpointDescriptor, List<HttpEndpoint>> endpointDescriptors;
private HttpEndpointResolver(HttpEndpoint defaultEndpoint,
List<HttpEndpoint> endpoints,
private HttpEndpointResolver(List<HttpEndpoint> endpoints,
EndpointDispatcher endpointDispatcher,
int limit) {
this.defaultEndpoint = defaultEndpoint == null ? createDefaultEndpoint() : defaultEndpoint;
Objects.requireNonNull(endpointDispatcher);
this.endpoints = endpoints;
this.endpointDispatcher = endpointDispatcher;
this.endpointDescriptors = new LimitedMap<>(limit);
this.endpointDescriptors = new LimitedConcurrentHashMap<>(limit);
}
public void handle(ServerRequest serverRequest, ServerResponse serverResponse) throws IOException {
/**
* Find matching endpoints for a server request.
* @param serverRequest the server request
* @return a
*/
public List<HttpEndpoint> matchingEndpointsFor(ServerRequest serverRequest) {
HttpEndpointDescriptor httpEndpointDescriptor = serverRequest.getEndpointDescriptor();
endpointDescriptors.putIfAbsent(httpEndpointDescriptor, endpoints.stream()
.filter(endpoint -> endpoint.matches(httpEndpointDescriptor))
.sorted(new HttpEndpoint.EndpointPathComparator(httpEndpointDescriptor.getPath()))
.collect(Collectors.toList()));
List<HttpEndpoint> matchingEndpoints = endpointDescriptors.get(httpEndpointDescriptor);
return endpointDescriptors.get(httpEndpointDescriptor);
}
public void handle(List<HttpEndpoint> matchingEndpoints,
ServerRequest serverRequest, ServerResponse serverResponse) throws IOException {
Objects.requireNonNull(matchingEndpoints);
if (logger.isLoggable(Level.FINE)) {
logger.log(Level.FINE, () -> "endpoint = " + httpEndpointDescriptor +
" matching endpoints = " + matchingEndpoints);
logger.log(Level.FINE, () ->
"endpoint = " + serverRequest.getEndpointDescriptor() +
" matching endpoints = " + matchingEndpoints.size() + " --> " + matchingEndpoints);
}
if (matchingEndpoints.isEmpty()) {
if (defaultEndpoint != null) {
defaultEndpoint.resolveUriTemplate(serverRequest);
defaultEndpoint.handle(serverRequest, serverResponse);
if (endpointDispatcher != null) {
endpointDispatcher.dispatch(defaultEndpoint, serverRequest, serverResponse);
}
} else {
ServerResponse.write(serverResponse, HttpResponseStatus.NOT_IMPLEMENTED);
}
} else {
for (HttpEndpoint endpoint : matchingEndpoints) {
endpoint.resolveUriTemplate(serverRequest);
endpoint.handle(serverRequest, serverResponse);
if (serverResponse.getStatus() != null) {
break;
}
}
if (endpointDispatcher != null) {
for (HttpEndpoint endpoint : matchingEndpoints) {
endpointDispatcher.dispatch(endpoint, serverRequest, serverResponse);
if (serverResponse.getStatus() != null) {
logger.log(Level.FINEST, () -> "endpoint " + endpoint + " status = " + serverResponse.getStatus());
break;
}
}
}
}
}
public Map<HttpEndpointDescriptor, List<HttpEndpoint>> getEndpointDescriptors() {
return endpointDescriptors;
}
private HttpEndpoint createDefaultEndpoint() {
return HttpEndpoint.builder()
.setPath("/**")
.addMethod("GET")
.addMethod("HEAD")
.addFilter((req, resp) -> {
ServerResponse.write(resp, HttpResponseStatus.NOT_FOUND,
"application/octet-stream","no endpoint configured");
}).build();
}
public static Builder builder() {
return new Builder();
}
@ -104,32 +85,26 @@ public class HttpEndpointResolver {
private String prefix;
private HttpEndpoint defaultEndpoint;
private List<HttpEndpoint> endpoints;
private EndpointDispatcher endpointDispatcher;
Builder() {
this.limit = 1024;
this.limit = DEFAULT_LIMIT;
this.endpoints = new ArrayList<>();
}
public Builder setLimit(int limit) {
this.limit = limit;
this.limit = limit > 0 ? limit < 1024 * DEFAULT_LIMIT ? limit : DEFAULT_LIMIT : DEFAULT_LIMIT;
return this;
}
public Builder setPrefix(String prefix) {
Objects.requireNonNull(prefix);
this.prefix = prefix;
return this;
}
public Builder setDefaultEndpoint(HttpEndpoint endpoint) {
this.defaultEndpoint = endpoint;
return this;
}
/**
* Add endpoint.
*
@ -137,12 +112,19 @@ public class HttpEndpointResolver {
* @return this builder
*/
public Builder addEndpoint(HttpEndpoint endpoint) {
if (endpoint.getPrefix().equals("/") && prefix != null && !prefix.isEmpty()) {
HttpEndpoint thisEndpoint = HttpEndpoint.builder(endpoint).setPrefix(prefix).build();
logger.log(Level.FINE, "adding endpoint = " + thisEndpoint);
endpoints.add(thisEndpoint);
Objects.requireNonNull(endpoint);
if (prefix != null && !prefix.isEmpty()) {
HttpEndpoint prefixedEndpoint = HttpEndpoint.builder(endpoint)
.setPrefix(prefix + endpoint.getPrefix())
.build();
if (logger.isLoggable(Level.FINE)) {
logger.log(Level.FINE, () -> "prefix " + prefix + ": adding endpoint = " + prefixedEndpoint);
}
endpoints.add(prefixedEndpoint);
} else {
logger.log(Level.FINE, "adding endpoint = " + endpoint);
if (logger.isLoggable(Level.FINE)) {
logger.log(Level.FINE, () -> "adding endpoint = " + endpoint);
}
endpoints.add(endpoint);
}
return this;
@ -155,6 +137,7 @@ public class HttpEndpointResolver {
* @return this builder
*/
public Builder addEndpoint(Object classWithAnnotatedMethods) {
Objects.requireNonNull(classWithAnnotatedMethods);
for (Class<?> clazz = classWithAnnotatedMethods.getClass(); clazz != null; clazz = clazz.getSuperclass()) {
for (Method method : clazz.getDeclaredMethods()) {
Endpoint endpoint = method.getAnnotation(Endpoint.class);
@ -174,12 +157,13 @@ public class HttpEndpointResolver {
}
public Builder setDispatcher(EndpointDispatcher endpointDispatcher) {
Objects.requireNonNull(endpointDispatcher);
this.endpointDispatcher = endpointDispatcher;
return this;
}
public HttpEndpointResolver build() {
return new HttpEndpointResolver(defaultEndpoint, endpoints, endpointDispatcher, limit);
return new HttpEndpointResolver(endpoints, endpointDispatcher, limit);
}
}
}

View file

@ -7,16 +7,27 @@ import java.io.IOException;
import java.net.URL;
import java.net.URLConnection;
import java.time.Instant;
import java.util.logging.Level;
import java.util.logging.Logger;
public class ClassLoaderService extends ResourceService {
private static final Logger logger = Logger.getLogger(ClassLoaderService.class.getName());
private final Class<?> clazz;
private final String prefix;
private final String indexFileName;
public ClassLoaderService(Class<?> clazz, String prefix) {
this(clazz, prefix, "index.html");
}
public ClassLoaderService(Class<?> clazz, String prefix, String indexFileName) {
this.clazz = clazz;
this.prefix = prefix;
this.indexFileName = indexFileName;
}
@Override
@ -50,11 +61,24 @@ public class ClassLoaderService extends ResourceService {
private final long length;
ClassLoaderResource(ServerRequest serverRequest) throws IOException {
this.resourcePath = serverRequest.getEffectiveRequestPath().substring(1);
this.url = clazz.getResource(prefix + "/" + resourcePath);
String effectivePath = serverRequest.getEffectiveRequestPath();
this.resourcePath = effectivePath.startsWith("/") ? effectivePath.substring(1) : effectivePath;
String path = prefix.endsWith("/") ? prefix : prefix + "/";
path = resourcePath.startsWith("/") ? path + resourcePath.substring(1) : path + resourcePath;
this.url = clazz.getResource(path);
if (url != null) {
URLConnection urlConnection = url.openConnection();
this.lastModified = Instant.ofEpochMilli(urlConnection.getLastModified());
this.length = urlConnection.getContentLength();
if (logger.isLoggable(Level.FINER)) {
logger.log(Level.FINER, "success: path=[" + path +
"] -> url=" + url + " lastModified=" + lastModified + "length=" + length);
}
} else {
this.lastModified = Instant.now();
this.length = 0;
logger.log(Level.FINER, "fail: resource not found, url=" + url);
}
}
@Override
@ -76,5 +100,15 @@ public class ClassLoaderService extends ResourceService {
public long getLength() {
return length;
}
@Override
public boolean isDirectory() {
return resourcePath.endsWith("/");
}
@Override
public String indexFileName() {
return indexFileName;
}
}
}

View file

@ -13,14 +13,15 @@ public class FileService extends ResourceService {
private final Path prefix;
private final String indexFileName;
public FileService(Path prefix) {
this(prefix, "index.html");
}
public FileService(Path prefix, String indexFileName) {
this.prefix = prefix;
if (!Files.exists(prefix)) {
throw new IllegalArgumentException("prefix: " + prefix + " (does not exist)");
}
if (!Files.isDirectory(prefix)) {
throw new IllegalArgumentException("prefix: " + prefix + " (not a directory)");
}
this.indexFileName = indexFileName;
}
@Override
@ -49,16 +50,26 @@ public class FileService extends ResourceService {
private final URL url;
private final boolean isDirectory;
private final Instant lastModified;
private final long length;
ChunkedFileResource(ServerRequest serverRequest) throws IOException {
this.resourcePath = serverRequest.getEffectiveRequestPath().substring(1);
String effectivePath = serverRequest.getEffectiveRequestPath();
this.resourcePath = effectivePath.startsWith("/") ? effectivePath.substring(1) : effectivePath;
Path path = prefix.resolve(resourcePath);
this.url = path.toUri().toURL();
boolean isExists = Files.exists(path);
this.isDirectory = Files.isDirectory(path);
if (isExists) {
this.lastModified = Files.getLastModifiedTime(path).toInstant();
this.length = Files.size(path);
} else {
this.lastModified = Instant.now();
this.length = 0;
}
}
@Override
@ -71,6 +82,16 @@ public class FileService extends ResourceService {
return url;
}
@Override
public boolean isDirectory() {
return isDirectory;
}
@Override
public String indexFileName() {
return indexFileName;
}
@Override
public Instant getLastModified() {
return lastModified;

View file

@ -12,4 +12,8 @@ public interface Resource {
Instant getLastModified();
long getLength();
boolean isDirectory();
String indexFileName();
}

View file

@ -24,6 +24,7 @@ import java.nio.channels.ReadableByteChannel;
import java.nio.channels.SeekableByteChannel;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.Instant;
import java.util.ArrayList;
@ -50,6 +51,23 @@ public abstract class ResourceService implements Service {
protected abstract boolean isRangeResponseEnabled();
private void handleResource(ServerRequest serverRequest, ServerResponse serverResponse, Resource resource) {
if (resource.isDirectory()) {
if (!resource.getResourcePath().endsWith("/")) {
// external redirect to
serverResponse.withHeader(HttpHeaderNames.LOCATION, resource.getResourcePath() + "/");
ServerResponse.write(serverResponse, HttpResponseStatus.MOVED_PERMANENTLY);
return;
} else if (resource.indexFileName() != null) {
// external redirect to default index file in this directory
serverResponse.withHeader(HttpHeaderNames.LOCATION, resource.indexFileName());
ServerResponse.write(serverResponse, HttpResponseStatus.MOVED_PERMANENTLY);
return;
} else {
// send forbidden, we do not allow directory access
ServerResponse.write(serverResponse, HttpResponseStatus.FORBIDDEN);
return;
}
}
HttpHeaders headers = serverRequest.getHeaders();
String contentType = MimeTypeUtils.guessFromPath(resource.getResourcePath(), false);
long maxAgeSeconds = 24 * 3600;
@ -187,7 +205,9 @@ public abstract class ResourceService implements Service {
private void send(URL url, String contentType,
ServerRequest serverRequest, ServerResponse serverResponse) {
if (serverRequest.getMethod() == HttpMethod.HEAD) {
if (url == null) {
ServerResponse.write(serverResponse, HttpResponseStatus.NOT_FOUND);
} else if (serverRequest.getMethod() == HttpMethod.HEAD) {
ServerResponse.write(serverResponse, HttpResponseStatus.OK, contentType);
} else {
if ("file".equals(url.getProtocol())) {
@ -211,15 +231,19 @@ public abstract class ResourceService implements Service {
private void send(URL url, HttpResponseStatus httpResponseStatus, String contentType,
ServerRequest serverRequest, ServerResponse serverResponse, long offset, long size) {
if (serverRequest.getMethod() == HttpMethod.HEAD) {
if (url == null) {
ServerResponse.write(serverResponse, HttpResponseStatus.NOT_FOUND);
} else if (serverRequest.getMethod() == HttpMethod.HEAD) {
ServerResponse.write(serverResponse, HttpResponseStatus.OK, contentType);
} else {
if ("file".equals(url.getProtocol())) {
Path path = null;
try {
send((FileChannel) Files.newByteChannel(Paths.get(url.toURI())), httpResponseStatus,
path = Paths.get(url.toURI());
send((FileChannel) Files.newByteChannel(path), httpResponseStatus,
contentType, serverResponse, offset, size);
} catch (URISyntaxException | IOException e) {
logger.log(Level.SEVERE, e.getMessage(), e);
logger.log(Level.SEVERE, e.getMessage() + " path=" + path, e);
ServerResponse.write(serverResponse, HttpResponseStatus.NOT_FOUND);
}
} else {
@ -240,27 +264,47 @@ public abstract class ResourceService implements Service {
private void send(FileChannel fileChannel, HttpResponseStatus httpResponseStatus, String contentType,
ServerResponse serverResponse, long offset, long size) throws IOException {
MappedByteBuffer mappedByteBuffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, offset, size);
if (fileChannel == null) {
ServerResponse.write(serverResponse, HttpResponseStatus.NOT_FOUND);
} else {
MappedByteBuffer mappedByteBuffer = null;
try {
mappedByteBuffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, offset, size);
} catch (IOException e) {
// resource is not a file that can be mapped
ServerResponse.write(serverResponse, HttpResponseStatus.NOT_FOUND);
}
if (mappedByteBuffer != null) {
serverResponse.withStatus(httpResponseStatus)
.withContentType(contentType)
.write(Unpooled.wrappedBuffer(mappedByteBuffer));
}
}
}
private void send(InputStream inputStream, HttpResponseStatus httpResponseStatus, String contentType,
ServerResponse serverResponse) throws IOException {
if (inputStream == null) {
ServerResponse.write(serverResponse, HttpResponseStatus.NOT_FOUND);
} else {
try (ReadableByteChannel channel = Channels.newChannel(inputStream)) {
serverResponse.withStatus(httpResponseStatus)
.withContentType(contentType)
.write(new ChunkedNioStream(channel));
}
}
}
private void send(InputStream inputStream, HttpResponseStatus httpResponseStatus, String contentType,
ServerResponse serverResponse, long offset, long size) throws IOException {
if (inputStream == null) {
ServerResponse.write(serverResponse, HttpResponseStatus.NOT_FOUND);
} else {
serverResponse.withStatus(httpResponseStatus)
.withContentType(contentType)
.write(Unpooled.wrappedBuffer(readBuffer(inputStream, offset, size)));
}
}
private static ByteBuffer readBuffer(URL url, long offset, long size) throws IOException, URISyntaxException {
if ("file".equals(url.getProtocol())) {

View file

@ -61,7 +61,6 @@ public class HttpServerRequest implements ServerRequest {
HttpServerRequest(Server server, FullHttpRequest fullHttpRequest,
ChannelHandlerContext ctx) {
// server not required yet
this.httpRequest = fullHttpRequest.retainedDuplicate();
this.ctx = ctx;
this.httpEndpointDescriptor = new HttpEndpointDescriptor(this);
@ -70,7 +69,9 @@ public class HttpServerRequest implements ServerRequest {
void handleParameters() throws IOException {
try {
HttpParameters httpParameters = new HttpParameters();
this.url = URL.builder().path(httpRequest.uri()).build();
this.url = URL.builder()
.path(httpRequest.uri()) // creates path, query params, fragment
.build();
QueryParameters queryParameters = url.getQueryParams();
ByteBuf byteBuf = httpRequest.content();
if (APPLICATION_FORM_URL_ENCODED.equals(HttpUtil.getMimeType(httpRequest)) && byteBuf != null) {

View file

@ -38,8 +38,6 @@ public class HttpServerResponse implements ServerResponse {
private static final Logger logger = Logger.getLogger(HttpServerResponse.class.getName());
private static final ByteBuf EMPTY = Unpooled.buffer(0);
private final Server server;
private final ServerRequest serverRequest;
@ -113,7 +111,7 @@ public class HttpServerResponse implements ServerResponse {
@Override
public void flush() {
write((ByteBuf) null);
write(Unpooled.buffer(0));
}
@Override
@ -130,6 +128,7 @@ public class HttpServerResponse implements ServerResponse {
@Override
public void write(ByteBuf byteBuf) {
Objects.requireNonNull(byteBuf);
if (httpResponseStatus == null) {
httpResponseStatus = HttpResponseStatus.OK;
}
@ -139,11 +138,9 @@ public class HttpServerResponse implements ServerResponse {
}
if (httpResponseStatus.code() >= 200 && httpResponseStatus.code() != 204) {
if (!headers.contains(HttpHeaderNames.CONTENT_LENGTH) && !headers.contains(HttpHeaderNames.TRANSFER_ENCODING)) {
if (byteBuf != null) {
headers.add(HttpHeaderNames.CONTENT_LENGTH, Long.toString(byteBuf.readableBytes()));
}
}
}
if (serverRequest != null && "close".equalsIgnoreCase(serverRequest.getHeaders().get(HttpHeaderNames.CONNECTION)) &&
!headers.contains(HttpHeaderNames.CONNECTION)) {
headers.add(HttpHeaderNames.CONNECTION, "close");
@ -154,11 +151,7 @@ public class HttpServerResponse implements ServerResponse {
headers.add(HttpHeaderNames.SERVER, ServerName.getServerName());
if (ctx.channel().isWritable()) {
FullHttpResponse fullHttpResponse;
if (byteBuf != null) {
fullHttpResponse = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, httpResponseStatus, byteBuf, headers, trailingHeaders);
} else {
fullHttpResponse = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, httpResponseStatus, EMPTY, headers, trailingHeaders);
}
if (serverRequest != null && serverRequest.getSequenceId() != null) {
HttpPipelinedResponse httpPipelinedResponse = new HttpPipelinedResponse(fullHttpResponse,
ctx.channel().newPromise(), serverRequest.getSequenceId());

View file

@ -1,97 +0,0 @@
package org.xbib.netty.http.server.test;
import io.netty.handler.codec.http.HttpVersion;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.xbib.netty.http.client.Client;
import org.xbib.netty.http.client.Request;
import org.xbib.netty.http.common.HttpAddress;
import org.xbib.netty.http.server.Server;
import org.xbib.netty.http.server.Domain;
import org.xbib.netty.http.server.endpoint.service.FileService;
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.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
@ExtendWith(NettyHttpTestExtension.class)
class FileServiceTest {
private static final Logger logger = Logger.getLogger(FileServiceTest.class.getName());
@Test
void testFileServiceHttp1() throws Exception {
Path vartmp = Paths.get("/var/tmp/");
HttpAddress httpAddress = HttpAddress.http1("localhost", 8008);
Domain domain = Domain.builder(httpAddress)
.singleEndpoint("/static", "/**", new FileService(vartmp))
.build();
Server server = Server.builder(domain)
.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(resp -> {
assertEquals("Hello Jörg", resp.getBodyAsString(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"));
logger.log(Level.INFO, "server and client shut down");
}
assertTrue(success.get());
}
@Test
void testFileServiceHttp2() throws Exception {
Path vartmp = Paths.get("/var/tmp/");
HttpAddress httpAddress = HttpAddress.http2("localhost", 8008);
Domain domain = Domain.builder(httpAddress)
.singleEndpoint("/static", "/**", new FileService(vartmp))
.build();
Server server = Server.builder(domain)
.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.valueOf("HTTP/2.0"))
.url(server.getServerConfig().getAddress().base().resolve("/static/test.txt"))
.build()
.setResponseListener(resp -> {
assertEquals("Hello Jörg", resp.getBodyAsString(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"));
logger.log(Level.INFO, "server and client shut down");
}
assertTrue(success.get());
}
}

View file

@ -0,0 +1,89 @@
package org.xbib.netty.http.server.test;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.xbib.netty.http.client.Client;
import org.xbib.netty.http.client.Request;
import org.xbib.netty.http.client.listener.ResponseListener;
import org.xbib.netty.http.common.HttpAddress;
import org.xbib.netty.http.common.HttpParameters;
import org.xbib.netty.http.common.HttpResponse;
import org.xbib.netty.http.server.Domain;
import org.xbib.netty.http.server.Server;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level;
import java.util.logging.Logger;
@ExtendWith(NettyHttpTestExtension.class)
class FlushTest {
private static final Logger logger = Logger.getLogger(FlushTest.class.getName());
/**
* This test checks the flush() operation of the server response.
* Flush uses Unpooled.buffer(0), and requires a fresh instance each call.
* If a static Unpooled.buffer(0) is used, a retry will fail -
* and the second "302 Found" will never be sent because of a server hang.
* @throws Exception exception
*/
@Test
void testFlush() throws Exception {
HttpAddress httpAddress = HttpAddress.http1("localhost", 8008);
Domain domain = Domain.builder(httpAddress)
.singleEndpoint("/flush", "/**", (req, resp) -> {
HttpParameters parameters = req.getParameters();
logger.log(Level.INFO, "got request " + parameters.toString() + ", sending 302 Found");
resp.withStatus(HttpResponseStatus.FOUND).flush();
})
.build();
Server server = Server.builder(domain)
.enableDebug()
.build();
Client client = Client.builder()
.build();
final AtomicBoolean success1 = new AtomicBoolean(false);
final AtomicBoolean success2 = new AtomicBoolean(false);
try {
server.accept();
// first request to trigger flush()
ResponseListener<HttpResponse> responseListener1 = (resp) -> {
logger.log(Level.INFO, "got response = " + resp.getStatus());
if (resp.getStatus().getCode() == HttpResponseStatus.FOUND.code()) {
success1.set(true);
}
};
Request getRequest = Request.get().setVersion(HttpVersion.HTTP_1_1)
.url(server.getServerConfig().getAddress().base().resolve("/flush"))
.addParameter("a", "b")
.build()
.setResponseListener(responseListener1);
client.execute(getRequest).get();
// second request to trigger flush()
ResponseListener<HttpResponse> responseListener2 = (resp) -> {
logger.log(Level.INFO, "got response = " + resp.getStatus());
if (resp.getStatus().getCode() == HttpResponseStatus.FOUND.code()) {
success2.set(true);
}
};
getRequest = Request.get().setVersion(HttpVersion.HTTP_1_1)
.url(server.getServerConfig().getAddress().base().resolve("/flush"))
.addParameter("a", "b")
.build()
.setResponseListener(responseListener2);
client.execute(getRequest).get();
} finally {
server.shutdownGracefully();
client.shutdownGracefully();
logger.log(Level.INFO, "server and client shut down");
}
assertTrue(success1.get());
assertTrue(success2.get());
}
}

View file

@ -105,7 +105,7 @@ class SecureHttp2Test {
break;
}
}
transport.get();
transport.get(60, TimeUnit.SECONDS);
} finally {
client.shutdownGracefully();
server.shutdownGracefully();

View file

@ -1,4 +1,4 @@
package org.xbib.netty.http.server.test;
package org.xbib.netty.http.server.test.endpoint;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
@ -10,6 +10,7 @@ import org.xbib.netty.http.common.HttpAddress;
import org.xbib.netty.http.server.Server;
import org.xbib.netty.http.server.Domain;
import org.xbib.netty.http.server.endpoint.service.ClassLoaderService;
import org.xbib.netty.http.server.test.NettyHttpTestExtension;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.atomic.AtomicInteger;
@ -24,16 +25,14 @@ class ClassloaderServiceTest {
private static final Logger logger = Logger.getLogger(ClassloaderServiceTest.class.getName());
@Test
void testSimpleClassloader() throws Exception {
void testClassloaderFileResource() throws Exception {
HttpAddress httpAddress = HttpAddress.http1("localhost", 8008);
Domain domain = Domain.builder(httpAddress)
.singleEndpoint("/classloader", "/**",
new ClassLoaderService(ClassloaderServiceTest.class, "/cl"))
.build();
Server server = Server.builder(domain)
.enableDebug()
.build();
server.logDiagnostics(Level.INFO);
Client client = Client.builder()
.build();
int max = 1;
@ -41,12 +40,52 @@ class ClassloaderServiceTest {
try {
server.accept();
Request request = Request.get().setVersion(HttpVersion.HTTP_1_1)
.url(server.getServerConfig().getAddress().base().resolve("/classloader/test.txt"))
.url(server.getServerConfig().getAddress().base()
.resolve("/classloader/test.txt"))
.build()
.setResponseListener(resp -> {
if (resp.getStatus().getCode() == HttpResponseStatus.OK.code()) {
assertEquals("Hello Jörg", resp.getBodyAsString(StandardCharsets.UTF_8));
count.incrementAndGet();
} else {
logger.log(Level.WARNING, resp.getStatus().getMessage());
}
});
for (int i = 0; i < max; i++) {
client.execute(request).get();
}
} finally {
server.shutdownGracefully();
client.shutdownGracefully();
logger.log(Level.INFO, "server and client shut down");
}
assertEquals(max, count.get());
}
@Test
void testClassloaderDirectoryResource() throws Exception {
HttpAddress httpAddress = HttpAddress.http1("localhost", 8008);
Domain domain = Domain.builder(httpAddress)
.singleEndpoint("/classloader", "/**",
new ClassLoaderService(ClassloaderServiceTest.class, "/cl"))
.build();
Server server = Server.builder(domain)
.build();
Client client = Client.builder()
.build();
int max = 1;
final AtomicInteger count = new AtomicInteger(0);
try {
server.accept();
Request request = Request.get().setVersion(HttpVersion.HTTP_1_1)
.url(server.getServerConfig().getAddress().base()
.resolve("/classloader"))
.build()
.setResponseListener(resp -> {
if (resp.getStatus().getCode() == HttpResponseStatus.NOT_FOUND.code()) {
count.incrementAndGet();
} else {
logger.log(Level.WARNING, resp.getStatus().getMessage());
}
});
for (int i = 0; i < max; i++) {

View file

@ -1,4 +1,4 @@
package org.xbib.netty.http.server.test;
package org.xbib.netty.http.server.test.endpoint;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
@ -14,6 +14,7 @@ import org.xbib.netty.http.server.endpoint.HttpEndpointResolver;
import org.xbib.netty.http.server.Domain;
import org.xbib.netty.http.server.endpoint.service.FileService;
import org.xbib.netty.http.server.endpoint.service.Service;
import org.xbib.netty.http.server.test.NettyHttpTestExtension;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
@ -41,7 +42,8 @@ class EndpointTest {
HttpEndpointResolver httpEndpointResolver = HttpEndpointResolver.builder()
.addEndpoint(HttpEndpoint.builder().setPath("/**").build())
.setDispatcher((endpoint, req, resp) -> {
logger.log(Level.FINE, "dispatching endpoint = " + endpoint + " req = " + req);
logger.log(Level.FINE, "dispatching endpoint = " + endpoint +
" req = " + req + " req context path = " + req.getContextPath());
service.handle(req, resp);
})
.build();
@ -81,7 +83,8 @@ class EndpointTest {
HttpEndpointResolver httpEndpointResolver = HttpEndpointResolver.builder()
.addEndpoint(HttpEndpoint.builder().setPrefix("/").setPath("/**").build())
.setDispatcher((endpoint, req, resp) -> {
logger.log(Level.FINE, "dispatching endpoint = " + endpoint + " req = " + req);
logger.log(Level.FINE, "dispatching endpoint = " + endpoint +
" req = " + req + " req context path = " + req.getContextPath());
service.handle(req, resp);
})
.build();
@ -113,16 +116,82 @@ class EndpointTest {
assertTrue(success.get());
}
@Test
void testSimplePathEndpoints() throws Exception {
Path vartmp = Paths.get("/var/tmp/");
Service service = new FileService(vartmp);
HttpAddress httpAddress = HttpAddress.http1("localhost", 8008);
HttpEndpointResolver httpEndpointResolver = HttpEndpointResolver.builder()
.addEndpoint(HttpEndpoint.builder().setPrefix("/static").setPath("/**").build())
.addEndpoint(HttpEndpoint.builder().setPrefix("/static1").setPath("/**").build())
.addEndpoint(HttpEndpoint.builder().setPrefix("/static2").setPath("/**").build())
.addEndpoint(HttpEndpoint.builder().setPrefix("/static3").setPath("/**").build())
.setDispatcher((endpoint, req, resp) -> {
logger.log(Level.FINE, "dispatching endpoint = " + endpoint +
" req = " + req + " req context path = " + req.getContextPath());
service.handle(req, resp);
})
.build();
Domain domain = Domain.builder(httpAddress)
.addEndpointResolver(httpEndpointResolver)
.build();
Server server = Server.builder(domain)
.build();
Client client = Client.builder()
.build();
final AtomicBoolean success1 = new AtomicBoolean(false);
final AtomicBoolean success2 = new AtomicBoolean(false);
final AtomicBoolean success3 = new AtomicBoolean(false);
try {
Files.write(vartmp.resolve("test1.txt"), "Hello Jörg 1".getBytes(StandardCharsets.UTF_8));
Files.write(vartmp.resolve("test2.txt"), "Hello Jörg 2".getBytes(StandardCharsets.UTF_8));
Files.write(vartmp.resolve("test3.txt"), "Hello Jörg 3".getBytes(StandardCharsets.UTF_8));
server.accept();
Request request = Request.get().setVersion(HttpVersion.HTTP_1_1)
.url(server.getServerConfig().getAddress().base().resolve("/static1/test1.txt"))
.build()
.setResponseListener(resp -> {
assertEquals("Hello Jörg 1", resp.getBodyAsString(StandardCharsets.UTF_8));
success1.set(true);
});
client.execute(request).get();
Request request1 = Request.get().setVersion(HttpVersion.HTTP_1_1)
.url(server.getServerConfig().getAddress().base().resolve("/static2/test2.txt"))
.build()
.setResponseListener(resp -> {
assertEquals("Hello Jörg 2",resp.getBodyAsString(StandardCharsets.UTF_8));
success2.set(true);
});
client.execute(request1).get();
Request request2 = Request.get().setVersion(HttpVersion.HTTP_1_1)
.url(server.getServerConfig().getAddress().base().resolve("/static3/test3.txt"))
.build()
.setResponseListener(resp -> {
assertEquals("Hello Jörg 3", resp.getBodyAsString(StandardCharsets.UTF_8));
success3.set(true);
});
client.execute(request2).get();
} finally {
server.shutdownGracefully();
client.shutdownGracefully();
Files.delete(vartmp.resolve("test1.txt"));
Files.delete(vartmp.resolve("test2.txt"));
Files.delete(vartmp.resolve("test3.txt"));
logger.log(Level.INFO, "server and client shut down");
}
assertTrue(success1.get());
assertTrue(success2.get());
assertTrue(success3.get());
}
@Test
void testQueryEndpoints() throws Exception {
Path vartmp = Paths.get("/var/tmp/");
Service service = new FileService(vartmp);
HttpAddress httpAddress = HttpAddress.http1("localhost", 8008);
HttpEndpointResolver httpEndpointResolver = HttpEndpointResolver.builder()
.addEndpoint(HttpEndpoint.builder().setPrefix("/static1").setPath("/**").build())
.addEndpoint(HttpEndpoint.builder().setPrefix("/static2").setPath("/**").build())
.addEndpoint(HttpEndpoint.builder().setPrefix("/static3").setPath("/**").build())
.setDispatcher((endpoint, req, resp) -> {
logger.log(Level.FINE, "dispatching endpoint = " + endpoint + " req = " + req);
service.handle(req, resp);
@ -135,97 +204,18 @@ class EndpointTest {
.build();
Client client = Client.builder()
.build();
final AtomicBoolean success = new AtomicBoolean(false);
final AtomicBoolean success1 = new AtomicBoolean(false);
final AtomicBoolean success2 = new AtomicBoolean(false);
final AtomicBoolean success3 = new AtomicBoolean(false);
try {
Files.write(vartmp.resolve("test.txt"), "Hello Jörg".getBytes(StandardCharsets.UTF_8));
Files.write(vartmp.resolve("test1.txt"), "Hello Jörg 1".getBytes(StandardCharsets.UTF_8));
Files.write(vartmp.resolve("test2.txt"), "Hello Jörg 2".getBytes(StandardCharsets.UTF_8));
Files.write(vartmp.resolve("test3.txt"), "Hello Jörg 3".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(resp -> {
assertEquals("Hello Jörg", resp.getBodyAsString(StandardCharsets.UTF_8));
success.set(true);
});
client.execute(request).get();
Request request1 = Request.get().setVersion(HttpVersion.HTTP_1_1)
.url(server.getServerConfig().getAddress().base().resolve("/static1/test1.txt"))
.build()
.setResponseListener(resp -> {
assertEquals("Hello Jörg 1",resp.getBodyAsString(StandardCharsets.UTF_8));
success1.set(true);
});
client.execute(request1).get();
Request request2 = Request.get().setVersion(HttpVersion.HTTP_1_1)
.url(server.getServerConfig().getAddress().base().resolve("/static2/test2.txt"))
.build()
.setResponseListener(resp -> {
assertEquals("Hello Jörg 2", resp.getBodyAsString(StandardCharsets.UTF_8));
success2.set(true);
});
client.execute(request2).get();
} finally {
server.shutdownGracefully();
client.shutdownGracefully();
Files.delete(vartmp.resolve("test.txt"));
Files.delete(vartmp.resolve("test1.txt"));
Files.delete(vartmp.resolve("test2.txt"));
logger.log(Level.INFO, "server and client shut down");
}
assertTrue(success.get());
assertTrue(success1.get());
assertTrue(success2.get());
}
@Test
void testQueryAndFragmentEndpoints() throws Exception {
Path vartmp = Paths.get("/var/tmp/");
Service service = new FileService(vartmp);
HttpAddress httpAddress = HttpAddress.http1("localhost", 8008);
HttpEndpointResolver httpEndpointResolver = HttpEndpointResolver.builder()
.addEndpoint(HttpEndpoint.builder().setPrefix("/static").setPath("/**").build())
.addEndpoint(HttpEndpoint.builder().setPrefix("/static1").setPath("/**").build())
.addEndpoint(HttpEndpoint.builder().setPrefix("/static2").setPath("/**").build())
.setDispatcher((endpoint, req, resp) -> {
logger.log(Level.FINE, "dispatching endpoint = " + endpoint + " req = " + req +
" fragment=" + req.getURL().getFragment());
service.handle(req, resp);
})
.build();
Domain domain = Domain.builder(httpAddress)
.addEndpointResolver(httpEndpointResolver)
.build();
Server server = Server.builder(domain)
.build();
Client client = Client.builder()
.build();
final AtomicBoolean success = new AtomicBoolean(false);
final AtomicBoolean success1 = new AtomicBoolean(false);
final AtomicBoolean success2 = new AtomicBoolean(false);
try {
Files.write(vartmp.resolve("test.txt"), "Hello Jörg".getBytes(StandardCharsets.UTF_8));
Files.write(vartmp.resolve("test1.txt"), "Hello Jörg 1".getBytes(StandardCharsets.UTF_8));
Files.write(vartmp.resolve("test2.txt"), "Hello Jörg 2".getBytes(StandardCharsets.UTF_8));
server.accept();
Request request = Request.get().setVersion(HttpVersion.HTTP_1_1)
.url(server.getServerConfig().getAddress().base().resolve("/static/test.txt"))
.addParameter("a", "b")
.build()
.setResponseListener(resp -> {
if (resp.getStatus().getCode() == HttpResponseStatus.OK.code()) {
assertEquals("Hello Jörg", resp.getBodyAsString(StandardCharsets.UTF_8));
success.set(true);
} else {
logger.log(Level.WARNING, resp.getStatus().getReasonPhrase());
}
});
client.execute(request).get();
Request request1 = Request.get().setVersion(HttpVersion.HTTP_1_1)
.url(server.getServerConfig().getAddress().base()
.resolve("/static1/test1.txt").mutator().fragment("frag").build())
.resolve("/static1/test1.txt"))
.addParameter("a", "b")
.build()
.setResponseListener(resp -> {
if (resp.getStatus().getCode() == HttpResponseStatus.OK.code()) {
@ -239,44 +229,157 @@ class EndpointTest {
Request request2 = Request.get().setVersion(HttpVersion.HTTP_1_1)
.url(server.getServerConfig().getAddress().base()
.resolve("/static2/test2.txt"))
.content("{\"a\":\"b\"}","application/json")
.build()
.setResponseListener(resp -> {
if (resp.getStatus().getCode() == HttpResponseStatus.OK.code()) {
assertEquals("Hello Jörg 2",resp.getBodyAsString(StandardCharsets.UTF_8));
assertEquals("Hello Jörg 2", resp.getBodyAsString(StandardCharsets.UTF_8));
success2.set(true);
} else {
logger.log(Level.WARNING, resp.getStatus().getReasonPhrase());
}
});
client.execute(request2).get();
Request request3 = Request.get().setVersion(HttpVersion.HTTP_1_1)
.url(server.getServerConfig().getAddress().base()
.resolve("/static3/test3.txt"))
.content("{\"a\":\"b\"}","application/json")
.build()
.setResponseListener(resp -> {
if (resp.getStatus().getCode() == HttpResponseStatus.OK.code()) {
assertEquals("Hello Jörg 3",resp.getBodyAsString(StandardCharsets.UTF_8));
success3.set(true);
} else {
logger.log(Level.WARNING, resp.getStatus().getReasonPhrase());
}
});
client.execute(request3).get();
} finally {
server.shutdownGracefully();
client.shutdownGracefully();
Files.delete(vartmp.resolve("test.txt"));
logger.log(Level.INFO, "server and client shut down");
Files.delete(vartmp.resolve("test1.txt"));
Files.delete(vartmp.resolve("test2.txt"));
logger.log(Level.INFO, "server and client shut down");
Files.delete(vartmp.resolve("test3.txt"));
}
assertTrue(success.get());
assertTrue(success1.get());
assertTrue(success2.get());
assertTrue(success3.get());
}
@Test
void testMultiResolver() throws Exception {
Path vartmp = Paths.get("/var/tmp/");
Service service1 = new FileService(vartmp);
Service service2 = new FileService(vartmp);
Service service3 = new FileService(vartmp);
HttpAddress httpAddress = HttpAddress.http1("localhost", 8008);
HttpEndpointResolver httpEndpointResolver1 = HttpEndpointResolver.builder()
.addEndpoint(HttpEndpoint.builder().setPrefix("/static1").setPath("/**").build())
.setDispatcher((endpoint, req, resp) -> {
logger.log(Level.FINE, "dispatching endpoint = " + endpoint + " context path = " + req.getContextPath());
service1.handle(req, resp);
})
.build();
HttpEndpointResolver httpEndpointResolver2 = HttpEndpointResolver.builder()
.addEndpoint(HttpEndpoint.builder().setPrefix("/static2").setPath("/**").build())
.setDispatcher((endpoint, req, resp) -> {
logger.log(Level.FINE, "dispatching endpoint = " + endpoint + " context path = " + req.getContextPath());
service2.handle(req, resp);
})
.build();
HttpEndpointResolver httpEndpointResolver3 = HttpEndpointResolver.builder()
.addEndpoint(HttpEndpoint.builder().setPrefix("/static3").setPath("/**").build())
.setDispatcher((endpoint, req, resp) -> {
logger.log(Level.FINE, "dispatching endpoint = " + endpoint + " context path = " + req.getContextPath());
service3.handle(req, resp);
})
.build();
Domain domain = Domain.builder(httpAddress)
.addEndpointResolver(httpEndpointResolver1)
.addEndpointResolver(httpEndpointResolver2)
.addEndpointResolver(httpEndpointResolver3)
.build();
Server server = Server.builder(domain)
.build();
Client client = Client.builder()
.build();
final AtomicBoolean success1 = new AtomicBoolean(false);
final AtomicBoolean success2 = new AtomicBoolean(false);
final AtomicBoolean success3 = new AtomicBoolean(false);
try {
Files.write(vartmp.resolve("test1.txt"), "Hello Jörg 1".getBytes(StandardCharsets.UTF_8));
Files.write(vartmp.resolve("test2.txt"), "Hello Jörg 2".getBytes(StandardCharsets.UTF_8));
Files.write(vartmp.resolve("test3.txt"), "Hello Jörg 3".getBytes(StandardCharsets.UTF_8));
server.accept();
Request request1 = Request.get().setVersion(HttpVersion.HTTP_1_1)
.url(server.getServerConfig().getAddress().base()
.resolve("/static1/test1.txt"))
.addParameter("a", "b")
.build()
.setResponseListener(resp -> {
if (resp.getStatus().getCode() == HttpResponseStatus.OK.code()) {
assertEquals("Hello Jörg 1", resp.getBodyAsString(StandardCharsets.UTF_8));
success1.set(true);
} else {
logger.log(Level.WARNING, resp.getStatus().getReasonPhrase());
}
});
client.execute(request1).get();
Request request2 = Request.get().setVersion(HttpVersion.HTTP_1_1)
.url(server.getServerConfig().getAddress().base()
.resolve("/static2/test2.txt"))
.build()
.setResponseListener(resp -> {
if (resp.getStatus().getCode() == HttpResponseStatus.OK.code()) {
assertEquals("Hello Jörg 2", resp.getBodyAsString(StandardCharsets.UTF_8));
success2.set(true);
} else {
logger.log(Level.WARNING, resp.getStatus().getReasonPhrase());
}
});
client.execute(request2).get();
Request request3 = Request.get().setVersion(HttpVersion.HTTP_1_1)
.url(server.getServerConfig().getAddress().base()
.resolve("/static3/test3.txt"))
.content("{\"a\":\"b\"}","application/json")
.build()
.setResponseListener(resp -> {
if (resp.getStatus().getCode() == HttpResponseStatus.OK.code()) {
assertEquals("Hello Jörg 3",resp.getBodyAsString(StandardCharsets.UTF_8));
success3.set(true);
} else {
logger.log(Level.WARNING, resp.getStatus().getReasonPhrase());
}
});
client.execute(request3).get();
} finally {
server.shutdownGracefully();
client.shutdownGracefully();
logger.log(Level.INFO, "server and client shut down");
Files.delete(vartmp.resolve("test1.txt"));
Files.delete(vartmp.resolve("test2.txt"));
Files.delete(vartmp.resolve("test3.txt"));
}
assertTrue(success1.get());
assertTrue(success2.get());
assertTrue(success3.get());
}
@Test
void testMassiveEndpoints() throws IOException {
int max = 1024;
int max = 1024; // the default limit, must work
HttpAddress httpAddress = HttpAddress.http1("localhost", 8008);
HttpEndpointResolver.Builder endpointResolverBuilder = HttpEndpointResolver.builder()
.setPrefix("/static");
for (int i = 0; i < max; i++) {
endpointResolverBuilder.addEndpoint(HttpEndpoint.builder()
.setPath("/" + i + "/**")
.addFilter((req, resp) -> ServerResponse.write(resp, HttpResponseStatus.OK))
.build());
}
Domain domain = Domain.builder(httpAddress)
.addEndpointResolver(endpointResolverBuilder.build())
.addEndpointResolver(endpointResolverBuilder
.setDispatcher((endpoint,req, resp) -> ServerResponse.write(resp, HttpResponseStatus.OK))
.build())
.build();
Server server = Server.builder(domain)
.build();
@ -305,4 +408,45 @@ class EndpointTest {
}
assertEquals(max, count.get());
}
@Test
void testMassiveEndpointResolvers() throws IOException {
int max = 1024; // the default limit, must work
HttpAddress httpAddress = HttpAddress.http1("localhost", 8008);
HttpEndpointResolver.Builder endpointResolverBuilder = HttpEndpointResolver.builder()
.setPrefix("/static");
Domain.Builder domainBuilder = Domain.builder(httpAddress);
for (int i = 0; i < max; i++) {
domainBuilder.addEndpointResolver(endpointResolverBuilder.addEndpoint(HttpEndpoint.builder()
.setPath("/" + i + "/**").build())
.setDispatcher((endpoint,req, resp) -> ServerResponse.write(resp, HttpResponseStatus.OK))
.build());
}
Server server = Server.builder(domainBuilder.build())
.build();
Client client = Client.builder()
.build();
final AtomicInteger count = new AtomicInteger(0);
try {
server.accept();
for (int i = 0; i < max; i++) {
Request request = Request.get().setVersion(HttpVersion.HTTP_1_1)
.url(server.getServerConfig().getAddress().base().resolve("/static/" + i + "/test.txt"))
.build()
.setResponseListener(resp -> {
if (resp.getStatus().getCode() == HttpResponseStatus.OK.code()) {
count.incrementAndGet();
} else {
logger.log(Level.WARNING, resp.getStatus().getReasonPhrase());
}
});
client.execute(request).get();
}
} finally {
server.shutdownGracefully();
client.shutdownGracefully();
logger.log(Level.INFO, "server and client shut down");
}
assertEquals(max, count.get());
}
}

View file

@ -0,0 +1,210 @@
package org.xbib.netty.http.server.test.endpoint;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.xbib.netty.http.client.Client;
import org.xbib.netty.http.client.Request;
import org.xbib.netty.http.common.HttpAddress;
import org.xbib.netty.http.server.Server;
import org.xbib.netty.http.server.Domain;
import org.xbib.netty.http.server.endpoint.service.FileService;
import org.xbib.netty.http.server.test.NettyHttpTestExtension;
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.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
@ExtendWith(NettyHttpTestExtension.class)
class FileServiceTest {
private static final Logger logger = Logger.getLogger(FileServiceTest.class.getName());
@Test
void testFileServiceHttp1() throws Exception {
Path vartmp = Paths.get("/var/tmp/");
HttpAddress httpAddress = HttpAddress.http1("localhost", 8008);
Domain domain = Domain.builder(httpAddress)
.singleEndpoint("/static", "/**", new FileService(vartmp))
.build();
Server server = Server.builder(domain)
.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(resp -> {
assertEquals("Hello Jörg", resp.getBodyAsString(StandardCharsets.UTF_8));
success.set(true);
});
client.execute(request).get();
} finally {
server.shutdownGracefully();
client.shutdownGracefully();
Files.delete(vartmp.resolve("test.txt"));
logger.log(Level.INFO, "server and client shut down");
}
assertTrue(success.get());
}
@Test
void testFileServiceHttp2() throws Exception {
Path vartmp = Paths.get("/var/tmp/");
HttpAddress httpAddress = HttpAddress.http2("localhost", 8008);
Domain domain = Domain.builder(httpAddress)
.singleEndpoint("/static", "/**", new FileService(vartmp))
.build();
Server server = Server.builder(domain)
.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.valueOf("HTTP/2.0"))
.url(server.getServerConfig().getAddress().base().resolve("/static/test.txt"))
.build()
.setResponseListener(resp -> {
assertEquals("Hello Jörg", resp.getBodyAsString(StandardCharsets.UTF_8));
success.set(true);
});
client.execute(request).get();
} finally {
server.shutdownGracefully();
client.shutdownGracefully();
Files.delete(vartmp.resolve("test.txt"));
logger.log(Level.INFO, "server and client shut down");
}
assertTrue(success.get());
}
@Test
void testIndexFileHttp1() throws Exception {
Path vartmp = Paths.get("/var/tmp");
Path vartmpforward = vartmp.resolve("forward_test");
Files.createDirectories(vartmpforward);
Files.write(vartmpforward.resolve("test.txt"), "Hello Jörg".getBytes(StandardCharsets.UTF_8));
HttpAddress httpAddress = HttpAddress.http1("localhost", 8008);
Domain domain = Domain.builder(httpAddress)
.singleEndpoint("/static", "/**", new FileService(vartmp, "test.txt"))
.build();
Server server = Server.builder(domain)
.build();
Client client = Client.builder()
.build();
final AtomicBoolean success = new AtomicBoolean(false);
try {
server.accept();
Request request = Request.get().setVersion(HttpVersion.HTTP_1_1)
.url(server.getServerConfig().getAddress().base().resolve("/static/forward_test"))
.build()
.setResponseListener(resp -> {
if (resp.getStatus().getCode() == HttpResponseStatus.OK.code()) {
assertEquals("Hello Jörg", resp.getBodyAsString(StandardCharsets.UTF_8));
success.set(true);
}
});
client.execute(request).get();
} finally {
server.shutdownGracefully();
client.shutdownGracefully();
Files.delete(vartmpforward.resolve("test.txt"));
Files.delete(vartmpforward);
logger.log(Level.INFO, "server and client shut down");
}
assertTrue(success.get());
}
@Test
void testIndexFileHttp2() throws Exception {
Path vartmp = Paths.get("/var/tmp");
Path vartmpforward = vartmp.resolve("forward_test");
Files.createDirectories(vartmpforward);
Files.write(vartmpforward.resolve("test.txt"), "Hello Jörg".getBytes(StandardCharsets.UTF_8));
HttpAddress httpAddress = HttpAddress.http2("localhost", 8008);
Domain domain = Domain.builder(httpAddress)
.singleEndpoint("/static", "/**", new FileService(vartmp, "test.txt"))
.build();
Server server = Server.builder(domain)
.build();
Client client = Client.builder()
.build();
final AtomicBoolean success = new AtomicBoolean(false);
try {
server.accept();
Request request = Request.get()
.setVersion(HttpVersion.valueOf("HTTP/2.0"))
.url(server.getServerConfig().getAddress().base().resolve("/static/forward_test"))
.build()
.setResponseListener(resp -> {
if (resp.getStatus().getCode() == HttpResponseStatus.OK.code()) {
assertEquals("Hello Jörg", resp.getBodyAsString(StandardCharsets.UTF_8));
success.set(true);
}
});
client.execute(request).get();
// client waits for settings, we wait too
Thread.sleep(1000L);
} finally {
server.shutdownGracefully();
client.shutdownGracefully();
Files.delete(vartmpforward.resolve("test.txt"));
Files.delete(vartmpforward);
logger.log(Level.INFO, "server and client shut down");
}
assertTrue(success.get());
}
@Test
void testIndexFileParamsHttp1() throws Exception {
Path vartmp = Paths.get("/var/tmp");
Path vartmpforward = vartmp.resolve("forward_test");
Files.createDirectories(vartmpforward);
Files.write(vartmpforward.resolve("test.txt"), "Hello Jörg".getBytes(StandardCharsets.UTF_8));
HttpAddress httpAddress = HttpAddress.http1("localhost", 8008);
Domain domain = Domain.builder(httpAddress)
.singleEndpoint("/static", "/**",
new FileService(vartmp, "test.txt"))
.build();
Server server = Server.builder(domain)
.build();
Client client = Client.builder()
.build();
final AtomicBoolean success = new AtomicBoolean(false);
try {
server.accept();
Request request = Request.get().setVersion(HttpVersion.HTTP_1_1)
.url(server.getServerConfig().getAddress().base().resolve("/static/forward_test?a=b"))
.build()
.setResponseListener(resp -> {
if (resp.getStatus().getCode() == HttpResponseStatus.OK.code()) {
assertEquals("Hello Jörg", resp.getBodyAsString(StandardCharsets.UTF_8));
success.set(true);
}
});
client.execute(request).get();
} finally {
server.shutdownGracefully();
client.shutdownGracefully();
Files.delete(vartmpforward.resolve("test.txt"));
Files.delete(vartmpforward);
}
assertTrue(success.get());
}
}

View file

@ -1,4 +1,4 @@
package org.xbib.netty.http.server.test;
package org.xbib.netty.http.server.test.endpoint;
import io.netty.handler.codec.http.HttpVersion;
import org.junit.jupiter.api.Disabled;
@ -10,6 +10,7 @@ import org.xbib.netty.http.common.HttpAddress;
import org.xbib.netty.http.server.Server;
import org.xbib.netty.http.server.Domain;
import org.xbib.netty.http.server.endpoint.service.FileService;
import org.xbib.netty.http.server.test.NettyHttpTestExtension;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
@ -27,7 +28,6 @@ class SecureFileServiceTest {
private static final Logger logger = Logger.getLogger(SecureFileServiceTest.class.getName());
@Disabled
@Test
void testSecureFileServerHttp1() throws Exception {
Path vartmp = Paths.get("/var/tmp/");
@ -39,12 +39,10 @@ class SecureFileServiceTest {
.build())
.setChildThreadCount(8)
.build();
//server.logDiagnostics(Level.INFO);
Client client = Client.builder()
.setJdkSslProvider()
.trustInsecure()
.build();
//client.logDiagnostics(Level.INFO);
final AtomicBoolean success = new AtomicBoolean(false);
try {
Files.write(vartmp.resolve("test.txt"), "Hello Jörg".getBytes(StandardCharsets.UTF_8));
@ -57,14 +55,11 @@ class SecureFileServiceTest {
assertEquals("Hello Jörg", resp.getBodyAsString(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"));
logger.log(Level.INFO, "server and client shut down");
}
assertTrue(success.get());
}
@ -105,7 +100,6 @@ class SecureFileServiceTest {
server.shutdownGracefully();
client.shutdownGracefully();
Files.delete(vartmp.resolve("test.txt"));
logger.log(Level.INFO, "server and client shut down");
}
assertTrue(success.get());
}
@ -134,20 +128,18 @@ class SecureFileServiceTest {
server.accept();
Request request = Request.get()
.setVersion(HttpVersion.HTTP_1_1)
.url(server.getServerConfig().getAddress().base().resolve("/static/test.txt"))
.url(server.getServerConfig().getAddress().base()
.resolve("/static/test.txt"))
.build()
.setResponseListener(resp -> {
assertEquals("Hello Jörg", resp.getBodyAsString(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"));
logger.log(Level.INFO, "server and client shut down");
}
assertTrue(success.get());
}

View file

@ -19,7 +19,6 @@ import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.codec.http.LastHttpContent;
import io.netty.handler.codec.http.QueryStringDecoder;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.extension.ExtendWith;
@ -49,8 +48,6 @@ import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
/** flaky */
@Disabled
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@ExtendWith(NettyHttpTestExtension.class)
class HttpPipeliningHandlerTest {
@ -72,7 +69,7 @@ class HttpPipeliningHandlerTest {
EmbeddedChannel embeddedChannel = new EmbeddedChannel(new HttpPipeliningHandler(10000),
handler);
for (int i = 0; i < 5; i++) {
embeddedChannel.writeInbound(createHttpRequest("/" + String.valueOf(i)));
embeddedChannel.writeInbound(createHttpRequest("/" + i));
}
for (String url : waitingRequests.keySet()) {
finishRequest(url);
@ -90,7 +87,7 @@ class HttpPipeliningHandlerTest {
EmbeddedChannel embeddedChannel = new EmbeddedChannel(new HttpPipeliningHandler(10000),
handler);
for (int i = 0; i < 5; i++) {
embeddedChannel.writeInbound(createHttpRequest("/" + String.valueOf(i)));
embeddedChannel.writeInbound(createHttpRequest("/" + i));
}
List<String> urls = new ArrayList<>(waitingRequests.keySet());
Collections.shuffle(urls);