@@ -51,9 +51,19 @@ public final class URIAuthority implements NamedEndpoint, Serializable {
5151 private final String userInfo ;
5252 private final Host host ;
5353
54+ static URIAuthority parse (final CharSequence s ) throws URISyntaxException {
55+ if (TextUtils .isBlank (s )) {
56+ return null ;
57+ }
58+ final Tokenizer .Cursor cursor = new Tokenizer .Cursor (0 , s .length ());
59+ return parse (s , cursor ); // intentionally no cursor.atEnd() check
60+ }
61+
5462 static URIAuthority parse (final CharSequence s , final Tokenizer .Cursor cursor ) throws URISyntaxException {
5563 final Tokenizer tokenizer = Tokenizer .INSTANCE ;
5664 String userInfo = null ;
65+
66+ // optional userinfo@
5767 final int initPos = cursor .getPos ();
5868 final String token = tokenizer .parseContent (s , cursor , URISupport .HOST_DELIMITERS );
5969 if (!cursor .atEnd () && s .charAt (cursor .getPos ()) == '@' ) {
@@ -62,26 +72,112 @@ static URIAuthority parse(final CharSequence s, final Tokenizer.Cursor cursor) t
6272 userInfo = token ;
6373 }
6474 } else {
65- //Rewind
6675 cursor .updatePos (initPos );
6776 }
77+
78+ if (!cursor .atEnd () && s .charAt (cursor .getPos ()) == '[' ) {
79+ final int lb = cursor .getPos ();
80+ final int upper = cursor .getUpperBound ();
81+ int rb = -1 ;
82+ for (int i = lb + 1 ; i < upper ; i ++) {
83+ if (s .charAt (i ) == ']' ) {
84+ rb = i ;
85+ break ;
86+ }
87+ }
88+ if (rb < 0 ) {
89+ throw URISupport .createException (s .toString (), cursor , "Expected closing bracket for IPv6 address" );
90+ }
91+
92+ final String literal = s .subSequence (lb + 1 , rb ).toString ();
93+ final int zoneMark = literal .indexOf ("%25" );
94+ final String addrPart = zoneMark >= 0 ? literal .substring (0 , zoneMark ) : literal ;
95+
96+ int colons = 0 ;
97+ for (int i = 0 ; i < addrPart .length (); i ++) {
98+ if (addrPart .charAt (i ) == ':' ) {
99+ if (++colons >= 2 ) {
100+ break ;
101+ }
102+ }
103+ }
104+ if (colons < 2 ) {
105+ throw URISupport .createException (s .toString (), cursor , "Expected an IPv6 address" );
106+ }
107+
108+ if (zoneMark >= 0 ) {
109+ final String zoneEnc = literal .substring (zoneMark + 3 );
110+ ZoneIdSupport .validateZoneIdEncoded (zoneEnc );
111+ }
112+ // Store host in friendly form: "...%<decoded-zone>" (or literal as-is if no zone)
113+ final String hostName = ZoneIdSupport .decodeZoneId (literal );
114+
115+ // optional :port
116+ int pos = rb + 1 ;
117+ int port = -1 ;
118+ if (pos < upper && s .charAt (pos ) == ':' ) {
119+ pos ++;
120+ if (pos >= upper || !Character .isDigit (s .charAt (pos ))) {
121+ throw URISupport .createException (s .toString (), cursor , "Invalid port" );
122+ }
123+ long acc = 0 ;
124+ while (pos < upper && Character .isDigit (s .charAt (pos ))) {
125+ acc = acc * 10 + (s .charAt (pos ) - '0' );
126+ if (acc > 65535 ) {
127+ throw URISupport .createException (s .toString (), cursor , "Port out of range" );
128+ }
129+ pos ++;
130+ }
131+ port = (int ) acc ;
132+ }
133+ cursor .updatePos (pos );
134+ return new URIAuthority (userInfo , hostName , port );
135+ }
136+
137+ {
138+ final int start = cursor .getPos ();
139+ final int upper = cursor .getUpperBound ();
140+ int i = start ;
141+ int colonCount = 0 ;
142+ while (i < upper ) {
143+ final char ch = s .charAt (i );
144+ if (ch == '/' || ch == '?' || ch == '#' ) {
145+ break ; // end of authority
146+ }
147+ if (ch == ']' ) {
148+ break ; // safety
149+ }
150+ if (ch == ':' ) {
151+ if (++colonCount > 1 ) {
152+ throw URISupport .createException (s .toString (), cursor , "Expected an IPv6 address" );
153+ }
154+ }
155+ i ++;
156+ }
157+ }
158+
68159 final Host host = Host .parse (s , cursor );
69160 return new URIAuthority (userInfo , host );
70161 }
71162
72- static URIAuthority parse (final CharSequence s ) throws URISyntaxException {
73- final Tokenizer .Cursor cursor = new Tokenizer .Cursor (0 , s .length ());
74- return parse (s , cursor );
75- }
76163
77164 static void format (final StringBuilder buf , final URIAuthority uriAuthority ) {
78165 if (uriAuthority .getUserInfo () != null ) {
79- buf .append (uriAuthority .getUserInfo ());
80- buf .append ("@" );
166+ buf .append (uriAuthority .getUserInfo ()).append ("@" );
167+ }
168+ final String hostName = uriAuthority .getHostName ();
169+ final int port = uriAuthority .getPort ();
170+
171+ if (ZoneIdSupport .appendBracketedIPv6 (buf , hostName )) {
172+ if (port >= 0 ) {
173+ buf .append (':' ).append (port );
174+ }
175+ } else {
176+ Host .format (buf , uriAuthority );
81177 }
82- Host .format (buf , uriAuthority );
83178 }
84179
180+
85181 static String format (final URIAuthority uriAuthority ) {
86182 final StringBuilder buf = new StringBuilder ();
87183 format (buf , uriAuthority );
0 commit comments