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 }