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