Skip to content

Commit af4141b

Browse files
Use CRT by default when h2 is needed
This updates the code generator to set the default http client to the CRT when the protocol configures it or when bidirectional event streaming is needed.
1 parent 3f372f6 commit af4141b

File tree

9 files changed

+110
-28
lines changed

9 files changed

+110
-28
lines changed

codegen/smithy-python-codegen-test/model/main.smithy

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@ use smithy.test#httpResponseTests
88
use smithy.waiters#waitable
99

1010
/// Provides weather forecasts.
11-
@restJson1
11+
@restJson1(
12+
http: ["h2", "http/1.1"]
13+
eventStreamHttp: ["h2"]
14+
)
1215
@fakeProtocol
1316
@paginated(inputToken: "nextToken", outputToken: "nextToken", pageSize: "pageSize")
1417
@httpApiKeyAuth(name: "weather-auth", in: "header")

codegen/smithy-python-codegen/src/main/java/software/amazon/smithy/python/codegen/ApplicationProtocol.java

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,20 @@
1717

1818
import software.amazon.smithy.codegen.core.Symbol;
1919
import software.amazon.smithy.codegen.core.SymbolReference;
20+
import software.amazon.smithy.model.node.ObjectNode;
2021
import software.amazon.smithy.utils.SmithyUnstableApi;
2122

