|
Packit |
90a5c9 |
/* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
Packit |
90a5c9 |
* contributor license agreements. See the NOTICE file distributed with
|
|
Packit |
90a5c9 |
* this work for additional information regarding copyright ownership.
|
|
Packit |
90a5c9 |
* The ASF licenses this file to You under the Apache License, Version 2.0
|
|
Packit |
90a5c9 |
* (the "License"); you may not use this file except in compliance with
|
|
Packit |
90a5c9 |
* the License. You may obtain a copy of the License at
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* http://www.apache.org/licenses/LICENSE-2.0
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* Unless required by applicable law or agreed to in writing, software
|
|
Packit |
90a5c9 |
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
Packit |
90a5c9 |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
Packit |
90a5c9 |
* See the License for the specific language governing permissions and
|
|
Packit |
90a5c9 |
* limitations under the License.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
#include <assert.h>
|
|
Packit |
90a5c9 |
#include <apr_strings.h>
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
#include <ap_mpm.h>
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
#include <httpd.h>
|
|
Packit |
90a5c9 |
#include <http_core.h>
|
|
Packit |
90a5c9 |
#include <http_config.h>
|
|
Packit |
90a5c9 |
#include <http_log.h>
|
|
Packit |
90a5c9 |
#include <http_connection.h>
|
|
Packit |
90a5c9 |
#include <http_protocol.h>
|
|
Packit |
90a5c9 |
#include <http_request.h>
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
#include <mpm_common.h>
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
#include "h2_private.h"
|
|
Packit |
90a5c9 |
#include "h2.h"
|
|
Packit |
90a5c9 |
#include "h2_config.h"
|
|
Packit |
90a5c9 |
#include "h2_ctx.h"
|
|
Packit |
90a5c9 |
#include "h2_filter.h"
|
|
Packit |
90a5c9 |
#include "h2_mplx.h"
|
|
Packit |
90a5c9 |
#include "h2_session.h"
|
|
Packit |
90a5c9 |
#include "h2_stream.h"
|
|
Packit |
90a5c9 |
#include "h2_h2.h"
|
|
Packit |
90a5c9 |
#include "h2_task.h"
|
|
Packit |
90a5c9 |
#include "h2_workers.h"
|
|
Packit |
90a5c9 |
#include "h2_conn.h"
|
|
Packit |
90a5c9 |
#include "h2_version.h"
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static struct h2_workers *workers;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static h2_mpm_type_t mpm_type = H2_MPM_UNKNOWN;
|
|
Packit |
90a5c9 |
static module *mpm_module;
|
|
Packit |
90a5c9 |
static int async_mpm;
|
|
Packit |
90a5c9 |
static int mpm_supported = 1;
|
|
Packit |
90a5c9 |
static apr_socket_t *dummy_socket;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static void check_modules(int force)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
static int checked = 0;
|
|
Packit |
90a5c9 |
int i;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (force || !checked) {
|
|
Packit |
90a5c9 |
for (i = 0; ap_loaded_modules[i]; ++i) {
|
|
Packit |
90a5c9 |
module *m = ap_loaded_modules[i];
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (!strcmp("event.c", m->name)) {
|
|
Packit |
90a5c9 |
mpm_type = H2_MPM_EVENT;
|
|
Packit |
90a5c9 |
mpm_module = m;
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (!strcmp("motorz.c", m->name)) {
|
|
Packit |
90a5c9 |
mpm_type = H2_MPM_MOTORZ;
|
|
Packit |
90a5c9 |
mpm_module = m;
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (!strcmp("mpm_netware.c", m->name)) {
|
|
Packit |
90a5c9 |
mpm_type = H2_MPM_NETWARE;
|
|
Packit |
90a5c9 |
mpm_module = m;
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (!strcmp("prefork.c", m->name)) {
|
|
Packit |
90a5c9 |
mpm_type = H2_MPM_PREFORK;
|
|
Packit |
90a5c9 |
mpm_module = m;
|
|
Packit |
90a5c9 |
/* While http2 can work really well on prefork, it collides
|
|
Packit |
90a5c9 |
* today's use case for prefork: runnning single-thread app engines
|
|
Packit |
90a5c9 |
* like php. If we restrict h2_workers to 1 per process, php will
|
|
Packit |
90a5c9 |
* work fine, but browser will be limited to 1 active request at a
|
|
Packit |
90a5c9 |
* time. */
|
|
Packit |
90a5c9 |
mpm_supported = 0;
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (!strcmp("simple_api.c", m->name)) {
|
|
Packit |
90a5c9 |
mpm_type = H2_MPM_SIMPLE;
|
|
Packit |
90a5c9 |
mpm_module = m;
|
|
Packit |
90a5c9 |
mpm_supported = 0;
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (!strcmp("mpm_winnt.c", m->name)) {
|
|
Packit |
90a5c9 |
mpm_type = H2_MPM_WINNT;
|
|
Packit |
90a5c9 |
mpm_module = m;
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (!strcmp("worker.c", m->name)) {
|
|
Packit |
90a5c9 |
mpm_type = H2_MPM_WORKER;
|
|
Packit |
90a5c9 |
mpm_module = m;
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
checked = 1;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
apr_status_t h2_conn_child_init(apr_pool_t *pool, server_rec *s)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
const h2_config *config = h2_config_sget(s);
|
|
Packit |
90a5c9 |
apr_status_t status = APR_SUCCESS;
|
|
Packit |
90a5c9 |
int minw, maxw;
|
|
Packit |
90a5c9 |
int max_threads_per_child = 0;
|
|
Packit |
90a5c9 |
int idle_secs = 0;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
check_modules(1);
|
|
Packit |
90a5c9 |
ap_mpm_query(AP_MPMQ_MAX_THREADS, &max_threads_per_child);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
status = ap_mpm_query(AP_MPMQ_IS_ASYNC, &async_mpm);
|
|
Packit |
90a5c9 |
if (status != APR_SUCCESS) {
|
|
Packit |
90a5c9 |
/* some MPMs do not implemnent this */
|
|
Packit |
90a5c9 |
async_mpm = 0;
|
|
Packit |
90a5c9 |
status = APR_SUCCESS;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
h2_config_init(pool);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
h2_get_num_workers(s, &minw, &maxw);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
idle_secs = h2_config_geti(config, H2_CONF_MAX_WORKER_IDLE_SECS);
|
|
Packit |
90a5c9 |
ap_log_error(APLOG_MARK, APLOG_TRACE3, 0, s,
|
|
Packit |
90a5c9 |
"h2_workers: min=%d max=%d, mthrpchild=%d, idle_secs=%d",
|
|
Packit |
90a5c9 |
minw, maxw, max_threads_per_child, idle_secs);
|
|
Packit |
90a5c9 |
workers = h2_workers_create(s, pool, minw, maxw, idle_secs);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
ap_register_input_filter("H2_IN", h2_filter_core_input,
|
|
Packit |
90a5c9 |
NULL, AP_FTYPE_CONNECTION);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
status = h2_mplx_child_init(pool, s);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (status == APR_SUCCESS) {
|
|
Packit |
90a5c9 |
status = apr_socket_create(&dummy_socket, APR_INET, SOCK_STREAM,
|
|
Packit |
90a5c9 |
APR_PROTO_TCP, pool);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
return status;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
h2_mpm_type_t h2_conn_mpm_type(void)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
check_modules(0);
|
|
Packit |
90a5c9 |
return mpm_type;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
const char *h2_conn_mpm_name(void)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
check_modules(0);
|
|
Packit |
90a5c9 |
return mpm_module? mpm_module->name : "unknown";
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
int h2_mpm_supported(void)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
check_modules(0);
|
|
Packit |
90a5c9 |
return mpm_supported;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static module *h2_conn_mpm_module(void)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
check_modules(0);
|
|
Packit |
90a5c9 |
return mpm_module;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
apr_status_t h2_conn_setup(h2_ctx *ctx, conn_rec *c, request_rec *r)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
h2_session *session;
|
|
Packit |
90a5c9 |
apr_status_t status;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (!workers) {
|
|
Packit |
90a5c9 |
ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(02911)
|
|
Packit |
90a5c9 |
"workers not initialized");
|
|
Packit |
90a5c9 |
return APR_EGENERAL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (r) {
|
|
Packit |
90a5c9 |
status = h2_session_rcreate(&session, r, ctx, workers);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
status = h2_session_create(&session, c, ctx, workers);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (status == APR_SUCCESS) {
|
|
Packit |
90a5c9 |
h2_ctx_session_set(ctx, session);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return status;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
apr_status_t h2_conn_run(struct h2_ctx *ctx, conn_rec *c)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
apr_status_t status;
|
|
Packit |
90a5c9 |
int mpm_state = 0;
|
|
Packit |
90a5c9 |
h2_session *session = h2_ctx_session_get(ctx);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
ap_assert(session);
|
|
Packit |
90a5c9 |
do {
|
|
Packit |
90a5c9 |
if (c->cs) {
|
|
Packit |
90a5c9 |
c->cs->sense = CONN_SENSE_DEFAULT;
|
|
Packit |
90a5c9 |
c->cs->state = CONN_STATE_HANDLER;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
status = h2_session_process(session, async_mpm);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (APR_STATUS_IS_EOF(status)) {
|
|
Packit |
90a5c9 |
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, c,
|
|
Packit |
90a5c9 |
H2_SSSN_LOG(APLOGNO(03045), session,
|
|
Packit |
90a5c9 |
"process, closing conn"));
|
|
Packit |
90a5c9 |
c->keepalive = AP_CONN_CLOSE;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
c->keepalive = AP_CONN_KEEPALIVE;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (ap_mpm_query(AP_MPMQ_MPM_STATE, &mpm_state)) {
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
} while (!async_mpm
|
|
Packit |
90a5c9 |
&& c->keepalive == AP_CONN_KEEPALIVE
|
|
Packit |
90a5c9 |
&& mpm_state != AP_MPMQ_STOPPING);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (c->cs) {
|
|
Packit |
90a5c9 |
switch (session->state) {
|
|
Packit |
90a5c9 |
case H2_SESSION_ST_INIT:
|
|
Packit |
90a5c9 |
case H2_SESSION_ST_IDLE:
|
|
Packit |
90a5c9 |
case H2_SESSION_ST_BUSY:
|
|
Packit |
90a5c9 |
case H2_SESSION_ST_WAIT:
|
|
Packit |
90a5c9 |
c->cs->state = CONN_STATE_WRITE_COMPLETION;
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
case H2_SESSION_ST_CLEANUP:
|
|
Packit |
90a5c9 |
case H2_SESSION_ST_DONE:
|
|
Packit |
90a5c9 |
default:
|
|
Packit |
90a5c9 |
c->cs->state = CONN_STATE_LINGER;
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
return APR_SUCCESS;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
apr_status_t h2_conn_pre_close(struct h2_ctx *ctx, conn_rec *c)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
h2_session *session = h2_ctx_session_get(ctx);
|
|
Packit |
90a5c9 |
if (session) {
|
|
Packit |
90a5c9 |
apr_status_t status = h2_session_pre_close(session, async_mpm);
|
|
Packit |
90a5c9 |
return (status == APR_SUCCESS)? DONE : status;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return DONE;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
conn_rec *h2_slave_create(conn_rec *master, int slave_id, apr_pool_t *parent)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
apr_allocator_t *allocator;
|
|
Packit |
90a5c9 |
apr_status_t status;
|
|
Packit |
90a5c9 |
apr_pool_t *pool;
|
|
Packit |
90a5c9 |
conn_rec *c;
|
|
Packit |
90a5c9 |
void *cfg;
|
|
Packit |
90a5c9 |
module *mpm;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
ap_assert(master);
|
|
Packit |
90a5c9 |
ap_log_cerror(APLOG_MARK, APLOG_TRACE3, 0, master,
|
|
Packit |
90a5c9 |
"h2_stream(%ld-%d): create slave", master->id, slave_id);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* We create a pool with its own allocator to be used for
|
|
Packit |
90a5c9 |
* processing a request. This is the only way to have the processing
|
|
Packit |
90a5c9 |
* independant of its parent pool in the sense that it can work in
|
|
Packit |
90a5c9 |
* another thread. Also, the new allocator needs its own mutex to
|
|
Packit |
90a5c9 |
* synchronize sub-pools.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
apr_allocator_create(&allocator);
|
|
Packit |
90a5c9 |
apr_allocator_max_free_set(allocator, ap_max_mem_free);
|
|
Packit |
90a5c9 |
status = apr_pool_create_ex(&pool, parent, NULL, allocator);
|
|
Packit |
90a5c9 |
if (status != APR_SUCCESS) {
|
|
Packit |
90a5c9 |
ap_log_cerror(APLOG_MARK, APLOG_ERR, status, master,
|
|
Packit |
90a5c9 |
APLOGNO(10004) "h2_session(%ld-%d): create slave pool",
|
|
Packit |
90a5c9 |
master->id, slave_id);
|
|
Packit |
90a5c9 |
return NULL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
apr_allocator_owner_set(allocator, pool);
|
|
Packit |
90a5c9 |
apr_pool_tag(pool, "h2_slave_conn");
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
c = (conn_rec *) apr_palloc(pool, sizeof(conn_rec));
|
|
Packit |
90a5c9 |
if (c == NULL) {
|
|
Packit |
90a5c9 |
ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_ENOMEM, master,
|
|
Packit |
90a5c9 |
APLOGNO(02913) "h2_session(%ld-%d): create slave",
|
|
Packit |
90a5c9 |
master->id, slave_id);
|
|
Packit |
90a5c9 |
apr_pool_destroy(pool);
|
|
Packit |
90a5c9 |
return NULL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
memcpy(c, master, sizeof(conn_rec));
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
c->master = master;
|
|
Packit |
90a5c9 |
c->pool = pool;
|
|
Packit |
90a5c9 |
c->conn_config = ap_create_conn_config(pool);
|
|
Packit |
90a5c9 |
c->notes = apr_table_make(pool, 5);
|
|
Packit |
90a5c9 |
c->input_filters = NULL;
|
|
Packit |
90a5c9 |
c->output_filters = NULL;
|
|
Packit |
90a5c9 |
c->bucket_alloc = apr_bucket_alloc_create(pool);
|
|
Packit |
90a5c9 |
c->data_in_input_filters = 0;
|
|
Packit |
90a5c9 |
c->data_in_output_filters = 0;
|
|
Packit |
90a5c9 |
/* prevent mpm_event from making wrong assumptions about this connection,
|
|
Packit |
90a5c9 |
* like e.g. using its socket for an async read check. */
|
|
Packit |
90a5c9 |
c->clogging_input_filters = 1;
|
|
Packit |
90a5c9 |
c->log = NULL;
|
|
Packit |
90a5c9 |
c->log_id = apr_psprintf(pool, "%ld-%d",
|
|
Packit |
90a5c9 |
master->id, slave_id);
|
|
Packit |
90a5c9 |
c->aborted = 0;
|
|
Packit |
90a5c9 |
/* We cannot install the master connection socket on the slaves, as
|
|
Packit |
90a5c9 |
* modules mess with timeouts/blocking of the socket, with
|
|
Packit |
90a5c9 |
* unwanted side effects to the master connection processing.
|
|
Packit |
90a5c9 |
* Fortunately, since we never use the slave socket, we can just install
|
|
Packit |
90a5c9 |
* a single, process-wide dummy and everyone is happy.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
ap_set_module_config(c->conn_config, &core_module, dummy_socket);
|
|
Packit |
90a5c9 |
/* TODO: these should be unique to this thread */
|
|
Packit |
90a5c9 |
c->sbh = master->sbh;
|
|
Packit |
90a5c9 |
/* TODO: not all mpm modules have learned about slave connections yet.
|
|
Packit |
90a5c9 |
* copy their config from master to slave.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
if ((mpm = h2_conn_mpm_module()) != NULL) {
|
|
Packit |
90a5c9 |
cfg = ap_get_module_config(master->conn_config, mpm);
|
|
Packit |
90a5c9 |
ap_set_module_config(c->conn_config, mpm, cfg);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, c,
|
|
Packit |
90a5c9 |
"h2_stream(%ld-%d): created slave", master->id, slave_id);
|
|
Packit |
90a5c9 |
return c;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
void h2_slave_destroy(conn_rec *slave)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, slave,
|
|
Packit |
90a5c9 |
"h2_stream(%s): destroy slave",
|
|
Packit |
90a5c9 |
apr_table_get(slave->notes, H2_TASK_ID_NOTE));
|
|
Packit |
90a5c9 |
slave->sbh = NULL;
|
|
Packit |
90a5c9 |
apr_pool_destroy(slave->pool);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
apr_status_t h2_slave_run_pre_connection(conn_rec *slave, apr_socket_t *csd)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
if (slave->keepalives == 0) {
|
|
Packit |
90a5c9 |
/* Simulate that we had already a request on this connection. Some
|
|
Packit |
90a5c9 |
* hooks trigger special behaviour when keepalives is 0.
|
|
Packit |
90a5c9 |
* (Not necessarily in pre_connection, but later. Set it here, so it
|
|
Packit |
90a5c9 |
* is in place.) */
|
|
Packit |
90a5c9 |
slave->keepalives = 1;
|
|
Packit |
90a5c9 |
return ap_run_pre_connection(slave, csd);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return APR_SUCCESS;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|