Skip to content
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

Eagerly throw on null request parameters #1920

Merged
merged 4 commits into from
Apr 28, 2023

Conversation

westinrm
Copy link
Contributor

@westinrm westinrm commented Apr 24, 2023

Before this PR

Null header names, query parameter names/values, and path parameter names/values will throw exceptions during request execution or URL rendering respectively without any useful stack trace, making debugging the root cause of these issues difficult.

For example, this is the error when a null query parameter value is used:

java.lang.NullPointerException: Cannot invoke "String.getBytes(java.nio.charset.Charset)" because "source" is null
    at com.palantir.dialogue.core.BaseUrl$UrlEncoder.encode(BaseUrl.java:279)
    at com.palantir.dialogue.core.BaseUrl$UrlEncoder.encodeQueryNameOrValue(BaseUrl.java:271)
    at com.palantir.dialogue.core.BaseUrl$DefaultUrlBuilder.queryParam(BaseUrl.java:164)
    ...
    at com.palantir.dialogue.core.BaseUrl.render(BaseUrl.java:58)
    at com.palantir.dialogue.hc5.ApacheHttpClientBlockingChannel.execute(ApacheHttpClientBlockingChannel.java:99)
    ...

After this PR

==COMMIT_MSG==
Eagerly throw on null request parameters

By throwing when the null values are inserted into the Request builder it's possible to get a helpful stack trace instead of one that just traces back into one of Dialogue's async threads.
==COMMIT_MSG==

Possible downsides?

  • Slightly worse performance due to having to check all entries inserted against null

Null header names, query parameter names/values, and path parameter names/values
will throw exceptions during request execution or URL rendering respectively without
any useful stack trace, making debugging the root cause of these issues difficult.

By throwing when the null values are inserted into the Request builder it's possible
to get a more accurate stack trace.
@changelog-app
Copy link

changelog-app bot commented Apr 24, 2023

Generate changelog in changelog/@unreleased

Type

  • Feature
  • Improvement
  • Fix
  • Break
  • Deprecation
  • Manual task
  • Migration

Description

Eagerly throw on null request parameters (header names, query parameter names and values, path parameter names and values) to provide helpful stack traces into user code when null values are inserted into the Request builder instead of stack traces that just point to Dialogue's async threads.

Check the box to generate changelog(s)

  • Generate changelog entry

@@ -197,26 +199,38 @@ public Request.Builder headerParams(Multimap<String, ? extends String> entries)
}

public Request.Builder putAllHeaderParams(String key, Iterable<String> values) {
Preconditions.checkArgumentNotNull(key, "Header name must not be null");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's match conjure builder semantics where we Preconditions.checkNotNull individual elements (like this one), but we don't iterate through collections (like putAllHeaderParams(Multimap<String, ? extends String> entries) below)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am curious how your bug came about, I would have expected the recent nullaway rollout to catch that sort of thing before hitting prod. Perhaps there's a case we're missing?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still have no idea how my bug came about, which is part of why I want to make this change.
I'm okay trying just checking non-collections for now, though I doubt there are ever enough query/path/header parameters that this will actually be a notable performance hit.

mutableQueryParams().putAll(key, Arrays.asList(values));
return this;
}

public Request.Builder putQueryParams(String key, String value) {
Preconditions.checkNotNull(key, "Query parameter name must not be null");
Preconditions.checkNotNull(value, "Query parameter value must not be null");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we check key+value here, but only key in public Request.Builder putHeaderParams(String key, String value) {?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

headers actually permit null values from what I could determine, while query and path parameters don't

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if null headers don't result in failures, I'd consider that a bug, but reasonable not to make it throw in this change 👍

@bulldozer-bot bulldozer-bot bot merged commit 9c78ef0 into develop Apr 28, 2023
@bulldozer-bot bulldozer-bot bot deleted the westinm/eagerly-throw-on-null branch April 28, 2023 18:37
@svc-autorelease
Copy link
Collaborator

Released 3.83.0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants