18
18
19
19
import java .util .Set ;
20
20
21
- import org .springframework .lang .Contract ;
21
+ import org .apache .commons .logging .Log ;
22
+
23
+ import org .springframework .core .log .LogDelegateFactory ;
22
24
import org .springframework .lang .Nullable ;
23
25
import org .springframework .util .Assert ;
24
26
25
27
/**
26
- * Parser for URI's based on the syntax in RFC 3986.
28
+ * Parser for URI's based on RFC 3986 syntax .
27
29
*
28
30
* @author Rossen Stoyanchev
29
31
* @since 6.2
32
34
*/
33
35
abstract class RfcUriParser {
34
36
37
+ private static final Log logger = LogDelegateFactory .getHiddenLog (RfcUriParser .class );
38
+
35
39
36
40
/**
37
41
* Parse the given URI string.
@@ -44,19 +48,20 @@ public static UriRecord parse(String uri) {
44
48
}
45
49
46
50
47
- @ Contract ("false, _ -> fail" )
48
- private static void verify (boolean expression , String message ) {
51
+ private static void verify (boolean expression , InternalParser parser , String message ) {
49
52
if (!expression ) {
50
- fail (message );
53
+ fail (parser , message );
51
54
}
52
55
}
53
56
54
- public static void verifyIsHexDigit (char c , String message ) {
55
- verify ((c >= 'a' && c <= 'f' ) || (c >= 'A' && c <= 'F' ) || (c >= '0' && c <= '9' ), message );
57
+ private static void verifyIsHexDigit (char c , InternalParser parser , String message ) {
58
+ verify ((c >= 'a' && c <= 'f' ) || (c >= 'A' && c <= 'F' ) || (c >= '0' && c <= '9' ), parser , message );
56
59
}
57
60
58
- @ Contract ("_ -> fail" )
59
- private static void fail (String message ) {
61
+ private static void fail (InternalParser parser , String message ) {
62
+ if (logger .isTraceEnabled ()) {
63
+ logger .trace (InvalidUrlException .class .getSimpleName () + ": \" " + message + "\" " + parser );
64
+ }
60
65
throw new InvalidUrlException (message );
61
66
}
62
67
@@ -202,7 +207,7 @@ public void handleNext(InternalParser parser, char c, int i) {
202
207
parser .captureUser ().componentIndex (i + 1 );
203
208
break ;
204
209
case '[' :
205
- verify (parser .isAtStartOfComponent (), "Bad authority" );
210
+ verify (parser .isAtStartOfComponent (), parser , "Bad authority" );
206
211
parser .advanceTo (IPV6 );
207
212
break ;
208
213
case '%' :
@@ -212,7 +217,7 @@ public void handleNext(InternalParser parser, char c, int i) {
212
217
boolean isAllowed = (parser .processCurlyBrackets (c ) ||
213
218
parser .countDownPercentEncodingInHost (c ) ||
214
219
HierarchicalUriComponents .Type .URI .isUnreservedOrSubDelimiter (c ));
215
- verify (isAllowed , "Bad authority" );
220
+ verify (isAllowed , parser , "Bad authority" );
216
221
}
217
222
}
218
223
@@ -242,13 +247,13 @@ public void handleNext(InternalParser parser, char c, int i) {
242
247
case ':' :
243
248
break ;
244
249
default :
245
- verifyIsHexDigit (c , "Bad authority" );
250
+ verifyIsHexDigit (c , parser , "Bad authority" );
246
251
}
247
252
}
248
253
249
254
@ Override
250
255
public void handleEnd (InternalParser parser ) {
251
- verify (parser .hasHost (), "Bad authority" ); // no closing ']'
256
+ verify (parser .hasHost (), parser , "Bad authority" ); // no closing ']'
252
257
}
253
258
},
254
259
@@ -257,7 +262,7 @@ public void handleEnd(InternalParser parser) {
257
262
@ Override
258
263
public void handleNext (InternalParser parser , char c , int i ) {
259
264
if (c == '@' ) {
260
- verify (!parser .hasUser (), "Bad authority" );
265
+ verify (!parser .hasUser (), parser , "Bad authority" );
261
266
parser .switchPortForFullPassword ().advanceTo (HOST , i + 1 );
262
267
}
263
268
else if (c == '/' ) {
@@ -274,7 +279,7 @@ else if (HierarchicalUriComponents.Type.URI.isUnreservedOrSubDelimiter(c) || c =
274
279
parser .switchPortForPassword ().advanceTo (HOST );
275
280
return ;
276
281
}
277
- fail ("Bad authority" );
282
+ fail (parser , "Bad authority" );
278
283
}
279
284
}
280
285
@@ -342,7 +347,7 @@ public void handleEnd(InternalParser parser) {
342
347
343
348
@ Override
344
349
public void handleNext (InternalParser parser , char c , int i ) {
345
- fail ("Bad character '*'" );
350
+ fail (parser , "Bad character '*'" );
346
351
}
347
352
348
353
@ Override
@@ -422,31 +427,6 @@ public InternalParser(String uri) {
422
427
this .uri = uri ;
423
428
}
424
429
425
- /**
426
- * Parse the input string and return a {@link UriRecord} with the results.
427
- */
428
- public UriRecord parse () {
429
- Assert .isTrue (this .state == State .START && this .index == 0 , "Internal Error" );
430
-
431
- while (hasNext ()) {
432
- this .state .handleNext (this , charAtIndex (), this .index );
433
- this .index ++;
434
- }
435
-
436
- this .state .handleEnd (this );
437
-
438
- return new UriRecord (this .scheme , this .isOpaque ,
439
- this .user , this .host , this .port , this .path , this .query , this .fragment );
440
- }
441
-
442
- public boolean hasNext () {
443
- return (this .index < this .uri .length ());
444
- }
445
-
446
- public char charAtIndex () {
447
- return this .uri .charAt (this .index );
448
- }
449
-
450
430
// Check internal state
451
431
452
432
public boolean hasScheme () {
@@ -469,15 +449,43 @@ public boolean isAtStartOfComponent() {
469
449
return (this .index == this .componentIndex );
470
450
}
471
451
452
+ // Top-level parse loop, iterate over chars and delegate to states
453
+
454
+ public UriRecord parse () {
455
+ Assert .isTrue (this .state == State .START && this .index == 0 , "Internal Error" );
456
+
457
+ while (hasNext ()) {
458
+ this .state .handleNext (this , charAtIndex (), this .index );
459
+ this .index ++;
460
+ }
461
+
462
+ this .state .handleEnd (this );
463
+
464
+ return new UriRecord (this .scheme , this .isOpaque ,
465
+ this .user , this .host , this .port , this .path , this .query , this .fragment );
466
+ }
467
+
468
+ public boolean hasNext () {
469
+ return (this .index < this .uri .length ());
470
+ }
471
+
472
+ public char charAtIndex () {
473
+ return this .uri .charAt (this .index );
474
+ }
475
+
472
476
// Transitions and index updates
473
477
474
478
public void advanceTo (State state ) {
479
+ if (logger .isTraceEnabled ()) {
480
+ logger .trace (this .state + " -> " + state + ", " +
481
+ "index=" + this .index + ", componentIndex=" + this .componentIndex );
482
+ }
475
483
this .state = state ;
476
484
}
477
485
478
486
public void advanceTo (State state , int componentIndex ) {
479
- this .state = state ;
480
487
this .componentIndex = componentIndex ;
488
+ advanceTo (state );
481
489
}
482
490
483
491
public InternalParser componentIndex (int componentIndex ) {
@@ -498,19 +506,19 @@ public InternalParser resolveIfOpaque() {
498
506
}
499
507
500
508
public InternalParser captureScheme () {
501
- this .scheme = captureComponent ().toLowerCase ();
509
+ this .scheme = captureComponent ("scheme" ).toLowerCase ();
502
510
return this ;
503
511
}
504
512
505
513
public InternalParser captureUser () {
506
514
this .inPassword = false ;
507
- this .user = captureComponent ();
515
+ this .user = captureComponent ("user" );
508
516
return this ;
509
517
}
510
518
511
519
public InternalParser captureHost () {
512
- verify (this .remainingPercentEncodedChars == 0 && !this .inPassword , "Bad authority" );
513
- this .host = captureComponent ();
520
+ verify (this .remainingPercentEncodedChars == 0 && !this .inPassword , this , "Bad authority" );
521
+ this .host = captureComponent ("host" );
514
522
return this ;
515
523
}
516
524
@@ -522,29 +530,32 @@ public InternalParser captureHostIfNotEmpty() {
522
530
}
523
531
524
532
public InternalParser capturePort () {
525
- verify (this .openCurlyBracketCount == 0 , "Bad authority" );
526
- this .port = captureComponent ();
533
+ verify (this .openCurlyBracketCount == 0 , this , "Bad authority" );
534
+ this .port = captureComponent ("port" );
527
535
return this ;
528
536
}
529
537
530
538
public InternalParser capturePath () {
531
- this .path = captureComponent ();
539
+ this .path = captureComponent ("path" );
532
540
return this ;
533
541
}
534
542
535
543
public InternalParser captureQuery () {
536
- this .query = captureComponent ();
544
+ this .query = captureComponent ("query" );
537
545
return this ;
538
546
}
539
547
540
548
public void captureFragmentIfNotEmpty () {
541
549
if (this .index > this .componentIndex + 1 ) {
542
- this .fragment = captureComponent ();
550
+ this .fragment = captureComponent ("fragment" );
543
551
}
544
552
}
545
553
546
554
public InternalParser switchPortForFullPassword () {
547
555
this .user = this .host + ":" + captureComponent ();
556
+ if (logger .isTraceEnabled ()) {
557
+ logger .trace ("Switching from host/port to user=" + this .user );
558
+ }
548
559
return this ;
549
560
}
550
561
@@ -553,16 +564,27 @@ public InternalParser switchPortForPassword() {
553
564
if (this .host != null ) {
554
565
this .componentIndex = (this .componentIndex - this .host .length () - 1 );
555
566
this .host = null ;
567
+ if (logger .isTraceEnabled ()) {
568
+ logger .trace ("Switching from host/port to username/password" );
569
+ }
556
570
}
557
571
return this ;
558
572
}
559
573
574
+ private String captureComponent (String logPrefix ) {
575
+ String value = captureComponent ();
576
+ if (logger .isTraceEnabled ()) {
577
+ logger .trace (logPrefix + " set to '" + value + "'" );
578
+ }
579
+ return value ;
580
+ }
581
+
560
582
private String captureComponent () {
561
583
return this .uri .substring (this .componentIndex , this .index );
562
584
}
563
585
564
586
public InternalParser markPercentEncoding () {
565
- verify (this .remainingPercentEncodedChars == 0 , "Bad encoding" );
587
+ verify (this .remainingPercentEncodedChars == 0 , this , "Bad encoding" );
566
588
this .remainingPercentEncodedChars = 2 ;
567
589
this .inUtf16Sequence = false ;
568
590
return this ;
@@ -578,7 +600,7 @@ public boolean countDownPercentEncodingInHost(char c) {
578
600
return false ;
579
601
}
580
602
this .remainingPercentEncodedChars --;
581
- verifyIsHexDigit (c , "Bad authority" );
603
+ verifyIsHexDigit (c , this , "Bad authority" );
582
604
return true ;
583
605
}
584
606
@@ -595,7 +617,7 @@ public boolean countDownPercentEncodingInPath(char c) {
595
617
return true ;
596
618
}
597
619
this .remainingPercentEncodedChars --;
598
- verifyIsHexDigit (c , "Bad path" );
620
+ verifyIsHexDigit (c , this , "Bad path" );
599
621
this .inUtf16Sequence &= (this .remainingPercentEncodedChars > 0 );
600
622
return true ;
601
623
}
@@ -615,6 +637,13 @@ else if (c == '}') {
615
637
return (this .openCurlyBracketCount > 0 );
616
638
}
617
639
640
+ @ Override
641
+ public String toString () {
642
+ return "[State=" + this .state + ", index=" + this .index + ", componentIndex=" + this .componentIndex +
643
+ ", uri='" + this .uri + "', scheme='" + this .scheme + "', user='" + this .user +
644
+ "', host='" + this .host + "', path='" + this .path + "', port='" + this .port +
645
+ "', query='" + this .query + "', fragment='" + this .fragment + "']" ;
646
+ }
618
647
}
619
648
620
649
}
0 commit comments