static int brigade_split_line(lua_State*L) { apr_bucket_brigade *bbout = (apr_bucket_brigade *)CHECK_BUCKETBRIGADE_OBJECT(1); apr_bucket_brigade *bbin = (apr_bucket_brigade *)CHECK_BUCKETBRIGADE_OBJECT(2); apr_read_type_e block = luaL_checkint(L,3); apr_off_t maxbytes = luaL_checkint(L,4); apr_status_t rc = apr_brigade_split_line(bbout,bbin,block,maxbytes); lua_pushinteger(L,rc); return 1; }
/* Return a pool-allocated NUL-terminated line, with CRLF stripped, * read from brigade 'bbin' using 'bbout' as temporary storage. */ static char *get_line(apr_bucket_brigade *bbout, apr_bucket_brigade *bbin, conn_rec *c, apr_pool_t *p) { apr_status_t rv; apr_size_t len; char *line; apr_brigade_cleanup(bbout); rv = apr_brigade_split_line(bbout, bbin, APR_BLOCK_READ, 8192); if (rv) { ap_log_cerror(APLOG_MARK, APLOG_ERR, rv, c, APLOGNO(01977) "failed reading line from OCSP server"); return NULL; } rv = apr_brigade_pflatten(bbout, &line, &len, p); if (rv) { ap_log_cerror(APLOG_MARK, APLOG_ERR, rv, c, APLOGNO(01978) "failed reading line from OCSP server"); return NULL; } if (len == 0) { ap_log_cerror(APLOG_MARK, APLOG_ERR, rv, c, APLOGNO(02321) "empty response from OCSP server"); return NULL; } if (line[len-1] != APR_ASCII_LF) { ap_log_cerror(APLOG_MARK, APLOG_ERR, rv, c, APLOGNO(01979) "response header line too long from OCSP server"); return NULL; } line[len-1] = '\0'; if (len > 1 && line[len-2] == APR_ASCII_CR) { line[len-2] = '\0'; } return line; }
static void test_splitline(abts_case *tc, void *data) { apr_bucket_alloc_t *ba = apr_bucket_alloc_create(p); apr_bucket_brigade *bin, *bout; bin = make_simple_brigade(ba, "blah blah blah-", "end of line.\nfoo foo foo"); bout = apr_brigade_create(p, ba); apr_assert_success(tc, "split line", apr_brigade_split_line(bout, bin, APR_BLOCK_READ, 100)); flatten_match(tc, "split line", bout, "blah blah blah-end of line.\n"); flatten_match(tc, "remainder", bin, "foo foo foo"); apr_brigade_destroy(bout); apr_brigade_destroy(bin); apr_bucket_alloc_destroy(ba); }
apr_status_t ap_core_input_filter(ap_filter_t *f, apr_bucket_brigade *b, ap_input_mode_t mode, apr_read_type_e block, apr_off_t readbytes) { apr_status_t rv; core_net_rec *net = f->ctx; core_ctx_t *ctx = net->in_ctx; const char *str; apr_size_t len; if (mode == AP_MODE_INIT) { /* * this mode is for filters that might need to 'initialize' * a connection before reading request data from a client. * NNTP over SSL for example needs to handshake before the * server sends the welcome message. * such filters would have changed the mode before this point * is reached. however, protocol modules such as NNTP should * not need to know anything about SSL. given the example, if * SSL is not in the filter chain, AP_MODE_INIT is a noop. */ return APR_SUCCESS; } if (!ctx) { net->in_ctx = ctx = apr_palloc(f->c->pool, sizeof(*ctx)); ctx->b = apr_brigade_create(f->c->pool, f->c->bucket_alloc); ctx->tmpbb = apr_brigade_create(f->c->pool, f->c->bucket_alloc); /* seed the brigade with the client socket. */ rv = ap_run_insert_network_bucket(f->c, ctx->b, net->client_socket); if (rv != APR_SUCCESS) return rv; } else if (APR_BRIGADE_EMPTY(ctx->b)) { return APR_EOF; } /* ### This is bad. */ BRIGADE_NORMALIZE(ctx->b); /* check for empty brigade again *AFTER* BRIGADE_NORMALIZE() * If we have lost our socket bucket (see above), we are EOF. * * Ideally, this should be returning SUCCESS with EOS bucket, but * some higher-up APIs (spec. read_request_line via ap_rgetline) * want an error code. */ if (APR_BRIGADE_EMPTY(ctx->b)) { return APR_EOF; } if (mode == AP_MODE_GETLINE) { /* we are reading a single LF line, e.g. the HTTP headers */ rv = apr_brigade_split_line(b, ctx->b, block, HUGE_STRING_LEN); /* We should treat EAGAIN here the same as we do for EOF (brigade is * empty). We do this by returning whatever we have read. This may * or may not be bogus, but is consistent (for now) with EOF logic. */ if (APR_STATUS_IS_EAGAIN(rv) && block == APR_NONBLOCK_READ) { rv = APR_SUCCESS; } return rv; } /* ### AP_MODE_PEEK is a horrific name for this mode because we also * eat any CRLFs that we see. That's not the obvious intention of * this mode. Determine whether anyone actually uses this or not. */ if (mode == AP_MODE_EATCRLF) { apr_bucket *e; const char *c; /* The purpose of this loop is to ignore any CRLF (or LF) at the end * of a request. Many browsers send extra lines at the end of POST * requests. We use the PEEK method to determine if there is more * data on the socket, so that we know if we should delay sending the * end of one request until we have served the second request in a * pipelined situation. We don't want to actually delay sending a * response if the server finds a CRLF (or LF), becuause that doesn't * mean that there is another request, just a blank line. */ while (1) { if (APR_BRIGADE_EMPTY(ctx->b)) return APR_EOF; e = APR_BRIGADE_FIRST(ctx->b); rv = apr_bucket_read(e, &str, &len, APR_NONBLOCK_READ); if (rv != APR_SUCCESS) return rv; c = str; while (c < str + len) { if (*c == APR_ASCII_LF) c++; else if (*c == APR_ASCII_CR && *(c + 1) == APR_ASCII_LF) c += 2; else return APR_SUCCESS; } /* If we reach here, we were a bucket just full of CRLFs, so * just toss the bucket. */ /* FIXME: Is this the right thing to do in the core? */ apr_bucket_delete(e); } return APR_SUCCESS; } /* If mode is EXHAUSTIVE, we want to just read everything until the end * of the brigade, which in this case means the end of the socket. * To do this, we attach the brigade that has currently been setaside to * the brigade that was passed down, and send that brigade back. * * NOTE: This is VERY dangerous to use, and should only be done with * extreme caution. FWLIW, this would be needed by an MPM like Perchild; * such an MPM can easily request the socket and all data that has been * read, which means that it can pass it to the correct child process. */ if (mode == AP_MODE_EXHAUSTIVE) { apr_bucket *e; /* Tack on any buckets that were set aside. */ APR_BRIGADE_CONCAT(b, ctx->b); /* Since we've just added all potential buckets (which will most * likely simply be the socket bucket) we know this is the end, * so tack on an EOS too. */ /* We have read until the brigade was empty, so we know that we * must be EOS. */ e = apr_bucket_eos_create(f->c->bucket_alloc); APR_BRIGADE_INSERT_TAIL(b, e); return APR_SUCCESS; } /* read up to the amount they specified. */ if (mode == AP_MODE_READBYTES || mode == AP_MODE_SPECULATIVE) { apr_bucket *e; AP_DEBUG_ASSERT(readbytes > 0); e = APR_BRIGADE_FIRST(ctx->b); rv = apr_bucket_read(e, &str, &len, block); if (APR_STATUS_IS_EAGAIN(rv) && block == APR_NONBLOCK_READ) { /* getting EAGAIN for a blocking read is an error; for a * non-blocking read, return an empty brigade. */ return APR_SUCCESS; } else if (rv != APR_SUCCESS) { return rv; } else if (block == APR_BLOCK_READ && len == 0) { /* We wanted to read some bytes in blocking mode. We read * 0 bytes. Hence, we now assume we are EOS. * * When we are in normal mode, return an EOS bucket to the * caller. * When we are in speculative mode, leave ctx->b empty, so * that the next call returns an EOS bucket. */ apr_bucket_delete(e); if (mode == AP_MODE_READBYTES) { e = apr_bucket_eos_create(f->c->bucket_alloc); APR_BRIGADE_INSERT_TAIL(b, e); } return APR_SUCCESS; } /* Have we read as much data as we wanted (be greedy)? */ if (len < readbytes) { apr_size_t bucket_len; rv = APR_SUCCESS; /* We already registered the data in e in len */ e = APR_BUCKET_NEXT(e); while ((len < readbytes) && (rv == APR_SUCCESS) && (e != APR_BRIGADE_SENTINEL(ctx->b))) { /* Check for the availability of buckets with known length */ if (e->length != -1) { len += e->length; e = APR_BUCKET_NEXT(e); } else { /* * Read from bucket, but non blocking. If there isn't any * more data, well than this is fine as well, we will * not wait for more since we already got some and we are * only checking if there isn't more. */ rv = apr_bucket_read(e, &str, &bucket_len, APR_NONBLOCK_READ); if (rv == APR_SUCCESS) { len += bucket_len; e = APR_BUCKET_NEXT(e); } } } } /* We can only return at most what we read. */ if (len < readbytes) { readbytes = len; } rv = apr_brigade_partition(ctx->b, readbytes, &e); if (rv != APR_SUCCESS) { return rv; } /* Must do move before CONCAT */ ctx->tmpbb = apr_brigade_split_ex(ctx->b, e, ctx->tmpbb); if (mode == AP_MODE_READBYTES) { APR_BRIGADE_CONCAT(b, ctx->b); } else if (mode == AP_MODE_SPECULATIVE) { apr_bucket *copy_bucket; for (e = APR_BRIGADE_FIRST(ctx->b); e != APR_BRIGADE_SENTINEL(ctx->b); e = APR_BUCKET_NEXT(e)) { rv = apr_bucket_copy(e, ©_bucket); if (rv != APR_SUCCESS) { return rv; } APR_BRIGADE_INSERT_TAIL(b, copy_bucket); } } /* Take what was originally there and place it back on ctx->b */ APR_BRIGADE_CONCAT(ctx->b, ctx->tmpbb); } return APR_SUCCESS; }
static apr_status_t readfile_heartbeats(const char *path, apr_hash_t *servers, apr_pool_t *pool) { apr_finfo_t fi; apr_status_t rv; apr_file_t *fp; if (!path) { return APR_SUCCESS; } rv = apr_file_open(&fp, path, APR_READ|APR_BINARY|APR_BUFFERED, APR_OS_DEFAULT, pool); if (rv) { return rv; } rv = apr_file_info_get(&fi, APR_FINFO_SIZE, fp); if (rv) { return rv; } { char *t; int lineno = 0; apr_bucket_alloc_t *ba = apr_bucket_alloc_create(pool); apr_bucket_brigade *bb = apr_brigade_create(pool, ba); apr_bucket_brigade *tmpbb = apr_brigade_create(pool, ba); apr_table_t *hbt = apr_table_make(pool, 10); apr_brigade_insert_file(bb, fp, 0, fi.size, pool); do { hb_server_t *server; char buf[4096]; apr_size_t bsize = sizeof(buf); const char *ip; apr_brigade_cleanup(tmpbb); if (APR_BRIGADE_EMPTY(bb)) { break; } rv = apr_brigade_split_line(tmpbb, bb, APR_BLOCK_READ, sizeof(buf)); lineno++; if (rv) { return rv; } apr_brigade_flatten(tmpbb, buf, &bsize); if (bsize == 0) { break; } buf[bsize - 1] = 0; /* comment */ if (buf[0] == '#') { continue; } /* line format: <IP> <query_string>\n */ t = strchr(buf, ' '); if (!t) { continue; } ip = apr_pstrmemdup(pool, buf, t - buf); t++; server = apr_hash_get(servers, ip, APR_HASH_KEY_STRING); if (server == NULL) { server = apr_pcalloc(pool, sizeof(hb_server_t)); server->ip = ip; server->port = 80; server->seen = -1; apr_hash_set(servers, server->ip, APR_HASH_KEY_STRING, server); } apr_table_clear(hbt); argstr_to_table(pool, apr_pstrdup(pool, t), hbt); if (apr_table_get(hbt, "busy")) { server->busy = atoi(apr_table_get(hbt, "busy")); } if (apr_table_get(hbt, "ready")) { server->ready = atoi(apr_table_get(hbt, "ready")); } if (apr_table_get(hbt, "lastseen")) { server->seen = atoi(apr_table_get(hbt, "lastseen")); } if (apr_table_get(hbt, "port")) { server->port = atoi(apr_table_get(hbt, "port")); } if (server->busy == 0 && server->ready != 0) { /* Server has zero threads active, but lots of them ready, * it likely just started up, so lets /4 the number ready, * to prevent us from completely flooding it with all new * requests. */ server->ready = server->ready / 4; } } while (1); } return APR_SUCCESS; }
static apr_status_t h2_filter_slave_in(ap_filter_t* f, apr_bucket_brigade* bb, ap_input_mode_t mode, apr_read_type_e block, apr_off_t readbytes) { h2_task *task; apr_status_t status = APR_SUCCESS; apr_bucket *b, *next; apr_off_t bblen; const int trace1 = APLOGctrace1(f->c); apr_size_t rmax = ((readbytes <= APR_SIZE_MAX)? (apr_size_t)readbytes : APR_SIZE_MAX); task = h2_ctx_cget_task(f->c); ap_assert(task); if (trace1) { ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, f->c, "h2_slave_in(%s): read, mode=%d, block=%d, readbytes=%ld", task->id, mode, block, (long)readbytes); } if (mode == AP_MODE_INIT) { return ap_get_brigade(f->c->input_filters, bb, mode, block, readbytes); } if (f->c->aborted) { return APR_ECONNABORTED; } if (!task->input.bb) { return APR_EOF; } /* Cleanup brigades from those nasty 0 length non-meta buckets * that apr_brigade_split_line() sometimes produces. */ for (b = APR_BRIGADE_FIRST(task->input.bb); b != APR_BRIGADE_SENTINEL(task->input.bb); b = next) { next = APR_BUCKET_NEXT(b); if (b->length == 0 && !APR_BUCKET_IS_METADATA(b)) { apr_bucket_delete(b); } } while (APR_BRIGADE_EMPTY(task->input.bb)) { /* Get more input data for our request. */ if (trace1) { ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, f->c, "h2_slave_in(%s): get more data from mplx, block=%d, " "readbytes=%ld", task->id, block, (long)readbytes); } if (task->input.beam) { status = h2_beam_receive(task->input.beam, task->input.bb, block, 128*1024); } else { status = APR_EOF; } if (trace1) { ap_log_cerror(APLOG_MARK, APLOG_TRACE2, status, f->c, "h2_slave_in(%s): read returned", task->id); } if (APR_STATUS_IS_EAGAIN(status) && (mode == AP_MODE_GETLINE || block == APR_BLOCK_READ)) { /* chunked input handling does not seem to like it if we * return with APR_EAGAIN from a GETLINE read... * upload 100k test on test-ser.example.org hangs */ status = APR_SUCCESS; } else if (APR_STATUS_IS_EOF(status)) { break; } else if (status != APR_SUCCESS) { return status; } if (trace1) { h2_util_bb_log(f->c, task->stream_id, APLOG_TRACE2, "input.beam recv raw", task->input.bb); } if (h2_task_logio_add_bytes_in) { apr_brigade_length(bb, 0, &bblen); h2_task_logio_add_bytes_in(f->c, bblen); } } /* Nothing there, no more data to get. Return APR_EAGAIN on * speculative reads, this is ap_check_pipeline()'s trick to * see if the connection needs closing. */ if (status == APR_EOF && APR_BRIGADE_EMPTY(task->input.bb)) { return (mode == AP_MODE_SPECULATIVE)? APR_EAGAIN : APR_EOF; } if (trace1) { h2_util_bb_log(f->c, task->stream_id, APLOG_TRACE2, "task_input.bb", task->input.bb); } if (APR_BRIGADE_EMPTY(task->input.bb)) { if (trace1) { ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, f->c, "h2_slave_in(%s): no data", task->id); } return (block == APR_NONBLOCK_READ)? APR_EAGAIN : APR_EOF; } if (mode == AP_MODE_EXHAUSTIVE) { /* return all we have */ APR_BRIGADE_CONCAT(bb, task->input.bb); } else if (mode == AP_MODE_READBYTES) { status = h2_brigade_concat_length(bb, task->input.bb, rmax); } else if (mode == AP_MODE_SPECULATIVE) { status = h2_brigade_copy_length(bb, task->input.bb, rmax); } else if (mode == AP_MODE_GETLINE) { /* we are reading a single LF line, e.g. the HTTP headers. * this has the nasty side effect to split the bucket, even * though it ends with CRLF and creates a 0 length bucket */ status = apr_brigade_split_line(bb, task->input.bb, block, HUGE_STRING_LEN); if (APLOGctrace1(f->c)) { char buffer[1024]; apr_size_t len = sizeof(buffer)-1; apr_brigade_flatten(bb, buffer, &len); buffer[len] = 0; if (trace1) { ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, f->c, "h2_slave_in(%s): getline: %s", task->id, buffer); } } } else { /* Hmm, well. There is mode AP_MODE_EATCRLF, but we chose not * to support it. Seems to work. */ ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_ENOTIMPL, f->c, APLOGNO(03472) "h2_slave_in(%s), unsupported READ mode %d", task->id, mode); status = APR_ENOTIMPL; } if (trace1) { apr_brigade_length(bb, 0, &bblen); ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, f->c, "h2_slave_in(%s): %ld data bytes", task->id, (long)bblen); } return status; }
apr_status_t h2_task_input_read(h2_task_input *input, ap_filter_t* f, apr_bucket_brigade* bb, ap_input_mode_t mode, apr_read_type_e block, apr_off_t readbytes) { apr_status_t status = APR_SUCCESS; apr_off_t bblen = 0; ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, f->c, "h2_task_input(%s): read, block=%d, mode=%d, readbytes=%ld", input->task->id, block, mode, (long)readbytes); if (mode == AP_MODE_INIT) { return ap_get_brigade(f->c->input_filters, bb, mode, block, readbytes); } if (is_aborted(f)) { ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, f->c, "h2_task_input(%s): is aborted", input->task->id); return APR_ECONNABORTED; } if (input->bb) { status = apr_brigade_length(input->bb, 1, &bblen); if (status != APR_SUCCESS) { ap_log_cerror(APLOG_MARK, APLOG_WARNING, status, f->c, APLOGNO(02958) "h2_task_input(%s): brigade length fail", input->task->id); return status; } } if ((bblen == 0) && input->task->input_eos) { return APR_EOF; } while ((bblen == 0) || (mode == AP_MODE_READBYTES && bblen < readbytes)) { /* Get more data for our stream from mplx. */ ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, f->c, "h2_task_input(%s): get more data from mplx, block=%d, " "readbytes=%ld, queued=%ld", input->task->id, block, (long)readbytes, (long)bblen); /* Although we sometimes get called with APR_NONBLOCK_READs, we seem to fill our buffer blocking. Otherwise we get EAGAIN, return that to our caller and everyone throws up their hands, never calling us again. */ status = h2_mplx_in_read(input->task->mplx, APR_BLOCK_READ, input->task->stream_id, input->bb, input->task->io); ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, f->c, "h2_task_input(%s): mplx in read returned", input->task->id); if (status != APR_SUCCESS) { return status; } status = apr_brigade_length(input->bb, 1, &bblen); if (status != APR_SUCCESS) { return status; } if ((bblen == 0) && (block == APR_NONBLOCK_READ)) { return h2_util_has_eos(input->bb, -1)? APR_EOF : APR_EAGAIN; } ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, f->c, "h2_task_input(%s): mplx in read, %ld bytes in brigade", input->task->id, (long)bblen); } ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, f->c, "h2_task_input(%s): read, mode=%d, block=%d, " "readbytes=%ld, queued=%ld", input->task->id, mode, block, (long)readbytes, (long)bblen); if (!APR_BRIGADE_EMPTY(input->bb)) { if (mode == AP_MODE_EXHAUSTIVE) { /* return all we have */ return h2_util_move(bb, input->bb, readbytes, NULL, "task_input_read(exhaustive)"); } else if (mode == AP_MODE_READBYTES) { return h2_util_move(bb, input->bb, readbytes, NULL, "task_input_read(readbytes)"); } else if (mode == AP_MODE_SPECULATIVE) { /* return not more than was asked for */ return h2_util_copy(bb, input->bb, readbytes, "task_input_read(speculative)"); } else if (mode == AP_MODE_GETLINE) { /* we are reading a single LF line, e.g. the HTTP headers */ status = apr_brigade_split_line(bb, input->bb, block, HUGE_STRING_LEN); if (APLOGctrace1(f->c)) { char buffer[1024]; apr_size_t len = sizeof(buffer)-1; apr_brigade_flatten(bb, buffer, &len); buffer[len] = 0; ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, f->c, "h2_task_input(%s): getline: %s", input->task->id, buffer); } return status; } else { /* Hmm, well. There is mode AP_MODE_EATCRLF, but we chose not * to support it. Seems to work. */ ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_ENOTIMPL, f->c, APLOGNO(02942) "h2_task_input, unsupported READ mode %d", mode); return APR_ENOTIMPL; } } if (is_aborted(f)) { return APR_ECONNABORTED; } return (block == APR_NONBLOCK_READ)? APR_EAGAIN : APR_EOF; }