/* Implements svn_read_fn_t */ static svn_error_t * sock_read_cb(void *baton, char *buffer, apr_size_t *len) { sock_baton_t *b = baton; apr_status_t status; apr_interval_time_t interval; status = apr_socket_timeout_get(b->sock, &interval); if (status) return svn_error_wrap_apr(status, _("Can't get socket timeout")); /* Always block on read. * During pipelining, we set the timeout to 0 for some write * operations so that we can try them without blocking. If APR had * separate timeouts for read and write, we would only set the * write timeout, but it doesn't. So here, we revert back to blocking. */ apr_socket_timeout_set(b->sock, -1); status = apr_socket_recv(b->sock, buffer, len); apr_socket_timeout_set(b->sock, interval); if (status && !APR_STATUS_IS_EOF(status)) return svn_error_wrap_apr(status, _("Can't read from connection")); return SVN_NO_ERROR; }
static apr_status_t send_brigade_blocking(apr_socket_t *s, apr_bucket_brigade *bb, apr_size_t *bytes_written, conn_rec *c) { apr_status_t rv; rv = APR_SUCCESS; while (!APR_BRIGADE_EMPTY(bb)) { rv = send_brigade_nonblocking(s, bb, bytes_written, c); if (rv != APR_SUCCESS) { if (APR_STATUS_IS_EAGAIN(rv)) { /* Wait until we can send more data */ apr_int32_t nsds; apr_interval_time_t timeout; apr_pollfd_t pollset; pollset.p = c->pool; pollset.desc_type = APR_POLL_SOCKET; pollset.reqevents = APR_POLLOUT; pollset.desc.s = s; apr_socket_timeout_get(s, &timeout); rv = apr_poll(&pollset, 1, &nsds, timeout); if (rv != APR_SUCCESS) { break; } } else { break; } } } return rv; }
static apr_status_t sendfile_nonblocking(apr_socket_t *s, apr_bucket *bucket, apr_size_t *cumulative_bytes_written, conn_rec *c) { apr_status_t rv = APR_SUCCESS; apr_bucket_file *file_bucket; apr_file_t *fd; apr_size_t file_length; apr_off_t file_offset; apr_size_t bytes_written = 0; if (!APR_BUCKET_IS_FILE(bucket)) { ap_log_error(APLOG_MARK, APLOG_ERR, rv, c->base_server, APLOGNO(00006) "core_filter: sendfile_nonblocking: " "this should never happen"); return APR_EGENERAL; } file_bucket = (apr_bucket_file *)(bucket->data); fd = file_bucket->fd; file_length = bucket->length; file_offset = bucket->start; if (bytes_written < file_length) { apr_size_t n = file_length - bytes_written; apr_status_t arv; apr_interval_time_t old_timeout; arv = apr_socket_timeout_get(s, &old_timeout); if (arv != APR_SUCCESS) { return arv; } arv = apr_socket_timeout_set(s, 0); if (arv != APR_SUCCESS) { return arv; } rv = apr_socket_sendfile(s, fd, NULL, &file_offset, &n, 0); if (rv == APR_SUCCESS) { bytes_written += n; file_offset += n; } arv = apr_socket_timeout_set(s, old_timeout); if ((arv != APR_SUCCESS) && (rv == APR_SUCCESS)) { rv = arv; } } if ((ap__logio_add_bytes_out != NULL) && (bytes_written > 0)) { ap__logio_add_bytes_out(c, bytes_written); } *cumulative_bytes_written += bytes_written; if ((bytes_written < file_length) && (bytes_written > 0)) { apr_bucket_split(bucket, bytes_written); apr_bucket_delete(bucket); } else if (bytes_written == file_length) { apr_bucket_delete(bucket); } return rv; }
static apr_status_t socket_bucket_read(apr_bucket *a, const char **str, apr_size_t *len, apr_read_type_e block) { apr_socket_t *p = a->data; char *buf; apr_status_t rv; apr_interval_time_t timeout; if (block == APR_NONBLOCK_READ) { apr_socket_timeout_get(p, &timeout); apr_socket_timeout_set(p, 0); } *str = NULL; *len = APR_BUCKET_BUFF_SIZE; buf = apr_bucket_alloc(*len, a->list); /* XXX: check for failure? */ rv = apr_socket_recv(p, buf, len); if (block == APR_NONBLOCK_READ) { apr_socket_timeout_set(p, timeout); } if (rv != APR_SUCCESS && rv != APR_EOF) { apr_bucket_free(buf); return rv; } /* * If there's more to read we have to keep the rest of the socket * for later. XXX: Note that more complicated bucket types that * refer to data not in memory and must therefore have a read() * function similar to this one should be wary of copying this * code because if they have a destroy function they probably * want to migrate the bucket's subordinate structure from the * old bucket to a raw new one and adjust it as appropriate, * rather than destroying the old one and creating a completely * new bucket. * * Even if there is nothing more to read, don't close the socket here * as we have to use it to send any response :) We could shut it * down for reading, but there is no benefit to doing so. */ if (*len > 0) { apr_bucket_heap *h; /* Change the current bucket to refer to what we read */ a = apr_bucket_heap_make(a, buf, *len, apr_bucket_free); h = a->data; h->alloc_len = APR_BUCKET_BUFF_SIZE; /* note the real buffer size */ *str = buf; APR_BUCKET_INSERT_AFTER(a, apr_bucket_socket_create(p, a->list)); } else { apr_bucket_free(buf); a = apr_bucket_immortal_make(a, "", 0); *str = a->data; } return APR_SUCCESS; }
static apr_status_t bucket_socket_ex_read(apr_bucket *a, const char **str, apr_size_t *len, apr_read_type_e block) { socket_ex_data *data = a->data; apr_socket_t *p = data->sock; char *buf; apr_status_t rv; apr_interval_time_t timeout; if (block == APR_NONBLOCK_READ) { apr_socket_timeout_get(p, &timeout); apr_socket_timeout_set(p, 0); } *str = NULL; *len = APR_BUCKET_BUFF_SIZE; buf = apr_bucket_alloc(*len, a->list); rv = apr_socket_recv(p, buf, len); if (block == APR_NONBLOCK_READ) { apr_socket_timeout_set(p, timeout); } if (rv != APR_SUCCESS && rv != APR_EOF) { apr_bucket_free(buf); return rv; } if (*len > 0) { apr_bucket_heap *h; /* count for stats */ *data->counter += *len; /* Change the current bucket to refer to what we read */ a = apr_bucket_heap_make(a, buf, *len, apr_bucket_free); h = a->data; h->alloc_len = APR_BUCKET_BUFF_SIZE; /* note the real buffer size */ *str = buf; APR_BUCKET_INSERT_AFTER(a, bucket_socket_ex_create(data, a->list)); } else { apr_bucket_free(buf); a = apr_bucket_immortal_make(a, "", 0); *str = a->data; } return APR_SUCCESS; }
static int socket_timeout_get(lua_State *L) { lua_apr_socket *object; apr_status_t status; apr_interval_time_t timeout; object = socket_check(L, 1, 1); status = apr_socket_timeout_get(object->handle, &timeout); if (status != APR_SUCCESS) return push_error_status(L, status); else if (timeout <= 0) lua_pushboolean(L, timeout != 0); else lua_pushinteger(L, (lua_Integer) timeout); return 1; }
static apr_status_t writev_nonblocking(apr_socket_t *s, struct iovec *vec, apr_size_t nvec, apr_bucket_brigade *bb, apr_size_t *cumulative_bytes_written, conn_rec *c) { apr_status_t rv = APR_SUCCESS, arv; apr_size_t bytes_written = 0, bytes_to_write = 0; apr_size_t i, offset; apr_interval_time_t old_timeout; arv = apr_socket_timeout_get(s, &old_timeout); if (arv != APR_SUCCESS) { return arv; } arv = apr_socket_timeout_set(s, 0); if (arv != APR_SUCCESS) { return arv; } for (i = 0; i < nvec; i++) { bytes_to_write += vec[i].iov_len; } offset = 0; while (bytes_written < bytes_to_write) { apr_size_t n = 0; rv = apr_socket_sendv(s, vec + offset, nvec - offset, &n); if (n > 0) { bytes_written += n; for (i = offset; i < nvec; ) { apr_bucket *bucket = APR_BRIGADE_FIRST(bb); if (APR_BUCKET_IS_METADATA(bucket)) { apr_bucket_delete(bucket); } else if (n >= vec[i].iov_len) { apr_bucket_delete(bucket); offset++; n -= vec[i++].iov_len; } else { apr_bucket_split(bucket, n); apr_bucket_delete(bucket); vec[i].iov_len -= n; vec[i].iov_base = (char *) vec[i].iov_base + n; break; } } } if (rv != APR_SUCCESS) { break; } } if ((ap__logio_add_bytes_out != NULL) && (bytes_written > 0)) { ap__logio_add_bytes_out(c, bytes_written); } *cumulative_bytes_written += bytes_written; arv = apr_socket_timeout_set(s, old_timeout); if ((arv != APR_SUCCESS) && (rv == APR_SUCCESS)) { return arv; } else { return rv; } }
static apr_status_t sendfile_it_all(core_net_rec *c, apr_file_t *fd, apr_hdtr_t *hdtr, apr_off_t file_offset, apr_size_t file_bytes_left, apr_size_t total_bytes_left, apr_size_t *bytes_sent, apr_int32_t flags) { apr_status_t rv; #ifdef AP_DEBUG apr_interval_time_t timeout = 0; #endif AP_DEBUG_ASSERT((apr_socket_timeout_get(c->client_socket, &timeout) == APR_SUCCESS) && timeout > 0); /* socket must be in timeout mode */ /* Reset the bytes_sent field */ *bytes_sent = 0; do { apr_size_t tmplen = file_bytes_left; rv = apr_socket_sendfile(c->client_socket, fd, hdtr, &file_offset, &tmplen, flags); *bytes_sent += tmplen; total_bytes_left -= tmplen; if (!total_bytes_left || rv != APR_SUCCESS) { return rv; /* normal case & error exit */ } AP_DEBUG_ASSERT(total_bytes_left > 0 && tmplen > 0); /* partial write, oooh noooo... * Skip over any header data which was written */ while (tmplen && hdtr->numheaders) { if (tmplen >= hdtr->headers[0].iov_len) { tmplen -= hdtr->headers[0].iov_len; --hdtr->numheaders; ++hdtr->headers; } else { char *iov_base = (char *)hdtr->headers[0].iov_base; hdtr->headers[0].iov_len -= tmplen; iov_base += tmplen; hdtr->headers[0].iov_base = iov_base; tmplen = 0; } } /* Skip over any file data which was written */ if (tmplen <= file_bytes_left) { file_offset += tmplen; file_bytes_left -= tmplen; continue; } tmplen -= file_bytes_left; file_bytes_left = 0; file_offset = 0; /* Skip over any trailer data which was written */ while (tmplen && hdtr->numtrailers) { if (tmplen >= hdtr->trailers[0].iov_len) { tmplen -= hdtr->trailers[0].iov_len; --hdtr->numtrailers; ++hdtr->trailers; } else { char *iov_base = (char *)hdtr->trailers[0].iov_base; hdtr->trailers[0].iov_len -= tmplen; iov_base += tmplen; hdtr->trailers[0].iov_base = iov_base; tmplen = 0; } } } while (1); }
apr_status_t h2_filter_core_input(ap_filter_t* f, apr_bucket_brigade* brigade, ap_input_mode_t mode, apr_read_type_e block, apr_off_t readbytes) { h2_filter_cin *cin = f->ctx; apr_status_t status = APR_SUCCESS; apr_interval_time_t saved_timeout = UNSET; ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, f->c, "core_input(%ld): read, %s, mode=%d, readbytes=%ld", (long)f->c->id, (block == APR_BLOCK_READ)? "BLOCK_READ" : "NONBLOCK_READ", mode, (long)readbytes); if (mode == AP_MODE_INIT || mode == AP_MODE_SPECULATIVE) { return ap_get_brigade(f->next, brigade, mode, block, readbytes); } if (mode != AP_MODE_READBYTES) { return (block == APR_BLOCK_READ)? APR_SUCCESS : APR_EAGAIN; } if (!cin->bb) { cin->bb = apr_brigade_create(cin->pool, f->c->bucket_alloc); } if (!cin->socket) { cin->socket = ap_get_conn_socket(f->c); } cin->start_read = apr_time_now(); if (APR_BRIGADE_EMPTY(cin->bb)) { /* We only do a blocking read when we have no streams to process. So, * in httpd scoreboard lingo, we are in a KEEPALIVE connection state. * When reading non-blocking, we do have streams to process and update * child with NULL request. That way, any current request information * in the scoreboard is preserved. */ if (block == APR_BLOCK_READ) { if (cin->timeout > 0) { apr_socket_timeout_get(cin->socket, &saved_timeout); apr_socket_timeout_set(cin->socket, cin->timeout); } } status = ap_get_brigade(f->next, cin->bb, AP_MODE_READBYTES, block, readbytes); if (saved_timeout != UNSET) { apr_socket_timeout_set(cin->socket, saved_timeout); } } switch (status) { case APR_SUCCESS: status = consume_brigade(cin, cin->bb, block); break; case APR_EOF: case APR_EAGAIN: case APR_TIMEUP: ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, f->c, "core_input(%ld): read", (long)f->c->id); break; default: ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, f->c, APLOGNO(03046) "h2_conn_io: error reading"); break; } return status; }
static apr_status_t dispatch(proxy_conn_rec *conn, proxy_dir_conf *conf, request_rec *r, apr_pool_t *setaside_pool, apr_uint16_t request_id, const char **err, int *bad_request, int *has_responded) { apr_bucket_brigade *ib, *ob; int seen_end_of_headers = 0, done = 0, ignore_body = 0; apr_status_t rv = APR_SUCCESS; int script_error_status = HTTP_OK; conn_rec *c = r->connection; struct iovec vec[2]; ap_fcgi_header header; unsigned char farray[AP_FCGI_HEADER_LEN]; apr_pollfd_t pfd; int header_state = HDR_STATE_READING_HEADERS; char stack_iobuf[AP_IOBUFSIZE]; apr_size_t iobuf_size = AP_IOBUFSIZE; char *iobuf = stack_iobuf; *err = NULL; if (conn->worker->s->io_buffer_size_set) { iobuf_size = conn->worker->s->io_buffer_size; iobuf = apr_palloc(r->pool, iobuf_size); } pfd.desc_type = APR_POLL_SOCKET; pfd.desc.s = conn->sock; pfd.p = r->pool; pfd.reqevents = APR_POLLIN | APR_POLLOUT; ib = apr_brigade_create(r->pool, c->bucket_alloc); ob = apr_brigade_create(r->pool, c->bucket_alloc); while (! done) { apr_interval_time_t timeout; apr_size_t len; int n; /* We need SOME kind of timeout here, or virtually anything will * cause timeout errors. */ apr_socket_timeout_get(conn->sock, &timeout); rv = apr_poll(&pfd, 1, &n, timeout); if (rv != APR_SUCCESS) { if (APR_STATUS_IS_EINTR(rv)) { continue; } *err = "polling"; break; } if (pfd.rtnevents & APR_POLLOUT) { apr_size_t to_send, writebuflen; int last_stdin = 0; char *iobuf_cursor; rv = ap_get_brigade(r->input_filters, ib, AP_MODE_READBYTES, APR_BLOCK_READ, iobuf_size); if (rv != APR_SUCCESS) { *err = "reading input brigade"; *bad_request = 1; break; } if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(ib))) { last_stdin = 1; } writebuflen = iobuf_size; rv = apr_brigade_flatten(ib, iobuf, &writebuflen); apr_brigade_cleanup(ib); if (rv != APR_SUCCESS) { *err = "flattening brigade"; break; } to_send = writebuflen; iobuf_cursor = iobuf; while (to_send > 0) { int nvec = 0; apr_size_t write_this_time; write_this_time = to_send < AP_FCGI_MAX_CONTENT_LEN ? to_send : AP_FCGI_MAX_CONTENT_LEN; ap_fcgi_fill_in_header(&header, AP_FCGI_STDIN, request_id, (apr_uint16_t)write_this_time, 0); ap_fcgi_header_to_array(&header, farray); vec[nvec].iov_base = (void *)farray; vec[nvec].iov_len = sizeof(farray); ++nvec; if (writebuflen) { vec[nvec].iov_base = iobuf_cursor; vec[nvec].iov_len = write_this_time; ++nvec; } rv = send_data(conn, vec, nvec, &len); if (rv != APR_SUCCESS) { *err = "sending stdin"; break; } to_send -= write_this_time; iobuf_cursor += write_this_time; } if (rv != APR_SUCCESS) { break; } if (last_stdin) { pfd.reqevents = APR_POLLIN; /* Done with input data */ /* signal EOF (empty FCGI_STDIN) */ ap_fcgi_fill_in_header(&header, AP_FCGI_STDIN, request_id, 0, 0); ap_fcgi_header_to_array(&header, farray); vec[0].iov_base = (void *)farray; vec[0].iov_len = sizeof(farray); rv = send_data(conn, vec, 1, &len); if (rv != APR_SUCCESS) { *err = "sending empty stdin"; break; } } } if (pfd.rtnevents & APR_POLLIN) { apr_size_t readbuflen; apr_uint16_t clen, rid; apr_bucket *b; unsigned char plen; unsigned char type, version; /* First, we grab the header... */ rv = get_data_full(conn, (char *) farray, AP_FCGI_HEADER_LEN); if (rv != APR_SUCCESS) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01067) "Failed to read FastCGI header"); break; } ap_log_rdata(APLOG_MARK, APLOG_TRACE8, r, "FastCGI header", farray, AP_FCGI_HEADER_LEN, 0); ap_fcgi_header_fields_from_array(&version, &type, &rid, &clen, &plen, farray); if (version != AP_FCGI_VERSION_1) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01068) "Got bogus version %d", (int)version); rv = APR_EINVAL; break; } if (rid != request_id) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01069) "Got bogus rid %d, expected %d", rid, request_id); rv = APR_EINVAL; break; } recv_again: if (clen > iobuf_size) { readbuflen = iobuf_size; } else { readbuflen = clen; } /* Now get the actual data. Yes it sucks to do this in a second * recv call, this will eventually change when we move to real * nonblocking recv calls. */ if (readbuflen != 0) { rv = get_data(conn, iobuf, &readbuflen); if (rv != APR_SUCCESS) { *err = "reading response body"; break; } } switch (type) { case AP_FCGI_STDOUT: if (clen != 0) { b = apr_bucket_transient_create(iobuf, readbuflen, c->bucket_alloc); APR_BRIGADE_INSERT_TAIL(ob, b); if (! seen_end_of_headers) { int st = handle_headers(r, &header_state, iobuf, readbuflen); if (st == 1) { int status; seen_end_of_headers = 1; status = ap_scan_script_header_err_brigade_ex(r, ob, NULL, APLOG_MODULE_INDEX); /* suck in all the rest */ if (status != OK) { apr_bucket *tmp_b; apr_brigade_cleanup(ob); tmp_b = apr_bucket_eos_create(c->bucket_alloc); APR_BRIGADE_INSERT_TAIL(ob, tmp_b); *has_responded = 1; r->status = status; rv = ap_pass_brigade(r->output_filters, ob); if (rv != APR_SUCCESS) { *err = "passing headers brigade to output filters"; } else if (status == HTTP_NOT_MODIFIED) { /* The 304 response MUST NOT contain * a message-body, ignore it. */ ignore_body = 1; } else { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01070) "Error parsing script headers"); rv = APR_EINVAL; } break; } if (conf->error_override && ap_is_HTTP_ERROR(r->status)) { /* * set script_error_status to discard * everything after the headers */ script_error_status = r->status; /* * prevent ap_die() from treating this as a * recursive error, initially: */ r->status = HTTP_OK; } if (script_error_status == HTTP_OK && !APR_BRIGADE_EMPTY(ob) && !ignore_body) { /* Send the part of the body that we read while * reading the headers. */ *has_responded = 1; rv = ap_pass_brigade(r->output_filters, ob); if (rv != APR_SUCCESS) { *err = "passing brigade to output filters"; break; } } apr_brigade_cleanup(ob); apr_pool_clear(setaside_pool); } else { /* We're still looking for the end of the * headers, so this part of the data will need * to persist. */ apr_bucket_setaside(b, setaside_pool); } } else { /* we've already passed along the headers, so now pass * through the content. we could simply continue to * setaside the content and not pass until we see the * 0 content-length (below, where we append the EOS), * but that could be a huge amount of data; so we pass * along smaller chunks */ if (script_error_status == HTTP_OK && !ignore_body) { *has_responded = 1; rv = ap_pass_brigade(r->output_filters, ob); if (rv != APR_SUCCESS) { *err = "passing brigade to output filters"; break; } } apr_brigade_cleanup(ob); } /* If we didn't read all the data, go back and get the * rest of it. */ if (clen > readbuflen) { clen -= readbuflen; goto recv_again; } } else { /* XXX what if we haven't seen end of the headers yet? */ if (script_error_status == HTTP_OK) { b = apr_bucket_eos_create(c->bucket_alloc); APR_BRIGADE_INSERT_TAIL(ob, b); *has_responded = 1; rv = ap_pass_brigade(r->output_filters, ob); if (rv != APR_SUCCESS) { *err = "passing brigade to output filters"; break; } } /* XXX Why don't we cleanup here? (logic from AJP) */ } break; case AP_FCGI_STDERR: /* TODO: Should probably clean up this logging a bit... */ if (clen) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01071) "Got error '%.*s'", (int)readbuflen, iobuf); } if (clen > readbuflen) { clen -= readbuflen; goto recv_again; } break; case AP_FCGI_END_REQUEST: done = 1; break; default: ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01072) "Got bogus record %d", type); break; } /* Leave on above switch's inner error. */ if (rv != APR_SUCCESS) { break; } if (plen) { rv = get_data_full(conn, iobuf, plen); if (rv != APR_SUCCESS) { ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(02537) "Error occurred reading padding"); break; } } } } apr_brigade_destroy(ib); apr_brigade_destroy(ob); if (script_error_status != HTTP_OK) { ap_die(script_error_status, r); /* send ErrorDocument */ *has_responded = 1; } return rv; }