ronantakizawa/codereview-bench huggingface dataset:
Repository: nginx/nginx
File: src/event/quic/ngx_event_quic_output.c (C)

Code before:

    qc = ngx_quic_get_connection(c);

    cg = &qc->congestion;

    while (!ngx_queue_empty(&ctx->sending)) {

        q = ngx_queue_head(&ctx->sending);
        f = ngx_queue_data(q, ngx_quic_frame_t, queue);

        ngx_queue_remove(q);

        if (f->pkt_need_ack && !qc->closing) {
            ngx_queue_insert_tail(&ctx->sent, q);

            cg->in_flight += f->plen;

        } else {
            ngx_quic_free_frame(c, f);
        }
    }

    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
                   "quic congestion send if:%uz", cg->in_flight);

    ngx_quic_congestion_idle(c, ngx_quic_queue_empty(c));
}


static ngx_uint_t
ngx_quic_queue_empty(ngx_connection_t *c)
{
    ngx_uint_t              i;
    ngx_quic_send_ctx_t    *ctx;
    ngx_quic_connection_t  *qc;

    qc = ngx_quic_get_connection(c);

    for (i = 0; i < NGX_QUIC_SEND_CTX_LAST; i++) {
        ctx = &qc->send_ctx[i];

        if (!ngx_queue_empty(&ctx->frames)) {
            return 0;
        }
    }

    return 1;
}


static void

Review comment (performance):
Mind moving ngx_quic_congestion_idle() call out of ngx_quic_commit_send() routines?
Same for ngx_quic_revert_send().
Otherwise it will try to update `cg->idle` for every send context, doing extra (quadratic complexity) work.

On a related note, it's quite annoying to see `cg->in_flight` noisy logging for typically empty `ctx->sending` in 2 out of 3 contexts, see 8c59ebd57 for a proposed fix.


Code after:

    for (i = 0; i < NGX_QUIC_SEND_CTX_LAST; i++) {
        ctx = &qc->send_ctx[i];

        if (!ngx_queue_empty(&ctx->frames)) {
            idle = 0;
        }

        while (!ngx_queue_empty(&ctx->sending)) {

            q = ngx_queue_head(&ctx->sending);
            f = ngx_queue_data(q, ngx_quic_frame_t, queue);

            ngx_queue_remove(q);

            if (f->pkt_need_ack && !qc->closing) {
                ngx_queue_insert_tail(&ctx->sent, q);

                cg->in_flight += f->plen;

            } else {
                ngx_quic_free_frame(c, f);
            }
        }
    }

    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
                   "quic congestion send if:%uz", cg->in_flight);

    ngx_quic_congestion_idle(c, idle);
}


static void
ngx_quic_revert_send(ngx_connection_t *c, uint64_t pnum[NGX_QUIC_SEND_CTX_LAST])
{
    ngx_uint_t              i;
    ngx_queue_t            *q;
    ngx_quic_send_ctx_t    *ctx;
    ngx_quic_connection_t  *qc;

    qc = ngx_quic_get_connection(c);

    for (i = 0; i < NGX_QUIC_SEND_CTX_LAST; i++) {
        ctx = &qc->send_ctx[i];

        if (ngx_queue_empty(&ctx->sending)) {
            continue;
        }

        do {
Repository: nginx/nginx
File: src/event/quic/ngx_event_quic_ack.c (C)

Code before:
    w = t * t * t / cc + (int64_t) cg->w_max;

    if (w > NGX_MAX_SIZE_T_VALUE) {
        w = NGX_MAX_SIZE_T_VALUE;
    }

    if (w < 0) {
        w = 0;
    }

done:

    ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
                   "quic cubic t:%L w:%L wm:%uz", t, w, cg->w_max);

    return w;
}


void
ngx_quic_congestion_idle(ngx_connection_t *c, ngx_uint_t idle)
{
    ngx_msec_t              now;
    ngx_quic_congestion_t  *cg;
    ngx_quic_connection_t  *qc;

    qc = ngx_quic_get_connection(c);
    cg = &qc->congestion;

    if (idle != cg->idle) {
        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
                       "quic congestion idle:%ui", idle);
    }

    if (cg->window >= cg->ssthresh) {
        /* RFC 9438, 5.8. Behavior for Application-Limited Flows */

        now = ngx_current_msec;

        if (cg->idle) {
            cg->k += now - cg->idle_start;
        }

        cg->idle_start = now;
    }

    cg->idle = idle;
}


