Mercurial
comparison 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 |
comparison
equal
deleted
inserted
replaced
| 118:249881ceff7b | 119:c39582f937e5 |
|---|---|
| 1 #include "seobeo/seobeo.h" | |
| 2 | |
| 3 | |
| 4 Seobeo_Handle *Seobeo_Stream_Handle_Server_Create(const char *host, const char* port) | |
| 5 { | |
| 6 Seobeo_Handle *p_handle; | |
| 7 struct addrinfo hints, *server_infos, *free_server_info; | |
| 8 int32 socket_fd, yes = 1; // Need this for setsockopt | |
| 9 | |
| 10 memset(&hints, 0, sizeof hints); | |
| 11 hints.ai_family = AF_UNSPEC; | |
| 12 hints.ai_socktype = SOCK_STREAM; | |
| 13 hints.ai_protocol = IPPROTO_TCP; | |
| 14 hints.ai_flags = AI_PASSIVE; | |
| 15 | |
| 16 if (getaddrinfo(host, port, &hints, &server_infos) != 0) | |
| 17 { perror("getaddrinfo"); return NULL; } | |
| 18 | |
| 19 for | |
| 20 ( | |
| 21 free_server_info = server_infos; | |
| 22 free_server_info != NULL; | |
| 23 free_server_info = free_server_info->ai_next | |
| 24 ) | |
| 25 { | |
| 26 if((socket_fd = socket(free_server_info->ai_family, | |
| 27 free_server_info->ai_socktype, free_server_info->ai_protocol)) == -1) | |
| 28 { perror("socket"); continue; } | |
| 29 | |
| 30 if (setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) == -1) | |
| 31 { perror("setsockopt SO_REUSEADDR"); continue; } | |
| 32 | |
| 33 #ifdef SO_REUSEPORT | |
| 34 // SO_REUSEPORT allows multiple threads/processes to bind to the same port | |
| 35 // The kernel will distribute incoming connections among them | |
| 36 if (setsockopt(socket_fd, SOL_SOCKET, SO_REUSEPORT, &yes, sizeof(yes)) == -1) | |
| 37 { perror("setsockopt SO_REUSEPORT"); continue; } | |
| 38 #endif | |
| 39 | |
| 40 if (bind(socket_fd, free_server_info->ai_addr, free_server_info->ai_addrlen) == -1) | |
| 41 { perror("v_network: Couldn't make socket non-blocking\n"); continue; } | |
| 42 | |
| 43 break; | |
| 44 } | |
| 45 | |
| 46 if (listen(socket_fd, 16) != 0) | |
| 47 { | |
| 48 Seobeo_Log(SEOBEO_DEBUG, "Closing socket: %d\n", socket_fd); | |
| 49 perror("listen"); close(socket_fd); return NULL; | |
| 50 } | |
| 51 | |
| 52 int flags = fcntl(socket_fd, F_GETFL, 0); | |
| 53 if(fcntl(socket_fd, F_SETFL, flags | O_NONBLOCK) != 0) { perror("fcntl"); return NULL; } | |
| 54 freeaddrinfo(server_infos); | |
| 55 | |
| 56 p_handle = malloc(sizeof(*p_handle)); | |
| 57 p_handle->socket = socket_fd; | |
| 58 p_handle->type = SEOBEO_STREAM_TYPE_SERVER; | |
| 59 p_handle->connected = FALSE; | |
| 60 | |
| 61 p_handle->host = host != NULL ? strdup(host) : "localhost"; | |
| 62 p_handle->port = strdup(port); | |
| 63 | |
| 64 p_handle->ssl_ctx = NULL; | |
| 65 p_handle->ssl = NULL; | |
| 66 | |
| 67 | |
| 68 p_handle->read_buffer = malloc(sizeof(*p_handle->read_buffer) * INITIAL_BUFFER_CAPACITY); | |
| 69 p_handle->read_buffer_capacity = INITIAL_BUFFER_CAPACITY; | |
| 70 p_handle->read_buffer_len = 0; | |
| 71 | |
| 72 p_handle->write_buffer = malloc(sizeof(*p_handle->read_buffer) * INITIAL_BUFFER_CAPACITY); | |
| 73 p_handle->write_buffer_capacity = INITIAL_BUFFER_CAPACITY; | |
| 74 p_handle->write_buffer_len = 0; | |
| 75 | |
| 76 p_handle->destroyed = FALSE; | |
| 77 | |
| 78 return p_handle; | |
| 79 } | |
| 80 | |
| 81 | |
| 82 Seobeo_Handle *Seobeo_Stream_Handle_Client_Create(const char *host, const char* port, boolean use_tls) | |
| 83 { | |
| 84 Seobeo_Handle *p_handle; | |
| 85 p_handle = malloc(sizeof(*p_handle)); | |
| 86 | |
| 87 struct addrinfo hints, *server_infos; | |
| 88 int32 socket_fd; // Need this for setsockopt | |
| 89 | |
| 90 memset(&hints, 0, sizeof hints); | |
| 91 hints.ai_family = AF_UNSPEC; | |
| 92 hints.ai_socktype = SOCK_STREAM; | |
| 93 | |
| 94 if (getaddrinfo(host, port, &hints, &server_infos) != 0) | |
| 95 { perror("getaddrinfo"); return NULL; } | |
| 96 | |
| 97 | |
| 98 if((socket_fd = socket(server_infos->ai_family, | |
| 99 server_infos->ai_socktype, server_infos->ai_protocol)) == -1) | |
| 100 { perror("socket"); return NULL; } | |
| 101 | |
| 102 if (connect(socket_fd, server_infos->ai_addr, server_infos->ai_addrlen) != 0) | |
| 103 { perror("connect"); return NULL; } | |
| 104 freeaddrinfo(server_infos); | |
| 105 | |
| 106 p_handle->socket = socket_fd; | |
| 107 p_handle->type = SEOBEO_STREAM_TYPE_CLIENT; | |
| 108 | |
| 109 p_handle->ssl_ctx = NULL; | |
| 110 p_handle->ssl = NULL; | |
| 111 | |
| 112 if (use_tls) | |
| 113 { | |
| 114 if (Seobeo_SSL_Setup_Client(p_handle, host, socket_fd) != 0) | |
| 115 { | |
| 116 free(p_handle); | |
| 117 return NULL; | |
| 118 } | |
| 119 } | |
| 120 p_handle->connected = TRUE; | |
| 121 | |
| 122 p_handle->host = host != NULL ? strdup(host) : "localhost"; | |
| 123 p_handle->port = strdup(port); | |
| 124 | |
| 125 p_handle->read_buffer = malloc(sizeof(*p_handle->read_buffer) * INITIAL_BUFFER_CAPACITY); | |
| 126 p_handle->read_buffer_capacity = INITIAL_BUFFER_CAPACITY; | |
| 127 p_handle->read_buffer_len = 0; | |
| 128 | |
| 129 p_handle->write_buffer = malloc(sizeof(*p_handle->read_buffer) * INITIAL_BUFFER_CAPACITY); | |
| 130 p_handle->write_buffer_capacity = INITIAL_BUFFER_CAPACITY; | |
| 131 p_handle->write_buffer_len = 0; | |
| 132 | |
| 133 p_handle->destroyed = FALSE; | |
| 134 | |
| 135 return p_handle; | |
| 136 } | |
| 137 | |
| 138 Seobeo_Handle *Seobeo_Stream_Handle_Server_Accept(Seobeo_Handle *p_server_handle) | |
| 139 { | |
| 140 struct sockaddr_storage addr; | |
| 141 socklen_t addrlen = sizeof addr; | |
| 142 char client_inet_addr[INET6_ADDRSTRLEN]; | |
| 143 | |
| 144 int client_fd = accept(p_server_handle->socket, | |
| 145 (struct sockaddr*)&addr, | |
| 146 &addrlen); | |
| 147 inet_ntop( | |
| 148 addr.ss_family, | |
| 149 Seobeo_Get_IP4_Or_IP6((struct sockaddr *)&addr), | |
| 150 client_inet_addr, sizeof client_inet_addr); | |
| 151 if (client_fd == -1) return NULL; | |
| 152 | |
| 153 // Set non blocking... | |
| 154 int flags = fcntl(client_fd, F_GETFL, 0); | |
| 155 if (flags == -1) return NULL; | |
| 156 fcntl(client_fd, F_SETFL, flags | O_NONBLOCK); | |
| 157 | |
| 158 Seobeo_Handle *p_client_handle = malloc(sizeof *p_client_handle); | |
| 159 | |
| 160 p_client_handle->socket = client_fd; | |
| 161 p_client_handle->type = SEOBEO_STREAM_TYPE_CLIENT; | |
| 162 p_client_handle->connected = TRUE; | |
| 163 | |
| 164 // TODO: support SSL in the future. | |
| 165 p_client_handle->ssl_ctx = NULL; | |
| 166 p_client_handle->ssl = NULL; | |
| 167 | |
| 168 p_client_handle->host = strdup(client_inet_addr); | |
| 169 p_client_handle->port = NULL; | |
| 170 | |
| 171 p_client_handle->read_buffer_capacity = p_server_handle->read_buffer_capacity; | |
| 172 p_client_handle->read_buffer_len = 0; | |
| 173 p_client_handle->read_buffer = malloc(p_client_handle->read_buffer_capacity); | |
| 174 | |
| 175 p_client_handle->write_buffer_capacity = p_server_handle->write_buffer_capacity; | |
| 176 p_client_handle->write_buffer_len = 0; | |
| 177 p_client_handle->write_buffer = malloc(p_client_handle->write_buffer_capacity); | |
| 178 | |
| 179 p_client_handle->read_buffer_used = 0; | |
| 180 p_client_handle->file = NULL; | |
| 181 p_client_handle->text_copy = NULL; | |
| 182 p_client_handle->file_name = NULL; | |
| 183 p_client_handle->destroyed = FALSE; | |
| 184 | |
| 185 return p_client_handle; | |
| 186 } | |
| 187 | |
| 188 void Seobeo_Handle_Destroy(Seobeo_Handle *p_handle) | |
| 189 { | |
| 190 if (!p_handle) return; | |
| 191 | |
| 192 boolean expected = FALSE; | |
| 193 // Need to check | |
| 194 if (!atomic_compare_exchange_strong(&p_handle->destroyed, &expected, TRUE)) | |
| 195 { | |
| 196 return; | |
| 197 } | |
| 198 | |
| 199 if (p_handle->host) Dowa_Free(p_handle->host); | |
| 200 if (p_handle->port) Dowa_Free(p_handle->port); | |
| 201 | |
| 202 Seobeo_SSL_Cleanup(p_handle); | |
| 203 | |
| 204 if (p_handle->socket) { | |
| 205 Seobeo_Log(SEOBEO_DEBUG, "Closing handle socket: %d\n", p_handle->socket); | |
| 206 close(p_handle->socket); | |
| 207 } | |
| 208 | |
| 209 if (p_handle->read_buffer) Dowa_Free(p_handle->read_buffer); | |
| 210 if (p_handle->write_buffer) Dowa_Free(p_handle->write_buffer); | |
| 211 | |
| 212 Dowa_Free(p_handle); | |
| 213 } | |
| 214 | |
| 215 | |
| 216 int32 Seobeo_Handle_Flush(Seobeo_Handle *p_handle) | |
| 217 { | |
| 218 uint32 total = p_handle->write_buffer_len; | |
| 219 uint32 sent = 0; | |
| 220 | |
| 221 Seobeo_Log(SEOBEO_DEBUG, "Write buffer total: %d\n", p_handle->write_buffer_len); | |
| 222 | |
| 223 while (sent < total) | |
| 224 { | |
| 225 if (p_handle->ssl) | |
| 226 { | |
| 227 int n = Seobeo_SSL_Write(p_handle, p_handle->write_buffer + sent, total - sent); | |
| 228 if (n < 0) return -1; | |
| 229 if (n == 0) return 0; // would block | |
| 230 sent += (uint32)n; | |
| 231 }else | |
| 232 { | |
| 233 Seobeo_Log(SEOBEO_DEBUG, "Flushing socket: %d\n", p_handle->socket); | |
| 234 ssize_t n = write( | |
| 235 p_handle->socket, | |
| 236 p_handle->write_buffer + sent, | |
| 237 total - sent | |
| 238 ); | |
| 239 if (n < 0) { | |
| 240 if (errno == EINTR) continue; | |
| 241 if (errno == EAGAIN) return 1; | |
| 242 return -1; | |
| 243 } | |
| 244 sent += (uint32)n; | |
| 245 } | |
| 246 } | |
| 247 | |
| 248 p_handle->write_buffer_len = 0; | |
| 249 return 0; | |
| 250 } | |
| 251 | |
| 252 int32 Seobeo_Handle_Queue(Seobeo_Handle *p_handle, const uint8 *data, uint32 data_size) | |
| 253 { | |
| 254 if (p_handle->write_buffer_len + data_size > p_handle->write_buffer_capacity) | |
| 255 { | |
| 256 int32 rc = Seobeo_Handle_Flush(p_handle); | |
| 257 if (rc < 0) return -1; | |
| 258 if (rc > 0) return 1; | |
| 259 } | |
| 260 | |
| 261 if (data_size > p_handle->write_buffer_capacity) | |
| 262 { | |
| 263 uint32 offset = 0; | |
| 264 while (offset < data_size) | |
| 265 { | |
| 266 ssize_t n = write(p_handle->socket, | |
| 267 data + offset, | |
| 268 data_size - offset); | |
| 269 if (n==0) | |
| 270 { | |
| 271 // DEBUG | |
| 272 Seobeo_Log(SEOBEO_DEBUG, "Write offset: %d\n", offset); | |
| 273 break; | |
| 274 } | |
| 275 if (n < 0) | |
| 276 { | |
| 277 if (errno == EINTR || errno == EAGAIN) | |
| 278 { | |
| 279 // DEBUG | |
| 280 // printf("Partial write, returning early (offset=%d)\n", offset); | |
| 281 continue; | |
| 282 } | |
| 283 if (errno == EAGAIN) return 1; | |
| 284 return -1; | |
| 285 } | |
| 286 offset += (uint32)n; | |
| 287 // DEBUG | |
| 288 Seobeo_Log(SEOBEO_DEBUG, "Write completed - offset: %d, data_size: %d\n", offset, data_size); | |
| 289 } | |
| 290 // DEBUG | |
| 291 Seobeo_Log(SEOBEO_DEBUG, "Total bytes written: %d\n", offset); | |
| 292 return 0; | |
| 293 } | |
| 294 | |
| 295 if (!p_handle) | |
| 296 { | |
| 297 Seobeo_Log(SEOBEO_ERROR, "p_handle is NULL before memcpy\n"); | |
| 298 return -1; | |
| 299 } | |
| 300 | |
| 301 if (!p_handle->write_buffer) | |
| 302 { | |
| 303 Seobeo_Log(SEOBEO_ERROR, "p_handle->write_buffer is NULL (len=%u, size=%u)\n", | |
| 304 p_handle->write_buffer_len, data_size); | |
| 305 return -1; | |
| 306 } | |
| 307 | |
| 308 Seobeo_Log(SEOBEO_DEBUG, "memcpy -> dest=%p (write_buffer=%p + offset=%u), src=%p, size=%u\n", | |
| 309 p_handle->write_buffer + p_handle->write_buffer_len, | |
| 310 p_handle->write_buffer, | |
| 311 p_handle->write_buffer_len, | |
| 312 data, | |
| 313 data_size); | |
| 314 | |
| 315 memcpy(p_handle->write_buffer + p_handle->write_buffer_len, | |
| 316 data, | |
| 317 data_size); | |
| 318 p_handle->write_buffer_len += data_size; | |
| 319 return 0; | |
| 320 } | |
| 321 | |
| 322 int32 Seobeo_Handle_Read(Seobeo_Handle *p_handle) | |
| 323 { | |
| 324 int32 read_size; | |
| 325 if (!p_handle) return -1; | |
| 326 | |
| 327 // How many bytes we can still read into the buffer | |
| 328 uint32 free_space = p_handle->read_buffer_capacity - p_handle->read_buffer_len; | |
| 329 if (free_space == 0) | |
| 330 return -1; | |
| 331 | |
| 332 if (p_handle->ssl) | |
| 333 { | |
| 334 read_size = Seobeo_SSL_Read(p_handle, p_handle->read_buffer + p_handle->read_buffer_len, free_space); | |
| 335 if (read_size < 0) return read_size; // -1 for error, -2 for closed | |
| 336 if (read_size == 0) return 0; // would block | |
| 337 } | |
| 338 else | |
| 339 { | |
| 340 read_size = (int32)read(p_handle->socket, | |
| 341 p_handle->read_buffer + p_handle->read_buffer_len, | |
| 342 free_space); | |
| 343 if (read_size == 0) return -2; | |
| 344 if (read_size < 0) | |
| 345 { | |
| 346 if (errno == EAGAIN || errno == EWOULDBLOCK) return 0; | |
| 347 return -1; | |
| 348 } | |
| 349 } | |
| 350 | |
| 351 p_handle->read_buffer_len += (uint32)read_size; | |
| 352 return read_size; | |
| 353 } | |
| 354 | |
| 355 void Seobeo_Handle_Consume(Seobeo_Handle *p_handle, uint32 consumed) | |
| 356 { | |
| 357 if (consumed >= p_handle->read_buffer_len) | |
| 358 { | |
| 359 p_handle->read_buffer_len = 0; | |
| 360 return; | |
| 361 } | |
| 362 | |
| 363 // Slide remaining bytes to the front | |
| 364 memmove( | |
| 365 p_handle->read_buffer, | |
| 366 p_handle->read_buffer + consumed, | |
| 367 p_handle->read_buffer_len - consumed | |
| 368 ); | |
| 369 p_handle->read_buffer_len -= consumed; | |
| 370 } | |
| 371 | |
| 372 void *Seobeo_Get_IP4_Or_IP6(struct sockaddr *sa) | |
| 373 { | |
| 374 if (sa->sa_family == AF_INET) | |
| 375 { | |
| 376 return &(((struct sockaddr_in*)sa)->sin_addr); | |
| 377 } | |
| 378 | |
| 379 return &(((struct sockaddr_in6*)sa)->sin6_addr); | |
| 380 } |