-
Notifications
You must be signed in to change notification settings - Fork 2.5k
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
CORE: Allow HTTPClient to parse headers from properties. #12595
base: main
Are you sure you want to change the base?
CORE: Allow HTTPClient to parse headers from properties. #12595
Conversation
@wolflex888 thank you so much for reporting this. I was initially surprised because this functionality was supposed to work just as before. After digging a bit, I found the culprit: in this commit I inadvertently removed one line that should not have been removed: 77a25fa#diff-86450612dbe323d6d06cbc3846aa1913f042eaedadc0ca027c36bfbe08d3a46cL174 I think the proper "fix" would be to just put that line back. For example: public RESTSessionCatalog() {
this(
config -> {
HTTPClient.Builder builder =
HTTPClient.builder(config).uri(config.get(CatalogProperties.URI));
configHeaders(config).forEach(builder::withHeader);
return builder.build();
},
null);
} I am sorry for the inconvenience. |
Let me test that a bit. I think i did something similar and it did not work as expected because there's also this |
@@ -55,7 +55,7 @@ public class RESTCatalog | |||
public RESTCatalog() { | |||
this( | |||
SessionCatalog.SessionContext.createEmpty(), | |||
config -> HTTPClient.builder(config).uri(config.get(CatalogProperties.URI)).build()); | |||
config -> HTTPClient.builder(config).uri(config.get(CatalogProperties.URI)).withHeaders(RESTUtil.configHeaders(config)).build()); |
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.
I am moving out the function configHeaders
to RESTUtil
so RESTCatalog
can use it here. It's passing the clientBuilder
to RESTSessionCatalog
in class construction; therefore, only doing this in RESTSessionCatalog
would not work.
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.
Thanks @wolflex888 for fixing this!
\cc @nastra
@@ -55,7 +55,11 @@ public class RESTCatalog | |||
public RESTCatalog() { | |||
this( | |||
SessionCatalog.SessionContext.createEmpty(), | |||
config -> HTTPClient.builder(config).uri(config.get(CatalogProperties.URI)).build()); | |||
config -> |
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.
we haven't been setting the headers here previously which might be because it was missed earlier. I believe it should be fine to set them here
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.
can you please add a test to TestRESTCatalog
that make sure that headers are properly passed from the properties
to the underlying HTTP client when a request is being done?
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.
Thanks @wolflex888 for catching this. I'm +1 pending addition of tests per @nastra
@@ -53,9 +54,11 @@ public class RESTCatalog | |||
private final ViewCatalog viewSessionCatalog; | |||
|
|||
public RESTCatalog() { | |||
this( | |||
SessionCatalog.SessionContext.createEmpty(), |
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.
all of this refactoring shouldn't really be necessary
@@ -38,6 +40,12 @@ public class RESTUtil { | |||
private static final Joiner NAMESPACE_ESCAPED_JOINER = Joiner.on(NAMESPACE_ESCAPED_SEPARATOR); | |||
private static final Splitter NAMESPACE_ESCAPED_SPLITTER = | |||
Splitter.on(NAMESPACE_ESCAPED_SEPARATOR); | |||
public static final Function<Map<String, String>, RESTClient> DEFAULT_CLIENT_BUILDER = |
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.
this shouldn't be necessary. Can you please revert the refactorings from 1536b7f and only add the new tests?
@@ -32,6 +32,7 @@ private CatalogProperties() {} | |||
public static final String VIEW_DEFAULT_PREFIX = "view-default."; | |||
public static final String VIEW_OVERRIDE_PREFIX = "view-override."; | |||
public static final String METRICS_REPORTER_IMPL = "metrics-reporter-impl"; | |||
public static final String HTTP_HEADER_PREFIX = "header."; |
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.
this is specific to REST, so shouldn't be in CatalogProperties
. RESTUtil
would be the right place but I'm currently not sure whether we should be exposing this
@@ -332,6 +330,27 @@ public void testInitializeWithBadArguments() throws IOException { | |||
restCat.close(); | |||
} | |||
|
|||
@Test | |||
public void testCatalogProperties() { |
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.
I was thinking of something similar to this, where we provide a custom client that we spy on and check what headers are sent:
@Test
public void testDefaultHeadersPropagated() {
Map<String, String> catalogHeaders =
ImmutableMap.of("Authorization", "Bearer client-credentials-token:sub=catalog");
Map<String, String> properties =
ImmutableMap.of(
"header.test-header",
"test-value",
CatalogProperties.URI,
httpServer.getURI().toString());
Map<String, String> expectedHeaders = ImmutableMap.of("test-header", "test-value");
HTTPClient client =
Mockito.spy(
HTTPClient.builder(properties)
.withHeaders(RESTUtil.merge(RESTUtil.configHeaders(properties), catalogHeaders))
.uri(properties.get(CatalogProperties.URI))
.build());
RESTCatalog catalog =
new RESTCatalog(SessionCatalog.SessionContext.createEmpty(), (config) -> client);
catalog.initialize("test", properties);
assertThat(catalog.namespaceExists(Namespace.of("non-existing"))).isFalse();
Mockito.verify(client)
.execute(
reqMatcher(HTTPMethod.GET, "v1/config", expectedHeaders, Map.of()),
eq(ConfigResponse.class),
any(),
any());
Mockito.verify(client)
.execute(
reqMatcher(HTTPMethod.HEAD, "v1/namespaces/non-existing", expectedHeaders, Map.of()),
any(),
any(),
any());
}
I haven't executed/tried this, so you might need to tune/adjust this test a little bit
.uri(properties.get(CatalogProperties.URI)) | ||
.build()); | ||
RESTCatalog catalog = | ||
new RESTCatalog(SessionCatalog.SessionContext.createEmpty(), (config) -> client); |
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.
Still trying to figure out why passing in HTTPClient using a lambda function here will close the client.
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.
It will be closed by this try-with-resources block when initialize
is called:
try (RESTClient initClient = clientBuilder.apply(props); |
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.
Sorry for being slow, but how do we usually deal with this kind of situation? Normally, clientBuilder
will build a new HttpClient for us to use, but in this case we are returning a static client. We can't do Mokito.spy
unless it's static.
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.
Try this:
Mockito.doNothing().when(client).close();
Currently, a header can be set within the catalog properties with a prefix
header.
. We are using this property to pass in properties likeheader.X-Iceberg-Access-Delegation
to enablevended-credential
. The base headers were defined here back in 1.7.x, but this behavior disappeared when we refactored the code to use AuthManager.I want to have HTTPClient parse the headers from the configs it got from
RestSessionCatalog
instead of havingRestSessionCatalog
set it every time it wants to make a request.