Apache HTTPD
h2_c1_io.c
Go to the documentation of this file.
1/* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements. See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <assert.h>
18#include <apr_strings.h>
19#include <ap_mpm.h>
20#include <mpm_common.h>
21
22#include <httpd.h>
23#include <http_core.h>
24#include <http_log.h>
25#include <http_connection.h>
26#include <http_protocol.h>
27#include <http_request.h>
28#include <http_ssl.h>
29
30#include "h2_private.h"
31#include "h2_bucket_eos.h"
32#include "h2_config.h"
33#include "h2_c1.h"
34#include "h2_c1_io.h"
35#include "h2_protocol.h"
36#include "h2_session.h"
37#include "h2_util.h"
38
39#define TLS_DATA_MAX (16*1024)
40
41/* Calculated like this: assuming MTU 1500 bytes
42 * 1500 - 40 (IP) - 20 (TCP) - 40 (TCP options)
43 * - TLS overhead (60-100)
44 * ~= 1300 bytes */
45#define WRITE_SIZE_INITIAL 1300
46
47/* The maximum we'd like to write in one chunk is
48 * the max size of a TLS record. When pushing
49 * many frames down the h2 connection, this might
50 * align differently because of headers and other
51 * frames or simply as not sufficient data is
52 * in a response body.
53 * However keeping frames at or below this limit
54 * should make optimizations at the layer that writes
55 * to TLS easier.
56 */
57#define WRITE_SIZE_MAX (TLS_DATA_MAX)
58
59#define BUF_REMAIN ((apr_size_t)(bmax-off))
60
61static void h2_c1_io_bb_log(conn_rec *c, int stream_id, int level,
62 const char *tag, apr_bucket_brigade *bb)
63{
64 char buffer[16 * 1024];
65 const char *line = "(null)";
66 int bmax = sizeof(buffer)/sizeof(buffer[0]);
67 int off = 0;
69
70 (void)stream_id;
71 if (bb) {
72 memset(buffer, 0, bmax--);
73 for (b = APR_BRIGADE_FIRST(bb);
74 bmax && (b != APR_BRIGADE_SENTINEL(bb));
75 b = APR_BUCKET_NEXT(b)) {
76
78 if (APR_BUCKET_IS_EOS(b)) {
80 }
81 else if (APR_BUCKET_IS_FLUSH(b)) {
82 off += apr_snprintf(buffer+off, BUF_REMAIN, "flush ");
83 }
84 else if (AP_BUCKET_IS_EOR(b)) {
86 }
87 else if (H2_BUCKET_IS_H2EOS(b)) {
88 off += apr_snprintf(buffer+off, BUF_REMAIN, "h2eos ");
89 }
90 else {
91 off += apr_snprintf(buffer+off, BUF_REMAIN, "meta(unknown) ");
92 }
93 }
94 else {
95 const char *btype = "data";
96 if (APR_BUCKET_IS_FILE(b)) {
97 btype = "file";
98 }
99 else if (APR_BUCKET_IS_PIPE(b)) {
100 btype = "pipe";
101 }
102 else if (APR_BUCKET_IS_SOCKET(b)) {
103 btype = "socket";
104 }
105 else if (APR_BUCKET_IS_HEAP(b)) {
106 btype = "heap";
107 }
108 else if (APR_BUCKET_IS_TRANSIENT(b)) {
109 btype = "transient";
110 }
111 else if (APR_BUCKET_IS_IMMORTAL(b)) {
112 btype = "immortal";
113 }
114#if APR_HAS_MMAP
115 else if (APR_BUCKET_IS_MMAP(b)) {
116 btype = "mmap";
117 }
118#endif
119 else if (APR_BUCKET_IS_POOL(b)) {
120 btype = "pool";
121 }
122
123 off += apr_snprintf(buffer+off, BUF_REMAIN, "%s[%ld] ",
124 btype,
125 (long)(b->length == ((apr_size_t)-1)? -1UL : b->length));
126 }
127 }
128 line = *buffer? buffer : "(empty)";
129 }
130 /* Intentional no APLOGNO */
131 ap_log_cerror(APLOG_MARK, level, 0, c, "h2_session(%ld)-%s: %s",
132 c->id, tag, line);
133
134}
135#define C1_IO_BB_LOG(c, stream_id, level, tag, bb) \
136 if (APLOG_C_IS_LEVEL(c, level)) { \
137 h2_c1_io_bb_log((c), (stream_id), (level), (tag), (bb)); \
138 }
139
140
142{
143 conn_rec *c = session->c1;
144
145 io->session = session;
146 io->output = apr_brigade_create(c->pool, c->bucket_alloc);
147 io->is_tls = ap_ssl_conn_is_ssl(session->c1);
148 io->buffer_output = io->is_tls;
150
151 if (io->buffer_output) {
152 /* This is what we start with,
153 * see https://issues.apache.org/jira/browse/TS-2503
154 */
158 io->cooldown_usecs = 0;
159 io->write_size = (io->cooldown_usecs > 0?
161 }
162 else {
163 io->warmup_size = 0;
164 io->cooldown_usecs = 0;
165 io->write_size = 0;
166 }
167
168 if (APLOGctrace1(c)) {
170 "h2_c1_io(%ld): init, buffering=%d, warmup_size=%ld, "
171 "cd_secs=%f", c->id, io->buffer_output,
172 (long)io->warmup_size,
173 ((double)io->cooldown_usecs/APR_USEC_PER_SEC));
174 }
175
176 return APR_SUCCESS;
177}
178
179static void append_scratch(h2_c1_io *io)
180{
181 if (io->scratch && io->slen > 0) {
184 io->session->c1->bucket_alloc);
186 io->buffered_len += io->slen;
187 io->scratch = NULL;
188 io->slen = io->ssize = 0;
189 }
190}
191
193 apr_size_t remain = io->ssize - io->slen;
194 if (io->scratch && remain == 0) {
195 append_scratch(io);
196 }
197 if (!io->scratch) {
198 /* we control the size and it is larger than what buckets usually
199 * allocate. */
201 io->ssize = io->write_size;
202 io->slen = 0;
203 remain = io->ssize;
204 }
205 return remain;
206}
207
209{
211 const char *data;
213
214 if (!b->length) {
215 return APR_SUCCESS;
216 }
217
218 ap_assert(b->length <= (io->ssize - io->slen));
219 if (APR_BUCKET_IS_FILE(b)) {
221 apr_file_t *fd = f->fd;
222 apr_off_t offset = b->start;
223
224 len = b->length;
225 /* file buckets will read 8000 byte chunks and split
226 * themselves. However, we do know *exactly* how many
227 * bytes we need where. So we read the file directly to
228 * where we need it.
229 */
231 if (status != APR_SUCCESS) {
232 return status;
233 }
234 status = apr_file_read(fd, io->scratch + io->slen, &len);
235 if (status != APR_SUCCESS && status != APR_EOF) {
236 return status;
237 }
238 io->slen += len;
239 }
240 else if (APR_BUCKET_IS_MMAP(b)) {
242 "h2_c1_io(%ld): seeing mmap bucket of size %ld, scratch remain=%ld",
243 io->session->c1->id, (long)b->length, (long)(io->ssize - io->slen));
245 if (status == APR_SUCCESS) {
246 memcpy(io->scratch+io->slen, data, len);
247 io->slen += len;
248 }
249 }
250 else {
252 if (status == APR_SUCCESS) {
253 memcpy(io->scratch+io->slen, data, len);
254 io->slen += len;
255 }
256 }
257 return status;
258}
259
261{
262 conn_rec *c = io->session->c1;
263 apr_off_t bblen = 0;
264 apr_status_t rv;
265
266 if (io->is_passing) {
267 /* recursive call, may be triggered by an H2EOS bucket
268 * being destroyed and triggering sending more data? */
271 "h2_c1_io(%ld): recursive call of h2_c1_io_pass. "
272 "Denied to prevent output corruption. This "
273 "points to a bug in the HTTP/2 implementation.",
274 c->id);
275 return APR_EGENERAL;
276 }
277 io->is_passing = 1;
278
279 append_scratch(io);
280 if (flush) {
282 apr_bucket *b = apr_bucket_flush_create(c->bucket_alloc);
284 }
285 }
286 if (APR_BRIGADE_EMPTY(io->output)) {
287 rv = APR_SUCCESS;
288 goto cleanup;
289 }
290
292 apr_brigade_length(io->output, 0, &bblen);
293 C1_IO_BB_LOG(c, 0, APLOG_TRACE2, "out", io->output);
294
295 rv = ap_pass_brigade(c->output_filters, io->output);
296 if (APR_SUCCESS != rv) goto cleanup;
297 io->bytes_written += (apr_size_t)bblen;
298
299 if (io->write_size < WRITE_SIZE_MAX
300 && io->bytes_written >= io->warmup_size) {
301 /* connection is hot, use max size */
303 }
304 else if (io->cooldown_usecs > 0
307 if ((now - io->last_write) >= io->cooldown_usecs) {
308 /* long time not written, reset write size */
310 io->bytes_written = 0;
311 }
312 else {
313 io->last_write = now;
314 }
315 }
316
317cleanup:
318 if (APR_SUCCESS != rv) {
320 "h2_c1_io(%ld): pass_out brigade %ld bytes",
321 c->id, (long)bblen);
322 }
324 io->buffered_len = 0;
325 io->is_passing = 0;
326 return rv;
327}
328
330{
331 return io->buffered_len >= io->flush_threshold;
332}
333
335{
336 return !APR_BRIGADE_EMPTY(io->output) || (io->scratch && io->slen > 0);
337}
338
340{
342
343 if (h2_c1_io_pending(io)) {
344 rv = pass_output(io, 0);
345 }
346 return rv;
347}
348
350{
352
353 if (h2_c1_io_pending(io) || io->unflushed) {
354 rv = pass_output(io, 1);
355 if (APR_SUCCESS != rv) goto cleanup;
356 }
357cleanup:
358 return rv;
359}
360
362{
365
367 "h2_c1_io(%ld): adding %ld data bytes",
368 io->session->c1->id, (long)length);
369 if (io->buffer_output) {
370 while (length > 0) {
372 if (remain >= length) {
373 memcpy(io->scratch + io->slen, data, length);
374 io->slen += length;
375 length = 0;
376 }
377 else {
378 memcpy(io->scratch + io->slen, data, remain);
379 io->slen += remain;
380 data += remain;
381 length -= remain;
382 }
383 }
384 }
385 else {
387 io->buffered_len += length;
388 }
389 return status;
390}
391
393{
394 apr_bucket *b;
396
397 while (!APR_BRIGADE_EMPTY(bb)) {
398 b = APR_BRIGADE_FIRST(bb);
400 /* need to finish any open scratch bucket, as meta data
401 * needs to be forward "in order". */
402 append_scratch(io);
405 }
406 else if (io->buffer_output) {
408 if (b->length > remain) {
410 if (io->slen == 0) {
411 /* complete write_size bucket, append unchanged */
414 io->buffered_len += b->length;
415 continue;
416 }
417 }
418 else {
419 /* bucket fits in remain, copy to scratch */
420 rv = read_to_scratch(io, b);
422 if (APR_SUCCESS != rv) goto cleanup;
423 continue;
424 }
425 }
426 else {
427 /* no buffering, forward buckets setaside on flush */
431 io->buffered_len += b->length;
432 }
433 }
434cleanup:
435 return rv;
436}
437
440{
443 const char *data;
444 ssize_t n;
445
447 while (APR_SUCCESS == rv && len > 0) {
448 n = nghttp2_session_mem_recv(session->ngh2, (const uint8_t *)data, len);
449
451 H2_SSSN_MSG(session, "fed %ld bytes to nghttp2, %ld read"),
452 (long)len, (long)n);
453 if (n < 0) {
454 if (nghttp2_is_fatal((int)n)) {
456 (int)n, nghttp2_strerror((int)n));
457 rv = APR_EGENERAL;
458 }
459 }
460 else {
461 *inout_len += n;
462 if ((apr_ssize_t)len <= n) {
463 break;
464 }
465 len -= (apr_size_t)n;
466 data += n;
467 }
468 }
469
470 return rv;
471}
472
476{
478 apr_bucket* b;
479
480 *inout_len = 0;
481 while (!APR_BRIGADE_EMPTY(bb)) {
482 b = APR_BRIGADE_FIRST(bb);
484 rv = c1_in_feed_bucket(session, b, inout_len);
485 if (APR_SUCCESS != rv) goto cleanup;
486 }
488 }
489cleanup:
491 return rv;
492}
493
495{
497 apr_status_t rv;
498
500 rv = ap_get_brigade(session->c1->input_filters,
501 session->bbtmp, AP_MODE_READBYTES,
503
504 if (APR_SUCCESS == rv) {
505 if (!APR_BRIGADE_EMPTY(session->bbtmp)) {
506 h2_util_bb_log(session->c1, session->id, APLOG_TRACE2, "c1 in",
507 session->bbtmp);
508 rv = c1_in_feed_brigade(session, session->bbtmp, &bytes_fed);
509 session->io.bytes_read += bytes_fed;
510 }
511 else {
512 rv = APR_EAGAIN;
513 }
514 }
515 return rv;
516}
517
519{
520 apr_status_t rv;
521
522 /* H2_IN filter handles all incoming data against the session.
523 * We just pull at the filter chain to make it happen */
525 H2_SSSN_MSG(session, "session_read start"));
526 rv = read_and_feed(session);
527
528 if (APR_SUCCESS == rv) {
530 }
531 else if (APR_STATUS_IS_EAGAIN(rv)) {
532 /* Signal that we have exhausted the input momentarily.
533 * This might switch to polling the socket */
535 }
536 else if (APR_SUCCESS != rv) {
540 || APR_STATUS_IS_EOF(rv)
541 || APR_STATUS_IS_EBADF(rv)) {
542 /* common status for a client that has left */
544 H2_SSSN_MSG(session, "input gone"));
545 }
546 else {
547 /* uncommon status, log on INFO so that we see this */
548 ap_log_cerror( APLOG_MARK, APLOG_DEBUG, rv, session->c1,
549 H2_SSSN_LOG(APLOGNO(02950), session,
550 "error reading, terminating"));
551 }
553 }
554
555 apr_brigade_cleanup(session->bbtmp);
557 H2_SSSN_MSG(session, "session_read done"));
558 return rv;
559}
Apache Multi-Processing Module library.
int n
Definition ap_regex.h:278
const char apr_size_t len
Definition ap_regex.h:187
APR Strings library.
int flush
apr_status_t ap_pass_brigade(ap_filter_t *filter, apr_bucket_brigade *bucket)
apr_status_t ap_get_brigade(ap_filter_t *filter, apr_bucket_brigade *bucket, ap_input_mode_t mode, apr_read_type_e block, apr_off_t readbytes)
#define APLOGNO(n)
Definition http_log.h:117
#define APLOG_TRACE4
Definition http_log.h:75
#define APLOG_INFO
Definition http_log.h:70
#define APLOG_ERR
Definition http_log.h:67
#define ap_log_cerror
Definition http_log.h:498
#define APLOG_MARK
Definition http_log.h:283
#define APLOG_TRACE2
Definition http_log.h:73
#define APLOGctrace1(c)
Definition http_log.h:257
#define APLOG_DEBUG
Definition http_log.h:71
int ap_ssl_conn_is_ssl(conn_rec *c)
Definition ssl.c:90
#define AP_BUCKET_IS_EOR(e)
#define APR_EAGAIN
Definition apr_errno.h:730
#define APR_EGENERAL
Definition apr_errno.h:313
#define APR_EOF
Definition apr_errno.h:461
#define APR_STATUS_IS_ECONNABORTED(s)
Definition apr_errno.h:1304
#define APR_STATUS_IS_ETIMEDOUT(s)
Definition apr_errno.h:1311
#define APR_STATUS_IS_ECONNRESET(s)
Definition apr_errno.h:1308
#define APR_STATUS_IS_EAGAIN(s)
Definition apr_errno.h:1272
#define APR_STATUS_IS_EBADF(s)
Definition apr_errno.h:1264
#define APR_STATUS_IS_EOF(s)
Definition apr_errno.h:567
#define APR_BUCKET_IS_PIPE(e)
apr_file_t * f
#define APR_BUCKET_IS_TRANSIENT(e)
#define APR_BUCKET_IS_FILE(e)
#define APR_BUCKET_IS_FLUSH(e)
#define APR_BUCKET_REMOVE(e)
#define APR_BUCKET_IS_HEAP(e)
#define APR_BRIGADE_LAST(b)
#define APR_BUCKET_IS_SOCKET(e)
#define APR_BUCKET_IS_METADATA(e)
#define APR_BUCKET_IS_IMMORTAL(e)
#define APR_BRIGADE_INSERT_TAIL(b, e)
#define apr_bucket_split(e, point)
#define APR_BUCKET_NEXT(e)
#define APR_BUCKET_BUFF_SIZE
Definition apr_buckets.h:54
#define APR_BRIGADE_EMPTY(b)
#define APR_BRIGADE_SENTINEL(b)
#define apr_bucket_delete(e)
#define APR_BUCKET_IS_EOS(e)
#define apr_bucket_setaside(e, p)
#define APR_BRIGADE_FIRST(b)
#define APR_BUCKET_IS_POOL(e)
apr_file_t * fd
#define apr_bucket_read(e, str, len, block)
int apr_off_t * length
@ APR_BLOCK_READ
Definition apr_buckets.h:58
@ APR_NONBLOCK_READ
Definition apr_buckets.h:59
#define AP_DEBUG_ASSERT(exp)
Definition httpd.h:2283
#define ap_assert(exp)
Definition httpd.h:2271
apr_size_t size
#define APR_SUCCESS
Definition apr_errno.h:225
int apr_status_t
Definition apr_errno.h:44
apr_seek_where_t apr_off_t * offset
void * data
void const char apr_status_t(* cleanup)(void *))
char * buffer
#define APR_SET
apr_vformatter_buff_t * c
Definition apr_lib.h:175
apr_pool_t * b
Definition apr_pools.h:529
int int status
#define APR_USEC_PER_SEC
Definition apr_time.h:60
apr_int64_t apr_time_t
Definition apr_time.h:45
#define H2MAX(x, y)
Definition h2.h:100
#define H2_BUCKET_IS_H2EOS(e)
#define BUF_REMAIN
Definition h2_c1_io.c:59
static apr_status_t c1_in_feed_bucket(h2_session *session, apr_bucket *b, apr_ssize_t *inout_len)
Definition h2_c1_io.c:438
int h2_c1_io_pending(h2_c1_io *io)
Definition h2_c1_io.c:334
static apr_status_t read_and_feed(h2_session *session)
Definition h2_c1_io.c:494
apr_status_t h2_c1_io_pass(h2_c1_io *io)
Definition h2_c1_io.c:339
#define WRITE_SIZE_INITIAL
Definition h2_c1_io.c:45
apr_status_t h2_c1_io_append(h2_c1_io *io, apr_bucket_brigade *bb)
Definition h2_c1_io.c:392
apr_status_t h2_c1_io_add_data(h2_c1_io *io, const char *data, size_t length)
Definition h2_c1_io.c:361
static apr_status_t pass_output(h2_c1_io *io, int flush)
Definition h2_c1_io.c:260
static apr_size_t assure_scratch_space(h2_c1_io *io)
Definition h2_c1_io.c:192
apr_status_t h2_c1_io_init(h2_c1_io *io, h2_session *session)
Definition h2_c1_io.c:141
static void append_scratch(h2_c1_io *io)
Definition h2_c1_io.c:179
static apr_status_t read_to_scratch(h2_c1_io *io, apr_bucket *b)
Definition h2_c1_io.c:208
static void h2_c1_io_bb_log(conn_rec *c, int stream_id, int level, const char *tag, apr_bucket_brigade *bb)
Definition h2_c1_io.c:61
apr_status_t h2_c1_io_assure_flushed(h2_c1_io *io)
Definition h2_c1_io.c:349
#define C1_IO_BB_LOG(c, stream_id, level, tag, bb)
Definition h2_c1_io.c:135
apr_status_t h2_c1_read(h2_session *session)
Definition h2_c1_io.c:518
static apr_status_t c1_in_feed_brigade(h2_session *session, apr_bucket_brigade *bb, apr_ssize_t *inout_len)
Definition h2_c1_io.c:473
#define WRITE_SIZE_MAX
Definition h2_c1_io.c:57
int h2_c1_io_needs_flush(h2_c1_io *io)
Definition h2_c1_io.c:329
apr_int64_t h2_config_sgeti64(server_rec *s, h2_config_var_t var)
Definition h2_config.c:511
int h2_config_sgeti(server_rec *s, h2_config_var_t var)
Definition h2_config.c:506
@ H2_CONF_TLS_WARMUP_SIZE
Definition h2_config.h:36
@ H2_CONF_TLS_COOLDOWN_SECS
Definition h2_config.h:37
@ H2_CONF_STREAM_MAX_MEM
Definition h2_config.h:32
void h2_session_event(h2_session *session, h2_session_event_t ev, int err, const char *msg)
Definition h2_session.c:84
void h2_session_dispatch_event(h2_session *session, h2_session_event_t ev, apr_status_t arg, const char *msg)
#define H2_SSSN_LOG(aplogno, s, msg)
Definition h2_session.h:201
@ H2_SESSION_EV_PROTO_ERROR
Definition h2_session.h:56
@ H2_SESSION_EV_INPUT_EXHAUSTED
Definition h2_session.h:52
@ H2_SESSION_EV_INPUT_PENDING
Definition h2_session.h:51
@ H2_SESSION_EV_CONN_ERROR
Definition h2_session.h:55
#define H2_SSSN_MSG(s, msg)
Definition h2_session.h:196
#define h2_util_bb_log(c, sid, level, tag, bb)
Definition h2_util.h:456
Apache connection library.
CORE HTTP Daemon.
Apache Logging library.
HTTP protocol handling.
Apache Request library.
SSL protocol handling.
HTTP Daemon routines.
return NULL
Definition mod_so.c:359
Multi-Processing Modules functions.
Structure to store things which are per connection.
Definition httpd.h:1152
apr_pool_t * pool
Definition httpd.h:1154
struct ap_filter_t * input_filters
Definition httpd.h:1195
struct apr_bucket_alloc_t * bucket_alloc
Definition httpd.h:1201
long id
Definition httpd.h:1187
apr_size_t write_size
Definition h2_c1_io.h:38
apr_size_t slen
Definition h2_c1_io.h:51
apr_int64_t bytes_read
Definition h2_c1_io.h:40
unsigned int is_passing
Definition h2_c1_io.h:47
apr_time_t cooldown_usecs
Definition h2_c1_io.h:35
apr_int64_t warmup_size
Definition h2_c1_io.h:36
int unflushed
Definition h2_c1_io.h:34
char * scratch
Definition h2_c1_io.h:49
struct h2_session * session
Definition h2_c1_io.h:30
apr_bucket_brigade * output
Definition h2_c1_io.h:31
apr_time_t last_write
Definition h2_c1_io.h:39
apr_int64_t bytes_written
Definition h2_c1_io.h:41
apr_off_t flush_threshold
Definition h2_c1_io.h:45
int buffer_output
Definition h2_c1_io.h:43
int is_tls
Definition h2_c1_io.h:33
apr_size_t ssize
Definition h2_c1_io.h:50
apr_off_t buffered_len
Definition h2_c1_io.h:44
apr_bucket_brigade * bbtmp
Definition h2_session.h:111
apr_uint32_t id
Definition h2_session.h:66
h2_c1_io io
Definition h2_session.h:75
conn_rec * c1
Definition h2_session.h:67
apr_size_t max_stream_mem
Definition h2_session.h:105
server_rec * s
Definition h2_session.h:70
struct nghttp2_session * ngh2
Definition h2_session.h:78
static apr_time_t now
Definition testtime.c:33
@ AP_MODE_READBYTES
Definition util_filter.h:43