-
Notifications
You must be signed in to change notification settings - Fork 14
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Refactor: Stop Splitting Header Values #121
Refactor: Stop Splitting Header Values #121
Conversation
...c/main/java/com/sap/cloud/sdk/cloudplatform/requestheader/DefaultRequestHeaderContainer.java
Show resolved
Hide resolved
...c/main/java/com/sap/cloud/sdk/cloudplatform/requestheader/DefaultRequestHeaderContainer.java
Outdated
Show resolved
Hide resolved
...c/main/java/com/sap/cloud/sdk/cloudplatform/requestheader/DefaultRequestHeaderContainer.java
Outdated
Show resolved
Hide resolved
|
||
final RequestHeaderContainer sut = DefaultRequestHeaderContainer.fromMultiValueMap(input); | ||
|
||
assertThat(sut.getHeaderValues("Header1")).containsExactlyInAnyOrder(" Value1-1 "); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(Comment)
I'm wondering whether it'd be worth it investigating how Spring/Apache/JDK11 HttpClient
is handling white spaces in response header values-
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TL;DR
- Spring does NOT process header values in any way
- Apache DOES trim header values
Spring Header Handling
public class HttpHeadersLearningTest {
@Test
public void testGetSimple() {
final HttpHeaders sut = new HttpHeaders();
sut.add("name", "value1, value2");
assertThat(sut.get("name")).containsExactlyInAnyOrder("value1, value2");
}
@Test
public void testAddEmptyName() {
final HttpHeaders sut = new HttpHeaders();
sut.add("", "value");
assertThat(sut.keySet()).containsExactlyInAnyOrder("");
}
@Test
public void testGetValueWithTrailingSpaces() {
final HttpHeaders sut = new HttpHeaders();
sut.add("name", " value\t");
assertThat(sut.get("name")).containsExactlyInAnyOrder(" value\t");
}
@Test
public void testGetValueIgnoresKeyCasing() {
final HttpHeaders sut = new HttpHeaders();
sut.add("name", "value");
assertThat(sut.get("name")).containsExactlyInAnyOrder("value");
assertThat(sut.get("NAME")).containsExactlyInAnyOrder("value");
}
}
Apache Header Handling
public class ApacheHeaderLearningTest {
@Test
public void testGetElementsSimple() {
final BasicHeader sut = new BasicHeader("name", "value1, value2");
assertThat(Arrays.stream(sut.getElements()).map(HeaderElement::getName))
.containsExactlyInAnyOrder("value1", "value2");
}
@Test
public void testGetElementsCookies() {
final BasicHeader sut = new BasicHeader("Set-Cookie", "cookie1; cookie2");
assertThat(sut.getElements())
.containsExactly(
new BasicHeaderElement(
"cookie1",
null,
new NameValuePair[]{new BasicNameValuePair("cookie2", null)}));
}
@Test
public void testGetElementsQuoted() {
final BasicHeader sut = new BasicHeader("name", "\"value1, value2\", value3");
assertThat(Arrays.stream(sut.getElements()).map(HeaderElement::getName))
.containsExactlyInAnyOrder("\"value1", "value2\"", "value3");
}
@Test
public void testGetElementsSingleQuoted() {
final BasicHeader sut = new BasicHeader("name", "'value1, value2', value3");
assertThat(Arrays.stream(sut.getElements()).map(HeaderElement::getName))
.containsExactlyInAnyOrder("'value1", "value2'", "value3");
}
@Test
public void testGetElementsWithEmptyEntry() {
final BasicHeader sut = new BasicHeader("name", "value1, , value3");
assertThat(Arrays.stream(sut.getElements()).map(HeaderElement::getName)).containsExactly("value1", "value3");
}
@Test
public void testGetElementsWithTrailingSpaces() {
final BasicHeader sut = new BasicHeader("name", " value\t");
assertThat(Arrays.stream(sut.getElements()).map(HeaderElement::getName)).containsExactly("value");
}
}
|
||
@SafeVarargs | ||
@Nonnull | ||
private static HttpResponse mockResponseWithHeaders( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(Minor)
Feels like this method can be nicely reduced by just using implementation BasicHttpResponse
with respective string-based header-methods.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Only minor comments, otherwise LGTM
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm!
When we offer the same public API we should also have a look the concrete implementation. Best to use the same kind of Map (ImmutableListMultimap with "normalized" keys vs. TreeMap with String::compareToIgnoreCase
). But for the moment that should result in the same behavior.
Co-authored-by: Alexander Dümont <alexander.duemont@sap.com>
@@ -32,15 +31,8 @@ | |||
@Beta | |||
@EqualsAndHashCode | |||
@RequiredArgsConstructor( access = AccessLevel.PRIVATE ) | |||
public class DefaultRequestHeaderContainer implements RequestHeaderContainer | |||
public final class DefaultRequestHeaderContainer implements RequestHeaderContainer |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(Question)
Why final?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As discussed, the constructor is private
so the class cannot be inherited (in productive code).
For mocking (i.e. during tests), I'd expect customers to either mock the interface (RequestHeaderContainer
) or to use an actual (i.e. non-mocked) instance of the implementation (DefaultRequestHeaderContainer
).
Therefore, I'd argue this change to be fine.
Besides: This guide seems to hint that mocking final classes is not a problem at all.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
Very minor, map implementation choice as Chris mentioned. Minor code quality.
Context
SAP/cloud-sdk-java-backlog#156.
SAP/cloud-sdk-java-backlog/discussions/343
This PR changes our
RequestHeaderContainer
and theODataRequestResult
to no longer split header values.Feature scope:
RequestHeaderContainer
DefaultRequestHeaderContainer
)ODataRequestResult
Definition of Done
Error handling created / updated & covered by the tests above