diff --git a/include/aws/common/private/xml_parser_impl.h b/include/aws/common/private/xml_parser_impl.h index eea061b1e..d9763e88e 100644 --- a/include/aws/common/private/xml_parser_impl.h +++ b/include/aws/common/private/xml_parser_impl.h @@ -14,6 +14,7 @@ struct aws_xml_node { struct aws_array_list attributes; struct aws_byte_cursor doc_at_body; bool processed; + bool is_empty; }; struct aws_xml_parser { diff --git a/source/xml_parser.c b/source/xml_parser.c index e1b580740..62ea0ae58 100644 --- a/source/xml_parser.c +++ b/source/xml_parser.c @@ -40,6 +40,8 @@ static int s_load_node_decl( AWS_PRECONDITION(decl_body); AWS_PRECONDITION(node); + node->is_empty = decl_body->ptr[decl_body->len - 1] == '/'; + struct aws_array_list splits; AWS_ZERO_STRUCT(splits); @@ -158,6 +160,12 @@ int s_advance_to_closing_tag( AWS_PRECONDITION(parser); AWS_PRECONDITION(node); + if (node->is_empty) { + out_body->ptr = NULL; + out_body->len = 0; + return AWS_OP_SUCCESS; + } + /* currently the max node name is 256 characters. This is arbitrary, but should be enough * for our uses. If we ever generalize this, we'll have to come back and rethink this. */ uint8_t name_close[MAX_NAME_LEN + NODE_CLOSE_OVERHEAD] = {0}; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index d3556c5f7..36feaa367 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -467,6 +467,7 @@ add_test_case(xml_parser_nested_node_same_name_test) add_test_case(xml_parser_nested_node_deep_recursion_test) add_test_case(xml_parser_too_many_attributes_test) add_test_case(xml_parser_name_too_long_test) +add_test_case(xml_parser_child_empty_tag) add_test_case(test_thread_scheduler_ordering) add_test_case(test_thread_scheduler_happy_path_cancellation) diff --git a/tests/xml_parser_test.c b/tests/xml_parser_test.c index 9a3b33041..ac890f224 100644 --- a/tests/xml_parser_test.c +++ b/tests/xml_parser_test.c @@ -165,6 +165,40 @@ static int s_xml_parser_siblings_with_text_test(struct aws_allocator *allocator, AWS_TEST_CASE(xml_parser_siblings_with_text, s_xml_parser_siblings_with_text_test) +const char *siblings_with_empty_tag = + "TestBody2"; + +static int s_xml_parser_child_empty_tag(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + struct sibling_text_capture capture; + AWS_ZERO_STRUCT(capture); + + struct aws_xml_parser_options options = { + .doc = aws_byte_cursor_from_c_str(siblings_with_empty_tag), + .on_root_encountered = s_root_with_child_siblings, + .user_data = &capture, + }; + ASSERT_SUCCESS(aws_xml_parse(allocator, &options)); + + const char expected_name1[] = "child1"; + + const char expected_name2[] = "child2"; + const char expected_value2[] = "TestBody2"; + + ASSERT_BIN_ARRAYS_EQUALS(expected_name1, sizeof(expected_name1) - 1, capture.node_name1.ptr, capture.node_name1.len); + ASSERT_PTR_EQUALS(capture.capture1.ptr, NULL); + ASSERT_UINT_EQUALS(capture.capture1.len, 0); + + ASSERT_BIN_ARRAYS_EQUALS( + expected_name2, sizeof(expected_name2) - 1, capture.node_name2.ptr, capture.node_name2.len); + ASSERT_BIN_ARRAYS_EQUALS(expected_value2, sizeof(expected_value2) - 1, capture.capture2.ptr, capture.capture2.len); + + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE(xml_parser_child_empty_tag, s_xml_parser_child_empty_tag) + const char *preamble_and_attributes = "\n" "