Skip to content

Commit 9efda56

Browse files
authored
Merge branch 'master' into conflict_fix
2 parents 07f1ded + 1ffab24 commit 9efda56

File tree

137 files changed

+1569
-504
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

137 files changed

+1569
-504
lines changed

client/trino-cli/src/main/java/io/trino/cli/ClientOptions.java

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
import static com.google.common.base.Strings.nullToEmpty;
4848
import static io.trino.cli.TerminalUtils.getTerminal;
4949
import static io.trino.client.KerberosUtil.defaultCredentialCachePath;
50+
import static io.trino.client.ProtocolHeaders.TRINO_HEADERS;
5051
import static io.trino.client.uri.PropertyName.ACCESS_TOKEN;
5152
import static io.trino.client.uri.PropertyName.CATALOG;
5253
import static io.trino.client.uri.PropertyName.CLIENT_INFO;
@@ -56,6 +57,7 @@
5657
import static io.trino.client.uri.PropertyName.EXTERNAL_AUTHENTICATION;
5758
import static io.trino.client.uri.PropertyName.EXTERNAL_AUTHENTICATION_REDIRECT_HANDLERS;
5859
import static io.trino.client.uri.PropertyName.EXTRA_CREDENTIALS;
60+
import static io.trino.client.uri.PropertyName.EXTRA_HEADERS;
5961
import static io.trino.client.uri.PropertyName.HTTP_LOGGING_LEVEL;
6062
import static io.trino.client.uri.PropertyName.HTTP_PROXY;
6163
import static io.trino.client.uri.PropertyName.KERBEROS_CONFIG_PATH;
@@ -204,6 +206,10 @@ public class ClientOptions
204206
@Option(names = "--client-tags", paramLabel = "<tags>", description = "Client tags", converter = ClientTagsConverter.class)
205207
public Optional<Set<String>> clientTags;
206208

209+
@PropertyMapping(EXTRA_HEADERS)
210+
@Option(names = "--extra-header", paramLabel = "<header>", description = "Additional HTTP header to add to HTTP requests (property can be used multiple times; format is key=value)")
211+
public final List<ExtraHeader> extraHeaders = new ArrayList<>();
212+
207213
@PropertyMapping(TRACE_TOKEN)
208214
@Option(names = "--trace-token", paramLabel = "<token>", description = "Trace token")
209215
public Optional<String> traceToken;
@@ -422,6 +428,9 @@ public TrinoUri getTrinoUri(Map<PropertyName, String> restrictedProperties)
422428
if (!sessionProperties.isEmpty()) {
423429
builder.setSessionProperties(toProperties(sessionProperties));
424430
}
431+
if (!extraHeaders.isEmpty()) {
432+
builder.setExtraHeaders(toExtraHeaders(extraHeaders));
433+
}
425434
if (!resourceEstimates.isEmpty()) {
426435
builder.setResourceEstimates(toResourceEstimates(resourceEstimates));
427436
}
@@ -498,6 +507,15 @@ public static URI parseServer(String server)
498507
}
499508
}
500509

510+
public static Map<String, String> toExtraHeaders(List<ExtraHeader> extraHeaders)
511+
{
512+
ImmutableMap.Builder<String, String> builder = ImmutableMap.builder();
513+
for (ExtraHeader extraHeader : extraHeaders) {
514+
builder.put(extraHeader.getHeader(), extraHeader.getValue());
515+
}
516+
return builder.buildOrThrow();
517+
}
518+
501519
private static Map<String, String> toProperties(List<ClientSessionProperty> sessionProperties)
502520
{
503521
ImmutableMap.Builder<String, String> builder = ImmutableMap.builder();
@@ -605,6 +623,59 @@ public int hashCode()
605623
}
606624
}
607625

