]> granicus.if.org Git - apache/blob - modules/http2/h2_session.c
5b46838d94dd46145916ed9ebdc1a84d748c6cbb
[apache] / modules / http2 / h2_session.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 #include <apr_thread_cond.h>
18 #include <apr_base64.h>
19 #include <apr_strings.h>
20
21 #include <httpd.h>
22 #include <http_core.h>
23 #include <http_config.h>
24 #include <http_log.h>
25
26 #include "h2_private.h"
27 #include "h2_config.h"
28 #include "h2_h2.h"
29 #include "h2_mplx.h"
30 #include "h2_response.h"
31 #include "h2_stream.h"
32 #include "h2_stream_set.h"
33 #include "h2_from_h1.h"
34 #include "h2_task.h"
35 #include "h2_session.h"
36 #include "h2_util.h"
37 #include "h2_version.h"
38 #include "h2_workers.h"
39
40 static int frame_print(const nghttp2_frame *frame, char *buffer, size_t maxlen);
41
42 static int h2_session_status_from_apr_status(apr_status_t rv)
43 {
44     switch (rv) {
45         case APR_SUCCESS:
46             return NGHTTP2_NO_ERROR;
47         case APR_EAGAIN:
48         case APR_TIMEUP:
49             return NGHTTP2_ERR_WOULDBLOCK;
50         case APR_EOF:
51             return NGHTTP2_ERR_EOF;
52         default:
53             return NGHTTP2_ERR_PROTO;
54     }
55 }
56
57 static int stream_open(h2_session *session, int stream_id)
58 {
59     h2_stream * stream;
60     if (session->aborted) {
61         return NGHTTP2_ERR_CALLBACK_FAILURE;
62     }
63     
64     stream = h2_mplx_open_io(session->mplx, stream_id);
65     if (stream) {
66         h2_stream_set_add(session->streams, stream);
67         
68         ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c,
69                       "h2_session: stream(%ld-%d): opened",
70                       session->id, stream_id);
71         
72         return 0;
73     }
74     
75     ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_ENOMEM, session->c,
76                   APLOGNO(02918) 
77                   "h2_session: stream(%ld-%d): unable to create",
78                   session->id, stream_id);
79     return NGHTTP2_ERR_INVALID_STREAM_ID;
80 }
81
82 static apr_status_t stream_end_headers(h2_session *session,
83                                        h2_stream *stream, int eos)
84 {
85     (void)session;
86     return h2_stream_write_eoh(stream, eos);
87 }
88
89 static apr_status_t send_data(h2_session *session, const char *data, 
90                               apr_size_t length);
91
92 /*
93  * Callback when nghttp2 wants to send bytes back to the client.
94  */
95 static ssize_t send_cb(nghttp2_session *ngh2,
96                        const uint8_t *data, size_t length,
97                        int flags, void *userp)
98 {
99     h2_session *session = (h2_session *)userp;
100     apr_status_t status = send_data(session, (const char *)data, length);
101     
102     if (status == APR_SUCCESS) {
103         return length;
104     }
105     if (status == APR_EAGAIN || status == APR_TIMEUP) {
106         return NGHTTP2_ERR_WOULDBLOCK;
107     }
108     ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, session->c,
109                   "h2_session: send error");
110     return h2_session_status_from_apr_status(status);
111 }
112
113 static int on_invalid_frame_recv_cb(nghttp2_session *ngh2,
114                                     const nghttp2_frame *frame,
115                                     int error, void *userp)
116 {
117     h2_session *session = (h2_session *)userp;
118     (void)ngh2;
119     
120     if (session->aborted) {
121         return NGHTTP2_ERR_CALLBACK_FAILURE;
122     }
123     if (APLOGctrace2(session->c)) {
124         char buffer[256];
125         
126         frame_print(frame, buffer, sizeof(buffer)/sizeof(buffer[0]));
127         ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, session->c,
128                       "h2_session: callback on_invalid_frame_recv error=%d %s",
129                       error, buffer);
130     }
131     return 0;
132 }
133
134 static int on_data_chunk_recv_cb(nghttp2_session *ngh2, uint8_t flags,
135                                  int32_t stream_id,
136                                  const uint8_t *data, size_t len, void *userp)
137 {
138     int rv;
139     h2_session *session = (h2_session *)userp;
140     h2_stream * stream;
141     apr_status_t status;
142     
143     if (session->aborted) {
144         return NGHTTP2_ERR_CALLBACK_FAILURE;
145     }
146     stream = h2_stream_set_get(session->streams, stream_id);
147     if (!stream) {
148         ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, session->c,
149                       APLOGNO(02919) 
150                       "h2_session:  stream(%ld-%d): on_data_chunk for unknown stream",
151                       session->id, (int)stream_id);
152         rv = nghttp2_submit_rst_stream(ngh2, NGHTTP2_FLAG_NONE, stream_id,
153                                        NGHTTP2_INTERNAL_ERROR);
154         if (nghttp2_is_fatal(rv)) {
155             return NGHTTP2_ERR_CALLBACK_FAILURE;
156         }
157         return 0;
158     }
159     
160     status = h2_stream_write_data(stream, (const char *)data, len);
161     ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, session->c,
162                   "h2_stream(%ld-%d): written DATA, length %d",
163                   session->id, stream_id, (int)len);
164     if (status != APR_SUCCESS) {
165         rv = nghttp2_submit_rst_stream(ngh2, NGHTTP2_FLAG_NONE, stream_id,
166                                        NGHTTP2_INTERNAL_ERROR);
167         if (nghttp2_is_fatal(rv)) {
168             return NGHTTP2_ERR_CALLBACK_FAILURE;
169         }
170     }
171     return 0;
172 }
173
174 static int before_frame_send_cb(nghttp2_session *ngh2,
175                                 const nghttp2_frame *frame,
176                                 void *userp)
177 {
178     h2_session *session = (h2_session *)userp;
179     (void)ngh2;
180
181     if (session->aborted) {
182         return NGHTTP2_ERR_CALLBACK_FAILURE;
183     }
184     if (APLOGctrace2(session->c)) {
185         char buffer[256];
186         frame_print(frame, buffer, sizeof(buffer)/sizeof(buffer[0]));
187         ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c,
188                       "h2_session(%ld): before_frame_send %s", 
189                       session->id, buffer);
190     }
191     return 0;
192 }
193
194 static int on_frame_send_cb(nghttp2_session *ngh2,
195                             const nghttp2_frame *frame,
196                             void *userp)
197 {
198     h2_session *session = (h2_session *)userp;
199     (void)ngh2; (void)frame;
200     ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, session->c,
201                   "h2_session(%ld): on_frame_send", session->id);
202     return 0;
203 }
204
205 static int on_frame_not_send_cb(nghttp2_session *ngh2,
206                                 const nghttp2_frame *frame,
207                                 int lib_error_code, void *userp)
208 {
209     h2_session *session = (h2_session *)userp;
210     (void)ngh2;
211     
212     if (APLOGctrace2(session->c)) {
213         char buffer[256];
214         
215         frame_print(frame, buffer, sizeof(buffer)/sizeof(buffer[0]));
216         ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c,
217                       "h2_session: callback on_frame_not_send error=%d %s",
218                       lib_error_code, buffer);
219     }
220     return 0;
221 }
222
223 static apr_status_t stream_destroy(h2_session *session, h2_stream *stream) 
224 {
225     ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c,
226                   "h2_stream(%ld-%d): closing", session->id, (int)stream->id);
227     h2_stream_set_remove(session->streams, stream);
228     return h2_mplx_cleanup_stream(session->mplx, stream);
229 }
230
231 static int on_stream_close_cb(nghttp2_session *ngh2, int32_t stream_id,
232                               uint32_t error_code, void *userp)
233 {
234     h2_session *session = (h2_session *)userp;
235     h2_stream *stream;
236     
237     if (session->aborted) {
238         return NGHTTP2_ERR_CALLBACK_FAILURE;
239     }
240     stream = h2_stream_set_get(session->streams, stream_id);
241     if (stream) {
242         stream_destroy(session, stream);
243     }
244     
245     if (error_code) {
246         ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c,
247                       "h2_stream(%ld-%d): close error %d",
248                       session->id, (int)stream_id, error_code);
249     }
250     
251     return 0;
252 }
253
254 static int on_begin_headers_cb(nghttp2_session *ngh2,
255                                const nghttp2_frame *frame, void *userp)
256 {
257     /* This starts a new stream. */
258     int rv = stream_open((h2_session *)userp, frame->hd.stream_id);
259     if (rv != NGHTTP2_ERR_CALLBACK_FAILURE) {
260       /* on_header_cb or on_frame_recv_cb will dectect that stream
261          does not exist and submit RST_STREAM. */
262       return 0;
263     }
264     return NGHTTP2_ERR_CALLBACK_FAILURE;
265 }
266
267 static int on_header_cb(nghttp2_session *ngh2, const nghttp2_frame *frame,
268                         const uint8_t *name, size_t namelen,
269                         const uint8_t *value, size_t valuelen,
270                         uint8_t flags,
271                         void *userp)
272 {
273     h2_session *session = (h2_session *)userp;
274     h2_stream * stream;
275     apr_status_t status;
276     
277     if (session->aborted) {
278         return NGHTTP2_ERR_CALLBACK_FAILURE;
279     }
280     stream = h2_stream_set_get(session->streams,
281                                            frame->hd.stream_id);
282     if (!stream) {
283         ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, session->c,
284                       APLOGNO(02920) 
285                       "h2_session:  stream(%ld-%d): on_header for unknown stream",
286                       session->id, (int)frame->hd.stream_id);
287         return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
288     }
289     
290     status = h2_stream_write_header(stream,
291                                                (const char *)name, namelen,
292                                                (const char *)value, valuelen);
293     if (status != APR_SUCCESS) {
294         return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
295     }
296     return 0;
297 }
298
299 /**
300  * nghttp2 session has received a complete frame. Most, it uses
301  * for processing of internal state. HEADER and DATA frames however
302  * we need to handle ourself.
303  */
304 static int on_frame_recv_cb(nghttp2_session *ng2s,
305                             const nghttp2_frame *frame,
306                             void *userp)
307 {
308     int rv;
309     h2_session *session = (h2_session *)userp;
310     apr_status_t status = APR_SUCCESS;
311     if (session->aborted) {
312         return NGHTTP2_ERR_CALLBACK_FAILURE;
313     }
314     
315     ++session->frames_received;
316     ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c,
317                   "h2_session(%ld): on_frame_rcv #%ld, type=%d", session->id,
318                   (long)session->frames_received, frame->hd.type);
319     switch (frame->hd.type) {
320         case NGHTTP2_HEADERS: {
321             int eos;
322             h2_stream * stream = h2_stream_set_get(session->streams,
323                                                    frame->hd.stream_id);
324             if (stream == NULL) {
325                 ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, session->c,
326                               APLOGNO(02921) 
327                               "h2_session:  stream(%ld-%d): HEADERS frame "
328                               "for unknown stream", session->id,
329                               (int)frame->hd.stream_id);
330                 rv = nghttp2_submit_rst_stream(ng2s, NGHTTP2_FLAG_NONE,
331                                                frame->hd.stream_id,
332                                                NGHTTP2_INTERNAL_ERROR);
333                 if (nghttp2_is_fatal(rv)) {
334                     return NGHTTP2_ERR_CALLBACK_FAILURE;
335                 }
336                 return 0;
337             }
338
339             eos = (frame->hd.flags & NGHTTP2_FLAG_END_STREAM);
340             status = stream_end_headers(session, stream, eos);
341
342             break;
343         }
344         case NGHTTP2_DATA: {
345             h2_stream * stream = h2_stream_set_get(session->streams,
346                                                    frame->hd.stream_id);
347             if (stream == NULL) {
348                 ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, session->c,
349                               APLOGNO(02922) 
350                               "h2_session:  stream(%ld-%d): DATA frame "
351                               "for unknown stream", session->id,
352                               (int)frame->hd.stream_id);
353                 rv = nghttp2_submit_rst_stream(ng2s, NGHTTP2_FLAG_NONE,
354                                                frame->hd.stream_id,
355                                                NGHTTP2_INTERNAL_ERROR);
356                 if (nghttp2_is_fatal(rv)) {
357                     return NGHTTP2_ERR_CALLBACK_FAILURE;
358                 }
359                 return 0;
360             }
361             break;
362         }
363         case NGHTTP2_PRIORITY: {
364             ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c,
365                           "h2_session:  stream(%ld-%d): PRIORITY frame "
366                           " weight=%d, dependsOn=%d, exclusive=%d", 
367                           session->id, (int)frame->hd.stream_id,
368                           frame->priority.pri_spec.weight,
369                           frame->priority.pri_spec.stream_id,
370                           frame->priority.pri_spec.exclusive);
371             break;
372         }
373         default:
374             if (APLOGctrace2(session->c)) {
375                 char buffer[256];
376                 
377                 frame_print(frame, buffer,
378                             sizeof(buffer)/sizeof(buffer[0]));
379                 ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, session->c,
380                               "h2_session: on_frame_rcv %s", buffer);
381             }
382             break;
383     }
384
385     /* only DATA and HEADERS frame can bear END_STREAM flag.  Other
386        frame types may have other flag which has the same value, so we
387        have to check the frame type first.  */
388     if ((frame->hd.type == NGHTTP2_DATA || frame->hd.type == NGHTTP2_HEADERS) &&
389         frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
390         h2_stream * stream = h2_stream_set_get(session->streams,
391                                                frame->hd.stream_id);
392         if (stream != NULL) {
393             status = h2_stream_write_eos(stream);
394             ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, session->c,
395                           "h2_stream(%ld-%d): input closed",
396                           session->id, (int)frame->hd.stream_id);
397         }
398     }
399     
400     if (status != APR_SUCCESS) {
401         ap_log_cerror(APLOG_MARK, APLOG_ERR, status, session->c,
402                       APLOGNO(02923) 
403                       "h2_session: stream(%ld-%d): error handling frame",
404                       session->id, (int)frame->hd.stream_id);
405         rv = nghttp2_submit_rst_stream(ng2s, NGHTTP2_FLAG_NONE,
406                                        frame->hd.stream_id,
407                                        NGHTTP2_INTERNAL_ERROR);
408         if (nghttp2_is_fatal(rv)) {
409             return NGHTTP2_ERR_CALLBACK_FAILURE;
410         }
411         return 0;
412     }
413     
414     return 0;
415 }
416
417 static apr_status_t send_data(h2_session *session, const char *data, 
418                               apr_size_t length)
419 {
420     return h2_conn_io_write(&session->io, data, length);
421 }
422
423 static apr_status_t pass_data(void *ctx, 
424                               const char *data, apr_size_t length)
425 {
426     return send_data((h2_session*)ctx, data, length);
427 }
428
429 static int on_send_data_cb(nghttp2_session *ngh2, 
430                            nghttp2_frame *frame, 
431                            const uint8_t *framehd, 
432                            size_t length, 
433                            nghttp2_data_source *source, 
434                            void *userp)
435 {
436     apr_status_t status = APR_SUCCESS;
437     h2_session *session = (h2_session *)userp;
438     int stream_id = (int)frame->hd.stream_id;
439     const unsigned char padlen = frame->data.padlen;
440     int eos;
441     h2_stream *stream;
442     
443     (void)ngh2;
444     (void)source;
445     if (session->aborted) {
446         return NGHTTP2_ERR_CALLBACK_FAILURE;
447     }
448     
449     stream = h2_stream_set_get(session->streams, stream_id);
450     if (!stream) {
451         ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_NOTFOUND, session->c,
452                       APLOGNO(02924) 
453                       "h2_stream(%ld-%d): send_data",
454                       session->id, (int)stream_id);
455         return NGHTTP2_ERR_CALLBACK_FAILURE;
456     }
457     
458     status = send_data(session, (const char *)framehd, 9);
459     if (status == APR_SUCCESS) {
460         if (padlen) {
461             status = send_data(session, (const char *)&padlen, 1);
462         }
463
464         if (status == APR_SUCCESS) {
465             apr_size_t len = length;
466             status = h2_stream_readx(stream, pass_data, session, 
467                                      &len, &eos);
468             if (status == APR_SUCCESS && len != length) {
469                 status = APR_EINVAL;
470             }
471         }
472         
473         if (status == APR_SUCCESS && padlen) {
474             if (padlen) {
475                 char pad[256];
476                 memset(pad, 0, padlen);
477                 status = send_data(session, pad, padlen);
478             }
479         }
480     }
481     
482     if (status == APR_SUCCESS) {
483         return 0;
484     }
485     else if (status != APR_EOF) {
486         ap_log_cerror(APLOG_MARK, APLOG_ERR, status, session->c,
487                       APLOGNO(02925) 
488                       "h2_stream(%ld-%d): failed send_data_cb",
489                       session->id, (int)stream_id);
490         return NGHTTP2_ERR_CALLBACK_FAILURE;
491     }
492     
493     return h2_session_status_from_apr_status(status);
494 }
495
496
497 #define NGH2_SET_CALLBACK(callbacks, name, fn)\
498 nghttp2_session_callbacks_set_##name##_callback(callbacks, fn)
499
500 static apr_status_t init_callbacks(conn_rec *c, nghttp2_session_callbacks **pcb)
501 {
502     int rv = nghttp2_session_callbacks_new(pcb);
503     if (rv != 0) {
504         ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c,
505                       APLOGNO(02926) "nghttp2_session_callbacks_new: %s",
506                       nghttp2_strerror(rv));
507         return APR_EGENERAL;
508     }
509     
510     NGH2_SET_CALLBACK(*pcb, send, send_cb);
511     NGH2_SET_CALLBACK(*pcb, on_frame_recv, on_frame_recv_cb);
512     NGH2_SET_CALLBACK(*pcb, on_invalid_frame_recv, on_invalid_frame_recv_cb);
513     NGH2_SET_CALLBACK(*pcb, on_data_chunk_recv, on_data_chunk_recv_cb);
514     NGH2_SET_CALLBACK(*pcb, before_frame_send, before_frame_send_cb);
515     NGH2_SET_CALLBACK(*pcb, on_frame_send, on_frame_send_cb);
516     NGH2_SET_CALLBACK(*pcb, on_frame_not_send, on_frame_not_send_cb);
517     NGH2_SET_CALLBACK(*pcb, on_stream_close, on_stream_close_cb);
518     NGH2_SET_CALLBACK(*pcb, on_begin_headers, on_begin_headers_cb);
519     NGH2_SET_CALLBACK(*pcb, on_header, on_header_cb);
520     NGH2_SET_CALLBACK(*pcb, send_data, on_send_data_cb);
521     
522     return APR_SUCCESS;
523 }
524
525 static h2_session *h2_session_create_int(conn_rec *c,
526                                          request_rec *r,
527                                          h2_config *config, 
528                                          h2_workers *workers)
529 {
530     nghttp2_session_callbacks *callbacks = NULL;
531     nghttp2_option *options = NULL;
532
533     apr_pool_t *pool = NULL;
534     apr_status_t status = apr_pool_create(&pool, r? r->pool : c->pool);
535     h2_session *session;
536     if (status != APR_SUCCESS) {
537         return NULL;
538     }
539
540     session = apr_pcalloc(pool, sizeof(h2_session));
541     if (session) {
542         int rv;
543         session->id = c->id;
544         session->c = c;
545         session->r = r;
546         
547         session->max_stream_count = h2_config_geti(config, H2_CONF_MAX_STREAMS);
548         session->max_stream_mem = h2_config_geti(config, H2_CONF_STREAM_MAX_MEM);
549
550         session->pool = pool;
551         
552         status = apr_thread_cond_create(&session->iowait, session->pool);
553         if (status != APR_SUCCESS) {
554             return NULL;
555         }
556         
557         session->streams = h2_stream_set_create(session->pool);
558         
559         session->workers = workers;
560         session->mplx = h2_mplx_create(c, session->pool, workers);
561         
562         h2_conn_io_init(&session->io, c);
563         session->bbtmp = apr_brigade_create(session->pool, c->bucket_alloc);
564         
565         status = init_callbacks(c, &callbacks);
566         if (status != APR_SUCCESS) {
567             ap_log_cerror(APLOG_MARK, APLOG_ERR, status, c, APLOGNO(02927) 
568                           "nghttp2: error in init_callbacks");
569             h2_session_destroy(session);
570             return NULL;
571         }
572         
573         rv = nghttp2_option_new(&options);
574         if (rv != 0) {
575             ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_EGENERAL, c,
576                           APLOGNO(02928) "nghttp2_option_new: %s", 
577                           nghttp2_strerror(rv));
578             h2_session_destroy(session);
579             return NULL;
580         }
581
582         nghttp2_option_set_peer_max_concurrent_streams(options, 
583                                                        (uint32_t)session->max_stream_count);
584
585         /* We need to handle window updates ourself, otherwise we
586          * get flooded by nghttp2. */
587         nghttp2_option_set_no_auto_window_update(options, 1);
588         
589         rv = nghttp2_session_server_new2(&session->ngh2, callbacks,
590                                          session, options);
591         nghttp2_session_callbacks_del(callbacks);
592         nghttp2_option_del(options);
593         
594         if (rv != 0) {
595             ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_EGENERAL, c,
596                           APLOGNO(02929) "nghttp2_session_server_new: %s",
597                           nghttp2_strerror(rv));
598             h2_session_destroy(session);
599             return NULL;
600         }
601         
602     }
603     return session;
604 }
605
606 h2_session *h2_session_create(conn_rec *c, h2_config *config, 
607                               h2_workers *workers)
608 {
609     return h2_session_create_int(c, NULL, config, workers);
610 }
611
612 h2_session *h2_session_rcreate(request_rec *r, h2_config *config, 
613                                h2_workers *workers)
614 {
615     return h2_session_create_int(r->connection, r, config, workers);
616 }
617
618 void h2_session_destroy(h2_session *session)
619 {
620     AP_DEBUG_ASSERT(session);
621     if (session->mplx) {
622         h2_mplx_release_and_join(session->mplx, session->iowait);
623         session->mplx = NULL;
624     }
625     if (session->streams) {
626         if (h2_stream_set_size(session->streams)) {
627             ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, session->c,
628                           "h2_session(%ld): destroy, %d streams open",
629                           session->id, (int)h2_stream_set_size(session->streams));
630         }
631         h2_stream_set_destroy(session->streams);
632         session->streams = NULL;
633     }
634     if (session->ngh2) {
635         nghttp2_session_del(session->ngh2);
636         session->ngh2 = NULL;
637     }
638     h2_conn_io_destroy(&session->io);
639     
640     if (session->iowait) {
641         apr_thread_cond_destroy(session->iowait);
642         session->iowait = NULL;
643     }
644     
645     if (session->pool) {
646         apr_pool_destroy(session->pool);
647     }
648 }
649
650 apr_status_t h2_session_goaway(h2_session *session, apr_status_t reason)
651 {
652     apr_status_t status = APR_SUCCESS;
653     int rv;
654     AP_DEBUG_ASSERT(session);
655     if (session->aborted) {
656         return APR_EINVAL;
657     }
658     
659     rv = 0;
660     if (reason == APR_SUCCESS) {
661         rv = nghttp2_submit_shutdown_notice(session->ngh2);
662     }
663     else {
664         int err = 0;
665         int last_id = nghttp2_session_get_last_proc_stream_id(session->ngh2);
666         rv = nghttp2_submit_goaway(session->ngh2, last_id,
667                                    NGHTTP2_FLAG_NONE, err, NULL, 0);
668     }
669     if (rv != 0) {
670         status = APR_EGENERAL;
671         ap_log_cerror(APLOG_MARK, APLOG_ERR, status, session->c,
672                     APLOGNO(02930) "session(%ld): submit goaway: %s",
673                       session->id, nghttp2_strerror(rv));
674     }
675     return status;
676 }
677
678 static apr_status_t h2_session_abort_int(h2_session *session, int reason)
679 {
680     AP_DEBUG_ASSERT(session);
681     if (!session->aborted) {
682         session->aborted = 1;
683         if (session->ngh2) {
684             if (reason) {
685                 ap_log_cerror(APLOG_MARK, (reason == NGHTTP2_ERR_EOF)?
686                               APLOG_DEBUG : APLOG_INFO, 0, session->c,
687                               "session(%ld): aborting session, reason=%d %s",
688                               session->id, reason, nghttp2_strerror(reason));
689             }
690             nghttp2_session_terminate_session(session->ngh2, reason);
691             nghttp2_submit_goaway(session->ngh2, 0, 0, reason, NULL, 0);
692             nghttp2_session_send(session->ngh2);
693             h2_conn_io_flush(&session->io);
694         }
695         h2_mplx_abort(session->mplx);
696     }
697     return APR_SUCCESS;
698 }
699
700 apr_status_t h2_session_abort(h2_session *session, apr_status_t reason, int rv)
701 {
702     AP_DEBUG_ASSERT(session);
703     if (rv == 0) {
704         rv = NGHTTP2_ERR_PROTO;
705         switch (reason) {
706             case APR_ENOMEM:
707                 rv = NGHTTP2_ERR_NOMEM;
708                 break;
709             case APR_EOF:
710                 rv = 0;
711                 break;
712             case APR_EBADF:
713             case APR_ECONNABORTED:
714                 rv = NGHTTP2_ERR_EOF;
715                 break;
716             default:
717                 break;
718         }
719     }
720     return h2_session_abort_int(session, rv);
721 }
722
723 apr_status_t h2_session_start(h2_session *session, int *rv)
724 {
725     apr_status_t status = APR_SUCCESS;
726     h2_config *config;
727     nghttp2_settings_entry settings[3];
728     
729     AP_DEBUG_ASSERT(session);
730     /* Start the conversation by submitting our SETTINGS frame */
731     *rv = 0;
732     config = h2_config_get(session->c);
733     if (session->r) {
734         const char *s, *cs;
735         apr_size_t dlen; 
736         h2_stream * stream;
737
738         /* better for vhost matching */
739         config = h2_config_rget(session->r);
740         
741         /* 'h2c' mode: we should have a 'HTTP2-Settings' header with
742          * base64 encoded client settings. */
743         s = apr_table_get(session->r->headers_in, "HTTP2-Settings");
744         if (!s) {
745             ap_log_rerror(APLOG_MARK, APLOG_ERR, APR_EINVAL, session->r,
746                           APLOGNO(02931) 
747                           "HTTP2-Settings header missing in request");
748             return APR_EINVAL;
749         }
750         cs = NULL;
751         dlen = h2_util_base64url_decode(&cs, s, session->pool);
752         
753         if (APLOGrdebug(session->r)) {
754             char buffer[128];
755             h2_util_hex_dump(buffer, 128, (char*)cs, dlen);
756             ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, session->r,
757                           "upgrading h2c session with HTTP2-Settings: %s -> %s (%d)",
758                           s, buffer, (int)dlen);
759         }
760         
761         *rv = nghttp2_session_upgrade(session->ngh2, (uint8_t*)cs, dlen, NULL);
762         if (*rv != 0) {
763             status = APR_EINVAL;
764             ap_log_rerror(APLOG_MARK, APLOG_ERR, status, session->r,
765                           APLOGNO(02932) "nghttp2_session_upgrade: %s", 
766                           nghttp2_strerror(*rv));
767             return status;
768         }
769         
770         /* Now we need to auto-open stream 1 for the request we got. */
771         *rv = stream_open(session, 1);
772         if (*rv != 0) {
773             status = APR_EGENERAL;
774             ap_log_rerror(APLOG_MARK, APLOG_ERR, status, session->r,
775                           APLOGNO(02933) "open stream 1: %s", 
776                           nghttp2_strerror(*rv));
777             return status;
778         }
779         
780         stream = h2_stream_set_get(session->streams, 1);
781         if (stream == NULL) {
782             status = APR_EGENERAL;
783             ap_log_rerror(APLOG_MARK, APLOG_ERR, status, session->r,
784                           APLOGNO(02934) "lookup of stream 1");
785             return status;
786         }
787         
788         status = h2_stream_rwrite(stream, session->r);
789         if (status != APR_SUCCESS) {
790             return status;
791         }
792         status = stream_end_headers(session, stream, 1);
793         if (status != APR_SUCCESS) {
794             return status;
795         }
796     }
797
798     settings[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS;
799     settings[0].value = (uint32_t)session->max_stream_count;
800     settings[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE;
801     settings[1].value = h2_config_geti(config, H2_CONF_WIN_SIZE);
802     settings[2].settings_id = NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE;
803     settings[2].value = 64*1024;
804     
805     *rv = nghttp2_submit_settings(session->ngh2, NGHTTP2_FLAG_NONE,
806                                  settings,
807                                  sizeof(settings)/sizeof(settings[0]));
808     if (*rv != 0) {
809         status = APR_EGENERAL;
810         ap_log_cerror(APLOG_MARK, APLOG_ERR, status, session->c,
811                       APLOGNO(02935) "nghttp2_submit_settings: %s", 
812                       nghttp2_strerror(*rv));
813     }
814     
815     return status;
816 }
817
818 static int h2_session_want_write(h2_session *session)
819 {
820     return nghttp2_session_want_write(session->ngh2);
821 }
822
823 typedef struct {
824     h2_session *session;
825     int resume_count;
826 } resume_ctx;
827
828 static int resume_on_data(void *ctx, h2_stream *stream) {
829     resume_ctx *rctx = (resume_ctx*)ctx;
830     h2_session *session = rctx->session;
831     AP_DEBUG_ASSERT(session);
832     AP_DEBUG_ASSERT(stream);
833     
834     if (h2_stream_is_suspended(stream)) {
835         if (h2_mplx_out_has_data_for(stream->m, stream->id)) {
836             int rv;
837             h2_stream_set_suspended(stream, 0);
838             ++rctx->resume_count;
839             
840             rv = nghttp2_session_resume_data(session->ngh2, stream->id);
841             ap_log_cerror(APLOG_MARK, nghttp2_is_fatal(rv)?
842                           APLOG_ERR : APLOG_DEBUG, 0, session->c,
843                           APLOGNO(02936) 
844                           "h2_stream(%ld-%d): resuming stream %s",
845                           session->id, stream->id, nghttp2_strerror(rv));
846         }
847     }
848     return 1;
849 }
850
851 static int h2_session_resume_streams_with_data(h2_session *session) {
852     AP_DEBUG_ASSERT(session);
853     if (!h2_stream_set_is_empty(session->streams)
854         && session->mplx && !session->aborted) {
855         resume_ctx ctx;
856         
857         ctx.session      = session;
858         ctx.resume_count = 0;
859
860         /* Resume all streams where we have data in the out queue and
861          * which had been suspended before. */
862         h2_stream_set_iter(session->streams, resume_on_data, &ctx);
863         return ctx.resume_count;
864     }
865     return 0;
866 }
867
868 static void update_window(void *ctx, int stream_id, apr_size_t bytes_read)
869 {
870     h2_session *session = (h2_session*)ctx;
871     nghttp2_session_consume(session->ngh2, stream_id, bytes_read);
872 }
873
874 static apr_status_t h2_session_update_windows(h2_session *session)
875 {
876     return h2_mplx_in_update_windows(session->mplx, update_window, session);
877 }
878
879 apr_status_t h2_session_write(h2_session *session, apr_interval_time_t timeout)
880 {
881     apr_status_t status = APR_EAGAIN;
882     h2_stream *stream = NULL;
883     int flush_output = 0;
884     
885     AP_DEBUG_ASSERT(session);
886     
887     /* Check that any pending window updates are sent. */
888     status = h2_session_update_windows(session);
889     if (status == APR_SUCCESS) {
890         flush_output = 1;
891     }
892     else if (status != APR_EAGAIN) {
893         return status;
894     }
895     
896     if (h2_session_want_write(session)) {
897         int rv;
898         status = APR_SUCCESS;
899         rv = nghttp2_session_send(session->ngh2);
900         if (rv != 0) {
901             ap_log_cerror( APLOG_MARK, APLOG_DEBUG, 0, session->c,
902                           "h2_session: send: %s", nghttp2_strerror(rv));
903             if (nghttp2_is_fatal(rv)) {
904                 h2_session_abort_int(session, rv);
905                 status = APR_ECONNABORTED;
906             }
907         }
908         flush_output = 1;
909     }
910     
911     /* If we have responses ready, submit them now. */
912     while ((stream = h2_mplx_next_submit(session->mplx, 
913                                          session->streams)) != NULL) {
914         status = h2_session_handle_response(session, stream);
915         flush_output = 1;
916     }
917     
918     if (h2_session_resume_streams_with_data(session) > 0) {
919         flush_output = 1;
920     }
921     
922     if (!flush_output && timeout > 0 && !h2_session_want_write(session)) {
923         status = h2_mplx_out_trywait(session->mplx, timeout, session->iowait);
924
925         if (status != APR_TIMEUP
926             && h2_session_resume_streams_with_data(session) > 0) {
927             flush_output = 1;
928         }
929         else {
930             /* nothing happened to ongoing streams, do some house-keeping */
931         }
932     }
933     
934     if (h2_session_want_write(session)) {
935         int rv;
936         status = APR_SUCCESS;
937         rv = nghttp2_session_send(session->ngh2);
938         if (rv != 0) {
939             ap_log_cerror( APLOG_MARK, APLOG_DEBUG, 0, session->c,
940                           "h2_session: send2: %s", nghttp2_strerror(rv));
941             if (nghttp2_is_fatal(rv)) {
942                 h2_session_abort_int(session, rv);
943                 status = APR_ECONNABORTED;
944             }
945         }
946         flush_output = 1;
947     }
948     
949     if (flush_output) {
950         h2_conn_io_flush(&session->io);
951     }
952     
953     return status;
954 }
955
956 h2_stream *h2_session_get_stream(h2_session *session, int stream_id)
957 {
958     AP_DEBUG_ASSERT(session);
959     return h2_stream_set_get(session->streams, stream_id);
960 }
961
962 /* h2_io_on_read_cb implementation that offers the data read
963  * directly to the session for consumption.
964  */
965 static apr_status_t session_receive(const char *data, apr_size_t len,
966                                     apr_size_t *readlen, int *done,
967                                     void *puser)
968 {
969     h2_session *session = (h2_session *)puser;
970     AP_DEBUG_ASSERT(session);
971     if (len > 0) {
972         ssize_t n = nghttp2_session_mem_recv(session->ngh2,
973                                              (const uint8_t *)data, len);
974         if (n < 0) {
975             ap_log_cerror(APLOG_MARK, APLOG_DEBUG, APR_EGENERAL,
976                           session->c,
977                           "h2_session: nghttp2_session_mem_recv error %d",
978                           (int)n);
979             if (nghttp2_is_fatal((int)n)) {
980                 *done = 1;
981                 h2_session_abort_int(session, (int)n);
982                 return APR_EGENERAL;
983             }
984         }
985         else {
986             *readlen = n;
987         }
988     }
989     return APR_SUCCESS;
990 }
991
992 apr_status_t h2_session_read(h2_session *session, apr_read_type_e block)
993 {
994     AP_DEBUG_ASSERT(session);
995     return h2_conn_io_read(&session->io, block, session_receive, session);
996 }
997
998 apr_status_t h2_session_close(h2_session *session)
999 {
1000     AP_DEBUG_ASSERT(session);
1001     return h2_conn_io_flush(&session->io);
1002 }
1003
1004 /* The session wants to send more DATA for the given stream.
1005  */
1006 static ssize_t stream_data_cb(nghttp2_session *ng2s,
1007                               int32_t stream_id,
1008                               uint8_t *buf,
1009                               size_t length,
1010                               uint32_t *data_flags,
1011                               nghttp2_data_source *source,
1012                               void *puser)
1013 {
1014     h2_session *session = (h2_session *)puser;
1015     apr_size_t nread = length;
1016     int eos = 0;
1017     apr_status_t status;
1018     h2_stream *stream;
1019     AP_DEBUG_ASSERT(session);
1020     
1021     stream = h2_stream_set_get(session->streams, stream_id);
1022     if (!stream) {
1023         ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_NOTFOUND, session->c,
1024                       APLOGNO(02937) 
1025                       "h2_stream(%ld-%d): data requested but stream not found",
1026                       session->id, (int)stream_id);
1027         return NGHTTP2_ERR_CALLBACK_FAILURE;
1028     }
1029     
1030     AP_DEBUG_ASSERT(!h2_stream_is_suspended(stream));
1031     
1032     status = h2_stream_prep_read(stream, &nread, &eos);
1033     if (nread) {
1034         *data_flags |=  NGHTTP2_DATA_FLAG_NO_COPY;
1035     }
1036     
1037     switch (status) {
1038         case APR_SUCCESS:
1039             break;
1040             
1041         case APR_EAGAIN:
1042             /* If there is no data available, our session will automatically
1043              * suspend this stream and not ask for more data until we resume
1044              * it. Remember at our h2_stream that we need to do this.
1045              */
1046             nread = 0;
1047             h2_stream_set_suspended(stream, 1);
1048             ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c,
1049                           "h2_stream(%ld-%d): suspending stream",
1050                           session->id, (int)stream_id);
1051             return NGHTTP2_ERR_DEFERRED;
1052             
1053         case APR_EOF:
1054             nread = 0;
1055             eos = 1;
1056             break;
1057             
1058         default:
1059             nread = 0;
1060             ap_log_cerror(APLOG_MARK, APLOG_ERR, status, session->c,
1061                           APLOGNO(02938) "h2_stream(%ld-%d): reading data",
1062                           session->id, (int)stream_id);
1063             return NGHTTP2_ERR_CALLBACK_FAILURE;
1064     }
1065     
1066     if (eos) {
1067         *data_flags |= NGHTTP2_DATA_FLAG_EOF;
1068     }
1069     
1070     return (ssize_t)nread;
1071 }
1072
1073 typedef struct {
1074     nghttp2_nv *nv;
1075     size_t nvlen;
1076     size_t offset;
1077 } nvctx_t;
1078
1079 static int submit_response(h2_session *session, h2_response *response)
1080 {
1081     nghttp2_data_provider provider;
1082     int rv;
1083     
1084     memset(&provider, 0, sizeof(provider));
1085     provider.source.fd = response->stream_id;
1086     provider.read_callback = stream_data_cb;
1087     
1088     ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c,
1089                   "h2_stream(%ld-%d): submitting response %s",
1090                   session->id, response->stream_id, response->status);
1091     
1092     rv = nghttp2_submit_response(session->ngh2, response->stream_id,
1093                                  response->ngheader->nv, 
1094                                  response->ngheader->nvlen, &provider);
1095     
1096     if (rv != 0) {
1097         ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, session->c,
1098                       APLOGNO(02939) "h2_stream(%ld-%d): submit_response: %s",
1099                       session->id, response->stream_id, nghttp2_strerror(rv));
1100     }
1101     else {
1102         ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c,
1103                       "h2_stream(%ld-%d): submitted response %s, rv=%d",
1104                       session->id, response->stream_id, 
1105                       response->status, rv);
1106     }
1107     return rv;
1108 }
1109
1110 /* Start submitting the response to a stream request. This is possible
1111  * once we have all the response headers. The response body will be
1112  * read by the session using the callback we supply.
1113  */
1114 apr_status_t h2_session_handle_response(h2_session *session, h2_stream *stream)
1115 {
1116     apr_status_t status = APR_SUCCESS;
1117     int rv = 0;
1118     AP_DEBUG_ASSERT(session);
1119     AP_DEBUG_ASSERT(stream);
1120     AP_DEBUG_ASSERT(stream->response);
1121     
1122     if (stream->response->ngheader) {
1123         rv = submit_response(session, stream->response);
1124     }
1125     else {
1126         rv = nghttp2_submit_rst_stream(session->ngh2, NGHTTP2_FLAG_NONE,
1127                                        stream->id, NGHTTP2_PROTOCOL_ERROR);
1128     }
1129     
1130     if (nghttp2_is_fatal(rv)) {
1131         status = APR_EGENERAL;
1132         h2_session_abort_int(session, rv);
1133         ap_log_cerror(APLOG_MARK, APLOG_ERR, status, session->c,
1134                       APLOGNO(02940) "submit_response: %s", 
1135                       nghttp2_strerror(rv));
1136     }
1137     return status;
1138 }
1139
1140 int h2_session_is_done(h2_session *session)
1141 {
1142     AP_DEBUG_ASSERT(session);
1143     return (session->aborted
1144             || !session->ngh2
1145             || (!nghttp2_session_want_read(session->ngh2)
1146                 && !nghttp2_session_want_write(session->ngh2)));
1147 }
1148
1149 static int log_stream(void *ctx, h2_stream *stream)
1150 {
1151     h2_session *session = (h2_session *)ctx;
1152     AP_DEBUG_ASSERT(session);
1153     ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c,
1154                   "h2_stream(%ld-%d): in set, suspended=%d, aborted=%d, "
1155                   "has_data=%d",
1156                   session->id, stream->id, stream->suspended, stream->aborted,
1157                   h2_mplx_out_has_data_for(session->mplx, stream->id));
1158     return 1;
1159 }
1160
1161 void h2_session_log_stats(h2_session *session)
1162 {
1163     AP_DEBUG_ASSERT(session);
1164     ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c,
1165                   "h2_session(%ld): %d open streams",
1166                   session->id, (int)h2_stream_set_size(session->streams));
1167     h2_stream_set_iter(session->streams, log_stream, session);
1168 }
1169
1170 static int frame_print(const nghttp2_frame *frame, char *buffer, size_t maxlen)
1171 {
1172     char scratch[128];
1173     size_t s_len = sizeof(scratch)/sizeof(scratch[0]);
1174     
1175     switch (frame->hd.type) {
1176         case NGHTTP2_DATA: {
1177             return apr_snprintf(buffer, maxlen,
1178                                 "DATA[length=%d, flags=%d, stream=%d, padlen=%d]",
1179                                 (int)frame->hd.length, frame->hd.flags,
1180                                 frame->hd.stream_id, (int)frame->data.padlen);
1181         }
1182         case NGHTTP2_HEADERS: {
1183             return apr_snprintf(buffer, maxlen,
1184                                 "HEADERS[length=%d, hend=%d, stream=%d, eos=%d]",
1185                                 (int)frame->hd.length,
1186                                 !!(frame->hd.flags & NGHTTP2_FLAG_END_HEADERS),
1187                                 frame->hd.stream_id,
1188                                 !!(frame->hd.flags & NGHTTP2_FLAG_END_STREAM));
1189         }
1190         case NGHTTP2_PRIORITY: {
1191             return apr_snprintf(buffer, maxlen,
1192                                 "PRIORITY[length=%d, flags=%d, stream=%d]",
1193                                 (int)frame->hd.length,
1194                                 frame->hd.flags, frame->hd.stream_id);
1195         }
1196         case NGHTTP2_RST_STREAM: {
1197             return apr_snprintf(buffer, maxlen,
1198                                 "RST_STREAM[length=%d, flags=%d, stream=%d]",
1199                                 (int)frame->hd.length,
1200                                 frame->hd.flags, frame->hd.stream_id);
1201         }
1202         case NGHTTP2_SETTINGS: {
1203             if (frame->hd.flags & NGHTTP2_FLAG_ACK) {
1204                 return apr_snprintf(buffer, maxlen,
1205                                     "SETTINGS[ack=1, stream=%d]",
1206                                     frame->hd.stream_id);
1207             }
1208             return apr_snprintf(buffer, maxlen,
1209                                 "SETTINGS[length=%d, stream=%d]",
1210                                 (int)frame->hd.length, frame->hd.stream_id);
1211         }
1212         case NGHTTP2_PUSH_PROMISE: {
1213             return apr_snprintf(buffer, maxlen,
1214                                 "PUSH_PROMISE[length=%d, hend=%d, stream=%d]",
1215                                 (int)frame->hd.length,
1216                                 !!(frame->hd.flags & NGHTTP2_FLAG_END_HEADERS),
1217                                 frame->hd.stream_id);
1218         }
1219         case NGHTTP2_PING: {
1220             return apr_snprintf(buffer, maxlen,
1221                                 "PING[length=%d, ack=%d, stream=%d]",
1222                                 (int)frame->hd.length,
1223                                 frame->hd.flags&NGHTTP2_FLAG_ACK,
1224                                 frame->hd.stream_id);
1225         }
1226         case NGHTTP2_GOAWAY: {
1227             size_t len = (frame->goaway.opaque_data_len < s_len)?
1228             frame->goaway.opaque_data_len : s_len-1;
1229             memcpy(scratch, frame->goaway.opaque_data, len);
1230             scratch[len+1] = '\0';
1231             return apr_snprintf(buffer, maxlen, "GOAWAY[error=%d, reason='%s']",
1232                                 frame->goaway.error_code, scratch);
1233         }
1234         case NGHTTP2_WINDOW_UPDATE: {
1235             return apr_snprintf(buffer, maxlen,
1236                                 "WINDOW_UPDATE[length=%d, stream=%d]",
1237                                 (int)frame->hd.length, frame->hd.stream_id);
1238         }
1239         default:
1240             return apr_snprintf(buffer, maxlen,
1241                                 "FRAME[type=%d, length=%d, flags=%d, stream=%d]",
1242                                 frame->hd.type, (int)frame->hd.length,
1243                                 frame->hd.flags, frame->hd.stream_id);
1244     }
1245 }
1246