コード例 #1
0
static ngx_int_t
ngx_http_push_stream_publisher_handle_post(ngx_http_push_stream_loc_conf_t *cf, ngx_http_request_t *r, ngx_str_t *id)
{
    ngx_int_t                           rc;
    ngx_http_push_stream_channel_t     *channel = NULL;

    // check if channel id isn't equals to ALL or contain wildcard
    if ((ngx_memn2cmp(id->data, NGX_HTTP_PUSH_STREAM_ALL_CHANNELS_INFO_ID.data, id->len, NGX_HTTP_PUSH_STREAM_ALL_CHANNELS_INFO_ID.len) == 0) || (ngx_strchr(id->data, '*') != NULL)) {
        return ngx_http_push_stream_send_only_header_response(r, NGX_HTTP_FORBIDDEN, &NGX_HTTP_PUSH_STREAM_NO_CHANNEL_ID_NOT_AUTHORIZED_MESSAGE);
    }

    // create the channel if doesn't exist
    channel = ngx_http_push_stream_get_channel(id, r->connection->log, cf);
    if (channel == NULL) {
        ngx_log_error(NGX_LOG_ERR, (r)->connection->log, 0, "push stream module: unable to allocate memory for new channel");
        return ngx_http_push_stream_send_only_header_response(r, NGX_HTTP_INTERNAL_SERVER_ERROR, NULL);
    }

    if (channel == NGX_HTTP_PUSH_STREAM_NUMBER_OF_CHANNELS_EXCEEDED) {
        ngx_log_error(NGX_LOG_ERR, (r)->connection->log, 0, "push stream module: number of channels were exceeded");
        return ngx_http_push_stream_send_only_header_response(r, NGX_HTTP_FORBIDDEN, &NGX_HTTP_PUSH_STREAM_NUMBER_OF_CHANNELS_EXCEEDED_MESSAGE);
    }

    /*
     * Instruct ngx_http_read_subscriber_request_body to store the request
     * body entirely in a memory buffer or in a file.
     */
    r->request_body_in_single_buf = 0;
    r->request_body_in_persistent_file = 1;
    r->request_body_in_clean_file = 0;
    r->request_body_file_log_level = 0;

    // parse the body message and return
    rc = ngx_http_read_client_request_body(r, ngx_http_push_stream_publisher_body_handler);
    if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
        return rc;
    }

    return NGX_DONE;
}
static ngx_int_t
ngx_http_push_stream_validate_channels(ngx_http_request_t *r, ngx_http_push_stream_requested_channel_t *channels_ids, ngx_int_t *status_code, ngx_str_t **explain_error_message)
{
    ngx_http_push_stream_main_conf_t               *mcf = ngx_http_push_stream_module_main_conf;
    ngx_http_push_stream_loc_conf_t                *cf = ngx_http_get_module_loc_conf(r, ngx_http_push_stream_module);
    ngx_http_push_stream_requested_channel_t       *cur = channels_ids;
    ngx_uint_t                                      subscribed_channels_qtd = 0;
    ngx_uint_t                                      subscribed_broadcast_channels_qtd = 0;
    ngx_flag_t                                      is_broadcast_channel;
    ngx_http_push_stream_channel_t                 *channel;

    while ((cur = (ngx_http_push_stream_requested_channel_t *) ngx_queue_next(&cur->queue)) != channels_ids) {
        // could not be ALL channel or contain wildcard
        if ((ngx_memn2cmp(cur->id->data, NGX_HTTP_PUSH_STREAM_ALL_CHANNELS_INFO_ID.data, cur->id->len, NGX_HTTP_PUSH_STREAM_ALL_CHANNELS_INFO_ID.len) == 0) || (ngx_strchr(cur->id->data, '*') != NULL)) {
            *status_code = NGX_HTTP_FORBIDDEN;
            *explain_error_message = (ngx_str_t *) &NGX_HTTP_PUSH_STREAM_NO_CHANNEL_ID_NOT_AUTHORIZED_MESSAGE;
            return NGX_ERROR;
        }

        // could not have a large size
        if ((mcf->max_channel_id_length != NGX_CONF_UNSET_UINT) && (cur->id->len > mcf->max_channel_id_length)) {
            ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, "push stream module: channel id is larger than allowed %d", cur->id->len);
            *status_code = NGX_HTTP_BAD_REQUEST;
            *explain_error_message = (ngx_str_t *) &NGX_HTTP_PUSH_STREAM_TOO_LARGE_CHANNEL_ID_MESSAGE;
            return NGX_ERROR;
        }

        // count subscribed channel and broadcasts
        subscribed_channels_qtd++;
        is_broadcast_channel = 0;
        if ((mcf->broadcast_channel_prefix.len > 0) && (ngx_strncmp(cur->id->data, mcf->broadcast_channel_prefix.data, mcf->broadcast_channel_prefix.len) == 0)) {
            is_broadcast_channel = 1;
            subscribed_broadcast_channels_qtd++;
        }

        // check if channel exists when authorized_channels_only is on
        if (cf->authorized_channels_only && !is_broadcast_channel && (((channel = ngx_http_push_stream_find_channel(cur->id, r->connection->log)) == NULL) || (channel->stored_messages == 0))) {
            *status_code = NGX_HTTP_FORBIDDEN;
            *explain_error_message = (ngx_str_t *) &NGX_HTTP_PUSH_STREAM_CANNOT_CREATE_CHANNELS;
            return NGX_ERROR;
        }

        // check if channel is full of subscribers
        if ((mcf->max_subscribers_per_channel != NGX_CONF_UNSET_UINT) && (((channel = ngx_http_push_stream_find_channel(cur->id, r->connection->log)) != NULL) && (channel->subscribers >= mcf->max_subscribers_per_channel))) {
            *status_code = NGX_HTTP_FORBIDDEN;
            *explain_error_message = (ngx_str_t *) &NGX_HTTP_PUSH_STREAM_TOO_SUBSCRIBERS_PER_CHANNEL;
            return NGX_ERROR;
        }
    }

    // check if number of subscribed broadcast channels is acceptable
    if ((cf->broadcast_channel_max_qtd != NGX_CONF_UNSET_UINT) && (subscribed_broadcast_channels_qtd > 0) && ((subscribed_broadcast_channels_qtd > cf->broadcast_channel_max_qtd) || (subscribed_broadcast_channels_qtd == subscribed_channels_qtd))) {
        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "push stream module: max subscribed broadcast channels exceeded");
        *status_code = NGX_HTTP_FORBIDDEN;
        *explain_error_message = (ngx_str_t *) &NGX_HTTP_PUSH_STREAM_TOO_MUCH_BROADCAST_CHANNELS;
        return NGX_ERROR;
    }

    // create the channels in advance, if doesn't exist, to ensure max number of channels in the server
    cur = channels_ids;
    while ((cur = (ngx_http_push_stream_requested_channel_t *) ngx_queue_next(&cur->queue)) != channels_ids) {
        channel = ngx_http_push_stream_get_channel(cur->id, r->connection->log, cf);
        if (channel == NULL) {
            ngx_log_error(NGX_LOG_ERR, (r)->connection->log, 0, "push stream module: unable to allocate memory for new channel");
            *status_code = NGX_HTTP_INTERNAL_SERVER_ERROR;
            *explain_error_message = NULL;
            return NGX_ERROR;
        }

        if (channel == NGX_HTTP_PUSH_STREAM_NUMBER_OF_CHANNELS_EXCEEDED) {
            ngx_log_error(NGX_LOG_ERR, (r)->connection->log, 0, "push stream module: number of channels were exceeded");
            *status_code = NGX_HTTP_FORBIDDEN;
            *explain_error_message = (ngx_str_t *) &NGX_HTTP_PUSH_STREAM_NUMBER_OF_CHANNELS_EXCEEDED_MESSAGE;
            return NGX_ERROR;
        }
    }

    return NGX_OK;
}
static ngx_int_t
ngx_http_push_stream_publisher_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_module_ctx_t  *ctx;

    ngx_http_push_stream_requested_channel_t       *requested_channels, *requested_channel;
    ngx_str_t                                       vv_allowed_origins = ngx_null_string;
    ngx_queue_t                                     *q;

    ngx_http_push_stream_set_expires(r, NGX_HTTP_PUSH_STREAM_EXPIRES_EPOCH, 0);


    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);
        const ngx_str_t *header_value = (cf->location_type == NGX_HTTP_PUSH_STREAM_PUBLISHER_MODE_ADMIN) ? &NGX_HTTP_PUSH_STREAM_ALLOW_GET_POST_PUT_DELETE_METHODS : &NGX_HTTP_PUSH_STREAM_ALLOW_GET_POST_PUT_METHODS;
        ngx_http_push_stream_add_response_header(r, &NGX_HTTP_PUSH_STREAM_HEADER_ACCESS_CONTROL_ALLOW_METHODS, header_value);
        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, POST, PUT and DELETE methods if enable publisher administration
    if ((cf->location_type == NGX_HTTP_PUSH_STREAM_PUBLISHER_MODE_ADMIN) && !(r->method & (NGX_HTTP_GET|NGX_HTTP_POST|NGX_HTTP_PUT|NGX_HTTP_DELETE))) {
        ngx_http_push_stream_add_response_header(r, &NGX_HTTP_PUSH_STREAM_HEADER_ALLOW, &NGX_HTTP_PUSH_STREAM_ALLOW_GET_POST_PUT_DELETE_METHODS);
        return ngx_http_push_stream_send_only_header_response(r, NGX_HTTP_NOT_ALLOWED, NULL);
    }

    // only accept GET, POST and PUT methods if NOT enable publisher administration
    if ((cf->location_type != NGX_HTTP_PUSH_STREAM_PUBLISHER_MODE_ADMIN) && !(r->method & (NGX_HTTP_GET|NGX_HTTP_POST|NGX_HTTP_PUT))) {
        ngx_http_push_stream_add_response_header(r, &NGX_HTTP_PUSH_STREAM_HEADER_ALLOW, &NGX_HTTP_PUSH_STREAM_ALLOW_GET_POST_PUT_METHODS);
        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_push_stream_send_only_header_response(r, NGX_HTTP_INTERNAL_SERVER_ERROR, NULL);
    }

    //get channels ids
    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);
    }

    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);

        // check if channel id isn't equals to ALL or contain wildcard
        if ((ngx_memn2cmp(requested_channel->id->data, NGX_HTTP_PUSH_STREAM_ALL_CHANNELS_INFO_ID.data, requested_channel->id->len, NGX_HTTP_PUSH_STREAM_ALL_CHANNELS_INFO_ID.len) == 0) || (ngx_strchr(requested_channel->id->data, '*') != NULL)) {
            return ngx_http_push_stream_send_only_header_response(r, NGX_HTTP_FORBIDDEN, &NGX_HTTP_PUSH_STREAM_CHANNEL_ID_NOT_AUTHORIZED_MESSAGE);
        }

        // could not have a large size
        if ((mcf->max_channel_id_length != NGX_CONF_UNSET_UINT) && (requested_channel->id->len > mcf->max_channel_id_length)) {
            ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, "push stream module: channel id is larger than allowed %d", requested_channel->id->len);
            return ngx_http_push_stream_send_only_header_response(r, NGX_HTTP_BAD_REQUEST, &NGX_HTTP_PUSH_STREAM_TOO_LARGE_CHANNEL_ID_MESSAGE);
        }

        if (r->method & (NGX_HTTP_POST|NGX_HTTP_PUT)) {
            // create the channel if doesn't exist
            requested_channel->channel = ngx_http_push_stream_get_channel(requested_channel->id, r->connection->log, mcf);
            if (requested_channel->channel == NULL) {
                return ngx_http_push_stream_send_only_header_response(r, NGX_HTTP_INTERNAL_SERVER_ERROR, NULL);
            }

            if (requested_channel->channel == NGX_HTTP_PUSH_STREAM_NUMBER_OF_CHANNELS_EXCEEDED) {
                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "push stream module: number of channels were exceeded");
                return ngx_http_push_stream_send_only_header_response(r, NGX_HTTP_FORBIDDEN, &NGX_HTTP_PUSH_STREAM_NUMBER_OF_CHANNELS_EXCEEDED_MESSAGE);
            }
        } else {
            requested_channel->channel = ngx_http_push_stream_find_channel(requested_channel->id, r->connection->log, mcf);
        }

        if ((r->method != NGX_HTTP_GET) && (requested_channel->channel != NULL) && requested_channel->channel->for_events) {
            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "push stream module: only internal routines can change events channel");
            return ngx_http_push_stream_send_only_header_response(r, NGX_HTTP_FORBIDDEN, &NGX_HTTP_PUSH_STREAM_INTERNAL_ONLY_EVENTS_CHANNEL_MESSAGE);
        }
    }

    ctx->requested_channels = requested_channels;

    if (r->method & (NGX_HTTP_POST|NGX_HTTP_PUT)) {
        return ngx_http_push_stream_publisher_handle_after_read_body(r, ngx_http_push_stream_publisher_body_handler);
    }

    if ((cf->location_type == NGX_HTTP_PUSH_STREAM_PUBLISHER_MODE_ADMIN) && (r->method == NGX_HTTP_DELETE)) {
        return ngx_http_push_stream_publisher_handle_after_read_body(r, ngx_http_push_stream_publisher_delete_handler);
    }

    return ngx_http_push_stream_send_response_channels_info_detailed(r, requested_channels);
}
static ngx_int_t
ngx_http_push_stream_validate_channels(ngx_http_request_t *r, ngx_http_push_stream_requested_channel_t *requested_channels, ngx_int_t *status_code, ngx_str_t **explain_error_message)
{
    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_requested_channel_t       *requested_channel;
    ngx_queue_t                                    *q;
    ngx_uint_t                                      subscribed_channels_qtd = 0;
    ngx_uint_t                                      subscribed_wildcard_channels_qtd = 0;
    ngx_flag_t                                      is_wildcard_channel;

    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);
        // could not be ALL channel or contain wildcard
        if ((ngx_memn2cmp(requested_channel->id->data, NGX_HTTP_PUSH_STREAM_ALL_CHANNELS_INFO_ID.data, requested_channel->id->len, NGX_HTTP_PUSH_STREAM_ALL_CHANNELS_INFO_ID.len) == 0) || (ngx_strchr(requested_channel->id->data, '*') != NULL)) {
            *status_code = NGX_HTTP_FORBIDDEN;
            *explain_error_message = (ngx_str_t *) &NGX_HTTP_PUSH_STREAM_CHANNEL_ID_NOT_AUTHORIZED_MESSAGE;
            return NGX_ERROR;
        }

        // could not have a large size
        if ((mcf->max_channel_id_length != NGX_CONF_UNSET_UINT) && (requested_channel->id->len > mcf->max_channel_id_length)) {
            ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, "push stream module: channel id is larger than allowed %d", requested_channel->id->len);
            *status_code = NGX_HTTP_BAD_REQUEST;
            *explain_error_message = (ngx_str_t *) &NGX_HTTP_PUSH_STREAM_TOO_LARGE_CHANNEL_ID_MESSAGE;
            return NGX_ERROR;
        }

        // count subscribed normal and wildcard channels
        subscribed_channels_qtd++;
        is_wildcard_channel = 0;
        if ((mcf->wildcard_channel_prefix.len > 0) && (ngx_strncmp(requested_channel->id->data, mcf->wildcard_channel_prefix.data, mcf->wildcard_channel_prefix.len) == 0)) {
            is_wildcard_channel = 1;
            subscribed_wildcard_channels_qtd++;
        }

        requested_channel->channel = ngx_http_push_stream_find_channel(requested_channel->id, r->connection->log, mcf);

        // check if channel exists when authorized_channels_only is on
        if (cf->authorized_channels_only && !is_wildcard_channel && ((requested_channel->channel == NULL) || (requested_channel->channel->stored_messages == 0))) {
            *status_code = NGX_HTTP_FORBIDDEN;
            *explain_error_message = (ngx_str_t *) &NGX_HTTP_PUSH_STREAM_CANNOT_CREATE_CHANNELS;
            return NGX_ERROR;
        }

        // check if channel is full of subscribers
        if ((mcf->max_subscribers_per_channel != NGX_CONF_UNSET_UINT) && ((requested_channel->channel != NULL) && (requested_channel->channel->subscribers >= mcf->max_subscribers_per_channel))) {
            *status_code = NGX_HTTP_FORBIDDEN;
            *explain_error_message = (ngx_str_t *) &NGX_HTTP_PUSH_STREAM_TOO_SUBSCRIBERS_PER_CHANNEL;
            return NGX_ERROR;
        }

        // check if is allowed to connect to events channel
        if (!cf->allow_connections_to_events_channel && (requested_channel->channel != NULL) && requested_channel->channel->for_events) {
            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "push stream module: subscription to events channel is not allowed");
            *status_code = NGX_HTTP_FORBIDDEN;
            *explain_error_message = (ngx_str_t *) &NGX_HTTP_PUSH_STREAM_SUBSCRIPTION_EVENTS_CHANNEL_FORBIDDEN_MESSAGE;
            return NGX_ERROR;
        }
    }

    // check if number of subscribed wildcard channels is acceptable
    if ((cf->wildcard_channel_max_qtd != NGX_CONF_UNSET_UINT) && (subscribed_wildcard_channels_qtd > 0) && ((subscribed_wildcard_channels_qtd > cf->wildcard_channel_max_qtd) || (subscribed_wildcard_channels_qtd == subscribed_channels_qtd))) {
        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "push stream module: max subscribed wildcard channels exceeded");
        *status_code = NGX_HTTP_FORBIDDEN;
        *explain_error_message = (ngx_str_t *) &NGX_HTTP_PUSH_STREAM_TOO_MUCH_WILDCARD_CHANNELS;
        return NGX_ERROR;
    }

    // create the channels in advance, if doesn't exist, to ensure max number of channels in the server
    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 (requested_channel->channel != NULL) {
            continue;
        }

        requested_channel->channel = ngx_http_push_stream_get_channel(requested_channel->id, r->connection->log, mcf);
        if (requested_channel->channel == NULL) {
            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "push stream module: unable to allocate memory for new channel");
            *status_code = NGX_HTTP_INTERNAL_SERVER_ERROR;
            *explain_error_message = (ngx_str_t *) &NGX_HTTP_PUSH_STREAM_EMPTY;
            return NGX_ERROR;
        }

        if (requested_channel->channel == NGX_HTTP_PUSH_STREAM_NUMBER_OF_CHANNELS_EXCEEDED) {
            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "push stream module: number of channels were exceeded");
            *status_code = NGX_HTTP_FORBIDDEN;
            *explain_error_message = (ngx_str_t *) &NGX_HTTP_PUSH_STREAM_NUMBER_OF_CHANNELS_EXCEEDED_MESSAGE;
            return NGX_ERROR;
        }
    }

    return NGX_OK;
}