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.ConnectException;
23 import java.net.CookieManager;
24 import java.net.CookiePolicy;
25 import java.net.CookieStore;
26 import java.net.SocketAddress;
27 import java.net.SocketException;
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.Arrays;
33 import java.util.Collection;
34 import java.util.HashSet;
35 import java.util.Iterator;
36 import java.util.List;
37 import java.util.Locale;
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.Future;
45 import java.util.concurrent.TimeoutException;
46 import javax.net.ssl.SSLEngine;
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.ProxyConfiguration;
53 import org.eclipse.jetty.client.api.Request;
54 import org.eclipse.jetty.client.api.Response;
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.io.ByteBufferPool;
59 import org.eclipse.jetty.io.EndPoint;
60 import org.eclipse.jetty.io.MappedByteBufferPool;
61 import org.eclipse.jetty.io.SelectChannelEndPoint;
62 import org.eclipse.jetty.io.SelectorManager;
63 import org.eclipse.jetty.io.ssl.SslConnection;
64 import org.eclipse.jetty.util.FuturePromise;
65 import org.eclipse.jetty.util.Jetty;
66 import org.eclipse.jetty.util.Promise;
67 import org.eclipse.jetty.util.SocketAddressResolver;
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 public class HttpClient extends ContainerLifeCycle
111 {
112 private static final Logger LOG = Log.getLogger(HttpClient.class);
113
114 private final ConcurrentMap<String, HttpDestination> destinations = new ConcurrentHashMap<>();
115 private final ConcurrentMap<Long, HttpConversation> conversations = new ConcurrentHashMap<>();
116 private final List<ProtocolHandler> handlers = new ArrayList<>();
117 private final List<Request.Listener> requestListeners = new ArrayList<>();
118 private final AuthenticationStore authenticationStore = new HttpAuthenticationStore();
119 private final Set<ContentDecoder.Factory> decoderFactories = new ContentDecoderFactorySet();
120 private final SslContextFactory sslContextFactory;
121 private volatile CookieManager cookieManager;
122 private volatile CookieStore cookieStore;
123 private volatile Executor executor;
124 private volatile ByteBufferPool byteBufferPool;
125 private volatile Scheduler scheduler;
126 private volatile SocketAddressResolver resolver;
127 private volatile SelectorManager selectorManager;
128 private volatile HttpField agentField = new HttpField(HttpHeader.USER_AGENT, "Jetty/" + Jetty.VERSION);
129 private volatile boolean followRedirects = true;
130 private volatile int maxConnectionsPerDestination = 64;
131 private volatile int maxRequestsQueuedPerDestination = 1024;
132 private volatile int requestBufferSize = 4096;
133 private volatile int responseBufferSize = 4096;
134 private volatile int maxRedirects = 8;
135 private volatile SocketAddress bindAddress;
136 private volatile long connectTimeout = 15000;
137 private volatile long addressResolutionTimeout = 15000;
138 private volatile long idleTimeout;
139 private volatile boolean tcpNoDelay = true;
140 private volatile boolean dispatchIO = true;
141 private volatile ProxyConfiguration proxyConfig;
142 private volatile HttpField encodingField;
143
144
145
146
147
148
149
150 public HttpClient()
151 {
152 this(null);
153 }
154
155
156
157
158
159
160
161
162 public HttpClient(SslContextFactory sslContextFactory)
163 {
164 this.sslContextFactory = sslContextFactory;
165 }
166
167
168
169
170
171 public SslContextFactory getSslContextFactory()
172 {
173 return sslContextFactory;
174 }
175
176 @Override
177 protected void doStart() throws Exception
178 {
179 if (sslContextFactory != null)
180 {
181 addBean(sslContextFactory);
182
183 setDispatchIO(false);
184 }
185
186 String name = HttpClient.class.getSimpleName() + "@" + hashCode();
187
188 if (executor == null)
189 {
190 QueuedThreadPool threadPool = new QueuedThreadPool();
191 threadPool.setName(name);
192 executor = threadPool;
193 }
194 addBean(executor);
195
196 if (byteBufferPool == null)
197 byteBufferPool = new MappedByteBufferPool();
198 addBean(byteBufferPool);
199
200 if (scheduler == null)
201 scheduler = new ScheduledExecutorScheduler(name + "-scheduler", false);
202 addBean(scheduler);
203
204 resolver = new SocketAddressResolver(executor, scheduler, getAddressResolutionTimeout());
205
206 selectorManager = newSelectorManager();
207 selectorManager.setConnectTimeout(getConnectTimeout());
208 addBean(selectorManager);
209
210 handlers.add(new ContinueProtocolHandler(this));
211 handlers.add(new RedirectProtocolHandler(this));
212 handlers.add(new AuthenticationProtocolHandler(this));
213
214 decoderFactories.add(new GZIPContentDecoder.Factory());
215
216 cookieManager = newCookieManager();
217 cookieStore = cookieManager.getCookieStore();
218
219 super.doStart();
220
221 LOG.info("Started {}", this);
222 }
223
224 protected SelectorManager newSelectorManager()
225 {
226 return new ClientSelectorManager(getExecutor(), getScheduler());
227 }
228
229 private CookieManager newCookieManager()
230 {
231 return new CookieManager(getCookieStore(), CookiePolicy.ACCEPT_ALL);
232 }
233
234 @Override
235 protected void doStop() throws Exception
236 {
237 LOG.debug("Stopping {}", this);
238
239 cookieStore.removeAll();
240 cookieStore = null;
241 decoderFactories.clear();
242 handlers.clear();
243
244 for (HttpDestination destination : destinations.values())
245 destination.close();
246 destinations.clear();
247
248 conversations.clear();
249 requestListeners.clear();
250 authenticationStore.clearAuthentications();
251 authenticationStore.clearAuthenticationResults();
252
253 super.doStop();
254
255 LOG.info("Stopped {}", this);
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 public ContentResponse GET(String uri) throws InterruptedException, ExecutionException, TimeoutException
324 {
325 return GET(URI.create(uri));
326 }
327
328
329
330
331
332
333
334
335 public ContentResponse GET(URI uri) throws InterruptedException, ExecutionException, TimeoutException
336 {
337 return newRequest(uri).send();
338 }
339
340
341
342
343
344
345
346
347 public Request POST(String uri)
348 {
349 return POST(URI.create(uri));
350 }
351
352
353
354
355
356
357
358 public Request POST(URI uri)
359 {
360 return newRequest(uri).method(HttpMethod.POST);
361 }
362
363
364
365
366
367
368
369
370 public Request newRequest(String host, int port)
371 {
372 return newRequest(URI.create(address("http", host, port)));
373 }
374
375
376
377
378
379
380
381 public Request newRequest(String uri)
382 {
383 return newRequest(URI.create(uri));
384 }
385
386
387
388
389
390
391
392 public Request newRequest(URI uri)
393 {
394 return new HttpRequest(this, uri);
395 }
396
397 protected Request copyRequest(Request oldRequest, URI newURI)
398 {
399 Request newRequest = new HttpRequest(this, oldRequest.getConversationID(), newURI);
400 newRequest.method(oldRequest.getMethod())
401 .version(oldRequest.getVersion())
402 .content(oldRequest.getContent());
403 for (HttpField header : oldRequest.getHeaders())
404 {
405
406 if (HttpHeader.HOST == header.getHeader())
407 continue;
408
409
410 if (HttpHeader.EXPECT == header.getHeader())
411 continue;
412
413
414 if (HttpHeader.COOKIE == header.getHeader())
415 continue;
416
417
418 if (HttpHeader.AUTHORIZATION == header.getHeader() ||
419 HttpHeader.PROXY_AUTHORIZATION == header.getHeader())
420 continue;
421
422 newRequest.header(header.getName(), header.getValue());
423 }
424 return newRequest;
425 }
426
427 private String address(String scheme, String host, int port)
428 {
429 return scheme + "://" + host + ":" + port;
430 }
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445 public Destination getDestination(String scheme, String host, int port)
446 {
447 return destinationFor(scheme, host, port);
448 }
449
450 protected HttpDestination destinationFor(String scheme, String host, int port)
451 {
452 port = normalizePort(scheme, port);
453
454 String address = address(scheme, host, port);
455 HttpDestination destination = destinations.get(address);
456 if (destination == null)
457 {
458 destination = new HttpDestination(this, scheme, host, port);
459 if (isRunning())
460 {
461 HttpDestination existing = destinations.putIfAbsent(address, destination);
462 if (existing != null)
463 destination = existing;
464 else
465 LOG.debug("Created {}", destination);
466 if (!isRunning())
467 destinations.remove(address);
468 }
469
470 }
471 return destination;
472 }
473
474
475
476
477 public List<Destination> getDestinations()
478 {
479 return new ArrayList<Destination>(destinations.values());
480 }
481
482 protected void send(final Request request, List<Response.ResponseListener> listeners)
483 {
484 String scheme = request.getScheme().toLowerCase(Locale.ENGLISH);
485 if (!Arrays.asList("http", "https").contains(scheme))
486 throw new IllegalArgumentException("Invalid protocol " + scheme);
487
488 HttpDestination destination = destinationFor(scheme, request.getHost(), request.getPort());
489 destination.send(request, listeners);
490 }
491
492 protected void newConnection(final HttpDestination destination, final Promise<Connection> promise)
493 {
494 Destination.Address address = destination.getConnectAddress();
495 resolver.resolve(address.getHost(), address.getPort(), new Promise<SocketAddress>()
496 {
497 @Override
498 public void succeeded(SocketAddress socketAddress)
499 {
500 SocketChannel channel = null;
501 try
502 {
503 channel = SocketChannel.open();
504 SocketAddress bindAddress = getBindAddress();
505 if (bindAddress != null)
506 channel.bind(bindAddress);
507 configure(channel);
508 channel.configureBlocking(false);
509 channel.connect(socketAddress);
510
511 Future<Connection> futureConnection = new ConnectionCallback(destination, promise);
512 selectorManager.connect(channel, futureConnection);
513 }
514
515
516 catch (Throwable x)
517 {
518 if (channel != null)
519 close(channel);
520 promise.failed(x);
521 }
522 }
523
524 @Override
525 public void failed(Throwable x)
526 {
527 promise.failed(x);
528 }
529 });
530 }
531
532 protected void configure(SocketChannel channel) throws SocketException
533 {
534 channel.socket().setTcpNoDelay(isTCPNoDelay());
535 }
536
537 private void close(SocketChannel channel)
538 {
539 try
540 {
541 channel.close();
542 }
543 catch (IOException x)
544 {
545 LOG.ignore(x);
546 }
547 }
548
549 protected HttpConversation getConversation(long id, boolean create)
550 {
551 HttpConversation conversation = conversations.get(id);
552 if (conversation == null && create)
553 {
554 conversation = new HttpConversation(this, id);
555 HttpConversation existing = conversations.putIfAbsent(id, conversation);
556 if (existing != null)
557 conversation = existing;
558 else
559 LOG.debug("{} created", conversation);
560 }
561 return conversation;
562 }
563
564 protected void removeConversation(HttpConversation conversation)
565 {
566 conversations.remove(conversation.getID());
567 LOG.debug("{} removed", conversation);
568 }
569
570 protected List<ProtocolHandler> getProtocolHandlers()
571 {
572 return handlers;
573 }
574
575 protected ProtocolHandler findProtocolHandler(Request request, Response response)
576 {
577
578 List<ProtocolHandler> protocolHandlers = getProtocolHandlers();
579 for (int i = 0; i < protocolHandlers.size(); ++i)
580 {
581 ProtocolHandler handler = protocolHandlers.get(i);
582 if (handler.accept(request, response))
583 return handler;
584 }
585 return null;
586 }
587
588
589
590
591 public ByteBufferPool getByteBufferPool()
592 {
593 return byteBufferPool;
594 }
595
596
597
598
599 public void setByteBufferPool(ByteBufferPool byteBufferPool)
600 {
601 this.byteBufferPool = byteBufferPool;
602 }
603
604
605
606
607 public long getConnectTimeout()
608 {
609 return connectTimeout;
610 }
611
612
613
614
615
616 public void setConnectTimeout(long connectTimeout)
617 {
618 this.connectTimeout = connectTimeout;
619 }
620
621
622
623
624 public long getAddressResolutionTimeout()
625 {
626 return addressResolutionTimeout;
627 }
628
629
630
631
632 public void setAddressResolutionTimeout(long addressResolutionTimeout)
633 {
634 this.addressResolutionTimeout = addressResolutionTimeout;
635 }
636
637
638
639
640 public long getIdleTimeout()
641 {
642 return idleTimeout;
643 }
644
645
646
647
648 public void setIdleTimeout(long idleTimeout)
649 {
650 this.idleTimeout = idleTimeout;
651 }
652
653
654
655
656
657 public SocketAddress getBindAddress()
658 {
659 return bindAddress;
660 }
661
662
663
664
665
666
667 public void setBindAddress(SocketAddress bindAddress)
668 {
669 this.bindAddress = bindAddress;
670 }
671
672
673
674
675 public HttpField getUserAgentField()
676 {
677 return agentField;
678 }
679
680
681
682
683 public void setUserAgentField(HttpField agent)
684 {
685 if (agent.getHeader() != HttpHeader.USER_AGENT)
686 throw new IllegalArgumentException();
687 this.agentField = agent;
688 }
689
690
691
692
693
694 public boolean isFollowRedirects()
695 {
696 return followRedirects;
697 }
698
699
700
701
702
703 public void setFollowRedirects(boolean follow)
704 {
705 this.followRedirects = follow;
706 }
707
708
709
710
711 public Executor getExecutor()
712 {
713 return executor;
714 }
715
716
717
718
719 public void setExecutor(Executor executor)
720 {
721 this.executor = executor;
722 }
723
724
725
726
727 public Scheduler getScheduler()
728 {
729 return scheduler;
730 }
731
732
733
734
735 public void setScheduler(Scheduler scheduler)
736 {
737 this.scheduler = scheduler;
738 }
739
740 protected SelectorManager getSelectorManager()
741 {
742 return selectorManager;
743 }
744
745
746
747
748 public int getMaxConnectionsPerDestination()
749 {
750 return maxConnectionsPerDestination;
751 }
752
753
754
755
756
757
758
759
760
761
762
763
764 public void setMaxConnectionsPerDestination(int maxConnectionsPerDestination)
765 {
766 this.maxConnectionsPerDestination = maxConnectionsPerDestination;
767 }
768
769
770
771
772 public int getMaxRequestsQueuedPerDestination()
773 {
774 return maxRequestsQueuedPerDestination;
775 }
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790 public void setMaxRequestsQueuedPerDestination(int maxRequestsQueuedPerDestination)
791 {
792 this.maxRequestsQueuedPerDestination = maxRequestsQueuedPerDestination;
793 }
794
795
796
797
798 public int getRequestBufferSize()
799 {
800 return requestBufferSize;
801 }
802
803
804
805
806 public void setRequestBufferSize(int requestBufferSize)
807 {
808 this.requestBufferSize = requestBufferSize;
809 }
810
811
812
813
814 public int getResponseBufferSize()
815 {
816 return responseBufferSize;
817 }
818
819
820
821
822 public void setResponseBufferSize(int responseBufferSize)
823 {
824 this.responseBufferSize = responseBufferSize;
825 }
826
827
828
829
830
831 public int getMaxRedirects()
832 {
833 return maxRedirects;
834 }
835
836
837
838
839
840 public void setMaxRedirects(int maxRedirects)
841 {
842 this.maxRedirects = maxRedirects;
843 }
844
845
846
847
848 public boolean isTCPNoDelay()
849 {
850 return tcpNoDelay;
851 }
852
853
854
855
856
857 public void setTCPNoDelay(boolean tcpNoDelay)
858 {
859 this.tcpNoDelay = tcpNoDelay;
860 }
861
862
863
864
865
866 public boolean isDispatchIO()
867 {
868 return dispatchIO;
869 }
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884 public void setDispatchIO(boolean dispatchIO)
885 {
886 this.dispatchIO = dispatchIO;
887 }
888
889
890
891
892 public ProxyConfiguration getProxyConfiguration()
893 {
894 return proxyConfig;
895 }
896
897
898
899
900 public void setProxyConfiguration(ProxyConfiguration proxyConfig)
901 {
902 this.proxyConfig = proxyConfig;
903 }
904
905 protected HttpField getAcceptEncodingField()
906 {
907 return encodingField;
908 }
909
910 protected int normalizePort(String scheme, int port)
911 {
912 return port > 0 ? port :
913 "https".equalsIgnoreCase(scheme) ? 443 : 80;
914 }
915
916 protected HttpConnection newHttpConnection(HttpClient httpClient, EndPoint endPoint, HttpDestination destination)
917 {
918 return new HttpConnection(httpClient, endPoint, destination);
919 }
920
921 protected SslConnection newSslConnection(HttpClient httpClient, EndPoint endPoint, SSLEngine engine)
922 {
923 return new SslConnection(httpClient.getByteBufferPool(), httpClient.getExecutor(), endPoint, engine);
924 }
925
926 @Override
927 public void dump(Appendable out, String indent) throws IOException
928 {
929 dumpThis(out);
930 dump(out, indent, getBeans(), destinations.values());
931 }
932
933 protected class ClientSelectorManager extends SelectorManager
934 {
935 public ClientSelectorManager(Executor executor, Scheduler scheduler)
936 {
937 this(executor, scheduler, 1);
938 }
939
940 public ClientSelectorManager(Executor executor, Scheduler scheduler, int selectors)
941 {
942 super(executor, scheduler, selectors);
943 }
944
945 @Override
946 protected EndPoint newEndPoint(SocketChannel channel, ManagedSelector selector, SelectionKey key)
947 {
948 return new SelectChannelEndPoint(channel, selector, key, getScheduler(), getIdleTimeout());
949 }
950
951 @Override
952 public org.eclipse.jetty.io.Connection newConnection(SocketChannel channel, EndPoint endPoint, Object attachment) throws IOException
953 {
954 ConnectionCallback callback = (ConnectionCallback)attachment;
955 HttpDestination destination = callback.destination;
956
957 SslContextFactory sslContextFactory = getSslContextFactory();
958 if ("https".equals(destination.getScheme()))
959 {
960 if (sslContextFactory == null)
961 {
962 IOException failure = new ConnectException("Missing " + SslContextFactory.class.getSimpleName() + " for " + destination.getScheme() + " requests");
963 callback.failed(failure);
964 throw failure;
965 }
966 else
967 {
968 SSLEngine engine = sslContextFactory.newSSLEngine(destination.getHost(), destination.getPort());
969 engine.setUseClientMode(true);
970
971 SslConnection sslConnection = newSslConnection(HttpClient.this, endPoint, engine);
972 EndPoint appEndPoint = sslConnection.getDecryptedEndPoint();
973 HttpConnection connection = newHttpConnection(HttpClient.this, appEndPoint, destination);
974
975 appEndPoint.setConnection(connection);
976 callback.promise.succeeded(connection);
977
978 return sslConnection;
979 }
980 }
981 else
982 {
983 HttpConnection connection = newHttpConnection(HttpClient.this, endPoint, destination);
984 callback.promise.succeeded(connection);
985 return connection;
986 }
987 }
988
989 @Override
990 protected void connectionFailed(SocketChannel channel, Throwable ex, Object attachment)
991 {
992 ConnectionCallback callback = (ConnectionCallback)attachment;
993 callback.promise.failed(ex);
994 }
995 }
996
997 private class ConnectionCallback extends FuturePromise<Connection>
998 {
999 private final HttpDestination destination;
1000 private final Promise<Connection> promise;
1001
1002 private ConnectionCallback(HttpDestination destination, Promise<Connection> promise)
1003 {
1004 this.destination = destination;
1005 this.promise = promise;
1006 }
1007 }
1008
1009 private class ContentDecoderFactorySet implements Set<ContentDecoder.Factory>
1010 {
1011 private final Set<ContentDecoder.Factory> set = new HashSet<>();
1012
1013 @Override
1014 public boolean add(ContentDecoder.Factory e)
1015 {
1016 boolean result = set.add(e);
1017 invalidate();
1018 return result;
1019 }
1020
1021 @Override
1022 public boolean addAll(Collection<? extends ContentDecoder.Factory> c)
1023 {
1024 boolean result = set.addAll(c);
1025 invalidate();
1026 return result;
1027 }
1028
1029 @Override
1030 public boolean remove(Object o)
1031 {
1032 boolean result = set.remove(o);
1033 invalidate();
1034 return result;
1035 }
1036
1037 @Override
1038 public boolean removeAll(Collection<?> c)
1039 {
1040 boolean result = set.removeAll(c);
1041 invalidate();
1042 return result;
1043 }
1044
1045 @Override
1046 public boolean retainAll(Collection<?> c)
1047 {
1048 boolean result = set.retainAll(c);
1049 invalidate();
1050 return result;
1051 }
1052
1053 @Override
1054 public void clear()
1055 {
1056 set.clear();
1057 invalidate();
1058 }
1059
1060 @Override
1061 public int size()
1062 {
1063 return set.size();
1064 }
1065
1066 @Override
1067 public boolean isEmpty()
1068 {
1069 return set.isEmpty();
1070 }
1071
1072 @Override
1073 public boolean contains(Object o)
1074 {
1075 return set.contains(o);
1076 }
1077
1078 @Override
1079 public boolean containsAll(Collection<?> c)
1080 {
1081 return set.containsAll(c);
1082 }
1083
1084 @Override
1085 public Iterator<ContentDecoder.Factory> iterator()
1086 {
1087 return set.iterator();
1088 }
1089
1090 @Override
1091 public Object[] toArray()
1092 {
1093 return set.toArray();
1094 }
1095
1096 @Override
1097 public <T> T[] toArray(T[] a)
1098 {
1099 return set.toArray(a);
1100 }
1101
1102 protected void invalidate()
1103 {
1104 if (set.isEmpty())
1105 {
1106 encodingField = null;
1107 }
1108 else
1109 {
1110 StringBuilder value = new StringBuilder();
1111 for (Iterator<ContentDecoder.Factory> iterator = set.iterator(); iterator.hasNext();)
1112 {
1113 ContentDecoder.Factory decoderFactory = iterator.next();
1114 value.append(decoderFactory.getEncoding());
1115 if (iterator.hasNext())
1116 value.append(",");
1117 }
1118 encodingField = new HttpField(HttpHeader.ACCEPT_ENCODING, value.toString());
1119 }
1120 }
1121 }
1122 }