static ngx_int_t ngx_http_push_stream_respond_to_subscribers(ngx_http_push_stream_channel_t *channel, ngx_http_push_stream_queue_elem_t *subscribers_sentinel, ngx_http_push_stream_msg_t *msg) { ngx_http_push_stream_queue_elem_t *cur = subscribers_sentinel; if (subscribers_sentinel == NULL) { return NGX_ERROR; } if (msg != NULL) { // now let's respond to some requests! while ((cur = (ngx_http_push_stream_queue_elem_t *) ngx_queue_next(&cur->queue)) != subscribers_sentinel) { ngx_http_push_stream_subscriber_t *subscriber = (ngx_http_push_stream_subscriber_t *) cur->value; if (subscriber->longpolling) { ngx_http_push_stream_queue_elem_t *prev = (ngx_http_push_stream_queue_elem_t *) ngx_queue_prev(&cur->queue); ngx_http_push_stream_add_response_header(subscriber->request, &NGX_HTTP_PUSH_STREAM_HEADER_TRANSFER_ENCODING, &NGX_HTTP_PUSH_STREAM_HEADER_CHUNCKED); ngx_http_push_stream_add_polling_headers(subscriber->request, msg->time, msg->tag, subscriber->request->pool); ngx_http_send_header(subscriber->request); //Adam Konrad: putting If-Modified-Since and etag to the response body ngx_http_push_stream_send_http_time( subscriber->request, msg->time, msg->tag, subscriber->request->pool); ngx_http_push_stream_send_response_content_header(subscriber->request, ngx_http_get_module_loc_conf(subscriber->request, ngx_http_push_stream_module)); ngx_http_push_stream_send_response_message(subscriber->request, channel, msg, 1, 0); ngx_http_push_stream_send_response_finalize(subscriber->request); cur = prev; } else { if (ngx_http_push_stream_send_response_message(subscriber->request, channel, msg, 0, 0) != NGX_OK) { ngx_http_push_stream_queue_elem_t *prev = (ngx_http_push_stream_queue_elem_t *) ngx_queue_prev(&cur->queue); ngx_http_push_stream_send_response_finalize(subscriber->request); cur = prev; } else { ngx_http_push_stream_subscriber_ctx_t *ctx = ngx_http_get_module_ctx(subscriber->request, ngx_http_push_stream_module); ngx_http_push_stream_loc_conf_t *pslcf = ngx_http_get_module_loc_conf(subscriber->request, ngx_http_push_stream_module); ngx_http_push_stream_timer_reset(pslcf->ping_message_interval, ctx->ping_timer); } } } } return NGX_OK; }
void ngx_http_push_stream_websocket_reading(ngx_http_request_t *r) { ngx_http_push_stream_loc_conf_t *cf = ngx_http_get_module_loc_conf(r, ngx_http_push_stream_module); u_char buf[8]; ngx_err_t err; ngx_event_t *rev; ngx_connection_t *c; ngx_http_push_stream_frame_t frame; ngx_str_t *aux; uint64_t i; ngx_pool_t *temp_pool = NULL; c = r->connection; rev = c->read; #if (NGX_HAVE_KQUEUE) if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) { if (!rev->pending_eof) { return; } rev->eof = 1; c->error = 1; err = rev->kq_errno; goto closed; } #endif //reading frame header if (ngx_http_push_stream_recv(c, rev, &err, buf, 2) == NGX_ERROR) { goto closed; } frame.fin = (buf[0] >> 7) & 1; frame.rsv1 = (buf[0] >> 6) & 1; frame.rsv2 = (buf[0] >> 5) & 1; frame.rsv3 = (buf[0] >> 4) & 1; frame.opcode = buf[0] & 0xf; frame.mask = (buf[1] >> 7) & 1; frame.payload_len = buf[1] & 0x7f; if (frame.payload_len == 126) { if (ngx_http_push_stream_recv(c, rev, &err, buf, 2) == NGX_ERROR) { goto closed; } uint16_t len; ngx_memcpy(&len, buf, 2); frame.payload_len = ntohs(len); } else if (frame.payload_len == 127) { if (ngx_http_push_stream_recv(c, rev, &err, buf, 8) == NGX_ERROR) { goto closed; } uint64_t len; ngx_memcpy(&len, buf, 8); frame.payload_len = ngx_http_push_stream_ntohll(len); } if (frame.mask && (ngx_http_push_stream_recv(c, rev, &err, frame.mask_key, 4) == NGX_ERROR)) { goto closed; } if (frame.payload_len > 0) { //create a temporary pool to allocate temporary elements if ((temp_pool = ngx_create_pool(4096, r->connection->log)) == NULL) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "push stream module: unable to allocate memory for temporary pool"); ngx_http_finalize_request(r, NGX_OK); return; } aux = ngx_http_push_stream_create_str(temp_pool, frame.payload_len); if (ngx_http_push_stream_recv(c, rev, &err, aux->data, (ssize_t) frame.payload_len) == NGX_ERROR) { goto closed; } if (cf->websocket_allow_publish && (frame.opcode == NGX_HTTP_PUSH_STREAM_WEBSOCKET_TEXT_OPCODE)) { frame.payload = aux->data; if (frame.mask) { for (i = 0; i < frame.payload_len; i++) { frame.payload[i] = frame.payload[i] ^ frame.mask_key[i % 4]; } } ngx_http_push_stream_subscriber_ctx_t *ctx = ngx_http_get_module_ctx(r, ngx_http_push_stream_module); ngx_http_push_stream_subscription_t *subscription = (ngx_http_push_stream_subscription_t *)ngx_queue_head(&ctx->subscriber->subscriptions_sentinel.queue); if (ngx_http_push_stream_add_msg_to_channel(r, &subscription->channel->id, frame.payload, frame.payload_len, NULL, NULL, temp_pool) == NULL) { ngx_http_finalize_request(r, NGX_OK); ngx_destroy_pool(temp_pool); return; } else { ngx_http_push_stream_send_response_text(r, NGX_HTTP_PUSH_STREAM_WEBSOCKET_PING_LAST_FRAME_BYTE, sizeof(NGX_HTTP_PUSH_STREAM_WEBSOCKET_PING_LAST_FRAME_BYTE), 1); } } ngx_destroy_pool(temp_pool); } if (frame.opcode == NGX_HTTP_PUSH_STREAM_WEBSOCKET_CLOSE_OPCODE) { ngx_http_push_stream_send_response_finalize(r); } /* aio does not call this handler */ if ((ngx_event_flags & NGX_USE_LEVEL_EVENT) && rev->active) { if (ngx_del_event(rev, NGX_READ_EVENT, 0) != NGX_OK) { ngx_http_finalize_request(r, NGX_OK); } } return; closed: if (temp_pool != NULL) { ngx_destroy_pool(temp_pool); } if (err) { rev->error = 1; } ngx_log_error(NGX_LOG_INFO, c->log, err, "client closed prematurely connection"); ngx_http_finalize_request(r, NGX_OK); }