626+
public static final class ExtraHeader
627+
{
628+
private final String header;
629+
private final String value;
630+
631+
public ExtraHeader(String headerAndValue)
632+
{
633+
List<String> nameValue = NAME_VALUE_SPLITTER.splitToList(headerAndValue);
634+
checkArgument(nameValue.size() == 2, "Header and value: %s", headerAndValue);
635+
this.header = nameValue.get(0);
636+
this.value = nameValue.get(1);
637+
638+
checkArgument(!TRINO_HEADERS.isProtocolHeader(header), "Header '%s' is a protocol header and cannot be set as an extra header", header);
639+
checkArgument(!header.isEmpty(), "Header name is empty");
640+
checkArgument(!value.isEmpty(), "Header value is empty");
641+
}
642+
643+
public ExtraHeader(String header, String value)
644+
{
645+
this.header = header;
646+
this.value = value;
647+
}
648+
649+
public String getHeader()
650+
{
651+
return header;
652+
}
653+
654+
public String getValue()
655+
{
656+
return value;
657+
}
658+
659+
@Override
660+
public boolean equals(Object o)
661+
{
662+
if (this == o) {
663+
return true;
664+
}
665+
if (o == null || getClass() != o.getClass()) {
666+
return false;
667+
}
668+
ExtraHeader other = (ExtraHeader) o;
669+
return Objects.equals(header, other.header) && Objects.equals(value, other.value);
670+
}
671+
672+
@Override
673+
public int hashCode()
674+
{
675+
return Objects.hash(header, value);
676+
}
677+
}
678+
608679
public static final class ClientSessionProperty
609680
{
610681
private static final Splitter NAME_SPLITTER = Splitter.on('.');

client/trino-cli/src/main/java/io/trino/cli/Trino.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import io.trino.cli.ClientOptions.ClientExtraCredential;
2121
import io.trino.cli.ClientOptions.ClientResourceEstimate;
2222
import io.trino.cli.ClientOptions.ClientSessionProperty;
23+
import io.trino.cli.ClientOptions.ExtraHeader;
2324
import org.jline.utils.AttributedStringBuilder;
2425
import org.jline.utils.AttributedStyle;
2526
import picocli.CommandLine;
@@ -62,6 +63,7 @@ public static CommandLine createCommandLine(Object command)
6263
.registerConverter(ClientResourceEstimate.class, ClientResourceEstimate::new)
6364
.registerConverter(ClientSessionProperty.class, ClientSessionProperty::new)
6465
.registerConverter(ClientExtraCredential.class, ClientExtraCredential::new)
66+
.registerConverter(ExtraHeader.class, ExtraHeader::new)
6567
.registerConverter(HostAndPort.class, HostAndPort::fromString)
6668
.registerConverter(Duration.class, Duration::valueOf)
6769
.setResourceBundle(new TrinoResourceBundle())

client/trino-cli/src/test/java/io/trino/cli/TestClientOptions.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,20 @@ public void testExtraCredentials()
246246
new ClientOptions.ClientExtraCredential("test.token.bar", "bar")));
247247
}
248248