static void

Review comment (bug):
Changing path MTU between sending the packet and its acknowledgement
will result in the wrong Wcubic computation.

As currently implemented, the calculated path MTU may grow upward.
This results in a reduced Wcubic value, and consequently - using the Reno-friendly region if it was reduced below West, as seen in my tests.

To avoid storing the used PMTU in each sent frame, the workaround might be to remember one previous PMTU value and time of the last update, to use the correct value in Wcubic (and K) computations.

Remembering the only previous PMTU value should be enough for Wcubic, which is calculated on packet acknowledgement, and probably enough for K, calculated on packet loss event.

Code after:
    qc = ngx_quic_get_connection(c);
    cg = &qc->congestion;

    ngx_quic_congestion_idle(c, cg->idle);

    now = ngx_current_msec;
    t = (ngx_msec_int_t) (now - cg->k);

    if (t > 1000000) {
        w = NGX_MAX_SIZE_T_VALUE;
        goto done;
    }

    if (t < -1000000) {
        w = 0;
        goto done;
    }

    /*
     * RFC 9438, Figure 1
     *
     *   w_cubic = C * (t_msec / 1000) ^ 3 * mtu + w_max
     */

    cc = 10000000000ll / (int64_t) cg->mtu / MGX_QUIC_CUBIC_C;
    w = t * t * t / cc + (int64_t) cg->w_max;

    if (w > NGX_MAX_SIZE_T_VALUE) {
        w = NGX_MAX_SIZE_T_VALUE;
    }

    if (w < 0) {
        w = 0;
    }

done:

    ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
                   "quic cubic t:%L w:%L wm:%uz", t, w, cg->w_max);

    return w;
}


void
ngx_quic_congestion_idle(ngx_connection_t *c, ngx_uint_t idle)
{
    ngx_msec_t              now;
    ngx_quic_congestion_t  *cg;
    ngx_quic_connection_t  *qc;
Repository: nginx/nginx
File: src/event/ngx_event_openssl.c (C)

Code before:
            ngx_rbtree_delete(&cache->session_rbtree, node);

            ngx_explicit_memzero(sess_id->session, sess_id->len);

#if (NGX_PTR_SIZE == 8)
            ngx_slab_free_locked(shpool, sess_id->session);
#endif
            ngx_slab_free_locked(shpool, sess_id);

            sess = NULL;

            goto done;
        }

        node = (rc < 0) ? node->left : node->right;
    }

done:

    ngx_shmtx_unlock(&shpool->mutex);

    return sess;
}


u_char *ngx_ssl_get_session_buffer(void)
{
    static u_char  buf[NGX_SSL_MAX_SESSION_SIZE];

    return buf;
}


void
ngx_ssl_remove_cached_session(SSL_CTX *ssl, ngx_ssl_session_t *sess)
{
    SSL_CTX_remove_session(ssl, sess);

    ngx_ssl_remove_session(ssl, sess);
}


static void
ngx_ssl_remove_session(SSL_CTX *ssl, ngx_ssl_session_t *sess)
{
    u_char                   *id;
    uint32_t                  hash;
    ngx_int_t                 rc;
    unsigned int              len;
    ngx_shm_zone_t           *shm_zone;
    ngx_slab_pool_t          *shpool;

Review comment (performance):
Why not just reference a global array directly, similar to
```
extern ngx_conf_bitmask_t  ngx_http_upstream_cache_method_mask[];                
...
ngx_conf_bitmask_t  ngx_http_upstream_cache_method_mask[] = {                    
...
}
```

Code after:

            ngx_rbtree_delete(&cache->session_rbtree, node);

            ngx_explicit_memzero(sess_id->session, sess_id->len);

#if (NGX_PTR_SIZE == 8)
            ngx_slab_free_locked(shpool, sess_id->session);
#endif
            ngx_slab_free_locked(shpool, sess_id);

            sess = NULL;

            goto done;
        }

        node = (rc < 0) ? node->left : node->right;
    }

done:

    ngx_shmtx_unlock(&shpool->mutex);

    return sess;
}


void
ngx_ssl_remove_cached_session(SSL_CTX *ssl, ngx_ssl_session_t *sess)
{
    SSL_CTX_remove_session(ssl, sess);

    ngx_ssl_remove_session(ssl, sess);
}


