<commit_before>#define _GNU_SOURCE
#include "http-protocol.h"
#include "http-mime-types.h"
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <time.h>
#include <signal.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
#include <netdb.h>
#include <arpa/inet.h>

/////////////////
// functions used by the Http protocol, internally

#define _http_(protocol) ((struct HttpProtocol*)(protocol))

#define is_hex(c)                                              \
  (((c) >= '0' && (c) <= '9') || ((c) >= 'a' && (c) <= 'f') || \
   ((c) >= 'A' && c <= 'F'))
#define hex_val(c) (((c) >= '0' && (c) <= '9') ? ((c)-48) : (((c) | 32) - 87))

#define is_num(c) ((c) >= '0' && (c) <= '9')
#define num_val(c) ((c)-48)

static char* Day2Str[] = {"Sun",  // the week starts on Sunday.
                          "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};

static char* Mon2Str[] = {"Jan",  // the year starts on January.
                          "Feb", "Mar", "Apr", "May", "Jun", "Jul",
                          "Aug", "Sep", "Oct", "Nov", "Dec"};

// reviewes the request and attempts to answer with a static file.
// returns 0 if no file was found, otherwise returns 1.
static int http_sendfile(struct HttpRequest* req) {
  static char* http_file_response_no_mime =
      "HTTP/1.1 200 OK\r\n"
      "Connection: keep-alive\r\n"
      "Keep-Alive: timeout=1\r\n"
      "Accept-Ranges: none\r\n"
      "Content-Length: %lu\r\n"
      "Date: %s, %d %s %04d %02d:%02d:%02d GMT\r\n"
      "Last-Modified: %s, %d %s %04d %02d:%02d:%02d GMT\r\n"
      "\r\n";

  static char* http_file_response =
      "HTTP/1.1 200 OK\r\n"
      "Connection: keep-alive\r\n"
      "Keep-Alive: timeout=1\r\n"
      "Accept-Ranges: none\r\n"
      "Content-Type: %s\r\n"
      "Content-Length: %lu\r\n"
      "Date: %s, %d %s %04d %02d:%02d:%02d GMT\r\n"
      "Last-Modified: %s, %d %s %04d %02d:%02d:%02d GMT\r\n"
      "\r\n";

  FILE* file;
  char* mime = NULL;
  char* ext = NULL;
  struct stat file_data = {};

  // collect the protocol path and the request's path length and data
  struct HttpProtocol* protocol =
      (struct HttpProtocol*)Server.get_protocol(req->server, req->sockfd);
  if (!protocol)
    return 1;
  size_t len = strlen(protocol->public_folder);
  // create and initialize the filename, including decoding the path
  char fname[strlen(req->path) + len + 1];
  memcpy(fname, protocol->public_folder, len);
  // if the ast character is a '/', step back.
  if (fname[len - 1] == '/' || fname[len - 1] == '\\')
    len--;
  // decode and review the request->path data
  int i = 0;
  while (req->path[i]) {
    if (req->path[i] == '+')  // decode space
      fname[len] = ' ';
    else if (req->path[i] == '%') {
      // decode hex value
      if (is_hex(req->path[i + 1]) && is_hex(req->path[i + 2])) {
        // this is a percent encoded value.
        fname[len] =
            (hex_val(req->path[i + 1]) * 16) + hex_val(req->path[i + 2]);
        i += 2;
      } else {
        // there was an error in the URL encoding... what to do? ignore?
        return 0;
      }
    } else
      fname[len] = req->path[i];
    len++;
    i++;
  }
  fname[len] = 0;
  i = 0;

  // scan path string for double dots (security - prevent path manipulation)
  // set the extention point value, while were doing so.
  while (fname[i]) {
    if (fname[i] == '.')
      ext = fname + i;
    // return false if we found a "/.." in our string.
    if (fname[i++] == '/' && fname[i++] == '.' && fname[i++] == '.')
      return 0;
  }

  // get file data (prevent folder access and get modification date)
  if (stat(fname, &file_data))
    return 0;
  // check that we have a file and not something else
  if (!S_ISREG(file_data.st_mode) && !S_ISLNK(file_data.st_mode))
    return 0;
  // fprintf(stderr, "looking for file: %s\n", fname);
  if ((file = fopen(fname, "rb"))) {
    // we will recycle len for the headers size and other things we want
    len = 0;

    // get the mime type (we have an ext pointer and the string isn't empty)
    if (ext && ext[1]) {
      mime = MimeType.find(ext + 1);
    }

    // Get a date data
    struct tm t_file;
    gmtime_r(&file_data.st_mtime, &t_file);
    struct tm t_now;
    gmtime_r(&Server.reactor(req->server)->last_tick, &t_now);

    // we now need to write some headers... we can recycle the ext pointer
    // for
    // the data
    if (HttpRequest.find(req, "RANGE") && ((ext = HttpRequest.value(req))) &&
        (ext[0] | 32) == 'b' && (ext[1] | 32) == 'y' && (ext[2] | 32) == 't' &&
        (ext[3] | 32) == 'e' && (ext[4] | 32) == 's' && (ext[5] | 32) == '=') {
      // ext holds the first range, starting on index 6 i.e. RANGE: bytes=0-1
      static char* http_range_response =
          "HTTP/1.1 206 Partial content\r\n"
          "Connection: keep-alive\r\n"
          "Keep-Alive: timeout=1\r\n"
          "%s%s%s"
          "Content-Length: %lu\r\n"
          "Date: %s, %d %s %04d %02d:%02d:%02d GMT\r\n"
          "Last-Modified: %s, %d %s %04d %02d:%02d:%02d GMT\r\n"
          "Accept-Ranges: bytes\r\n"
          "Content-Range: bytes %lu-%lu/%lu\r\n"
          "\r\n";
      size_t start = 0, finish = 0;
      ext = ext + 6;
      while (is_num(*ext)) {
        start = start * 10;
        start += num_val(*ext);
        ext++;
      }
      if (start >= file_data.st_size - 1)
        goto invalid_range;
      ext++;
      while (is_num(*ext)) {
        finish = finish * 10;
        finish += num_val(*ext);<commit_msg>Specify default cache control of 1 hour<commit_after>#define _GNU_SOURCE
#include "http-protocol.h"
#include "http-mime-types.h"
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <time.h>
#include <signal.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
#include <netdb.h>
#include <arpa/inet.h>

/////////////////
// functions used by the Http protocol, internally

#define _http_(protocol) ((struct HttpProtocol*)(protocol))

#define is_hex(c)                                              \
  (((c) >= '0' && (c) <= '9') || ((c) >= 'a' && (c) <= 'f') || \
   ((c) >= 'A' && c <= 'F'))
#define hex_val(c) (((c) >= '0' && (c) <= '9') ? ((c)-48) : (((c) | 32) - 87))

#define is_num(c) ((c) >= '0' && (c) <= '9')
#define num_val(c) ((c)-48)

static char* Day2Str[] = {"Sun",  // the week starts on Sunday.
                          "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};

static char* Mon2Str[] = {"Jan",  // the year starts on January.
                          "Feb", "Mar", "Apr", "May", "Jun", "Jul",
                          "Aug", "Sep", "Oct", "Nov", "Dec"};

// reviewes the request and attempts to answer with a static file.
// returns 0 if no file was found, otherwise returns 1.
static int http_sendfile(struct HttpRequest* req) {
  static char* http_file_response_no_mime =
      "HTTP/1.1 200 OK\r\n"
      "Connection: keep-alive\r\n"
      "Keep-Alive: timeout=1\r\n"
      "Accept-Ranges: none\r\n"
      "Content-Length: %lu\r\n"
      "Date: %s, %d %s %04d %02d:%02d:%02d GMT\r\n"
      "Last-Modified: %s, %d %s %04d %02d:%02d:%02d GMT\r\n"
      "Cache-Control: public, max-age=3600\r\n"
      "\r\n";

  static char* http_file_response =
      "HTTP/1.1 200 OK\r\n"
      "Connection: keep-alive\r\n"
      "Keep-Alive: timeout=1\r\n"
      "Accept-Ranges: none\r\n"
      "Content-Type: %s\r\n"
      "Content-Length: %lu\r\n"
      "Date: %s, %d %s %04d %02d:%02d:%02d GMT\r\n"
      "Last-Modified: %s, %d %s %04d %02d:%02d:%02d GMT\r\n"
      "Cache-Control: public, max-age=3600\r\n"
      "\r\n";

  FILE* file;
  char* mime = NULL;
  char* ext = NULL;
  struct stat file_data = {};

  // collect the protocol path and the request's path length and data
  struct HttpProtocol* protocol =
      (struct HttpProtocol*)Server.get_protocol(req->server, req->sockfd);
  if (!protocol)
    return 1;
  size_t len = strlen(protocol->public_folder);
  // create and initialize the filename, including decoding the path
  char fname[strlen(req->path) + len + 1];
  memcpy(fname, protocol->public_folder, len);
  // if the ast character is a '/', step back.
  if (fname[len - 1] == '/' || fname[len - 1] == '\\')
    len--;
  // decode and review the request->path data
  int i = 0;
  while (req->path[i]) {
    if (req->path[i] == '+')  // decode space
      fname[len] = ' ';
    else if (req->path[i] == '%') {
      // decode hex value
      if (is_hex(req->path[i + 1]) && is_hex(req->path[i + 2])) {
        // this is a percent encoded value.
        fname[len] =
            (hex_val(req->path[i + 1]) * 16) + hex_val(req->path[i + 2]);
        i += 2;
      } else {
        // there was an error in the URL encoding... what to do? ignore?
        return 0;
      }
    } else
      fname[len] = req->path[i];
    len++;
    i++;
  }
  fname[len] = 0;
  i = 0;

  // scan path string for double dots (security - prevent path manipulation)
  // set the extention point value, while were doing so.
  while (fname[i]) {
    if (fname[i] == '.')
      ext = fname + i;
    // return false if we found a "/.." in our string.
    if (fname[i++] == '/' && fname[i++] == '.' && fname[i++] == '.')
      return 0;
  }

  // get file data (prevent folder access and get modification date)
  if (stat(fname, &file_data))
    return 0;
  // check that we have a file and not something else
  if (!S_ISREG(file_data.st_mode) && !S_ISLNK(file_data.st_mode))
    return 0;
  // fprintf(stderr, "looking for file: %s\n", fname);
  if ((file = fopen(fname, "rb"))) {
    // we will recycle len for the headers size and other things we want
    len = 0;

    // get the mime type (we have an ext pointer and the string isn't empty)
    if (ext && ext[1]) {
      mime = MimeType.find(ext + 1);
    }

    // Get a date data
    struct tm t_file;
    gmtime_r(&file_data.st_mtime, &t_file);
    struct tm t_now;
    gmtime_r(&Server.reactor(req->server)->last_tick, &t_now);

    // we now need to write some headers... we can recycle the ext pointer
    // for
    // the data
    if (HttpRequest.find(req, "RANGE") && ((ext = HttpRequest.value(req))) &&
        (ext[0] | 32) == 'b' && (ext[1] | 32) == 'y' && (ext[2] | 32) == 't' &&
        (ext[3] | 32) == 'e' && (ext[4] | 32) == 's' && (ext[5] | 32) == '=') {
      // ext holds the first range, starting on index 6 i.e. RANGE: bytes=0-1
      static char* http_range_response =
          "HTTP/1.1 206 Partial content\r\n"
          "Connection: keep-alive\r\n"
          "Keep-Alive: timeout=1\r\n"
          "%s%s%s"
          "Content-Length: %lu\r\n"
          "Date: %s, %d %s %04d %02d:%02d:%02d GMT\r\n"
          "Last-Modified: %s, %d %s %04d %02d:%02d:%02d GMT\r\n"
          "Accept-Ranges: bytes\r\n"
          "Content-Range: bytes %lu-%lu/%lu\r\n"
          "Cache-Control: public, max-age=3600\r\n"
          "\r\n";
      size_t start = 0, finish = 0;
      ext = ext + 6;
      while (is_num(*ext)) {
        start = start * 10;
        start += num_val(*ext);
        ext++;
      }
      if (start >= file_data.st_size - 1)
        goto invalid_range;
      ext++;
      while (is_num(*ext)) {
        finish = finish * 10;
        finish += num_val(*ext);
{'fraction_non_alphanumeric': 0.19210292812777285, 'fraction_numerical': 0.024933451641526175, 'mean_word_length': 2.257514450867052, 'pattern_counts': {'":': 0, '<': 37, '<?xml version=': 0, '>': 71, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3666705', 'n_tokens_mistral': 4421, 'n_tokens_neox': 4177, 'n_words': 1152}