8 n__network_8h
gullradriel edited this page 2026-11-02 12:42:25 +01:00

include/nilorea/n_network.h

Network Engine. More...

Structures

Name
struct N_WS_MESSAGE
WebSocket message.
struct N_WS_FRAME
A single parsed WebSocket frame (RFC 6455).
struct N_WS_CONN
WebSocket connection.
struct N_URL_PARAM
a single query parameter key=value pair
struct N_URL
parsed URL components
struct N_SSE_EVENT
SSE event received from server.
struct N_SSE_CONN
SSE connection handle.
struct N_SOCKET
Structure of a N_SOCKET.
struct N_PROXY_CFG
Parsed proxy URL components.
struct N_MOCK_SERVER
mock HTTP server handle
struct N_HTTP_RESPONSE
HTTP response to send from mock server callback.
struct N_HTTP_REQUEST
parsed HTTP request for mock server callback
struct N_HTTP_MULTIPART_PART
a single part of a parsed multipart/form-data body
struct NETWORK_POOL
structure of a network pool
struct NETWORK_HTTP_INFO
structure for splitting HTTP requests
struct NETWORK
Structure of a NETWORK.

Detailed Description

Network Engine.

Author: Castagnier Mickael

Version: 2.0

Date: 11/03/2015

Source code

/*
 * Nilorea Library
 * Copyright (C) 2005-2026 Castagnier Mickael
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 * implied. See the License for the specific language governing
 * permissions and limitations under the License.
 *
 * SPDX-License-Identifier: Apache-2.0
 */


#ifndef N_NETWORK
#define N_NETWORK

