/**
   * 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);
    }
  }
  /**
   * 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);
  }
  /**
   * 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();
  }
  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);
  }