1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28 package org.apache.http.client.utils;
29
30 import java.io.IOException;
31 import java.io.InputStream;
32 import java.io.InputStreamReader;
33 import java.io.Reader;
34 import java.net.URI;
35 import java.nio.ByteBuffer;
36 import java.nio.CharBuffer;
37 import java.nio.charset.Charset;
38 import java.util.ArrayList;
39 import java.util.Arrays;
40 import java.util.BitSet;
41 import java.util.Collections;
42 import java.util.List;
43 import java.util.Scanner;
44
45 import org.apache.http.Consts;
46 import org.apache.http.Header;
47 import org.apache.http.HeaderElement;
48 import org.apache.http.HttpEntity;
49 import org.apache.http.NameValuePair;
50 import org.apache.http.entity.ContentType;
51 import org.apache.http.message.BasicNameValuePair;
52 import org.apache.http.message.ParserCursor;
53 import org.apache.http.message.TokenParser;
54 import org.apache.http.protocol.HTTP;
55 import org.apache.http.util.Args;
56 import org.apache.http.util.CharArrayBuffer;
57
58
59
60
61
62
63 public class URLEncodedUtils {
64
65
66
67
68 public static final String CONTENT_TYPE = "application/x-www-form-urlencoded";
69
70 private static final char QP_SEP_A = '&';
71 private static final char QP_SEP_S = ';';
72 private static final String NAME_VALUE_SEPARATOR = "=";
73 private static final char PATH_SEPARATOR = '/';
74
75 private static final BitSet PATH_SEPARATORS = new BitSet(256);
76 static {
77 PATH_SEPARATORS.set(PATH_SEPARATOR);
78 }
79
80
81
82
83 @Deprecated
84 public static List <NameValuePair> parse(final URI uri, final String charsetName) {
85 return parse(uri, charsetName != null ? Charset.forName(charsetName) : null);
86 }
87
88
89
90
91
92
93
94
95
96
97
98 public static List <NameValuePair> parse(final URI uri, final Charset charset) {
99 Args.notNull(uri, "URI");
100 final String query = uri.getRawQuery();
101 if (query != null && !query.isEmpty()) {
102 return parse(query, charset);
103 }
104 return createEmptyList();
105 }
106
107
108
109
110
111
112
113
114
115
116
117
118
119 public static List <NameValuePair> parse(
120 final HttpEntity entity) throws IOException {
121 Args.notNull(entity, "HTTP entity");
122 final ContentType contentType = ContentType.get(entity);
123 if (contentType == null || !contentType.getMimeType().equalsIgnoreCase(CONTENT_TYPE)) {
124 return createEmptyList();
125 }
126 final long len = entity.getContentLength();
127 Args.check(len <= Integer.MAX_VALUE, "HTTP entity is too large");
128 final Charset charset = contentType.getCharset() != null ? contentType.getCharset() : HTTP.DEF_CONTENT_CHARSET;
129 final InputStream inStream = entity.getContent();
130 if (inStream == null) {
131 return createEmptyList();
132 }
133 final CharArrayBuffer buf;
134 try {
135 buf = new CharArrayBuffer(len > 0 ? (int) len : 1024);
136 final Reader reader = new InputStreamReader(inStream, charset);
137 final char[] tmp = new char[1024];
138 int l;
139 while((l = reader.read(tmp)) != -1) {
140 buf.append(tmp, 0, l);
141 }
142
143 } finally {
144 inStream.close();
145 }
146 if (buf.isEmpty()) {
147 return createEmptyList();
148 }
149 return parse(buf, charset, QP_SEP_A);
150 }
151
152
153
154
155
156 public static boolean isEncoded(final HttpEntity entity) {
157 Args.notNull(entity, "HTTP entity");
158 final Header h = entity.getContentType();
159 if (h != null) {
160 final HeaderElement[] elems = h.getElements();
161 if (elems.length > 0) {
162 final String contentType = elems[0].getName();
163 return contentType.equalsIgnoreCase(CONTENT_TYPE);
164 }
165 }
166 return false;
167 }
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184 @Deprecated
185 public static void parse(
186 final List<NameValuePair> parameters,
187 final Scanner scanner,
188 final String charset) {
189 parse(parameters, scanner, "[" + QP_SEP_A + QP_SEP_S + "]", charset);
190 }
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210 @Deprecated
211 public static void parse(
212 final List <NameValuePair> parameters,
213 final Scanner scanner,
214 final String parameterSepartorPattern,
215 final String charset) {
216 scanner.useDelimiter(parameterSepartorPattern);
217 while (scanner.hasNext()) {
218 final String name;
219 final String value;
220 final String token = scanner.next();
221 final int i = token.indexOf(NAME_VALUE_SEPARATOR);
222 if (i != -1) {
223 name = decodeFormFields(token.substring(0, i).trim(), charset);
224 value = decodeFormFields(token.substring(i + 1).trim(), charset);
225 } else {
226 name = decodeFormFields(token.trim(), charset);
227 value = null;
228 }
229 parameters.add(new BasicNameValuePair(name, value));
230 }
231 }
232
233
234
235
236
237
238
239
240
241
242
243 public static List<NameValuePair> parse(final String s, final Charset charset) {
244 if (s == null) {
245 return createEmptyList();
246 }
247 final CharArrayBuffer buffer = new CharArrayBuffer(s.length());
248 buffer.append(s);
249 return parse(buffer, charset, QP_SEP_A, QP_SEP_S);
250 }
251
252
253
254
255
256
257
258
259
260
261
262
263 public static List<NameValuePair> parse(final String s, final Charset charset, final char... separators) {
264 if (s == null) {
265 return createEmptyList();
266 }
267 final CharArrayBuffer buffer = new CharArrayBuffer(s.length());
268 buffer.append(s);
269 return parse(buffer, charset, separators);
270 }
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285 public static List<NameValuePair> parse(
286 final CharArrayBuffer buf, final Charset charset, final char... separators) {
287 Args.notNull(buf, "Char array buffer");
288 final TokenParser tokenParser = TokenParser.INSTANCE;
289 final BitSet delimSet = new BitSet();
290 for (final char separator: separators) {
291 delimSet.set(separator);
292 }
293 final ParserCursor cursor = new ParserCursor(0, buf.length());
294 final List<NameValuePair> list = new ArrayList<NameValuePair>();
295 while (!cursor.atEnd()) {
296 delimSet.set('=');
297 final String name = tokenParser.parseToken(buf, cursor, delimSet);
298 String value = null;
299 if (!cursor.atEnd()) {
300 final int delim = buf.charAt(cursor.getPos());
301 cursor.updatePos(cursor.getPos() + 1);
302 if (delim == '=') {
303 delimSet.clear('=');
304 value = tokenParser.parseToken(buf, cursor, delimSet);
305 if (!cursor.atEnd()) {
306 cursor.updatePos(cursor.getPos() + 1);
307 }
308 }
309 }
310 if (!name.isEmpty()) {
311 list.add(new BasicNameValuePair(
312 decodeFormFields(name, charset),
313 decodeFormFields(value, charset)));
314 }
315 }
316 return list;
317 }
318
319 static List<String> splitSegments(final CharSequence s, final BitSet separators) {
320 final ParserCursor cursor = new ParserCursor(0, s.length());
321
322 if (cursor.atEnd()) {
323 return Collections.emptyList();
324 }
325 if (separators.get(s.charAt(cursor.getPos()))) {
326 cursor.updatePos(cursor.getPos() + 1);
327 }
328 final List<String> list = new ArrayList<String>();
329 final StringBuilder buf = new StringBuilder();
330 for (;;) {
331 if (cursor.atEnd()) {
332 list.add(buf.toString());
333 break;
334 }
335 final char current = s.charAt(cursor.getPos());
336 if (separators.get(current)) {
337 list.add(buf.toString());
338 buf.setLength(0);
339 } else {
340 buf.append(current);
341 }
342 cursor.updatePos(cursor.getPos() + 1);
343 }
344 return list;
345 }
346
347 static List<String> splitPathSegments(final CharSequence s) {
348 return splitSegments(s, PATH_SEPARATORS);
349 }
350
351
352
353
354
355
356
357
358
359
360 public static List<String> parsePathSegments(final CharSequence s, final Charset charset) {
361 Args.notNull(s, "Char sequence");
362 final List<String> list = splitPathSegments(s);
363 for (int i = 0; i < list.size(); i++) {
364 list.set(i, urlDecode(list.get(i), charset != null ? charset : Consts.UTF_8, false));
365 }
366 return list;
367 }
368
369
370
371
372
373
374
375
376
377 public static List<String> parsePathSegments(final CharSequence s) {
378 return parsePathSegments(s, Consts.UTF_8);
379 }
380
381
382
383
384
385
386
387
388
389
390 public static String formatSegments(final Iterable<String> segments, final Charset charset) {
391 Args.notNull(segments, "Segments");
392 final StringBuilder result = new StringBuilder();
393 for (final String segment : segments) {
394 result.append(PATH_SEPARATOR).append(urlEncode(segment, charset, PATHSAFE, false));
395 }
396 return result.toString();
397 }
398
399
400
401
402
403
404
405
406
407 public static String formatSegments(final String... segments) {
408 return formatSegments(Arrays.asList(segments), Consts.UTF_8);
409 }
410
411
412
413
414
415
416
417
418
419 public static String format(
420 final List <? extends NameValuePair> parameters,
421 final String charset) {
422 return format(parameters, QP_SEP_A, charset);
423 }
424
425
426
427
428
429
430
431
432
433
434
435
436 public static String format(
437 final List <? extends NameValuePair> parameters,
438 final char parameterSeparator,
439 final String charset) {
440 final StringBuilder result = new StringBuilder();
441 for (final NameValuePair parameter : parameters) {
442 final String encodedName = encodeFormFields(parameter.getName(), charset);
443 final String encodedValue = encodeFormFields(parameter.getValue(), charset);
444 if (result.length() > 0) {
445 result.append(parameterSeparator);
446 }
447 result.append(encodedName);
448 if (encodedValue != null) {
449 result.append(NAME_VALUE_SEPARATOR);
450 result.append(encodedValue);
451 }
452 }
453 return result.toString();
454 }
455
456
457
458
459
460
461
462
463
464
465
466 public static String format(
467 final Iterable<? extends NameValuePair> parameters,
468 final Charset charset) {
469 return format(parameters, QP_SEP_A, charset);
470 }
471
472
473
474
475
476
477
478
479
480
481
482
483 public static String format(
484 final Iterable<? extends NameValuePair> parameters,
485 final char parameterSeparator,
486 final Charset charset) {
487 Args.notNull(parameters, "Parameters");
488 final StringBuilder result = new StringBuilder();
489 for (final NameValuePair parameter : parameters) {
490 final String encodedName = encodeFormFields(parameter.getName(), charset);
491 final String encodedValue = encodeFormFields(parameter.getValue(), charset);
492 if (result.length() > 0) {
493 result.append(parameterSeparator);
494 }
495 result.append(encodedName);
496 if (encodedValue != null) {
497 result.append(NAME_VALUE_SEPARATOR);
498 result.append(encodedValue);
499 }
500 }
501 return result.toString();
502 }
503
504
505
506
507
508
509
510 private static final BitSet UNRESERVED = new BitSet(256);
511
512
513
514
515
516 private static final BitSet PUNCT = new BitSet(256);
517
518
519 private static final BitSet USERINFO = new BitSet(256);
520
521
522 private static final BitSet PATHSAFE = new BitSet(256);
523
524
525 private static final BitSet URIC = new BitSet(256);
526
527
528
529
530
531
532
533
534
535 private static final BitSet RESERVED = new BitSet(256);
536
537
538
539
540
541
542 private static final BitSet URLENCODER = new BitSet(256);
543
544 private static final BitSet PATH_SPECIAL = new BitSet(256);
545
546 static {
547
548
549 for (int i = 'a'; i <= 'z'; i++) {
550 UNRESERVED.set(i);
551 }
552 for (int i = 'A'; i <= 'Z'; i++) {
553 UNRESERVED.set(i);
554 }
555
556 for (int i = '0'; i <= '9'; i++) {
557 UNRESERVED.set(i);
558 }
559 UNRESERVED.set('_');
560 UNRESERVED.set('-');
561 UNRESERVED.set('.');
562 UNRESERVED.set('*');
563 URLENCODER.or(UNRESERVED);
564 UNRESERVED.set('!');
565 UNRESERVED.set('~');
566 UNRESERVED.set('\'');
567 UNRESERVED.set('(');
568 UNRESERVED.set(')');
569
570 PUNCT.set(',');
571 PUNCT.set(';');
572 PUNCT.set(':');
573 PUNCT.set('$');
574 PUNCT.set('&');
575 PUNCT.set('+');
576 PUNCT.set('=');
577
578 USERINFO.or(UNRESERVED);
579 USERINFO.or(PUNCT);
580
581
582 PATHSAFE.or(UNRESERVED);
583 PATHSAFE.set(';');
584 PATHSAFE.set(':');
585 PATHSAFE.set('@');
586 PATHSAFE.set('&');
587 PATHSAFE.set('=');
588 PATHSAFE.set('+');
589 PATHSAFE.set('$');
590 PATHSAFE.set(',');
591
592 PATH_SPECIAL.or(PATHSAFE);
593 PATH_SPECIAL.set('/');
594
595 RESERVED.set(';');
596 RESERVED.set('/');
597 RESERVED.set('?');
598 RESERVED.set(':');
599 RESERVED.set('@');
600 RESERVED.set('&');
601 RESERVED.set('=');
602 RESERVED.set('+');
603 RESERVED.set('$');
604 RESERVED.set(',');
605 RESERVED.set('[');
606 RESERVED.set(']');
607
608 URIC.or(RESERVED);
609 URIC.or(UNRESERVED);
610 }
611
612 private static final int RADIX = 16;
613
614 private static List<NameValuePair> createEmptyList() {
615 return new ArrayList<NameValuePair>(0);
616 }
617
618 private static String urlEncode(
619 final String content,
620 final Charset charset,
621 final BitSet safechars,
622 final boolean blankAsPlus) {
623 if (content == null) {
624 return null;
625 }
626 final StringBuilder buf = new StringBuilder();
627 final ByteBuffer bb = charset.encode(content);
628 while (bb.hasRemaining()) {
629 final int b = bb.get() & 0xff;
630 if (safechars.get(b)) {
631 buf.append((char) b);
632 } else if (blankAsPlus && b == ' ') {
633 buf.append('+');
634 } else {
635 buf.append("%");
636 final char hex1 = Character.toUpperCase(Character.forDigit((b >> 4) & 0xF, RADIX));
637 final char hex2 = Character.toUpperCase(Character.forDigit(b & 0xF, RADIX));
638 buf.append(hex1);
639 buf.append(hex2);
640 }
641 }
642 return buf.toString();
643 }
644
645
646
647
648
649
650
651
652
653 private static String urlDecode(
654 final String content,
655 final Charset charset,
656 final boolean plusAsBlank) {
657 if (content == null) {
658 return null;
659 }
660 final ByteBuffer bb = ByteBuffer.allocate(content.length());
661 final CharBuffer cb = CharBuffer.wrap(content);
662 while (cb.hasRemaining()) {
663 final char c = cb.get();
664 if (c == '%' && cb.remaining() >= 2) {
665 final char uc = cb.get();
666 final char lc = cb.get();
667 final int u = Character.digit(uc, 16);
668 final int l = Character.digit(lc, 16);
669 if (u != -1 && l != -1) {
670 bb.put((byte) ((u << 4) + l));
671 } else {
672 bb.put((byte) '%');
673 bb.put((byte) uc);
674 bb.put((byte) lc);
675 }
676 } else if (plusAsBlank && c == '+') {
677 bb.put((byte) ' ');
678 } else {
679 bb.put((byte) c);
680 }
681 }
682 bb.flip();
683 return charset.decode(bb).toString();
684 }
685
686
687
688
689
690
691
692
693 private static String decodeFormFields (final String content, final String charset) {
694 if (content == null) {
695 return null;
696 }
697 return urlDecode(content, charset != null ? Charset.forName(charset) : Consts.UTF_8, true);
698 }
699
700
701
702
703
704
705
706
707 private static String decodeFormFields (final String content, final Charset charset) {
708 if (content == null) {
709 return null;
710 }
711 return urlDecode(content, charset != null ? charset : Consts.UTF_8, true);
712 }
713
714
715
716
717
718
719
720
721
722
723
724
725 private static String encodeFormFields(final String content, final String charset) {
726 if (content == null) {
727 return null;
728 }
729 return urlEncode(content, charset != null ? Charset.forName(charset) : Consts.UTF_8, URLENCODER, true);
730 }
731
732
733
734
735
736
737
738
739
740
741
742
743 private static String encodeFormFields (final String content, final Charset charset) {
744 if (content == null) {
745 return null;
746 }
747 return urlEncode(content, charset != null ? charset : Consts.UTF_8, URLENCODER, true);
748 }
749
750
751
752
753
754
755
756
757
758
759 static String encUserInfo(final String content, final Charset charset) {
760 return urlEncode(content, charset, USERINFO, false);
761 }
762
763
764
765
766
767
768
769
770
771
772 static String encUric(final String content, final Charset charset) {
773 return urlEncode(content, charset, URIC, false);
774 }
775
776
777
778
779
780
781
782
783
784
785 static String encPath(final String content, final Charset charset) {
786 return urlEncode(content, charset, PATH_SPECIAL, false);
787 }
788
789 }