static ngx_int_t ngx_http_push_stream_subscriber_handler(ngx_http_request_t *r) { ngx_slab_pool_t *shpool = (ngx_slab_pool_t *)ngx_http_push_stream_shm_zone->shm.addr; ngx_http_push_stream_loc_conf_t *cf = ngx_http_get_module_loc_conf(r, ngx_http_push_stream_module); ngx_http_push_stream_subscriber_t *worker_subscriber; ngx_http_push_stream_requested_channel_t *channels_ids, *cur; ngx_http_push_stream_subscriber_ctx_t *ctx; time_t if_modified_since; ngx_str_t *last_event_id, vv_time = ngx_null_string; ngx_str_t *push_mode; ngx_flag_t polling, longpolling; ngx_int_t rc; ngx_int_t status_code; ngx_str_t *explain_error_message; // add headers to support cross domain requests if (cf->allowed_origins.len > 0) { ngx_http_push_stream_add_response_header(r, &NGX_HTTP_PUSH_STREAM_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN, &cf->allowed_origins); ngx_http_push_stream_add_response_header(r, &NGX_HTTP_PUSH_STREAM_HEADER_ACCESS_CONTROL_ALLOW_METHODS, &NGX_HTTP_PUSH_STREAM_ALLOW_GET); ngx_http_push_stream_add_response_header(r, &NGX_HTTP_PUSH_STREAM_HEADER_ACCESS_CONTROL_ALLOW_HEADERS, &NGX_HTTP_PUSH_STREAM_ALLOWED_HEADERS); } if (r->method & NGX_HTTP_OPTIONS) { return ngx_http_push_stream_send_only_header_response(r, NGX_HTTP_OK, NULL); } // only accept GET method if (!(r->method & NGX_HTTP_GET)) { ngx_http_push_stream_add_response_header(r, &NGX_HTTP_PUSH_STREAM_HEADER_ALLOW, &NGX_HTTP_PUSH_STREAM_ALLOW_GET); return ngx_http_push_stream_send_only_header_response(r, NGX_HTTP_NOT_ALLOWED, NULL); } if ((ctx = ngx_http_push_stream_add_request_context(r)) == NULL) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "push stream module: unable to create request context"); return NGX_HTTP_INTERNAL_SERVER_ERROR; } //get channels ids and backtracks from path channels_ids = ngx_http_push_stream_parse_channels_ids_from_path(r, ctx->temp_pool); if ((channels_ids == NULL) || ngx_queue_empty(&channels_ids->queue)) { ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, "push stream module: the $push_stream_channels_path variable is required but is not set"); return ngx_http_push_stream_send_only_header_response(r, NGX_HTTP_BAD_REQUEST, &NGX_HTTP_PUSH_STREAM_NO_CHANNEL_ID_MESSAGE); } //validate channels: name, length and quantity. check if channel exists when authorized_channels_only is on. check if channel is full of subscribers if (ngx_http_push_stream_validate_channels(r, channels_ids, &status_code, &explain_error_message) == NGX_ERROR) { return ngx_http_push_stream_send_only_header_response(r, status_code, explain_error_message); } if (cf->last_received_message_time != NULL) { ngx_http_push_stream_complex_value(r, cf->last_received_message_time, &vv_time); } else if (r->headers_in.if_modified_since != NULL) { vv_time = r->headers_in.if_modified_since->value; } // get control headers if_modified_since = vv_time.len ? ngx_http_parse_time(vv_time.data, vv_time.len) : -1; last_event_id = ngx_http_push_stream_get_header(r, &NGX_HTTP_PUSH_STREAM_HEADER_LAST_EVENT_ID); push_mode = ngx_http_push_stream_get_header(r, &NGX_HTTP_PUSH_STREAM_HEADER_MODE); polling = ((cf->location_type == NGX_HTTP_PUSH_STREAM_SUBSCRIBER_MODE_POLLING) || ((push_mode != NULL) && (push_mode->len == NGX_HTTP_PUSH_STREAM_MODE_POLLING.len) && (ngx_strncasecmp(push_mode->data, NGX_HTTP_PUSH_STREAM_MODE_POLLING.data, NGX_HTTP_PUSH_STREAM_MODE_POLLING.len) == 0))); longpolling = ((cf->location_type == NGX_HTTP_PUSH_STREAM_SUBSCRIBER_MODE_LONGPOLLING) || ((push_mode != NULL) && (push_mode->len == NGX_HTTP_PUSH_STREAM_MODE_LONGPOLLING.len) && (ngx_strncasecmp(push_mode->data, NGX_HTTP_PUSH_STREAM_MODE_LONGPOLLING.data, NGX_HTTP_PUSH_STREAM_MODE_LONGPOLLING.len) == 0))); if (polling || longpolling) { ngx_int_t result = ngx_http_push_stream_subscriber_polling_handler(r, channels_ids, if_modified_since, last_event_id, longpolling, ctx->temp_pool); if (ctx->temp_pool != NULL) { ngx_destroy_pool(ctx->temp_pool); ctx->temp_pool = NULL; } return result; } ctx->padding = ngx_http_push_stream_get_padding_by_user_agent(r); // stream access if ((worker_subscriber = ngx_http_push_stream_subscriber_prepare_request_to_keep_connected(r)) == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } ngx_http_push_stream_add_response_header(r, &NGX_HTTP_PUSH_STREAM_HEADER_TRANSFER_ENCODING, &NGX_HTTP_PUSH_STREAM_HEADER_CHUNCKED); ngx_http_send_header(r); // sending response content header if (ngx_http_push_stream_send_response_content_header(r, cf) == NGX_ERROR) { ngx_log_error(NGX_LOG_ERR, (r)->connection->log, 0, "push stream module: could not send content header to subscriber"); return NGX_HTTP_INTERNAL_SERVER_ERROR; } ngx_shmtx_lock(&shpool->mutex); rc = ngx_http_push_stream_registry_subscriber_locked(r, worker_subscriber); ngx_shmtx_unlock(&shpool->mutex); if (rc == NGX_ERROR) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } // adding subscriber to channel(s) and send backtrack messages cur = channels_ids; while ((cur = (ngx_http_push_stream_requested_channel_t *) ngx_queue_next(&cur->queue)) != channels_ids) { if (ngx_http_push_stream_subscriber_assign_channel(shpool, cf, r, cur, if_modified_since, last_event_id, worker_subscriber, ctx->temp_pool) != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } } if (ctx->temp_pool != NULL) { ngx_destroy_pool(ctx->temp_pool); ctx->temp_pool = NULL; } return NGX_DONE; }
static ngx_int_t ngx_http_push_stream_websocket_handler(ngx_http_request_t *r) { #if !(NGX_HAVE_SHA1) ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "push stream module: sha1 support is needed to use WebSocket"); return NGX_OK; #endif ngx_http_push_stream_main_conf_t *mcf = ngx_http_get_module_main_conf(r, ngx_http_push_stream_module); ngx_http_push_stream_loc_conf_t *cf = ngx_http_get_module_loc_conf(r, ngx_http_push_stream_module); ngx_http_push_stream_subscriber_t *worker_subscriber; ngx_http_push_stream_requested_channel_t *requested_channels, *requested_channel; ngx_queue_t *q; ngx_http_push_stream_module_ctx_t *ctx; ngx_int_t tag; time_t if_modified_since; ngx_str_t *last_event_id = NULL; ngx_int_t status_code; ngx_str_t *explain_error_message; ngx_str_t *upgrade_header, *connection_header, *sec_key_header, *sec_version_header, *sec_accept_header, *user_agent; ngx_int_t version; // WebSocket connections must not use keepalive r->keepalive = 0; // only accept GET method if (!(r->method & NGX_HTTP_GET)) { ngx_http_push_stream_add_response_header(r, &NGX_HTTP_PUSH_STREAM_HEADER_ALLOW, &NGX_HTTP_PUSH_STREAM_ALLOW_GET); return ngx_http_push_stream_send_only_header_response(r, NGX_HTTP_NOT_ALLOWED, NULL); } ngx_http_push_stream_set_expires(r, NGX_HTTP_PUSH_STREAM_EXPIRES_EPOCH, 0); upgrade_header = ngx_http_push_stream_get_header(r, &NGX_HTTP_PUSH_STREAM_HEADER_UPGRADE); connection_header = ngx_http_push_stream_get_header(r, &NGX_HTTP_PUSH_STREAM_HEADER_CONNECTION); sec_key_header = ngx_http_push_stream_get_header(r, &NGX_HTTP_PUSH_STREAM_HEADER_SEC_WEBSOCKET_KEY); sec_version_header = ngx_http_push_stream_get_header(r, &NGX_HTTP_PUSH_STREAM_HEADER_SEC_WEBSOCKET_VERSION); user_agent = ngx_http_push_stream_get_header(r, &NGX_HTTP_HEADER_USER_AGENT); if ((upgrade_header == NULL) || (connection_header == NULL) || (sec_key_header == NULL) || (sec_version_header == NULL)) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "push stream module: %V, user_agent: %s, key %s, version: %s", &NGX_HTTP_PUSH_STREAM_NO_MANDATORY_HEADERS_MESSAGE, user_agent == NULL ? (void*)"N/A" : user_agent->data, sec_key_header == NULL ? (void*)"N/A" : sec_key_header == NULL ? (void*)"N/A" : sec_key_header->data, sec_version_header == NULL ? (void*)"N/A" : sec_version_header->data); ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, ":%d, %d, %d, %d:", upgrade_header == NULL ? 0 : upgrade_header->len, connection_header == NULL ? 0 : connection_header->len, sec_key_header == NULL ? 0 : sec_key_header->len, sec_version_header == NULL ? 0 : sec_version_header->len); return ngx_http_push_stream_send_only_header_response(r, NGX_HTTP_BAD_REQUEST, &NGX_HTTP_PUSH_STREAM_NO_MANDATORY_HEADERS_MESSAGE); } version = ngx_atoi(sec_version_header->data, sec_version_header->len); if ((version != NGX_HTTP_PUSH_STREAM_WEBSOCKET_VERSION_8) && (version != NGX_HTTP_PUSH_STREAM_WEBSOCKET_VERSION_13)) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "push stream module: version: %d %V", version, &NGX_HTTP_PUSH_STREAM_WRONG_WEBSOCKET_VERSION_MESSAGE); ngx_http_push_stream_add_response_header(r, &NGX_HTTP_PUSH_STREAM_HEADER_SEC_WEBSOCKET_VERSION, &NGX_HTTP_PUSH_STREAM_WEBSOCKET_SUPPORTED_VERSIONS); return ngx_http_push_stream_send_only_header_response(r, NGX_HTTP_BAD_REQUEST, &NGX_HTTP_PUSH_STREAM_WRONG_WEBSOCKET_VERSION_MESSAGE); } if ((ctx = ngx_http_push_stream_add_request_context(r)) == NULL) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "push stream module: unable to create request context"); return NGX_HTTP_INTERNAL_SERVER_ERROR; } if ((ctx->frame = ngx_pcalloc(r->pool, sizeof(ngx_http_push_stream_frame_t))) == NULL) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "push stream module: unable to create frame structure"); return NGX_HTTP_INTERNAL_SERVER_ERROR; } ctx->frame->step = NGX_HTTP_PUSH_STREAM_WEBSOCKET_READ_START_STEP; ctx->frame->last = NULL; ctx->frame->payload = NULL; if ((sec_accept_header = ngx_http_push_stream_generate_websocket_accept_value(r, sec_key_header, ctx->temp_pool)) == NULL) { ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, "push stream module: could not generate security accept header value"); return NGX_HTTP_INTERNAL_SERVER_ERROR; } ngx_http_push_stream_add_response_header(r, &NGX_HTTP_PUSH_STREAM_HEADER_UPGRADE, &NGX_HTTP_PUSH_STREAM_WEBSOCKET_UPGRADE); ngx_http_push_stream_add_response_header(r, &NGX_HTTP_PUSH_STREAM_HEADER_CONNECTION, &NGX_HTTP_PUSH_STREAM_WEBSOCKET_CONNECTION); ngx_http_push_stream_add_response_header(r, &NGX_HTTP_PUSH_STREAM_HEADER_SEC_WEBSOCKET_ACCEPT, sec_accept_header); r->headers_out.status_line = NGX_HTTP_PUSH_STREAM_101_STATUS_LINE; ngx_http_push_stream_send_only_added_headers(r); //get channels ids and backtracks from path requested_channels = ngx_http_push_stream_parse_channels_ids_from_path(r, ctx->temp_pool); if ((requested_channels == NULL) || ngx_queue_empty(&requested_channels->queue)) { ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, "push stream module: the push_stream_channels_path is required but is not set"); return ngx_http_push_stream_send_only_header_response(r, NGX_HTTP_BAD_REQUEST, &NGX_HTTP_PUSH_STREAM_NO_CHANNEL_ID_MESSAGE); } //validate channels: name, length and quantity. check if channel exists when authorized_channels_only is on. check if channel is full of subscribers if (ngx_http_push_stream_validate_channels(r, requested_channels, &status_code, &explain_error_message) == NGX_ERROR) { return ngx_http_push_stream_send_websocket_close_frame(r, status_code, explain_error_message); } // get control values ngx_http_push_stream_get_last_received_message_values(r, &if_modified_since, &tag, &last_event_id); // stream access if ((worker_subscriber = ngx_http_push_stream_subscriber_prepare_request_to_keep_connected(r)) == NULL) { return ngx_http_push_stream_send_websocket_close_frame(r, NGX_HTTP_INTERNAL_SERVER_ERROR, &NGX_HTTP_PUSH_STREAM_EMPTY); } // sending response content header if (ngx_http_push_stream_send_response_content_header(r, cf) == NGX_ERROR) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "push stream module: could not send content header to subscriber"); return ngx_http_push_stream_send_websocket_close_frame(r, NGX_HTTP_INTERNAL_SERVER_ERROR, &NGX_HTTP_PUSH_STREAM_EMPTY); } if (ngx_http_push_stream_registry_subscriber(r, worker_subscriber) == NGX_ERROR) { return ngx_http_push_stream_send_websocket_close_frame(r, NGX_HTTP_INTERNAL_SERVER_ERROR, &NGX_HTTP_PUSH_STREAM_EMPTY); } // adding subscriber to channel(s) and send backtrack messages for (q = ngx_queue_head(&requested_channels->queue); q != ngx_queue_sentinel(&requested_channels->queue); q = ngx_queue_next(q)) { requested_channel = ngx_queue_data(q, ngx_http_push_stream_requested_channel_t, queue); if (ngx_http_push_stream_subscriber_assign_channel(mcf, cf, r, requested_channel, if_modified_since, tag, last_event_id, worker_subscriber, ctx->temp_pool) != NGX_OK) { return ngx_http_push_stream_send_websocket_close_frame(r, NGX_HTTP_INTERNAL_SERVER_ERROR, &NGX_HTTP_PUSH_STREAM_EMPTY); } } if (ctx->temp_pool != NULL) { ngx_destroy_pool(ctx->temp_pool); ctx->temp_pool = NULL; } return NGX_DONE; }
static ngx_int_t ngx_http_push_stream_websocket_handler(ngx_http_request_t *r) { #if !(NGX_HAVE_SHA1) ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "push stream module: sha1 support is needed to use WebSocket"); return NGX_OK; #endif ngx_slab_pool_t *shpool = (ngx_slab_pool_t *)ngx_http_push_stream_shm_zone->shm.addr; ngx_http_push_stream_loc_conf_t *cf = ngx_http_get_module_loc_conf(r, ngx_http_push_stream_module); ngx_http_push_stream_subscriber_t *worker_subscriber; ngx_http_push_stream_requested_channel_t *channels_ids, *cur; ngx_http_push_stream_subscriber_ctx_t *ctx; ngx_int_t rc; ngx_int_t status_code; ngx_str_t *explain_error_message; ngx_str_t *upgrade_header, *connection_header, *sec_key_header, *sec_version_header, *sec_accept_header; ngx_int_t version; // only accept GET method if (!(r->method & NGX_HTTP_GET)) { ngx_http_push_stream_add_response_header(r, &NGX_HTTP_PUSH_STREAM_HEADER_ALLOW, &NGX_HTTP_PUSH_STREAM_ALLOW_GET); return ngx_http_push_stream_send_only_header_response(r, NGX_HTTP_NOT_ALLOWED, NULL); } ngx_http_push_stream_set_expires(r, NGX_HTTP_PUSH_STREAM_EXPIRES_EPOCH, 0); upgrade_header = ngx_http_push_stream_get_header(r, &NGX_HTTP_PUSH_STREAM_HEADER_UPGRADE); connection_header = ngx_http_push_stream_get_header(r, &NGX_HTTP_PUSH_STREAM_HEADER_CONNECTION); sec_key_header = ngx_http_push_stream_get_header(r, &NGX_HTTP_PUSH_STREAM_HEADER_SEC_WEBSOCKET_KEY); sec_version_header = ngx_http_push_stream_get_header(r, &NGX_HTTP_PUSH_STREAM_HEADER_SEC_WEBSOCKET_VERSION); if ((upgrade_header == NULL) || (connection_header == NULL) || (sec_key_header == NULL) || (sec_version_header == NULL)) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "push stream module: %V", &NGX_HTTP_PUSH_STREAM_NO_MANDATORY_HEADERS_MESSAGE); return ngx_http_push_stream_send_only_header_response(r, NGX_HTTP_BAD_REQUEST, &NGX_HTTP_PUSH_STREAM_NO_MANDATORY_HEADERS_MESSAGE); } version = ngx_atoi(sec_version_header->data, sec_version_header->len); if ((version != NGX_HTTP_PUSH_STREAM_WEBSOCKET_VERSION_8) && (version != NGX_HTTP_PUSH_STREAM_WEBSOCKET_VERSION_13)) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "push stream module: version: %d %V", version, &NGX_HTTP_PUSH_STREAM_WRONG_WEBSOCKET_VERSION_MESSAGE); ngx_http_push_stream_add_response_header(r, &NGX_HTTP_PUSH_STREAM_HEADER_SEC_WEBSOCKET_VERSION, &NGX_HTTP_PUSH_STREAM_WEBSOCKET_SUPPORTED_VERSIONS); return ngx_http_push_stream_send_only_header_response(r, NGX_HTTP_BAD_REQUEST, &NGX_HTTP_PUSH_STREAM_WRONG_WEBSOCKET_VERSION_MESSAGE); } if ((ctx = ngx_http_push_stream_add_request_context(r)) == NULL) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "push stream module: unable to create request context"); return NGX_HTTP_INTERNAL_SERVER_ERROR; } //get channels ids and backtracks from path channels_ids = ngx_http_push_stream_parse_channels_ids_from_path(r, ctx->temp_pool); if ((channels_ids == NULL) || ngx_queue_empty(&channels_ids->queue)) { ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, "push stream module: the $push_stream_channels_path variable is required but is not set"); return ngx_http_push_stream_send_only_header_response(r, NGX_HTTP_BAD_REQUEST, &NGX_HTTP_PUSH_STREAM_NO_CHANNEL_ID_MESSAGE); } //validate channels: name, length and quantity. check if channel exists when authorized_channels_only is on. check if channel is full of subscribers if (ngx_http_push_stream_validate_channels(r, channels_ids, &status_code, &explain_error_message) == NGX_ERROR) { return ngx_http_push_stream_send_only_header_response(r, status_code, explain_error_message); } // stream access if ((worker_subscriber = ngx_http_push_stream_subscriber_prepare_request_to_keep_connected(r)) == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } if ((sec_accept_header = ngx_http_push_stream_generate_websocket_accept_value(r, sec_key_header, ctx->temp_pool)) == NULL) { ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, "push stream module: could not generate security accept heade value"); return NGX_HTTP_INTERNAL_SERVER_ERROR; } ngx_http_push_stream_add_response_header(r, &NGX_HTTP_PUSH_STREAM_HEADER_UPGRADE, &NGX_HTTP_PUSH_STREAM_WEBSOCKET_UPGRADE); ngx_http_push_stream_add_response_header(r, &NGX_HTTP_PUSH_STREAM_HEADER_CONNECTION, &NGX_HTTP_PUSH_STREAM_WEBSOCKET_CONNECTION); ngx_http_push_stream_add_response_header(r, &NGX_HTTP_PUSH_STREAM_HEADER_SEC_WEBSOCKET_ACCEPT, sec_accept_header); r->headers_out.status_line = NGX_HTTP_PUSH_STREAM_101_STATUS_LINE; r->read_event_handler = ngx_http_push_stream_websocket_reading; ngx_http_push_stream_send_only_added_headers(r); // sending response content header if (ngx_http_push_stream_send_response_content_header(r, cf) == NGX_ERROR) { ngx_log_error(NGX_LOG_ERR, (r)->connection->log, 0, "push stream module: could not send content header to subscriber"); return NGX_HTTP_INTERNAL_SERVER_ERROR; } ngx_shmtx_lock(&shpool->mutex); rc = ngx_http_push_stream_registry_subscriber_locked(r, worker_subscriber); ngx_shmtx_unlock(&shpool->mutex); if (rc == NGX_ERROR) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } // adding subscriber to channel(s) and send backtrack messages cur = channels_ids; while ((cur = (ngx_http_push_stream_requested_channel_t *) ngx_queue_next(&cur->queue)) != channels_ids) { if (ngx_http_push_stream_subscriber_assign_channel(shpool, cf, r, cur, -1, NULL, worker_subscriber, ctx->temp_pool) != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } } if (ctx->temp_pool != NULL) { ngx_destroy_pool(ctx->temp_pool); ctx->temp_pool = NULL; } return NGX_DONE; }
static ngx_int_t ngx_http_push_stream_subscriber_handler(ngx_http_request_t *r) { ngx_http_push_stream_main_conf_t *mcf = ngx_http_get_module_main_conf(r, ngx_http_push_stream_module); ngx_http_push_stream_loc_conf_t *cf = ngx_http_get_module_loc_conf(r, ngx_http_push_stream_module); ngx_http_push_stream_subscriber_t *worker_subscriber; ngx_http_push_stream_requested_channel_t *requested_channels, *requested_channel; ngx_queue_t *q; ngx_http_push_stream_module_ctx_t *ctx; ngx_int_t tag; time_t if_modified_since; ngx_str_t *last_event_id = NULL; ngx_str_t *push_mode; ngx_flag_t polling, longpolling; ngx_int_t status_code; ngx_str_t *explain_error_message; ngx_str_t vv_allowed_origins = ngx_null_string; // add headers to support cross domain requests if (cf->allowed_origins != NULL) { ngx_http_push_stream_complex_value(r, cf->allowed_origins, &vv_allowed_origins); } if (vv_allowed_origins.len > 0) { ngx_http_push_stream_add_response_header(r, &NGX_HTTP_PUSH_STREAM_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN, &vv_allowed_origins); ngx_http_push_stream_add_response_header(r, &NGX_HTTP_PUSH_STREAM_HEADER_ACCESS_CONTROL_ALLOW_METHODS, &NGX_HTTP_PUSH_STREAM_ALLOW_GET); ngx_http_push_stream_add_response_header(r, &NGX_HTTP_PUSH_STREAM_HEADER_ACCESS_CONTROL_ALLOW_HEADERS, &NGX_HTTP_PUSH_STREAM_ALLOWED_HEADERS); } if (r->method & NGX_HTTP_OPTIONS) { return ngx_http_push_stream_send_only_header_response(r, NGX_HTTP_OK, NULL); } ngx_http_push_stream_set_expires(r, NGX_HTTP_PUSH_STREAM_EXPIRES_EPOCH, 0); // only accept GET method if (!(r->method & NGX_HTTP_GET)) { ngx_http_push_stream_add_response_header(r, &NGX_HTTP_PUSH_STREAM_HEADER_ALLOW, &NGX_HTTP_PUSH_STREAM_ALLOW_GET); return ngx_http_push_stream_send_only_header_response(r, NGX_HTTP_NOT_ALLOWED, NULL); } if ((ctx = ngx_http_push_stream_add_request_context(r)) == NULL) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "push stream module: unable to create request context"); return NGX_HTTP_INTERNAL_SERVER_ERROR; } //get channels ids and backtracks from path requested_channels = ngx_http_push_stream_parse_channels_ids_from_path(r, r->pool); if ((requested_channels == NULL) || ngx_queue_empty(&requested_channels->queue)) { ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, "push stream module: the push_stream_channels_path is required but is not set"); return ngx_http_push_stream_send_only_header_response(r, NGX_HTTP_BAD_REQUEST, &NGX_HTTP_PUSH_STREAM_NO_CHANNEL_ID_MESSAGE); } //validate channels: name, length and quantity. check if channel exists when authorized_channels_only is on. check if channel is full of subscribers if (ngx_http_push_stream_validate_channels(r, requested_channels, &status_code, &explain_error_message) == NGX_ERROR) { return ngx_http_push_stream_send_only_header_response(r, status_code, explain_error_message); } // get control values ngx_http_push_stream_get_last_received_message_values(r, &if_modified_since, &tag, &last_event_id); push_mode = ngx_http_push_stream_get_header(r, &NGX_HTTP_PUSH_STREAM_HEADER_MODE); polling = ((cf->location_type == NGX_HTTP_PUSH_STREAM_SUBSCRIBER_MODE_POLLING) || ((push_mode != NULL) && (push_mode->len == NGX_HTTP_PUSH_STREAM_MODE_POLLING.len) && (ngx_strncasecmp(push_mode->data, NGX_HTTP_PUSH_STREAM_MODE_POLLING.data, NGX_HTTP_PUSH_STREAM_MODE_POLLING.len) == 0))); longpolling = ((cf->location_type == NGX_HTTP_PUSH_STREAM_SUBSCRIBER_MODE_LONGPOLLING) || ((push_mode != NULL) && (push_mode->len == NGX_HTTP_PUSH_STREAM_MODE_LONGPOLLING.len) && (ngx_strncasecmp(push_mode->data, NGX_HTTP_PUSH_STREAM_MODE_LONGPOLLING.data, NGX_HTTP_PUSH_STREAM_MODE_LONGPOLLING.len) == 0))); if (polling || longpolling) { ngx_int_t result = ngx_http_push_stream_subscriber_polling_handler(r, requested_channels, if_modified_since, tag, last_event_id, longpolling, ctx->temp_pool); if (ctx->temp_pool != NULL) { ngx_destroy_pool(ctx->temp_pool); ctx->temp_pool = NULL; } return result; } ctx->padding = ngx_http_push_stream_get_padding_by_user_agent(r); // stream access if ((worker_subscriber = ngx_http_push_stream_subscriber_prepare_request_to_keep_connected(r)) == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } ngx_http_send_header(r); // sending response content header if (ngx_http_push_stream_send_response_content_header(r, cf) == NGX_ERROR) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "push stream module: could not send content header to subscriber"); return NGX_HTTP_INTERNAL_SERVER_ERROR; } if (ngx_http_push_stream_registry_subscriber(r, worker_subscriber) == NGX_ERROR) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } // adding subscriber to channel(s) and send old messages for (q = ngx_queue_head(&requested_channels->queue); q != ngx_queue_sentinel(&requested_channels->queue); q = ngx_queue_next(q)) { requested_channel = ngx_queue_data(q, ngx_http_push_stream_requested_channel_t, queue); if (ngx_http_push_stream_subscriber_assign_channel(mcf, cf, r, requested_channel, if_modified_since, tag, last_event_id, worker_subscriber, ctx->temp_pool) != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } } if (ctx->temp_pool != NULL) { ngx_destroy_pool(ctx->temp_pool); ctx->temp_pool = NULL; } return NGX_DONE; }