static ngx_str_t * ngx_http_push_stream_generate_websocket_accept_value(ngx_http_request_t *r, ngx_str_t *sec_key, ngx_pool_t *temp_pool) { #if (NGX_HAVE_SHA1) ngx_str_t *sha1_signed, *accept_value; ngx_sha1_t sha1; sha1_signed = ngx_http_push_stream_create_str(temp_pool, NGX_HTTP_PUSH_STREAM_WEBSOCKET_SHA1_SIGNED_HASH_LENGTH); accept_value = ngx_http_push_stream_create_str(r->pool, ngx_base64_encoded_length(NGX_HTTP_PUSH_STREAM_WEBSOCKET_SHA1_SIGNED_HASH_LENGTH)); if ((sha1_signed == NULL) || (accept_value == NULL)) { return NULL; } ngx_sha1_init(&sha1); ngx_sha1_update(&sha1, sec_key->data, sec_key->len); ngx_sha1_update(&sha1, NGX_HTTP_PUSH_STREAM_WEBSOCKET_SIGN_KEY.data, NGX_HTTP_PUSH_STREAM_WEBSOCKET_SIGN_KEY.len); ngx_sha1_final(sha1_signed->data, &sha1); ngx_encode_base64(accept_value, sha1_signed); return accept_value; #else return NULL; #endif }
static ngx_str_t * ngx_http_push_stream_get_channel_id(ngx_http_request_t *r, ngx_http_push_stream_loc_conf_t *cf) { ngx_http_variable_value_t *vv = ngx_http_get_indexed_variable(r, cf->index_channel_id); ngx_str_t *id; if (vv == NULL || vv->not_found || vv->len == 0) { return NGX_HTTP_PUSH_STREAM_UNSET_CHANNEL_ID; } // maximum length limiter for channel id if ((ngx_http_push_stream_module_main_conf->max_channel_id_length != NGX_CONF_UNSET_UINT) && (vv->len > ngx_http_push_stream_module_main_conf->max_channel_id_length)) { ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, "push stream module: channel id is larger than allowed %d", vv->len); return NGX_HTTP_PUSH_STREAM_TOO_LARGE_CHANNEL_ID; } if ((id = ngx_http_push_stream_create_str(r->pool, vv->len)) == NULL) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "push stream module: unable to allocate memory for $push_stream_channel_id string"); return NULL; } ngx_memcpy(id->data, vv->data, vv->len); return id; }
static ngx_int_t ngx_http_push_stream_send_response_all_channels_info_summarized(ngx_http_request_t *r) { ngx_uint_t len; ngx_str_t *currenttime, *hostname, *format, *text; u_char *subscribers_by_workers, *start; int i, j, used_slots; ngx_http_push_stream_shm_data_t *data = (ngx_http_push_stream_shm_data_t *) ngx_http_push_stream_shm_zone->data; ngx_http_push_stream_worker_data_t *worker_data; ngx_http_push_stream_content_subtype_t *subtype; ngx_pool_t *temp_pool = ngx_http_push_stream_get_temp_pool(r); subtype = ngx_http_push_stream_match_channel_info_format_and_content_type(r, 1); currenttime = ngx_http_push_stream_get_formatted_current_time(temp_pool); hostname = ngx_http_push_stream_get_formatted_hostname(temp_pool); used_slots = 0; for(i = 0; i < NGX_MAX_PROCESSES; i++) { if (data->ipc[i].pid > 0) { used_slots++; } } len = (subtype->format_summarized_worker_item->len > subtype->format_summarized_worker_last_item->len) ? subtype->format_summarized_worker_item->len : subtype->format_summarized_worker_last_item->len; len = used_slots * (3*NGX_INT_T_LEN + len - 8); //minus 8 sprintf if ((subscribers_by_workers = ngx_pcalloc(temp_pool, len)) == NULL) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "Failed to allocate memory to write workers statistics."); return NGX_HTTP_INTERNAL_SERVER_ERROR; } start = subscribers_by_workers; for (i = 0, j = 0; (i < used_slots) && (j < NGX_MAX_PROCESSES); j++) { worker_data = data->ipc + j; if (worker_data->pid > 0) { format = (i < used_slots - 1) ? subtype->format_summarized_worker_item : subtype->format_summarized_worker_last_item; start = ngx_sprintf(start, (char *) format->data, worker_data->pid, worker_data->subscribers, ngx_time() - worker_data->startup); i++; } } *start = '\0'; len = 4*NGX_INT_T_LEN + subtype->format_summarized->len + hostname->len + currenttime->len + ngx_strlen(subscribers_by_workers) - 21;// minus 21 sprintf if ((text = ngx_http_push_stream_create_str(temp_pool, len)) == NULL) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "Failed to allocate response buffer."); return NGX_HTTP_INTERNAL_SERVER_ERROR; } ngx_sprintf(text->data, (char *) subtype->format_summarized->data, hostname->data, currenttime->data, data->channels, data->broadcast_channels, data->published_messages, data->subscribers, ngx_time() - data->startup, subscribers_by_workers); text->len = ngx_strlen(text->data); return ngx_http_push_stream_send_response(r, text, subtype->content_type, NGX_HTTP_OK); }
ngx_http_push_stream_requested_channel_t * ngx_http_push_stream_parse_channels_ids_from_path(ngx_http_request_t *r, ngx_pool_t *pool) { 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_variable_value_t *vv_channels_path = ngx_http_get_indexed_variable(r, cf->index_channels_path); ngx_http_push_stream_requested_channel_t *channels_ids, *cur; ngx_str_t aux; int captures[15]; ngx_int_t n; if (vv_channels_path == NULL || vv_channels_path->not_found || vv_channels_path->len == 0) { return NULL; } if ((channels_ids = ngx_pcalloc(pool, sizeof(ngx_http_push_stream_requested_channel_t))) == NULL) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "push stream module: unable to allocate memory for channels_ids queue"); return NULL; } ngx_queue_init(&channels_ids->queue); // doing the parser of given channel path aux.data = vv_channels_path->data; do { aux.len = vv_channels_path->len - (aux.data - vv_channels_path->data); if ((n = ngx_regex_exec(mcf->backtrack_parser_regex, &aux, captures, 15)) >= 0) { if ((cur = ngx_pcalloc(pool, sizeof(ngx_http_push_stream_requested_channel_t))) == NULL) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "push stream module: unable to allocate memory for channel_id item"); return NULL; } if ((cur->id = ngx_http_push_stream_create_str(pool, captures[0])) == NULL) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "push stream module: unable to allocate memory for channel_id string"); return NULL; } ngx_memcpy(cur->id->data, aux.data, captures[0]); cur->backtrack_messages = 0; if (captures[7] > captures[6]) { cur->backtrack_messages = ngx_atoi(aux.data + captures[6], captures[7] - captures[6]); } ngx_queue_insert_tail(&channels_ids->queue, &cur->queue); aux.data = aux.data + captures[1]; } } while ((n != NGX_REGEX_NO_MATCHED) && (aux.data < (vv_channels_path->data + vv_channels_path->len))); return channels_ids; }
static ngx_str_t * ngx_http_push_stream_channel_info_formatted(ngx_pool_t *pool, const ngx_str_t *format, ngx_str_t *id, ngx_uint_t published_messages, ngx_uint_t stored_messages, ngx_uint_t subscribers) { ngx_str_t *text; ngx_uint_t len; if ((format == NULL) || (id == NULL)) { return NULL; } len = 3*NGX_INT_T_LEN + format->len + id->len - 11;// minus 11 sprintf if ((text = ngx_http_push_stream_create_str(pool, len)) == NULL) { return NULL; } ngx_sprintf(text->data, (char *) format->data, id->data, published_messages, stored_messages, subscribers); text->len = ngx_strlen(text->data); return text; }
static ngx_int_t ngx_http_push_stream_postconfig(ngx_conf_t *cf) { if ((ngx_http_push_stream_padding_max_len > 0) && (ngx_http_push_stream_module_paddings_chunks == NULL)) { ngx_uint_t steps = ngx_http_push_stream_padding_max_len / 100; if ((ngx_http_push_stream_module_paddings_chunks = ngx_palloc(cf->pool, sizeof(ngx_str_t) * (steps + 1))) == NULL) { ngx_conf_log_error(NGX_LOG_ERR, cf, 0, "push stream module: unable to allocate memory to create padding messages"); return NGX_ERROR; } u_int padding_max_len = ngx_http_push_stream_padding_max_len + ((ngx_http_push_stream_padding_max_len % 2) ? 1 : 0); ngx_str_t *aux = ngx_http_push_stream_create_str(cf->pool, padding_max_len); if (aux == NULL) { ngx_conf_log_error(NGX_LOG_ERR, cf, 0, "push stream module: unable to allocate memory to create padding messages value"); return NGX_ERROR; } while (padding_max_len > 0) { padding_max_len -= 2; ngx_memcpy(aux->data + padding_max_len, CRLF, 2); } ngx_int_t i, len = ngx_http_push_stream_padding_max_len; for (i = steps; i >= 0; i--) { ngx_str_t *padding = ngx_pcalloc(cf->pool, sizeof(ngx_str_t)); if ((*(ngx_http_push_stream_module_paddings_chunks + i) = padding) == NULL) { ngx_conf_log_error(NGX_LOG_ERR, cf, 0, "push stream module: unable to allocate memory to create padding messages"); return NGX_ERROR; } padding->data = aux->data; padding->len = len; len = i * 100; } } return NGX_OK; }
static ngx_int_t ngx_http_push_stream_send_response_all_channels_info_detailed(ngx_http_request_t *r, ngx_str_t *prefix) { ngx_int_t rc, content_len = 0; ngx_chain_t *chain, *first = NULL, *last = NULL; ngx_str_t *currenttime, *hostname, *text, *header_response; ngx_queue_t queue_channel_info; ngx_queue_t *cur, *next; ngx_http_push_stream_shm_data_t *data = (ngx_http_push_stream_shm_data_t *) ngx_http_push_stream_shm_zone->data; ngx_slab_pool_t *shpool = (ngx_slab_pool_t *) ngx_http_push_stream_shm_zone->shm.addr; ngx_http_push_stream_content_subtype_t *subtype = ngx_http_push_stream_match_channel_info_format_and_content_type(r, 1); ngx_pool_t *temp_pool = ngx_http_push_stream_get_temp_pool(r); const ngx_str_t *format; const ngx_str_t *head = subtype->format_group_head; const ngx_str_t *tail = subtype->format_group_tail; ngx_queue_init(&queue_channel_info); ngx_shmtx_lock(&shpool->mutex); ngx_http_push_stream_rbtree_walker_channel_info_locked(&data->tree, temp_pool, data->tree.root, &queue_channel_info, prefix); ngx_shmtx_unlock(&shpool->mutex); // format content body cur = ngx_queue_head(&queue_channel_info); while (cur != &queue_channel_info) { next = ngx_queue_next(cur); ngx_http_push_stream_channel_info_t *channel_info = (ngx_http_push_stream_channel_info_t *) cur; if ((chain = ngx_http_push_stream_get_buf(r)) == NULL) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "push stream module: unable to allocate memory for response channels info"); return NGX_HTTP_INTERNAL_SERVER_ERROR; } format = (next != &queue_channel_info) ? subtype->format_group_item : subtype->format_group_last_item; if ((text = ngx_http_push_stream_channel_info_formatted(temp_pool, format, &channel_info->id, channel_info->published_messages, channel_info->stored_messages, channel_info->subscribers)) == NULL) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "push stream module: unable to allocate memory to format channel info"); return NGX_HTTP_INTERNAL_SERVER_ERROR; } chain->buf->last_buf = 0; chain->buf->memory = 1; chain->buf->pos = text->data; chain->buf->last = text->data + text->len; chain->buf->start = chain->buf->pos; chain->buf->end = chain->buf->last; content_len += text->len; if (first == NULL) { first = chain; } if (last != NULL) { last->next = chain; } last = chain; cur = next; } // get formatted current time currenttime = ngx_http_push_stream_get_formatted_current_time(temp_pool); // get formatted hostname hostname = ngx_http_push_stream_get_formatted_hostname(temp_pool); // format content header if ((header_response = ngx_http_push_stream_create_str(temp_pool, head->len + hostname->len + currenttime->len + NGX_INT_T_LEN)) == NULL) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "push stream module: unable to allocate memory for response channels info"); return NGX_HTTP_INTERNAL_SERVER_ERROR; } ngx_sprintf(header_response->data, (char *) head->data, hostname->data, currenttime->data, data->channels, data->broadcast_channels, ngx_time() - data->startup); header_response->len = ngx_strlen(header_response->data); content_len += header_response->len + tail->len; r->headers_out.content_type.len = subtype->content_type->len; r->headers_out.content_type.data = subtype->content_type->data; r->headers_out.content_length_n = content_len; r->headers_out.status = NGX_HTTP_OK; rc = ngx_http_send_header(r); if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) { return rc; } // send content header ngx_http_push_stream_send_response_text(r, header_response->data, header_response->len,0); // send content body if (first != NULL) { ngx_http_push_stream_output_filter(r, first); } // send content footer return ngx_http_push_stream_send_response_text(r, tail->data, tail->len, 1); }
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); }
static ngx_int_t ngx_http_push_stream_send_response_channels_info(ngx_http_request_t *r, ngx_queue_t *queue_channel_info) { ngx_int_t rc, content_len = 0; ngx_chain_t *chain, *first = NULL, *last = NULL; ngx_str_t *currenttime, *hostname, *text, *header_response; ngx_queue_t *q; ngx_http_push_stream_main_conf_t *mcf = ngx_http_get_module_main_conf(r, ngx_http_push_stream_module); ngx_http_push_stream_shm_data_t *data = mcf->shm_data; ngx_http_push_stream_content_subtype_t *subtype = ngx_http_push_stream_match_channel_info_format_and_content_type(r, 1); const ngx_str_t *format; const ngx_str_t *head = subtype->format_group_head; const ngx_str_t *tail = subtype->format_group_tail; // format content body for (q = ngx_queue_head(queue_channel_info); q != ngx_queue_sentinel(queue_channel_info); q = ngx_queue_next(q)) { ngx_http_push_stream_channel_info_t *channel_info = ngx_queue_data(q, ngx_http_push_stream_channel_info_t, queue); if ((chain = ngx_http_push_stream_get_buf(r)) == NULL) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "push stream module: unable to allocate memory for response channels info"); return NGX_HTTP_INTERNAL_SERVER_ERROR; } format = (q != ngx_queue_last(queue_channel_info)) ? subtype->format_group_item : subtype->format_group_last_item; if ((text = ngx_http_push_stream_channel_info_formatted(r->pool, format, &channel_info->id, channel_info->published_messages, channel_info->stored_messages, channel_info->subscribers)) == NULL) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "push stream module: unable to allocate memory to format channel info"); return NGX_HTTP_INTERNAL_SERVER_ERROR; } chain->buf->last_buf = 0; chain->buf->memory = 1; chain->buf->temporary = 0; chain->buf->pos = text->data; chain->buf->last = text->data + text->len; chain->buf->start = chain->buf->pos; chain->buf->end = chain->buf->last; content_len += text->len; if (first == NULL) { first = chain; } if (last != NULL) { last->next = chain; } last = chain; } // get formatted current time currenttime = ngx_http_push_stream_get_formatted_current_time(r->pool); // get formatted hostname hostname = ngx_http_push_stream_get_formatted_hostname(r->pool); // format content header if ((header_response = ngx_http_push_stream_create_str(r->pool, head->len + hostname->len + currenttime->len + NGX_INT_T_LEN)) == NULL) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "push stream module: unable to allocate memory for response channels info"); return NGX_HTTP_INTERNAL_SERVER_ERROR; } ngx_sprintf(header_response->data, (char *) head->data, hostname->data, currenttime->data, data->channels, data->wildcard_channels, ngx_time() - data->startup); header_response->len = ngx_strlen(header_response->data); content_len += header_response->len + tail->len; r->headers_out.content_type_len = subtype->content_type->len; r->headers_out.content_type = *subtype->content_type; r->headers_out.content_length_n = content_len; r->headers_out.status = NGX_HTTP_OK; rc = ngx_http_send_header(r); if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) { return rc; } // send content header ngx_http_push_stream_send_response_text(r, header_response->data, header_response->len,0); // send content body if (first != NULL) { ngx_http_push_stream_output_filter(r, first); } // send content footer return ngx_http_push_stream_send_response_text(r, tail->data, tail->len, 1); }