249+
@Test
250+
public void testExtraHeaders()
251+
{
252+
Console console = createConsole("--extra-header", "X-Trino-Routing-Group=foo", "--extra-header", "x-foo=bar");
253+
ClientOptions options = console.clientOptions;
254+
assertThat(options.extraHeaders).isEqualTo(ImmutableList.of(
255+
new ClientOptions.ExtraHeader("X-Trino-Routing-Group", "foo"),
256+
new ClientOptions.ExtraHeader("x-foo", "bar")));
257+
258+
assertThatThrownBy(() -> createConsole("--extra-header", "X-Trino-User=Forbidden"))
259+
.hasCauseInstanceOf(IllegalArgumentException.class)
260+
.hasMessageContaining("Header 'X-Trino-User' is a protocol header and cannot be set as an extra header");
261+
}
262+
249263
@Test
250264
public void testSessionProperties()
251265
{

client/trino-client/src/main/java/io/trino/client/ClientSession.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ public class ClientSession
5151
private final ZoneId timeZone;
5252
private final Locale locale;
5353
private final Map<String, String> resourceEstimates;
54+
private final Map<String, String> extraHeaders;
5455
private final Map<String, String> properties;
5556
private final Map<String, String> preparedStatements;
5657
private final Map<String, ClientSelectedRole> roles;
@@ -87,6 +88,7 @@ private ClientSession(
8788
String source,
8889
Optional<String> traceToken,
8990
Set<String> clientTags,
91+
Map<String, String> extraHeaders,
9092
String clientInfo,
9193
Optional<String> catalog,
9294
Optional<String> schema,
@@ -112,6 +114,7 @@ private ClientSession(
112114
this.source = requireNonNull(source, "source is null");
113115
this.traceToken = requireNonNull(traceToken, "traceToken is null");
114116
this.clientTags = ImmutableSet.copyOf(requireNonNull(clientTags, "clientTags is null"));
117+
this.extraHeaders = ImmutableMap.copyOf(requireNonNull(extraHeaders, "extraHeaders is null"));
115118
this.clientInfo = clientInfo;
116119
this.catalog = catalog;
117120
this.schema = schema;
@@ -198,6 +201,11 @@ public Set<String> getClientTags()
198201
return clientTags;
199202
}
200203

204+
public Map<String, String> getExtraHeaders()
205+
{
206+
return extraHeaders;
207+
}
208+
201209
public String getClientInfo()
202210
{
203211
return clientInfo;
@@ -295,6 +303,7 @@ public String toString()
295303
.add("sessionUser", sessionUser)
296304
.add("authorizationUser", authorizationUser)
297305
.add("clientTags", clientTags)
306+
.add("extraHeaders", extraHeaders)
298307
.add("clientInfo", clientInfo)
299308
.add("catalog", catalog)
300309
.add("schema", schema)
@@ -324,6 +333,7 @@ public static final class Builder
324333
private String source;
325334
private Optional<String> traceToken = Optional.empty();
326335
private Set<String> clientTags = ImmutableSet.of();
336+
private Map<String, String> extraHeaders = ImmutableMap.of();
327337
private String clientInfo;
328338
private String catalog;
329339
private String schema;
@@ -354,6 +364,7 @@ private Builder(ClientSession clientSession)
354364
source = clientSession.getSource();
355365
traceToken = clientSession.getTraceToken();
356366
clientTags = clientSession.getClientTags();
367+
extraHeaders = clientSession.getExtraHeaders();
357368
clientInfo = clientSession.getClientInfo();
358369
catalog = clientSession.getCatalog().orElse(null);
359370
schema = clientSession.getSchema().orElse(null);
@@ -419,6 +430,12 @@ public Builder clientTags(Set<String> clientTags)
419430
return this;
420431
}
421432

433+
public Builder extraHeaders(Map<String, String> extraHeaders)
434+
{
435+
this.extraHeaders = extraHeaders;
436+
return this;
437+
}
438+
422439
public Builder clientInfo(String clientInfo)
423440
{
424441
this.clientInfo = clientInfo;
@@ -526,6 +543,7 @@ public ClientSession build()
526543
source,
527544
traceToken,
528545
clientTags,
546+
extraHeaders,
529547
clientInfo,
530548
Optional.ofNullable(catalog),
531549
Optional.ofNullable(schema),

client/trino-client/src/main/java/io/trino/client/OkHttpUtil.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
import java.security.cert.X509Certificate;
5656
import java.util.Arrays;
5757
import java.util.List;
58+
import java.util.Map;
5859
import java.util.Optional;
5960
import java.util.concurrent.TimeUnit;
6061

@@ -109,6 +110,17 @@ public static Interceptor tokenAuth(String accessToken)
109110
.build());
110111
}
111112

113+
public static Interceptor extraHeaders(Map<String, String> extraHeaders)
114+
{
115+
requireNonNull(extraHeaders, "extraHeaders is null");
116+
117+
return chain -> {
118+
okhttp3.Request.Builder builder = chain.request().newBuilder();
119+
extraHeaders.forEach(builder::addHeader);
120+
return chain.proceed(builder.build());
121+
};
122+
}
123+
112124
public static void setupTimeouts(OkHttpClient.Builder clientBuilder, int timeout, TimeUnit unit)
113125
{
114126
clientBuilder

client/trino-client/src/main/java/io/trino/client/ProtocolHeaders.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,16 @@ private ProtocolHeaders(String name)
194194
responseOriginalRole = RESPONSE_SET_ORIGINAL_ROLES.withProtocolName(name);
195195
}
196196

197+
public boolean isProtocolHeader(String headerName)
198+
{
199+
for (Headers header : Headers.values()) {
200+
if (header.withProtocolName(name).equalsIgnoreCase(headerName)) {
201+
return true;
202+
}
203+
}
204+
return false;
205+
}
206+
197207
public String getProtocolName()
198208
{
199209
return name;

client/trino-client/src/main/java/io/trino/client/uri/ConnectionProperties.java

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import com.google.common.base.CharMatcher;
1717
import com.google.common.base.Joiner;
1818
import com.google.common.base.Splitter;
19+
import com.google.common.collect.ImmutableMap;
1920
import com.google.common.collect.ImmutableSet;
2021
import com.google.common.net.HostAndPort;
2122
import io.airlift.units.Duration;
@@ -44,6 +45,7 @@
4445
import static com.google.common.collect.Streams.stream;
4546
import static io.trino.client.ClientSelectedRole.Type.ALL;
4647
import static io.trino.client.ClientSelectedRole.Type.NONE;
48+
import static io.trino.client.ProtocolHeaders.TRINO_HEADERS;
4749
import static io.trino.client.uri.AbstractConnectionProperty.Validator;
4850
import static io.trino.client.uri.AbstractConnectionProperty.validator;
4951
import static io.trino.client.uri.ConnectionProperties.SslVerificationMode.FULL;
@@ -99,6 +101,7 @@ enum SslVerificationMode
99101
public static final ConnectionProperty<String, Map<String, String>> EXTRA_CREDENTIALS = new ExtraCredentials();
100102
public static final ConnectionProperty<String, String> CLIENT_INFO = new ClientInfo();
101103
public static final ConnectionProperty<String, Set<String>> CLIENT_TAGS = new ClientTags();
104+
public static final ConnectionProperty<String, Map<String, String>> EXTRA_HEADERS = new ExtraHeaders();
102105
public static final ConnectionProperty<String, String> TRACE_TOKEN = new TraceToken();
103106
public static final ConnectionProperty<String, Map<String, String>> SESSION_PROPERTIES = new SessionProperties();
104107
public static final ConnectionProperty<String, String> SOURCE = new Source();
@@ -139,6 +142,7 @@ enum SslVerificationMode
139142
.add(EXTERNAL_AUTHENTICATION_TIMEOUT)
140143
.add(EXTERNAL_AUTHENTICATION_TOKEN_CACHE)
141144
.add(EXTRA_CREDENTIALS)
145+
.add(EXTRA_HEADERS)
142146
.add(HOSTNAME_IN_CERTIFICATE)
143147
.add(HTTP_LOGGING_LEVEL)
144148
.add(HTTP_PROXY)
@@ -789,6 +793,42 @@ public static String toString(Map<String, String> values)
789793
}
790794
}
791795