#ifdef __cplusplus
extern "C" {
#endif


#include "n_common.h"
#include "n_str.h"
#include "n_list.h"
#include "n_hash.h"

#define NETWORK_DISABLE_ZERO_LENGTH_RECV FALSE
#define NETWORK_IPALL 0
#define NETWORK_IPV4 1
#define NETWORK_IPV6 2
#define NETWORK_TCP 0
#define NETWORK_UDP 1
#define NETWORK_DEPLETE_SOCKET_TIMEOUT 512
#define NETWORK_DEPLETE_QUEUES_TIMEOUT 1024
#define NETWORK_CONSECUTIVE_SEND_WAIT 2048
#define NETWORK_WAIT_CLOSE_TIMEOUT 4096
#define NETW_SOCKOPT_TIMEO_MS 1
#define NETWORK_HANDSHAKE_TIMEOUT 8192
#define HEAD_SIZE 10
#define HEAD_CODE 3
#define NETW_SOCKET_ERROR -1
#define NETW_SOCKET_DISCONNECTED -2
#define NETW_IO_WANT_READ -10
#define NETW_IO_WANT_WRITE -11
#define NETW_MAX_RETRIES 8
#define NETW_RETRY_DELAY 1000
#ifndef NETW_MSG_MAX_FRAME_SIZE
#define NETW_MSG_MAX_FRAME_SIZE (1024UL * 1024UL * 1024UL)
#endif
#define NETW_THR_EXIT_OK 100
#define NETW_THR_EXIT_ERROR 666

#ifndef SOCKET
#ifdef __windows__
#ifndef ARCH32BITS
typedef long long unsigned int SOCKET;
#define SOCKET_SIZE_FORMAT "%zu"
#endif
#else
typedef int SOCKET;
#define SOCKET_SIZE_FORMAT "%d"
#endif
#endif

#if defined(__linux__) || defined(__sun) || defined(_AIX)
#include <sys/types.h>
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <malloc.h>
#include <string.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <semaphore.h>

#define SOCKET_SIZE_FORMAT "%d"

#ifdef __linux__
#include <linux/sockios.h>
#include <signal.h>
#endif

#define netw_unload()

#define closesocket close
#define NETFLAGS MSG_NOSIGNAL /* for program to not quit under linux when a connection is crashing 2 times */
#define INVALID_SOCKET -1
#define NETW_BUFLEN_CAST(x) ((size_t)(x))

#include "n_time.h"

#elif defined __windows__

#define SHUT_WR SD_SEND
#define SHUT_RD SD_RECEIVE
#define SHUT_RDWR SD_BOTH

#ifndef ECONNRESET
#define ECONNRESET 104
#endif

#include "nilorea/n_windows.h"

#include <errno.h>
#include <unistd.h>
#include <malloc.h>
#include <string.h>
#include "n_time.h"

#ifndef MSG_EOR
#define MSG_EOR 0
#endif

#ifndef MSG_NOSIGNAL
#define MSG_NOSIGNAL 0
#endif

#ifndef MSG_WAITALL
#define MSG_WAITALL 0
#endif

#ifndef EAI_SYSTEM
#define EAI_SYSTEM 0
#endif

#ifndef AI_PASSIVE
#define AI_PASSIVE 0x00000001
#endif
#ifndef AI_CANONNAME
#define AI_CANONNAME 0x00000002
#endif
#ifndef AI_NUMERICHOST
#define AI_NUMERICHOST 0x00000004
#endif
#ifndef AI_NUMERICSERV
#define AI_NUMERICSERV 0x00000008
#endif
#ifndef AI_ALL
#define AI_ALL 0x00000100
#endif
#ifndef AI_ADDRCONFIG
#define AI_ADDRCONFIG 0x00000400
#endif
#ifndef AI_V4MAPPED
#define AI_V4MAPPED 0x00000800
#endif
#ifndef AI_NON_AUTHORITATIVE
#define AI_NON_AUTHORITATIVE 0x00004000
#endif
#ifndef AI_SECURE
#define AI_SECURE 0x00008000
#endif
#ifndef AI_RETURN_PREFERRED_NAMES
#define AI_RETURN_PREFERRED_NAMES 0x00010000
#endif

#include <pthread.h>
#include <semaphore.h>

#define NETFLAGS 0 /* no flags needed for microsoft */

#define NETW_BUFLEN_CAST(x) ((int)(x))

#define netw_unload() netw_init_wsa(0, 2, 2)

#endif

#ifdef HAVE_OPENSSL
#define _OPEN_SYS_SOCK_IPV6 1
#include "nilorea/n_openssl_compat.h"
#include <openssl/err.h>
#include <openssl/ssl.h>
#include <openssl/crypto.h>
/* OpenSSL 3.0 renamed SSL_get_peer_certificate() to SSL_get1_peer_certificate()
 * and deprecated the old spelling. Both hand back a certificate whose reference
 * count is already incremented, so the caller X509_free()s it either way. Use
 * the 3.0 name everywhere and map it back on OpenSSL 1.x and LibreSSL, which
 * report an OPENSSL_VERSION_NUMBER below 0x30000000L. */
#if OPENSSL_VERSION_NUMBER < 0x30000000L
#ifndef SSL_get1_peer_certificate
#define SSL_get1_peer_certificate(ssl) SSL_get_peer_certificate(ssl)
#endif
#endif

#define NETW_HAVE_TLS13 N_OSSL_HAVE_TLS13

#if NETW_HAVE_TLS13
#define NETW_TLS_MAX_VERSION TLS1_3_VERSION
#else
#define NETW_TLS_MAX_VERSION TLS1_2_VERSION
#endif

#if !N_OSSL_HAVE_SSL_SET1_HOST
/* SSL_set_hostflags() and SSL_set1_host() arrived in OpenSSL 1.1.0 (LibreSSL
 * 2.9). Before that the same thing is reached through the connection's
 * X509_VERIFY_PARAM, which is all the newer calls are: thin wrappers over it.
 * Both the parameter object and the two X509_VERIFY_PARAM_* entry points below
 * exist in OpenSSL 1.0.2 (declared in <openssl/x509_vfy.h>, which the
 * <openssl/ssl.h> above pulls in), so the shims are exact, and call sites need
 * no version guard of their own. */

static inline void SSL_set_hostflags(SSL* ssl, unsigned int flags) {
    X509_VERIFY_PARAM* param = SSL_get0_param(ssl);
    if (param)
        X509_VERIFY_PARAM_set_hostflags(param, flags);
}

static inline int SSL_set1_host(SSL* ssl, const char* host) {
    X509_VERIFY_PARAM* param = SSL_get0_param(ssl);
    if (!param)
        return 0;
    return X509_VERIFY_PARAM_set1_host(param, host, 0);
}
#endif
#endif

#define N_ENUM_netw_code_type(_)     \
    _(NETW_CLIENT, 2)                \
    _(NETW_SERVER, 4)                \
    _(NETW_RESTART_TIMER, 8)         \
    _(NETW_EMPTY_SENDBUF, 16)        \
    _(NETW_EMPTY_RECVBUF, 32)        \
    _(NETW_RUN, 64)                  \
    _(NETW_EXIT_ASKED, 128)          \
    _(NETW_EXITED, 256)              \
    _(NETW_ERROR, 512)               \
    _(NETW_ENCRYPT_NONE, 1024)       \
    _(NETW_ENCRYPT_OPENSSL, 2048)    \
    _(NETW_THR_ENGINE_STARTED, 4096) \
    _(NETW_THR_ENGINE_STOPPED, 8192) \
    _(NETW_DESTROY_RECVBUF, 16384)   \
    _(NETW_DESTROY_SENDBUF, 32768)   \
    _(NETW_COMPRESSED_ZLIB, 65536)   \
    _(NETW_COMPRESSED_LZ4, 131072)

#define NETW_COMPRESSED NETW_COMPRESSED_ZLIB

typedef enum NETW_COMPRESS_MODE {
    NETW_COMPRESS_NONE = 0,
    NETW_COMPRESS_ZLIB = 1,
    NETW_COMPRESS_LZ4 = 2
} NETW_COMPRESS_MODE;

#define NETW_COMPRESS_THRESHOLD 256u
#define NETW_COMPRESS_MIN_RATIO 10u

N_ENUM_DECLARE(N_ENUM_netw_code_type, __netw_code_type);

typedef ssize_t (*netw_func)(void*, char*, uint32_t);

typedef struct N_SOCKET {
    char* port;
    SOCKET sock;
    char* ip;

    unsigned long int is_blocking;

    struct addrinfo hints;
    struct addrinfo* rhost;
    struct addrinfo* connect_rp;
    struct sockaddr_storage raddr;
} N_SOCKET;

typedef struct NETWORK {
    int nb_pending,
        mode,
        threaded_engine_status,
        addr_infos_loaded,
        send_queue_consecutive_wait,
        so_reuseaddr,
        /* disabled so reuseport state */
        // so_reuseport,
        so_keepalive,
        tcpnodelay,
        so_sndbuf,
        so_rcvbuf,
        so_sndtimeo,
        so_rcvtimeo,
        so_linger,
        crypto_mode,
        crypto_algo,
        user_id,
        nb_running_threads,
        deplete_queues_timeout,
        deplete_socket_timeout,
        wait_close_timeout,
        handshake_timeout,
        transport_type;

    uint32_t state;

    netw_func send_data,
        recv_data,
        send_data_once,
        recv_data_once;

#ifdef HAVE_OPENSSL
    const SSL_METHOD* method;
    SSL_CTX* ctx;
    SSL* ssl;
    char *certificate,
        *key;
#endif

    N_SOCKET link;

    LIST* send_buf;
    LIST* recv_buf;

    pthread_t send_thr;
    pthread_t recv_thr;

    pthread_mutex_t sendbolt;
    pthread_mutex_t recvbolt;
    pthread_mutex_t eventbolt;
    sem_t send_blocker;

    LIST* pools;

    char netw_errors[8][512];
    int netw_err_count;
    int netw_err_next;

    int compress_mode;

    int reactor_mode;

    void* reactor_handle;

    /* Incremental recv state for reactor mode. Unused by thread
     * mode (`netw_recv_func` reads blocking). The state machine
     * has three phases: STATE word (4 B), LENGTH word (4 B),
     * PAYLOAD (length B), then push to recv_buf and reset. Per-frame
     * state lives across multiple readable events because TCP can
     * deliver any prefix of a frame before EAGAIN. */
    int reactor_read_phase;                
    unsigned char reactor_read_hdr_buf[4]; 
    int reactor_read_hdr_have;             
    uint32_t reactor_read_pkt_state;       
    uint32_t reactor_read_pkt_length;      
    char* reactor_read_payload;            
    size_t reactor_read_payload_have;      

    /* Incremental send state for reactor mode. Mirrors
     * `netw_send_func`'s output: each queued N_STR is pre-framed
     * (4-byte state + 4-byte length + payload, with optional
     * compression) into a contiguous buffer; the reactor sends as
     * the kernel buffer accepts bytes, tracking offset across
     * multiple writable events. `reactor_write_armed` mirrors
     * whether EPOLLOUT is in the registered events, used to
     * arm/disarm exactly once per back-pressure cycle. */
    char* reactor_send_buf;  
    size_t reactor_send_len; 
    size_t reactor_send_off; 
    int reactor_write_armed; 
    int reactor_send_wants_read;
    int reactor_recv_wants_write;

    int in_dirty_list;

    int reactor_close_acked;

    int reactor_registered;

    long long connect_dns_usec;
    long long connect_tcp_usec;

} NETWORK;

#define netw_atomic_read_state(netw) __atomic_load_n(&(netw)->state, __ATOMIC_ACQUIRE)

#define netw_atomic_write_state(netw, val) __atomic_store_n(&(netw)->state, (val), __ATOMIC_RELEASE)

#define netw_atomic_read_reactor_mode(netw) __atomic_load_n(&(netw)->reactor_mode, __ATOMIC_ACQUIRE)
#define netw_atomic_write_reactor_mode(netw, val) __atomic_store_n(&(netw)->reactor_mode, (val), __ATOMIC_RELEASE)
#define netw_atomic_read_reactor_handle(netw) __atomic_load_n(&(netw)->reactor_handle, __ATOMIC_ACQUIRE)
#define netw_atomic_write_reactor_handle(netw, val) __atomic_store_n(&(netw)->reactor_handle, (val), __ATOMIC_RELEASE)

typedef struct NETWORK_POOL {
    HASH_TABLE* pool;

    pthread_rwlock_t rwlock;

} NETWORK_POOL;

typedef struct NETWORK_HTTP_INFO {
    char content_type[256];
    size_t content_length;
    char* body;
    char* type;
} NETWORK_HTTP_INFO;

size_t htonst(size_t value);
size_t ntohst(size_t value);
#ifdef HAVE_OPENSSL
int netw_set_crypto(NETWORK* netw, char* key, char* certificate);
int netw_set_crypto_pem(NETWORK* netw, const char* key_pem, const char* cert_pem);
int netw_set_crypto_chain(NETWORK* netw, char* key, char* certificate, char* ca_file);
int netw_set_crypto_chain_pem(NETWORK* netw, const char* key_pem, const char* cert_pem, const char* ca_pem);
int netw_ssl_set_ca(NETWORK* netw, const char* ca_file, const char* ca_path);
int netw_ssl_set_verify(NETWORK* netw, int enable);
int netw_ssl_set_versions(NETWORK* netw, int min_version, int max_version);
int netw_ssl_set_ciphers(NETWORK* netw, const char* cipher_list, const char* ciphersuites);
SSL_CTX* netw_ssl_server_ctx_new(const char* key_file, const char* cert_chain_file);
int netw_ssl_get_verify_result(NETWORK* netw, const char* expected_host, char* errbuf, size_t errsz);
int netw_ssl_set_client_cert(NETWORK* netw, const char* cert_file, const char* key_file);
int netw_init_openssl(void);
typedef int (*netw_sni_pick_cb)(const char* sni, N_STR** cert_pem, N_STR** key_pem, void* user_data);
const char* netw_ssl_get_sni(NETWORK* netw);
int netw_set_crypto_pem_ctx(NETWORK* netw, const N_STR* key_pem, const N_STR* cert_pem);
NETWORK* netw_accept_ssl_with_sni_cb(NETWORK* listen, netw_sni_pick_cb pick, void* user_data);
int netw_ssl_server_handshake(NETWORK* netw, netw_sni_pick_cb pick, void* user_data);
int netw_ssl_server_handshake_ex(NETWORK* netw, netw_sni_pick_cb pick, void* user_data, const char* const* alpn, size_t alpn_count);
int netw_ssl_set_alpn(NETWORK* netw, const char* const* protos, size_t count);
int netw_ssl_get_alpn(NETWORK* netw, char* out, size_t out_len);
int netw_unload_openssl(void);
int netw_ssl_connect(NETWORK** netw, char* host, char* port, int ip_version, char* ssl_key_file, char* ssl_cert_file);
int netw_ssl_connect_client(NETWORK** netw, char* host, char* port, int ip_version);
int netw_ssl_connect_client_to(NETWORK** netw, char* host, char* port, int ip_version, int connect_timeout_ms);
int netw_ssl_start_client(NETWORK* netw);
int netw_ssl_do_handshake(NETWORK* netw, const char* sni_hostname);
int netw_ssl_handshake_once(NETWORK* netw, const char* sni_hostname);
int netw_ssl_server_handshake_once(NETWORK* netw, SSL_CTX* ctx);
ssize_t send_ssl_data(void* netw, char* buf, uint32_t n);
ssize_t recv_ssl_data(void* netw, char* buf, uint32_t n);
ssize_t send_ssl_data_once(void* netw, char* buf, uint32_t n);
ssize_t recv_ssl_data_once(void* netw, char* buf, uint32_t n);
#endif
int netw_init_wsa(int mode, int v1, int v2);
int netw_set(NETWORK* netw, int flag);
int netw_get_state(NETWORK* netw, uint32_t* state, int* thr_engine_status);
int netw_setsockopt(NETWORK* netw, int optname, int value);
int netw_set_blocking(NETWORK* netw, unsigned long int is_blocking);
int netw_set_compression_mode(NETWORK* netw, int mode);
int netw_connect_ex(NETWORK** netw, char* host, char* port, size_t send_list_limit, size_t recv_list_limit, int ip_version, char* ssl_key_file, char* ssl_cert_file);
int netw_connect_ex_to(NETWORK** netw, char* host, char* port, size_t send_list_limit, size_t recv_list_limit, int ip_version, char* ssl_key_file, char* ssl_cert_file, int connect_timeout_ms);
int netw_connect(NETWORK** netw, char* host, char* port, int ip_version);
int netw_connect_to(NETWORK** netw, char* host, char* port, int ip_version, int connect_timeout_ms);
int netw_connect_nonblock(NETWORK** netw, char* host, char* port, int ip_version);
int netw_connect_check(NETWORK* netw);
void netw_set_connect_abort_cb(int (*cb)(void* ctx), void* ctx);
int netw_adopt_client_fd(NETWORK** netw, SOCKET fd, const char* host, const char* port);
int deplete_send_buffer(int fd, int timeout);
int netw_bind_udp(NETWORK** netw, char* addr, char* port, int ip_version);
int netw_connect_udp(NETWORK** netw, char* host, char* port, int ip_version);
ssize_t send_udp_data(void* netw, char* buf, uint32_t n);
ssize_t recv_udp_data(void* netw, char* buf, uint32_t n);
ssize_t netw_udp_sendto(NETWORK* netw, char* buf, uint32_t n, struct sockaddr* dest_addr, socklen_t dest_len);
ssize_t netw_udp_recvfrom(NETWORK* netw, char* buf, uint32_t n, struct sockaddr* src_addr, socklen_t* src_len);
int netw_close(NETWORK** netw);
int netw_wait_close(NETWORK** netw);
int netw_wait_close_timed(NETWORK** netw, size_t timeout);
int netw_stop_thr_engine(NETWORK* netw);
int netw_make_listening(NETWORK** netw, char* addr, char* port, int nbpending, int ip_version);
NETWORK* netw_accept_from_ex(NETWORK* from, size_t send_list_limit, size_t recv_list_limit, int blocking, int* retval);
NETWORK* netw_accept_from(NETWORK* from);
NETWORK* netw_accept_nonblock_from(NETWORK* from, int blocking);
int netw_add_msg(NETWORK* netw, N_STR* msg);
int netw_add_msg_ex(NETWORK* netw, char* str, unsigned int length);
N_STR* netw_get_msg(NETWORK* netw);
N_STR* netw_wait_msg(NETWORK* netw, unsigned int refresh, size_t timeout);
int netw_start_thr_engine(NETWORK* netw);
void* netw_send_func(void* NET);
void* netw_recv_func(void* NET);
ssize_t send_data(void* netw, char* buf, uint32_t n);
ssize_t recv_data(void* netw, char* buf, uint32_t n);
ssize_t send_data_once(void* netw, char* buf, uint32_t n);
ssize_t recv_data_once(void* netw, char* buf, uint32_t n);
ssize_t send_php(SOCKET s, int _code, char* buf, int n);
ssize_t recv_php(SOCKET s, int* _code, char** buf);
int netw_get_queue_status(NETWORK* netw, size_t* nb_to_send, size_t* nb_to_read);

NETWORK_POOL* netw_new_pool(size_t nb_min_element);
int netw_destroy_pool(NETWORK_POOL** netw_pool);
void netw_pool_netw_close(void* netw_ptr);
int netw_pool_add(NETWORK_POOL* netw_pool, NETWORK* netw);
int netw_pool_remove(NETWORK_POOL* netw_pool, NETWORK* netw);
int netw_pool_broadcast(NETWORK_POOL* netw_pool, const NETWORK* from, N_STR* net_msg);
size_t netw_pool_nbclients(NETWORK_POOL* netw_pool);

int netw_set_user_id(NETWORK* netw, int id);

int netw_send_ping(NETWORK* netw, int type, int id_from, int id_to, int time);
int netw_send_ident(NETWORK* netw, int type, int id, N_STR* name, N_STR* passwd);
int netw_send_position(NETWORK* netw, int id, double X, double Y, double vx, double vy, double acc_x, double acc_y, int time_stamp);
int netw_send_string_to(NETWORK* netw, int id_to, N_STR* name, N_STR* chan, N_STR* txt, int color);
int netw_send_string_to_all(NETWORK* netw, N_STR* name, N_STR* chan, N_STR* txt, int color);
int netw_send_quit(NETWORK* netw);

size_t netw_calculate_urlencoded_size(const char* str, size_t len);
char* netw_extract_http_request_type(const char* request);
NETWORK_HTTP_INFO netw_extract_http_info(char* request);
int netw_info_destroy(NETWORK_HTTP_INFO http_request);
char* netw_urlencode(const char* str, size_t len);
int netw_get_url_from_http_request(const char* request, char* url, size_t size);
char* netw_urldecode(const char* str);
HASH_TABLE* netw_parse_post_data(const char* post_data);
const char* netw_guess_http_content_type(const char* url);
const char* netw_get_http_status_message(int status_code);
int n_http_status_class(int status_code);
int n_http_status_is_informational(int status_code);
int n_http_status_is_success(int status_code);
int n_http_status_is_redirect(int status_code);
int n_http_status_is_client_error(int status_code);
int n_http_status_is_server_error(int status_code);
int n_http_decompress_body(const N_STR* body, const char* encoding, N_STR** out);

typedef struct N_HTTP_MULTIPART_PART {
    char* name;              
    char* filename;          
    char* content_type;      
    N_STR* body;             
    char* disposition_extra; 
} N_HTTP_MULTIPART_PART;

int n_http_multipart_boundary(const char* content_type, char* out, size_t outsz);
LIST* n_http_parse_multipart(const N_STR* body, const char* boundary);
N_STR* n_http_build_multipart(LIST* parts, const char* boundary);
void n_http_multipart_free(LIST** parts);
int netw_get_http_date(char* buffer, size_t buffer_size);
int netw_build_http_response(N_STR** http_response, int status_code, const char* server_name, const char* content_type, char* additional_headers, N_STR* body);

typedef struct N_PROXY_CFG {
    char* scheme;   
    char* host;     
    int port;       
    char* username; 
    char* password; 
} N_PROXY_CFG;

N_PROXY_CFG* n_proxy_cfg_parse(const char* url);

void n_proxy_cfg_free(N_PROXY_CFG** cfg);

int n_proxy_connect_tunnel(const N_PROXY_CFG* proxy,
                           const char* target_host,
                           int target_port);

#ifdef HAVE_OPENSSL
int n_proxy_connect_tunnel_ssl(const N_PROXY_CFG* proxy,
                               const char* target_host,
                               int target_port);
#endif /* HAVE_OPENSSL */

int n_proxy_connect_socks5(const N_PROXY_CFG* proxy,
                           const char* target_host,
                           int target_port);

#define N_URL_MAX_PARAMS 64

typedef struct N_URL_PARAM {
    char* key;   
    char* value; 
} N_URL_PARAM;

typedef struct N_URL {
    char* scheme;                         
    char* host;                           
    int port;                             
    char* path;                           
    char* query;                          
    N_URL_PARAM params[N_URL_MAX_PARAMS]; 
    int nb_params;                        
} N_URL;

N_URL* n_url_parse(const char* url);
N_STR* n_url_build(const N_URL* u);
N_STR* n_url_canonicalize(const N_URL* u);
N_STR* n_url_canonicalize_string(const char* url);
N_STR* n_url_resolve(const char* base, const char* ref);
N_STR* n_url_encode(const char* str);
N_STR* n_url_decode(const char* str);
void n_url_free(N_URL** u);

#define N_WS_OP_TEXT 0x01
#define N_WS_OP_BINARY 0x02
#define N_WS_OP_CLOSE 0x08
#define N_WS_OP_PING 0x09
#define N_WS_OP_PONG 0x0A

#define N_WS_MAX_PAYLOAD (64 * 1024 * 1024)

typedef struct N_WS_MESSAGE {
    int opcode;     
    N_STR* payload; 
    int masked;     
} N_WS_MESSAGE;

typedef struct N_WS_CONN {
    NETWORK* netw; 
    int connected; 
    char* host;    
    char* path;    
} N_WS_CONN;

typedef struct N_WS_FRAME {
    int fin;                      
    int rsv;                      
    int opcode;                   
    int masked;                   
    unsigned char mask[4];        
    uint64_t payload_len;         
    const unsigned char* payload; 
} N_WS_FRAME;

int n_ws_frame_parse(const unsigned char* buf, size_t len, N_WS_FRAME* frame, size_t* consumed);

void n_ws_unmask(unsigned char* dst, const unsigned char* src, size_t len, const unsigned char mask[4]);

size_t n_ws_frame_build(int fin, int opcode, int do_mask, const unsigned char* payload, size_t len, const unsigned char mask_key[4], unsigned char* out, size_t out_cap);

#ifdef HAVE_OPENSSL
N_WS_CONN* n_ws_connect(const char* host, const char* port, const char* path, int use_ssl);

int n_ws_send(N_WS_CONN* conn, const char* payload, size_t len, int opcode);

int n_ws_recv(N_WS_CONN* conn, N_WS_MESSAGE* msg_out);

void n_ws_close(N_WS_CONN* conn);

void n_ws_conn_free(N_WS_CONN** conn);
#endif

typedef struct N_SSE_EVENT {
    N_STR* event; 
    N_STR* data;  
    N_STR* id;    
    int retry;    
} N_SSE_EVENT;

struct N_SSE_CONN;

typedef struct N_SSE_CONN {
    NETWORK* netw;                                                                  
    volatile int stop_flag;                                                         
    void (*on_event)(N_SSE_EVENT* event, struct N_SSE_CONN* conn, void* user_data); 
    void* user_data;                                                                
} N_SSE_CONN;

#ifdef HAVE_OPENSSL
N_SSE_CONN* n_sse_connect(const char* host, const char* port, const char* path, int use_ssl, const char* user_agent, void (*on_event)(N_SSE_EVENT*, N_SSE_CONN*, void*), void* user_data);

void n_sse_stop(N_SSE_CONN* conn);

void n_sse_conn_free(N_SSE_CONN** conn);

void n_sse_event_clean(N_SSE_EVENT* event);
#endif

typedef struct N_HTTP_REQUEST {
    char method[16];  
    char path[2048];  
    char query[2048]; 
    LIST* headers;    
    N_STR* body;      
} N_HTTP_REQUEST;

typedef struct N_HTTP_RESPONSE {
    int status_code;        
    char content_type[128]; 
    N_STR* body;            
} N_HTTP_RESPONSE;

typedef struct N_MOCK_SERVER {
    NETWORK* listener;                                                               
    volatile int stop_flag;                                                          
    void (*on_request)(N_HTTP_REQUEST* req, N_HTTP_RESPONSE* resp, void* user_data); 
    void* user_data;                                                                 
    int port;                                                                        
} N_MOCK_SERVER;

N_MOCK_SERVER* n_mock_server_start(int port,
                                   void (*on_request)(N_HTTP_REQUEST*, N_HTTP_RESPONSE*, void*),
                                   void* user_data);

void n_mock_server_run(N_MOCK_SERVER* server);

void n_mock_server_stop(N_MOCK_SERVER* server);

void n_mock_server_free(N_MOCK_SERVER** server);

/* per-NETWORK error capture */

int n_netw_get_error_count(const NETWORK* netw);
const char* n_netw_get_error(const NETWORK* netw, int index);
void n_netw_clear_errors(NETWORK* netw);

/* pre-connection error capture (thread-local, no NETWORK yet) */

int n_netw_get_connect_error_count(void);
const char* n_netw_get_connect_error(int index);
void n_netw_clear_connect_errors(void);


#ifdef __cplusplus
}
#endif

#endif /*#ifndef N_NETWORK*/