1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79 package org.metricshub.wbem.sblim.cimclient.internal.http;
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101 import java.io.BufferedInputStream;
102 import java.io.BufferedOutputStream;
103 import java.io.ByteArrayOutputStream;
104 import java.io.IOException;
105 import java.io.InputStream;
106 import java.io.OutputStream;
107 import java.net.HttpURLConnection;
108 import java.net.InetSocketAddress;
109 import java.net.Socket;
110 import java.net.SocketTimeoutException;
111 import java.net.URI;
112 import java.security.AccessController;
113 import java.security.MessageDigest;
114 import java.security.NoSuchAlgorithmException;
115 import java.security.PrivilegedAction;
116 import java.util.Arrays;
117 import java.util.Iterator;
118 import java.util.Map.Entry;
119 import java.util.StringTokenizer;
120 import java.util.Vector;
121 import java.util.logging.Level;
122 import java.util.zip.GZIPInputStream;
123 import javax.net.SocketFactory;
124 import javax.net.ssl.HandshakeCompletedEvent;
125 import javax.net.ssl.HandshakeCompletedListener;
126 import javax.net.ssl.SSLSession;
127 import javax.net.ssl.SSLSocket;
128 import javax.net.ssl.SSLSocketFactory;
129 import org.metricshub.wbem.sblim.cimclient.WBEMConfigurationProperties;
130 import org.metricshub.wbem.sblim.cimclient.internal.http.HttpHeader.HeaderEntry;
131 import org.metricshub.wbem.sblim.cimclient.internal.http.io.ASCIIPrintStream;
132 import org.metricshub.wbem.sblim.cimclient.internal.http.io.BoundedInputStream;
133 import org.metricshub.wbem.sblim.cimclient.internal.http.io.ChunkedInputStream;
134 import org.metricshub.wbem.sblim.cimclient.internal.http.io.KeepAliveInputStream;
135 import org.metricshub.wbem.sblim.cimclient.internal.http.io.PersistentInputStream;
136 import org.metricshub.wbem.sblim.cimclient.internal.logging.LogAndTraceBroker;
137 import org.metricshub.wbem.sblim.cimclient.internal.logging.Messages;
138 import org.metricshub.wbem.sblim.cimclient.internal.util.WBEMConstants;
139
140
141
142
143
144 public class HttpClient implements HandshakeCompletedListener {
145
146 private static class HostPortPair {
147 String iHost;
148
149
150
151
152
153
154
155 public HostPortPair(URI url) {
156 this.iHost = url.getScheme().toLowerCase() + ':' + url.getHost().toLowerCase() + ':' + url.getPort();
157 }
158
159 @Override
160 public boolean equals(Object o) {
161 if (!(o instanceof HostPortPair)) return false;
162
163 return this.iHost.equals(((HostPortPair) o).iHost);
164 }
165
166 @Override
167 public int hashCode() {
168 return this.iHost.hashCode();
169 }
170
171 @Override
172 public String toString() {
173 return "HostPortPair=[+" + this.iHost + "]";
174 }
175 }
176
177 private static class GetProperty implements PrivilegedAction<Object> {
178 String iPropertyName;
179
180 GetProperty(String propertyName) {
181 this.iPropertyName = propertyName;
182 }
183
184 public Object run() {
185 return System.getProperty(this.iPropertyName);
186 }
187 }
188
189 private static String iEncoding;
190
191 static {
192 try {
193 iEncoding =
194 (String) AccessController.doPrivileged(
195 new PrivilegedAction<Object>() {
196
197 public Object run() {
198 return System.getProperty("file.encoding", "ISO8859_1");
199 }
200 }
201 );
202 if (!isASCIISuperset(iEncoding)) iEncoding = "ISO8859_1";
203 } catch (Exception exception) {
204 iEncoding = "ISO8859_1";
205 }
206 }
207
208
209
210
211
212
213
214
215 public static String convertToHexString(byte[] digest) {
216 char hexDigit[] = "0123456789abcdef".toCharArray();
217
218 StringBuffer buf = new StringBuffer();
219 for (int i = 0; i < digest.length; i++) {
220 int b = digest[i];
221 buf.append(hexDigit[(b >> 4) & 0xF]);
222 buf.append(hexDigit[(b) & 0xF]);
223 }
224 return buf.toString();
225 }
226
227
228
229
230
231
232
233
234
235
236
237
238 public static HttpClient getClient(URI url, HttpClientPool clientPool, AuthorizationHandler auth_handler) {
239 return clientPool.retrieveAvailableConnectionFromPool(url, auth_handler);
240 }
241
242 protected static String dequote(String str) {
243 int len = str.length();
244 if (len > 1 && str.charAt(0) == '\"' && str.charAt(len - 1) == '\"') return str.substring(1, len - 1);
245 return str;
246 }
247
248 protected static void handleRsp(String authInfo, AuthorizationInfo prevAuthInfo) throws IOException {
249 if (authInfo != null) {
250 HttpHeader params = HttpHeader.parse(authInfo);
251
252 String nonce = params.getField("nextnonce");
253 if (nonce != null) {
254 prevAuthInfo.setNonce(nonce);
255 prevAuthInfo.setNc(0);
256 } else {
257 nonce = prevAuthInfo.getNonce();
258 }
259 String qop = params.getField("qop");
260 if (qop != null) {
261 if (!"auth".equalsIgnoreCase(qop) && !"auth-int".equalsIgnoreCase(qop)) {
262
263 throw new IOException("Authentication Digest with integrity check not supported");
264 }
265 byte[] rspauth;
266 String rspauthStr = dequote(params.getField("rspauth"));
267 if (rspauthStr != null) {
268 rspauth = parseHex(rspauthStr);
269
270 String cnonce = dequote(params.getField("cnonce"));
271 if (cnonce != null && !cnonce.equals(prevAuthInfo.getCnonce())) {
272 throw new IOException("Digest authentication: Invalid nonce counter");
273 }
274 String ncStr = params.getField("nc");
275 if (ncStr != null) {
276 try {
277 long nc = Long.parseLong(ncStr, 16);
278 if (nc != prevAuthInfo.getNc()) {
279 throw new IOException();
280 }
281 } catch (Exception e) {
282 throw new IOException("Digest authentication: Invalid nonce counter");
283 }
284 }
285
286 String HA1, HA2;
287 MessageDigest md5;
288 try {
289 md5 = MessageDigest.getInstance("MD5");
290 md5.reset();
291 byte[] bytes = prevAuthInfo.getA1().getBytes("UTF-8");
292 md5.update(bytes);
293 HA1 = convertToHexString(md5.digest());
294 if ("MD5-sess".equalsIgnoreCase(params.getField("algorithm"))) {
295 md5.reset();
296 md5.update((HA1 + ":" + nonce + ":" + cnonce).getBytes("UTF-8"));
297 HA1 = convertToHexString(md5.digest());
298 }
299
300 HA2 = ":" + prevAuthInfo.getURI();
301 if ("auth-int".equalsIgnoreCase(qop)) {
302 md5.reset();
303 md5.update(new byte[] {});
304 HA2 += ":" + convertToHexString(md5.digest());
305 }
306 md5.reset();
307 md5.update(HA2.getBytes("UTF-8"));
308 HA2 = convertToHexString(md5.digest());
309
310 md5.reset();
311 md5.update((HA1 + ":" + nonce + ":" + ncStr + ":" + cnonce + ":" + qop + ":" + HA2).getBytes("UTF-8"));
312 String hsh = convertToHexString(md5.digest());
313 byte[] hash = parseHex(hsh);
314
315 if (!Arrays.equals(hash, rspauth)) throw new IOException("Digest Authentication failed!");
316 } catch (NoSuchAlgorithmException e1) {
317 throw new IOException("Unable to validate Authentication response: NoSuchAlgorithmException");
318 }
319 }
320 } else {
321
322 }
323 }
324 }
325
326 protected static byte[] parseHex(String hex) {
327 byte[] value = new byte[hex.length() >> 1];
328 int n = 0;
329 for (int i = 0; i < value.length; i++) {
330 value[i] = (byte) (0xff & Integer.parseInt(hex.substring(n, n + 1), 16));
331 n += 2;
332 }
333 return value;
334 }
335
336 private static boolean isASCIISuperset(String charset) throws Exception {
337 String asciiSuperSet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_.!~*'();/?:@&=+$,";
338 byte abyte0[] = {
339 48,
340 49,
341 50,
342 51,
343 52,
344 53,
345 54,
346 55,
347 56,
348 57,
349 65,
350 66,
351 67,
352 68,
353 69,
354 70,
355 71,
356 72,
357 73,
358 74,
359 75,
360 76,
361 77,
362 78,
363 79,
364 80,
365 81,
366 82,
367 83,
368 84,
369 85,
370 86,
371 87,
372 88,
373 89,
374 90,
375 97,
376 98,
377 99,
378 100,
379 101,
380 102,
381 103,
382 104,
383 105,
384 106,
385 107,
386 108,
387 109,
388 110,
389 111,
390 112,
391 113,
392 114,
393 115,
394 116,
395 117,
396 118,
397 119,
398 120,
399 121,
400 122,
401 45,
402 95,
403 46,
404 33,
405 126,
406 42,
407 39,
408 40,
409 41,
410 59,
411 47,
412 63,
413 58,
414 64,
415 38,
416 61,
417 43,
418 36,
419 44
420 };
421 byte convertedArray[] = asciiSuperSet.getBytes(charset);
422 return Arrays.equals(convertedArray, abyte0);
423 }
424
425 private boolean iConnected = false;
426
427 private HttpClientPool iHttpClientPool;
428
429 private AuthorizationHandler iAuth_handler;
430
431 private SSLSession iSession;
432
433 private InputStream iIStream;
434
435 private boolean iUseHttp11 = true;
436
437 private boolean iKeepAlive = true;
438
439 private HttpClientMethod iMethod;
440
441 private OutputStream iOStream;
442
443 private AuthorizationInfo iPrevAuthInfo;
444
445 private AuthorizationInfo iPrevProxy;
446
447 private HttpHeader iRequestHeaders = new HttpHeader();
448
449 private String iRequestMethod = "POST";
450
451 private boolean iReset = true;
452
453 private HttpClientMethod iResponse;
454
455 private HttpHeader iResponseHeaders = new HttpHeader();
456
457 private InputStream iServerInput;
458
459 private ByteArrayOutputStream iServerOutput;
460
461 private Socket iSocket;
462
463 private URI iUrl;
464
465 private long iPreviousResponseTime = -1;
466
467
468
469
470
471
472
473
474
475
476
477 public HttpClient(URI url, HttpClientPool clientPool, AuthorizationHandler auth_handler) {
478 this.iUrl = url;
479 this.iAuth_handler = auth_handler;
480 this.iHttpClientPool = clientPool;
481 }
482
483
484
485
486
487
488 public void connect() throws IOException {
489 LogAndTraceBroker logger = LogAndTraceBroker.getBroker();
490 logger.entry();
491 try {
492 this.iReset = true;
493 this.iResponse = null;
494 this.iConnected = true;
495 this.iServerOutput = null;
496 resetSocket();
497 } finally {
498 logger.exit();
499 }
500 }
501
502
503
504
505 public synchronized void disconnect() {
506 LogAndTraceBroker logger = LogAndTraceBroker.getBroker();
507 logger.entry();
508 this.iConnected = false;
509 if (this.iSocket != null) {
510 try {
511 this.iSocket.close();
512 } catch (IOException e) {
513 logger.trace(Level.FINE, "Unexpected problem closing http socket", e);
514 }
515 this.iSocket = null;
516 this.iServerInput = null;
517 this.iReset = true;
518 this.iResponse = null;
519 }
520 logger.exit();
521 }
522
523 @Override
524 protected void finalize() throws Throwable {
525 try {
526 this.iSocket.close();
527 } catch (IOException e) {
528
529 } finally {
530 super.finalize();
531 }
532 }
533
534
535
536
537
538
539
540
541 public synchronized String getHeaderFieldValue(int index) {
542 if (index < 0) throw new IllegalArgumentException();
543 if (index == 0) return this.iResponse.toString();
544
545 Iterator<Entry<HeaderEntry, String>> iterator = this.iResponseHeaders.iterator();
546 while (iterator.hasNext() && --index >= 0) {
547 Entry<HeaderEntry, String> entry = iterator.next();
548 if (index == 0) return entry.getValue().toString();
549 }
550 return null;
551 }
552
553
554
555
556
557
558
559
560 public synchronized String getHeaderField(String name) {
561 return this.iResponseHeaders.getField(name);
562 }
563
564
565
566
567
568
569
570
571 public synchronized String getHeaderFieldName(int index) {
572 if (index < 0) throw new IllegalArgumentException();
573 if (index == 0) return null;
574
575 Iterator<Entry<HeaderEntry, String>> iterator = this.iResponseHeaders.iterator();
576 while (iterator.hasNext() && --index >= 0) {
577 Entry<HeaderEntry, String> entry = iterator.next();
578 if (index == 0) return entry.getKey().toString();
579 }
580 return null;
581 }
582
583
584
585
586
587
588
589 public synchronized InputStream getInputStream() throws IOException {
590 if (getResponseCode() < 500 && this.iResponse != null && this.iServerInput != null) return this.iServerInput;
591
592 throw new IOException("Failed to open an input stream from server: HTTPResponse " + getResponseCode());
593 }
594
595
596
597
598
599
600 public synchronized OutputStream getOutputStream() {
601 if (this.iServerOutput == null) {
602 this.iServerOutput = new ByteArrayOutputStream();
603 }
604 return this.iServerOutput;
605 }
606
607
608
609
610
611
612 public String getRequestMethod() {
613 return this.iRequestMethod;
614 }
615
616
617
618
619
620
621
622
623 public String getRequestProperty(String key) {
624 return this.iRequestHeaders.getField(key);
625 }
626
627
628
629
630
631
632
633 public synchronized int getResponseCode() throws IOException {
634 LogAndTraceBroker logger = LogAndTraceBroker.getBroker();
635 logger.entry();
636 try {
637 long ResponseTime = -1;
638 Exception delayedException = null;
639 if (this.iReset && this.iResponse == null) {
640 boolean authFailed = false;
641 int IoRetry = 1;
642 int AuthentificationRetry = 1;
643 do {
644 logger.trace(
645 Level.FINER,
646 "Attempting http request (retry counters:" + IoRetry + "/" + AuthentificationRetry + ")"
647 );
648 long RequestTime = System.currentTimeMillis();
649
650 if (this.iPreviousResponseTime != -1) {
651 long time = RequestTime - this.iPreviousResponseTime;
652 long maxTime = this.iHttpClientPool.getConfigurationContext().getSocketIdleTimeout();
653 if (maxTime > 0 && time > maxTime) {
654 logger.trace(Level.FINER, "Closing socket after " + time + "ms of idle time");
655
656 if (this.iSocket != null && !this.iSocket.isClosed()) {
657 try {
658 this.iSocket.close();
659 } catch (IOException e) {
660 logger.trace(Level.FINER, "Exception caught while closing socket", e);
661 }
662 }
663 this.iSocket = null;
664 this.iReset = true;
665 this.iResponse = null;
666 }
667 }
668
669 ResponseTime = -1;
670 resetSocket();
671 this.iReset = false;
672 try {
673 ASCIIPrintStream out = (ASCIIPrintStream) this.iOStream;
674 if (out == null) throw new IOException("could not open output stream");
675
676 String file = this.iUrl.getPath();
677 if (file == null || file.length() == 0) file = "/";
678 String query = this.iUrl.getQuery();
679 if (query != null) file = file + '?' + query;
680
681 this.iMethod = new HttpClientMethod(this.iRequestMethod, this.iUrl.getPath(), 1, this.iUseHttp11 ? 1 : 0);
682 logger.trace(Level.FINER, "HTTP Operation= " + this.iMethod);
683
684 this.iMethod.write(out);
685
686 StringBuilder hostField = new StringBuilder(this.iUrl.getHost());
687 if (
688 this.iUrl.getPort() > 0 &&
689 (
690 (
691 WBEMConstants.HTTPS.equalsIgnoreCase(this.iUrl.getScheme()) &&
692 this.iUrl.getPort() != WBEMConstants.DEFAULT_WBEM_SECURE_PORT
693 ) ||
694 (
695 WBEMConstants.HTTP.equalsIgnoreCase(this.iUrl.getScheme()) &&
696 this.iUrl.getPort() != WBEMConstants.DEFAULT_WBEM_PORT
697 )
698 )
699 ) {
700 hostField.append(':');
701 hostField.append(this.iUrl.getPort());
702 }
703 this.iRequestHeaders.addField("Host", hostField.toString());
704
705 if (this.iServerOutput != null) this.iRequestHeaders.addField(
706 "Content-length",
707 "" + this.iServerOutput.size()
708 ); else this.iRequestHeaders.addField("Content-length", "0");
709 if (this.iHttpClientPool.getConfigurationContext().isHttpChunked()) {
710 this.iRequestHeaders.addField("TE", "trailers");
711 }
712
713 if (iUseHttp11 && !iHttpClientPool.getConfigurationContext().useKeepAliveStrictMode()) {
714
715
716
717
718
719 logger.trace(
720 Level.INFO,
721 "HTTP 1.1 protocol and 'Connection=Keep-alive' strict mode disabled, we add the header"
722 );
723 iRequestHeaders.addField("Connection", "Keep-alive");
724 }
725
726 if (this.iPrevAuthInfo == null) {
727 AuthorizationInfo authInfo = this.iAuth_handler.getAuthorizationInfo(0);
728 String authenticate = this.iHttpClientPool.getConfigurationContext().getHttpWwwAuthenticateInfo();
729
730 if (authInfo.isSentOnFirstRequest()) {
731 this.iRequestHeaders.addField(authInfo.getHeaderFieldName(), authInfo.toString());
732 } else if (authenticate != null) {
733 try {
734 this.iPrevAuthInfo = getAuthentication(false, this.iPrevAuthInfo, authenticate);
735 if (this.iPrevAuthInfo != null) {
736 this.iRequestHeaders.addField(
737 this.iPrevAuthInfo.getHeaderFieldName(),
738 this.iPrevAuthInfo.toString()
739 );
740 }
741 } catch (NoSuchAlgorithmException e) {
742 logger.trace(Level.FINER, "Unable to find digest algorithm", e);
743 } catch (IllegalArgumentException e) {
744 logger.trace(
745 Level.FINER,
746 WBEMConfigurationProperties.HTTP_WWW_AUTHENTICATE_INFO +
747 " did not contain WWW-Authenticate information",
748 e
749 );
750 } catch (HttpParseException e) {
751 logger.trace(
752 Level.FINER,
753 WBEMConfigurationProperties.HTTP_WWW_AUTHENTICATE_INFO +
754 " did not contain valid WWW-Authenticate information",
755 e
756 );
757 }
758 }
759 } else {
760 this.iRequestHeaders.addField(this.iPrevAuthInfo.getHeaderFieldName(), this.iPrevAuthInfo.toString());
761 }
762
763 if (this.iPrevProxy != null) this.iRequestHeaders.addField(
764 "Proxy-authorization",
765 this.iPrevProxy.toString()
766 );
767
768 boolean isGzipped = false;
769 if (this.iHttpClientPool.getConfigurationContext().isGzipEncodingEnabled()) {
770 isGzipped = true;
771 this.iRequestHeaders.addField("Accept-Encoding", "gzip,identity;q=0.5");
772 }
773
774 this.iRequestHeaders.write(out);
775
776 logger.trace(Level.FINER, "Request HTTP Headers= " + this.iRequestHeaders);
777
778 if (out.checkError() != null) {
779 delayedException = out.checkError();
780 logger.trace(Level.FINER, "Exception caught while writing to the http output stream.", delayedException);
781 if (this.iSocket != null && !this.iSocket.isClosed()) {
782 try {
783 this.iSocket.close();
784 } catch (IOException e) {
785 logger.trace(Level.FINER, "Exception caught while closing socket", e);
786 }
787 }
788 this.iSocket = null;
789 this.iReset = true;
790 this.iResponse = null;
791 --IoRetry;
792 continue;
793 }
794 if (this.iServerOutput != null) {
795 this.iServerOutput.writeTo(out);
796 }
797 out.flush();
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825 this.iResponse = new HttpClientMethod(this.iIStream);
826 logger.trace(Level.FINER, "HTTP Response= " + this.iResponse);
827 ResponseTime = System.currentTimeMillis();
828
829 this.iResponseHeaders = new HttpHeader(this.iIStream);
830 logger.trace(Level.FINER, "Response HTTP Headers= " + this.iResponseHeaders.toString());
831 this.iKeepAlive = false;
832 if (
833 "Keep-alive".equalsIgnoreCase(this.iResponseHeaders.getField("Connection")) ||
834 (this.iResponse.getMajorVersion() == 1 && this.iResponse.getMinorVersion() == 1) ||
835 this.iResponseHeaders.getField("Keep-alive") != null
836 ) {
837 this.iKeepAlive = true;
838 }
839
840 this.iServerInput = new PersistentInputStream(this.iIStream);
841
842
843
844 String contentLength = this.iResponseHeaders.getField("Content-length");
845 long length = -1;
846 try {
847 if (contentLength != null && contentLength.length() > 0) length = Long.parseLong(contentLength);
848 } catch (Exception e) {
849 logger.trace(Level.FINER, "Exception while parsing the content length of http response", e);
850 }
851 this.iKeepAlive = (length >= 0 || this.iResponse.getStatus() == 304 || this.iResponse.getStatus() == 204);
852
853 if (isGzipped) {
854 String contentEncoding = this.iResponseHeaders.getField("Content-Encoding");
855 if (contentEncoding != null && contentEncoding.toLowerCase().contains("gzip")) {
856 length = -1;
857 this.iServerInput = new GZIPInputStream(this.iServerInput);
858 }
859 }
860
861 String transferEncoding = this.iResponseHeaders.getField("Transfer-encoding");
862 if (transferEncoding != null) {
863 length = -1;
864 if (transferEncoding.toLowerCase().endsWith("chunked")) {
865 this.iServerInput =
866 new ChunkedInputStream(this.iServerInput, this.iResponseHeaders.getField("Trailer"), "Response");
867 this.iKeepAlive = true;
868 }
869 }
870
871 if (length >= 0) this.iServerInput = new BoundedInputStream(this.iServerInput, length);
872
873 logger.trace(Level.FINER, "KeepAlive=" + (this.iKeepAlive ? "true" : "false"));
874
875 if (this.iKeepAlive) {
876 this.iServerInput = new KeepAliveInputStream(this.iServerInput, this);
877 }
878
879 switch (this.iResponse.getStatus()) {
880 case 100: {
881 continue;
882 }
883 case HttpURLConnection.HTTP_OK:
884 String authInfo = this.iResponseHeaders.getField("Authentication-Info");
885 handleRsp(authInfo, this.iPrevAuthInfo);
886
887 authInfo = this.iResponseHeaders.getField("Authentication-Proxy");
888 handleRsp(authInfo, this.iPrevProxy);
889
890 if (this.iServerOutput != null) this.iServerOutput = null;
891
892 this.iPreviousResponseTime = ResponseTime;
893 return HttpURLConnection.HTTP_OK;
894 case HttpURLConnection.HTTP_UNAUTHORIZED:
895 --AuthentificationRetry;
896 String authenticate = this.iResponseHeaders.getField("WWW-Authenticate");
897 try {
898 this.iPrevAuthInfo = getAuthentication(false, this.iPrevAuthInfo, authenticate);
899 if (this.iPrevAuthInfo != null) {
900 this.iRequestHeaders.addField(
901 this.iPrevAuthInfo.getHeaderFieldName(),
902 this.iPrevAuthInfo.toString()
903 );
904 }
905 } catch (NoSuchAlgorithmException e) {
906 logger.trace(Level.FINER, "Unable to find digest algorithm", e);
907 } catch (IllegalArgumentException e) {
908 logger.trace(Level.FINER, "HTTP 401 response did not contain WWW-Authenticate information", e);
909 } catch (HttpParseException e) {
910 logger.trace(Level.FINER, "HTTP 401 response did not contain valid WWW-Authenticate information", e);
911 }
912
913 if (!authFailed) {
914 authFailed = true;
915 logger.trace(Level.FINER, "Authorization failed, retrying with authorization info.");
916 }
917 if (this.iPrevAuthInfo != null && this.iPrevAuthInfo.isKeptAlive()) {
918 this.iKeepAlive = true;
919 }
920 break;
921 case HttpURLConnection.HTTP_PROXY_AUTH:
922 --AuthentificationRetry;
923 logger.message(Messages.HTTP_PROXY_AUTH_UNSUPPORTED, this.iUrl);
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939 break;
940 default:
941 int status = this.iResponse.getStatus();
942 if (!this.iKeepAlive) closeConnection(); else this.iServerInput.close();
943 this.iPreviousResponseTime = ResponseTime;
944 return status;
945 }
946 } catch (SocketTimeoutException e) {
947 throw e;
948 } catch (IOException e) {
949 logger.message(Messages.HTTP_CONNECTION_FAILED, new Object[] { this.iUrl, e.getMessage() });
950 StringBuilder msg = new StringBuilder("Http connection failed ");
951 if (ResponseTime != -1) {
952 msg.append("after");
953 msg.append(ResponseTime - RequestTime);
954 msg.append(" milliseconds");
955 } else {
956 msg.append("before response received");
957 }
958 if (this.iPreviousResponseTime != -1) {
959 msg.append(", ");
960 msg.append(System.currentTimeMillis() - this.iPreviousResponseTime);
961 msg.append(" milliseconds after previous response on same socket");
962 }
963 logger.trace(Level.FINE, msg.toString(), e);
964 delayedException = e;
965 if (this.iSocket != null && !this.iSocket.isClosed()) {
966 try {
967 this.iSocket.close();
968 } catch (IOException e2) {
969 logger.trace(Level.FINER, "Exception caught while closing socket", e2);
970 }
971 }
972 this.iSocket = null;
973 this.iReset = true;
974 this.iResponse = null;
975 --IoRetry;
976 }
977 } while (AuthentificationRetry >= 0 && IoRetry >= 0);
978 }
979
980 if (this.iResponse != null) {
981 logger.trace(Level.FINER, "http response code=" + this.iResponse.getStatus());
982 if (ResponseTime != -1) this.iPreviousResponseTime = ResponseTime;
983 return this.iResponse.getStatus();
984 }
985 throw (IOException) (
986 delayedException != null ? delayedException : new Exception("Unable to get response after maximum retries")
987 );
988 } finally {
989 logger.exit();
990 }
991 }
992
993
994
995
996
997
998 public String getResponseMessage() {
999 if (this.iResponse != null) return this.iResponse.getResponseMessage();
1000 return null;
1001 }
1002
1003 public void handshakeCompleted(HandshakeCompletedEvent event) {
1004 LogAndTraceBroker.getBroker().trace(Level.FINER, "Http handshake completed.");
1005 this.iSession = event.getSession();
1006 }
1007
1008
1009
1010
1011 public void reset() {
1012 this.iRequestHeaders.clear();
1013 this.iResponseHeaders.clear();
1014 this.iResponse = null;
1015 this.iReset = true;
1016 }
1017
1018
1019
1020
1021
1022
1023
1024 public void setRequestMethod(String method) {
1025 this.iRequestMethod = method;
1026 }
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036 public void setRequestProperty(String key, String value) {
1037 this.iRequestHeaders.addField(key, value);
1038 }
1039
1040
1041
1042
1043 public void streamFinished() {
1044 streamFinished(true);
1045 }
1046
1047
1048
1049
1050
1051
1052
1053
1054 public void streamFinished(boolean keep) {
1055 LogAndTraceBroker logger = LogAndTraceBroker.getBroker();
1056 logger.entry();
1057
1058 HostPortPair hpp = new HostPortPair(this.iUrl);
1059 if (keep) {
1060 logger.trace(Level.FINER, "Adding http client to pool (" + hpp + ")");
1061 this.iHttpClientPool.returnAvailableConnectionToPool(this);
1062 } else {
1063 logger.trace(Level.FINER, "Disconnectiong http client (" + hpp + ")");
1064 this.iHttpClientPool.removeConnectionFromPool(this);
1065 disconnect();
1066 }
1067 logger.exit();
1068 }
1069
1070
1071
1072
1073
1074
1075
1076 public void useHttp11(boolean bool) {
1077 this.iUseHttp11 = bool;
1078 }
1079
1080
1081
1082
1083
1084
1085 public boolean usingProxy() {
1086
1087 return false;
1088 }
1089
1090 protected AuthorizationInfo getAuthentication(boolean proxy, AuthorizationInfo prevAuthInfo, String authenticate)
1091 throws HttpParseException, NoSuchAlgorithmException {
1092 Challenge[] challenges = Challenge.parseChallenge(authenticate);
1093
1094
1095
1096 int cntr = 0;
1097 prevAuthInfo = null;
1098 while (cntr < challenges.length) {
1099 Challenge challenge = challenges[cntr];
1100 cntr++;
1101
1102
1103
1104
1105 prevAuthInfo =
1106 this.iAuth_handler.getAuthorizationInfo(
1107 this.iHttpClientPool.getConfigurationContext().getHttpAuthenticationModule(),
1108 proxy ? Boolean.TRUE : Boolean.FALSE,
1109 this.iUrl.getHost(),
1110 this.iUrl.getPort(),
1111 this.iUrl.getScheme(),
1112 challenge.getRealm(),
1113 challenge.getScheme()
1114 );
1115
1116 if (prevAuthInfo != null) {
1117 prevAuthInfo.updateAuthenticationInfo(challenge, authenticate, this.iUrl, this.iRequestMethod);
1118 return prevAuthInfo;
1119 }
1120 }
1121 return null;
1122 }
1123
1124 private void closeConnection() {
1125 LogAndTraceBroker logger = LogAndTraceBroker.getBroker();
1126 logger.entry();
1127 if (this.iSocket != null) {
1128 try {
1129 this.iSocket.close();
1130 } catch (IOException e) {
1131 logger.trace(Level.FINER, "Exception while closing the socket", e);
1132 }
1133 this.iSocket = null;
1134 this.iServerInput = null;
1135
1136 }
1137 logger.exit();
1138 }
1139
1140 private String[] parseProperty(String propertyName) {
1141 String s = (String) AccessController.doPrivileged(new GetProperty(propertyName));
1142 String as[];
1143 if (s == null || s.length() == 0) {
1144 as = null;
1145 } else {
1146 Vector<Object> vector = new Vector<Object>();
1147 for (
1148 StringTokenizer stringtokenizer = new StringTokenizer(s, ",");
1149 stringtokenizer.hasMoreElements();
1150 vector.addElement(stringtokenizer.nextElement())
1151 ) {
1152
1153 }
1154 as = new String[vector.size()];
1155 for (int i1 = 0; i1 < as.length; i1++) as[i1] = (String) vector.elementAt(i1);
1156 }
1157 return as;
1158 }
1159
1160 private void resetSocket() throws IOException {
1161 LogAndTraceBroker logger = LogAndTraceBroker.getBroker();
1162 logger.entry();
1163 if (!this.iKeepAlive) {
1164 logger.trace(Level.FINER, "KeepAlive=false, closing http connection...");
1165 closeConnection();
1166 }
1167
1168 int httpTimeout = this.iHttpClientPool.getConfigurationContext().getHttpTimeout();
1169 logger.trace(Level.FINER, "Setting http timeout=" + httpTimeout);
1170
1171 if (this.iSocket == null) {
1172
1173 boolean socketConnectWithTimeout = this.iHttpClientPool.getConfigurationContext().socketConnectWithTimeout();
1174 logger.trace(
1175 Level.FINER,
1176 "Socket=null, creating http socket " + (socketConnectWithTimeout ? "with" : "without") + " timeout."
1177 );
1178
1179
1180
1181 if (!socketConnectWithTimeout) {
1182 SecurityManager sm = System.getSecurityManager();
1183 if (sm != null) {
1184 sm.checkConnect(this.iUrl.getHost(), this.iUrl.getPort());
1185 }
1186 }
1187
1188 SocketFactory factory = this.iHttpClientPool.getConfigurationContext().getCustomSocketFactory();
1189 if (factory == null) {
1190 factory =
1191 HttpSocketFactory
1192 .getInstance()
1193 .getClientSocketFactory(
1194 WBEMConstants.HTTPS.equalsIgnoreCase(this.iUrl.getScheme()) ? this.iHttpClientPool.getSslContext() : null
1195 );
1196 if (factory == null) {
1197 logger.message(Messages.HTTP_NO_SOCKET_FACTORY, this.iUrl.getScheme());
1198 throw new IllegalStateException("Unable to load socket factory:" + this.iUrl.getScheme());
1199 }
1200 }
1201 logger.trace(Level.FINER, "Creating new http for url " + this.iUrl.toString());
1202 if (socketConnectWithTimeout) {
1203 int connectTimeout = this.iHttpClientPool.getConfigurationContext().getSocketConnectTimeout();
1204 logger.trace(Level.FINER, "Setting socket connect timeout=" + connectTimeout);
1205
1206 if (factory instanceof SSLSocketFactory) {
1207 Socket sock = new Socket();
1208 sock.connect(new InetSocketAddress(this.iUrl.getHost(), this.iUrl.getPort()), connectTimeout);
1209 this.iSocket =
1210 ((SSLSocketFactory) factory).createSocket(sock, this.iUrl.getHost(), this.iUrl.getPort(), true);
1211 } else {
1212 this.iSocket = factory.createSocket();
1213 if (this.iSocket != null) this.iSocket.connect(
1214 new InetSocketAddress(this.iUrl.getHost(), this.iUrl.getPort()),
1215 connectTimeout
1216 );
1217 }
1218 } else {
1219 this.iSocket = factory.createSocket(this.iUrl.getHost(), this.iUrl.getPort());
1220 }
1221 if (this.iSocket == null) {
1222 logger.trace(Level.WARNING, "Socket factory " + factory.getClass().getName() + " returned null socket");
1223 throw new IOException("Socket factory did not create socket");
1224 }
1225
1226 this.iPreviousResponseTime = -1;
1227 this.iSocket.setTcpNoDelay(true);
1228 this.iSocket.setKeepAlive(true);
1229 this.iSocket.setSoTimeout(httpTimeout);
1230
1231 if (this.iSocket instanceof SSLSocket) {
1232
1233 boolean performHandshake = this.iHttpClientPool.getConfigurationContext().performSslHandshake();
1234 logger.trace(
1235 Level.FINER,
1236 "SSL socket created, handshake " + (performHandshake ? "will" : "will not") + " be performed."
1237 );
1238
1239 if (performHandshake) {
1240 SSLSocket sk = (SSLSocket) this.iSocket;
1241
1242 String protocols[] = parseProperty("https.protocols");
1243 if (protocols != null) {
1244 logger.trace(
1245 Level.FINER,
1246 "Setting SSLSocket.setEnabledProtocols() from \"https.protocols\"=" +
1247 String.valueOf(Arrays.asList(protocols))
1248 );
1249 sk.setEnabledProtocols(protocols);
1250 }
1251
1252 String ciphersuites[] = parseProperty("https.cipherSuites");
1253 if (ciphersuites != null) {
1254 logger.trace(
1255 Level.FINER,
1256 "Setting SSLSocket.setEnableCipheSuites() from \"https.cipherSuites\"=" +
1257 String.valueOf(Arrays.asList(ciphersuites))
1258 );
1259 sk.setEnabledCipherSuites(ciphersuites);
1260 }
1261
1262 String disableCipherSuites =
1263 this.iHttpClientPool.getConfigurationContext().getSslClientCipherSuitesToDisable();
1264 if (disableCipherSuites != null) {
1265 sk.setEnabledCipherSuites(
1266 this.iHttpClientPool.getUpdatedCipherSuites(sk.getEnabledCipherSuites(), disableCipherSuites)
1267 );
1268 }
1269
1270
1271
1272 boolean synchronizedHandshake = this.iHttpClientPool.getConfigurationContext().synchronizedSslHandshake();
1273 logger.trace(
1274 Level.FINER,
1275 "Starting " + (synchronizedHandshake ? "synchronized" : "unsynchronized") + " http handshake."
1276 );
1277
1278 sk.addHandshakeCompletedListener(this);
1279 if (synchronizedHandshake) {
1280 synchronized (SSLSocket.class) {
1281 sk.startHandshake();
1282 }
1283 } else {
1284 sk.startHandshake();
1285 }
1286 }
1287 }
1288
1289 this.iIStream = new BufferedInputStream(this.iSocket.getInputStream());
1290 this.iOStream =
1291 new ASCIIPrintStream(new BufferedOutputStream(this.iSocket.getOutputStream(), 1024), false, iEncoding);
1292 this.iServerInput = null;
1293 } else {
1294 if (this.iServerInput != null && !(this.iServerInput instanceof KeepAliveInputStream)) {
1295 logger.trace(Level.FINER, "Socket!=null, flushing the stream...");
1296 long totalBytes = 0;
1297 long total;
1298 while ((total = this.iServerInput.available()) > 0) {
1299 total = this.iServerInput.skip(total);
1300 if (total >= 0) totalBytes += total;
1301 }
1302 logger.trace(Level.FINER, "total bytes on the stream=" + totalBytes);
1303 }
1304 this.iSocket.setSoTimeout(httpTimeout);
1305 }
1306 }
1307
1308
1309
1310
1311
1312
1313 public boolean isConnected() {
1314 return this.iConnected;
1315 }
1316
1317 @Override
1318 public String toString() {
1319 StringBuffer result = new StringBuffer();
1320 result.append('[');
1321 result.append("URI=");
1322 result.append(this.iUrl);
1323 result.append(", ");
1324 result.append(this.iConnected ? "connected" : "not connected");
1325 result.append(", ");
1326 result.append(this.iKeepAlive ? "kept alive" : "not kept alive");
1327 result.append(", ");
1328 result.append(this.iUseHttp11 ? "HTTP 1.1" : "HTTP 1.0");
1329 result.append(", ");
1330 result.append("Method=");
1331 result.append(this.iMethod);
1332 result.append(", ");
1333 result.append("Request Method=");
1334 result.append(this.iRequestMethod);
1335 result.append(", ");
1336 result.append("Protocol=");
1337 result.append(this.iSession != null ? this.iSession.getProtocol() : "http");
1338 result.append(", ");
1339 result.append("CipherSuite=");
1340 result.append(this.iSession != null ? this.iSession.getCipherSuite() : "n/a");
1341 result.append(']');
1342 return result.toString();
1343 }
1344 }