@@ -125,13 +125,17 @@ public class PathTemplate {
125125
126126 /**
127127 * A constant identifying the special variable used for endpoint bindings in the result of
128- * {@link #matchFromFullName(String)}.
128+ * {@link #matchFromFullName(String)}. It may also contain protocol string, if its provided in the
129+ * input.
129130 */
130131 public static final String HOSTNAME_VAR = "$hostname" ;
131132
132133 // A regexp to match a custom verb at the end of a path.
133134 private static final Pattern CUSTOM_VERB_PATTERN = Pattern .compile (":([^/*}{=]+)$" );
134135
136+ // A regex to match a hostname with or without protocol.
137+ private static final Pattern HOSTNAME_PATTERN = Pattern .compile ("^(\\ w+:)?//" );
138+
135139 // A splitter on slash.
136140 private static final Splitter SLASH_SPLITTER = Splitter .on ('/' ).trimResults ();
137141
@@ -533,10 +537,10 @@ private Map<String, String> match(String path, boolean forceHostName) {
533537 path = path .substring (0 , matcher .start (0 ));
534538 }
535539
536- // Do full match.
537- boolean withHostName = path . startsWith ( "//" );
540+ Matcher matcher = HOSTNAME_PATTERN . matcher ( path );
541+ boolean withHostName = matcher . find ( );
538542 if (withHostName ) {
539- path = path . substring ( 2 );
543+ path = matcher . replaceFirst ( "" );
540544 }
541545 List <String > input = SLASH_SPLITTER .splitToList (path );
542546 int inPos = 0 ;
@@ -548,16 +552,42 @@ private Map<String, String> match(String path, boolean forceHostName) {
548552 String hostName = input .get (inPos ++);
549553 if (withHostName ) {
550554 // Put the // back, so we can distinguish this case from forceHostName.
551- hostName = "//" + hostName ;
555+ hostName = matcher . group ( 0 ) + hostName ;
552556 }
553557 values .put (HOSTNAME_VAR , hostName );
554558 }
559+ if (withHostName ) {
560+ inPos = alignInputToAlignableSegment (input , inPos , segments .get (0 ));
561+ }
555562 if (!match (input , inPos , segments , 0 , values )) {
556563 return null ;
557564 }
558565 return ImmutableMap .copyOf (values );
559566 }
560567
568+ // Aligns input to start of literal value of literal or binding segment if input contains hostname.
569+ private int alignInputToAlignableSegment (List <String > input , int inPos , Segment segment ) {
570+ switch (segment .kind ()) {
571+ case BINDING :
572+ inPos = alignInputPositionToLiteral (input , inPos , segment .value () + "s" );
573+ return inPos + 1 ;
574+ case LITERAL :
575+ return alignInputPositionToLiteral (input , inPos , segment .value ());
576+ }
577+ return inPos ;
578+ }
579+
580+ // Aligns input to start of literal value if input contains hostname.
581+ private int alignInputPositionToLiteral (
582+ List <String > input , int inPos , String literalSegmentValue ) {
583+ for (; inPos < input .size (); inPos ++) {
584+ if (literalSegmentValue .equals (input .get (inPos ))) {
585+ return inPos ;
586+ }
587+ }
588+ return inPos ;
589+ }
590+
561591 // Tries to match the input based on the segments at given positions. Returns a boolean
562592 // indicating whether the match was successful.
563593 private boolean match (
0 commit comments