<commit_before>#include "request.h"
#include "exceptions.h"
#include <cstring>

namespace REST {

const size_t Request::BUFFER_SIZE = 4096;

Request::Request(int client, struct sockaddr_storage client_addr) : handle(client), addr(client_addr) {
  time = std::chrono::high_resolution_clock::now();

  // TODO config
  struct timeval tv {
    .tv_sec = 30,
    .tv_usec = 0
  };

  // set timeout for reading
  setsockopt(handle, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(struct timeval));
}

void Request::process() {
  std::string line;
  bool is_header = true;
  char buffer[BUFFER_SIZE];
  ssize_t request_length;

  // receive data from client
  request_length = recv(handle, buffer, BUFFER_SIZE, 0);

  // handle timeout
  if (request_length <= 0)
    throw HTTP::RequestTimeout();

  // parse each line
  std::istringstream request_stream(buffer);

  // first line is HTTP header
  std::getline(request_stream, line);
  parse_header(line);

  // parse headers
  while (std::getline(request_stream, line)) {
    // next lines are headers, strip them
    line.erase(line.find_last_not_of(" \n\r\t")+1);

    // if line is empty, then content should follow
    if (line.size() == 0) {
      is_header = false;
      break;
    }

    // extract name and value from header
    size_t colon;
    std::string name = line.substr(0, colon = line.find(":"));
    std::string value = line.substr(colon+1);
    value.erase(0, value.find_first_not_of(" \n\r\t"));

    headers.emplace(name, value);
  }

  // TODO: handle long headers (longer than buffer)
  // if has content
  if (!is_header) {
    // assume proper content length
    size_t content_length = header("Content-Length", 0);

    // TODO: config
    if (content_length > 1024 * 1024 * 128)
      throw HTTP::PayloadTooLarge();

    if (content_length > 0) {
      raw.reserve(content_length);
      // read whats left in header
      length = std::min(content_length, (size_t)(request_length - request_stream.tellg()));
      raw.append(buffer + request_stream.tellg(), length);

      char* large_buffer = new char[content_length];

      // receive some more
      while (length < content_length) {
        memset(large_buffer, 0, content_length);
        ssize_t buffer_length = recv(handle, large_buffer, content_length, 0);

        if (buffer_length <= 0)
          throw HTTP::RequestTimeout();

        raw.append(large_buffer, buffer_length);
        length += buffer_length;
      }

      delete large_buffer;
    }
  }

  // if has some content
  if (!raw.empty()) {
    // try to parse it
    std::string ct = header("Content-Type", "");

    if (!ct.empty()) {
      if (ct == "application/x-www-form-urlencoded") {
        parse_query_string(raw);
      } else
      if (ct == "application/json" || ct == "text/json") {
        try {
          data = nlohmann::json::parse(raw);
        } catch (...) {
          throw HTTP::BadRequest();
        }
      }
    }
  }
}

void Request::parse_header(std::string const &line) {
  if (line.find("GET") == 0) {
    method = Method::GET;
  } else
  if (line.find("POST") == 0) {
    method = Method::POST;
  } else<commit_msg>no memory allocation for small requests<commit_after>#include "request.h"
#include "exceptions.h"
#include <cstring>

namespace REST {

const size_t Request::BUFFER_SIZE = 4096;

Request::Request(int client, struct sockaddr_storage client_addr) : handle(client), addr(client_addr) {
  time = std::chrono::high_resolution_clock::now();

  // TODO config
  struct timeval tv {
    .tv_sec = 30,
    .tv_usec = 0
  };

  // set timeout for reading
  setsockopt(handle, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(struct timeval));
}

void Request::process() {
  std::string line;
  bool is_header = true;
  char buffer[BUFFER_SIZE];
  ssize_t request_length;

  // receive data from client
  request_length = recv(handle, buffer, BUFFER_SIZE, 0);

  // handle timeout
  if (request_length <= 0)
    throw HTTP::RequestTimeout();

  // parse each line
  std::istringstream request_stream(buffer);

  // first line is HTTP header
  std::getline(request_stream, line);
  parse_header(line);

  // parse headers
  while (std::getline(request_stream, line)) {
    // next lines are headers, strip them
    line.erase(line.find_last_not_of(" \n\r\t")+1);

    // if line is empty, then content should follow
    if (line.size() == 0) {
      is_header = false;
      break;
    }

    // extract name and value from header
    size_t colon;
    std::string name = line.substr(0, colon = line.find(":"));
    std::string value = line.substr(colon+1);
    value.erase(0, value.find_first_not_of(" \n\r\t"));

    headers.emplace(name, value);
  }

  // TODO: handle long headers (longer than buffer)
  // if has content
  if (!is_header) {
    // assume proper content length
    size_t content_length = header("Content-Length", 0);

    // TODO: config
    if (content_length > 1024 * 1024 * 128)
      throw HTTP::PayloadTooLarge();

    if (content_length > 0) {
      raw.reserve(content_length);
      // read whats left in header
      length = std::min(content_length, (size_t)(request_length - request_stream.tellg()));
      raw.append(buffer + request_stream.tellg(), length);

      if (length < content_length) {
        char* large_buffer = new char[content_length];

        // receive some more
        while (length < content_length) {
          memset(large_buffer, 0, content_length);
          ssize_t buffer_length = recv(handle, large_buffer, content_length, 0);

          if (buffer_length <= 0)
            throw HTTP::RequestTimeout();

          raw.append(large_buffer, buffer_length);
          length += buffer_length;
        }

        delete large_buffer;
      }
    }
  }

  // if has some content
  if (!raw.empty()) {
    // try to parse it
    std::string ct = header("Content-Type", "");

    if (!ct.empty()) {
      if (ct == "application/x-www-form-urlencoded") {
        parse_query_string(raw);
      } else
      if (ct == "application/json" || ct == "text/json") {
        try {
          data = nlohmann::json::parse(raw);
        } catch (...) {
          throw HTTP::BadRequest();
        }
      }
    }
  }
}

void Request::parse_header(std::string const &line) {
  if (line.find("GET") == 0) {
    method = Method::GET;
  } else
  if (line.find("POST") == 0) {
    method = Method::POST;
  } else
{'fraction_non_alphanumeric': 0.13900642532518415, 'fraction_numerical': 0.010029775897194797, 'mean_word_length': 2.8353365384615383, 'pattern_counts': {'":': 2, '<': 12, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7619256', 'n_tokens_mistral': 2130, 'n_tokens_neox': 2027, 'n_words': 509}