/* Handle stdout from CGI child. Duplicate of logic from the _read * method of the real APR pipe bucket implementation. */ static apr_status_t aikido_read_stdout(apr_bucket *a, apr_file_t *out, const char **str, apr_size_t *len) { char *buf; apr_status_t rv; *str = NULL; *len = APR_BUCKET_BUFF_SIZE; buf = apr_bucket_alloc(*len, a->list); /* XXX: check for failure? */ rv = apr_file_read(out, buf, len); if (rv != APR_SUCCESS && rv != APR_EOF) { apr_bucket_free(buf); return rv; } if (*len > 0) { struct aikido_bucket_data *data = a->data; 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, aikido_bucket_dup(data, a->list)); } else { apr_bucket_free(buf); a = apr_bucket_immortal_make(a, "", 0); *str = a->data; } return rv; }
static apr_status_t pipe_bucket_read(apr_bucket *a, const char **str, apr_size_t *len, apr_read_type_e block) { apr_file_t *p = a->data; char *buf; apr_status_t rv; apr_interval_time_t timeout; if (block == APR_NONBLOCK_READ) { apr_file_pipe_timeout_get(p, &timeout); apr_file_pipe_timeout_set(p, 0); } *str = NULL; *len = APR_BUCKET_BUFF_SIZE; buf = apr_bucket_alloc(*len, a->list); /* XXX: check for failure? */ rv = apr_file_read(p, buf, len); if (block == APR_NONBLOCK_READ) { apr_file_pipe_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 pipe * for later. Otherwise, we'll close the pipe. * 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. */ 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_pipe_create(p, a->list)); } else { apr_bucket_free(buf); a = apr_bucket_immortal_make(a, "", 0); *str = a->data; if (rv == APR_EOF) { apr_file_close(p); } } return APR_SUCCESS; }
static void nginx_bucket_destroy(void *data) { apr_bucket_nginx *n = data; ngx_buf_t *buf = n->buf; if (apr_bucket_shared_destroy(n)) { if (!ngx_buf_in_memory(buf) && buf->pos != NULL) { apr_bucket_free(buf->pos); buf->pos = NULL; } apr_bucket_free(n); } }
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 void pool_bucket_destroy(void *data) { apr_bucket_pool *p = data; /* If the pool is cleaned up before the last reference goes * away, the data is really now on the heap; heap_destroy() takes * over. free() in heap_destroy() thinks it's freeing * an apr_bucket_heap, when in reality it's freeing the whole * apr_bucket_pool for us. */ if (p->pool) { /* the shared resource is still in the pool * because the pool has not been cleaned up yet */ if (apr_bucket_shared_destroy(p)) { apr_pool_cleanup_kill(p->pool, p, pool_bucket_cleanup); apr_bucket_free(p); } } else { /* the shared resource is no longer in the pool, it's * on the heap, but this reference still thinks it's a pool * bucket. we should just go ahead and pass control to * heap_destroy() for it since it doesn't know any better. */ apr_bucket_type_heap.destroy(p); } }
/* Warning: if you change this function, be sure to * change apr_bucket_pool_make() too! */ APU_DECLARE(apr_bucket *) apr_bucket_heap_make(apr_bucket *b, const char *buf, apr_size_t length, void (*free_func)(void *data)) { apr_bucket_heap *h; h = apr_bucket_alloc(sizeof(*h), b->list); if (!free_func) { h->alloc_len = length; h->base = apr_bucket_alloc(h->alloc_len, b->list); if (h->base == NULL) { apr_bucket_free(h); return NULL; } h->free_func = apr_bucket_free; memcpy(h->base, buf, length); } else { /* XXX: we lose the const qualifier here which indicates * there's something screwy with the API... */ h->base = (char *) buf; h->alloc_len = length; h->free_func = free_func; } b = apr_bucket_shared_make(b, h, 0, length); b->type = &apr_bucket_type_heap; return b; }
static int brigade_flatten(lua_State*L) { apr_bucket_brigade *bb = (apr_bucket_brigade*)CHECK_BUCKETBRIGADE_OBJECT(1); apr_off_t off = 0; apr_status_t rc = apr_brigade_length(bb, 1, &off); apr_size_t len = (apr_size_t)off; if(rc==APR_SUCCESS) { char* buf = apr_bucket_alloc(len, bb->bucket_alloc); rc = apr_brigade_flatten(bb, buf, &len); if(rc==APR_SUCCESS) { lua_pushlstring(L,buf, len); }else { lua_pushnil(L); } apr_bucket_free(buf); }else { lua_pushnil(L); } lua_pushinteger(L,rc); return 2; }
static apr_status_t nginx_bucket_read(apr_bucket *b, const char **str, apr_size_t *len, apr_read_type_e block) { apr_bucket_nginx *n = b->data; ngx_buf_t *buf = n->buf; u_char *data; ssize_t size; if (buf->pos == NULL && ngx_buf_size(buf) != 0) { data = apr_bucket_alloc(ngx_buf_size(buf), b->list); if (data == NULL) { return APR_EGENERAL; } size = ngx_read_file(buf->file, data, ngx_buf_size(buf), buf->file_pos); if (size != ngx_buf_size(buf)) { apr_bucket_free(data); return APR_EGENERAL; } buf->pos = data; } *str = (char *)buf->pos + b->start; *len = b->length; return APR_SUCCESS; }
static void error_bucket_destroy(void *data) { ap_bucket_error *h = data; if (apr_bucket_shared_destroy(h)) { apr_bucket_free(h); } }
static void heap_bucket_destroy(void *data) { apr_bucket_heap *h = data; if (apr_bucket_shared_destroy(h)) { (*h->free_func)(h->base); apr_bucket_free(h); } }
static void lob_bucket_destroy(void *data) { apr_bucket_lob *f = data; if (apr_bucket_shared_destroy(f)) { /* no need to destroy database objects here; it will get * done automatically when the pool gets cleaned up */ apr_bucket_free(f); } }
static void file_bucket_destroy(void *data) { apr_bucket_file *f = data; if (apr_bucket_shared_destroy(f)) { /* no need to close the file here; it will get * done automatically when the pool gets cleaned up */ apr_bucket_free(f); } }
static void bucket_destroy(void *data) { h2_bucket_eos *h = data; if (apr_bucket_shared_destroy(h)) { h2_stream *stream = h->stream; if (stream) { h2_stream_eos_destroy(stream); } apr_bucket_free(h); } }
static void bucket_destroy(void *data) { h2_bucket_eos *h = data; if (apr_bucket_shared_destroy(h)) { h2_stream *stream = h->stream; if (stream && stream->pool) { apr_pool_cleanup_kill(stream->pool, &h->stream, bucket_cleanup); } apr_bucket_free(h); if (stream) { h2_stream_dispatch(stream, H2_SEV_EOS_SENT); } } }
static void beam_bucket_destroy(void *data) { h2_beam_proxy *d = data; if (apr_bucket_shared_destroy(d)) { /* When the beam gets destroyed before this bucket, it will * NULLify its reference here. This is not protected by a mutex, * so it will not help with race conditions. * But it lets us shut down memory pool with circulare beam * references. */ if (d->beam) { h2_beam_emitted(d->beam, d); } apr_bucket_free(d); } }
static apr_status_t bucket_read(apr_bucket *bucket, const char **str, apr_size_t *len, apr_read_type_e block) { char *buf; ssize_t ret; BucketData *data; data = (BucketData *) bucket->data; *str = NULL; *len = 0; if (!data->bufferResponse && block == APR_NONBLOCK_READ) { /* * The bucket brigade that Hooks::handleRequest() passes using * ap_pass_brigade() is always passed through ap_content_length_filter, * which is a filter which attempts to read all data from the * bucket brigade and computes the Content-Length header from * that. We don't want this to happen; because suppose that the * Rails application sends back 1 GB of data, then * ap_content_length_filter will buffer this entire 1 GB of data * in memory before passing it to the HTTP client. * * ap_content_length_filter aborts and passes the bucket brigade * down the filter chain when it encounters an APR_EAGAIN, except * for the first read. So by returning APR_EAGAIN on every * non-blocking read request, we can prevent ap_content_length_filter * from buffering all data. */ //return APR_EAGAIN; } buf = (char *) apr_bucket_alloc(APR_BUCKET_BUFF_SIZE, bucket->list); if (buf == NULL) { return APR_ENOMEM; } do { ret = read(data->state->connection, buf, APR_BUCKET_BUFF_SIZE); } while (ret == -1 && errno == EINTR); if (ret > 0) { apr_bucket_heap *h; data->state->bytesRead += ret; *str = buf; *len = ret; bucket->data = NULL; /* Change the current bucket (which is a Passenger Bucket) into a heap bucket * that contains the data that we just read. This newly created heap bucket * will be the first in the bucket list. */ bucket = apr_bucket_heap_make(bucket, buf, *len, apr_bucket_free); h = (apr_bucket_heap *) bucket->data; h->alloc_len = APR_BUCKET_BUFF_SIZE; /* note the real buffer size */ /* And after this newly created bucket we insert a new Passenger Bucket * which can read the next chunk from the stream. */ APR_BUCKET_INSERT_AFTER(bucket, passenger_bucket_create( data->state, bucket->list, data->bufferResponse)); /* The newly created Passenger Bucket has a reference to the session * object, so we can delete data here. */ delete data; return APR_SUCCESS; } else if (ret == 0) { data->state->completed = true; delete data; bucket->data = NULL; apr_bucket_free(buf); bucket = apr_bucket_immortal_make(bucket, "", 0); *str = (const char *) bucket->data; *len = 0; return APR_SUCCESS; } else /* ret == -1 */ { int e = errno; data->state->completed = true; data->state->errorCode = e; delete data; bucket->data = NULL; apr_bucket_free(buf); return APR_FROM_OS_ERROR(e); } }
static apr_status_t file_bucket_read(apr_bucket *e, const char **str, apr_size_t *len, apr_read_type_e block) { apr_bucket_file *a = e->data; apr_file_t *f = a->fd; apr_bucket *b = NULL; char *buf; apr_status_t rv; apr_size_t filelength = e->length; /* bytes remaining in file past offset */ apr_off_t fileoffset = e->start; #if APR_HAS_THREADS && !APR_HAS_XTHREAD_FILES apr_int32_t flags; #endif #if APR_HAS_MMAP if (file_make_mmap(e, filelength, fileoffset, a->readpool)) { return apr_bucket_read(e, str, len, block); } #endif #if APR_HAS_THREADS && !APR_HAS_XTHREAD_FILES if ((flags = apr_file_flags_get(f)) & APR_FOPEN_XTHREAD) { /* this file descriptor is shared across multiple threads and * this OS doesn't support that natively, so as a workaround * we must reopen the file into a->readpool */ const char *fname; apr_file_name_get(&fname, f); rv = apr_file_open(&f, fname, (flags & ~APR_FOPEN_XTHREAD), 0, a->readpool); if (rv != APR_SUCCESS) return rv; a->fd = f; } #endif *len = (filelength > APR_BUCKET_BUFF_SIZE) ? APR_BUCKET_BUFF_SIZE : filelength; *str = NULL; /* in case we die prematurely */ buf = apr_bucket_alloc(*len, e->list); /* Handle offset ... */ rv = apr_file_seek(f, APR_SET, &fileoffset); if (rv != APR_SUCCESS) { apr_bucket_free(buf); return rv; } rv = apr_file_read(f, buf, len); if (rv != APR_SUCCESS && rv != APR_EOF) { apr_bucket_free(buf); return rv; } filelength -= *len; /* * Change the current bucket to refer to what we read, * even if we read nothing because we hit EOF. */ apr_bucket_heap_make(e, buf, *len, apr_bucket_free); /* If we have more to read from the file, then create another bucket */ if (filelength > 0 && rv != APR_EOF) { /* for efficiency, we can just build a new apr_bucket struct * to wrap around the existing file bucket */ b = apr_bucket_alloc(sizeof(*b), e->list); b->start = fileoffset + (*len); b->length = filelength; b->data = a; b->type = &apr_bucket_type_file; b->free = apr_bucket_free; b->list = e->list; APR_BUCKET_INSERT_AFTER(e, b); } else { file_bucket_destroy(a); } *str = buf; return rv; }
static int zevent_process_connection(conn_state_t *cs) { /* * code for your app,this just an example for echo test. */ apr_bucket *b; char *msg; apr_size_t len=0; int olen = 0; const char *buf; apr_status_t rv; cs->pfd->reqevents = APR_POLLIN; if(cs->pfd->rtnevents & APR_POLLIN){ len = 4096; msg = (char *)apr_bucket_alloc(len,cs->baout); if (msg == NULL) { return -1; } rv = apr_socket_recv(cs->pfd->desc.s,msg,&len); if(rv != APR_SUCCESS) { zevent_log_error(APLOG_MARK,NULL,"close socket!"); return -1; } zevent_log_error(APLOG_MARK,NULL,"recv:%s",msg); b = apr_bucket_heap_create(msg,len,NULL,cs->baout); apr_bucket_free(msg); APR_BRIGADE_INSERT_TAIL(cs->bbout,b); cs->pfd->reqevents |= APR_POLLOUT; } else { if(cs->bbout){ for (b = APR_BRIGADE_FIRST(cs->bbout); b != APR_BRIGADE_SENTINEL(cs->bbout); b = APR_BUCKET_NEXT(b)) { apr_bucket_read(b,&buf,&len,APR_BLOCK_READ); olen = len; //apr_brigade_flatten(cs->bbout,buf,&len); rv = apr_socket_send(cs->pfd->desc.s,buf,&len); if((rv == APR_SUCCESS) && (len>=olen)) { // zevent_log_error(APLOG_MARK,NULL,"send:%d bytes\n", // len); apr_bucket_delete(b); } if((rv == APR_SUCCESS && len < olen) || (rv != APR_SUCCESS)) { if(rv == APR_SUCCESS){ apr_bucket_split(b,len); apr_bucket *bucket = APR_BUCKET_NEXT(b); apr_bucket_delete(b); b = bucket; } break; } } if(b != APR_BRIGADE_SENTINEL(cs->bbout)) cs->pfd->reqevents |= APR_POLLOUT; } } apr_pollset_add(cs->pollset,cs->pfd); return 0; }
static unsigned int __stdcall winnt_accept(void *lr_) { ap_listen_rec *lr = (ap_listen_rec *)lr_; apr_os_sock_info_t sockinfo; winnt_conn_ctx_t *context = NULL; DWORD BytesRead; SOCKET nlsd; core_server_config *core_sconf; const char *accf_name; int rv; int accf; int err_count = 0; HANDLE events[3]; #if APR_HAVE_IPV6 SOCKADDR_STORAGE ss_listen; int namelen = sizeof(ss_listen); #endif u_long zero = 0; core_sconf = ap_get_core_module_config(ap_server_conf->module_config); accf_name = apr_table_get(core_sconf->accf_map, lr->protocol); if (strcmp(accf_name, "data") == 0) accf = 2; else if (strcmp(accf_name, "connect") == 0) accf = 1; else if (strcmp(accf_name, "none") == 0) accf = 0; else { accf = 0; accf_name = "none"; ap_log_error(APLOG_MARK, APLOG_WARNING, 0, ap_server_conf, APLOGNO(00331) "winnt_accept: unrecognized AcceptFilter '%s', " "only 'data', 'connect' or 'none' are valid. " "Using 'none' instead", accf_name); } apr_os_sock_get(&nlsd, lr->sd); #if APR_HAVE_IPV6 if (getsockname(nlsd, (struct sockaddr *)&ss_listen, &namelen) == SOCKET_ERROR) { ap_log_error(APLOG_MARK, APLOG_ERR, apr_get_netos_error(), ap_server_conf, APLOGNO(00332) "winnt_accept: getsockname error on listening socket, " "is IPv6 available?"); return 1; } #endif if (accf > 0) /* 'data' or 'connect' */ { /* first, high priority event is an already accepted connection */ events[1] = exit_event; events[2] = max_requests_per_child_event; } else /* accf == 0, 'none' */ { reinit: /* target of data or connect upon too many AcceptEx failures */ /* last, low priority event is a not yet accepted connection */ events[0] = exit_event; events[1] = max_requests_per_child_event; events[2] = CreateEvent(NULL, FALSE, FALSE, NULL); /* The event needs to be removed from the accepted socket, * if not removed from the listen socket prior to accept(), */ rv = WSAEventSelect(nlsd, events[2], FD_ACCEPT); if (rv) { ap_log_error(APLOG_MARK, APLOG_ERR, apr_get_netos_error(), ap_server_conf, APLOGNO(00333) "WSAEventSelect() failed."); CloseHandle(events[2]); return 1; } } ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, APLOGNO(00334) "Child: Accept thread listening on %pI using AcceptFilter %s", lr->bind_addr, accf_name); while (!shutdown_in_progress) { if (!context) { int timeout; context = mpm_get_completion_context(&timeout); if (!context) { if (!timeout) { /* Hopefully a temporary condition in the provider? */ ++err_count; if (err_count > MAX_ACCEPTEX_ERR_COUNT) { ap_log_error(APLOG_MARK, APLOG_CRIT, 0, ap_server_conf, APLOGNO(00335) "winnt_accept: Too many failures grabbing a " "connection ctx. Aborting."); break; } } Sleep(100); continue; } } if (accf > 0) /* Either 'connect' or 'data' */ { DWORD len; char *buf; /* Create and initialize the accept socket */ #if APR_HAVE_IPV6 if (context->accept_socket == INVALID_SOCKET) { context->accept_socket = socket(ss_listen.ss_family, SOCK_STREAM, IPPROTO_TCP); context->socket_family = ss_listen.ss_family; } else if (context->socket_family != ss_listen.ss_family) { closesocket(context->accept_socket); context->accept_socket = socket(ss_listen.ss_family, SOCK_STREAM, IPPROTO_TCP); context->socket_family = ss_listen.ss_family; } #else if (context->accept_socket == INVALID_SOCKET) context->accept_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); #endif if (context->accept_socket == INVALID_SOCKET) { ap_log_error(APLOG_MARK, APLOG_WARNING, apr_get_netos_error(), ap_server_conf, APLOGNO(00336) "winnt_accept: Failed to allocate an accept socket. " "Temporary resource constraint? Try again."); Sleep(100); continue; } if (accf == 2) { /* 'data' */ len = APR_BUCKET_BUFF_SIZE; buf = apr_bucket_alloc(len, context->ba); len -= PADDED_ADDR_SIZE * 2; } else /* (accf == 1) 'connect' */ { len = 0; buf = context->buff; } /* AcceptEx on the completion context. The completion context will be * signaled when a connection is accepted. */ if (!AcceptEx(nlsd, context->accept_socket, buf, len, PADDED_ADDR_SIZE, PADDED_ADDR_SIZE, &BytesRead, &context->overlapped)) { rv = apr_get_netos_error(); if ((rv == APR_FROM_OS_ERROR(WSAECONNRESET)) || (rv == APR_FROM_OS_ERROR(WSAEACCES))) { /* We can get here when: * 1) the client disconnects early * 2) handshake was incomplete */ if (accf == 2) apr_bucket_free(buf); closesocket(context->accept_socket); context->accept_socket = INVALID_SOCKET; continue; } else if ((rv == APR_FROM_OS_ERROR(WSAEINVAL)) || (rv == APR_FROM_OS_ERROR(WSAENOTSOCK))) { /* We can get here when: * 1) TransmitFile does not properly recycle the accept socket (typically * because the client disconnected) * 2) there is VPN or Firewall software installed with * buggy WSAAccept or WSADuplicateSocket implementation * 3) the dynamic address / adapter has changed * Give five chances, then fall back on AcceptFilter 'none' */ if (accf == 2) apr_bucket_free(buf); closesocket(context->accept_socket); context->accept_socket = INVALID_SOCKET; ++err_count; if (err_count > MAX_ACCEPTEX_ERR_COUNT) { ap_log_error(APLOG_MARK, APLOG_ERR, rv, ap_server_conf, APLOGNO(00337) "Child: Encountered too many AcceptEx " "faults accepting client connections. " "Possible causes: dynamic address renewal, " "or incompatible VPN or firewall software. "); ap_log_error(APLOG_MARK, APLOG_NOTICE, rv, ap_server_conf, APLOGNO(00338) "winnt_mpm: falling back to " "'AcceptFilter none'."); err_count = 0; accf = 0; } continue; } else if ((rv != APR_FROM_OS_ERROR(ERROR_IO_PENDING)) && (rv != APR_FROM_OS_ERROR(WSA_IO_PENDING))) { if (accf == 2) apr_bucket_free(buf); closesocket(context->accept_socket); context->accept_socket = INVALID_SOCKET; ++err_count; if (err_count > MAX_ACCEPTEX_ERR_COUNT) { ap_log_error(APLOG_MARK, APLOG_ERR, rv, ap_server_conf, APLOGNO(00339) "Child: Encountered too many AcceptEx " "faults accepting client connections."); ap_log_error(APLOG_MARK, APLOG_NOTICE, rv, ap_server_conf, APLOGNO(00340) "winnt_mpm: falling back to " "'AcceptFilter none'."); err_count = 0; accf = 0; goto reinit; } continue; } err_count = 0; events[0] = context->overlapped.hEvent; do { rv = WaitForMultipleObjectsEx(3, events, FALSE, INFINITE, TRUE); } while (rv == WAIT_IO_COMPLETION); if (rv == WAIT_OBJECT_0) { if ((context->accept_socket != INVALID_SOCKET) && !GetOverlappedResult((HANDLE)context->accept_socket, &context->overlapped, &BytesRead, FALSE)) { ap_log_error(APLOG_MARK, APLOG_WARNING, apr_get_os_error(), ap_server_conf, APLOGNO(00341) "winnt_accept: Asynchronous AcceptEx failed."); closesocket(context->accept_socket); context->accept_socket = INVALID_SOCKET; } } else { /* exit_event triggered or event handle was closed */ closesocket(context->accept_socket); context->accept_socket = INVALID_SOCKET; if (accf == 2) apr_bucket_free(buf); break; } if (context->accept_socket == INVALID_SOCKET) { if (accf == 2) apr_bucket_free(buf); continue; } } err_count = 0; /* Potential optimization; consider handing off to the worker */ /* Inherit the listen socket settings. Required for * shutdown() to work */ if (setsockopt(context->accept_socket, SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT, (char *)&nlsd, sizeof(nlsd))) { ap_log_error(APLOG_MARK, APLOG_WARNING, apr_get_netos_error(), ap_server_conf, APLOGNO(00342) "setsockopt(SO_UPDATE_ACCEPT_CONTEXT) failed."); /* Not a failure condition. Keep running. */ } /* Get the local & remote address * TODO; error check */ GetAcceptExSockaddrs(buf, len, PADDED_ADDR_SIZE, PADDED_ADDR_SIZE, &context->sa_server, &context->sa_server_len, &context->sa_client, &context->sa_client_len); /* For 'data', craft a bucket for our data result * and pass to worker_main as context->overlapped.Pointer */ if (accf == 2 && BytesRead) { apr_bucket *b; b = apr_bucket_heap_create(buf, APR_BUCKET_BUFF_SIZE, apr_bucket_free, context->ba); /* Adjust the bucket to refer to the actual bytes read */ b->length = BytesRead; context->overlapped.Pointer = b; } else context->overlapped.Pointer = NULL; } else /* (accf = 0) e.g. 'none' */ { /* There is no socket reuse without AcceptEx() */ if (context->accept_socket != INVALID_SOCKET) closesocket(context->accept_socket); /* This could be a persistent event per-listener rather than * per-accept. However, the event needs to be removed from * the target socket if not removed from the listen socket * prior to accept(), or the event select is inherited. * and must be removed from the accepted socket. */ do { rv = WaitForMultipleObjectsEx(3, events, FALSE, INFINITE, TRUE); } while (rv == WAIT_IO_COMPLETION); if (rv != WAIT_OBJECT_0 + 2) { /* not FD_ACCEPT; * exit_event triggered or event handle was closed */ break; } context->sa_server = (void *) context->buff; context->sa_server_len = sizeof(context->buff) / 2; context->sa_client_len = context->sa_server_len; context->sa_client = (void *) (context->buff + context->sa_server_len); context->accept_socket = accept(nlsd, context->sa_server, &context->sa_server_len); if (context->accept_socket == INVALID_SOCKET) { rv = apr_get_netos_error(); if ( rv == APR_FROM_OS_ERROR(WSAECONNRESET) || rv == APR_FROM_OS_ERROR(WSAEINPROGRESS) || rv == APR_FROM_OS_ERROR(WSAEWOULDBLOCK) ) { ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, ap_server_conf, APLOGNO(00343) "accept() failed, retrying."); continue; } /* A more serious error than 'retry', log it */ ap_log_error(APLOG_MARK, APLOG_WARNING, rv, ap_server_conf, APLOGNO(00344) "accept() failed."); if ( rv == APR_FROM_OS_ERROR(WSAEMFILE) || rv == APR_FROM_OS_ERROR(WSAENOBUFS) ) { /* Hopefully a temporary condition in the provider? */ Sleep(100); ++err_count; if (err_count > MAX_ACCEPTEX_ERR_COUNT) { ap_log_error(APLOG_MARK, APLOG_ERR, rv, ap_server_conf, APLOGNO(00345) "Child: Encountered too many accept() " "resource faults, aborting."); break; } continue; } break; } /* Per MSDN, cancel the inherited association of this socket * to the WSAEventSelect API, and restore the state corresponding * to apr_os_sock_make's default assumptions (really, a flaw within * os_sock_make and os_sock_put that it does not query). */ WSAEventSelect(context->accept_socket, 0, 0); context->overlapped.Pointer = NULL; err_count = 0; context->sa_server_len = sizeof(context->buff) / 2; if (getsockname(context->accept_socket, context->sa_server, &context->sa_server_len) == SOCKET_ERROR) { ap_log_error(APLOG_MARK, APLOG_WARNING, apr_get_netos_error(), ap_server_conf, APLOGNO(00346) "getsockname failed"); continue; } if ((getpeername(context->accept_socket, context->sa_client, &context->sa_client_len)) == SOCKET_ERROR) { ap_log_error(APLOG_MARK, APLOG_WARNING, apr_get_netos_error(), ap_server_conf, APLOGNO(00347) "getpeername failed"); memset(&context->sa_client, '\0', sizeof(context->sa_client)); } } sockinfo.os_sock = &context->accept_socket; sockinfo.local = context->sa_server; sockinfo.remote = context->sa_client; sockinfo.family = context->sa_server->sa_family; sockinfo.type = SOCK_STREAM; sockinfo.protocol = IPPROTO_TCP; /* Restore the state corresponding to apr_os_sock_make's default * assumption of timeout -1 (really, a flaw of os_sock_make and * os_sock_put that it does not query to determine ->timeout). * XXX: Upon a fix to APR, these three statements should disappear. */ ioctlsocket(context->accept_socket, FIONBIO, &zero); setsockopt(context->accept_socket, SOL_SOCKET, SO_RCVTIMEO, (char *) &zero, sizeof(zero)); setsockopt(context->accept_socket, SOL_SOCKET, SO_SNDTIMEO, (char *) &zero, sizeof(zero)); apr_os_sock_make(&context->sock, &sockinfo, context->ptrans); /* When a connection is received, send an io completion notification * to the ThreadDispatchIOCP. */ PostQueuedCompletionStatus(ThreadDispatchIOCP, BytesRead, IOCP_CONNECTION_ACCEPTED, &context->overlapped); context = NULL; } if (!accf) CloseHandle(events[2]); if (!shutdown_in_progress) { /* Yow, hit an irrecoverable error! Tell the child to die. */ SetEvent(exit_event); } ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, ap_server_conf, APLOGNO(00348) "Child: Accept thread exiting."); return 0; }
//本函数只处理一次buf读取操作,loop在caller中执行 static apr_status_t socket_bucket_read(apr_bucket *a, const char **str, apr_size_t *len, apr_read_type_e block) { //把数据从a->data中读入到a->list中的node空间中 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. * 如果read后还有剩余,则需要保留socket剩余部分下次再读。 * 类似这样的需要一个read()操作的非内存bucket,请谨慎copy当前代码, * 原因见上面解释 * * * 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. * read完成后不需要close/shut down socket * * */ if (*len > 0) { apr_bucket_heap *h; /* Change the current bucket to refer to what we read */ //把当前读取的那部分数据组织成一个heap bucket,并返回起始地址 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; //剩下的p作为一个新的socket bucket 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; }
/* * worker_main() * Main entry point for the worker threads. Worker threads block in * win*_get_connection() awaiting a connection to service. */ static DWORD __stdcall worker_main(void *thread_num_val) { apr_thread_t *thd; apr_os_thread_t osthd; static int requests_this_child = 0; winnt_conn_ctx_t *context = NULL; int thread_num = (int)thread_num_val; ap_sb_handle_t *sbh; apr_bucket *e; int rc; conn_rec *c; apr_int32_t disconnected; osthd = apr_os_thread_current(); while (1) { ap_update_child_status_from_indexes(0, thread_num, SERVER_READY, NULL); /* Grab a connection off the network */ context = winnt_get_connection(context); if (!context) { /* Time for the thread to exit */ break; } /* Have we hit MaxConnectionsPerChild connections? */ if (ap_max_requests_per_child) { requests_this_child++; if (requests_this_child > ap_max_requests_per_child) { SetEvent(max_requests_per_child_event); } } e = context->overlapped.Pointer; ap_create_sb_handle(&sbh, context->ptrans, 0, thread_num); c = ap_run_create_connection(context->ptrans, ap_server_conf, context->sock, thread_num, sbh, context->ba); if (!c) { /* ap_run_create_connection closes the socket on failure */ context->accept_socket = INVALID_SOCKET; if (e) apr_bucket_free(e); continue; } thd = NULL; apr_os_thread_put(&thd, &osthd, context->ptrans); c->current_thread = thd; /* follow ap_process_connection(c, context->sock) logic * as it left us no chance to reinject our first data bucket. */ ap_update_vhost_given_ip(c); rc = ap_run_pre_connection(c, context->sock); if (rc != OK && rc != DONE) { c->aborted = 1; } if (e && c->aborted) { apr_bucket_free(e); } else { ap_set_module_config(c->conn_config, &mpm_winnt_module, context); } if (!c->aborted) { ap_run_process_connection(c); apr_socket_opt_get(context->sock, APR_SO_DISCONNECTED, &disconnected); if (!disconnected) { context->accept_socket = INVALID_SOCKET; ap_lingering_close(c); } } } ap_update_child_status_from_indexes(0, thread_num, SERVER_DEAD, (request_rec *) NULL); return 0; }
apr_status_t chunk_bucket_read(apr_bucket *b, const char **str, apr_size_t *len, apr_read_type_e block) { apr_size_t written=0, length=0; apr_size_t offset = 0; apr_int64_t total64, remaining64, done64; apr_int64_t bl64, w64; ssize_t w; dav_resource_private *ctx; apr_status_t rc; rc = APR_SUCCESS; (void) block; *str = NULL; /* in case we die prematurely */ *len = 0; /*dummy bucket*/ if (b->length == (apr_size_t)(-1) || b->start == (apr_off_t)(-1)) return APR_SUCCESS; ctx = b->data; offset = b->start - ctx->cp_chunk.read; DAV_DEBUG_REQ(ctx->request, 0, "Reading data for this bucket start at current position + %"APR_SIZE_T_FMT, offset); DAV_DEBUG_REQ(ctx->request, 0, "Bucket length %"APR_SIZE_T_FMT, b->length); total64 = g_ascii_strtoll(ctx->cp_chunk.uncompressed_size, NULL, 10); done64 = b->start; bl64 = b->length; remaining64 = MIN(total64 - done64, bl64); DAV_DEBUG_REQ(ctx->request, 0, "Data already returned=%"APR_INT64_T_FMT", remaining=%"APR_INT64_T_FMT, done64, remaining64); if (remaining64 <= 0){ DAV_DEBUG_REQ(ctx->request, 0, "No remaining data, end of resource delivering."); apr_bucket_heap_make(b, NULL, *len, apr_bucket_free); return APR_SUCCESS; } else { /* determine the size of THIS bucket */ if (remaining64 > APR_BUCKET_BUFF_SIZE) length = APR_BUCKET_BUFF_SIZE; else length = remaining64; } *len = length; guint8 *buf = apr_bucket_alloc(length, b->list); for (written=0; written < length ;) { GError *gerror; DAV_DEBUG_REQ(ctx->request, 0, "Trying to read at most %"APR_SSIZE_T_FMT" bytes (%"APR_SSIZE_T_FMT" received)", length - written, written); gerror = NULL; w = ctx->comp_ctx.data_uncompressor(&ctx->cp_chunk, offset, buf+written, length-written, &gerror); offset = 0; DAV_DEBUG_REQ(ctx->request, 0 , "%"APR_SSIZE_T_FMT" bytes read from local resource", w); if (w < 0) { DAV_ERROR_REQ(ctx->request, 0, "Read from chunk failed : %s", gerror_get_message(gerror)); if (gerror) g_error_free(gerror); apr_bucket_free(buf); return APR_INCOMPLETE; } if (gerror) g_error_free(gerror); if (w == 0) { DAV_DEBUG_REQ(ctx->request, 0, "No bytes read from local resource whereas we" " must read again, this should never happened"); apr_bucket_free(buf); return APR_INCOMPLETE; } written += w; } *len = written; DAV_DEBUG_REQ(ctx->request, 0, "Bucket done (%"APR_SSIZE_T_FMT" bytes, rc=%d)", written, rc); w64 = written; DAV_DEBUG_REQ(ctx->request, 0, "Status info : %"APR_INT64_T_FMT" written , %"APR_INT64_T_FMT" length total", w64, bl64); apr_bucket_heap_make(b, (char*)buf, *len, apr_bucket_free); if(w64 < bl64) { apr_bucket *bkt; DAV_DEBUG_REQ(ctx->request, 0, "Creating bucket info: bkt->length = %"APR_INT64_T_FMT", bkt->start =" " %"APR_INT64_T_FMT", bkt->data = %p, bkt->list = %p\n", remaining64, done64 + w64, &(ctx->cp_chunk), b->list); bkt = apr_bucket_alloc(sizeof(*bkt), b->list); bkt->type = &chunk_bucket_type; bkt->length = remaining64 - w64; bkt->start = done64 + w64; bkt->data = ctx; bkt->free = chunk_bucket_free_noop; bkt->list = b->list; APR_BUCKET_INSERT_AFTER(b, bkt); DAV_DEBUG_REQ(ctx->request, 0, "Starting a new RAWX bucket (length=%"APR_SIZE_T_FMT" start=%"APR_INT64_T_FMT")", bkt->length, bkt->start); } *str = (char*)buf; return rc; }