static void
ngx_ssl_remove_session(SSL_CTX *ssl, ngx_ssl_session_t *sess)
{
    u_char                   *id;
    uint32_t                  hash;
    ngx_int_t                 rc;
    unsigned int              len;
    ngx_shm_zone_t           *shm_zone;
    ngx_slab_pool_t          *shpool;
    ngx_rbtree_node_t        *node, *sentinel;
    ngx_ssl_sess_id_t        *sess_id;
    ngx_ssl_session_cache_t  *cache;

    shm_zone = SSL_CTX_get_ex_data(ssl, ngx_ssl_session_cache_index);

    if (shm_zone == NULL) {
Repository: nginx/nginx
File: src/http/ngx_http_upstream_round_robin.c (C)

Code before:

void
ngx_http_upstream_save_round_robin_peer_session(ngx_peer_connection_t *pc,
    void *data)
{
    ngx_http_upstream_rr_peer_data_t  *rrp = data;

    ngx_ssl_session_t             *old_ssl_session, *ssl_session;
    ngx_http_upstream_rr_peer_t   *peer;
#if (NGX_HTTP_UPSTREAM_ZONE)
    int                            len;
    u_char                        *p, *buf;
    ngx_http_upstream_rr_peers_t  *peers;
#endif

#if (NGX_HTTP_UPSTREAM_ZONE)
    peers = rrp->peers;

    if (peers->shpool) {

        ssl_session = ngx_ssl_get0_session(pc->connection);

        if (ssl_session == NULL) {
            return;
        }

        len = i2d_SSL_SESSION(ssl_session, NULL);

        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0,
                       "save session: %p:%d", ssl_session, len);

        /* do not cache too big session */

        if (len > NGX_SSL_MAX_SESSION_SIZE) {
            return;
        }

        p = buf = ngx_ssl_get_session_buffer();
        (void) i2d_SSL_SESSION(ssl_session, &p);

        peer = rrp->current;

        ngx_http_upstream_rr_peers_rlock(peers);
        ngx_http_upstream_rr_peer_lock(peers, peer);

        if (len > peer->ssl_session_len) {
            ngx_shmtx_lock(&peers->shpool->mutex);

            if (peer->ssl_session) {
                ngx_slab_free_locked(peers->shpool, peer->ssl_session);
            }

Review comment (suggestion):
>This makes it easier to understand why too big sessions could be
discarded to save in shared memory.

I'd rephrase this.

Code after:
    void *data)
{
    ngx_http_upstream_rr_peer_data_t  *rrp = data;

    ngx_ssl_session_t             *old_ssl_session, *ssl_session;
    ngx_http_upstream_rr_peer_t   *peer;
#if (NGX_HTTP_UPSTREAM_ZONE)
    int                            len;
    u_char                        *p;
    ngx_http_upstream_rr_peers_t  *peers;
#endif

#if (NGX_HTTP_UPSTREAM_ZONE)
    peers = rrp->peers;

    if (peers->shpool) {

        ssl_session = ngx_ssl_get0_session(pc->connection);

        if (ssl_session == NULL) {
            return;
        }

        len = i2d_SSL_SESSION(ssl_session, NULL);

        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0,
                       "save session: %p:%d", ssl_session, len);

        /* do not cache too big session */

        if (len > NGX_SSL_MAX_SESSION_SIZE) {
            return;
        }

        p = ngx_ssl_session_buffer;
        (void) i2d_SSL_SESSION(ssl_session, &p);

        peer = rrp->current;

        ngx_http_upstream_rr_peers_rlock(peers);
        ngx_http_upstream_rr_peer_lock(peers, peer);

        if (len > peer->ssl_session_len) {
            ngx_shmtx_lock(&peers->shpool->mutex);

            if (peer->ssl_session) {
                ngx_slab_free_locked(peers->shpool, peer->ssl_session);
            }

            peer->ssl_session = ngx_slab_alloc_locked(peers->shpool, len);
Repository: nginx/nginx
File: src/event/ngx_event_openssl.c (C)

Code before:
        p = NULL;
        goto done;
    }

    /* RFC 6066 mandates non-zero HostName length, we follow OpenSSL */

    if (len < 5
        || (size_t) (p[0] << 8) + p[1] + 2 != len
        || p[2] != TLSEXT_NAMETYPE_host_name
        || (size_t) (p[3] << 8) + p[4] + 2 + 3 != len)
    {
        *ad = SSL_AD_DECODE_ERROR;
        return SSL_CLIENT_HELLO_ERROR;
    }

    len -= 5;
    p += 5;

    if (len > TLSEXT_MAXLEN_host_name || ngx_strlchr(p, p + len, '\0')) {
        *ad = SSL_AD_UNRECOGNIZED_NAME;
        return SSL_CLIENT_HELLO_ERROR;
    }

done:

    rc = cb->servername(ssl_conn, ad, p);

    if (rc == SSL_TLSEXT_ERR_ALERT_FATAL) {
        return SSL_CLIENT_HELLO_ERROR;
    }

    return SSL_CLIENT_HELLO_SUCCESS;
}

#endif


static int
ngx_ssl_cmp_x509_name(const X509_NAME *const *a, const X509_NAME *const *b)
{
    return (X509_NAME_cmp(*a, *b));
}


ngx_array_t *
ngx_ssl_read_password_file(ngx_conf_t *cf, ngx_str_t *file)
{
    u_char              *p, *last, *end;
    size_t               len;
    ssize_t              n;
    ngx_fd_t             fd;

Review comment (suggestion):
If I read ssl/statem/extensions.c correctly, `p` points directly to the ClientHello buffer and is not null-terminated. It may accidentally work if the next extension type is single byte (e.g. `00 10`), but we shouldn't rely on that.

Consider passing `ngx_str_t` with len.

Code after:

ngx_array_t *
ngx_ssl_read_password_file(ngx_conf_t *cf, ngx_str_t *file)
{
    u_char              *p, *last, *end;
    size_t               len;
    ssize_t              n;
    ngx_fd_t             fd;
    ngx_str_t           *pwd;
    ngx_array_t         *passwords;
    ngx_pool_cleanup_t  *cln;
    u_char               buf[NGX_SSL_PASSWORD_BUFFER_SIZE];

    if (ngx_conf_full_name(cf->cycle, file, 1) != NGX_OK) {
        return NULL;
    }

    passwords = ngx_array_create(cf->temp_pool, 4, sizeof(ngx_str_t));
    if (passwords == NULL) {
        return NULL;
    }

    cln = ngx_pool_cleanup_add(cf->temp_pool, 0);
    if (cln == NULL) {
        return NULL;
    }

    cln->handler = ngx_ssl_passwords_cleanup;
    cln->data = passwords;

    fd = ngx_open_file(file->data, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0);

    if (fd == NGX_INVALID_FILE) {
        ngx_conf_log_error(NGX_LOG_EMERG, cf, ngx_errno,
                           ngx_open_file_n " \"%s\" failed", file->data);
        return NULL;
    }

    len = 0;
    last = buf;

    do {
        n = ngx_read_fd(fd, last, NGX_SSL_PASSWORD_BUFFER_SIZE - len);

        if (n == -1) {
            ngx_conf_log_error(NGX_LOG_EMERG, cf, ngx_errno,
                               ngx_read_fd_n " \"%s\" failed", file->data);
            passwords = NULL;
            goto cleanup;
        }
Repository: nginx/nginx
File: src/event/ngx_event_openssl.c (C)

Code before:
    rc = cb->servername(ssl_conn, ad, &host);

    if (rc == SSL_TLSEXT_ERR_ALERT_FATAL) {
        return SSL_CLIENT_HELLO_ERROR;
    }

    return SSL_CLIENT_HELLO_SUCCESS;
}

#elif defined (OPENSSL_IS_BORINGSSL)

enum ssl_select_cert_result_t ngx_ssl_select_certificate(
    const SSL_CLIENT_HELLO *client_hello)
{
    int                        ad;
    ngx_int_t                  rc;
    ngx_ssl_conn_t            *ssl_conn;
    ngx_connection_t          *c;
    ngx_ssl_client_hello_arg  *cb;

    ssl_conn = client_hello->ssl;
    c = ngx_ssl_get_connection(ssl_conn);
    cb = SSL_CTX_get_ex_data(c->ssl->session_ctx, ngx_ssl_sni_index);

    /*
     * SSL_AD_HANDSHAKE_FAILURE is hardcoded to return on errors, we use
     * it to map SSL_AD_INTERNAL_ERROR.  To preserve other alert values,
     * error handling is postponed to the servername callback.
     */

    rc = cb->servername(ssl_conn, &ad, NULL);

    if (rc == SSL_TLSEXT_ERR_ALERT_FATAL && ad == SSL_AD_INTERNAL_ERROR) {
        return ssl_select_cert_error;
    }

    return ssl_select_cert_success;
}

#endif


static int
ngx_ssl_cmp_x509_name(const X509_NAME *const *a, const X509_NAME *const *b)
{
    return (X509_NAME_cmp(*a, *b));
}


ngx_array_t *
ngx_ssl_read_password_file(ngx_conf_t *cf, ngx_str_t *file)

Review comment (bug):
The comment sounds a bit unnatural. I'd suggest to rephrase the first part as "BoringSSL returns hardcoded alert SSL_AD_HANDSHAKE_FAILURE on error, ..."

Code after:
    do {
        n = ngx_read_fd(fd, last, NGX_SSL_PASSWORD_BUFFER_SIZE - len);

        if (n == -1) {
            ngx_conf_log_error(NGX_LOG_EMERG, cf, ngx_errno,
                               ngx_read_fd_n " \"%s\" failed", file->data);
            passwords = NULL;
            goto cleanup;
        }

        end = last + n;

        if (len && n == 0) {
            *end++ = LF;
        }

        p = buf;

        for ( ;; ) {
            last = ngx_strlchr(last, end, LF);

            if (last == NULL) {
                break;
            }

            len = last++ - p;

            if (len && p[len - 1] == CR) {
                len--;
            }

            if (len) {
                pwd = ngx_array_push(passwords);
                if (pwd == NULL) {
                    passwords = NULL;
                    goto cleanup;
                }

                pwd->len = len;
                pwd->data = ngx_pnalloc(cf->temp_pool, len);

                if (pwd->data == NULL) {
                    passwords->nelts--;
                    passwords = NULL;
                    goto cleanup;
                }

                ngx_memcpy(pwd->data, p, len);
            }

            p = last;
Repository: nginx/nginx
File: src/event/ngx_event_openssl.c (C)

Code before:
    }

    return pwds;
}


static void
ngx_ssl_passwords_cleanup(void *data)
{
    ngx_array_t *passwords = data;

    ngx_str_t   *pwd;
    ngx_uint_t   i;

    pwd = passwords->elts;

    for (i = 0; i < passwords->nelts; i++) {
        ngx_explicit_memzero(pwd[i].data, pwd[i].len);
    }
}


ngx_int_t
ngx_ssl_dhparam(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file)
{
    BIO  *bio;

    if (file->len == 0) {
        return NGX_OK;
    }

    if (ngx_conf_full_name(cf->cycle, file, 1) != NGX_OK) {
        return NGX_ERROR;
    }

    bio = BIO_new_file((char *) file->data, "r");
    if (bio == NULL) {
        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
                      "BIO_new_file(\"%s\") failed", file->data);
        return NGX_ERROR;
    }

#ifdef SSL_CTX_set_tmp_dh
    {
    DH  *dh;

    dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);
    if (dh == NULL) {
        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
                      "PEM_read_bio_DHparams(\"%s\") failed", file->data);
        BIO_free(bio);

Review comment (bug):
Ah, yes. That would be more correct. Despite having `SSL_CTX_set0_tmp_dh_pkey` available, we don't have an `EVP_PKEY_ASN1_METHOD` instance registered for parsing `DH PARAMETERS` and cannot succeed anyways.

I wonder if we should treat this as an error. The original patch resulted in this nice message:
```
nginx: [emerg] PEM_read_bio_Parameters("/tmp/nginx-test-CR8zJrHShe/dhparam.pem") failed (SSL: error:1E08010C:DECODER routines::unsupported:No supported data to decode. Input type: PEM)
```


Code after:
    }

    return pwds;
}


static void
ngx_ssl_passwords_cleanup(void *data)
{
    ngx_array_t *passwords = data;

    ngx_str_t   *pwd;
    ngx_uint_t   i;

    pwd = passwords->elts;

    for (i = 0; i < passwords->nelts; i++) {
        ngx_explicit_memzero(pwd[i].data, pwd[i].len);
    }
}


ngx_int_t
ngx_ssl_dhparam(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file)
{
#ifndef OPENSSL_NO_DH

    BIO  *bio;

    if (file->len == 0) {
        return NGX_OK;
    }

    if (ngx_conf_full_name(cf->cycle, file, 1) != NGX_OK) {
        return NGX_ERROR;
    }

    bio = BIO_new_file((char *) file->data, "r");
    if (bio == NULL) {
        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
                      "BIO_new_file(\"%s\") failed", file->data);
        return NGX_ERROR;
    }

#ifdef SSL_CTX_set_tmp_dh
    {
    DH  *dh;

    dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);
    if (dh == NULL) {
        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
Repository: nginx/nginx
File: src/event/ngx_event_openssl_cache.c (C)

Code before:
    ngx_file_uniq_t        uniq;
    ngx_file_info_t        fi;
    ngx_ssl_cache_t       *cache, *old_cache;
    ngx_ssl_cache_key_t    id;
    ngx_ssl_cache_type_t  *type;
    ngx_ssl_cache_node_t  *cn;

    *err = NULL;

    invalidate = index & NGX_SSL_CACHE_INVALIDATE;
    index &= ~NGX_SSL_CACHE_INVALIDATE;

    if (ngx_ssl_cache_init_key(cf->pool, index, path, &id) != NGX_OK) {
        return NULL;
    }

    cache = (ngx_ssl_cache_t *) ngx_get_conf(cf->cycle->conf_ctx,
                                             ngx_openssl_cache_module);

    type = &ngx_ssl_cache_types[index];
    hash = ngx_murmur_hash2(id.data, id.len);

    cn = ngx_ssl_cache_lookup(cache, type, &id, hash);

    if (cn != NULL) {
        if (!invalidate || id.type != NGX_SSL_CACHE_PATH) {
            return type->ref(err, cn->value);
        }

        type->free(cn->value);
        ngx_rbtree_delete(&cache->rbtree, &cn->node);
    }

    value = NULL;

    if (id.type == NGX_SSL_CACHE_PATH
        && (rc = ngx_file_info(id.data, &fi)) != NGX_FILE_ERROR)
    {
        mtime = ngx_file_mtime(&fi);
        uniq = ngx_file_uniq(&fi);

    } else {
        rc = NGX_FILE_ERROR;
        mtime = 0;
        uniq = 0;
    }

    /* try to use a reference from the old cycle */

    old_cache = ngx_ssl_cache_get_old_conf(cf->cycle);


Review comment (performance):
In https://github.com/nginx/nginx/commit/5d5d9adccfeaff7d5926737ee5dfa43937fe5899 we assumed that `invalidate` can be ignored for `NGX_SSL_CACHE_DATA` only. Engines/providers/etc may cause the same mismatch.

Code after:
    ngx_file_uniq_t        uniq;
    ngx_file_info_t        fi;
    ngx_ssl_cache_t       *cache, *old_cache;
    ngx_ssl_cache_key_t    id;
    ngx_ssl_cache_type_t  *type;
    ngx_ssl_cache_node_t  *cn;

    *err = NULL;

    invalidate = index & NGX_SSL_CACHE_INVALIDATE;
    index &= ~NGX_SSL_CACHE_INVALIDATE;

    if (ngx_ssl_cache_init_key(cf->pool, index, path, &id) != NGX_OK) {
        return NULL;
    }

    if (id.type == NGX_SSL_CACHE_DATA) {
        invalidate = 0;
    }

    cache = (ngx_ssl_cache_t *) ngx_get_conf(cf->cycle->conf_ctx,
                                             ngx_openssl_cache_module);

    type = &ngx_ssl_cache_types[index];
    hash = ngx_murmur_hash2(id.data, id.len);

    cn = ngx_ssl_cache_lookup(cache, type, &id, hash);

    if (cn != NULL) {
        if (!invalidate) {
            return type->ref(err, cn->value);
        }

        type->free(cn->value);
        ngx_rbtree_delete(&cache->rbtree, &cn->node);
    }

    value = NULL;

    if (id.type == NGX_SSL_CACHE_PATH
        && (rc = ngx_file_info(id.data, &fi)) != NGX_FILE_ERROR)
    {
        mtime = ngx_file_mtime(&fi);
        uniq = ngx_file_uniq(&fi);

    } else {
        rc = NGX_FILE_ERROR;
        mtime = 0;
        uniq = 0;
    }