Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
package io.github.mike10004.nanochamp.server;
import io.github.mike10004.nanochamp.repackaged.fi.iki.elonen.NanoHTTPD;
import io.github.mike10004.nanochamp.repackaged.fi.iki.elonen.NanoHTTPD.IHTTPSession;
import io.github.mike10004.nanochamp.repackaged.fi.iki.elonen.NanoHTTPD.Method;
import io.github.mike10004.nanochamp.repackaged.fi.iki.elonen.NanoHTTPD.Response;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.LoggerFactory;
import javax.annotation.Nullable;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.URI;
import java.util.AbstractMap.SimpleImmutableEntry;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
import static java.util.Objects.requireNonNull;
public class NanoServer {
private final List requestHandlers;
private final RequestHandler defaultRequestHandler;
@Nullable
private NanoControl.HttpdImplFactory httpdFactory;
private NanoServer(Builder b) {
this(b.requestHandlers, b.defaultRequestHandler);
httpdFactory = b.httpdImplFactory;
}
public NanoServer(Iterable requestHandlers, RequestHandler defaultRequestHandler) {
this.requestHandlers = Collections.unmodifiableList(StreamSupport.stream(requestHandlers.spliterator(), false).collect(Collectors.toList()));
this.defaultRequestHandler = requireNonNull(defaultRequestHandler);
this.httpdFactory = null;
}
/**
* Interface for handlers of requests.
*/
public interface RequestHandler {
/**
* Responds to a request or ignores it if another handler should handle the request instead.
* @param session the session
* @return a response, or null if this handler should not handle this request
*/
@Nullable
NanoHTTPD.Response serve(NanoHTTPD.IHTTPSession session);
static RequestHandler getDefault() {
return (session) -> {
LoggerFactory.getLogger(RequestHandler.class.getName() + ".default").debug("404 {} {}", session.getUri(), StringUtils.abbreviate(session.getQueryParameterString(), 128));
return produceNotFoundResponse();
};
}
}
private static NanoHTTPD.Response produceNotFoundResponse() {
return NanoHTTPD.newFixedLengthResponse(NanoHTTPD.Response.Status.NOT_FOUND, "text/plain; charset=us-ascii", "404 Not Found");
}
static int findUnusedPort() throws IOException {
try (ServerSocket socket = new ServerSocket(0)) {
return socket.getLocalPort();
}
}
public NanoControl startServer(int port) throws IOException {
return new NanoControl(port, requestHandlers, defaultRequestHandler, httpdFactory);
}
public NanoControl startServer() throws IOException {
int port = findUnusedPort();
return startServer(port);
}
/**
* Server builder. Use this builder to define how your server should respond to requests.
* Note that you should not pre-fabricate {@link NanoHTTPD.Response} objects because each
* instance's input stream will be exhausted after it is used the first time. That is,
* your {@link ResponseProvider} implementations should always construct a new response
* inside the {@link ResponseProvider#serve(ServiceRequest)} method.
*/
public static class Builder {
private final List requestHandlers = new ArrayList<>();
private RequestHandler defaultRequestHandler = RequestHandler.getDefault();
private NanoControl.HttpdImplFactory httpdImplFactory = null;
private Builder() {}
public Builder httpdFactory(NanoControl.HttpdImplFactory httpdFactory) {
this.httpdImplFactory = httpdFactory;
return this;
}
public Builder get(ResponseProvider responseProvider) {
return handle(request -> request.method == Method.GET, responseProvider);
}
public Builder getPath(String path, ResponseProvider responseProvider) {
return getPath(path::equals, responseProvider);
}
public Builder getPath(Predicate super String> pathPredicate, ResponseProvider responseProvider) {
return handle(request -> request.method == Method.GET && pathPredicate.test(request.uri.getPath()), responseProvider);
}
public Builder handle(Predicate super ServiceRequest> decider, ResponseProvider responseProvider) {
return handle(new ResponseProvider() {
@Nullable
@Override
public Response serve(ServiceRequest request) {
if (decider.test(request)) {
return responseProvider.serve(request);
}
return null;
}
});
}
public Builder handle(ResponseProvider requestHandler) {
return session(requestHandler);
}
public Builder session(RequestHandler requestHandler) {
requestHandlers.add(requireNonNull(requestHandler));
return this;
}
public Builder handleAll(Collection extends RequestHandler> requestHandlers) {
this.requestHandlers.addAll(requestHandlers);
return this;
}
public Builder setDefault(RequestHandler defaultRequestHandler) {
this.defaultRequestHandler = requireNonNull(defaultRequestHandler);
return this;
}
public NanoServer build() {
return new NanoServer(this);
}
}
public static Builder builder() {
return new Builder();
}
public interface ValueListMap {
Map> asMap();
}
public static class ServiceRequest {
public final NanoHTTPD.Method method;
public final URI uri;
public final ValueListMap query;
public final Function headers;
public final IHTTPSession session;
public ServiceRequest(Method method, URI uri, ValueListMap query, Function headers, IHTTPSession session) {
this.method = method;
this.uri = uri;
this.query = query;
this.headers = headers;
this.session = session;
}
public static ServiceRequest fromSession(IHTTPSession session) {
return new ServiceRequest(session.getMethod(), URI.create(session.getUri()), makeMultimap(session.getParameters()), new CaseInsensitiveMapFunction<>(session.getHeaders()), session);
}
}
private static ValueListMap makeMultimap(Map> map) {
List> list = new ArrayList<>();
map.forEach((k, values) -> values.forEach(v -> list.add(new SimpleImmutableEntry<>(k, v))));
Map> m = MultimapShim.copyOf(list);
return new ValueListMap() {
@Override
public Map> asMap() {
return m;
}
};
}
/**
* Interface of classes that provide responses to HTTP requests.
*/
public interface ResponseProvider extends RequestHandler {
/**
* Produces a response to a request.
* @param request parsed request
* @return a response, or null if another handler should handle this request
*/
@Nullable
NanoHTTPD.Response serve(ServiceRequest request);
/**
* Serves a response for a session. This method delegates to
* {@link #serve(ServiceRequest)}.
* @param session the session
* @return a response
* @see #serve(ServiceRequest)
*/
@Nullable
@Override
default NanoHTTPD.Response serve(IHTTPSession session) {
return serve(ServiceRequest.fromSession(session));
}
}
private static class CaseInsensitiveMapFunction implements Function {
private final Map data;
private CaseInsensitiveMapFunction(Map data) {
this.data = data;
}
@Override
public V apply(String key) {
V exact = data.get(key);
if (exact != null) {
return exact;
}
return data.entrySet().stream()
.filter(entry -> key.equalsIgnoreCase(entry.getKey()))
.map(Map.Entry::getValue)
.findFirst().orElse(null);
}
}
}