Build a test server
XQUIC serve as a middleware of transport layer, which induces QUIC facilities to socket application with optional HTTP(or HTTP/3) functionality.
To help you quickly familiarize yourself with how to utilize XQUIC in an socket application, we provide a minimum client and server with XQUIC interface and callback operations.
Please be aware that mini_client and mini_server only demonstrate part of the XQUIC interface, and are only suitable for learning and testing purposes.
mini_server basically includes the following modules:
Engine: Engine is the core module of XQUIC main process, which must be initialized in the first stage.
Connection: Connection module is a submodule of Engine, which is responsible for establishing and managing connections.
Request: Request module is a submodule of Connection, which is responsible for data transmission.
Engine
Engine manage the core logic of XQUIC, including connection management, common callback management, incoming and outgoing packet processing, etc. To make sure the correctness of packet processing, both c/s should be initialized with engine.
Here follows the basic steps to build a xquic engine in server:
Create an engine
Before building a xquic engine, make sure to initialize with several configurations as follows:
xqc_engine_type_t
engine_type: type of engine, 0: server, 1: clientxqc_config_t *
engine_config: configuration of engine
engine default config can be obtained using xqc_engine_get_default_config
:
/* Psudo */
xqc_engine_get_default_config(&engine_config, engine_type);
xqc_engine_ssl_config_t *
ssl_config: configuration of ssl
mini_server init ssl config using xqc_mini_svr_init_engine_ssl_config
, which defines the following parameters of ssl config:
/* Psudo */
ssl_cfg->private_key_file = args->env_cfg.private_key_file; //"server.key"
ssl_cfg->cert_file = args->env_cfg.cert_file; // "server.crt"
ssl_config->ciphers = args->quic_cfg.ciphers; // "TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256"
ssl_config->groups = args->quic_cfg.groups; // "P-256:X25519:P-384:P-521"
xqc_engine_callback_t *
engine_callbacks andxqc_transport_callbacks_t *
transport_callbacks: engine basic callbacks and transport callbacks.
mini_server using xqc_mini_svr_init_callback
to initialize the engine callbacks and transport callbacks with the following self-defined callback functions. It's recommended to refers to callbacks API for more details if you'd like to customize the callbacks:
/* Psudo */
static xqc_engine_callback_t callback = {
.set_event_timer = xqc_mini_svr_set_event_timer,
.log_callbacks = {
.xqc_log_write_err = xqc_mini_svr_write_log_file,
.xqc_log_write_stat = xqc_mini_svr_write_log_file,
.xqc_qlog_event_write = xqc_mini_svr_write_qlog_file
},
.keylog_cb = xqc_mini_svr_keylog_cb,
};
static xqc_transport_callbacks_t transport_cbs = {
.server_accept = xqc_mini_svr_accept,
.write_socket = xqc_mini_svr_write_socket,
.write_socket_ex = xqc_mini_svr_write_socket_ex,
.conn_update_cid_notify = xqc_mini_svr_conn_update_cid_notify,
};
void *
user_data: application context, will be passed to callback functions for convenient uses.
After that engine can be created using xqc_engine_create
:
/* Psudo */
xqc_engine_t *engine = xqc_engine_create(0, &engine_config, &ssl_config,
&callback, &transport_cbs, user_data);
Init engine context
After initialization of engine, there's still some context to be completed:
Init connection settings
The server typically accepts requests from the client, and passively establish connections with the client. Therefore, connection settings should be set up before accepting requests from the client:
mini_server uses xqc_mini_svr_init_conn_settings
to initialize the connection settings with the following parameters:
/* Psudo */
xqc_conn_settings_t conn_settings = {
.cong_ctrl_callback = ccc,
.cc_params = {
.customize_on = 1,
.init_cwnd = 32,
.bbr_enable_lt_bw = 1,
},
.spurious_loss_detect_on = 1,
.init_idle_time_out = 60000,
.enable_multipath = args->quic_cfg.multipath,
.scheduler_callback = sched,
.standby_path_probe_timeout = 1000,
.adaptive_ack_frequency = 1,
.anti_amplification_limit = 4,
};
/* set customized connection settings to engine ctx */
xqc_server_set_conn_settings(engine, &conn_settings);
Register application callbacks
Refers to mini_client section for details.
Connection
XQUIC can support reliable transmission upon the UDP transport layer based on the Connection module.
Unlike TCP, QUIC connection is more like an abstraction of socket state management, and is negotiate and maintained based on socket transmission.
Initialize a socket and socket callback
The server typically accepts requests from the client, and passively establish connections with the client.
To enable server receive and process packets from the client, it's necessary of server to create and initialize socket. All the socket logic are included in xqc_mini_svr_create_user_conn
.
mini_server uses xqc_mini_svr_create_socket
to create socket with specific address, port and several parameters and uses xqc_mini_svr_socket_event_callback
to register the callback function for socket events.
/* Psudo */
xqc_mini_svr_create_socket(user_conn, &args->net_cfg);
/* bind socket event callback to fd event */
user_conn->ev_socket = event_new(ctx->eb, user_conn->fd, EV_READ | EV_PERSIST,
xqc_mini_svr_socket_event_callback, user_conn);
event_add(user_conn->ev_socket, NULL);
Create a connection (passively)
The functionality of XQUIC is primarily introduced in the handling of READ EVENTS, which allows the server to process raw data packets from the client using the XQUIC interface, thereby enabling features such as parsing QUIC requests and establishing QUIC connections.
/* Psudo: socket read handler, receive raw data and process it in xquic engine */
ssize_t recv_size = recvfrom(fd, packet_buf, sizeof(packet_buf), 0,
(struct sockaddr *) &peer_addr, &peer_addrlen);
/* process quic packet with xquic engine */
xqc_engine_packet_process(engine, packet_buf, recv_size,
(struct sockaddr *)(user_conn->local_addr), user_conn->local_addrlen,
(struct sockaddr *)(user_conn->peer_addr), user_conn->peer_addrlen,
(xqc_usec_t)recv_time, user_conn);
xqc_engine_finish_recv(engine);
Request
Server process and respond request using application callback function, which was registered in regitster application callbacks.
mini_server enables requests sending & receiving via the following steps:
Receive request
mini_server uses the xqc_mini_svr_h3_request_read_notify
interface to customize the processing for client request data that has been parsed by the xquic main logic.
/* Psudo */
// Step 1: send headers
xqc_http_headers_t *headers = xqc_h3_request_recv_headers(h3_request, &fin);
// Step 2: send body
ssize_t read = xqc_h3_request_recv_body(h3_request, recv_buff, recv_buff_size, &fin);
Send request
When the server triggers write notify, mini_server uses xqc_mini_svr_h3_request_write_notify
to process sending request.
After receiving a request from the client, mini_server uses xqc_mini_cli_handle_h3_request
to send response to the client.
For example:
/* Psudo */
// Step 1: send headers
xqc_h3_request_send_headers(h3_request, &headers, 0);
// Step 2: send body
xqc_h3_request_send_body(user_stream->h3_request, send_buf, send_buf_size, fin);