88import org .apache .commons .collections4 .CollectionUtils ;
99import org .apache .commons .collections4 .MapUtils ;
1010import org .apache .commons .lang3 .StringUtils ;
11+ import org .apache .http .client .utils .URIBuilder ;
1112import org .prebid .server .exception .PreBidException ;
1213import org .prebid .server .execution .timeout .Timeout ;
1314import org .prebid .server .json .DecodeException ;
2526import org .prebid .server .vertx .httpclient .HttpClient ;
2627import org .prebid .server .vertx .httpclient .model .HttpClientResponse ;
2728
29+ import java .net .URISyntaxException ;
2830import java .util .ArrayList ;
2931import java .util .Collections ;
3032import java .util .HashMap ;
4446 * In order to enable caching and reduce latency for read operations {@link HttpApplicationSettings}
4547 * can be decorated by {@link CachingApplicationSettings}.
4648 * <p>
47- * Expected the endpoint to satisfy the following API:
49+ * Expected the endpoint to satisfy the following API (URL is encoded) :
4850 * <p>
4951 * GET {endpoint}?request-ids=["req1","req2"]&imp-ids=["imp1","imp2","imp3"]
5052 * <p>
53+ * or settings.http.rfc3986-compatible is set to true
54+ * <p>
55+ * * GET {endpoint}?request-id=req1&request-id=req2&imp-id=imp1&imp-id=imp2&imp-id=imp3
56+ * * <p>
5157 * This endpoint should return a payload like:
5258 * <pre>
5359 * {
@@ -76,15 +82,23 @@ public class HttpApplicationSettings implements ApplicationSettings {
7682 private final String categoryEndpoint ;
7783 private final HttpClient httpClient ;
7884 private final JacksonMapper mapper ;
85+ private final boolean isRfc3986Compatible ;
86+
87+ public HttpApplicationSettings (HttpClient httpClient ,
88+ JacksonMapper mapper ,
89+ String endpoint ,
90+ String ampEndpoint ,
91+ String videoEndpoint ,
92+ String categoryEndpoint ,
93+ boolean isRfc3986Compatible ) {
7994
80- public HttpApplicationSettings (HttpClient httpClient , JacksonMapper mapper , String endpoint , String ampEndpoint ,
81- String videoEndpoint , String categoryEndpoint ) {
8295 this .httpClient = Objects .requireNonNull (httpClient );
8396 this .mapper = Objects .requireNonNull (mapper );
8497 this .endpoint = HttpUtil .validateUrl (Objects .requireNonNull (endpoint ));
8598 this .ampEndpoint = HttpUtil .validateUrl (Objects .requireNonNull (ampEndpoint ));
8699 this .videoEndpoint = HttpUtil .validateUrl (Objects .requireNonNull (videoEndpoint ));
87100 this .categoryEndpoint = HttpUtil .validateUrl (Objects .requireNonNull (categoryEndpoint ));
101+ this .isRfc3986Compatible = isRfc3986Compatible ;
88102 }
89103
90104 @ Override
@@ -111,15 +125,20 @@ private Future<Set<Account>> fetchAccountsByIds(Set<String> accountIds, Timeout
111125 .recover (Future ::failedFuture );
112126 }
113127
114- private static String accountsRequestUrlFrom (String endpoint , Set <String > accountIds ) {
115- final StringBuilder url = new StringBuilder (endpoint );
116- url .append (endpoint .contains ("?" ) ? "&" : "?" );
117-
118- if (!accountIds .isEmpty ()) {
119- url .append ("account-ids=[\" " ).append (joinIds (accountIds )).append ("\" ]" );
128+ private String accountsRequestUrlFrom (String endpoint , Set <String > accountIds ) {
129+ try {
130+ final URIBuilder uriBuilder = new URIBuilder (endpoint );
131+ if (!accountIds .isEmpty ()) {
132+ if (isRfc3986Compatible ) {
133+ accountIds .forEach (accountId -> uriBuilder .addParameter ("account-id" , accountId ));
134+ } else {
135+ uriBuilder .addParameter ("account-ids" , "[\" %s\" ]" .formatted (joinIds (accountIds )));
136+ }
137+ }
138+ return uriBuilder .build ().toString ();
139+ } catch (URISyntaxException e ) {
140+ throw new PreBidException ("URL %s has bad syntax" .formatted (endpoint ));
120141 }
121-
122- return url .toString ();
123142 }
124143
125144 private Future <Set <Account >> processAccountsResponse (HttpClientResponse response , Set <String > accountIds ) {
@@ -165,9 +184,6 @@ public Future<StoredDataResult> getAmpStoredData(String accountId, Set<String> r
165184 return fetchStoredData (ampEndpoint , requestIds , Collections .emptySet (), timeout );
166185 }
167186
168- /**
169- * Not supported and returns failed result.
170- */
171187 @ Override
172188 public Future <StoredDataResult > getVideoStoredData (String accountId , Set <String > requestIds , Set <String > impIds ,
173189 Timeout timeout ) {
@@ -240,22 +256,27 @@ private Future<StoredDataResult> fetchStoredData(String endpoint, Set<String> re
240256 .recover (exception -> failStoredDataResponse (exception , requestIds , impIds ));
241257 }
242258
243- private static String storeRequestUrlFrom (String endpoint , Set <String > requestIds , Set <String > impIds ) {
244- final StringBuilder url = new StringBuilder (endpoint );
245- url .append (endpoint .contains ("?" ) ? "&" : "?" );
246-
247- if (!requestIds .isEmpty ()) {
248- url .append ("request-ids=[\" " ).append (joinIds (requestIds )).append ("\" ]" );
249- }
250-
251- if (!impIds .isEmpty ()) {
259+ private String storeRequestUrlFrom (String endpoint , Set <String > requestIds , Set <String > impIds ) {
260+ try {
261+ final URIBuilder uriBuilder = new URIBuilder (endpoint );
252262 if (!requestIds .isEmpty ()) {
253- url .append ("&" );
263+ if (isRfc3986Compatible ) {
264+ requestIds .forEach (requestId -> uriBuilder .addParameter ("request-id" , requestId ));
265+ } else {
266+ uriBuilder .addParameter ("request-ids" , "[\" %s\" ]" .formatted (joinIds (requestIds )));
267+ }
268+ }
269+ if (!impIds .isEmpty ()) {
270+ if (isRfc3986Compatible ) {
271+ impIds .forEach (impId -> uriBuilder .addParameter ("imp-id" , impId ));
272+ } else {
273+ uriBuilder .addParameter ("imp-ids" , "[\" %s\" ]" .formatted (joinIds (impIds )));
274+ }
254275 }
255- url .append ("imp-ids=[\" " ).append (joinIds (impIds )).append ("\" ]" );
276+ return uriBuilder .build ().toString ();
277+ } catch (URISyntaxException e ) {
278+ throw new PreBidException ("URL %s has bad syntax" .formatted (endpoint ));
256279 }
257-
258- return url .toString ();
259280 }
260281
261282 private static String joinIds (Set <String > ids ) {
0 commit comments