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