2626 */
2727package org .apache .hc .client5 .http .impl ;
2828
29-
3029import java .nio .charset .StandardCharsets ;
3130import java .util .ArrayList ;
3231import java .util .Collections ;
3534import org .apache .hc .core5 .annotation .Contract ;
3635import org .apache .hc .core5 .annotation .Internal ;
3736import org .apache .hc .core5 .annotation .ThreadingBehavior ;
37+ import org .apache .hc .core5 .http .Header ;
3838import org .apache .hc .core5 .http .message .MessageSupport ;
3939import org .apache .hc .core5 .http .message .ParserCursor ;
40+ import org .apache .hc .core5 .net .PercentCodec ;
4041import org .apache .hc .core5 .util .Args ;
4142
4243/**
4849@ Internal
4950public final class AlpnHeaderSupport {
5051
52+ private static final String HEADER_NAME = "ALPN" ;
5153 private static final char [] HEXADECIMAL = "0123456789ABCDEF" .toCharArray ();
5254
5355 private AlpnHeaderSupport () {
@@ -58,16 +60,12 @@ private AlpnHeaderSupport() {
5860 */
5961 public static String formatValue (final List <String > protocolIds ) {
6062 Args .notEmpty (protocolIds , "protocolIds" );
61- final StringBuilder sb = new StringBuilder ();
62- boolean first = true ;
63+ final List <String > tokens = new ArrayList <>(protocolIds .size ());
6364 for (final String id : protocolIds ) {
64- if (!first ) {
65- sb .append (", " );
66- }
67- sb .append (encodeId (id ));
68- first = false ;
65+ tokens .add (encodeId (id ));
6966 }
70- return sb .toString ();
67+ final Header header = MessageSupport .headerOfTokens (HEADER_NAME , tokens );
68+ return header .getValue ();
7169 }
7270
7371 /**
@@ -107,27 +105,10 @@ public static String encodeId(final String id) {
107105
108106 /**
109107 * Decodes percent-encoded token to raw ID using UTF-8.
110- * Accepts lowercase hex; malformed/incomplete sequences are left literal.
111108 */
112109 public static String decodeId (final String token ) {
113110 Args .notBlank (token , "token" );
114- final byte [] buf = new byte [token .length ()];
115- int bi = 0 ;
116- for (int i = 0 ; i < token .length (); ) {
117- final char c = token .charAt (i );
118- if (c == '%' && i + 2 < token .length ()) {
119- final int hi = hexVal (token .charAt (i + 1 ));
120- final int lo = hexVal (token .charAt (i + 2 ));
121- if (hi >= 0 && lo >= 0 ) {
122- buf [bi ++] = (byte ) ((hi << 4 ) | lo );
123- i += 3 ;
124- continue ;
125- }
126- }
127- buf [bi ++] = (byte ) c ;
128- i ++;
129- }
130- return new String (buf , 0 , bi , StandardCharsets .UTF_8 );
111+ return PercentCodec .decode (token , StandardCharsets .UTF_8 );
131112 }
132113
133114 // RFC7230 tchar minus '%' (RFC7639 requires '%' be percent-encoded)
@@ -167,17 +148,4 @@ private static void appendPctEncoded(final int b, final StringBuilder sb) {
167148 sb .append (HEXADECIMAL [(b >>> 4 ) & 0x0F ]);
168149 sb .append (HEXADECIMAL [b & 0x0F ]);
169150 }
170-
171- private static int hexVal (final char c ) {
172- if (c >= '0' && c <= '9' ) {
173- return c - '0' ;
174- }
175- if (c >= 'A' && c <= 'F' ) {
176- return 10 + (c - 'A' );
177- }
178- if (c >= 'a' && c <= 'f' ) {
179- return 10 + (c - 'a' );
180- }
181- return -1 ;
182- }
183151}
0 commit comments