view seobeo/s_linux_network.c @ 22:947b81010aba

[Dowa & Seobeo] Updated so that Dowa hashmaps can use arena and not be broken. Split up web so taht it can handle different paths. Also fixes issues with hash collisions which was pain in the ass.
author June Park <parkjune1995@gmail.com>
date Tue, 07 Oct 2025 07:11:02 -0700
parents 875bb6e10db7
children c0f6c8c7829f
line wrap: on
line source

#include "seobeo/seobeo.h"


Seobeo_PHandle Seobeo_Stream_Handle_Server_Create(const char *host,  const char* port)
{
  Seobeo_PHandle p_handle;
  struct addrinfo hints, *server_infos, *free_server_info;
  int32 socket_fd, yes = 1;  // Need this for setsockopt 

  memset(&hints, 0, sizeof hints);
  hints.ai_family   = AF_UNSPEC;
  hints.ai_socktype = SOCK_STREAM;
  hints.ai_protocol = IPPROTO_TCP;
  hints.ai_flags    = AI_PASSIVE;

  if (getaddrinfo(host, port, &hints, &server_infos) != 0)
  { perror("getaddrinfo"); return NULL; }

  for
  (
    free_server_info = server_infos;
    free_server_info != NULL;
    free_server_info = free_server_info->ai_next
  )
  {
    if((socket_fd = socket(free_server_info->ai_family,
                        free_server_info->ai_socktype, free_server_info->ai_protocol)) == -1)
    { perror("socket"); continue; }

     if (setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) == -1)
     { perror("setsockopt"); continue; }

     if (bind(socket_fd, free_server_info->ai_addr, free_server_info->ai_addrlen) == -1)
     { perror("v_network: Couldn't make socket non-blocking\n"); continue; }

     break;
  }

  if (listen(socket_fd, 16) != 0)
  { perror("listen"); close(socket_fd); return NULL; }

	if(fcntl(socket_fd, F_SETFL, O_NONBLOCK) != 0) { perror("fcntl"); return NULL; }
  freeaddrinfo(server_infos);

  p_handle = malloc(sizeof(*p_handle));
  p_handle->socket = socket_fd;
  p_handle->type = SEOBEO_STREAM_TYPE_SERVER;
  p_handle->connected = FALSE;

  p_handle->host = host != NULL ? strdup(host) : "localhost";
  p_handle->port = strdup(port);

  p_handle->ssl_ctx              = NULL;
  p_handle->ssl                  = NULL;


  p_handle->read_buffer = malloc(sizeof(*p_handle->read_buffer) * INITIAL_BUFFER_CAPACITY);
  p_handle->read_buffer_capacity = INITIAL_BUFFER_CAPACITY;
  p_handle->read_buffer_len = 0;

  p_handle->write_buffer = malloc(sizeof(*p_handle->read_buffer) * INITIAL_BUFFER_CAPACITY);
  p_handle->write_buffer_capacity = INITIAL_BUFFER_CAPACITY;
  p_handle->write_buffer_len = 0;

  p_handle->file = NULL;
  p_handle->text_copy = NULL;
  p_handle->file_name = NULL;
  p_handle->destroyed = false;

  return p_handle;
}


Seobeo_PHandle Seobeo_Stream_Handle_Client_Create(const char *host,  const char* port, boolean use_tls)
{
  Seobeo_PHandle p_handle;
  p_handle = malloc(sizeof(*p_handle));

  struct addrinfo hints, *server_infos;
  int32 socket_fd;  // Need this for setsockopt 

  memset(&hints, 0, sizeof hints);
  hints.ai_family   = AF_UNSPEC;
  hints.ai_socktype = SOCK_STREAM;

  if (getaddrinfo(host, port, &hints, &server_infos) != 0)
  { perror("getaddrinfo"); return NULL; }


  if((socket_fd = socket(server_infos->ai_family,
                      server_infos->ai_socktype, server_infos->ai_protocol)) == -1)
  { perror("socket"); return NULL; }

  if (connect(socket_fd, server_infos->ai_addr, server_infos->ai_addrlen) != 0)
  { perror("connect"); return NULL; }
  freeaddrinfo(server_infos);

  p_handle->socket = socket_fd;
  p_handle->type = SEOBEO_STREAM_TYPE_CLIENT;
  if (use_tls)
  {
    printf("USE SSL\n\n");
    Seobeo_Web_SSL_Init();
    p_handle->ssl_ctx = SSL_CTX_new(TLS_client_method());
    SSL_CTX_set_default_verify_paths(p_handle->ssl_ctx);

    p_handle->ssl = SSL_new(p_handle->ssl_ctx);
    SSL_set_fd(p_handle->ssl, p_handle->socket);

    SSL_set_tlsext_host_name(p_handle->ssl, host);
    // Blocking for TSL handshake
  	fcntl(socket_fd, F_SETFL, 0);

    if (SSL_connect(p_handle->ssl) != 1)
    {
      fprintf(stderr, "SSL_connect failed\n");
      ERR_print_errors_fp(stderr);
      return NULL;
    }
  }else
  {
    p_handle->ssl_ctx = NULL;
    p_handle->ssl = NULL;
  }
  p_handle->connected = true;

  p_handle->host = host != NULL ? strdup(host) : "localhost";
  p_handle->port = strdup(port);

  p_handle->read_buffer = malloc(sizeof(*p_handle->read_buffer) * INITIAL_BUFFER_CAPACITY);
  p_handle->read_buffer_capacity = INITIAL_BUFFER_CAPACITY;
  p_handle->read_buffer_len = 0;

  p_handle->write_buffer = malloc(sizeof(*p_handle->read_buffer) * INITIAL_BUFFER_CAPACITY);
  p_handle->write_buffer_capacity = INITIAL_BUFFER_CAPACITY;
  p_handle->write_buffer_len = 0;

  p_handle->file = NULL;
  p_handle->text_copy = NULL;
  p_handle->file_name = NULL;
  p_handle->destroyed = false;

  return p_handle;
}

Seobeo_PHandle Seobeo_Stream_Handle_Server_Accept(Seobeo_PHandle p_server_handle)
{
  struct sockaddr_storage addr;
  socklen_t addrlen = sizeof addr;
  char client_inet_addr[INET6_ADDRSTRLEN];
  int client_fd = accept(p_server_handle->socket,
                         (struct sockaddr*)&addr,
                         &addrlen);
  inet_ntop(
      addr.ss_family,
      Seobeo_Get_IP4_Or_IP6((struct sockaddr *)&addr),
      client_inet_addr, sizeof client_inet_addr);

  if (client_fd == -1)
  {
    return NULL;
  }

  Seobeo_PHandle p_client_handle        = malloc(sizeof *p_client_handle);

  p_client_handle->socket               = client_fd;
  p_client_handle->type                 = SEOBEO_STREAM_TYPE_CLIENT;
  p_client_handle->connected            = true;

  // TODO: support SSL in the future.
  p_client_handle->ssl_ctx              = NULL;
  p_client_handle->ssl                  = NULL;

  p_client_handle->host                 = strdup(client_inet_addr);
  p_client_handle->port                 = NULL;

  p_client_handle->read_buffer_capacity = p_server_handle->read_buffer_capacity;
  p_client_handle->read_buffer_len      = 0;
  p_client_handle->read_buffer          = malloc(p_client_handle->read_buffer_capacity);

  p_client_handle->write_buffer_capacity = p_server_handle->write_buffer_capacity;
  p_client_handle->write_buffer_len      = 0;
  p_client_handle->write_buffer          = malloc(p_client_handle->write_buffer_capacity);

  p_client_handle->destroyed = false;

  return p_client_handle;
}

void Seobeo_Handle_Destroy(Seobeo_PHandle p_handle)
{
  if (!p_handle) return;

  bool expected = false;
  if (!atomic_compare_exchange_strong(&p_handle->destroyed, &expected, true))
  {
    // Already destroyed by another thread
    return;
  }

  if (p_handle->host) free(p_handle->host);
  if (p_handle->port) free(p_handle->port);

  if (p_handle->ssl)
  {
    SSL_shutdown(p_handle->ssl);
    SSL_free(p_handle->ssl);
    p_handle->ssl = NULL;
  }

  if (p_handle->ssl_ctx)
  {
    SSL_CTX_free(p_handle->ssl_ctx);
    p_handle->ssl_ctx = NULL;
  }

  if (p_handle->socket) close(p_handle->socket);

  if (p_handle->read_buffer) free(p_handle->read_buffer);
  if (p_handle->write_buffer) free(p_handle->write_buffer);

  if (p_handle->text_copy) free(p_handle->text_copy);
  if (p_handle->file_name) free(p_handle->file_name);

  free(p_handle);
}


