@@ -197,7 +197,19 @@ public static UriComponentsBuilder fromUri(URI uri) {
197
197
}
198
198
199
199
/**
200
- * Create a builder that is initialized with the given URI string.
200
+ * Variant of {@link #fromUriString(String, ParserType)} that defaults to
201
+ * the {@link ParserType#RFC} parsing.
202
+ */
203
+ public static UriComponentsBuilder fromUriString (String uri ) throws InvalidUrlException {
204
+ Assert .notNull (uri , "URI must not be null" );
205
+ if (uri .isEmpty ()) {
206
+ return new UriComponentsBuilder ();
207
+ }
208
+ return fromUriString (uri , ParserType .RFC );
209
+ }
210
+
211
+ /**
212
+ * Create a builder that is initialized by parsing the given URI string.
201
213
* <p><strong>Note:</strong> The presence of reserved characters can prevent
202
214
* correct parsing of the URI string. For example if a query parameter
203
215
* contains {@code '='} or {@code '&'} characters, the query string cannot
@@ -208,47 +220,27 @@ public static UriComponentsBuilder fromUri(URI uri) {
208
220
* UriComponentsBuilder.fromUriString(uriString).buildAndExpand("hot&cold");
209
221
* </pre>
210
222
* @param uri the URI string to initialize with
223
+ * @param parserType the parsing algorithm to use
211
224
* @return the new {@code UriComponentsBuilder}
212
225
* @throws InvalidUrlException if {@code uri} cannot be parsed
226
+ * @since 6.2
213
227
*/
214
- public static UriComponentsBuilder fromUriString (String uri ) throws InvalidUrlException {
228
+ public static UriComponentsBuilder fromUriString (String uri , ParserType parserType ) throws InvalidUrlException {
215
229
Assert .notNull (uri , "URI must not be null" );
216
-
230
+ if (uri .isEmpty ()) {
231
+ return new UriComponentsBuilder ();
232
+ }
217
233
UriComponentsBuilder builder = new UriComponentsBuilder ();
218
- if (!uri .isEmpty ()) {
219
- WhatWgUrlParser .UrlRecord urlRecord = WhatWgUrlParser .parse (uri , EMPTY_URL_RECORD , null , null );
220
- if (!urlRecord .scheme ().isEmpty ()) {
221
- builder .scheme (urlRecord .scheme ());
222
- }
223
- if (urlRecord .includesCredentials ()) {
224
- StringBuilder userInfo = new StringBuilder (urlRecord .username ());
225
- if (!urlRecord .password ().isEmpty ()) {
226
- userInfo .append (':' );
227
- userInfo .append (urlRecord .password ());
228
- }
229
- builder .userInfo (userInfo .toString ());
234
+ return switch (parserType ) {
235
+ case RFC -> {
236
+ RfcUriParser .UriRecord record = RfcUriParser .parse (uri );
237
+ yield builder .rfcUriRecord (record );
230
238
}
231
- if (urlRecord .host () != null && !(urlRecord .host () instanceof WhatWgUrlParser .EmptyHost )) {
232
- builder .host (urlRecord .host ().toString ());
239
+ case WHAT_WG -> {
240
+ WhatWgUrlParser .UrlRecord record = WhatWgUrlParser .parse (uri , EMPTY_URL_RECORD , null , null );
241
+ yield builder .whatWgUrlRecord (record );
233
242
}
234
- if (urlRecord .port () != null ) {
235
- builder .port (urlRecord .port ().toString ());
236
- }
237
- if (urlRecord .path ().isOpaque ()) {
238
- String ssp = urlRecord .path () + urlRecord .search ();
239
- builder .schemeSpecificPart (ssp );
240
- }
241
- else {
242
- builder .path (urlRecord .path ().toString ());
243
- if (StringUtils .hasLength (urlRecord .query ())) {
244
- builder .query (urlRecord .query ());
245
- }
246
- }
247
- if (StringUtils .hasLength (urlRecord .fragment ())) {
248
- builder .fragment (urlRecord .fragment ());
249
- }
250
- }
251
- return builder ;
243
+ };
252
244
}
253
245
254
246
/**
@@ -517,6 +509,58 @@ public UriComponentsBuilder uriComponents(UriComponents uriComponents) {
517
509
return this ;
518
510
}
519
511
512
+ /**
513
+ * Internal method to initialize this builder from an RFC {@code UriRecord}.
514
+ */
515
+ private UriComponentsBuilder rfcUriRecord (RfcUriParser .UriRecord record ) {
516
+ scheme (record .scheme ());
517
+ if (record .isOpaque ()) {
518
+ if (record .path () != null ) {
519
+ schemeSpecificPart (record .path ());
520
+ }
521
+ }
522
+ else {
523
+ userInfo (record .user ());
524
+ host (record .host ());
525
+ port (record .port ());
526
+ if (record .path () != null ) {
527
+ path (record .path ());
528
+ }
529
+ query (record .query ());
530
+ }
531
+ fragment (record .fragment ());
532
+ return this ;
533
+ }
534
+
535
+ /**
536
+ * Internal method to initialize this builder from a WhatWG {@code UrlRecord}.
537
+ */
538
+ private UriComponentsBuilder whatWgUrlRecord (WhatWgUrlParser .UrlRecord record ) {
539
+ if (!record .scheme ().isEmpty ()) {
540
+ scheme (record .scheme ());
541
+ }
542
+ if (record .path ().isOpaque ()) {
543
+ String ssp = record .path () + record .search ();
544
+ schemeSpecificPart (ssp );
545
+ }
546
+ else {
547
+ userInfo (record .userInfo ());
548
+ String hostname = record .hostname ();
549
+ if (StringUtils .hasText (hostname )) {
550
+ host (hostname );
551
+ }
552
+ if (record .port () != null ) {
553
+ port (record .portString ());
554
+ }
555
+ path (record .path ().toString ());
556
+ query (record .query ());
557
+ if (StringUtils .hasText (record .fragment ())) {
558
+ fragment (record .fragment ());
559
+ }
560
+ }
561
+ return this ;
562
+ }
563
+
520
564
@ Override
521
565
public UriComponentsBuilder scheme (@ Nullable String scheme ) {
522
566
this .scheme = scheme ;
@@ -790,6 +834,34 @@ private interface PathComponentBuilder {
790
834
}
791
835
792
836
837
+ /**
838
+ * Enum to represent different URI parsing mechanisms.
839
+ */
840
+ public enum ParserType {
841
+
842
+ /**
843
+ * Parser that expects URI's conforming to RFC 3986 syntax.
844
+ */
845
+ RFC ,
846
+
847
+ /**
848
+ * Parser based on algorithm defined in the WhatWG URL Living standard.
849
+ * Browsers use this algorithm to align on lenient parsing of user typed
850
+ * URL's that may deviate from RFC syntax.
851
+ * <p>For more details, see:
852
+ * <ul>
853
+ * <li><a href="https://url.spec.whatwg.org">URL Living Standard</a>
854
+ * <li><a href="https://url.spec.whatwg.org/#url-parsing">Section 4.4: URL parsing</a>
855
+ * <li><a href="https://github.com/web-platform-tests/wpt/tree/master/url">web-platform-tests</a>
856
+ * </ul>
857
+ * <p>Use this if you need to leniently handle URL's that don't conform
858
+ * to RFC syntax, or for alignment with browser parsing.
859
+ */
860
+ WHAT_WG
861
+
862
+ }
863
+
864
+
793
865
private static class CompositePathComponentBuilder implements PathComponentBuilder {
794
866
795
867
private final Deque <PathComponentBuilder > builders = new ArrayDeque <>();
0 commit comments