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,20 +82,27 @@ 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 );
84- this .endpoint = HttpUtil .validateUrl (Objects .requireNonNull (endpoint ));
85- this .ampEndpoint = HttpUtil .validateUrl (Objects .requireNonNull (ampEndpoint ));
86- this .videoEndpoint = HttpUtil .validateUrl (Objects .requireNonNull (videoEndpoint ));
87- this .categoryEndpoint = HttpUtil .validateUrl (Objects .requireNonNull (categoryEndpoint ));
97+ this .endpoint = HttpUtil .validateUrlSyntax (Objects .requireNonNull (endpoint ));
98+ this .ampEndpoint = HttpUtil .validateUrlSyntax (Objects .requireNonNull (ampEndpoint ));
99+ this .videoEndpoint = HttpUtil .validateUrlSyntax (Objects .requireNonNull (videoEndpoint ));
100+ this .categoryEndpoint = HttpUtil .validateUrlSyntax (Objects .requireNonNull (categoryEndpoint ));
101+ this .isRfc3986Compatible = isRfc3986Compatible ;
88102 }
89103
90104 @ Override
91105 public Future <Account > getAccountById (String accountId , Timeout timeout ) {
92-
93106 return fetchAccountsByIds (Collections .singleton (accountId ), timeout )
94107 .map (accounts -> accounts .stream ()
95108 .findFirst ()
@@ -111,15 +124,20 @@ private Future<Set<Account>> fetchAccountsByIds(Set<String> accountIds, Timeout
111124 .recover (Future ::failedFuture );
112125 }
113126
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 ("\" ]" );
127+ private String accountsRequestUrlFrom (String endpoint , Set <String > accountIds ) {
128+ try {
129+ final URIBuilder uriBuilder = new URIBuilder (endpoint );
130+ if (!accountIds .isEmpty ()) {
131+ if (isRfc3986Compatible ) {
132+ accountIds .forEach (accountId -> uriBuilder .addParameter ("account-id" , accountId ));
133+ } else {
134+ uriBuilder .addParameter ("account-ids" , "[\" %s\" ]" .formatted (joinIds (accountIds )));
135+ }
136+ }
137+ return uriBuilder .build ().toString ();
138+ } catch (URISyntaxException e ) {
139+ throw new PreBidException ("URL %s has bad syntax" .formatted (endpoint ));
120140 }
121-
122- return url .toString ();
123141 }
124142
125143 private Future <Set <Account >> processAccountsResponse (HttpClientResponse response , Set <String > accountIds ) {
@@ -165,9 +183,6 @@ public Future<StoredDataResult> getAmpStoredData(String accountId, Set<String> r
165183 return fetchStoredData (ampEndpoint , requestIds , Collections .emptySet (), timeout );
166184 }
167185
168- /**
169- * Not supported and returns failed result.
170- */
171186 @ Override
172187 public Future <StoredDataResult > getVideoStoredData (String accountId , Set <String > requestIds , Set <String > impIds ,
173188 Timeout timeout ) {
@@ -240,22 +255,27 @@ private Future<StoredDataResult> fetchStoredData(String endpoint, Set<String> re
240255 .recover (exception -> failStoredDataResponse (exception , requestIds , impIds ));
241256 }
242257
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 ()) {
258+ private String storeRequestUrlFrom (String endpoint , Set <String > requestIds , Set <String > impIds ) {
259+ try {
260+ final URIBuilder uriBuilder = new URIBuilder (endpoint );
252261 if (!requestIds .isEmpty ()) {
253- url .append ("&" );
262+ if (isRfc3986Compatible ) {
263+ requestIds .forEach (requestId -> uriBuilder .addParameter ("request-id" , requestId ));
264+ } else {
265+ uriBuilder .addParameter ("request-ids" , "[\" %s\" ]" .formatted (joinIds (requestIds )));
266+ }
267+ }
268+ if (!impIds .isEmpty ()) {
269+ if (isRfc3986Compatible ) {
270+ impIds .forEach (impId -> uriBuilder .addParameter ("imp-id" , impId ));
271+ } else {
272+ uriBuilder .addParameter ("imp-ids" , "[\" %s\" ]" .formatted (joinIds (impIds )));
273+ }
254274 }
255- url .append ("imp-ids=[\" " ).append (joinIds (impIds )).append ("\" ]" );
275+ return uriBuilder .build ().toString ();
276+ } catch (URISyntaxException e ) {
277+ throw new PreBidException ("URL %s has bad syntax" .formatted (endpoint ));
256278 }
257-
258- return url .toString ();
259279 }
260280
261281 private static String joinIds (Set <String > ids ) {
0 commit comments