2223
/**
2324
* Represents the resolves {@link Symbol}s and references for an
2425
* application protocol (e.g., "http", "mqtt", etc).
2526
*/
2627
@SmithyUnstableApi
27-
public record ApplicationProtocol(String name, SymbolReference requestType, SymbolReference responseType) {
28-
28+
public record ApplicationProtocol(
29+
String name,
30+
SymbolReference requestType,
31+
SymbolReference responseType,
32+
ObjectNode configuration
33+
) {
2934
/**
3035
* Checks if the protocol is an HTTP based protocol.
3136
*
@@ -40,18 +45,28 @@ public boolean isHttpProtocol() {
4045
*
4146
* @return Returns the created application protocol.
4247
*/
43-
public static ApplicationProtocol createDefaultHttpApplicationProtocol() {
48+
public static ApplicationProtocol createDefaultHttpApplicationProtocol(ObjectNode config) {
4449
return new ApplicationProtocol(
4550
"http",
4651
SymbolReference.builder()
4752
.symbol(createHttpSymbol("HTTPRequest"))
4853
.build(),
4954
SymbolReference.builder()
5055
.symbol(createHttpSymbol("HTTPResponse"))
51-
.build()
56+
.build(),
57+
config
5258
);
5359
}
5460

61+
/**
62+
* Creates a default HTTP application protocol.
63+
*
64+
* @return Returns the created application protocol.
65+
*/
66+
public static ApplicationProtocol createDefaultHttpApplicationProtocol() {
67+
return createDefaultHttpApplicationProtocol(ObjectNode.objectNode());
68+
}
69+
5570
private static Symbol createHttpSymbol(String symbolName) {
5671
PythonDependency dependency = SmithyPythonDependency.SMITHY_HTTP;
5772
return Symbol.builder()

codegen/smithy-python-codegen/src/main/java/software/amazon/smithy/python/codegen/ConfigGenerator.java

Lines changed: 65 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,14 @@
2020
import java.util.Collection;
2121
import java.util.LinkedHashMap;
2222
import java.util.List;
23+
import java.util.Locale;
2324
import software.amazon.smithy.codegen.core.Symbol;
25+
import software.amazon.smithy.model.knowledge.EventStreamIndex;
2426
import software.amazon.smithy.model.knowledge.ServiceIndex;
2527
import software.amazon.smithy.model.knowledge.TopDownIndex;
28+
import software.amazon.smithy.model.node.ArrayNode;
29+
import software.amazon.smithy.model.node.StringNode;
30+
import software.amazon.smithy.model.shapes.OperationShape;
2631
import software.amazon.smithy.python.codegen.integration.PythonIntegration;
2732
import software.amazon.smithy.python.codegen.integration.RuntimeClientPlugin;
2833
import software.amazon.smithy.python.codegen.sections.ConfigSection;
@@ -65,23 +70,8 @@ final class ConfigGenerator implements Runnable {
6570
);
6671

6772
// This list contains any properties that must be added to any http-based
68-
// service client.
73+
// service client, except for the http client itself.
6974
private static final List<ConfigProperty> HTTP_PROPERTIES = Arrays.asList(
70-
ConfigProperty.builder()
71-
.name("http_client")
72-
.type(Symbol.builder()
73-
.name("HTTPClient")
74-
.namespace("smithy_http.aio.interfaces", ".")
75-
.addDependency(SmithyPythonDependency.SMITHY_HTTP)
76-
.build())
77-
.documentation("The HTTP client used to make requests.")
78-
.nullable(false)
79-
.initialize(writer -> {
80-
writer.addDependency(SmithyPythonDependency.SMITHY_HTTP);
81-
writer.addImport("smithy_http.aio.aiohttp", "AIOHTTPClient");
82-
writer.write("self.http_client = http_client or AIOHTTPClient()");
83-
})
84-
.build(),
8575
ConfigProperty.builder()
8676
.name("http_request_config")
8777
.type(Symbol.builder()
@@ -137,6 +127,64 @@ final class ConfigGenerator implements Runnable {
137127
this.settings = settings;
138128
}
139129

130+
private static List<ConfigProperty> getHttpProperties(GenerationContext context) {
131+
var properties = new ArrayList<ConfigProperty>(HTTP_PROPERTIES.size() + 1);
132+
var clientBuilder = ConfigProperty.builder()
133+
.name("http_client")
134+
.type(Symbol.builder()
135+
.name("HTTPClient")
136+
.namespace("smithy_http.aio.interfaces", ".")
137+
.addDependency(SmithyPythonDependency.SMITHY_HTTP)
138+
.build())
139+
.documentation("The HTTP client used to make requests.")
140+
.nullable(false);
141+
142+
if (usesHttp2(context)) {
143+
clientBuilder
144+
.initialize(writer -> {
145+
writer.addDependency(SmithyPythonDependency.SMITHY_HTTP.withOptionalDependencies("awscrt"));
146+
writer.addImport("smithy_http.aio.crt", "AWSCRTHTTPClient");
147+
writer.write("self.http_client = http_client or AWSCRTHTTPClient()");
148+
});
149+
150+
} else {
151+
clientBuilder
152+
.initialize(writer -> {
153+
writer.addDependency(SmithyPythonDependency.SMITHY_HTTP.withOptionalDependencies("aiohttp"));
154+
writer.addImport("smithy_http.aio.aiohttp", "AIOHTTPClient");
155+
writer.write("self.http_client = http_client or AIOHTTPClient()");
156+
});
157+
}
158+
properties.add(clientBuilder.build());
159+
160+
properties.addAll(HTTP_PROPERTIES);
161+
return List.copyOf(properties);
162+
}
163+
164+
private static boolean usesHttp2(GenerationContext context) {
165+
var configuration = context.applicationProtocol().configuration();
166+
var httpVersions = configuration.getArrayMember("http")
167+
.orElse(ArrayNode.arrayNode())
168+
.getElementsAs(StringNode.class)
169+
.stream().map(node -> node.getValue().toLowerCase(Locale.ENGLISH)).toList();
170+
171+
// An explicit http2 configuration
172+
if (httpVersions.contains("h2")) {
173+
return true;
174+
}
175+
176+
// Bidirectional streaming REQUIRES h2 inherently
177+
var eventIndex = EventStreamIndex.of(context.model());
178+
var topDownIndex = TopDownIndex.of(context.model());
179+
for (OperationShape operation : topDownIndex.getContainedOperations(context.settings().service())) {
180+
if (eventIndex.getInputInfo(operation).isPresent() && eventIndex.getOutputInfo(operation).isPresent()) {
181+
return true;
182+
}
183+
}
184+
185+
return false;
186+
}
187+
140188
private static List<ConfigProperty> getHttpAuthProperties(GenerationContext context) {
141189
return List.of(
142190
ConfigProperty.builder()
@@ -254,7 +302,7 @@ private void generateConfig(GenerationContext context, PythonWriter writer) {
254302
// and add them in if the protocol is going to need them.
255303
var serviceIndex = ServiceIndex.of(context.model());
256304
if (context.applicationProtocol().isHttpProtocol()) {
257-
properties.addAll(HTTP_PROPERTIES);
305+
properties.addAll(getHttpProperties(context));
258306
if (!serviceIndex.getAuthSchemes(settings.service()).isEmpty()) {
259307
properties.addAll(getHttpAuthProperties(context));
260308
writer.onSection(new AddAuthHelper());

codegen/smithy-python-codegen/src/main/java/software/amazon/smithy/python/codegen/GenerationContext.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ public ProtocolGenerator protocolGenerator() {
9696
*/
9797
public ApplicationProtocol applicationProtocol() {
9898
return protocolGenerator != null
99-
? protocolGenerator.getApplicationProtocol()
99+
? protocolGenerator.getApplicationProtocol(this)
100100
: ApplicationProtocol.createDefaultHttpApplicationProtocol();
101101
}
102102

codegen/smithy-python-codegen/src/main/java/software/amazon/smithy/python/codegen/PythonDependency.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,10 @@ public SymbolDependency getDependency() {
5656
.build();
5757
}
5858

59+
public PythonDependency withOptionalDependencies(String... optionalDependencies) {
60+
return new PythonDependency(packageName, version, type, isLink, List.of(optionalDependencies));
61+
}
62+
5963
/**
6064
* An enum of valid dependency types.
6165
*/

codegen/smithy-python-codegen/src/main/java/software/amazon/smithy/python/codegen/SmithyPythonDependency.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,7 @@ public final class SmithyPythonDependency {
4949
// You'll need to locally install this before we publish
5050
"==0.0.1",
5151
Type.DEPENDENCY,
52-
false,
53-
// TODO: make this configurable
54-
List.of("aiohttp")
52+
false
5553
);
5654

5755
/**

codegen/smithy-python-codegen/src/main/java/software/amazon/smithy/python/codegen/integration/HttpBindingProtocolGenerator.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ public abstract class HttpBindingProtocolGenerator implements ProtocolGenerator
9292
private final Set<Shape> deserializingDocumentShapes = new TreeSet<>();
9393

9494
@Override
95-
public ApplicationProtocol getApplicationProtocol() {
95+
public ApplicationProtocol getApplicationProtocol(GenerationContext context) {
9696
return ApplicationProtocol.createDefaultHttpApplicationProtocol();
9797
}
9898

codegen/smithy-python-codegen/src/main/java/software/amazon/smithy/python/codegen/integration/ProtocolGenerator.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ default String getName() {
5555
*
5656
* @return Returns the created application protocol.
5757
*/
58-
ApplicationProtocol getApplicationProtocol();
58+
ApplicationProtocol getApplicationProtocol(GenerationContext context);
5959

6060

6161
/**

codegen/smithy-python-codegen/src/main/java/software/amazon/smithy/python/codegen/integration/RestJsonProtocolGenerator.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
import java.util.Set;
2020
import software.amazon.smithy.aws.traits.protocols.RestJson1Trait;
2121
import software.amazon.smithy.model.knowledge.HttpBinding;
22+
import software.amazon.smithy.model.node.ArrayNode;
23+
import software.amazon.smithy.model.node.ObjectNode;
2224
import software.amazon.smithy.model.shapes.MemberShape;
2325
import software.amazon.smithy.model.shapes.OperationShape;
2426
import software.amazon.smithy.model.shapes.Shape;
@@ -27,6 +29,7 @@
2729
import software.amazon.smithy.model.traits.StreamingTrait;
2830
import software.amazon.smithy.model.traits.TimestampFormatTrait.Format;
2931
import software.amazon.smithy.protocoltests.traits.HttpMessageTestCase;
32+
import software.amazon.smithy.python.codegen.ApplicationProtocol;
3033
import software.amazon.smithy.python.codegen.CodegenUtils;
3134
import software.amazon.smithy.python.codegen.GenerationContext;
3235
import software.amazon.smithy.python.codegen.HttpProtocolTestGenerator;
@@ -90,6 +93,17 @@ public ShapeId getProtocol() {
9093
return RestJson1Trait.ID;
9194
}
9295

96+
@Override
97+
public ApplicationProtocol getApplicationProtocol(GenerationContext context) {
98+
var service = context.settings().service(context.model());
99+
var trait = service.expectTrait(RestJson1Trait.class);
100+
var config = ObjectNode.builder()
101+
.withMember("http", ArrayNode.fromStrings(trait.getHttp()))
102+
.withMember("eventStreamHttp", ArrayNode.fromStrings(trait.getEventStreamHttp()))
103+
.build();
104+
return ApplicationProtocol.createDefaultHttpApplicationProtocol(config);
105+
}
106+
93107
@Override
94108
protected Format getDocumentTimestampFormat() {
95109
return Format.EPOCH_SECONDS;

0 commit comments

Comments
 (0)