]> granicus.if.org Git - apache/blob - modules/http2/h2_conn.c
backport of mod_http2 v1.4.2
[apache] / modules / http2 / h2_conn.c
1 /* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
2  *
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15
16 #include <assert.h>
17
18 #include <ap_mpm.h>
19
20 #include <httpd.h>
21 #include <http_core.h>
22 #include <http_config.h>
23 #include <http_log.h>
24 #include <http_connection.h>
25 #include <http_protocol.h>
26 #include <http_request.h>
27
28 #include "h2_private.h"
29 #include "h2_config.h"
30 #include "h2_ctx.h"
31 #include "h2_filter.h"
32 #include "h2_mplx.h"
33 #include "h2_session.h"
34 #include "h2_stream.h"
35 #include "h2_h2.h"
36 #include "h2_task.h"
37 #include "h2_worker.h"
38 #include "h2_workers.h"
39 #include "h2_conn.h"
40 #include "h2_version.h"
41
42 static struct h2_workers *workers;
43
44 static h2_mpm_type_t mpm_type = H2_MPM_UNKNOWN;
45 static module *mpm_module;
46 static int async_mpm;
47 static apr_socket_t *dummy_socket;
48
49 static void check_modules(int force) 
50 {
51     static int checked = 0;
52     int i;
53
54     if (force || !checked) {
55         for (i = 0; ap_loaded_modules[i]; ++i) {
56             module *m = ap_loaded_modules[i];
57             
58             if (!strcmp("event.c", m->name)) {
59                 mpm_type = H2_MPM_EVENT;
60                 mpm_module = m;
61                 break;
62             }
63             else if (!strcmp("motorz.c", m->name)) {
64                 mpm_type = H2_MPM_MOTORZ;
65                 mpm_module = m;
66                 break;
67             }
68             else if (!strcmp("mpm_netware.c", m->name)) {
69                 mpm_type = H2_MPM_NETWARE;
70                 mpm_module = m;
71                 break;
72             }
73             else if (!strcmp("prefork.c", m->name)) {
74                 mpm_type = H2_MPM_PREFORK;
75                 mpm_module = m;
76                 break;
77             }
78             else if (!strcmp("simple_api.c", m->name)) {
79                 mpm_type = H2_MPM_SIMPLE;
80                 mpm_module = m;
81                 break;
82             }
83             else if (!strcmp("mpm_winnt.c", m->name)) {
84                 mpm_type = H2_MPM_WINNT;
85                 mpm_module = m;
86                 break;
87             }
88             else if (!strcmp("worker.c", m->name)) {
89                 mpm_type = H2_MPM_WORKER;
90                 mpm_module = m;
91                 break;
92             }
93         }
94         checked = 1;
95     }
96 }
97
98 apr_status_t h2_conn_child_init(apr_pool_t *pool, server_rec *s)
99 {
100     const h2_config *config = h2_config_sget(s);
101     apr_status_t status = APR_SUCCESS;
102     int minw, maxw, max_tx_handles, n;
103     int max_threads_per_child = 0;
104     int idle_secs = 0;
105
106     check_modules(1);
107     
108     ap_mpm_query(AP_MPMQ_MAX_THREADS, &max_threads_per_child);
109     
110     status = ap_mpm_query(AP_MPMQ_IS_ASYNC, &async_mpm);
111     if (status != APR_SUCCESS) {
112         /* some MPMs do not implemnent this */
113         async_mpm = 0;
114         status = APR_SUCCESS;
115     }
116
117     h2_config_init(pool);
118     
119     minw = h2_config_geti(config, H2_CONF_MIN_WORKERS);
120     maxw = h2_config_geti(config, H2_CONF_MAX_WORKERS);    
121     if (minw <= 0) {
122         minw = max_threads_per_child;
123     }
124     if (maxw <= 0) {
125         maxw = minw;
126     }
127     
128     /* How many file handles is it safe to use for transfer
129      * to the master connection to be streamed out? 
130      * Is there a portable APR rlimit on NOFILES? Have not
131      * found it. And if, how many of those would we set aside?
132      * This leads all into a process wide handle allocation strategy
133      * which ultimately would limit the number of accepted connections
134      * with the assumption of implicitly reserving n handles for every 
135      * connection and requiring modules with excessive needs to allocate
136      * from a central pool.
137      */
138     n = h2_config_geti(config, H2_CONF_SESSION_FILES);
139     if (n < 0) {
140         max_tx_handles = maxw * 2;
141     }
142     else {
143         max_tx_handles = maxw * n;
144     }
145     
146     ap_log_error(APLOG_MARK, APLOG_TRACE3, 0, s,
147                  "h2_workers: min=%d max=%d, mthrpchild=%d, tx_files=%d", 
148                  minw, maxw, max_threads_per_child, max_tx_handles);
149     workers = h2_workers_create(s, pool, minw, maxw, max_tx_handles);
150     
151     idle_secs = h2_config_geti(config, H2_CONF_MAX_WORKER_IDLE_SECS);
152     h2_workers_set_max_idle_secs(workers, idle_secs);
153  
154     ap_register_input_filter("H2_IN", h2_filter_core_input,
155                              NULL, AP_FTYPE_CONNECTION);
156    
157     status = h2_mplx_child_init(pool, s);
158
159     if (status == APR_SUCCESS) {
160         status = apr_socket_create(&dummy_socket, APR_INET, SOCK_STREAM,
161                                    APR_PROTO_TCP, pool);
162     }
163
164     return status;
165 }
166
167 h2_mpm_type_t h2_conn_mpm_type(void)
168 {
169     check_modules(0);
170     return mpm_type;
171 }
172
173 static module *h2_conn_mpm_module(void)
174 {
175     check_modules(0);
176     return mpm_module;
177 }
178
179 apr_status_t h2_conn_setup(h2_ctx *ctx, conn_rec *c, request_rec *r)
180 {
181     h2_session *session;
182     
183     if (!workers) {
184         ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(02911) 
185                       "workers not initialized");
186         return APR_EGENERAL;
187     }
188     
189     if (r) {
190         session = h2_session_rcreate(r, ctx, workers);
191     }
192     else {
193         session = h2_session_create(c, ctx, workers);
194     }
195
196     h2_ctx_session_set(ctx, session);
197     
198     return APR_SUCCESS;
199 }
200
201 apr_status_t h2_conn_run(struct h2_ctx *ctx, conn_rec *c)
202 {
203     apr_status_t status;
204     int mpm_state = 0;
205     
206     do {
207         if (c->cs) {
208             c->cs->sense = CONN_SENSE_DEFAULT;
209         }
210         status = h2_session_process(h2_ctx_session_get(ctx), async_mpm);
211         
212         if (APR_STATUS_IS_EOF(status)) {
213             ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, c, APLOGNO(03045)
214                           "h2_session(%ld): process, closing conn", c->id);
215             c->keepalive = AP_CONN_CLOSE;
216         }
217         else {
218             c->keepalive = AP_CONN_KEEPALIVE;
219         }
220         
221         if (ap_mpm_query(AP_MPMQ_MPM_STATE, &mpm_state)) {
222             break;
223         }
224     } while (!async_mpm
225              && c->keepalive == AP_CONN_KEEPALIVE 
226              && mpm_state != AP_MPMQ_STOPPING);
227     
228     return DONE;
229 }
230
231 apr_status_t h2_conn_pre_close(struct h2_ctx *ctx, conn_rec *c)
232 {
233     apr_status_t status;
234     
235     status = h2_session_pre_close(h2_ctx_session_get(ctx), async_mpm);
236     if (status == APR_SUCCESS) {
237         return DONE; /* This is the same, right? */
238     }
239     return status;
240 }
241
242
243 conn_rec *h2_slave_create(conn_rec *master, apr_pool_t *parent,
244                           apr_allocator_t *allocator)
245 {
246     apr_pool_t *pool;
247     conn_rec *c;
248     void *cfg;
249     
250     AP_DEBUG_ASSERT(master);
251     ap_log_cerror(APLOG_MARK, APLOG_TRACE3, 0, master,
252                   "h2_conn(%ld): create slave", master->id);
253     
254     /* We create a pool with its own allocator to be used for
255      * processing a request. This is the only way to have the processing
256      * independant of its parent pool in the sense that it can work in
257      * another thread.
258      */
259     if (!allocator) {
260         apr_allocator_create(&allocator);
261     }
262     apr_pool_create_ex(&pool, parent, NULL, allocator);
263     apr_pool_tag(pool, "h2_slave_conn");
264     apr_allocator_owner_set(allocator, pool);
265     
266     c = (conn_rec *) apr_palloc(pool, sizeof(conn_rec));
267     if (c == NULL) {
268         ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_ENOMEM, master, 
269                       APLOGNO(02913) "h2_task: creating conn");
270         return NULL;
271     }
272     
273     memcpy(c, master, sizeof(conn_rec));
274            
275     /* Replace these */
276     c->master                 = master;
277     c->pool                   = pool;   
278     c->conn_config            = ap_create_conn_config(pool);
279     c->notes                  = apr_table_make(pool, 5);
280     c->input_filters          = NULL;
281     c->output_filters         = NULL;
282     c->bucket_alloc           = apr_bucket_alloc_create(pool);
283     c->data_in_input_filters  = 0;
284     c->data_in_output_filters = 0;
285     c->clogging_input_filters = 1;
286     c->log                    = NULL;
287     c->log_id                 = NULL;
288     /* Simulate that we had already a request on this connection. */
289     c->keepalives             = 1;
290     /* We cannot install the master connection socket on the slaves, as
291      * modules mess with timeouts/blocking of the socket, with
292      * unwanted side effects to the master connection processing.
293      * Fortunately, since we never use the slave socket, we can just install
294      * a single, process-wide dummy and everyone is happy.
295      */
296     ap_set_module_config(c->conn_config, &core_module, dummy_socket);
297     /* TODO: these should be unique to this thread */
298     c->sbh                    = master->sbh;
299     /* TODO: not all mpm modules have learned about slave connections yet.
300      * copy their config from master to slave.
301      */
302     if (h2_conn_mpm_module()) {
303         cfg = ap_get_module_config(master->conn_config, h2_conn_mpm_module());
304         ap_set_module_config(c->conn_config, h2_conn_mpm_module(), cfg);
305     }
306
307     return c;
308 }
309
310 void h2_slave_destroy(conn_rec *slave, apr_allocator_t **pallocator)
311 {
312     apr_pool_t *parent;
313     apr_allocator_t *allocator = apr_pool_allocator_get(slave->pool);
314     ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, slave,
315                   "h2_slave_conn(%ld): destroy (task=%s)", slave->id,
316                   apr_table_get(slave->notes, H2_TASK_ID_NOTE));
317     /* Attache the allocator to the parent pool and return it for
318      * reuse, otherwise the own is still the slave pool and it will
319      * get destroyed with it. */
320     parent = apr_pool_parent_get(slave->pool);
321     if (pallocator && parent) {
322         apr_allocator_owner_set(allocator, parent);
323         *pallocator = allocator;
324     }
325     apr_pool_destroy(slave->pool);
326 }