796+
private static class ExtraHeaders
797+
extends AbstractConnectionProperty<String, Map<String, String>>
798+
{
799+
private static final Validator<Properties> VALIDATE_EXTRA_HEADER = validator(
800+
ExtraHeaders::isNotReservedHeader,
801+
format("Connection property %s cannot override any of the Trino protocol headers", PropertyName.EXTRA_HEADERS));
802+
803+
public ExtraHeaders()
804+
{
805+
super(PropertyName.EXTRA_HEADERS, NOT_REQUIRED, VALIDATE_EXTRA_HEADER, converter(ExtraHeaders::parseExtraHeaders, ExtraHeaders::toString));
806+
}
807+
808+
// Extra headers consists of a list of header name value pairs.
809+
// E.g., `jdbc:trino://example.net:8080/?extraHeaders=X-Trino-Route:foo;X-Trino-Custom:bar` will send
810+
// HTTP headers `X-Trino-Route=foo` and `X-Trino-Custom=bar`.
811+
// These headers must not conflict with Trino protocol headers.
812+
public static Map<String, String> parseExtraHeaders(String extraHeadersString)
813+
{
814+
return new MapPropertyParser(PropertyName.EXTRA_HEADERS.toString()).parse(extraHeadersString);
815+
}
816+
817+
public static String toString(Map<String, String> values)
818+
{
819+
return values.entrySet().stream()
820+
.map(entry -> entry.getKey() + ":" + entry.getValue())
821+
.collect(Collectors.joining(";"));
822+
}
823+
824+
private static boolean isNotReservedHeader(Properties properties)
825+
{
826+
Map<String, String> extraHeaders = EXTRA_HEADERS.getValueOrDefault(properties, ImmutableMap.of());
827+
return extraHeaders.keySet().stream()
828+
.noneMatch(TRINO_HEADERS::isProtocolHeader);
829+
}
830+
}
831+
792832
private static class SessionProperties
793833
extends AbstractConnectionProperty<String, Map<String, String>>
794834
{

client/trino-client/src/main/java/io/trino/client/uri/HttpClientFactory.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030

3131
import static io.trino.client.KerberosUtil.defaultCredentialCachePath;
3232
import static io.trino.client.OkHttpUtil.basicAuth;
33+
import static io.trino.client.OkHttpUtil.extraHeaders;
3334
import static io.trino.client.OkHttpUtil.setupAlternateHostnameVerification;
3435
import static io.trino.client.OkHttpUtil.setupCookieJar;
3536
import static io.trino.client.OkHttpUtil.setupHttpLogging;
@@ -87,6 +88,10 @@ public static OkHttpClient.Builder toHttpClientBuilder(TrinoUri uri, String user
8788
builder.addNetworkInterceptor(tokenAuth(uri.getAccessToken().get()));
8889
}
8990

91+
if (!uri.getExtraHeaders().isEmpty()) {
92+
builder.addNetworkInterceptor(extraHeaders(uri.getExtraHeaders()));
93+
}
94+
9095
if (uri.isExternalAuthenticationEnabled()) {
9196
if (!uri.isUseSecureConnection()) {
9297
throw new RuntimeException("TLS/SSL required for authentication using external authorization");

client/trino-client/src/main/java/io/trino/client/uri/PropertyName.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ public enum PropertyName
3131
CLIENT_INFO("clientInfo"),
3232
CLIENT_TAGS("clientTags"),
3333
DISABLE_COMPRESSION("disableCompression"),
34+
EXTRA_HEADERS("extraHeaders"),
3435
DNS_RESOLVER("dnsResolver"),
3536
DNS_RESOLVER_CONTEXT("dnsResolverContext"),
3637
ENCODING("encoding"),

0 commit comments

Comments
 (0)