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