/* Regression test for PR 34708, where a file bucket will keep * duplicating itself on being read() when EOF is reached * prematurely. */ static void test_truncfile(abts_case *tc, void *data) { apr_bucket_alloc_t *ba = apr_bucket_alloc_create(p); apr_bucket_brigade *bb = apr_brigade_create(p, ba); apr_file_t *f = make_test_file(tc, "testfile.txt", "hello"); apr_bucket *e; const char *buf; apr_size_t len; apr_brigade_insert_file(bb, f, 0, 5, p); apr_file_trunc(f, 0); e = APR_BRIGADE_FIRST(bb); ABTS_ASSERT(tc, "single bucket in brigade", APR_BUCKET_NEXT(e) == APR_BRIGADE_SENTINEL(bb)); apr_bucket_file_enable_mmap(e, 0); ABTS_ASSERT(tc, "read gave APR_EOF", apr_bucket_read(e, &buf, &len, APR_BLOCK_READ) == APR_EOF); ABTS_ASSERT(tc, "read length 0", len == 0); ABTS_ASSERT(tc, "still a single bucket in brigade", APR_BUCKET_NEXT(e) == APR_BRIGADE_SENTINEL(bb)); apr_file_close(f); apr_brigade_destroy(bb); apr_bucket_alloc_destroy(ba); }
static void test_manyfile(abts_case *tc, void *data) { apr_bucket_alloc_t *ba = apr_bucket_alloc_create(p); apr_bucket_brigade *bb = apr_brigade_create(p, ba); apr_file_t *f; f = make_test_file(tc, "manyfile.bin", "world" "hello" "brave" " ,\n"); apr_brigade_insert_file(bb, f, 5, 5, p); apr_brigade_insert_file(bb, f, 16, 1, p); apr_brigade_insert_file(bb, f, 15, 1, p); apr_brigade_insert_file(bb, f, 10, 5, p); apr_brigade_insert_file(bb, f, 15, 1, p); apr_brigade_insert_file(bb, f, 0, 5, p); apr_brigade_insert_file(bb, f, 17, 1, p); /* can you tell what it is yet? */ flatten_match(tc, "file seek test", bb, "hello, brave world\n"); apr_file_close(f); apr_brigade_destroy(bb); apr_bucket_alloc_destroy(ba); }
static void test_insertfile(abts_case *tc, void *ctx) { apr_bucket_alloc_t *ba = apr_bucket_alloc_create(p); apr_bucket_brigade *bb; const apr_off_t bignum = (APR_INT64_C(2) << 32) + 424242; apr_off_t count; apr_file_t *f; apr_bucket *e; ABTS_ASSERT(tc, "open test file", apr_file_open(&f, TIF_FNAME, APR_FOPEN_WRITE | APR_FOPEN_TRUNCATE | APR_FOPEN_CREATE | APR_FOPEN_SPARSE, APR_OS_DEFAULT, p) == APR_SUCCESS); if (apr_file_trunc(f, bignum)) { apr_file_close(f); apr_file_remove(TIF_FNAME, p); ABTS_NOT_IMPL(tc, "Skipped: could not create large file"); return; } bb = apr_brigade_create(p, ba); e = apr_brigade_insert_file(bb, f, 0, bignum, p); ABTS_ASSERT(tc, "inserted file was not at end of brigade", e == APR_BRIGADE_LAST(bb)); /* check that the total size of inserted buckets is equal to the * total size of the file. */ count = 0; for (e = APR_BRIGADE_FIRST(bb); e != APR_BRIGADE_SENTINEL(bb); e = APR_BUCKET_NEXT(e)) { ABTS_ASSERT(tc, "bucket size sane", e->length != (apr_size_t)-1); count += e->length; } ABTS_ASSERT(tc, "total size of buckets incorrect", count == bignum); apr_brigade_destroy(bb); /* Truncate the file to zero size before close() so that we don't * actually write out the large file if we are on a non-sparse file * system - like Mac OS X's HFS. Otherwise, pity the poor user who * has to wait for the 8GB file to be written to disk. */ apr_file_trunc(f, 0); apr_file_close(f); apr_bucket_alloc_destroy(ba); apr_file_remove(TIF_FNAME, p); }
static int sendfile_handler(request_rec *r, a_file *file) { #if APR_HAS_SENDFILE conn_rec *c = r->connection; apr_bucket *b; apr_bucket_brigade *bb = apr_brigade_create(r->pool, c->bucket_alloc); apr_brigade_insert_file(bb, file->file, 0, file->finfo.size, r->pool); b = apr_bucket_eos_create(c->bucket_alloc); APR_BRIGADE_INSERT_TAIL(bb, b); if (ap_pass_brigade(r->output_filters, bb) != APR_SUCCESS) return AP_FILTER_ERROR; #endif return OK; }
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 int drive_flvx(request_rec *r) { apr_finfo_t fi; apr_bucket_brigade *bb; apr_off_t offset = 0; apr_off_t length = 0; apr_file_t *fp = NULL; apr_status_t rv = APR_SUCCESS; rv = apr_stat(&fi, r->filename, APR_FINFO_SIZE, r->pool); if (rv) { /* Let the core handle it. */ return DECLINED; } /* Open the file */ rv = apr_file_open(&fp, r->filename, APR_READ, APR_OS_DEFAULT, r->pool); if (rv) { ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, "file permissions deny server access: %s", r->filename); return HTTP_FORBIDDEN; } offset = get_start(r); if (offset != 0 && offset < fi.size) { length = fi.size - offset; } else { length = fi.size; /* Offset should be reset if invalid mod by Artur Bodera */ offset = 0; } bb = apr_brigade_create(r->pool, r->connection->bucket_alloc); if (offset != 0) { length += FLVX_HEADER_LEN; rv = apr_brigade_write(bb, NULL, NULL, FLVX_HEADER, FLVX_HEADER_LEN); if (rv) { ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, "unable to write flv header in brigade"); return HTTP_INTERNAL_SERVER_ERROR; } } apr_brigade_insert_file(bb, fp, offset, length, r->pool); ap_set_content_type(r, "video/x-flv"); ap_set_content_length(r, length); /* Add last-modified headers mod by Artur Bodera */ ap_update_mtime(r, r->finfo.mtime); ap_set_last_modified(r); return ap_pass_brigade(r->output_filters, bb); }
apr_status_t h2_util_move(apr_bucket_brigade *to, apr_bucket_brigade *from, apr_size_t maxlen, int *pfile_handles_allowed, const char *msg) { apr_status_t status = APR_SUCCESS; int same_alloc; AP_DEBUG_ASSERT(to); AP_DEBUG_ASSERT(from); same_alloc = (to->bucket_alloc == from->bucket_alloc); if (!FILE_MOVE) { pfile_handles_allowed = NULL; } if (!APR_BRIGADE_EMPTY(from)) { apr_bucket *b, *end; status = last_not_included(from, maxlen, same_alloc, pfile_handles_allowed, &end); if (status != APR_SUCCESS) { return status; } while (!APR_BRIGADE_EMPTY(from) && status == APR_SUCCESS) { b = APR_BRIGADE_FIRST(from); if (b == end) { break; } if (same_alloc || (b->list == to->bucket_alloc)) { /* both brigades use the same bucket_alloc and auto-cleanups * have the same life time. It's therefore safe to just move * directly. */ APR_BUCKET_REMOVE(b); APR_BRIGADE_INSERT_TAIL(to, b); #if LOG_BUCKETS ap_log_perror(APLOG_MARK, LOG_LEVEL, 0, to->p, "h2_util_move: %s, passed bucket(same bucket_alloc) " "%ld-%ld, type=%s", msg, (long)b->start, (long)b->length, APR_BUCKET_IS_METADATA(b)? (APR_BUCKET_IS_EOS(b)? "EOS": (APR_BUCKET_IS_FLUSH(b)? "FLUSH" : "META")) : (APR_BUCKET_IS_FILE(b)? "FILE" : "DATA")); #endif } else if (DEEP_COPY) { /* we have not managed the magic of passing buckets from * one thread to another. Any attempts result in * cleanup of pools scrambling memory. */ if (APR_BUCKET_IS_METADATA(b)) { if (APR_BUCKET_IS_EOS(b)) { APR_BRIGADE_INSERT_TAIL(to, apr_bucket_eos_create(to->bucket_alloc)); } else if (APR_BUCKET_IS_FLUSH(b)) { APR_BRIGADE_INSERT_TAIL(to, apr_bucket_flush_create(to->bucket_alloc)); } else { /* ignore */ } } else if (pfile_handles_allowed && *pfile_handles_allowed > 0 && APR_BUCKET_IS_FILE(b)) { /* We do not want to read files when passing buckets, if * we can avoid it. However, what we've come up so far * is not working corrently, resulting either in crashes or * too many open file descriptors. */ apr_bucket_file *f = (apr_bucket_file *)b->data; apr_file_t *fd = f->fd; int setaside = (f->readpool != to->p); #if LOG_BUCKETS ap_log_perror(APLOG_MARK, LOG_LEVEL, 0, to->p, "h2_util_move: %s, moving FILE bucket %ld-%ld " "from=%lx(p=%lx) to=%lx(p=%lx), setaside=%d", msg, (long)b->start, (long)b->length, (long)from, (long)from->p, (long)to, (long)to->p, setaside); #endif if (setaside) { status = apr_file_setaside(&fd, fd, to->p); if (status != APR_SUCCESS) { ap_log_perror(APLOG_MARK, APLOG_ERR, status, to->p, APLOGNO(02947) "h2_util: %s, setaside FILE", msg); return status; } } apr_brigade_insert_file(to, fd, b->start, b->length, to->p); --(*pfile_handles_allowed); } else { const char *data; apr_size_t len; status = apr_bucket_read(b, &data, &len, APR_BLOCK_READ); if (status == APR_SUCCESS && len > 0) { status = apr_brigade_write(to, NULL, NULL, data, len); #if LOG_BUCKETS ap_log_perror(APLOG_MARK, LOG_LEVEL, 0, to->p, "h2_util_move: %s, copied bucket %ld-%ld " "from=%lx(p=%lx) to=%lx(p=%lx)", msg, (long)b->start, (long)b->length, (long)from, (long)from->p, (long)to, (long)to->p); #endif } } apr_bucket_delete(b); } else { apr_bucket_setaside(b, to->p); APR_BUCKET_REMOVE(b); APR_BRIGADE_INSERT_TAIL(to, b); #if LOG_BUCKETS ap_log_perror(APLOG_MARK, LOG_LEVEL, 0, to->p, "h2_util_move: %s, passed setaside bucket %ld-%ld " "from=%lx(p=%lx) to=%lx(p=%lx)", msg, (long)b->start, (long)b->length, (long)from, (long)from->p, (long)to, (long)to->p); #endif } } } return status; }
apr_status_t h2_beam_receive(h2_bucket_beam *beam, apr_bucket_brigade *bb, apr_read_type_e block, apr_off_t readbytes) { h2_beam_lock bl; apr_bucket *bred, *bgreen, *ng; int transferred = 0; apr_status_t status = APR_SUCCESS; apr_off_t remain = readbytes; /* Called from the green thread to take buckets from the beam */ if (enter_yellow(beam, &bl) == APR_SUCCESS) { transfer: if (beam->aborted) { if (beam->green && !APR_BRIGADE_EMPTY(beam->green)) { apr_brigade_cleanup(beam->green); } status = APR_ECONNABORTED; goto leave; } /* transfer enough buckets from our green brigade, if we have one */ while (beam->green && !APR_BRIGADE_EMPTY(beam->green) && (readbytes <= 0 || remain >= 0)) { bgreen = APR_BRIGADE_FIRST(beam->green); if (readbytes > 0 && bgreen->length > 0 && remain <= 0) { break; } APR_BUCKET_REMOVE(bgreen); APR_BRIGADE_INSERT_TAIL(bb, bgreen); remain -= bgreen->length; ++transferred; } /* transfer from our red brigade, transforming red buckets to * green ones until we have enough */ while (!H2_BLIST_EMPTY(&beam->red) && (readbytes <= 0 || remain >= 0)) { bred = H2_BLIST_FIRST(&beam->red); bgreen = NULL; if (readbytes > 0 && bred->length > 0 && remain <= 0) { break; } if (APR_BUCKET_IS_METADATA(bred)) { if (APR_BUCKET_IS_EOS(bred)) { bgreen = apr_bucket_eos_create(bb->bucket_alloc); beam->close_sent = 1; } else if (APR_BUCKET_IS_FLUSH(bred)) { bgreen = apr_bucket_flush_create(bb->bucket_alloc); } else { /* put red into hold, no green sent out */ } } else if (APR_BUCKET_IS_FILE(bred)) { /* This is set aside into the target brigade pool so that * any read operation messes with that pool and not * the red one. */ apr_bucket_file *f = (apr_bucket_file *)bred->data; apr_file_t *fd = f->fd; int setaside = (f->readpool != bb->p); if (setaside) { status = apr_file_setaside(&fd, fd, bb->p); if (status != APR_SUCCESS) { goto leave; } ++beam->files_beamed; } ng = apr_brigade_insert_file(bb, fd, bred->start, bred->length, bb->p); #if APR_HAS_MMAP /* disable mmap handling as this leads to segfaults when * the underlying file is changed while memory pointer has * been handed out. See also PR 59348 */ apr_bucket_file_enable_mmap(ng, 0); #endif remain -= bred->length; ++transferred; APR_BUCKET_REMOVE(bred); H2_BLIST_INSERT_TAIL(&beam->hold, bred); ++transferred; continue; } else { /* create a "green" standin bucket. we took care about the * underlying red bucket and its data when we placed it into * the red brigade. * the beam bucket will notify us on destruction that bred is * no longer needed. */ bgreen = h2_beam_bucket_create(beam, bred, bb->bucket_alloc, beam->buckets_sent++); } /* Place the red bucket into our hold, to be destroyed when no * green bucket references it any more. */ APR_BUCKET_REMOVE(bred); H2_BLIST_INSERT_TAIL(&beam->hold, bred); beam->received_bytes += bred->length; if (bgreen) { APR_BRIGADE_INSERT_TAIL(bb, bgreen); remain -= bgreen->length; ++transferred; } } if (readbytes > 0 && remain < 0) { /* too much, put some back */ remain = readbytes; for (bgreen = APR_BRIGADE_FIRST(bb); bgreen != APR_BRIGADE_SENTINEL(bb); bgreen = APR_BUCKET_NEXT(bgreen)) { remain -= bgreen->length; if (remain < 0) { apr_bucket_split(bgreen, bgreen->length+remain); beam->green = apr_brigade_split_ex(bb, APR_BUCKET_NEXT(bgreen), beam->green); break; } } } if (beam->closed && (!beam->green || APR_BRIGADE_EMPTY(beam->green)) && H2_BLIST_EMPTY(&beam->red)) { /* beam is closed and we have nothing more to receive */ if (!beam->close_sent) { apr_bucket *b = apr_bucket_eos_create(bb->bucket_alloc); APR_BRIGADE_INSERT_TAIL(bb, b); beam->close_sent = 1; ++transferred; status = APR_SUCCESS; } } if (transferred) { status = APR_SUCCESS; } else if (beam->closed) { status = APR_EOF; } else if (block == APR_BLOCK_READ && bl.mutex && beam->m_cond) { status = wait_cond(beam, bl.mutex); if (status != APR_SUCCESS) { goto leave; } goto transfer; } else { status = APR_EAGAIN; } leave: leave_yellow(beam, &bl); } return status; }