static ngx_int_t ngx_http_push_respond_to_subscribers(ngx_http_push_channel_t *channel, ngx_http_push_subscriber_t *sentinel, ngx_http_push_msg_t *msg, ngx_int_t status_code, const ngx_str_t *status_line) { ngx_slab_pool_t *shpool = ngx_http_push_shpool; ngx_http_push_subscriber_t *cur, *next; ngx_int_t responded_subscribers=0; if(sentinel==NULL) { return NGX_OK; } cur=(ngx_http_push_subscriber_t *)ngx_queue_head(&sentinel->queue); if(msg!=NULL) { //copy everything we need first ngx_str_t *content_type=NULL; ngx_str_t *etag=NULL; time_t last_modified_time; ngx_chain_t *chain; size_t content_type_len; ngx_http_request_t *r; ngx_buf_t *buffer; u_char *pos; ngx_shmtx_lock(&shpool->mutex); //etag NGX_HTTP_PUSH_MAKE_ETAG(msg->message_tag, etag, ngx_pcalloc, ngx_http_push_pool); if(etag==NULL) { //oh, nevermind... ngx_shmtx_unlock(&shpool->mutex); return NGX_ERROR; } //content-type content_type_len = msg->content_type.len; if(content_type_len>0) { NGX_HTTP_PUSH_MAKE_CONTENT_TYPE(content_type, content_type_len, msg, ngx_http_push_pool); if(content_type==NULL) { ngx_shmtx_unlock(&shpool->mutex); ngx_pfree(ngx_http_push_pool, etag); ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, "push module: unable to allocate memory for content-type header while responding to several subscriber request"); return NGX_ERROR; } } //preallocate output chain. yes, same one for every waiting subscriber if((chain = ngx_http_push_create_output_chain_locked(msg->buf, ngx_http_push_pool, ngx_cycle->log, shpool))==NULL) { ngx_shmtx_unlock(&shpool->mutex); ngx_pfree(ngx_http_push_pool, etag); ngx_pfree(ngx_http_push_pool, content_type); ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, "push module: unable to create output chain while responding to several subscriber request"); return NGX_ERROR; } buffer = chain->buf; pos = buffer->pos; last_modified_time = msg->message_time; ngx_shmtx_unlock(&shpool->mutex); //now let's respond to some requests! while(cur!=sentinel) { next=(ngx_http_push_subscriber_t *)ngx_queue_next(&cur->queue); //in this block, nothing in shared memory should be dereferenced. r=cur->request; //cleanup oughtn't dequeue anything. or decrement the subscriber count, for that matter ngx_http_push_subscriber_clear_ctx(cur); r->discard_body=0; //hacky hacky! ngx_http_finalize_request(r, ngx_http_push_prepare_response_to_subscriber_request(r, chain, content_type, etag, last_modified_time)); //BAM! responded_subscribers++; //done with this subscriber. free the sucker. ngx_pfree(ngx_http_push_pool, cur); //rewind the buffer, please buffer->pos = pos; buffer->last_buf=1; cur=next; } //free everything relevant ngx_pfree(ngx_http_push_pool, etag); ngx_pfree(ngx_http_push_pool, content_type); if(buffer->file) { ngx_close_file(buffer->file->fd); } ngx_pfree(ngx_http_push_pool, buffer); ngx_pfree(ngx_http_push_pool, chain); if(responded_subscribers) { ngx_shmtx_lock(&shpool->mutex); //message deletion ngx_http_push_release_message_locked(channel, msg); ngx_shmtx_unlock(&shpool->mutex); } } else { //headers only probably ngx_http_request_t *r; while(cur!=sentinel) { next=(ngx_http_push_subscriber_t *)ngx_queue_next(&cur->queue); r=cur->request; //cleanup oughtn't dequeue anything. or decrement the subscriber count, for that matter ngx_http_push_subscriber_clear_ctx(cur); ngx_http_finalize_request(r, ngx_http_push_respond_status_only(r, status_code, status_line)); responded_subscribers++; ngx_pfree(ngx_http_push_pool, cur); cur=next; } } ngx_shmtx_lock(&shpool->mutex); channel->subscribers-=responded_subscribers; //is the message still needed? ngx_shmtx_unlock(&shpool->mutex); ngx_pfree(ngx_http_push_pool, sentinel); return NGX_OK; }
ngx_int_t ngx_http_push_respond_to_subscribers(ngx_http_push_channel_t *channel, ngx_http_push_subscriber_t *sentinel, ngx_http_push_msg_t *msg, ngx_int_t status_code, const ngx_str_t *status_line) { //copy everything we need first ngx_str_t *content_type=NULL; ngx_str_t *etag=NULL; time_t last_modified = 0; ngx_chain_t *chain=NULL; ngx_http_request_t *r; ngx_buf_t *buffer = NULL; ngx_chain_t *rchain; ngx_buf_t *rbuffer; ngx_int_t *buf_use_count = NULL; ngx_http_push_subscriber_cleanup_t *clndata; ngx_http_push_subscriber_t *cur=NULL; ngx_int_t responded_subscribers=0; if(sentinel==NULL) { //ngx_log_error(NGX_LOG_WARN, ngx_cycle->log, 0, "respond_to_subscribers with sentinel==NULL"); return NGX_OK; } if(msg!=NULL) { if(ngx_http_push_alloc_for_subscriber_response(ngx_http_push_pool, 1, msg, &chain, &content_type, &etag, &last_modified)==NGX_ERROR) { ngx_http_push_store->release_message(channel, msg); return NGX_ERROR; } buffer = chain->buf; buffer->recycled = 1; buf_use_count = ngx_pcalloc(ngx_http_push_pool, sizeof(*buf_use_count)); *buf_use_count = ngx_http_push_store->channel_worker_subscribers(sentinel); } while((cur=ngx_http_push_store->next_subscriber(channel, sentinel, cur, 1))!=NULL) { //in this block, nothing in shared memory should be dereferenced. r=cur->request; if(msg!=NULL) { //chain and buffer for this request rchain = ngx_pcalloc(r->pool, sizeof(*rchain)); rchain->next = NULL; rbuffer = ngx_pcalloc(r->pool, sizeof(*rbuffer)); rchain->buf = rbuffer; ngx_memcpy(rbuffer, buffer, sizeof(*buffer)); //request buffer cleanup clndata = cur->clndata; clndata->buf = buffer; clndata->buf_use_count = buf_use_count; clndata->rchain = rchain; clndata->rpool = r->pool; if (rbuffer->in_file && (fcntl(rbuffer->file->fd, F_GETFD) == -1)) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "push module: buffer in invalid file descriptor"); } //cleanup oughtn't dequeue anything. or decrement the subscriber count, for that matter ngx_http_push_subscriber_clear_ctx(cur); ngx_http_finalize_request(r, ngx_http_push_prepare_response_to_subscriber_request(r, rchain, content_type, etag, last_modified)); //BAM! } else { ngx_http_push_subscriber_clear_ctx(cur); ngx_http_finalize_request(r, ngx_http_push_respond_status_only(r, status_code, status_line)); } responded_subscribers++; } if(msg!=NULL) { ngx_http_push_store->release_message(channel, msg); ngx_pfree(ngx_http_push_pool, etag); ngx_pfree(ngx_http_push_pool, content_type); ngx_pfree(ngx_http_push_pool, chain); } //ngx_log_error(NGX_LOG_WARN, ngx_cycle->log, 0, "respond_to_subscribers with msg %p finished", msg); ngx_http_push_store->lock(); channel->subscribers-=responded_subscribers; //is the message still needed? ngx_http_push_store->unlock(); ngx_http_push_store->release_subscriber_sentinel(channel, sentinel); return NGX_OK; }