/** * A h2_mplx needs to be thread-safe *and* if will be called by * the h2_session thread *and* the h2_worker threads. Therefore: * - calls are protected by a mutex lock, m->lock * - the pool needs its own allocator, since apr_allocator_t are * not re-entrant. The separate allocator works without a * separate lock since we already protect h2_mplx itself. * Since HTTP/2 connections can be expected to live longer than * their HTTP/1 cousins, the separate allocator seems to work better * than protecting a shared h2_session one with an own lock. */ h2_mplx *h2_mplx_create(conn_rec *c, apr_pool_t *parent, h2_workers *workers) { apr_status_t status = APR_SUCCESS; h2_config *conf = h2_config_get(c); apr_allocator_t *allocator = NULL; h2_mplx *m; AP_DEBUG_ASSERT(conf); status = apr_allocator_create(&allocator); if (status != APR_SUCCESS) { return NULL; } m = apr_pcalloc(parent, sizeof(h2_mplx)); if (m) { m->id = c->id; APR_RING_ELEM_INIT(m, link); apr_atomic_set32(&m->refs, 1); m->c = c; apr_pool_create_ex(&m->pool, parent, NULL, allocator); if (!m->pool) { return NULL; } apr_allocator_owner_set(allocator, m->pool); status = apr_thread_mutex_create(&m->lock, APR_THREAD_MUTEX_DEFAULT, m->pool); if (status != APR_SUCCESS) { h2_mplx_destroy(m); return NULL; } m->bucket_alloc = apr_bucket_alloc_create(m->pool); m->q = h2_tq_create(m->id, m->pool); m->stream_ios = h2_io_set_create(m->pool); m->ready_ios = h2_io_set_create(m->pool); m->closed = h2_stream_set_create(m->pool); m->stream_max_mem = h2_config_geti(conf, H2_CONF_STREAM_MAX_MEM); m->workers = workers; m->file_handles_allowed = h2_config_geti(conf, H2_CONF_SESSION_FILES); } return m; }
static h2_session *h2_session_create_int(conn_rec *c, request_rec *r, h2_config *config, h2_workers *workers) { nghttp2_session_callbacks *callbacks = NULL; nghttp2_option *options = NULL; apr_pool_t *pool = NULL; apr_status_t status = apr_pool_create(&pool, r? r->pool : c->pool); h2_session *session; if (status != APR_SUCCESS) { return NULL; } session = apr_pcalloc(pool, sizeof(h2_session)); if (session) { int rv; session->id = c->id; session->c = c; session->r = r; session->max_stream_count = h2_config_geti(config, H2_CONF_MAX_STREAMS); session->max_stream_mem = h2_config_geti(config, H2_CONF_STREAM_MAX_MEM); session->pool = pool; status = apr_thread_cond_create(&session->iowait, session->pool); if (status != APR_SUCCESS) { return NULL; } session->streams = h2_stream_set_create(session->pool); session->workers = workers; session->mplx = h2_mplx_create(c, session->pool, workers); h2_conn_io_init(&session->io, c); session->bbtmp = apr_brigade_create(session->pool, c->bucket_alloc); status = init_callbacks(c, &callbacks); if (status != APR_SUCCESS) { ap_log_cerror(APLOG_MARK, APLOG_ERR, status, c, APLOGNO(02927) "nghttp2: error in init_callbacks"); h2_session_destroy(session); return NULL; } rv = nghttp2_option_new(&options); if (rv != 0) { ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_EGENERAL, c, APLOGNO(02928) "nghttp2_option_new: %s", nghttp2_strerror(rv)); h2_session_destroy(session); return NULL; } nghttp2_option_set_peer_max_concurrent_streams(options, (uint32_t)session->max_stream_count); /* We need to handle window updates ourself, otherwise we * get flooded by nghttp2. */ nghttp2_option_set_no_auto_window_update(options, 1); rv = nghttp2_session_server_new2(&session->ngh2, callbacks, session, options); nghttp2_session_callbacks_del(callbacks); nghttp2_option_del(options); if (rv != 0) { ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_EGENERAL, c, APLOGNO(02929) "nghttp2_session_server_new: %s", nghttp2_strerror(rv)); h2_session_destroy(session); return NULL; } } return session; }
static h2_session *h2_session_create_int(conn_rec *c, request_rec *r, h2_config *config) { nghttp2_session_callbacks *callbacks = NULL; nghttp2_option *options = NULL; apr_allocator_t *allocator = NULL; apr_status_t status = apr_allocator_create(&allocator); if (status != APR_SUCCESS) { return NULL; } apr_pool_t *pool = NULL; status = apr_pool_create_ex(&pool, c->pool, NULL, allocator); if (status != APR_SUCCESS) { return NULL; } h2_session *session = apr_pcalloc(pool, sizeof(h2_session)); if (session) { session->id = c->id; session->allocator = allocator; session->pool = pool; status = apr_thread_mutex_create(&session->alock, APR_THREAD_MUTEX_DEFAULT, session->pool); if (status != APR_SUCCESS) { return NULL; } apr_allocator_mutex_set(session->allocator, session->alock); status = apr_thread_cond_create(&session->iowait, session->pool); if (status != APR_SUCCESS) { return NULL; } session->c = c; session->r = r; session->ngh2 = NULL; session->streams = h2_stream_set_create(session->pool); session->zombies = h2_stream_set_create(session->pool); session->mplx = h2_mplx_create(c, session->pool); h2_conn_io_init(&session->io, c, 0); apr_status_t status = init_callbacks(c, &callbacks); if (status != APR_SUCCESS) { ap_log_cerror(APLOG_MARK, APLOG_ERR, status, c, "nghttp2: error in init_callbacks"); h2_session_destroy(session); return NULL; } int rv = nghttp2_option_new(&options); if (rv != 0) { ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_EGENERAL, c, "nghttp2_option_new: %s", nghttp2_strerror(rv)); h2_session_destroy(session); return NULL; } /* Nowadays, we handle the preface ourselves. We had problems * with nghttp2 internal state machine when traffic action occured * before the preface was read. */ nghttp2_option_set_recv_client_preface(options, 1); /* Set a value, to be observed before we receive any SETTINGS * from the client. */ nghttp2_option_set_peer_max_concurrent_streams(options, 100); /* We need to handle window updates ourself, otherwise we * get flooded by nghttp2. */ nghttp2_option_set_no_auto_window_update(options, 1); rv = nghttp2_session_server_new2(&session->ngh2, callbacks, session, options); nghttp2_session_callbacks_del(callbacks); nghttp2_option_del(options); if (rv != 0) { ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_EGENERAL, c, "nghttp2_session_server_new: %s", nghttp2_strerror(rv)); h2_session_destroy(session); return NULL; } } return session; }