2121import software .amazon .smithy .java .core .serde .ShapeSerializer ;
2222import software .amazon .smithy .java .core .serde .TimestampFormatter ;
2323import software .amazon .smithy .java .http .api .HeaderName ;
24+ import software .amazon .smithy .java .io .uri .URLEncoding ;
2425import software .amazon .smithy .model .shapes .ShapeType ;
2526import software .amazon .smithy .model .traits .HttpTrait ;
2627import software .amazon .smithy .utils .SmithyInternalApi ;
@@ -40,6 +41,10 @@ public final class HttpBindingSchemaExtensions
4041 */
4142 public static final SchemaExtensionKey <HttpBindingExt > KEY = new SchemaExtensionKey <>();
4243
44+ private static final Schema [] NO_SCHEMAS = new Schema [0 ];
45+ private static final HeaderName [] NO_HEADER_NAMES = new HeaderName [0 ];
46+ private static final String [] NO_STRINGS = new String [0 ];
47+
4348 /**
4449 * Look up the {@link MemberBinding} for a member schema. Throws if no binding or not a member.
4550 */
@@ -87,9 +92,6 @@ static StructBindings structBindingsOf(Schema schema) {
8792 return sb ;
8893 }
8994
90- private static final Schema [] NO_SCHEMAS = new Schema [0 ];
91- private static final String [] NO_QUERY_LITERALS = new String [0 ];
92-
9395 /**
9496 * Binding kind for a single member, derived from the traits applied to it.
9597 *
@@ -356,12 +358,14 @@ ByteBuffer emptyBody(Codec codec, Schema schema) {
356358 * Pre-computed HTTP-binding data for an operation schema.
357359 *
358360 * @param httpTrait the cached {@code @http} trait — saves an {@code expectTrait} call per request.
359- * @param queryLiterals flat array of (name, value) pairs from the URI's static query literals, or empty.
361+ * @param queryLiteralKeys raw {@code @http} URI query literal keys (e.g. {@code ["x-id"]}).
362+ * @param queryLiteralEntries pre-encoded {@code "key=value"} strings parallel to {@link #queryLiteralKeys}.
360363 * @param defaultResponseStatus default response status declared by the {@code @http} trait.
361364 */
362365 record OperationBinding (
363366 HttpTrait httpTrait ,
364- String [] queryLiterals ,
367+ String [] queryLiteralKeys ,
368+ String [] queryLiteralEntries ,
365369 int defaultResponseStatus ) implements HttpBindingExt {}
366370
367371 @ Override
@@ -732,9 +736,6 @@ private static Schema[] toArray(List<Schema> list) {
732736 return list .isEmpty () ? NO_SCHEMAS : list .toArray (new Schema [0 ]);
733737 }
734738
735- private static final HeaderName [] NO_HEADER_NAMES = new HeaderName [0 ];
736- private static final String [] NO_STRINGS = new String [0 ];
737-
738739 /**
739740 * Pre-resolve canonical {@link HeaderName}s parallel to a {@code Schema[]} of
740741 * list-header members. Reading the name later in the deserializer becomes a
@@ -772,21 +773,31 @@ private static OperationBinding forOperation(Schema schema) {
772773 var httpTrait = schema .expectTrait (TraitKey .HTTP_TRAIT );
773774 var uriPattern = httpTrait .getUri ();
774775
775- // Flatten query literals into a (name, value) pair array. The trait's own map iteration is stable for a
776- // given trait instance, but this avoids the per-call iterator + Map.Entry allocations .
776+ // Pre-encode static @http URI query literals into "key= value" strings so the request hot path can append
777+ // them verbatim instead of re-encoding per call .
777778 var queryLiteralMap = uriPattern .getQueryLiterals ();
778- String [] queryLiterals ;
779+ String [] queryLiteralKeys ;
780+ String [] queryLiteralEntries ;
779781 if (queryLiteralMap .isEmpty ()) {
780- queryLiterals = NO_QUERY_LITERALS ;
782+ queryLiteralKeys = NO_STRINGS ;
783+ queryLiteralEntries = NO_STRINGS ;
781784 } else {
782- queryLiterals = new String [2 * queryLiteralMap .size ()];
785+ int n = queryLiteralMap .size ();
786+ queryLiteralKeys = new String [n ];
787+ queryLiteralEntries = new String [n ];
783788 int i = 0 ;
789+ StringBuilder pair = new StringBuilder ();
784790 for (var entry : queryLiteralMap .entrySet ()) {
785- queryLiterals [i ++] = entry .getKey ();
786- queryLiterals [i ++] = entry .getValue ();
791+ pair .setLength (0 );
792+ URLEncoding .encodeUnreserved (entry .getKey (), pair , false );
793+ pair .append ('=' );
794+ URLEncoding .encodeUnreserved (entry .getValue (), pair , false );
795+ queryLiteralKeys [i ] = entry .getKey ();
796+ queryLiteralEntries [i ] = pair .toString ();
797+ i ++;
787798 }
788799 }
789800
790- return new OperationBinding (httpTrait , queryLiterals , httpTrait .getCode ());
801+ return new OperationBinding (httpTrait , queryLiteralKeys , queryLiteralEntries , httpTrait .getCode ());
791802 }
792803}
0 commit comments