Last active
September 21, 2024 18:56
-
-
Save nigjo/9007e06c58f16aecafb7a423ea3c14d1 to your computer and use it in GitHub Desktop.
A simple, insecure One-File-Java-Server to serve static pages. Main purpose is to have a simple server to locally test some github pages.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright 2020 Jens "Nigjo" Hofschröer. | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
*/ | |
package com.github.gist.nigjo.server; | |
/** | |
* Runs a simple Debug Server in a local folder. This file can be directly started with | |
* Java 11+ in a console. May be used from other Java classes. | |
* | |
* <strong>DO NOT RUN ON A PUBLIC SYSTEM!</strong> | |
* | |
* <h2>Syntax</h2> | |
* <pre>java Server.java [<port>] [<prefix>] [[<fileext>=<mediatype>]...]</pre> | |
* | |
* <dl> | |
* <dt>{@code <port>} | |
* </dt><dd>must be a positive integer. Default port is 8080.</dd> | |
* <dt>{@code <prefix>} | |
* </dt><dd>is a path prefix the server expects for every request. The default prefix is | |
* "/"</dd> | |
* <dt>{@code <fileext>=<mediatype>} | |
* </dt><dd>adds more known media types to the server via cli. some basic media types are | |
* defined in this class: {@code html}, {@code js}, {@code css}, {@code jpg}, {@code png}, | |
* {@code ico} and {@code json}.</dd> | |
* </dl> | |
* | |
* <h2>System properties</h2> | |
* <p> | |
* Some System Properties recognized by this class:</p> | |
* | |
* <dl> | |
* <dt>{@code com.github.gist.nigjo.server.defaultfile} | |
* </dt><dd>Fallback for folder requests. Defaults to "{@code index.html}".</dd> | |
* <dt>{@code com.github.gist.nigjo.server.allowDirlist} | |
* </dt><dd>Show folder content if "{@code Server.defaultfile}" is not found. Defaults to | |
* "{@code false}". Will always send a 404 resonse code.</dd> | |
* <dt>{@code com.github.gist.nigjo.server.multithread} | |
* </dt><dd>Use multiple threads to serve the requests. Defaults to "{@code false}".</dd> | |
* </dl> | |
* | |
* <h2>Source</h2> | |
* The original sources are from | |
* https://gist.github.com/nigjo/9007e06c58f16aecafb7a423ea3c14d1 | |
* | |
* last changed: 2024-09-15 | |
*/ | |
public class Server | |
{ | |
final java.util.Map<String, String> types = | |
new java.util.HashMap<>(java.util.Map.of( | |
"html", "text/html", | |
"js", "text/javascript", | |
"css", "text/css", | |
"jpg", "image/jpeg", | |
"png", "image/png", | |
"ico", "image/x-icon", | |
"json", "application/json" | |
)); | |
private int port = 8080; | |
private String prefix = "/"; | |
private String indexFile = | |
System.getProperty("com.github.gist.nigjo.server.defaultfile", "index.html"); | |
private boolean allowDirlist = | |
Boolean.getBoolean("com.github.gist.nigjo.server.allowDirlist"); | |
private boolean multithread = | |
Boolean.getBoolean("com.github.gist.nigjo.server.multithread"); | |
private java.io.File rootdir = new java.io.File("."); | |
private java.util.function.Consumer<String> logger; | |
private final java.util.Map<Integer, HandlerData> handlers = | |
new java.util.TreeMap<>(); | |
private static final class HandlerData | |
{ | |
RequestFilter filter; | |
ResponseWriter writer; | |
public HandlerData(RequestFilter filter, ResponseWriter writer) | |
{ | |
this.filter = filter; | |
this.writer = writer; | |
} | |
} | |
public Server() | |
{ | |
initDefaultHandlers(); | |
} | |
private void initDefaultHandlers() | |
{ | |
handlers.put(10000, new HandlerData((t, uri, local, dirRequest) | |
-> local != null && local.exists(), this::sendLocalFile)); | |
handlers.put(Integer.MAX_VALUE / 2, | |
new HandlerData((t, uri, local, dirRequest) -> dirRequest && allowDirlist | |
&& local != null && local.getParentFile() != null, | |
this::sendDirectoryListing)); | |
handlers.put(Integer.MAX_VALUE, | |
new HandlerData((t, u, f, d) -> true, this::sendErrorPage)); | |
} | |
public Server setPort(int port) | |
{ | |
this.port = port; | |
return this; | |
} | |
public Server setPrefix(String prefix) | |
{ | |
this.prefix = prefix; | |
return this; | |
} | |
public Server setRootdir(java.io.File rootdir) | |
{ | |
this.rootdir = rootdir; | |
return this; | |
} | |
public int getPort() | |
{ | |
return port; | |
} | |
public String getPrefix() | |
{ | |
return prefix; | |
} | |
public java.io.File getRootdir() | |
{ | |
return rootdir; | |
} | |
public String getIndexFile() | |
{ | |
return indexFile; | |
} | |
public Server setIndexFile(String indexFile) | |
{ | |
this.indexFile = indexFile; | |
return this; | |
} | |
public boolean isAllowDirlist() | |
{ | |
return allowDirlist; | |
} | |
public Server setAllowDirlist(boolean allowDirlist) | |
{ | |
this.allowDirlist = allowDirlist; | |
return this; | |
} | |
public Server addType(String extension, String mediatype) | |
{ | |
types.putIfAbsent(extension, mediatype); | |
return this; | |
} | |
public Server setLogger(java.util.function.Consumer<String> logger) | |
{ | |
this.logger = logger; | |
return this; | |
} | |
public boolean isMultithread() | |
{ | |
return multithread; | |
} | |
public Server setMultithread(boolean multithread) | |
{ | |
this.multithread = multithread; | |
return this; | |
} | |
/** | |
* Adds or changes a handler for requests. | |
* | |
* @param position Position for the new/existing Handler. If a handler exists at that | |
* position it will be overwriten. The default static file handler is at position 10000, | |
* the "directory list view" at MAX_INTEGER/2. | |
* @param filter A filter if a specific request should be handled by {@code writer}. The | |
* first filter that will return {@code true} will be used. | |
* @param writer The handler for the request. | |
*/ | |
public void setHandler(int position, RequestFilter filter, ResponseWriter writer) | |
{ | |
handlers.put(position, new HandlerData( | |
java.util.Objects.requireNonNull(filter), | |
java.util.Objects.requireNonNull(writer))); | |
} | |
private static void args(Server server, String[] a) | |
{ | |
for(String arg : a) | |
{ | |
try | |
{ | |
server.setPort(Integer.parseInt(arg)); | |
} | |
catch(NumberFormatException ex) | |
{ | |
if(arg.indexOf('=') > 0) | |
{ | |
String pair[] = arg.split("=", 2); | |
server.addType(pair[0], pair[1]); | |
} | |
else | |
{ | |
String prefix = arg; | |
if(prefix.charAt(0) != '/') | |
{ | |
prefix = '/' + prefix; | |
} | |
if(prefix.charAt(prefix.length() - 1) != '/') | |
{ | |
prefix += '/'; | |
} | |
server.setPrefix(prefix); | |
} | |
} | |
} | |
} | |
public static void main(String[] a) throws java.io.IOException | |
{ | |
Server server = new Server(); | |
server.setLogger(System.out::println); | |
args(server, a); | |
server.startServer(); | |
} | |
/** | |
* Create all context handlers for the server. The default implementation will only | |
* serve all static files. | |
* | |
* @param server | |
*/ | |
protected void createContext(com.sun.net.httpserver.HttpServer server) | |
{ | |
server.createContext("/", this::handleRequest); | |
} | |
public void startServer() throws java.io.IOException | |
{ | |
java.net.InetSocketAddress host = | |
new java.net.InetSocketAddress(port); | |
com.sun.net.httpserver.HttpServer server = | |
com.sun.net.httpserver.HttpServer.create(host, 0); | |
createContext(server); | |
if(multithread) | |
{ | |
server.setExecutor(java.util.concurrent.Executors.newCachedThreadPool()); | |
} | |
server.start(); | |
if(logger != null) | |
{ | |
logger.accept("Server is running at " + toString()); | |
} | |
} | |
@Override | |
public String toString() | |
{ | |
return "http://localhost:" + getPort() + prefix; | |
} | |
private void handleRequest(com.sun.net.httpserver.HttpExchange t) | |
throws java.io.IOException | |
{ | |
boolean isDirRequest; | |
java.net.URI requestUri = t.getRequestURI(); | |
if(requestUri.getPath().endsWith("/")) | |
{ | |
isDirRequest = true; | |
String query = requestUri.getQuery(); | |
String fragment = requestUri.getFragment(); | |
requestUri = requestUri.resolve(indexFile | |
+ (query == null ? "" : "?" + query) | |
+ (fragment == null ? "" : "#" + fragment) | |
); | |
} | |
else | |
{ | |
isDirRequest = false; | |
} | |
java.net.URI uri = requestUri; | |
String path = uri.getPath(); | |
if(multithread) | |
{ | |
toLogger((b) -> b | |
.append(getThreadId()) | |
.append(": ") | |
.append(new java.util.Date().toString()) | |
.append(" ") | |
.append(path) | |
); | |
} | |
java.io.File requested; | |
if(path.startsWith(prefix)) | |
{ | |
requested = new java.io.File(rootdir, path.substring(prefix.length())); | |
} | |
else | |
{ | |
requested = null; | |
} | |
java.util.function.Consumer<StringBuilder> logmessage = b -> | |
{ | |
}; | |
try | |
{ | |
if(multithread) | |
{ | |
logmessage = logmessage | |
.andThen(b -> b.append(getThreadId()) | |
.append(": ")); | |
} | |
//logmessage. | |
logmessage = logmessage | |
.andThen(b -> b.append(new java.util.Date().toString())) | |
.andThen(b -> b.append(" GET ").append(uri)); | |
ResponseWriter responseWriter = | |
handlers.values().stream() | |
.filter(h -> h.filter.acceptsRequest(t, uri, requested, isDirRequest)) | |
.findFirst() | |
.map(h -> h.writer) | |
.orElseThrow(); | |
var sendLog = new StringBuilder(); | |
ResponseSender sender = | |
responseWriter.writeResponse(t, uri, requested, sendLog); | |
logmessage = logmessage.andThen(sb -> sb.append(sendLog)); | |
try(java.io.OutputStream os = t.getResponseBody()) | |
{ | |
sender.sendResponse(os); | |
} | |
catch(java.io.IOException ex) | |
{ | |
logmessage = logmessage | |
.andThen(b -> b.append(":: ")) | |
.andThen(b -> b.append(ex.toString())); | |
throw ex; | |
} | |
} | |
finally | |
{ | |
toLogger(logmessage); | |
} | |
} | |
private ResponseSender sendErrorPage( | |
com.sun.net.httpserver.HttpExchange t, java.net.URI uri, | |
java.io.File local, StringBuilder logmessage) | |
throws java.io.IOException | |
{ | |
logmessage.append(" 404"); | |
String response = "File not found " + uri.toString(); | |
t.sendResponseHeaders(404, response.length()); | |
return (out) -> out.write(response.getBytes()); | |
} | |
private ResponseSender sendDirectoryListing( | |
com.sun.net.httpserver.HttpExchange t, java.net.URI uri, | |
java.io.File local, StringBuilder logmessage) | |
throws java.io.IOException | |
{ | |
logmessage.append(" ").append(types.get("html")); | |
t.getResponseHeaders() | |
.add("Content-Type", types.get("html")); | |
StringBuilder dirlist = new StringBuilder(); | |
dirlist.append("<p>Folder content of <code>") | |
.append(uri.getPath()) | |
.append("</code></p>"); | |
dirlist.append("<ul>"); | |
if(!local.getParentFile().equals(rootdir)) | |
{ | |
dirlist.append("<li><code><a href='../'>../</a></code></li>"); | |
} | |
for(java.io.File child : local.getParentFile().listFiles()) | |
{ | |
String childname = child.getName() + (child.isDirectory() ? "/" : ""); | |
dirlist.append("<li><code><a href='") | |
.append(childname) | |
.append("'>") | |
.append(childname) | |
.append("</a></code></li>"); | |
} | |
dirlist.append("</ul>"); | |
String response = dirlist.toString(); | |
t.sendResponseHeaders(404, response.length()); | |
return (out) -> out.write(response.getBytes()); | |
} | |
private ResponseSender sendLocalFile( | |
com.sun.net.httpserver.HttpExchange t, java.net.URI uri, | |
java.io.File local, StringBuilder logmessage) | |
throws java.io.IOException | |
{ | |
//String response = "This is the response of "+local.getAbsolutePath(); | |
String filename = local.getName(); | |
String ext = filename.substring(filename.lastIndexOf('.') + 1); | |
if(types.containsKey(ext)) | |
{ | |
logmessage.append(" ").append(types.get(ext)); | |
t.getResponseHeaders() | |
.add("Content-Type", types.get(ext)); | |
} | |
logmessage.append(" 200 ").append(local.length()); | |
t.sendResponseHeaders(200, local.length()); | |
return (o) -> java.nio.file.Files.copy(local.toPath(), o); | |
} | |
@SuppressWarnings("deprecation") | |
private static long getThreadId() | |
{ | |
Runtime.Version vmVersion = Runtime.Version.parse(System | |
.getProperty("java.vm.version")); | |
try | |
{ | |
if(vmVersion.feature() >= 19) | |
{ | |
return (Long)Thread.class.getMethod("threadId").invoke(null); | |
} | |
else | |
{ | |
return (Long)Thread.class.getMethod("getId").invoke(null); | |
} | |
} | |
catch(ReflectiveOperationException ex) | |
{ | |
System.err.println(ex.toString()); | |
return 0; | |
} | |
} | |
protected void toLogger(java.util.function.Consumer<StringBuilder> logmessage) | |
{ | |
if(logger != null) | |
{ | |
StringBuilder fullMessage = new StringBuilder(); | |
logmessage.accept(fullMessage); | |
logger.accept(fullMessage.toString()); | |
} | |
} | |
/** | |
* A Consumer to write the Response-Content to an OutputStream. | |
*/ | |
@FunctionalInterface | |
public static interface ResponseSender | |
{ | |
/** | |
* Sends the content body of the response. This should match the size in the | |
* {@link com.sun.net.httpserver.HttpExchange#sendResponseHeaders(int, long)} call | |
* from the {@link ResponseWriter}. | |
* | |
* @param out Stream to write to. | |
* | |
* @throws java.io.IOException Any problem while sending the response content. | |
*/ | |
void sendResponse(java.io.OutputStream out) throws java.io.IOException; | |
} | |
/** | |
* A Handler for a request. Some work is already done link mapping the request to a | |
* local file. "Defaultfile"-mapping for directory request are handled for that. | |
* | |
* <p> | |
* <strong>IMPORTANT:</strong>An implementation must call | |
* {@link com.sun.net.httpserver.HttpExchange#sendResponseHeaders(int, long) t.sendResponseHeaders()} | |
* with valid parameter values. | |
*/ | |
@FunctionalInterface | |
public static interface ResponseWriter | |
{ | |
/** | |
* Creates a response for a Request. | |
* | |
* @param t Server Request/Response Instance. Can be used to get request headers or | |
* set response headers. Do not call | |
* {@link com.sun.net.httpserver.HttpExchange#getResponseBody()} as this is managed | |
* otherwise. | |
* @param uri the request uri, modified to map "indexFile" requests. | |
* @param localFile the request uri, mapping to a local file path. | |
* @param logmessage buffer to be added to the server log message. Do not include any | |
* line breaks. | |
* | |
* @return A provider for the response content. This should match the size in the | |
* {@link com.sun.net.httpserver.HttpExchange#sendResponseHeaders(int, long)} call. | |
* | |
* @throws java.io.IOException any problem while creating the response. | |
*/ | |
ResponseSender writeResponse( | |
com.sun.net.httpserver.HttpExchange t, java.net.URI uri, | |
java.io.File localFile, StringBuilder logmessage) | |
throws java.io.IOException; | |
} | |
/** | |
* Filter for a Request in conjunction with a ResponseWriter. Every ResponseWriter needs | |
* a RequestFilter that is filters a Request to be handled by the ResponseWriter. | |
* | |
* @see #addHandler | |
*/ | |
@FunctionalInterface | |
public static interface RequestFilter | |
{ | |
boolean acceptsRequest( | |
com.sun.net.httpserver.HttpExchange t, java.net.URI uri, | |
java.io.File localFile, boolean isDirRequest); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright 2020-2024 Jens "Nigjo" Hofschröer. | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
*/ | |
package com.github.gist.nigjo.server; | |
import java.io.ByteArrayInputStream; | |
import java.io.IOException; | |
import java.io.InputStream; | |
import java.net.InetSocketAddress; | |
import java.nio.charset.StandardCharsets; | |
import java.nio.file.Path; | |
import java.util.ArrayList; | |
import java.util.HashMap; | |
import java.util.List; | |
import java.util.Map; | |
import java.util.Objects; | |
/** | |
* A simple, insecure Server that can execute Single-File Source-Code Programs as | |
* CGI-Scripts. | |
* | |
* To run this Single-File Source-Code Program you must compile the {@code Server} class | |
* beforehand (or your JDK is capable of Multi-File Source-Code Programs): | |
* | |
* <pre>javac -d . Server.java</pre> | |
* | |
* Otherwise you get ClassNotFoundExceptions. | |
* | |
* @author nigjo | |
*/ | |
public class ServerCgi | |
{ | |
public static final String GATEWAY_INTERFACE = "GATEWAY_INTERFACE"; | |
public static final String QUERY_STRING = "QUERY_STRING"; | |
public static final String REMOTE_ADDR = "REMOTE_ADDR"; | |
public static final String REQUEST_METHOD = "REQUEST_METHOD"; | |
public static final String SCRIPT_NAME = "SCRIPT_NAME"; | |
public static final String SERVER_NAME = "SERVER_NAME"; | |
public static final String SERVER_PORT = "SERVER_PORT"; | |
public static final String SERVER_PROTOCOL = "SERVER_PROTOCOL"; | |
public static final String SERVER_SOFTWARE = "SERVER_SOFTWARE"; | |
public static final String PATH_INFO = "PATH_INFO"; | |
private static final String CGIBIN_DIR = System.getProperty( | |
"com.github.gist.nigjo.server.cgibin_prefix", "/cgi-bin/"); | |
private static void debugLog() | |
{ | |
//System.err.println(); | |
} | |
private static void debugLog(Object msg) | |
{ | |
//System.err.println(msg); | |
} | |
private static void debugText(Object msg) | |
{ | |
//System.err.print(msg); | |
} | |
/** | |
* @param args the command line arguments | |
*/ | |
public static void main(String args[]) throws Exception | |
{ | |
Server server = new Server(); | |
server.setLogger(System.out::println); | |
server.setAllowDirlist(true); | |
server.setHandler(5000, (t, uri, localFile, isDirRequest) -> | |
{ | |
if(uri.getPath().startsWith(CGIBIN_DIR) && localFile != null) | |
{ | |
Path requestPath = Path.of(uri.getPath()); | |
debugLog("??requestPath=" + requestPath); | |
Path localPath = localFile.toPath(); | |
debugLog("??localPath=" + localPath); | |
Path cgiPath = requestPath.subpath(0, 2); | |
debugLog("??cgiPath=" + cgiPath); | |
while(localPath != null && !localPath.endsWith(cgiPath)) | |
localPath = localPath.getParent(); | |
debugLog("??localPath=" + localPath); | |
if(localPath != null && localPath.toFile().exists() | |
&& localPath.toString().endsWith(".java")) | |
{ | |
return true; | |
} | |
} | |
return false; | |
}, (t, uri, localFile, logmessage) -> | |
{ | |
Map<String, String> cgienv = new HashMap<>(); | |
if(!"GET".equals(t.getRequestMethod())) | |
{ | |
t.sendResponseHeaders(405, 0); | |
return out -> out.write("Method not allowd".getBytes()); | |
} | |
cgienv.put("RequestURI", t.getRequestURI().toASCIIString()); | |
cgienv.put("header", t.getRequestHeaders().toString()); | |
cgienv.put(GATEWAY_INTERFACE, "CGI/1.1"); | |
cgienv.put(REMOTE_ADDR, t.getRemoteAddress().getHostName() | |
+ ':' + t.getRemoteAddress().getPort()); | |
cgienv.put(REQUEST_METHOD, t.getRequestMethod()); | |
cgienv.put(SCRIPT_NAME, uri.getPath()); | |
InetSocketAddress localAddress = t.getLocalAddress(); | |
cgienv.put(SERVER_NAME, | |
Objects.toString(t.getRequestHeaders().getFirst("HOST") | |
.replaceFirst(":\\d+$", ""), | |
localAddress.getHostName()) | |
); | |
cgienv.put(SERVER_SOFTWARE, ServerCgi.class.getName() + "/1.0"); | |
cgienv.put(SERVER_PROTOCOL, "HTTP/1.0"); | |
cgienv.put(SERVER_PORT, String.valueOf(server.getPort())); | |
cgienv.put("JAVA_HOME", System.getProperty("java.home")); | |
Path localPath = localFile.toPath(); | |
debugLog("<<localPath=" + localPath); | |
Path requestPath = Path.of(uri.getPath()); | |
debugLog("<<requestPath=" + requestPath); | |
if(requestPath.getNameCount() > 2) | |
{ | |
Path cgiPath = requestPath.subpath(0, 2); | |
debugLog("<<cgiPath=" + cgiPath); | |
Path pathInfo = requestPath.subpath(2, requestPath.getNameCount()); | |
debugLog("<<pathInfo=" + pathInfo); | |
localPath = localPath.subpath(0, localPath.getNameCount() - pathInfo | |
.getNameCount()); | |
debugLog("<<realPath=" + localPath); | |
// while(localPath != null && !localPath.endsWith(cgiPath)) | |
// localPath = localPath.getParent(); | |
//cgienv = new HashMap<>(cgienv); | |
cgienv.put(PATH_INFO, '/' + pathInfo.toString().replace('\\', '/')); | |
cgienv.put(SCRIPT_NAME, '/' + cgiPath.toString().replace('\\', '/')); | |
} | |
String query = uri.getQuery(); | |
if(query != null && !query.isBlank() && !"?".equals(query.trim())) | |
{ | |
cgienv.put(QUERY_STRING, query); | |
} | |
t.getRequestHeaders() | |
.forEach((k, v) -> | |
{ | |
cgienv.put("HTTP_" + k.toUpperCase().replace('-', '_'), | |
String.join(" ", v)); | |
}); | |
List<String> command = new ArrayList<>(); | |
command.add(Path.of(System.getProperty("java.home")) | |
.resolve("bin/java") | |
.toString()); | |
Map.of( | |
"file.encoding", "UTF-8" | |
).forEach((p, v) -> command.add("-D" + p + "=" + v)); | |
command.add("-cp"); | |
command.add(localPath.getParent().toString()); | |
command.add(localPath.toString()); | |
//t.getResponseHeaders().add("Content-Type", "text/html; charset=utf-8"); | |
debugLog("bin:" + command); | |
try | |
{ | |
Path docRoot = Path.of(System.getProperty("user.dir")); | |
ProcessBuilder builder = new ProcessBuilder() | |
.command(command) | |
.directory(docRoot.toFile()); | |
builder.environment().clear(); | |
builder.environment().putAll(cgienv); | |
Process binScript = builder.start(); | |
if(binScript == null || !binScript.isAlive()) | |
{ | |
System.err.println("unable to start script"); | |
t.sendResponseHeaders(500, 0); | |
return o -> o.write("unable to start script".getBytes()); | |
} | |
int returnStatus = 200; | |
InputStream headerReader = binScript.getInputStream(); | |
StringBuilder buffer; | |
do | |
{ | |
debugText("##"); | |
buffer = new StringBuilder(); | |
int val = headerReader.read(); | |
while(val != '\n') | |
{ | |
if(val < 0) | |
{ | |
break; | |
} | |
if(val != '\r') | |
{ | |
buffer.append(Character.toString(val)); | |
} | |
val = headerReader.read(); | |
} | |
if(buffer.length() > 0) | |
{ | |
debugText(buffer); | |
String[] headerEntry = buffer.toString().split(":\\s*", 2); | |
if(headerEntry.length == 2) | |
{ | |
switch(headerEntry[0]) | |
{ | |
case "Location": | |
t.getResponseHeaders().add(headerEntry[0], headerEntry[1]); | |
returnStatus = returnStatus == 200 ? 302 : returnStatus; | |
break; | |
case "Stauts": | |
returnStatus = Integer.parseInt(headerEntry[1]); | |
break; | |
default: | |
t.getResponseHeaders().add(headerEntry[0], headerEntry[1]); | |
break; | |
} | |
} | |
} | |
debugLog(); | |
} | |
while(buffer.length() != 0); | |
try | |
{ | |
int rc = binScript.exitValue(); | |
if(rc != 0) | |
{ | |
System.err.println("external script exited with code " + rc); | |
t.getResponseHeaders().add("Content-Type", "text/plain"); | |
t.sendResponseHeaders(500, 0); | |
return out -> binScript.getErrorStream().transferTo(out); | |
} | |
else | |
{ | |
t.sendResponseHeaders(returnStatus, 0); | |
} | |
} | |
catch(IllegalThreadStateException runEx) | |
{ | |
//macht nichts, wenns noch laeuft. | |
//System.err.println("running..."); | |
t.sendResponseHeaders(returnStatus, 0); | |
} | |
return out -> binScript.getInputStream().transferTo(out); | |
} | |
catch(IOException ex) | |
{ | |
String msg = ex.toString(); | |
System.err.println(msg); | |
byte[] bytes = msg.getBytes(StandardCharsets.UTF_8); | |
t.sendResponseHeaders(500, bytes.length); | |
return (o) -> new ByteArrayInputStream(bytes).transferTo(o); | |
} | |
}); | |
server.startServer(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment