/** * Writes the HTTP response to the output stream. * * @param http The HttpExchange object that allows access to the request and response. * @param isGet Flag indicating whether or not the request was a GET request or POST request. * @param pretty Flag indicating whether or not the output, if JSON, should be pretty printed or * not. * @param response The DMR response from the operation. * @param status The HTTP status code to be included in the response. * @param encode Flag indicating whether or not to Base64 encode the response payload. * @throws IOException if an error occurs while attempting to generate the HTTP response. */ private void writeResponse( final HttpExchange http, boolean isGet, boolean pretty, ModelNode response, int status, boolean encode, String contentType) throws IOException { final Headers responseHeaders = http.getResponseHeaders(); responseHeaders.add(CONTENT_TYPE, contentType); http.sendResponseHeaders(status, 0); final OutputStream out = http.getResponseBody(); final PrintWriter print = new PrintWriter(out); // GET (read) operations will never have a compensating update, and the status is already // available via the http response status code, so unwrap them. if (isGet && status == OK) response = response.get("result"); try { if (encode) { response.writeBase64(out); } else { response.writeJSONString(print, !pretty); } } finally { print.flush(); out.flush(); safeClose(print); safeClose(out); } }
/** * Handle a form POST deployment upload request. * * @param http The HttpExchange object that allows access to the request and response. * @throws IOException if an error occurs while attempting to extract the deployment from the * multipart/form data. */ private void processUploadRequest(final HttpExchange http) throws IOException { ModelNode response = null; try { SeekResult result = seekToDeployment(http); final ModelNode dmr = new ModelNode(); dmr.get("operation").set("upload-deployment-stream"); dmr.get("address").setEmptyList(); dmr.get("input-stream-index").set(0); OperationBuilder operation = new OperationBuilder(dmr); operation.addInputStream(result.stream); response = modelController.execute(operation.build()); drain(http.getRequestBody()); } catch (Throwable t) { // TODO Consider draining input stream ROOT_LOGGER.uploadError(t); http.sendResponseHeaders(INTERNAL_SERVER_ERROR, -1); return; } // TODO Determine what format the response should be in for a deployment upload request. writeResponse(http, false, false, response, OK, false, TEXT_HTML); }
/** * Extracts the body content contained in a POST request. * * @param http The <code>HttpExchange</code> object containing POST request data. * @return a result containing the stream and the file name reported by the client * @throws IOException if an error occurs while attempting to extract the POST request data. */ private SeekResult seekToDeployment(final HttpExchange http) throws IOException { final String type = http.getRequestHeaders().getFirst(CONTENT_TYPE); if (type == null) throw MESSAGES.invalidContentType(); Matcher matcher = MULTIPART_FD_BOUNDARY.matcher(type); if (!matcher.matches()) throw MESSAGES.invalidContentType(type); final String boundary = "--" + matcher.group(1); final BoundaryDelimitedInputStream stream = new BoundaryDelimitedInputStream(http.getRequestBody(), boundary.getBytes("US-ASCII")); // Eat preamble byte[] ignore = new byte[1024]; while (stream.read(ignore) != -1) {} // From here on out a boundary is prefixed with a CRLF that should be skipped stream.setBoundary(("\r\n" + boundary).getBytes(US_ASCII)); while (!stream.isOuterStreamClosed()) { // purposefully send the trailing CRLF to headers so that a headerless body can be detected MimeHeaderParser.ParseResult result = MimeHeaderParser.parseHeaders(stream); if (result.eof()) continue; // Skip content-less part Headers partHeaders = result.headers(); String disposition = partHeaders.getFirst(CONTENT_DISPOSITION); if (disposition != null) { matcher = DISPOSITION_FILE.matcher(disposition); if (matcher.matches()) { SeekResult seek = new SeekResult(); seek.fileName = matcher.group(1); seek.stream = stream; return seek; } } while (stream.read(ignore) != -1) {} } throw MESSAGES.invalidDeployment(); }
/** * Handles a operation request via HTTP. * * @param http The HttpExchange object that allows access to the request and response. * @throws IOException if an error occurs while attempting to process the request. */ private void processRequest(final HttpExchange http) throws IOException { final URI request = http.getRequestURI(); final String requestMethod = http.getRequestMethod(); boolean isGet = GET.equals(requestMethod); if (!isGet && !POST.equals(requestMethod)) { http.sendResponseHeaders(METHOD_NOT_ALLOWED, -1); return; } ModelNode dmr; ModelNode response; int status = OK; Headers requestHeaders = http.getRequestHeaders(); boolean encode = APPLICATION_DMR_ENCODED.equals(requestHeaders.getFirst(ACCEPT)) || APPLICATION_DMR_ENCODED.equals(requestHeaders.getFirst(CONTENT_TYPE)); try { dmr = isGet ? convertGetRequest(request) : convertPostRequest(http.getRequestBody(), encode); } catch (IllegalArgumentException iae) { ROOT_LOGGER.debugf("Unable to construct ModelNode '%s'", iae.getMessage()); http.sendResponseHeaders(INTERNAL_SERVER_ERROR, -1); return; } try { response = modelController.execute(new OperationBuilder(dmr).build()); } catch (Throwable t) { ROOT_LOGGER.modelRequestError(t); http.sendResponseHeaders(INTERNAL_SERVER_ERROR, -1); return; } if (response.hasDefined(OUTCOME) && FAILED.equals(response.get(OUTCOME).asString())) { status = INTERNAL_SERVER_ERROR; } boolean pretty = dmr.hasDefined("json.pretty") && dmr.get("json.pretty").asBoolean(); writeResponse(http, isGet, pretty, response, status, encode); }
private void drain(HttpExchange exchange) throws IOException { exchange.getRequestBody().close(); }
public void handle(HttpExchange http) throws IOException { /** * Request Verification - before the request is handled a set of checks are performed for CSRF * and XSS */ /* * Completely disallow OPTIONS - if the browser suspects this is a cross site request just reject it. */ final String requestMethod = http.getRequestMethod(); if (OPTIONS.equals(requestMethod)) { drain(http); http.sendResponseHeaders(METHOD_NOT_ALLOWED, -1); return; } /* * Origin check, if it is set the Origin header should match the Host otherwise reject the request. * * This check is for cross site scripted GET and POST requests. */ final Headers headers = http.getRequestHeaders(); final URI request = http.getRequestURI(); if (headers.containsKey(ORIGIN)) { String origin = headers.getFirst(ORIGIN); String host = headers.getFirst(HOST); String protocol = http.getHttpContext().getServer() instanceof HttpServer ? HTTP : HTTPS; String allowedOrigin = protocol + "://" + host; // This will reject multi-origin Origin headers due to the exact match. if (origin.equals(allowedOrigin) == false) { drain(http); http.sendResponseHeaders(FORBIDDEN, -1); return; } } /* * Cross Site Request Forgery makes use of a specially constructed form to pass in what appears to be * a valid operation request - except for upload requests any inbound requests where the Content-Type * is not application/json or application/dmr-encoded will be rejected. */ final boolean uploadRequest = UPLOAD_REQUEST.equals(request.getPath()); if (POST.equals(requestMethod)) { if (uploadRequest) { // This type of request doesn't need the content type check. processUploadRequest(http); return; } String contentType = extractContentType(headers.getFirst(CONTENT_TYPE)); if (!(APPLICATION_JSON.equals(contentType) || APPLICATION_DMR_ENCODED.equals(contentType))) { drain(http); http.sendResponseHeaders(FORBIDDEN, -1); return; } } processRequest(http); }