| /* | |
| * Licensed to the Apache Software Foundation (ASF) under one | |
| * or more contributor license agreements. See the NOTICE file | |
| * distributed with this work for additional information | |
| * regarding copyright ownership. The ASF licenses this file | |
| * to you under the Apache License, Version 2.0 (the | |
| * "License"); you may not use this file except in compliance | |
| * with the License. You may obtain a copy of the License at | |
| * | |
| * http://www.apache.org/licenses/LICENSE-2.0 | |
| * | |
| * Unless required by applicable law or agreed to in writing, | |
| * software distributed under the License is distributed on an | |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
| * KIND, either express or implied. See the License for the | |
| * specific language governing permissions and limitations | |
| * under the License. | |
| */ | |
| #include "qpid/dispatch/parse.h" | |
| #include "qpid/dispatch/alloc.h" | |
| #include "qpid/dispatch/amqp.h" | |
| #include "qpid/dispatch/ctools.h" | |
| #include "buffer_field_api.h" | |
| #include "buffer_field_api.h" | |
| #include <assert.h> | |
| #include <inttypes.h> | |
| #include <stdio.h> | |
| DEQ_DECLARE(qd_parsed_field_t, qd_parsed_field_list_t); | |
| typedef struct qd_amqp_field_t { | |
| uint8_t tag; | |
| uint32_t size; // includes length of count! | |
| uint32_t count; | |
| qd_buffer_field_t value; // the raw (encoded) value | |
| } qd_amqp_field_t; | |
| struct qd_parsed_field_t { | |
| DEQ_LINKS(qd_parsed_field_t); | |
| const qd_parsed_field_t *parent; | |
| qd_parsed_field_list_t children; | |
| qd_iterator_t *typed_iter; // iterator over the full field (header and value) | |
| qd_iterator_t *raw_iter; // iterator over just the value | |
| const char *parse_error; | |
| qd_buffer_field_t full_field; // contains encoded AMQP type header and value | |
| qd_amqp_field_t amqp; // decoded header and raw value | |
| }; | |
| ALLOC_DECLARE(qd_parsed_field_t); | |
| ALLOC_DEFINE(qd_parsed_field_t); | |
| qd_parsed_field_t* qd_field_first_child(qd_parsed_field_t *field) | |
| { | |
| return DEQ_HEAD(field->children); | |
| } | |
| qd_parsed_field_t* qd_field_next_child(qd_parsed_field_t *field) | |
| { | |
| return DEQ_NEXT(field); | |
| } | |
| // length of size and count of AMQP data fields can be determined by the value | |
| // of the top 4 bits of the tag octet. See AMQP 1.0 Part 1 Types. | |
| // | |
| static inline int tag_get_size_length(uint8_t tag) | |
| { | |
| tag &= 0xF0; | |
| if (tag < 0xA0) return 0; | |
| if ((tag & 0x10) == 0) return 1; | |
| return 4; | |
| } | |
| static inline int tag_get_count_length(uint8_t tag) | |
| { | |
| tag &= 0xF0; | |
| if (tag < 0xC0) return 0; | |
| if ((tag & 0x10) == 0) return 1; | |
| return 4; | |
| } | |
| /** | |
| * Extract an AMQP value from the encoded data held in *bfield and store it in *value. | |
| * bfield is expected to point to the tag octet and will be advanced past the decoded value. | |
| * Returns 0 on success, else an error message. | |
| */ | |
| static inline char *parse_amqp_field(qd_buffer_field_t *bfield, qd_amqp_field_t *value) | |
| { | |
| ZERO(value); | |
| if (!qd_buffer_field_octet(bfield, &value->tag)) | |
| return "Insufficient Data to Determine Tag"; | |
| uint32_t length_of_count = tag_get_count_length(value->tag); | |
| uint32_t length_of_size = tag_get_size_length(value->tag); | |
| // extract size and content (optional) | |
| switch (value->tag & 0xF0) { | |
| case 0x40: | |
| break; | |
| case 0x50: | |
| value->size = 1; | |
| break; | |
| case 0x60: | |
| value->size = 2; | |
| break; | |
| case 0x70: | |
| value->size = 4; | |
| break; | |
| case 0x80: | |
| value->size = 8; | |
| break; | |
| case 0x90: | |
| value->size = 16; | |
| break; | |
| case 0xB0: | |
| case 0xD0: | |
| case 0xF0: | |
| { | |
| (void) length_of_size; // ignore unused var error | |
| assert(length_of_size == 4); | |
| if (!qd_buffer_field_uint32(bfield, &value->size)) { | |
| return "Insufficient Data to Determine Length"; | |
| } | |
| if (length_of_count) { | |
| assert(length_of_count == 4); | |
| if (!qd_buffer_field_uint32(bfield, &value->count)) { | |
| return "Insufficient Data to Determine Count"; | |
| } | |
| } | |
| } | |
| break; | |
| case 0xA0: | |
| case 0xC0: | |
| case 0xE0: | |
| { | |
| uint8_t octet; | |
| assert(length_of_size == 1); | |
| if (!qd_buffer_field_octet(bfield, &octet)) { | |
| return "Insufficient Data to Determine Length"; | |
| } | |
| value->size = octet; | |
| if (length_of_count) { | |
| assert(length_of_count == 1); | |
| if (!qd_buffer_field_octet(bfield, &octet)) { | |
| return "Insufficient Data to Determine Count"; | |
| } | |
| value->count = octet; | |
| } | |
| break; | |
| } | |
| default: | |
| return "Invalid Tag - No Length Information"; | |
| } | |
| if ((value->tag == QD_AMQP_MAP8 || value->tag == QD_AMQP_MAP32) && (value->count & 1)) | |
| return "Odd Number of Elements in a Map"; | |
| if (length_of_count > value->size) | |
| return "Insufficient Length to Determine Count"; | |
| value->value = *bfield; | |
| value->value.remaining = value->size - length_of_count; | |
| size_t moved = qd_buffer_field_advance(bfield, value->value.remaining); | |
| if (moved != value->value.remaining) | |
| return "Truncated field"; | |
| return 0; | |
| } | |
| // bfield contains the encoded AMQP data to be parsed. bfield starts at the | |
| // type tag octet and should be long enough to hold the entire AMQP data type. | |
| // On return bfield has been advanced past the encoded AMQP data. | |
| // | |
| static qd_parsed_field_t *qd_parse_internal(qd_buffer_field_t *bfield, qd_parsed_field_t *p) | |
| { | |
| qd_parsed_field_t *field = new_qd_parsed_field_t(); | |
| if (!field) | |
| return 0; | |
| ZERO(field); | |
| DEQ_ITEM_INIT(field); | |
| DEQ_INIT(field->children); | |
| field->parent = p; | |
| field->full_field = *bfield; | |
| field->parse_error = parse_amqp_field(bfield, &field->amqp); | |
| if (!field->parse_error) { | |
| // truncate full_field in case bfield holds multiple values. | |
| // since bfield has advanced past the parsed field we just subtract it. | |
| field->full_field.remaining -= bfield->remaining; | |
| // now parse out the content of any contained types: | |
| qd_buffer_field_t children = field->amqp.value; | |
| for (uint32_t idx = 0; idx < field->amqp.count; idx++) { | |
| qd_parsed_field_t *child = qd_parse_internal(&children, field); | |
| DEQ_INSERT_TAIL(field->children, child); | |
| if (!qd_parse_ok(child)) { | |
| field->parse_error = child->parse_error; | |
| break; | |
| } | |
| } | |
| } | |
| return field; | |
| } | |
| qd_parsed_field_t *qd_parse(const qd_iterator_t *iter) | |
| { | |
| if (!iter) | |
| return 0; | |
| qd_buffer_field_t bfield = qd_iterator_get_view_cursor(iter); | |
| return qd_parse_internal(&bfield, 0); | |
| } | |
| void qd_parse_free(qd_parsed_field_t *field) | |
| { | |
| if (!field) | |
| return; | |
| assert(field->parent == 0); | |
| if (field->raw_iter) | |
| qd_iterator_free(field->raw_iter); | |
| if (field->typed_iter) | |
| qd_iterator_free(field->typed_iter); | |
| qd_parsed_field_t *sub_field = DEQ_HEAD(field->children); | |
| while (sub_field) { | |
| qd_parsed_field_t *next = DEQ_NEXT(sub_field); | |
| DEQ_REMOVE_HEAD(field->children); | |
| sub_field->parent = 0; | |
| qd_parse_free(sub_field); | |
| sub_field = next; | |
| } | |
| free_qd_parsed_field_t(field); | |
| } | |
| static qd_parsed_field_t *qd_parse_dup_internal(const qd_parsed_field_t *field, const qd_parsed_field_t *parent) | |
| { | |
| qd_parsed_field_t *dup = new_qd_parsed_field_t(); | |
| if (dup == 0) | |
| return 0; | |
| ZERO(dup); | |
| dup->parent = parent; | |
| dup->raw_iter = qd_iterator_dup(field->raw_iter); | |
| dup->typed_iter = qd_iterator_dup(field->typed_iter); | |
| dup->amqp = field->amqp; | |
| dup->full_field = field->full_field; | |
| qd_parsed_field_t *child = DEQ_HEAD(field->children); | |
| while (child) { | |
| qd_parsed_field_t *dup_child = qd_parse_dup_internal(child, field); | |
| DEQ_INSERT_TAIL(dup->children, dup_child); | |
| child = DEQ_NEXT(child); | |
| } | |
| return dup; | |
| } | |
| qd_parsed_field_t *qd_parse_dup(const qd_parsed_field_t *field) | |
| { | |
| return field ? qd_parse_dup_internal(field, 0) : 0; | |
| } | |
| int qd_parse_ok(qd_parsed_field_t *field) | |
| { | |
| return field && field->parse_error == 0; | |
| } | |
| const char *qd_parse_error(qd_parsed_field_t *field) | |
| { | |
| return field ? field->parse_error : "No field"; | |
| } | |
| uint8_t qd_parse_tag(qd_parsed_field_t *field) | |
| { | |
| assert(field); | |
| return field->amqp.tag; | |
| } | |
| // just the data (no header/tag) | |
| qd_iterator_t *qd_parse_raw(qd_parsed_field_t *field) | |
| { | |
| if (!field) | |
| return 0; | |
| if (!field->raw_iter) { | |
| field->raw_iter = qd_iterator_buffer_field(&field->amqp.value, | |
| ITER_VIEW_ALL); | |
| } | |
| return field->raw_iter; | |
| } | |
| // includes type header, tag and data | |
| qd_iterator_t *qd_parse_typed(qd_parsed_field_t *field) | |
| { | |
| if (!field) | |
| return 0; | |
| if (!field->typed_iter) { | |
| field->typed_iter = qd_iterator_buffer_field(&field->full_field, | |
| ITER_VIEW_ALL); | |
| } | |
| return field->typed_iter; | |
| } | |
| qd_buffer_field_t qd_parse_value(const qd_parsed_field_t *field) | |
| { | |
| assert(field && !field->parse_error); | |
| return field->amqp.value; | |
| } | |
| uint32_t qd_parse_as_uint(qd_parsed_field_t *field) | |
| { | |
| uint32_t result = 0; | |
| uint64_t tmp = qd_parse_as_ulong(field); | |
| if (qd_parse_ok(field)) { | |
| if (tmp <= UINT32_MAX) { | |
| result = tmp; | |
| } else { | |
| field->parse_error = "Integer value too large to parse as uint"; | |
| } | |
| } | |
| return result; | |
| } | |
| uint64_t qd_parse_as_ulong(qd_parsed_field_t *parsed_field) | |
| { | |
| uint64_t result = 0; | |
| uint32_t tmp32 = 0; | |
| uint8_t octet = 0; | |
| qd_buffer_field_t field = parsed_field->amqp.value; | |
| switch (parsed_field->amqp.tag) { | |
| case QD_AMQP_ULONG: | |
| case QD_AMQP_TIMESTAMP: | |
| qd_buffer_field_uint32(&field, &tmp32); | |
| result = ((uint64_t) tmp32) << 32; | |
| qd_buffer_field_uint32(&field, &tmp32); | |
| result |= ((uint64_t) tmp32); | |
| break; | |
| case QD_AMQP_UINT: | |
| qd_buffer_field_uint32(&field, &tmp32); | |
| result = tmp32; | |
| break; | |
| case QD_AMQP_USHORT: | |
| qd_buffer_field_octet(&field, &octet); | |
| result = ((uint64_t) octet) << 8; | |
| // Fall Through... | |
| case QD_AMQP_BOOLEAN: | |
| case QD_AMQP_UBYTE: | |
| case QD_AMQP_SMALLUINT: | |
| case QD_AMQP_SMALLULONG: | |
| qd_buffer_field_octet(&field, &octet); | |
| result |= (uint64_t) octet; | |
| break; | |
| case QD_AMQP_TRUE: | |
| result = 1; | |
| break; | |
| case QD_AMQP_FALSE: | |
| case QD_AMQP_UINT0: | |
| case QD_AMQP_ULONG0: | |
| // already zeroed | |
| break; | |
| case QD_AMQP_STR8_UTF8: | |
| case QD_AMQP_STR32_UTF8: | |
| case QD_AMQP_SYM8: | |
| case QD_AMQP_SYM32: | |
| { | |
| // conversion from string to 64 bit unsigned integer: | |
| char *value = qd_buffer_field_strdup(&field); | |
| if (sscanf(value, "%"SCNu64, &result) != 1) | |
| parsed_field->parse_error = "Cannot convert string to unsigned long"; | |
| free(value); | |
| } | |
| break; | |
| case QD_AMQP_BYTE: | |
| case QD_AMQP_SHORT: | |
| case QD_AMQP_INT: | |
| case QD_AMQP_SMALLINT: | |
| case QD_AMQP_LONG: | |
| case QD_AMQP_SMALLLONG: | |
| { | |
| // if a signed integer is positive, accept it | |
| int64_t ltmp = qd_parse_as_long(parsed_field); | |
| if (qd_parse_ok(parsed_field)) { | |
| if (ltmp >= 0) { | |
| result = (uint64_t)ltmp; | |
| } else { | |
| parsed_field->parse_error = "Unable to parse negative integer as unsigned"; | |
| } | |
| } | |
| } | |
| break; | |
| default: | |
| parsed_field->parse_error = "Unable to parse as an unsigned integer"; | |
| // catch any missing types during development | |
| assert(false); | |
| } | |
| return result; | |
| } | |
| int32_t qd_parse_as_int(qd_parsed_field_t *field) | |
| { | |
| int32_t result = 0; | |
| int64_t tmp = qd_parse_as_long(field); | |
| if (qd_parse_ok(field)) { | |
| if (INT32_MIN <= tmp && tmp <= INT32_MAX) { | |
| result = tmp; | |
| } else { | |
| field->parse_error = "Integer value too large to parse as int"; | |
| } | |
| } | |
| return result; | |
| } | |
| int64_t qd_parse_as_long(qd_parsed_field_t *parsed_field) | |
| { | |
| int64_t result = 0; | |
| qd_buffer_field_t field = parsed_field->amqp.value; | |
| switch (parsed_field->amqp.tag) { | |
| case QD_AMQP_LONG: { | |
| uint64_t convert; | |
| uint32_t tmp32 = 0; | |
| qd_buffer_field_uint32(&field, &tmp32); | |
| convert = ((uint64_t) tmp32) << 32; | |
| qd_buffer_field_uint32(&field, &tmp32); | |
| convert |= (uint64_t) tmp32; | |
| result = (int64_t) convert; | |
| break; | |
| } | |
| case QD_AMQP_INT: { | |
| uint32_t tmp = 0; | |
| qd_buffer_field_uint32(&field, &tmp); | |
| result = (int32_t) tmp; | |
| break; | |
| } | |
| case QD_AMQP_SHORT: { | |
| uint16_t convert; | |
| uint8_t octet = 0; | |
| qd_buffer_field_octet(&field, &octet); | |
| convert = ((uint16_t) octet) << 8; | |
| qd_buffer_field_octet(&field, &octet); | |
| convert |= ((uint16_t) octet); | |
| result = (int16_t) convert; | |
| break; | |
| } | |
| case QD_AMQP_BYTE: | |
| case QD_AMQP_BOOLEAN: | |
| case QD_AMQP_SMALLLONG: | |
| case QD_AMQP_SMALLINT: { | |
| uint8_t octet = 0; | |
| qd_buffer_field_octet(&field, &octet); | |
| result = (int8_t) octet; | |
| break; | |
| } | |
| case QD_AMQP_TRUE: | |
| result = 1; | |
| break; | |
| case QD_AMQP_FALSE: | |
| case QD_AMQP_UINT0: | |
| case QD_AMQP_ULONG0: | |
| // already zeroed | |
| break; | |
| case QD_AMQP_STR8_UTF8: | |
| case QD_AMQP_STR32_UTF8: | |
| case QD_AMQP_SYM8: | |
| case QD_AMQP_SYM32: | |
| { | |
| // conversion from string to 64 bit integer: | |
| char *value = qd_buffer_field_strdup(&field); | |
| if (sscanf(value, "%"SCNi64, &result) != 1) | |
| parsed_field->parse_error = "Cannot convert string to long"; | |
| free(value); | |
| } | |
| break; | |
| case QD_AMQP_UBYTE: | |
| case QD_AMQP_SMALLUINT: | |
| case QD_AMQP_SMALLULONG: | |
| case QD_AMQP_USHORT: | |
| case QD_AMQP_UINT: | |
| case QD_AMQP_ULONG: | |
| { | |
| // if an unsigned integer "fits" accept it | |
| uint64_t utmp = qd_parse_as_ulong(parsed_field); | |
| if (qd_parse_ok(parsed_field)) { | |
| uint64_t max = INT8_MAX; | |
| switch (parsed_field->amqp.tag) { | |
| case QD_AMQP_USHORT: | |
| max = INT16_MAX; | |
| break; | |
| case QD_AMQP_UINT: | |
| max = INT32_MAX; | |
| break; | |
| case QD_AMQP_ULONG: | |
| max = INT64_MAX; | |
| break; | |
| } | |
| if (utmp <= max) { | |
| result = (int64_t)utmp; | |
| } else { | |
| parsed_field->parse_error = "Unable to parse unsigned integer as a signed integer"; | |
| } | |
| } | |
| } | |
| break; | |
| default: | |
| parsed_field->parse_error = "Unable to parse as a signed integer"; | |
| // catch any missing types during development | |
| assert(false); | |
| } | |
| return result; | |
| } | |
| bool qd_parse_as_bool(qd_parsed_field_t *parsed_field) | |
| { | |
| bool result = false; | |
| qd_buffer_field_t field = parsed_field->amqp.value; | |
| switch (parsed_field->amqp.tag) { | |
| case QD_AMQP_BYTE: | |
| case QD_AMQP_BOOLEAN: { | |
| uint8_t octet = 0; | |
| qd_buffer_field_octet(&field, &octet); | |
| result = !!octet; | |
| break; | |
| } | |
| case QD_AMQP_TRUE: | |
| result = true; | |
| break; | |
| } | |
| return result; | |
| } | |
| char *qd_parse_as_string(const qd_parsed_field_t *parsed_field) | |
| { | |
| char *str = 0; | |
| switch (parsed_field->amqp.tag) { | |
| case QD_AMQP_STR8_UTF8: | |
| case QD_AMQP_SYM8: | |
| case QD_AMQP_STR32_UTF8: | |
| case QD_AMQP_SYM32: { | |
| qd_buffer_field_t tmp = parsed_field->amqp.value; | |
| str = qd_buffer_field_strdup(&tmp); | |
| break; | |
| } | |
| default: | |
| break; | |
| } | |
| return str; | |
| } | |
| uint32_t qd_parse_sub_count(qd_parsed_field_t *field) | |
| { | |
| uint32_t count = DEQ_SIZE(field->children); | |
| if (field->amqp.tag == QD_AMQP_MAP8 || field->amqp.tag == QD_AMQP_MAP32) | |
| count = count >> 1; | |
| return count; | |
| } | |
| qd_parsed_field_t *qd_parse_sub_key(qd_parsed_field_t *field, uint32_t idx) | |
| { | |
| if (field->amqp.tag != QD_AMQP_MAP8 && field->amqp.tag != QD_AMQP_MAP32) | |
| return 0; | |
| idx = idx << 1; | |
| qd_parsed_field_t *key = DEQ_HEAD(field->children); | |
| while (idx && key) { | |
| idx--; | |
| key = DEQ_NEXT(key); | |
| } | |
| return key; | |
| } | |
| qd_parsed_field_t *qd_parse_sub_value(qd_parsed_field_t *field, uint32_t idx) | |
| { | |
| if (field->amqp.tag == QD_AMQP_MAP8 || field->amqp.tag == QD_AMQP_MAP32) | |
| idx = (idx << 1) + 1; | |
| qd_parsed_field_t *key = DEQ_HEAD(field->children); | |
| while (idx && key) { | |
| idx--; | |
| key = DEQ_NEXT(key); | |
| } | |
| return key; | |
| } | |
| int is_tag_a_map(uint8_t tag) | |
| { | |
| return tag == QD_AMQP_MAP8 || tag == QD_AMQP_MAP32; | |
| } | |
| int qd_parse_is_map(qd_parsed_field_t *field) | |
| { | |
| if (!field) | |
| return 0; | |
| return is_tag_a_map(field->amqp.tag); | |
| } | |
| int qd_parse_is_list(qd_parsed_field_t *field) | |
| { | |
| if (!field) | |
| return 0; | |
| return field->amqp.tag == QD_AMQP_LIST8 | |
| || field->amqp.tag == QD_AMQP_LIST32 | |
| || field->amqp.tag == QD_AMQP_LIST0; | |
| } | |
| int qd_parse_is_scalar(qd_parsed_field_t *field) | |
| { | |
| return DEQ_SIZE(field->children) == 0; | |
| } | |
| static inline bool qd_parse_is_string(const qd_parsed_field_t *field) | |
| { | |
| return field->amqp.tag == QD_AMQP_STR8_UTF8 | |
| || field->amqp.tag == QD_AMQP_STR32_UTF8; | |
| } | |
| qd_parsed_field_t *qd_parse_value_by_key(qd_parsed_field_t *field, const char *key) | |
| { | |
| if (!key) | |
| return 0; | |
| uint32_t count = qd_parse_sub_count(field); | |
| for (uint32_t idx = 0; idx < count; idx++) { | |
| qd_parsed_field_t *sub = qd_parse_sub_key(field, idx); | |
| if (!sub) | |
| return 0; | |
| qd_buffer_field_t value = sub->amqp.value; | |
| size_t len = strlen(key); | |
| if (qd_buffer_field_equal(&value, (const uint8_t*) key, len)) { | |
| return qd_parse_sub_value(field, idx); | |
| } | |
| } | |
| return 0; | |
| } | |
| const char *qd_parse_annotations( | |
| bool strip_annotations_in, | |
| qd_iterator_t *ma_iter_in, | |
| qd_parsed_field_t **ma_ingress, | |
| qd_parsed_field_t **ma_phase, | |
| qd_parsed_field_t **ma_to_override, | |
| qd_parsed_field_t **ma_trace, | |
| qd_parsed_field_t **ma_stream, | |
| qd_buffer_field_t *user_annotations, | |
| uint32_t *user_count) | |
| { | |
| *ma_ingress = 0; | |
| *ma_phase = 0; | |
| *ma_to_override = 0; | |
| *ma_trace = 0; | |
| ZERO(user_annotations); | |
| *user_count = 0; | |
| if (!ma_iter_in) | |
| return 0; // ok - MA not present | |
| const char *error = 0; | |
| qd_buffer_field_t bfield = qd_iterator_get_view_cursor(ma_iter_in); | |
| qd_amqp_field_t ma_map; | |
| error = parse_amqp_field(&bfield, &ma_map); | |
| if (error) | |
| return error; | |
| if (ma_map.tag != QD_AMQP_MAP8 && ma_map.tag != QD_AMQP_MAP32) | |
| return "Invalid message annotations section - missing map type"; | |
| if (ma_map.count & 0x01) | |
| return "Invalid MA map count (odd number of fields)"; | |
| if (ma_map.count == 0) | |
| return 0; // empty map, ignore | |
| // ma_map.value now holds all of the key/value fields in the map and points | |
| // to the first key/value pair. The router-specific map entries always | |
| // come after any user-supplied MA data. Snapshot the current location for | |
| // the start of user data | |
| user_annotations->buffer = ma_map.value.buffer; | |
| user_annotations->cursor = ma_map.value.cursor; | |
| bool user_anno = true; // assume first annotations are non-router | |
| size_t user_annos_size = 0; | |
| uint32_t user_annos_count = 0; | |
| // Now iterate over each key looking for router-specific map entires | |
| qd_buffer_field_t ma_fields = ma_map.value; | |
| int kv_count = ma_map.count / 2; // pairs of key,value fields | |
| while (kv_count--) { | |
| qd_amqp_field_t key; | |
| // extract key, advance ma_fields to the value field | |
| error = parse_amqp_field(&ma_fields, &key); | |
| if (error) | |
| return error; | |
| if (key.tag == QD_AMQP_SYM8 || key.tag == QD_AMQP_SYM32) { | |
| switch (key.value.remaining) { | |
| case QD_MA_PREFIX_LEN: | |
| if (qd_buffer_field_equal(&key.value, (uint8_t*) QD_MA_PREFIX, QD_MA_PREFIX_LEN)) { | |
| qd_amqp_field_t skip; | |
| user_anno = false; | |
| // empty router annotation - ignore it | |
| error = parse_amqp_field(&ma_fields, &skip); | |
| if (error) | |
| return error; | |
| } | |
| break; | |
| case QD_MA_TO_LEN: | |
| if (qd_buffer_field_equal(&key.value, (uint8_t*) QD_MA_TO, QD_MA_TO_LEN)) { | |
| user_anno = false; | |
| if (!strip_annotations_in) { | |
| (*ma_to_override) = qd_parse_internal(&ma_fields, 0); | |
| if (!qd_parse_ok((*ma_to_override))) | |
| return (*ma_to_override)->parse_error; | |
| if (!qd_parse_is_string(*ma_to_override)) | |
| return "to-override not a valid string type"; | |
| } | |
| } | |
| break; | |
| case QD_MA_TRACE_LEN: | |
| // Same length as QD_MA_PHASE_LEN and QD_MA_CLASS_LEN: | |
| assert(QD_MA_TRACE_LEN == QD_MA_PHASE_LEN); | |
| assert(QD_MA_PHASE_LEN == QD_MA_CLASS_LEN); | |
| if (qd_buffer_field_equal(&key.value, (uint8_t*) QD_MA_TRACE, QD_MA_TRACE_LEN)) { | |
| user_anno = false; | |
| if (!strip_annotations_in) { | |
| (*ma_trace) = qd_parse_internal(&ma_fields, 0); | |
| if (!qd_parse_ok((*ma_trace))) | |
| return (*ma_trace)->parse_error; | |
| if (!qd_parse_is_list((*ma_trace))) | |
| return "trace annotation is not a list"; | |
| bool all_str = true; | |
| for (qd_parsed_field_t *node = DEQ_HEAD((*ma_trace)->children); | |
| node && all_str; | |
| node = DEQ_NEXT(node)) { | |
| all_str = qd_parse_is_string(node); | |
| } | |
| if (!all_str) | |
| return "trace list contains non-string entries"; | |
| } | |
| } else if (qd_buffer_field_equal(&key.value, (uint8_t*) QD_MA_PHASE, QD_MA_PHASE_LEN)) { | |
| user_anno = false; | |
| // always encoded as an int, may be small: | |
| if (!strip_annotations_in) { | |
| (*ma_phase) = qd_parse_internal(&ma_fields, 0); | |
| if (!qd_parse_ok((*ma_phase))) | |
| return (*ma_phase)->parse_error; | |
| } | |
| } else if (qd_buffer_field_equal(&key.value, (uint8_t*) QD_MA_CLASS, QD_MA_CLASS_LEN)) { | |
| // no longer used - skip it | |
| qd_amqp_field_t skip; | |
| user_anno = false; | |
| error = parse_amqp_field(&ma_fields, &skip); | |
| if (error) | |
| return error; | |
| } | |
| break; | |
| case QD_MA_STREAM_LEN: | |
| if (qd_buffer_field_equal(&key.value, (uint8_t*) QD_MA_STREAM, QD_MA_STREAM_LEN)) { | |
| user_anno = false; | |
| if (!strip_annotations_in) { | |
| (*ma_stream) = qd_parse_internal(&ma_fields, 0); | |
| if (!qd_parse_ok((*ma_stream))) | |
| return (*ma_stream)->parse_error; | |
| } | |
| } | |
| break; | |
| case QD_MA_INGRESS_LEN: | |
| if (qd_buffer_field_equal(&key.value, (uint8_t*) QD_MA_INGRESS, QD_MA_INGRESS_LEN)) { | |
| user_anno = false; | |
| if (!strip_annotations_in) { | |
| (*ma_ingress) = qd_parse_internal(&ma_fields, 0); | |
| if (!qd_parse_ok((*ma_ingress))) | |
| return (*ma_ingress)->parse_error; | |
| if (!qd_parse_is_string(*ma_ingress)) | |
| return "ingress router not a string type"; | |
| } | |
| } | |
| break; | |
| default: // user value | |
| break; | |
| } | |
| } | |
| if (user_anno) { | |
| qd_amqp_field_t user; | |
| // move past the value: | |
| error = parse_amqp_field(&ma_fields, &user); | |
| if (error) | |
| return error; | |
| size_t key_len = 1 + tag_get_size_length(key.tag) + key.size; | |
| size_t value_len = 1 + tag_get_size_length(user.tag) + user.size; | |
| user_annos_size += key_len + value_len; | |
| user_annos_count += 2; | |
| } else if (strip_annotations_in) { | |
| // hit the first non-user key - stop | |
| break; | |
| } | |
| } | |
| user_annotations->remaining = user_annos_size; | |
| *user_count = user_annos_count; | |
| return 0; | |
| } | |