private HtmlResponse respond400(HtmlRequest htmlRequest) {
    HtmlResponse response400 = new HtmlResponse();

    response400.setStatus(htmlRequest.httpVersion, 400);
    response400.setContentTypeLine("text/html");
    String body400 =
        "<HTML><HEAD><TITLE>Bad Request</TITLE></HEAD>"
            + "<BODY><H1>Bad Request</H1><H2>You are not bad, only the request, and requests can change.</H2></BODY></HTML>";
    response400.setEntityBody(body400.getBytes());

    return response400;
  }
  private HtmlResponse respond403(HtmlRequest htmlRequest) {
    HtmlResponse response403 = new HtmlResponse();

    response403.setStatus(htmlRequest.httpVersion, 403);
    response403.setContentTypeLine("text/html");
    String body403 =
        "<HTML><HEAD><TITLE>403 Forbidden</TITLE></HEAD>"
            + "<BODY><H1>Forbidden Request</H1><H2>How can something so wrong feel so right?</H2></BODY></HTML>";
    response403.setEntityBody(body403.getBytes());

    return response403;
  }
  private HtmlResponse respond500(HtmlRequest htmlRequest) {
    HtmlResponse response500 = new HtmlResponse();

    response500.setStatus(htmlRequest.httpVersion, 500);
    response500.setContentTypeLine("text/html");
    String body500 =
        "<HTML><HEAD><TITLE>Internal Server Error</TITLE></HEAD>"
            + "<BODY><H1>Internal Server Error</H1><H2>This is totally our fault, so relax.</H2></BODY></HTML>";
    response500.setEntityBody(body500.getBytes());

    return response500;
  }
  private HtmlResponse respond501(HtmlRequest htmlRequest) {
    HtmlResponse response501 = new HtmlResponse();

    response501.setStatus(htmlRequest.httpVersion, 501);
    response501.setContentTypeLine("text/html");
    String body501 =
        "<HTML><HEAD><TITLE>Unknown Method</TITLE></HEAD>"
            + "<BODY><H1>Unknown Method</H1><H2>But may you find knowledge in your path</H2></BODY></HTML>";
    response501.setEntityBody(body501.getBytes());

    return response501;
  }
  private HtmlResponse respond404(HtmlRequest htmlRequest) {
    HtmlResponse response404 = new HtmlResponse();

    response404.setStatus(htmlRequest.httpVersion, 404);
    response404.setContentTypeLine("text/html");
    String body404 =
        "<HTML><HEAD><TITLE>404 Not Found</TITLE></HEAD>"
            + "<BODY><H1>File Not Found</H1><H2>But have a nice day</H2></BODY></HTML>";
    response404.setEntityBody(body404.getBytes());

    return response404;
  }
  private void sendEntityBodyToClient(
      DataOutputStream socketOutputStream, HtmlResponse htmlResponse, boolean isChunked)
      throws IOException {

    byte[] content = htmlResponse.getEntityBody();

    if (!isChunked) {
      try {
        socketOutputStream.write(content, 0, content.length);
        socketOutputStream.flush();
      } catch (IOException e) {
        System.out.println("Writing the answer caused an error" + e.toString());
      }
    } else {

      int currentIndexStart = 0;
      int currentIndexEnd = Math.min(CHUNCKED_BYTES - 1, content.length - 1);
      int lengthOfBytesSent = currentIndexEnd - currentIndexStart + 1;

      while (currentIndexStart < content.length - 1) {
        socketOutputStream.writeBytes(Integer.toHexString(lengthOfBytesSent) + CRLF);
        socketOutputStream.write(content, currentIndexStart, lengthOfBytesSent);
        socketOutputStream.writeBytes(CRLF);
        socketOutputStream.flush();

        currentIndexStart = currentIndexEnd + 1;
        currentIndexEnd = Math.min(currentIndexStart + CHUNCKED_BYTES - 1, content.length - 1);
        lengthOfBytesSent = currentIndexEnd - currentIndexStart + 1;
      }

      socketOutputStream.writeBytes("0" + CRLF);
      socketOutputStream.writeBytes(CRLF);
      socketOutputStream.flush();
    }
  }
  private HtmlResponse respond200(HtmlRequest htmlRequest)
      throws IOException, InterruptedException {
    HtmlResponse response200 = new HtmlResponse();
    byte[] bodyInBytes;

    if (htmlRequest.type.equals("TRACE")) {
      bodyInBytes = htmlRequest.unparsedRequest.getBytes();
    } else if (htmlRequest.type.equals("POST")) {
      if (htmlRequest.requestedFile.equals("/params_info.html")) {
        bodyInBytes = makeTable(htmlRequest.parametersInRequestBody);
      } else if (htmlRequest.requestedFile.equals(execResults)) {
        System.out.println(
            "Parameters for Crawler : " + htmlRequest.parametersInRequestBody.toString());
        if (serverCrawler.isBusy()) {
          bodyInBytes = prepareDefaultPage("Crawler is busy");
        } else {
          String crawlerInputCheckResults = checkCrawlerInput(htmlRequest);
          if (crawlerInputCheckResults == null) {
            bodyInBytes = activateCrawler(htmlRequest);
            Thread crawlerThread = new Thread(serverCrawler);
            crawlerThread.start();
          } else {
            bodyInBytes = prepareDefaultPage(crawlerInputCheckResults);
          }
        }
      } else {
        bodyInBytes = null;
      }
    } else {
      bodyInBytes = readFileForResponse(htmlRequest);
    }

    response200.setEntityBody(bodyInBytes);
    response200.setStatus(htmlRequest.httpVersion, 200);
    String contentType;

    if (!htmlRequest.type.equals("POST")) {
      contentType = getContentTypeFromFile(htmlRequest.requestedFile);
    } else {
      contentType = getContentTypeFromFile(htmlRequest.requestedFile);
    }

    response200.setContentTypeLine(contentType);

    return response200;
  }
  private void processRequest() throws Exception {
    while (true) {

      Socket socket = socketRequestsQueue.take();

      DataOutputStream socketOutputStream = new DataOutputStream(socket.getOutputStream());

      HtmlRequest htmlRequest = readRequest(socket);

      // If the request is empty than the socket was closed on the other side
      if (htmlRequest.equals(null)) {

        try {
          socket.close();
        } catch (Exception e) {
          System.out.println("Error on trying to close socket on empty request: " + e.toString());
        }
        continue;
      }

      HtmlResponse responseToClient;

      if (!htmlRequest.isLegalRequest) {
        // The request format is illegal
        responseToClient = respond400(htmlRequest);
      } else if (!legalRequestType(htmlRequest)) {
        // The request method is unimplemented
        responseToClient = respond501(htmlRequest);
      } else if (directRequestToResultPages(htmlRequest)) {
        responseToClient = respond403(htmlRequest);
      } else {
        if (!htmlRequest.type.equals("TRACE") && !htmlRequest.type.equals("POST")) {
          boolean isFileLegal = false;
          try {
            isFileLegal = checkIfRequestedFileLegal(htmlRequest);
          } catch (IOException e) {
            responseToClient = respond500(htmlRequest);
          }
          if (!isFileLegal) {
            responseToClient = respond404(htmlRequest);
          } else {
            responseToClient = respond200(htmlRequest);
          }
        } else {
          responseToClient = respond200(htmlRequest);
        }
      }

      try {
        // Send the status line.
        socketOutputStream.writeBytes(responseToClient.getStatusLine());

        // Send the content type line.
        socketOutputStream.writeBytes(responseToClient.getContentType());

        // Send content length.
        if (!htmlRequest.isChunked) {
          socketOutputStream.writeBytes(responseToClient.getContentLengthLine());
        }

        if (htmlRequest.isChunked) {
          socketOutputStream.writeBytes(responseToClient.getTransferEncoding());
        }
        // Send a blank line to indicate the end of the header lines.
        socketOutputStream.writeBytes(CRLF);

      } catch (Exception e) {
        System.out.println("Writing the header caused an error" + e.toString());
      }

      // Send the content of the HTTP.
      if (!htmlRequest.type.equals("HEAD")) {
        sendEntityBodyToClient(socketOutputStream, responseToClient, htmlRequest.isChunked);
      }

      // Close streams and socket.
      try {
        socketOutputStream.close();
        socket.close();
      } catch (Exception e) {
        System.out.println("closing the socket caused an error");
      }
    }
  }