com.purej.vminspect.http.server.RequestExecutor Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of purej-vminspect Show documentation
Show all versions of purej-vminspect Show documentation
An easy to use, feature-rich, JMX-based and embeddable Java VM monitoring tool with a web-based user-interface
package com.purej.vminspect.http.server;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
import java.net.SocketException;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.purej.vminspect.http.HttpRequest;
import com.purej.vminspect.http.HttpResponse;
import com.purej.vminspect.http.RequestController;
import com.purej.vminspect.util.Utils;
/**
* Each instance of this class executes a single HTTP request and writes
* the response to the socket's output-stream. The given socket will be closed
* after processing the request.
*
* This class implements the {@link Runnable} interface and allows to be executed
* asynchronously using an ExecutorService.
*
* @author Stefan Mueller
*/
final class RequestExecutor implements Runnable {
private static final Logger LOGGER = LoggerFactory.getLogger(VmInspectionServer.class);
private final Socket _socket;
private final RequestController _controller;
private final boolean _mbeansReadonly;
/**
* Creates a new instance of this class that executes the sockets request.
*/
RequestExecutor(Socket socket, RequestController controller, boolean mbeansReadonly) {
super();
_socket = socket;
_controller = controller;
_mbeansReadonly = mbeansReadonly;
}
/**
* Executes the request from the specified client-socket and closes streams and the socket.
*/
@Override
public void run() {
try {
try {
InputStream in = _socket.getInputStream();
OutputStream out = _socket.getOutputStream();
try {
// Create the request, process it and write the response:
HttpRequest request = parseRequest(in);
if (request == null) {
LOGGER.debug("Non HTTP GET request from {}, will be ignored", _socket.getRemoteSocketAddress());
}
else {
LOGGER.debug("HTTP GET request from {} with parameters {}", _socket.getRemoteSocketAddress(), request.getParameters());
try {
HttpResponse httpResponse = _controller.process(request, _mbeansReadonly);
writeResponse(httpResponse, out);
}
catch (SocketException e) {
// This might occur if the browser terminates open requests, just log...
LOGGER.debug("Socket closed by remote host: {}", e.getMessage());
}
catch (Exception e) {
LOGGER.debug("Could not handle HTTP request!", e);
writeErrorResponse(e, "500 Server Error", out);
}
}
}
finally {
out.close();
in.close();
}
}
finally {
_socket.close();
}
}
catch (Exception e) {
LOGGER.error("An error occurred handling HTTP request!", e);
}
}
private static HttpRequest parseRequest(InputStream input) throws Exception {
BufferedReader in = new BufferedReader(new InputStreamReader(input, "UTF-8"));
HttpRequest request = new HttpRequest();
boolean getContained = false;
while (true) {
String line = in.readLine();
line = line != null ? line.trim() : line;
if (line == null || line.length() == 0) {
break; // End of request...
}
else if (line.startsWith("GET ")) {
getContained = true;
parseGet(line, request);
}
else if (line.startsWith("Cookie: ")) {
parseCookies(line, request);
}
}
return getContained ? request : null;
}
private static void parseGet(String line, HttpRequest request) throws Exception {
// Format is: GET[space]Request-URI[space]HTTP-Version
int idx = line.indexOf('?');
if (idx > 0) {
String[] params = line.substring(idx + 1, line.lastIndexOf(' ')).split("&");
splitKeyValues(params, request.getParameters());
}
}
private static void parseCookies(String line, HttpRequest request) throws Exception {
// Format is: Cookies:[space]name=value;[space]name=value;[space]...
String[] cookies = line.substring("Cookie: ".length()).split("; ");
splitKeyValues(cookies, request.getCookies());
}
private static void splitKeyValues(String[] keyValues, Map map) {
for (String keyValue : keyValues) {
int idx = keyValue.indexOf('=');
if (idx > 0) {
String key = keyValue.substring(0, idx);
String value = keyValue.substring(idx + 1);
int length = value.length();
value = length > 2 && value.charAt(0) == '"' && value.charAt(length - 1) == '"' ? value.substring(1, length - 1) : value;
map.put(key, Utils.urlDecode(value));
}
}
}
private static void writeResponse(HttpResponse response, OutputStream out) throws IOException {
// Sanity check first:
byte[] data = response.getContentBytes();
if (data == null || data.length == 0) {
writeErrorResponse(null, "", out);
return;
}
// Write in correct order: a) Status
StringBuilder builder = new StringBuilder(512);
appendResponseStatus("200 OK", builder);
// b) Cookies:
for (Map.Entry entry : response.getCookies().entrySet()) {
builder.append("\r\nSet-Cookie: ").append(entry.getKey()).append("=").append(Utils.urlEncode(entry.getValue()));
builder.append("; Max-Age=").append(30 * 24 * 60 * 60); // 30 days
}
// c) Caching:
if (response.getCacheSeconds() > 0) {
builder.append("\r\nCache-Control: max-age=" + response.getCacheSeconds());
}
else {
builder.append("\r\nCache-Control: no-cache\r\nPragma: no-cache\r\nExpires: -1");
}
// d) Content type and length:
builder.append("\r\nContent-Type: " + response.getContentType());
builder.append("\r\nContent-Length: " + data.length);
// e) Finalize to output-stream - header and binary content:
builder.append("\r\n\r\n");
out.write(builder.toString().getBytes("UTF-8"));
out.write(data);
}
private static void writeErrorResponse(Exception e, String errorPart, OutputStream out) throws IOException {
StringBuilder builder = new StringBuilder(100);
appendResponseStatus(errorPart, builder);
String msg = Utils.htmlEncode(e.getMessage());
String details = Utils.htmlEncode(Utils.getExceptionInfo(e));
builder.append("\r\nCache-Control: no-cache\r\nPragma: no-cache\r\nExpires: -1");
builder.append("\r\nContent-Type: text/html; charset=utf-8");
builder.append("\r\n\r\nError ").append(errorPart).append(" ");
builder.append("").append(errorPart).append(" - ").append(msg).append("
");
builder.append("").append(details).append("
Powered by adopus consulting gmbh");
out.write(builder.toString().getBytes("UTF-8"));
}
private static void appendResponseStatus(String statusPart, StringBuilder builder) {
builder.append("HTTP/1.0 ").append(statusPart);
builder.append("\r\nServer: VmInspectionServer (simple Java HTTP server)\r\nAllow: GET\r\nConnection: close");
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy