Mercurial
diff seobeo/s_network.c @ 119:c39582f937e5
[Seobeo Client] Added client side logic which will be used for all my other calls instead of curl.
| author | June Park <parkjune1995@gmail.com> |
|---|---|
| date | Wed, 07 Jan 2026 16:05:57 -0800 |
| parents | seobeo/s_linux_network.c@70401cf61e97 |
| children | 7eb79fd91c7e |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/seobeo/s_network.c Wed Jan 07 16:05:57 2026 -0800 @@ -0,0 +1,380 @@ +#include "seobeo/seobeo.h" + + +Seobeo_Handle *Seobeo_Stream_Handle_Server_Create(const char *host, const char* port) +{ + Seobeo_Handle *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 SO_REUSEADDR"); continue; } + +#ifdef SO_REUSEPORT + // SO_REUSEPORT allows multiple threads/processes to bind to the same port + // The kernel will distribute incoming connections among them + if (setsockopt(socket_fd, SOL_SOCKET, SO_REUSEPORT, &yes, sizeof(yes)) == -1) + { perror("setsockopt SO_REUSEPORT"); continue; } +#endif + + 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) + { + Seobeo_Log(SEOBEO_DEBUG, "Closing socket: %d\n", socket_fd); + perror("listen"); close(socket_fd); return NULL; + } + + int flags = fcntl(socket_fd, F_GETFL, 0); + if(fcntl(socket_fd, F_SETFL, flags | 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->destroyed = FALSE; + + return p_handle; +} + + +Seobeo_Handle *Seobeo_Stream_Handle_Client_Create(const char *host, const char* port, boolean use_tls) +{ + Seobeo_Handle *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; + + p_handle->ssl_ctx = NULL; + p_handle->ssl = NULL; + + if (use_tls) + { + if (Seobeo_SSL_Setup_Client(p_handle, host, socket_fd) != 0) + { + free(p_handle); + return 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->destroyed = FALSE; + + return p_handle; +} + +Seobeo_Handle *Seobeo_Stream_Handle_Server_Accept(Seobeo_Handle *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; + + // Set non blocking... + int flags = fcntl(client_fd, F_GETFL, 0); + if (flags == -1) return NULL; + fcntl(client_fd, F_SETFL, flags | O_NONBLOCK); + + Seobeo_Handle *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->read_buffer_used = 0; + p_client_handle->file = NULL; + p_client_handle->text_copy = NULL; + p_client_handle->file_name = NULL; + p_client_handle->destroyed = FALSE; + + return p_client_handle; +} + +void Seobeo_Handle_Destroy(Seobeo_Handle *p_handle) +{ + if (!p_handle) return; + + boolean expected = FALSE; + // Need to check + if (!atomic_compare_exchange_strong(&p_handle->destroyed, &expected, TRUE)) + { + return; + } + + if (p_handle->host) Dowa_Free(p_handle->host); + if (p_handle->port) Dowa_Free(p_handle->port); + + Seobeo_SSL_Cleanup(p_handle); + + if (p_handle->socket) { + Seobeo_Log(SEOBEO_DEBUG, "Closing handle socket: %d\n", p_handle->socket); + close(p_handle->socket); + } + + if (p_handle->read_buffer) Dowa_Free(p_handle->read_buffer); + if (p_handle->write_buffer) Dowa_Free(p_handle->write_buffer); + + Dowa_Free(p_handle); +} + + +int32 Seobeo_Handle_Flush(Seobeo_Handle *p_handle) +{ + uint32 total = p_handle->write_buffer_len; + uint32 sent = 0; + + Seobeo_Log(SEOBEO_DEBUG, "Write buffer total: %d\n", p_handle->write_buffer_len); + + while (sent < total) + { + if (p_handle->ssl) + { + int n = Seobeo_SSL_Write(p_handle, p_handle->write_buffer + sent, total - sent); + if (n < 0) return -1; + if (n == 0) return 0; // would block + sent += (uint32)n; + }else + { + Seobeo_Log(SEOBEO_DEBUG, "Flushing socket: %d\n", p_handle->socket); + 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_Handle *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 + Seobeo_Log(SEOBEO_DEBUG, "Write offset: %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 + Seobeo_Log(SEOBEO_DEBUG, "Write completed - offset: %d, data_size: %d\n", offset, data_size); + } + // DEBUG + Seobeo_Log(SEOBEO_DEBUG, "Total bytes written: %d\n", offset); + return 0; + } + + if (!p_handle) + { + Seobeo_Log(SEOBEO_ERROR, "p_handle is NULL before memcpy\n"); + return -1; + } + + if (!p_handle->write_buffer) + { + Seobeo_Log(SEOBEO_ERROR, "p_handle->write_buffer is NULL (len=%u, size=%u)\n", + p_handle->write_buffer_len, data_size); + return -1; + } + + Seobeo_Log(SEOBEO_DEBUG, "memcpy -> dest=%p (write_buffer=%p + offset=%u), src=%p, size=%u\n", + p_handle->write_buffer + p_handle->write_buffer_len, + p_handle->write_buffer, + p_handle->write_buffer_len, + data, + data_size); + + memcpy(p_handle->write_buffer + p_handle->write_buffer_len, + data, + data_size); + p_handle->write_buffer_len += data_size; + return 0; +} + +int32 Seobeo_Handle_Read(Seobeo_Handle *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 = Seobeo_SSL_Read(p_handle, p_handle->read_buffer + p_handle->read_buffer_len, free_space); + if (read_size < 0) return read_size; // -1 for error, -2 for closed + if (read_size == 0) return 0; // would block + } + 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_Handle *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); +}