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