int32 Seobeo_Handle_Flush(Seobeo_PHandle p_handle)
{
  uint32 total = p_handle->write_buffer_len;
  uint32 sent  = 0;

  printf("Total: %d\n\n", p_handle->write_buffer_len);

  while (sent < total)
  {
    if (p_handle->ssl)
    {
      int n = SSL_write(p_handle->ssl, p_handle->write_buffer + sent, total - sent);
      if (n < 0)
      {
        int err = SSL_get_error(p_handle->ssl, n);
        if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE)
        {
          // caller must wait for socket readiness and retry
          return 0;
        }
        ERR_print_errors_fp(stderr);
        return -1;
      }
      sent += (uint32)n;
    }else
    {
      ssize_t n = write(
        p_handle->socket,
        p_handle->write_buffer + sent,
        total - sent
      );
      if (n < 0) {
        if (errno == EINTR)  continue;
        if (errno == EAGAIN) return 1;
        return -1;
      }
      sent += (uint32)n;
    }
  }

  p_handle->write_buffer_len = 0;
  return 0;
}

int32 Seobeo_Handle_Queue(Seobeo_PHandle p_handle, const uint8 *data, uint32 data_size)
{
  if (p_handle->write_buffer_len + data_size > p_handle->write_buffer_capacity)
  {
    int32 rc = Seobeo_Handle_Flush(p_handle);
    if (rc < 0) return -1;
    if (rc > 0) return 1;
  }

  if (data_size > p_handle->write_buffer_capacity)
  {
    uint32 offset = 0;
    while (offset < data_size)
    {
      ssize_t n = write(p_handle->socket,
                data + offset,
                data_size - offset);
      if (n==0)
      {
        // DEBUG
        printf("NONE %d\n", offset);
        break;
      }
      if (n < 0)
      {
        if (errno == EINTR || errno == EAGAIN)
        {
          // DEBUG
          // printf("Partial write, returning early (offset=%d)\n", offset);
          continue;
        }
        if (errno == EAGAIN) return 1;
        return -1;
      }
      offset += (uint32)n;
      // DEBUG
      printf("\n\noffset: %d data_size: %d\n\n", offset, data_size);
    }
    // DEBUG
    printf("\n\nTotal: %d\n", offset);
    return 0;
  }

  memcpy(p_handle->write_buffer + p_handle->write_buffer_len,
         data,
         data_size);
  p_handle->write_buffer_len += data_size;
  // DEBUG
  printf("\nheader data_size: %d\n\n", data_size);
  return 0;
}

int32 Seobeo_Handle_Read(Seobeo_PHandle p_handle)
{
  int32 read_size;
  if (!p_handle) return -1;

  // How many bytes we can still read into the buffer
  uint32 free_space = p_handle->read_buffer_capacity - p_handle->read_buffer_len;
  if (free_space == 0)
    return -1;

  if (p_handle->ssl)
  {
    read_size = (int32)SSL_read(p_handle->ssl, p_handle->read_buffer,
                                free_space);
    if (read_size <= 0)
    {
      int err = SSL_get_error(p_handle->ssl, read_size);
      switch (err)
      {
        case SSL_ERROR_WANT_READ:
        case SSL_ERROR_WANT_WRITE:
          return 0;
        case SSL_ERROR_ZERO_RETURN:
        default:
          // TODO: Handle these errors
          return -2;
      }
    }
  }else
  {
    read_size = (int32)read(p_handle->socket,
                            p_handle->read_buffer + p_handle->read_buffer_len,
                            free_space);
    if (read_size == 0) return -2; 
    if (read_size < 0)
    {
      if (errno == EAGAIN || errno == EWOULDBLOCK) return 0;
      return -1;
    }
  }

  p_handle->read_buffer_len += (uint32)read_size;
  return read_size;
}

void Seobeo_Handle_Consume(Seobeo_PHandle p_handle, uint32 consumed)
{
  if (consumed >= p_handle->read_buffer_len)
  {
    p_handle->read_buffer_len = 0;
    return;
  }

  // Slide remaining bytes to the front
  memmove(
    p_handle->read_buffer,
    p_handle->read_buffer + consumed,
    p_handle->read_buffer_len - consumed
  );
  p_handle->read_buffer_len -= consumed;
}

void *Seobeo_Get_IP4_Or_IP6(struct sockaddr *sa)
{
  if (sa->sa_family == AF_INET) 
  {
    return &(((struct sockaddr_in*)sa)->sin_addr);
  }

  return &(((struct sockaddr_in6*)sa)->sin6_addr);
}