static void ngx_http_push_release_message_locked(ngx_http_push_channel_t *channel, ngx_http_push_msg_t *msg) {
	msg->refcount--;
	if(msg->queue.next==NULL && msg->refcount<=0) { 
		//message had been dequeued and nobody needs it anymore
		ngx_http_push_free_message_locked(msg, ngx_http_push_shpool);
	}
	if(channel->messages > msg->delete_oldest_received_min_messages && ngx_http_push_get_oldest_message_locked(channel) == msg) {
		ngx_http_push_delete_message_locked(channel, msg, ngx_http_push_shpool);
	}
}
// remove a message from queue and free all associated memory. assumes shpool is already locked.
static ngx_inline void ngx_http_push_general_delete_message_locked(ngx_http_push_channel_t *channel, ngx_http_push_msg_t *msg, ngx_int_t force, ngx_slab_pool_t *shpool) {
	if (msg==NULL) { 
		return; 
	}
	if(channel!=NULL) {
		ngx_queue_remove(&msg->queue);
		channel->messages--;
	}
	if(msg->refcount==0 || force) {
		//nobody needs this message, or we were forced at integer-point to delete
		ngx_http_push_free_message_locked(msg, shpool);
	}
}
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_slab_pool_t *)ngx_http_push_shm_zone->shm.addr;
	ngx_queue_t                    *cur, *next;
	ngx_int_t                       responded_subscribers=0;
	if(sentinel==NULL) {
		return NGX_OK;
	}
	
	cur=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!=(ngx_queue_t *)sentinel) {
			next=ngx_queue_next(cur);
			//in this block, nothing in shared memory should be dereferenced.
			r=((ngx_http_push_subscriber_t *)cur)->request;
			//cleanup oughtn't dequeue anything. or decrement the subscriber count, for that matter
			((ngx_http_push_subscriber_t *)cur)->clndata->subscriber=NULL;
			((ngx_http_push_subscriber_t *)cur)->clndata->channel=NULL;
			
			//unpostpone request
			r->postponed=NULL;
			
			ngx_http_finalize_request(r, ngx_http_push_prepare_response_to_subscriber_request(r, chain, content_type, etag, last_modified_time)); //BAM!
			responded_subscribers++;
			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);
		
	}
	else {
		//headers only probably
		ngx_http_request_t     *r;
		while(cur!=(ngx_queue_t *)sentinel) {
			next=ngx_queue_next(cur);
			r=((ngx_http_push_subscriber_t *)cur)->request;
			
			//cleanup oughtn't dequeue anything. or decrement the subscriber count, for that matter
			((ngx_http_push_subscriber_t *)cur)->clndata->subscriber=NULL;
			((ngx_http_push_subscriber_t *)cur)->clndata->channel=NULL;
			ngx_http_push_respond_status_only(((ngx_http_push_subscriber_t *)cur)->request, 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?
	if(msg!=NULL && (--msg->refcount)==0 && msg->queue.next==NULL) { 
		//message was dequeued, and nobody needs it anymore
		ngx_http_push_free_message_locked(msg, shpool);
	}
	ngx_shmtx_unlock(&shpool->mutex);
	ngx_pfree(ngx_http_push_pool, sentinel);
	return NGX_OK;
}
static ngx_int_t ngx_http_push_subscriber_handler(ngx_http_request_t *r) {
	ngx_http_push_loc_conf_t       *cf = ngx_http_get_module_loc_conf(r, ngx_http_push_module);
	ngx_slab_pool_t                *shpool = (ngx_slab_pool_t *)ngx_http_push_shm_zone->shm.addr;
	ngx_str_t                      *id;
	ngx_http_push_channel_t        *channel;
	ngx_http_push_msg_t            *msg;
	ngx_int_t                       msg_search_outcome;
	
	ngx_str_t                      *content_type=NULL;
	ngx_str_t                      *etag;
	
	if (r->method != NGX_HTTP_GET) {
		ngx_http_push_add_response_header(r, &NGX_HTTP_PUSH_HEADER_ALLOW, &NGX_HTTP_PUSH_ALLOW_GET); //valid HTTP for teh win
		return NGX_HTTP_NOT_ALLOWED;
	}
	
	if((id=ngx_http_push_get_channel_id(r, cf)) == NULL) {
		return r->headers_out.status ? NGX_OK : NGX_HTTP_INTERNAL_SERVER_ERROR;
	}

	//get the channel and check channel authorization while we're at it.
	ngx_shmtx_lock(&shpool->mutex);
	channel = (cf->authorize_channel==1 ? ngx_http_push_find_channel : ngx_http_push_get_channel)(id, &((ngx_http_push_shm_data_t *) ngx_http_push_shm_zone->data)->tree, shpool, r->connection->log);

	if (channel==NULL) {
		//unable to allocate channel OR channel not found
		ngx_shmtx_unlock(&shpool->mutex);
		if(cf->authorize_channel) {
			return NGX_HTTP_FORBIDDEN;
		}
		else {
			ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "push module: unable to allocate shared memory for channel");
			return NGX_HTTP_INTERNAL_SERVER_ERROR;
		}
	}
	msg = ngx_http_push_find_message_locked(channel, r, &msg_search_outcome); 
	channel->last_seen = ngx_time();
	ngx_shmtx_unlock(&shpool->mutex);
	
	switch(ngx_http_push_handle_subscriber_concurrency_setting(cf->subscriber_concurrency, channel, r, shpool)) {
		case NGX_DECLINED: //this request was declined for some reason.
			//status codes and whatnot should have already been written. just get out of here quickly.
			return NGX_OK;
		case NGX_ERROR:
			ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "push module: error handling subscriber concurrency setting");
			return NGX_ERROR;
	}

	switch(msg_search_outcome) {
		//for message-found:
		ngx_chain_t                *chain;
		time_t                      last_modified;
		size_t                      content_type_len;
		ngx_http_postponed_request_t  *pr, *p;

		case NGX_HTTP_PUSH_MESSAGE_EXPECTED:
			// ♫ It's gonna be the future soon ♫
			switch(cf->subscriber_poll_mechanism) {
				//for NGX_HTTP_PUSH_MECHANISM_LONGPOLL
				ngx_http_push_pid_queue_t  *sentinel, *cur, *found;
				ngx_http_push_subscriber_t *subscriber;
				ngx_http_push_subscriber_t *subscriber_sentinel;
				
				case NGX_HTTP_PUSH_MECHANISM_LONGPOLL:
					//long-polling subscriber. wait for a message.
					
					//subscribers are queued up in a local pool. Queue sentinels are separate and also local, but not in the pool.
					ngx_shmtx_lock(&shpool->mutex);
					sentinel = &channel->workers_with_subscribers;
					cur = (ngx_http_push_pid_queue_t *)ngx_queue_head(&sentinel->queue);
					found = NULL;
					
					ngx_http_push_subscriber_cleanup_t *clndata;
					ngx_pool_cleanup_t             *cln;
					while(cur!=sentinel) {
						if(cur->pid==ngx_pid) {
							found = cur;
							break;
						}
						cur = (ngx_http_push_pid_queue_t *)ngx_queue_next(&cur->queue);
					}
					if(found==NULL) { //found nothing
						if((found=ngx_slab_alloc_locked(shpool, sizeof(*found)))==NULL) {
							ngx_shmtx_unlock(&shpool->mutex);
							ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "push module: unable to allocate worker subscriber queue marker in shared memory");
							return NGX_HTTP_INTERNAL_SERVER_ERROR;
						}
						//initialize
						ngx_queue_insert_tail(&sentinel->queue, &found->queue);
						found->pid=ngx_pid;
						found->slot=ngx_process_slot;
						found->subscriber_sentinel=NULL;
					}
					ngx_shmtx_unlock(&shpool->mutex);
					
					if((subscriber = ngx_palloc(ngx_http_push_pool, sizeof(*subscriber)))==NULL) { //unable to allocate request queue element
						return NGX_ERROR;
					}

					//postpone the request. this seems to be magical.
					pr = ngx_palloc(r->pool, sizeof(ngx_http_postponed_request_t));
					if (pr == NULL) {
						return NGX_ERROR;
					}
					pr->request = r; //really?
					pr->out = NULL;
					pr->next = NULL;
					if (r->postponed) {
						for (p = r->postponed; p->next; p = p->next) { /* void */ }
						p->next = pr;
					} else {
						r->postponed = pr;
					}
					
					 //attach a cleaner to remove the request from the channel.
					if ((cln=ngx_pool_cleanup_add(r->pool, sizeof(*clndata))) == NULL) { //make sure we can.
						return NGX_ERROR;
					}
					cln->handler = (ngx_pool_cleanup_pt) ngx_http_push_subscriber_cleanup;
					clndata = (ngx_http_push_subscriber_cleanup_t *) cln->data;
					clndata->channel=channel;
					clndata->subscriber=subscriber;
					
					subscriber->request = r;
					subscriber->clndata=clndata;
					
					ngx_shmtx_lock(&shpool->mutex);
					channel->subscribers++; // do this only when we know everything went okay.
					
					//figure out the subscriber sentinel
					subscriber_sentinel = ((ngx_http_push_pid_queue_t *)found)->subscriber_sentinel;
					if(subscriber_sentinel==NULL) {
						if((subscriber_sentinel=ngx_palloc(ngx_http_push_pool, sizeof(*subscriber_sentinel)))==NULL) {
							ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "push module: unable to allocate channel subscriber sentinel");
							return NGX_HTTP_INTERNAL_SERVER_ERROR;
						}
						ngx_queue_init(&subscriber_sentinel->queue);
						((ngx_http_push_pid_queue_t *)found)->subscriber_sentinel=subscriber_sentinel;
					}
					ngx_shmtx_unlock(&shpool->mutex);
					
					ngx_queue_insert_tail(&subscriber_sentinel->queue, &subscriber->queue);
					
#if defined(nginx_version) && nginx_version >= 7000
					return NGX_OK; //do recall that the request was postponed
#else
					return NGX_DONE; //oldschool
#endif
				case NGX_HTTP_PUSH_MECHANISM_INTERVALPOLL:
				
					//interval-polling subscriber requests get a 304 with their entity tags preserved.
					if (r->headers_in.if_modified_since != NULL) {
						r->headers_out.last_modified_time=ngx_http_parse_time(r->headers_in.if_modified_since->value.data, r->headers_in.if_modified_since->value.len);
					}
					if ((etag=ngx_http_push_subscriber_get_etag(r)) != NULL) {
						r->headers_out.etag=ngx_http_push_add_response_header(r, &NGX_HTTP_PUSH_HEADER_ETAG, etag);
					}
					return NGX_HTTP_NOT_MODIFIED;
					
				default:
					//if this ever happens, there's a bug somewhere else. probably config stuff.
					return NGX_HTTP_INTERNAL_SERVER_ERROR;
			}
		
		case NGX_HTTP_PUSH_MESSAGE_EXPIRED:
			//subscriber wants an expired message
			//TODO: maybe respond with entity-identifiers for oldest available message?
			return NGX_HTTP_NO_CONTENT; 
		
		case NGX_HTTP_PUSH_MESSAGE_FOUND:
			//found the message
			ngx_shmtx_lock(&shpool->mutex);
			msg->refcount++; // this probably isn't necessary, but i'm not thinking too straight at the moment. so just in case.
			if((msg->received)!=(ngx_uint_t) NGX_MAX_UINT32_VALUE){ //overflow check?
				msg->received++;
			}
			NGX_HTTP_PUSH_MAKE_ETAG(msg->message_tag, etag, ngx_palloc, r->pool);
			if(etag==NULL) {
				//oh, nevermind...
				ngx_shmtx_unlock(&shpool->mutex);
				ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "push module: unable to allocate memory for Etag header");
				return NGX_ERROR;
			}
			
			content_type_len = msg->content_type.len;
			if(content_type_len>0) {
				NGX_HTTP_PUSH_MAKE_CONTENT_TYPE(content_type, content_type_len, msg, r->pool);
				if(content_type==NULL) {
					ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "push module: unable to allocate memory for content-type header while responding to subscriber request");
					ngx_shmtx_unlock(&shpool->mutex);
					return NGX_ERROR;
				}
			}
			
			//preallocate output chain. yes, same one for every waiting subscriber
			if((chain = ngx_http_push_create_output_chain_locked(msg->buf, r->pool, r->connection->log, shpool))==NULL) {
				ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "push module: unable to allocate buffer chain while responding to subscriber request");
				ngx_shmtx_unlock(&shpool->mutex);
				return NGX_ERROR;
			}
			
			last_modified = msg->message_time;
			
			if(msg->received!=NGX_MAX_UINT32_VALUE) {
				msg->received++;
			}
			//is the message still needed?
			if(msg!=NULL && (--msg->refcount)==0 && msg->queue.next==NULL) { 
				//message was dequeued, and nobody needs it anymore
				ngx_http_push_free_message_locked(msg, shpool);
			}
			ngx_shmtx_unlock(&shpool->mutex);
			
			if(chain->buf->file!=NULL) {
				//close file when we're done with it
				ngx_pool_cleanup_t *cln;
				ngx_pool_cleanup_file_t *clnf;

				if((cln = ngx_pool_cleanup_add(r->pool, sizeof(ngx_pool_cleanup_file_t)))==NULL) {
					return NGX_HTTP_INTERNAL_SERVER_ERROR;
				}
				cln->handler = ngx_pool_cleanup_file;
				clnf = cln->data;
				clnf->fd = chain->buf->file->fd;
				clnf->name = chain->buf->file->name.data;
				clnf->log = r->pool->log;
			}

			
			return ngx_http_push_prepare_response_to_subscriber_request(r, chain, content_type, etag, last_modified);
			
		default: //we shouldn't be here.
			return NGX_HTTP_INTERNAL_SERVER_ERROR;
	}
}