1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.eclipse.jetty.client;
20
21 import java.io.IOException;
22 import java.net.CookieManager;
23 import java.net.CookiePolicy;
24 import java.net.CookieStore;
25 import java.net.InetSocketAddress;
26 import java.net.Socket;
27 import java.net.SocketAddress;
28 import java.net.URI;
29 import java.nio.channels.SelectionKey;
30 import java.nio.channels.SocketChannel;
31 import java.util.ArrayList;
32 import java.util.Collection;
33 import java.util.HashMap;
34 import java.util.HashSet;
35 import java.util.Iterator;
36 import java.util.List;
37 import java.util.Locale;
38 import java.util.Map;
39 import java.util.Objects;
40 import java.util.Set;
41 import java.util.concurrent.ConcurrentHashMap;
42 import java.util.concurrent.ConcurrentMap;
43 import java.util.concurrent.ExecutionException;
44 import java.util.concurrent.Executor;
45 import java.util.concurrent.TimeUnit;
46 import java.util.concurrent.TimeoutException;
47
48 import org.eclipse.jetty.client.api.AuthenticationStore;
49 import org.eclipse.jetty.client.api.Connection;
50 import org.eclipse.jetty.client.api.ContentResponse;
51 import org.eclipse.jetty.client.api.Destination;
52 import org.eclipse.jetty.client.api.Request;
53 import org.eclipse.jetty.client.api.Response;
54 import org.eclipse.jetty.client.http.HttpClientTransportOverHTTP;
55 import org.eclipse.jetty.client.util.FormContentProvider;
56 import org.eclipse.jetty.http.HttpField;
57 import org.eclipse.jetty.http.HttpHeader;
58 import org.eclipse.jetty.http.HttpMethod;
59 import org.eclipse.jetty.http.HttpScheme;
60 import org.eclipse.jetty.io.ByteBufferPool;
61 import org.eclipse.jetty.io.MappedByteBufferPool;
62 import org.eclipse.jetty.util.Fields;
63 import org.eclipse.jetty.util.Jetty;
64 import org.eclipse.jetty.util.Promise;
65 import org.eclipse.jetty.util.SocketAddressResolver;
66 import org.eclipse.jetty.util.annotation.ManagedAttribute;
67 import org.eclipse.jetty.util.annotation.ManagedObject;
68 import org.eclipse.jetty.util.component.ContainerLifeCycle;
69 import org.eclipse.jetty.util.log.Log;
70 import org.eclipse.jetty.util.log.Logger;
71 import org.eclipse.jetty.util.ssl.SslContextFactory;
72 import org.eclipse.jetty.util.thread.QueuedThreadPool;
73 import org.eclipse.jetty.util.thread.ScheduledExecutorScheduler;
74 import org.eclipse.jetty.util.thread.Scheduler;
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112 @ManagedObject("The HTTP client")
113 public class HttpClient extends ContainerLifeCycle
114 {
115 private static final Logger LOG = Log.getLogger(HttpClient.class);
116
117 private final ConcurrentMap<Origin, HttpDestination> destinations = new ConcurrentHashMap<>();
118 private final ProtocolHandlers handlers = new ProtocolHandlers();
119 private final List<Request.Listener> requestListeners = new ArrayList<>();
120 private final AuthenticationStore authenticationStore = new HttpAuthenticationStore();
121 private final Set<ContentDecoder.Factory> decoderFactories = new ContentDecoderFactorySet();
122 private final ProxyConfiguration proxyConfig = new ProxyConfiguration();
123 private final HttpClientTransport transport;
124 private final SslContextFactory sslContextFactory;
125 private volatile CookieManager cookieManager;
126 private volatile CookieStore cookieStore;
127 private volatile Executor executor;
128 private volatile ByteBufferPool byteBufferPool;
129 private volatile Scheduler scheduler;
130 private volatile SocketAddressResolver resolver;
131 private volatile HttpField agentField = new HttpField(HttpHeader.USER_AGENT, "Jetty/" + Jetty.VERSION);
132 private volatile boolean followRedirects = true;
133 private volatile int maxConnectionsPerDestination = 64;
134 private volatile int maxRequestsQueuedPerDestination = 1024;
135 private volatile int requestBufferSize = 4096;
136 private volatile int responseBufferSize = 16384;
137 private volatile int maxRedirects = 8;
138 private volatile SocketAddress bindAddress;
139 private volatile long connectTimeout = 15000;
140 private volatile long addressResolutionTimeout = 15000;
141 private volatile long idleTimeout;
142 private volatile boolean tcpNoDelay = true;
143 private volatile boolean strictEventOrdering = false;
144 private volatile HttpField encodingField;
145 private volatile boolean removeIdleDestinations = false;
146 private volatile boolean connectBlocking = false;
147
148
149
150
151
152
153
154 public HttpClient()
155 {
156 this(null);
157 }
158
159
160
161
162
163
164
165
166 public HttpClient(SslContextFactory sslContextFactory)
167 {
168 this(new HttpClientTransportOverHTTP(), sslContextFactory);
169 }
170
171 public HttpClient(HttpClientTransport transport, SslContextFactory sslContextFactory)
172 {
173 this.transport = transport;
174 this.sslContextFactory = sslContextFactory;
175 }
176
177 public HttpClientTransport getTransport()
178 {
179 return transport;
180 }
181
182
183
184
185
186 public SslContextFactory getSslContextFactory()
187 {
188 return sslContextFactory;
189 }
190
191 @Override
192 protected void doStart() throws Exception
193 {
194 if (sslContextFactory != null)
195 addBean(sslContextFactory);
196
197 String name = HttpClient.class.getSimpleName() + "@" + hashCode();
198
199 if (executor == null)
200 {
201 QueuedThreadPool threadPool = new QueuedThreadPool();
202 threadPool.setName(name);
203 executor = threadPool;
204 }
205 addBean(executor);
206
207 if (byteBufferPool == null)
208 byteBufferPool = new MappedByteBufferPool();
209 addBean(byteBufferPool);
210
211 if (scheduler == null)
212 scheduler = new ScheduledExecutorScheduler(name + "-scheduler", false);
213 addBean(scheduler);
214
215 transport.setHttpClient(this);
216 addBean(transport);
217
218 if (resolver == null)
219 resolver = new SocketAddressResolver.Async(executor, scheduler, getAddressResolutionTimeout());
220 addBean(resolver);
221
222 handlers.put(new ContinueProtocolHandler());
223 handlers.put(new RedirectProtocolHandler(this));
224 handlers.put(new WWWAuthenticationProtocolHandler(this));
225 handlers.put(new ProxyAuthenticationProtocolHandler(this));
226
227 decoderFactories.add(new GZIPContentDecoder.Factory());
228
229 cookieManager = newCookieManager();
230 cookieStore = cookieManager.getCookieStore();
231
232 super.doStart();
233 }
234
235 private CookieManager newCookieManager()
236 {
237 return new CookieManager(getCookieStore(), CookiePolicy.ACCEPT_ALL);
238 }
239
240 @Override
241 protected void doStop() throws Exception
242 {
243 cookieStore.removeAll();
244 decoderFactories.clear();
245 handlers.clear();
246
247 for (HttpDestination destination : destinations.values())
248 destination.close();
249 destinations.clear();
250
251 requestListeners.clear();
252 authenticationStore.clearAuthentications();
253 authenticationStore.clearAuthenticationResults();
254
255 super.doStop();
256 }
257
258
259
260
261
262
263
264 public List<Request.Listener> getRequestListeners()
265 {
266 return requestListeners;
267 }
268
269
270
271
272 public CookieStore getCookieStore()
273 {
274 return cookieStore;
275 }
276
277
278
279
280 public void setCookieStore(CookieStore cookieStore)
281 {
282 this.cookieStore = Objects.requireNonNull(cookieStore);
283 this.cookieManager = newCookieManager();
284 }
285
286
287
288
289
290
291
292 CookieManager getCookieManager()
293 {
294 return cookieManager;
295 }
296
297
298
299
300 public AuthenticationStore getAuthenticationStore()
301 {
302 return authenticationStore;
303 }
304
305
306
307
308
309
310
311 public Set<ContentDecoder.Factory> getContentDecoderFactories()
312 {
313 return decoderFactories;
314 }
315
316
317
318
319
320
321
322
323
324
325
326 public ContentResponse GET(String uri) throws InterruptedException, ExecutionException, TimeoutException
327 {
328 return GET(URI.create(uri));
329 }
330
331
332
333
334
335
336
337
338
339
340
341 public ContentResponse GET(URI uri) throws InterruptedException, ExecutionException, TimeoutException
342 {
343 return newRequest(uri).send();
344 }
345
346
347
348
349
350
351
352
353
354
355
356 public ContentResponse FORM(String uri, Fields fields) throws InterruptedException, ExecutionException, TimeoutException
357 {
358 return FORM(URI.create(uri), fields);
359 }
360
361
362
363
364
365
366
367
368
369
370
371 public ContentResponse FORM(URI uri, Fields fields) throws InterruptedException, ExecutionException, TimeoutException
372 {
373 return POST(uri).content(new FormContentProvider(fields)).send();
374 }
375
376
377
378
379
380
381
382
383 public Request POST(String uri)
384 {
385 return POST(URI.create(uri));
386 }
387
388
389
390
391
392
393
394 public Request POST(URI uri)
395 {
396 return newRequest(uri).method(HttpMethod.POST);
397 }
398
399
400
401
402
403
404
405
406 public Request newRequest(String host, int port)
407 {
408 return newRequest(new Origin("http", host, port).asString());
409 }
410
411
412
413
414
415
416
417 public Request newRequest(String uri)
418 {
419 return newRequest(URI.create(uri));
420 }
421
422
423
424
425
426
427
428 public Request newRequest(URI uri)
429 {
430 return newHttpRequest(newConversation(), uri);
431 }
432
433 protected Request copyRequest(HttpRequest oldRequest, URI newURI)
434 {
435 Request newRequest = newHttpRequest(oldRequest.getConversation(), newURI);
436 newRequest.method(oldRequest.getMethod())
437 .version(oldRequest.getVersion())
438 .content(oldRequest.getContent())
439 .idleTimeout(oldRequest.getIdleTimeout(), TimeUnit.MILLISECONDS)
440 .timeout(oldRequest.getTimeout(), TimeUnit.MILLISECONDS)
441 .followRedirects(oldRequest.isFollowRedirects());
442 for (HttpField field : oldRequest.getHeaders())
443 {
444 HttpHeader header = field.getHeader();
445
446 if (HttpHeader.HOST == header)
447 continue;
448
449
450 if (HttpHeader.EXPECT == header)
451 continue;
452
453
454 if (HttpHeader.COOKIE == header)
455 continue;
456
457
458 if (HttpHeader.AUTHORIZATION == header ||
459 HttpHeader.PROXY_AUTHORIZATION == header)
460 continue;
461
462 String value = field.getValue();
463 if (!newRequest.getHeaders().contains(header, value))
464 newRequest.header(field.getName(), value);
465 }
466 return newRequest;
467 }
468
469 protected HttpRequest newHttpRequest(HttpConversation conversation, URI uri)
470 {
471 return new HttpRequest(this, conversation, uri);
472 }
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487 public Destination getDestination(String scheme, String host, int port)
488 {
489 return destinationFor(scheme, host, port);
490 }
491
492 protected HttpDestination destinationFor(String scheme, String host, int port)
493 {
494 port = normalizePort(scheme, port);
495
496 Origin origin = new Origin(scheme, host, port);
497 HttpDestination destination = destinations.get(origin);
498 if (destination == null)
499 {
500 destination = transport.newHttpDestination(origin);
501 if (isRunning())
502 {
503 HttpDestination existing = destinations.putIfAbsent(origin, destination);
504 if (existing != null)
505 {
506 destination = existing;
507 }
508 else
509 {
510 addManaged(destination);
511 if (LOG.isDebugEnabled())
512 LOG.debug("Created {}", destination);
513 }
514
515 if (!isRunning())
516 removeDestination(destination);
517 }
518
519 }
520 return destination;
521 }
522
523 protected boolean removeDestination(HttpDestination destination)
524 {
525 removeBean(destination);
526 return destinations.remove(destination.getOrigin()) != null;
527 }
528
529
530
531
532 public List<Destination> getDestinations()
533 {
534 return new ArrayList<Destination>(destinations.values());
535 }
536
537 protected void send(final HttpRequest request, List<Response.ResponseListener> listeners)
538 {
539 String scheme = request.getScheme().toLowerCase(Locale.ENGLISH);
540 if (!HttpScheme.HTTP.is(scheme) && !HttpScheme.HTTPS.is(scheme))
541 throw new IllegalArgumentException("Invalid protocol " + scheme);
542
543 String host = request.getHost().toLowerCase(Locale.ENGLISH);
544 HttpDestination destination = destinationFor(scheme, host, request.getPort());
545 destination.send(request, listeners);
546 }
547
548 protected void newConnection(final HttpDestination destination, final Promise<Connection> promise)
549 {
550 Origin.Address address = destination.getConnectAddress();
551 resolver.resolve(address.getHost(), address.getPort(), new Promise<List<InetSocketAddress>>()
552 {
553 @Override
554 public void succeeded(List<InetSocketAddress> socketAddresses)
555 {
556 Map<String, Object> context = new HashMap<>();
557 context.put(HttpClientTransport.HTTP_DESTINATION_CONTEXT_KEY, destination);
558 connect(socketAddresses, 0, context);
559 }
560
561 @Override
562 public void failed(Throwable x)
563 {
564 promise.failed(x);
565 }
566
567 private void connect(List<InetSocketAddress> socketAddresses, int index, Map<String, Object> context)
568 {
569 context.put(HttpClientTransport.HTTP_CONNECTION_PROMISE_CONTEXT_KEY, new Promise<Connection>()
570 {
571 @Override
572 public void succeeded(Connection result)
573 {
574 promise.succeeded(result);
575 }
576
577 @Override
578 public void failed(Throwable x)
579 {
580 int nextIndex = index + 1;
581 if (nextIndex == socketAddresses.size())
582 promise.failed(x);
583 else
584 connect(socketAddresses, nextIndex, context);
585 }
586 });
587 transport.connect(socketAddresses.get(index), context);
588 }
589 });
590 }
591
592 private HttpConversation newConversation()
593 {
594 return new HttpConversation();
595 }
596
597 public ProtocolHandlers getProtocolHandlers()
598 {
599 return handlers;
600 }
601
602 protected ProtocolHandler findProtocolHandler(Request request, Response response)
603 {
604 return handlers.find(request, response);
605 }
606
607
608
609
610 public ByteBufferPool getByteBufferPool()
611 {
612 return byteBufferPool;
613 }
614
615
616
617
618 public void setByteBufferPool(ByteBufferPool byteBufferPool)
619 {
620 this.byteBufferPool = byteBufferPool;
621 }
622
623
624
625
626 @ManagedAttribute("The timeout, in milliseconds, for connect() operations")
627 public long getConnectTimeout()
628 {
629 return connectTimeout;
630 }
631
632
633
634
635
636 public void setConnectTimeout(long connectTimeout)
637 {
638 this.connectTimeout = connectTimeout;
639 }
640
641
642
643
644
645 public long getAddressResolutionTimeout()
646 {
647 return addressResolutionTimeout;
648 }
649
650
651
652
653
654
655
656
657
658
659 public void setAddressResolutionTimeout(long addressResolutionTimeout)
660 {
661 this.addressResolutionTimeout = addressResolutionTimeout;
662 }
663
664
665
666
667 @ManagedAttribute("The timeout, in milliseconds, to close idle connections")
668 public long getIdleTimeout()
669 {
670 return idleTimeout;
671 }
672
673
674
675
676 public void setIdleTimeout(long idleTimeout)
677 {
678 this.idleTimeout = idleTimeout;
679 }
680
681
682
683
684
685 public SocketAddress getBindAddress()
686 {
687 return bindAddress;
688 }
689
690
691
692
693
694
695 public void setBindAddress(SocketAddress bindAddress)
696 {
697 this.bindAddress = bindAddress;
698 }
699
700
701
702
703 public HttpField getUserAgentField()
704 {
705 return agentField;
706 }
707
708
709
710
711 public void setUserAgentField(HttpField agent)
712 {
713 if (agent.getHeader() != HttpHeader.USER_AGENT)
714 throw new IllegalArgumentException();
715 this.agentField = agent;
716 }
717
718
719
720
721
722 @ManagedAttribute("Whether HTTP redirects are followed")
723 public boolean isFollowRedirects()
724 {
725 return followRedirects;
726 }
727
728
729
730
731
732 public void setFollowRedirects(boolean follow)
733 {
734 this.followRedirects = follow;
735 }
736
737
738
739
740 public Executor getExecutor()
741 {
742 return executor;
743 }
744
745
746
747
748 public void setExecutor(Executor executor)
749 {
750 this.executor = executor;
751 }
752
753
754
755
756 public Scheduler getScheduler()
757 {
758 return scheduler;
759 }
760
761
762
763
764 public void setScheduler(Scheduler scheduler)
765 {
766 this.scheduler = scheduler;
767 }
768
769
770
771
772 public SocketAddressResolver getSocketAddressResolver()
773 {
774 return resolver;
775 }
776
777
778
779
780 public void setSocketAddressResolver(SocketAddressResolver resolver)
781 {
782 this.resolver = resolver;
783 }
784
785
786
787
788 @ManagedAttribute("The max number of connections per each destination")
789 public int getMaxConnectionsPerDestination()
790 {
791 return maxConnectionsPerDestination;
792 }
793
794
795
796
797
798
799
800
801
802
803
804
805 public void setMaxConnectionsPerDestination(int maxConnectionsPerDestination)
806 {
807 this.maxConnectionsPerDestination = maxConnectionsPerDestination;
808 }
809
810
811
812
813 @ManagedAttribute("The max number of requests queued per each destination")
814 public int getMaxRequestsQueuedPerDestination()
815 {
816 return maxRequestsQueuedPerDestination;
817 }
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832 public void setMaxRequestsQueuedPerDestination(int maxRequestsQueuedPerDestination)
833 {
834 this.maxRequestsQueuedPerDestination = maxRequestsQueuedPerDestination;
835 }
836
837
838
839
840 @ManagedAttribute("The request buffer size")
841 public int getRequestBufferSize()
842 {
843 return requestBufferSize;
844 }
845
846
847
848
849 public void setRequestBufferSize(int requestBufferSize)
850 {
851 this.requestBufferSize = requestBufferSize;
852 }
853
854
855
856
857 @ManagedAttribute("The response buffer size")
858 public int getResponseBufferSize()
859 {
860 return responseBufferSize;
861 }
862
863
864
865
866 public void setResponseBufferSize(int responseBufferSize)
867 {
868 this.responseBufferSize = responseBufferSize;
869 }
870
871
872
873
874
875 public int getMaxRedirects()
876 {
877 return maxRedirects;
878 }
879
880
881
882
883
884 public void setMaxRedirects(int maxRedirects)
885 {
886 this.maxRedirects = maxRedirects;
887 }
888
889
890
891
892 @ManagedAttribute(value = "Whether the TCP_NODELAY option is enabled", name = "tcpNoDelay")
893 public boolean isTCPNoDelay()
894 {
895 return tcpNoDelay;
896 }
897
898
899
900
901
902 public void setTCPNoDelay(boolean tcpNoDelay)
903 {
904 this.tcpNoDelay = tcpNoDelay;
905 }
906
907
908
909
910
911 @Deprecated
912 public boolean isDispatchIO()
913 {
914
915 return false;
916 }
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931 @Deprecated
932 public void setDispatchIO(boolean dispatchIO)
933 {
934 }
935
936
937
938
939
940 @ManagedAttribute("Whether request/response events must be strictly ordered")
941 public boolean isStrictEventOrdering()
942 {
943 return strictEventOrdering;
944 }
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972 public void setStrictEventOrdering(boolean strictEventOrdering)
973 {
974 this.strictEventOrdering = strictEventOrdering;
975 }
976
977
978
979
980
981 @ManagedAttribute("Whether idle destinations are removed")
982 public boolean isRemoveIdleDestinations()
983 {
984 return removeIdleDestinations;
985 }
986
987
988
989
990
991
992
993
994
995
996
997
998
999 public void setRemoveIdleDestinations(boolean removeIdleDestinations)
1000 {
1001 this.removeIdleDestinations = removeIdleDestinations;
1002 }
1003
1004
1005
1006
1007 @ManagedAttribute("Whether the connect() operation is blocking")
1008 public boolean isConnectBlocking()
1009 {
1010 return connectBlocking;
1011 }
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023 public void setConnectBlocking(boolean connectBlocking)
1024 {
1025 this.connectBlocking = connectBlocking;
1026 }
1027
1028
1029
1030
1031 public ProxyConfiguration getProxyConfiguration()
1032 {
1033 return proxyConfig;
1034 }
1035
1036 protected HttpField getAcceptEncodingField()
1037 {
1038 return encodingField;
1039 }
1040
1041 protected String normalizeHost(String host)
1042 {
1043 if (host != null && host.matches("\\[.*\\]"))
1044 return host.substring(1, host.length() - 1);
1045 return host;
1046 }
1047
1048 protected int normalizePort(String scheme, int port)
1049 {
1050 return port > 0 ? port : HttpScheme.HTTPS.is(scheme) ? 443 : 80;
1051 }
1052
1053 public boolean isDefaultPort(String scheme, int port)
1054 {
1055 return HttpScheme.HTTPS.is(scheme) ? port == 443 : port == 80;
1056 }
1057
1058 @Override
1059 public void dump(Appendable out, String indent) throws IOException
1060 {
1061 dumpThis(out);
1062 dump(out, indent, getBeans(), destinations.values());
1063 }
1064
1065 private class ContentDecoderFactorySet implements Set<ContentDecoder.Factory>
1066 {
1067 private final Set<ContentDecoder.Factory> set = new HashSet<>();
1068
1069 @Override
1070 public boolean add(ContentDecoder.Factory e)
1071 {
1072 boolean result = set.add(e);
1073 invalidate();
1074 return result;
1075 }
1076
1077 @Override
1078 public boolean addAll(Collection<? extends ContentDecoder.Factory> c)
1079 {
1080 boolean result = set.addAll(c);
1081 invalidate();
1082 return result;
1083 }
1084
1085 @Override
1086 public boolean remove(Object o)
1087 {
1088 boolean result = set.remove(o);
1089 invalidate();
1090 return result;
1091 }
1092
1093 @Override
1094 public boolean removeAll(Collection<?> c)
1095 {
1096 boolean result = set.removeAll(c);
1097 invalidate();
1098 return result;
1099 }
1100
1101 @Override
1102 public boolean retainAll(Collection<?> c)
1103 {
1104 boolean result = set.retainAll(c);
1105 invalidate();
1106 return result;
1107 }
1108
1109 @Override
1110 public void clear()
1111 {
1112 set.clear();
1113 invalidate();
1114 }
1115
1116 @Override
1117 public int size()
1118 {
1119 return set.size();
1120 }
1121
1122 @Override
1123 public boolean isEmpty()
1124 {
1125 return set.isEmpty();
1126 }
1127
1128 @Override
1129 public boolean contains(Object o)
1130 {
1131 return set.contains(o);
1132 }
1133
1134 @Override
1135 public boolean containsAll(Collection<?> c)
1136 {
1137 return set.containsAll(c);
1138 }
1139
1140 @Override
1141 public Iterator<ContentDecoder.Factory> iterator()
1142 {
1143 final Iterator<ContentDecoder.Factory> iterator = set.iterator();
1144 return new Iterator<ContentDecoder.Factory>()
1145 {
1146 @Override
1147 public boolean hasNext()
1148 {
1149 return iterator.hasNext();
1150 }
1151
1152 @Override
1153 public ContentDecoder.Factory next()
1154 {
1155 return iterator.next();
1156 }
1157
1158 @Override
1159 public void remove()
1160 {
1161 iterator.remove();
1162 invalidate();
1163 }
1164 };
1165 }
1166
1167 @Override
1168 public Object[] toArray()
1169 {
1170 return set.toArray();
1171 }
1172
1173 @Override
1174 public <T> T[] toArray(T[] a)
1175 {
1176 return set.toArray(a);
1177 }
1178
1179 private void invalidate()
1180 {
1181 if (set.isEmpty())
1182 {
1183 encodingField = null;
1184 }
1185 else
1186 {
1187 StringBuilder value = new StringBuilder();
1188 for (Iterator<ContentDecoder.Factory> iterator = set.iterator(); iterator.hasNext();)
1189 {
1190 ContentDecoder.Factory decoderFactory = iterator.next();
1191 value.append(decoderFactory.getEncoding());
1192 if (iterator.hasNext())
1193 value.append(",");
1194 }
1195 encodingField = new HttpField(HttpHeader.ACCEPT_ENCODING, value.toString());
1196 }
1197 }
1198 }
1199 }