diff --git a/common/http/src/main/java/io/helidon/common/http/MediaType.java b/common/http/src/main/java/io/helidon/common/http/MediaType.java index 05c4fd50afb..6dcf1d9616b 100644 --- a/common/http/src/main/java/io/helidon/common/http/MediaType.java +++ b/common/http/src/main/java/io/helidon/common/http/MediaType.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2021 Oracle and/or its affiliates. + * Copyright (c) 2018, 2022 Oracle and/or its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -325,23 +325,27 @@ public static MediaType parse(String input) { String attribute = tokenizer.consumeToken(TOKEN_MATCHER); tokenizer.consumeCharacter('='); final String value; - if ('"' == tokenizer.previewChar()) { - tokenizer.consumeCharacter('"'); - StringBuilder valueBuilder = new StringBuilder(); - while ('"' != tokenizer.previewChar()) { - if ('\\' == tokenizer.previewChar()) { - tokenizer.consumeCharacter('\\'); - valueBuilder.append(tokenizer.consumeCharacter(CharMatcher.ascii())); - } else { - valueBuilder.append(tokenizer.consumeToken(QUOTED_TEXT_MATCHER)); + if (tokenizer.hasMore()) { + if ('"' == tokenizer.previewChar()) { + tokenizer.consumeCharacter('"'); + StringBuilder valueBuilder = new StringBuilder(); + while ('"' != tokenizer.previewChar()) { + if ('\\' == tokenizer.previewChar()) { + tokenizer.consumeCharacter('\\'); + valueBuilder.append(tokenizer.consumeCharacter(CharMatcher.ascii())); + } else { + valueBuilder.append(tokenizer.consumeToken(QUOTED_TEXT_MATCHER)); + } } + value = valueBuilder.toString(); + tokenizer.consumeCharacter('"'); + } else { + value = tokenizer.consumeTokenIfPresent(TOKEN_MATCHER); + } + if (value != null) { + parameters.put(attribute, value); } - value = valueBuilder.toString(); - tokenizer.consumeCharacter('"'); - } else { - value = tokenizer.consumeToken(TOKEN_MATCHER); } - parameters.put(attribute, value); } return create(type, subtype, parameters); } catch (IllegalStateException e) { diff --git a/common/http/src/main/java/io/helidon/common/http/Tokenizer.java b/common/http/src/main/java/io/helidon/common/http/Tokenizer.java index 37fbe275557..b0f4fc67b22 100644 --- a/common/http/src/main/java/io/helidon/common/http/Tokenizer.java +++ b/common/http/src/main/java/io/helidon/common/http/Tokenizer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2021 Oracle and/or its affiliates. + * Copyright (c) 2018, 2022 Oracle and/or its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -46,7 +46,7 @@ public Tokenizer(String input) { * position the to next character if matched. * * @param matcher matcher to use - * @return token matched + * @return token matched, or {@code null} if not matched * @throws IllegalStateException if {@link #hasMore() } returns * {@code false} */ @@ -54,7 +54,8 @@ public String consumeTokenIfPresent(CharMatcher matcher) { checkState(hasMore(), "No more elements!"); int startPosition = position; position = matcher.negate().indexIn(input, startPosition); - return hasMore() ? input.substring(startPosition, position) + return position == startPosition ? null + : hasMore() ? input.substring(startPosition, position) : input.substring(startPosition); } diff --git a/common/http/src/test/java/io/helidon/common/http/MediaTypeTest.java b/common/http/src/test/java/io/helidon/common/http/MediaTypeTest.java index db333713bd8..f6deb45c929 100644 --- a/common/http/src/test/java/io/helidon/common/http/MediaTypeTest.java +++ b/common/http/src/test/java/io/helidon/common/http/MediaTypeTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2021 Oracle and/or its affiliates. + * Copyright (c) 2018, 2022 Oracle and/or its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -77,6 +77,12 @@ void parseDuplicateParameters() { assertThat(mediaType.parameters(), is(Map.of("option", "value2"))); } + @Test + void parseEmptyParameterValue() { + assertThat(MediaType.parse("type/subtype; o1=").parameters(), is(Map.of())); + assertThat(MediaType.parse("type/subtype; o1=; o2=v2").parameters(), is(Map.of("o2", "v2"))); + } + @Test void qualityFactor() { MediaType mediaType = MediaType.parse("unknown-type/unknown-subtype; q=0.2");