ngx_int_t ngx_http_vod_parse_uri_file_name( ngx_http_request_t* r, u_char* start_pos, u_char* end_pos, uint32_t flags, request_params_t* result) { uint32_t default_tracks_mask; uint32_t* tracks_mask; uint32_t* end_mask; uint32_t* cur_mask; uint32_t masks_per_sequence; uint32_t sequence_index; uint32_t clip_index; uint32_t media_type; language_id_t lang_id; default_tracks_mask = (flags & PARSE_FILE_NAME_MULTI_STREAMS_PER_TYPE) ? 0xffffffff : 1; for (media_type = 0; media_type < MEDIA_TYPE_COUNT; media_type++) { result->tracks_mask[media_type] = default_tracks_mask; } result->sequences_mask = 0xffffffff; result->clip_index = INVALID_CLIP_INDEX; // segment index if ((flags & PARSE_FILE_NAME_EXPECT_SEGMENT_INDEX) != 0) { if (start_pos < end_pos && *start_pos == '-') { start_pos++; // skip the - } start_pos = parse_utils_extract_uint32_token(start_pos, end_pos, &result->segment_index); if (result->segment_index <= 0) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "ngx_http_vod_parse_uri_file_name: failed to parse segment index"); return NGX_HTTP_BAD_REQUEST; } result->segment_index--; // convert to 0-based } skip_dash(start_pos, end_pos); // clip index if (*start_pos == 'c') { start_pos++; // skip the c start_pos = parse_utils_extract_uint32_token(start_pos, end_pos, &clip_index); if (clip_index <= 0) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "ngx_http_vod_parse_uri_file_name: failed to parse clip index"); return NGX_HTTP_BAD_REQUEST; } result->clip_index = clip_index - 1; skip_dash(start_pos, end_pos); } // sequence id if (*start_pos == 's') { start_pos++; // skip the s result->sequence_id.data = start_pos; while (start_pos < end_pos && *start_pos != '-') { start_pos++; } result->sequence_id.len = start_pos - result->sequence_id.data; skip_dash(start_pos, end_pos); } // sequence (file) index if (*start_pos == 'f') { tracks_mask = result->tracks_mask; masks_per_sequence = 0; result->sequences_mask = 0; for (;;) { start_pos++; // skip the f if (start_pos >= end_pos || *start_pos < '1' || *start_pos > '9') { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "ngx_http_vod_parse_uri_file_name: missing index following sequence selector"); return NGX_HTTP_BAD_REQUEST; } sequence_index = *start_pos - '0'; start_pos++; // skip the digit if (start_pos < end_pos && *start_pos >= '0' && *start_pos <= '9') { sequence_index = sequence_index * 10 + *start_pos - '0'; if (sequence_index > MAX_SEQUENCES) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "ngx_http_vod_parse_uri_file_name: sequence index too big"); return NGX_HTTP_BAD_REQUEST; } start_pos++; // skip the digit } sequence_index--; // Note: sequence_index cannot be 0 here result->sequences_mask |= (1 << sequence_index); skip_dash(start_pos, end_pos); if (*start_pos == 'v' || *start_pos == 'a') { start_pos = ngx_http_vod_extract_track_tokens( start_pos, end_pos, tracks_mask + masks_per_sequence * sequence_index); if (start_pos == NULL) { return NGX_OK; } } if (*start_pos != 'f') { break; } if (result->sequence_tracks_mask != NULL) { continue; } // more than one sequence, allocate the per sequence tracks mask result->sequence_tracks_mask = ngx_palloc(r->pool, sizeof(result->sequence_tracks_mask[0]) * MEDIA_TYPE_COUNT * MAX_SEQUENCES); if (result->sequence_tracks_mask == NULL) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ngx_http_vod_parse_uri_file_name: ngx_palloc failed"); return NGX_HTTP_INTERNAL_SERVER_ERROR; } // initialize the mask with the default cur_mask = result->sequence_tracks_mask; end_mask = cur_mask + MEDIA_TYPE_COUNT * MAX_SEQUENCES; for (; cur_mask < end_mask; cur_mask++) { *cur_mask = default_tracks_mask; } // copy the currently parsed mask to its place tracks_mask = result->sequence_tracks_mask + sequence_index * MEDIA_TYPE_COUNT; ngx_memcpy(tracks_mask, result->tracks_mask, sizeof(tracks_mask[0]) * MEDIA_TYPE_COUNT); // restore the global mask to the default for (media_type = 0; media_type < MEDIA_TYPE_COUNT; media_type++) { result->tracks_mask[media_type] = default_tracks_mask; } // from now on, parse directly to the sequence tracks mask tracks_mask = result->sequence_tracks_mask; masks_per_sequence = MEDIA_TYPE_COUNT; } } else if (*start_pos == 'v' || *start_pos == 'a') { // tracks start_pos = ngx_http_vod_extract_track_tokens(start_pos, end_pos, result->tracks_mask); if (start_pos == NULL) { return NGX_OK; } } // languages if (*start_pos == 'l') { result->langs_mask = ngx_pnalloc(r->pool, LANG_MASK_SIZE); if (result->langs_mask == NULL) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ngx_http_vod_parse_uri_file_name: ngx_pnalloc failed"); return NGX_HTTP_INTERNAL_SERVER_ERROR; } ngx_memzero(result->langs_mask, LANG_MASK_SIZE); for (;;) { start_pos++; // skip the l if (start_pos + LANG_ISO639_2_LEN > end_pos) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "ngx_http_vod_parse_uri_file_name: language specifier length must be 3 characters"); return NGX_HTTP_BAD_REQUEST; } lang_id = lang_parse_iso639_2_code(iso639_2_str_to_int(start_pos)); if (lang_id == 0) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "ngx_http_vod_parse_uri_file_name: failed to parse language specifier %*s", (size_t)3, start_pos); return NGX_HTTP_BAD_REQUEST; } vod_set_bit(result->langs_mask, lang_id); start_pos += LANG_ISO639_2_LEN; skip_dash(start_pos, end_pos); if (*start_pos != 'l') { break; } } } ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "ngx_http_vod_parse_uri_file_name: did not consume the whole name"); return NGX_HTTP_BAD_REQUEST; }
ngx_int_t ngx_http_vod_parse_uri_file_name( ngx_http_request_t* r, u_char* start_pos, u_char* end_pos, uint32_t flags, request_params_t* result) { sequence_tracks_mask_t* sequence_tracks_mask_end; sequence_tracks_mask_t* sequence_tracks_mask; ngx_str_t* cur_sequence_id; ngx_str_t* last_sequence_id; uint32_t default_tracks_mask; uint32_t* tracks_mask; uint32_t segment_index_shift; uint32_t sequence_index; uint32_t clip_index; uint32_t media_type; uint32_t pts_delay; uint32_t version; bool_t tracks_mask_updated; language_id_t lang_id; default_tracks_mask = (flags & PARSE_FILE_NAME_MULTI_STREAMS_PER_TYPE) ? 0xffffffff : 1; for (media_type = 0; media_type < MEDIA_TYPE_COUNT; media_type++) { result->tracks_mask[media_type] = default_tracks_mask; } result->sequences_mask = 0xffffffff; result->clip_index = INVALID_CLIP_INDEX; // segment index if ((flags & PARSE_FILE_NAME_EXPECT_SEGMENT_INDEX) != 0) { if (start_pos < end_pos && *start_pos == '-') { start_pos++; // skip the - } start_pos = parse_utils_extract_uint32_token(start_pos, end_pos, &result->segment_index); if (result->segment_index <= 0) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "ngx_http_vod_parse_uri_file_name: failed to parse segment index"); return ngx_http_vod_status_to_ngx_error(r, VOD_BAD_REQUEST); } result->segment_index--; // convert to 0-based skip_dash(start_pos, end_pos); // index shift if (*start_pos == 'i') { start_pos++; // skip the i start_pos = parse_utils_extract_uint32_token(start_pos, end_pos, &segment_index_shift); if (segment_index_shift <= 0) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "ngx_http_vod_parse_uri_file_name: failed to parse segment index shift"); return ngx_http_vod_status_to_ngx_error(r, VOD_BAD_REQUEST); } result->segment_index += segment_index_shift; skip_dash(start_pos, end_pos); } } else { skip_dash(start_pos, end_pos); } // clip index if (*start_pos == 'c' && (flags & PARSE_FILE_NAME_ALLOW_CLIP_INDEX) != 0) { start_pos++; // skip the c start_pos = parse_utils_extract_uint32_token(start_pos, end_pos, &clip_index); if (clip_index <= 0) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "ngx_http_vod_parse_uri_file_name: failed to parse clip index"); return ngx_http_vod_status_to_ngx_error(r, VOD_BAD_REQUEST); } result->clip_index = clip_index - 1; skip_dash(start_pos, end_pos); } // sequence (file) index if (*start_pos == 'f' || *start_pos == 's') { result->sequences_mask = 0; cur_sequence_id = result->sequence_ids; last_sequence_id = cur_sequence_id + MAX_SEQUENCE_IDS; sequence_tracks_mask = NULL; sequence_tracks_mask_end = NULL; tracks_mask_updated = FALSE; for (;;) { if (*start_pos == 'f') { // sequence index start_pos++; // skip the f if (start_pos >= end_pos || *start_pos < '1' || *start_pos > '9') { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "ngx_http_vod_parse_uri_file_name: missing index following sequence selector"); return ngx_http_vod_status_to_ngx_error(r, VOD_BAD_REQUEST); } sequence_index = *start_pos - '0'; start_pos++; // skip the digit if (start_pos < end_pos && *start_pos >= '0' && *start_pos <= '9') { sequence_index = sequence_index * 10 + *start_pos - '0'; if (sequence_index > MAX_SEQUENCES) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "ngx_http_vod_parse_uri_file_name: sequence index too big"); return ngx_http_vod_status_to_ngx_error(r, VOD_BAD_REQUEST); } start_pos++; // skip the digit } sequence_index--; // Note: sequence_index cannot be 0 here result->sequences_mask |= (1 << sequence_index); } else { // sequence id start_pos++; // skip the s if (cur_sequence_id >= last_sequence_id) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "ngx_http_vod_parse_uri_file_name: the number of sequence ids exceeds the limit"); return ngx_http_vod_status_to_ngx_error(r, VOD_BAD_REQUEST); } cur_sequence_id->data = start_pos; while (start_pos < end_pos && *start_pos != '-') { start_pos++; } cur_sequence_id->len = start_pos - cur_sequence_id->data; cur_sequence_id++; sequence_index = -(cur_sequence_id - result->sequence_ids); } skip_dash(start_pos, end_pos); // tracks spec if (*start_pos == 'v' || *start_pos == 'a') { if (sequence_tracks_mask != NULL) { if (sequence_tracks_mask >= sequence_tracks_mask_end) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "ngx_http_vod_parse_uri_file_name: the number of track specs exceeds the limit"); return ngx_http_vod_status_to_ngx_error(r, VOD_BAD_REQUEST); } sequence_tracks_mask->index = sequence_index; tracks_mask = sequence_tracks_mask->tracks_mask; sequence_tracks_mask++; result->sequence_tracks_mask_end = sequence_tracks_mask; } else { tracks_mask_updated = TRUE; tracks_mask = result->tracks_mask; } start_pos = ngx_http_vod_extract_track_tokens( start_pos, end_pos, tracks_mask); if (start_pos == NULL) { return NGX_OK; } } if (*start_pos != 'f' && *start_pos != 's') { break; } if (sequence_tracks_mask != NULL) { continue; } // more than one sequence, allocate the per sequence tracks mask sequence_tracks_mask = ngx_palloc(r->pool, sizeof(sequence_tracks_mask[0]) * MAX_SEQUENCE_TRACKS_MASKS); if (sequence_tracks_mask == NULL) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ngx_http_vod_parse_uri_file_name: ngx_palloc failed"); return ngx_http_vod_status_to_ngx_error(r, VOD_ALLOC_FAILED); } sequence_tracks_mask_end = sequence_tracks_mask + MAX_SEQUENCE_TRACKS_MASKS; result->sequence_tracks_mask = sequence_tracks_mask; result->sequence_tracks_mask_end = sequence_tracks_mask; if (tracks_mask_updated) { // add the currently parsed mask to the array sequence_tracks_mask->index = sequence_index; ngx_memcpy(sequence_tracks_mask->tracks_mask, result->tracks_mask, sizeof(sequence_tracks_mask->tracks_mask)); sequence_tracks_mask++; result->sequence_tracks_mask_end = sequence_tracks_mask; // restore the global mask to the default for (media_type = 0; media_type < MEDIA_TYPE_COUNT; media_type++) { result->tracks_mask[media_type] = default_tracks_mask; } } } } else if (*start_pos == 'v' || *start_pos == 'a') { // tracks start_pos = ngx_http_vod_extract_track_tokens(start_pos, end_pos, result->tracks_mask); if (start_pos == NULL) { return NGX_OK; } } // pts delay if (*start_pos == 'p') { start_pos++; // skip the p start_pos = parse_utils_extract_uint32_token(start_pos, end_pos, &pts_delay); if (pts_delay <= 0) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "ngx_http_vod_parse_uri_file_name: failed to parse pts delay"); return ngx_http_vod_status_to_ngx_error(r, VOD_BAD_REQUEST); } result->pts_delay = pts_delay; skip_dash(start_pos, end_pos); } // languages if (*start_pos == 'l') { result->langs_mask = ngx_pnalloc(r->pool, LANG_MASK_SIZE); if (result->langs_mask == NULL) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ngx_http_vod_parse_uri_file_name: ngx_pnalloc failed"); return ngx_http_vod_status_to_ngx_error(r, VOD_ALLOC_FAILED); } ngx_memzero(result->langs_mask, LANG_MASK_SIZE); for (;;) { start_pos++; // skip the l if (start_pos + LANG_ISO639_3_LEN > end_pos) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "ngx_http_vod_parse_uri_file_name: language specifier length must be 3 characters"); return ngx_http_vod_status_to_ngx_error(r, VOD_BAD_REQUEST); } lang_id = lang_parse_iso639_3_code(iso639_3_str_to_int(start_pos)); if (lang_id == 0) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "ngx_http_vod_parse_uri_file_name: failed to parse language specifier %*s", (size_t)3, start_pos); return ngx_http_vod_status_to_ngx_error(r, VOD_BAD_REQUEST); } vod_set_bit(result->langs_mask, lang_id); start_pos += LANG_ISO639_3_LEN; skip_dash(start_pos, end_pos); if (*start_pos != 'l') { break; } } } // version if (*start_pos == 'x') { start_pos++; // skip the x start_pos = parse_utils_extract_uint32_token(start_pos, end_pos, &version); if (version <= 0) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "ngx_http_vod_parse_uri_file_name: failed to parse version"); return ngx_http_vod_status_to_ngx_error(r, VOD_BAD_REQUEST); } result->version = version - 1; skip_dash(start_pos, end_pos); } ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "ngx_http_vod_parse_uri_file_name: did not consume the whole name"); return ngx_http_vod_status_to_ngx_error(r, VOD_BAD_REQUEST); }