# How XQUIC Handles TLS Session Resumption: Architecture and Implementation Guide

> Discover how XQUIC handles TLS 1.3 session resumption with session tickets, OpenSSL callbacks, and 0-RTT handshake enablement. Learn the architecture and implementation details.

- Repository: [Alibaba/xquic](https://github.com/alibaba/xquic)
- Tags: internals
- Published: 2026-02-24

---

**XQUIC implements TLS 1.3 session resumption using session tickets, where the server generates tickets via OpenSSL callbacks, forwards them to applications through `save_session_cb`, and clients load persisted tickets into `xqc_conn_ssl_config_t.session_ticket` to enable 0-RTT handshakes.**

Alibaba's XQUIC library provides a complete implementation of QUIC protocol with optimized TLS 1.3 integration. Understanding how XQUIC handles TLS session resumption is essential for building high-performance applications that minimize connection latency through 0-RTT data transmission.

## The TLS Session Resumption Architecture

XQUIC's session resumption flow relies on three coordinated components that bridge OpenSSL's TLS layer with the transport implementation:

| Component | Source File | Key Function |
|-----------|-------------|--------------|
| **TLS Context** | [`src/tls/xqc_tls_ctx.c`](https://github.com/alibaba/xquic/blob/main/src/tls/xqc_tls_ctx.c) | Sets up `SSL_CTX`, registers ticket-key callbacks (`SSL_CTX_set_tlsext_ticket_key_cb`), and enables client session caching. |
| **TLS Instance** | [`src/tls/xqc_tls.c`](https://github.com/alibaba/xquic/blob/main/src/tls/xqc_tls.c) | Implements `xqc_ssl_new_session_cb` for ticket generation and `xqc_tls_cli_set_session_data` for ticket loading. |
| **Transport Glue** | [`src/transport/xqc_conn.c`](https://github.com/alibaba/xquic/blob/main/src/transport/xqc_conn.c) | Bridges TLS callbacks to user-defined `save_session_cb` via `xqc_conn_tls_session_cb`. |

This architecture ensures that session tickets flow securely from the TLS stack through the transport layer to application-level storage.

## Server-Side Session Ticket Generation

When a TLS handshake completes, XQUIC generates a new session ticket through OpenSSL's new-session callback mechanism. In `xqc_create_server_ssl_ctx` ([`src/tls/xqc_tls_ctx.c`](https://github.com/alibaba/xquic/blob/main/src/tls/xqc_tls_ctx.c), lines 60-64), the library registers the callback:

```c
SSL_CTX_sess_set_new_cb(ssl_ctx, xqc_ssl_new_session_cb);

```

The callback implementation resides in [`src/tls/xqc_tls.c`](https://github.com/alibaba/xquic/blob/main/src/tls/xqc_tls.c) (lines 977-1004). The `xqc_ssl_new_session_cb` function performs three critical operations:

1. **Validates early data permissions** using `xqc_ssl_session_is_early_data_enabled`
2. **Serializes the session** into PEM format via memory BIO
3. **Invokes the application callback** stored in `tls->cbs->session_cb`

```c
if (tls->cbs->session_cb != NULL) {
    BIO *bio = BIO_new(BIO_s_mem());
    PEM_write_bio_SSL_SESSION(bio, session);
    size_t data_len = BIO_get_mem_data(bio, &data);
    tls->cbs->session_cb(data, data_len, tls->user_data);
    BIO_free(bio);
}

```

## Application-Level Ticket Storage

The transport layer bridges the TLS callback to user-space storage. During connection initialization in [`src/transport/xqc_conn.c`](https://github.com/alibaba/xquic/blob/main/src/transport/xqc_conn.c) (line 6370), XQUIC wires the TLS session callback to the transport handler:

```c
.conn_cbs.session_cb = xqc_conn_tls_session_cb,

```

The `xqc_conn_tls_session_cb` function forwards tickets to the application's `save_session_cb`:

```c
void xqc_conn_tls_session_cb(const char *data, size_t data_len, void *user_data) {
    xqc_connection_t *conn = (xqc_connection_t *)user_data;
    conn->transport_cbs.save_session_cb(data, data_len, conn->user_data);
}

```

Applications must implement `save_session_cb` (defined as `xqc_save_session_pt` in [`include/xquic/xquic.h`](https://github.com/alibaba/xquic/blob/main/include/xquic/xquic.h)) to persist tickets to disk, database, or shared cache.

## Client-Side Session Resumption and 0-RTT

To resume a session, the client loads a previously stored ticket into the connection configuration before initiating the handshake. In [`src/transport/xqc_client.c`](https://github.com/alibaba/xquic/blob/main/src/transport/xqc_client.c) (lines 78-88), `xqc_client_create_tls` copies the ticket data:

```c
cfg.session_ticket = xqc_malloc(conn_ssl_config->session_ticket_len + 1);
xqc_memcpy(cfg.session_ticket, conn_ssl_config->session_ticket_data,
           conn_ssl_config->session_ticket_len);
cfg.session_ticket_len = conn_ssl_config->session_ticket_len;

```

During TLS initialization (`xqc_tls_init_client_ssl`), XQUIC injects the ticket via `xqc_tls_cli_set_session_data` ([`src/tls/xqc_tls.c`](https://github.com/alibaba/xquic/blob/main/src/tls/xqc_tls.c), lines 22-30):

```c
if (cfg->session_ticket && cfg->session_ticket_len > 0) {
    if (xqc_tls_cli_set_session_data(tls, cfg->session_ticket,
                                     cfg->session_ticket_len) == XQC_OK) {
        tls->resumption = XQC_TRUE;
        xqc_ssl_enable_max_early_data(ssl);
    }
}

```

The `xqc_tls_cli_set_session_data` function loads the PEM ticket into an `SSL_SESSION` object and calls `SSL_set_session`. It also validates ticket expiration through `xqc_tls_check_session_ticket_timeout`. When successful, the client sets `tls->resumption` to `true` and enables 0-RTT data transmission.

## Server-Side Ticket Key Management

Server instances require consistent ticket encryption keys to decrypt tickets across restarts or load-balanced deployments. XQUIC loads the session ticket key from engine configuration in `xqc_tls_ctx_set_config` ([`src/tls/xqc_tls_ctx.c`](https://github.com/alibaba/xquic/blob/main/src/tls/xqc_tls_ctx.c), lines 268-280), storing it in `xqc_tls_ctx_t.session_ticket_key`.

The key is utilized by `xqc_ssl_session_ticket_key_cb` ([`src/tls/xqc_tls.c`](https://github.com/alibaba/xquic/blob/main/src/tls/xqc_tls.c), lines 898-907), which OpenSSL invokes when encrypting or decrypting session tickets. This callback ensures that all servers in a cluster can validate tickets issued by peers sharing the same key material.

## Complete Implementation Example

The following example demonstrates the application callbacks required for full session resumption support:

```c
/* Application callback to persist session tickets */
static void save_session_cb(const char *data, size_t data_len, void *user_data) {
    FILE *fp = fopen("session.ticket", "wb");
    if (fp) {
        fwrite(data, 1, data_len, fp);
        fclose(fp);
    }
}

/* Helper to load persisted tickets */
static xqc_int_t load_session_ticket(xqc_conn_ssl_config_t *cfg) {
    FILE *fp = fopen("session.ticket", "rb");
    if (!fp) return -XQC_EMALLOC;

    fseek(fp, 0, SEEK_END);
    long len = ftell(fp);
    fseek(fp, 0, SEEK_SET);
    unsigned char *buf = xqc_malloc(len);
    if (!buf) { fclose(fp); return -XQC_EMALLOC; }

    fread(buf, 1, len, fp);
    fclose(fp);

    cfg->session_ticket = buf;
    cfg->session_ticket_len = (size_t)len;
    return XQC_OK;
}

/* Connection setup with session resumption */
xqc_conn_ssl_config_t cfg = {0};
cfg.save_session_cb = save_session_cb;

/* Load previous ticket for resumption */
load_session_ticket(&cfg);

xqc_connection_t *conn = xqc_client_create_connection(
    engine, dcid, scid, &settings, "example.com", 0, &cfg, "h3", NULL);

```

This implementation ensures that session tickets are preserved across application restarts, enabling subsequent connections to skip the full TLS handshake.

## Summary

- **XQUIC uses TLS 1.3 session tickets** rather than session IDs for resumption, implemented through OpenSSL callbacks in [`src/tls/xqc_tls.c`](https://github.com/alibaba/xquic/blob/main/src/tls/xqc_tls.c).
- **Server ticket generation** occurs in `xqc_ssl_new_session_cb`, which serializes sessions to PEM and forwards them via the transport layer callback `xqc_conn_tls_session_cb`.
- **Application integration** requires implementing `save_session_cb` to persist tickets and populating `xqc_conn_ssl_config_t.session_ticket` before connection creation.
- **0-RTT capability** is automatically enabled when valid tickets are loaded via `xqc_tls_cli_set_session_data`, which sets `tls->resumption = XQC_TRUE`.
- **Ticket encryption** relies on configurable keys set through `xqc_tls_ctx_set_config`, allowing multi-instance deployments to share session state.

## Frequently Asked Questions

### What TLS version does XQUIC use for session resumption?

XQUIC implements **TLS 1.3 session resumption** exclusively. The codebase relies on TLS 1.3-specific features such as 0-RTT data and the `SSL_CTX_sess_set_new_cb` callback mechanism for ticket-based resumption, as defined in RFC 8446.

### How does XQUIC handle session ticket expiration?

XQUIC validates ticket freshness through `xqc_tls_check_session_ticket_timeout` when loading sessions via `xqc_tls_cli_set_session_data`. Expired tickets are rejected, forcing a full handshake. Applications should implement rotation strategies for the `session_ticket_key` to ensure security while maintaining cluster-wide ticket validity periods.

### Can XQUIC resume sessions across different server instances?

Yes, provided all instances share the same **session ticket key**. The key is configured through the engine's TLS context setup (`xqc_tls_ctx_set_config` in [`src/tls/xqc_tls_ctx.c`](https://github.com/alibaba/xquic/blob/main/src/tls/xqc_tls_ctx.c)) and used by `xqc_ssl_session_ticket_key_cb` for encryption and decryption. Without shared keys, tickets issued by one server will be rejected by others.

### What callback must applications implement to save session tickets?

Applications must implement the `xqc_save_session_pt` callback (typically named `save_session_cb`) and register it in `xqc_conn_ssl_config_t`. This callback receives the serialized ticket data when `xqc_ssl_new_session_cb` generates a new ticket, allowing persistent storage for subsequent connection attempts.