Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/openjdk-multiarch-jdk8u
Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/sun/net/www/http/HttpClient.java
38923 views
1
/*
2
* Copyright (c) 1994, 2017, Oracle and/or its affiliates. All rights reserved.
3
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4
*
5
* This code is free software; you can redistribute it and/or modify it
6
* under the terms of the GNU General Public License version 2 only, as
7
* published by the Free Software Foundation. Oracle designates this
8
* particular file as subject to the "Classpath" exception as provided
9
* by Oracle in the LICENSE file that accompanied this code.
10
*
11
* This code is distributed in the hope that it will be useful, but WITHOUT
12
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14
* version 2 for more details (a copy is included in the LICENSE file that
15
* accompanied this code).
16
*
17
* You should have received a copy of the GNU General Public License version
18
* 2 along with this work; if not, write to the Free Software Foundation,
19
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20
*
21
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22
* or visit www.oracle.com if you need additional information or have any
23
* questions.
24
*/
25
26
package sun.net.www.http;
27
28
import java.io.*;
29
import java.net.*;
30
import java.util.Locale;
31
import sun.net.NetworkClient;
32
import sun.net.ProgressSource;
33
import sun.net.www.MessageHeader;
34
import sun.net.www.HeaderParser;
35
import sun.net.www.MeteredStream;
36
import sun.net.www.ParseUtil;
37
import sun.net.www.protocol.http.HttpURLConnection;
38
import sun.util.logging.PlatformLogger;
39
import static sun.net.www.protocol.http.HttpURLConnection.TunnelState.*;
40
41
/**
42
* @author Herb Jellinek
43
* @author Dave Brown
44
*/
45
public class HttpClient extends NetworkClient {
46
// whether this httpclient comes from the cache
47
protected boolean cachedHttpClient = false;
48
49
protected boolean inCache;
50
51
// Http requests we send
52
MessageHeader requests;
53
54
// Http data we send with the headers
55
PosterOutputStream poster = null;
56
57
// true if we are in streaming mode (fixed length or chunked)
58
boolean streaming;
59
60
// if we've had one io error
61
boolean failedOnce = false;
62
63
/** Response code for CONTINUE */
64
private boolean ignoreContinue = true;
65
private static final int HTTP_CONTINUE = 100;
66
67
/** Default port number for http daemons. REMIND: make these private */
68
static final int httpPortNumber = 80;
69
70
/** return default port number (subclasses may override) */
71
protected int getDefaultPort () { return httpPortNumber; }
72
73
static private int getDefaultPort(String proto) {
74
if ("http".equalsIgnoreCase(proto))
75
return 80;
76
if ("https".equalsIgnoreCase(proto))
77
return 443;
78
return -1;
79
}
80
81
/* All proxying (generic as well as instance-specific) may be
82
* disabled through use of this flag
83
*/
84
protected boolean proxyDisabled;
85
86
// are we using proxy in this instance?
87
public boolean usingProxy = false;
88
// target host, port for the URL
89
protected String host;
90
protected int port;
91
92
/* where we cache currently open, persistent connections */
93
protected static KeepAliveCache kac = new KeepAliveCache();
94
95
private static boolean keepAliveProp = true;
96
97
// retryPostProp is true by default so as to preserve behavior
98
// from previous releases.
99
private static boolean retryPostProp = true;
100
101
/* Value of the system property jdk.ntlm.cache;
102
if false, then NTLM connections will not be cached.
103
The default value is 'true'. */
104
private static final boolean cacheNTLMProp;
105
/* Value of the system property jdk.spnego.cache;
106
if false, then connections authentified using the Negotiate/Kerberos
107
scheme will not be cached.
108
The default value is 'true'. */
109
private static final boolean cacheSPNEGOProp;
110
111
volatile boolean keepingAlive = false; /* this is a keep-alive connection */
112
volatile boolean disableKeepAlive;/* keep-alive has been disabled for this
113
connection - this will be used when
114
recomputing the value of keepingAlive */
115
int keepAliveConnections = -1; /* number of keep-alives left */
116
117
/**Idle timeout value, in milliseconds. Zero means infinity,
118
* iff keepingAlive=true.
119
* Unfortunately, we can't always believe this one. If I'm connected
120
* through a Netscape proxy to a server that sent me a keep-alive
121
* time of 15 sec, the proxy unilaterally terminates my connection
122
* after 5 sec. So we have to hard code our effective timeout to
123
* 4 sec for the case where we're using a proxy. *SIGH*
124
*/
125
int keepAliveTimeout = 0;
126
127
/** whether the response is to be cached */
128
private CacheRequest cacheRequest = null;
129
130
/** Url being fetched. */
131
protected URL url;
132
133
/* if set, the client will be reused and must not be put in cache */
134
public boolean reuse = false;
135
136
// Traffic capture tool, if configured. See HttpCapture class for info
137
private HttpCapture capture = null;
138
139
private static final PlatformLogger logger = HttpURLConnection.getHttpLogger();
140
private static void logFinest(String msg) {
141
if (logger.isLoggable(PlatformLogger.Level.FINEST)) {
142
logger.finest(msg);
143
}
144
}
145
146
/**
147
* A NOP method kept for backwards binary compatibility
148
* @deprecated -- system properties are no longer cached.
149
*/
150
@Deprecated
151
public static synchronized void resetProperties() {
152
}
153
154
int getKeepAliveTimeout() {
155
return keepAliveTimeout;
156
}
157
158
static {
159
String keepAlive = java.security.AccessController.doPrivileged(
160
new sun.security.action.GetPropertyAction("http.keepAlive"));
161
162
String retryPost = java.security.AccessController.doPrivileged(
163
new sun.security.action.GetPropertyAction("sun.net.http.retryPost"));
164
165
String cacheNTLM = java.security.AccessController.doPrivileged(
166
new sun.security.action.GetPropertyAction("jdk.ntlm.cache"));
167
168
String cacheSPNEGO = java.security.AccessController.doPrivileged(
169
new sun.security.action.GetPropertyAction("jdk.spnego.cache"));
170
171
if (keepAlive != null) {
172
keepAliveProp = Boolean.valueOf(keepAlive).booleanValue();
173
} else {
174
keepAliveProp = true;
175
}
176
177
if (retryPost != null) {
178
retryPostProp = Boolean.valueOf(retryPost).booleanValue();
179
} else {
180
retryPostProp = true;
181
}
182
183
if (cacheNTLM != null) {
184
cacheNTLMProp = Boolean.parseBoolean(cacheNTLM);
185
} else {
186
cacheNTLMProp = true;
187
}
188
189
if (cacheSPNEGO != null) {
190
cacheSPNEGOProp = Boolean.parseBoolean(cacheSPNEGO);
191
} else {
192
cacheSPNEGOProp = true;
193
}
194
}
195
196
/**
197
* @return true iff http keep alive is set (i.e. enabled). Defaults
198
* to true if the system property http.keepAlive isn't set.
199
*/
200
public boolean getHttpKeepAliveSet() {
201
return keepAliveProp;
202
}
203
204
205
protected HttpClient() {
206
}
207
208
private HttpClient(URL url)
209
throws IOException {
210
this(url, (String)null, -1, false);
211
}
212
213
protected HttpClient(URL url,
214
boolean proxyDisabled) throws IOException {
215
this(url, null, -1, proxyDisabled);
216
}
217
218
/* This package-only CTOR should only be used for FTP piggy-backed on HTTP
219
* HTTP URL's that use this won't take advantage of keep-alive.
220
* Additionally, this constructor may be used as a last resort when the
221
* first HttpClient gotten through New() failed (probably b/c of a
222
* Keep-Alive mismatch).
223
*
224
* XXX That documentation is wrong ... it's not package-private any more
225
*/
226
public HttpClient(URL url, String proxyHost, int proxyPort)
227
throws IOException {
228
this(url, proxyHost, proxyPort, false);
229
}
230
231
protected HttpClient(URL url, Proxy p, int to) throws IOException {
232
proxy = (p == null) ? Proxy.NO_PROXY : p;
233
this.host = url.getHost();
234
this.url = url;
235
port = url.getPort();
236
if (port == -1) {
237
port = getDefaultPort();
238
}
239
setConnectTimeout(to);
240
241
capture = HttpCapture.getCapture(url);
242
openServer();
243
}
244
245
static protected Proxy newHttpProxy(String proxyHost, int proxyPort,
246
String proto) {
247
if (proxyHost == null || proto == null)
248
return Proxy.NO_PROXY;
249
int pport = proxyPort < 0 ? getDefaultPort(proto) : proxyPort;
250
InetSocketAddress saddr = InetSocketAddress.createUnresolved(proxyHost, pport);
251
return new Proxy(Proxy.Type.HTTP, saddr);
252
}
253
254
/*
255
* This constructor gives "ultimate" flexibility, including the ability
256
* to bypass implicit proxying. Sometimes we need to be using tunneling
257
* (transport or network level) instead of proxying (application level),
258
* for example when we don't want the application level data to become
259
* visible to third parties.
260
*
261
* @param url the URL to which we're connecting
262
* @param proxy proxy to use for this URL (e.g. forwarding)
263
* @param proxyPort proxy port to use for this URL
264
* @param proxyDisabled true to disable default proxying
265
*/
266
private HttpClient(URL url, String proxyHost, int proxyPort,
267
boolean proxyDisabled)
268
throws IOException {
269
this(url, proxyDisabled ? Proxy.NO_PROXY :
270
newHttpProxy(proxyHost, proxyPort, "http"), -1);
271
}
272
273
public HttpClient(URL url, String proxyHost, int proxyPort,
274
boolean proxyDisabled, int to)
275
throws IOException {
276
this(url, proxyDisabled ? Proxy.NO_PROXY :
277
newHttpProxy(proxyHost, proxyPort, "http"), to);
278
}
279
280
/* This class has no public constructor for HTTP. This method is used to
281
* get an HttpClient to the specified URL. If there's currently an
282
* active HttpClient to that server/port, you'll get that one.
283
*/
284
public static HttpClient New(URL url)
285
throws IOException {
286
return HttpClient.New(url, Proxy.NO_PROXY, -1, true, null);
287
}
288
289
public static HttpClient New(URL url, boolean useCache)
290
throws IOException {
291
return HttpClient.New(url, Proxy.NO_PROXY, -1, useCache, null);
292
}
293
294
public static HttpClient New(URL url, Proxy p, int to, boolean useCache,
295
HttpURLConnection httpuc) throws IOException
296
{
297
if (p == null) {
298
p = Proxy.NO_PROXY;
299
}
300
HttpClient ret = null;
301
/* see if one's already around */
302
if (useCache) {
303
ret = kac.get(url, null);
304
if (ret != null && httpuc != null &&
305
httpuc.streaming() &&
306
httpuc.getRequestMethod() == "POST") {
307
if (!ret.available()) {
308
ret.inCache = false;
309
ret.closeServer();
310
ret = null;
311
}
312
}
313
314
if (ret != null) {
315
if ((ret.proxy != null && ret.proxy.equals(p)) ||
316
(ret.proxy == null && p == null)) {
317
synchronized (ret) {
318
ret.cachedHttpClient = true;
319
assert ret.inCache;
320
ret.inCache = false;
321
if (httpuc != null && ret.needsTunneling())
322
httpuc.setTunnelState(TUNNELING);
323
logFinest("KeepAlive stream retrieved from the cache, " + ret);
324
}
325
} else {
326
// We cannot return this connection to the cache as it's
327
// KeepAliveTimeout will get reset. We simply close the connection.
328
// This should be fine as it is very rare that a connection
329
// to the same host will not use the same proxy.
330
synchronized(ret) {
331
ret.inCache = false;
332
ret.closeServer();
333
}
334
ret = null;
335
}
336
}
337
}
338
if (ret == null) {
339
ret = new HttpClient(url, p, to);
340
} else {
341
SecurityManager security = System.getSecurityManager();
342
if (security != null) {
343
if (ret.proxy == Proxy.NO_PROXY || ret.proxy == null) {
344
security.checkConnect(InetAddress.getByName(url.getHost()).getHostAddress(), url.getPort());
345
} else {
346
security.checkConnect(url.getHost(), url.getPort());
347
}
348
}
349
ret.url = url;
350
}
351
return ret;
352
}
353
354
public static HttpClient New(URL url, Proxy p, int to,
355
HttpURLConnection httpuc) throws IOException
356
{
357
return New(url, p, to, true, httpuc);
358
}
359
360
public static HttpClient New(URL url, String proxyHost, int proxyPort,
361
boolean useCache)
362
throws IOException {
363
return New(url, newHttpProxy(proxyHost, proxyPort, "http"),
364
-1, useCache, null);
365
}
366
367
public static HttpClient New(URL url, String proxyHost, int proxyPort,
368
boolean useCache, int to,
369
HttpURLConnection httpuc)
370
throws IOException {
371
return New(url, newHttpProxy(proxyHost, proxyPort, "http"),
372
to, useCache, httpuc);
373
}
374
375
/* return it to the cache as still usable, if:
376
* 1) It's keeping alive, AND
377
* 2) It still has some connections left, AND
378
* 3) It hasn't had a error (PrintStream.checkError())
379
* 4) It hasn't timed out
380
*
381
* If this client is not keepingAlive, it should have been
382
* removed from the cache in the parseHeaders() method.
383
*/
384
385
public void finished() {
386
if (reuse) /* will be reused */
387
return;
388
keepAliveConnections--;
389
poster = null;
390
if (keepAliveConnections > 0 && isKeepingAlive() &&
391
!(serverOutput.checkError())) {
392
/* This connection is keepingAlive && still valid.
393
* Return it to the cache.
394
*/
395
putInKeepAliveCache();
396
} else {
397
closeServer();
398
}
399
}
400
401
protected synchronized boolean available() {
402
boolean available = true;
403
int old = -1;
404
405
try {
406
try {
407
old = serverSocket.getSoTimeout();
408
serverSocket.setSoTimeout(1);
409
BufferedInputStream tmpbuf =
410
new BufferedInputStream(serverSocket.getInputStream());
411
int r = tmpbuf.read();
412
if (r == -1) {
413
logFinest("HttpClient.available(): " +
414
"read returned -1: not available");
415
available = false;
416
}
417
} catch (SocketTimeoutException e) {
418
logFinest("HttpClient.available(): " +
419
"SocketTimeout: its available");
420
} finally {
421
if (old != -1)
422
serverSocket.setSoTimeout(old);
423
}
424
} catch (IOException e) {
425
logFinest("HttpClient.available(): " +
426
"SocketException: not available");
427
available = false;
428
}
429
return available;
430
}
431
432
protected synchronized void putInKeepAliveCache() {
433
if (inCache) {
434
assert false : "Duplicate put to keep alive cache";
435
return;
436
}
437
inCache = true;
438
kac.put(url, null, this);
439
}
440
441
protected synchronized boolean isInKeepAliveCache() {
442
return inCache;
443
}
444
445
/*
446
* Close an idle connection to this URL (if it exists in the
447
* cache).
448
*/
449
public void closeIdleConnection() {
450
HttpClient http = kac.get(url, null);
451
if (http != null) {
452
http.closeServer();
453
}
454
}
455
456
/* We're very particular here about what our InputStream to the server
457
* looks like for reasons that are apparent if you can decipher the
458
* method parseHTTP(). That's why this method is overidden from the
459
* superclass.
460
*/
461
@Override
462
public void openServer(String server, int port) throws IOException {
463
serverSocket = doConnect(server, port);
464
try {
465
OutputStream out = serverSocket.getOutputStream();
466
if (capture != null) {
467
out = new HttpCaptureOutputStream(out, capture);
468
}
469
serverOutput = new PrintStream(
470
new BufferedOutputStream(out),
471
false, encoding);
472
} catch (UnsupportedEncodingException e) {
473
throw new InternalError(encoding+" encoding not found", e);
474
}
475
serverSocket.setTcpNoDelay(true);
476
}
477
478
/*
479
* Returns true if the http request should be tunneled through proxy.
480
* An example where this is the case is Https.
481
*/
482
public boolean needsTunneling() {
483
return false;
484
}
485
486
/*
487
* Returns true if this httpclient is from cache
488
*/
489
public synchronized boolean isCachedConnection() {
490
return cachedHttpClient;
491
}
492
493
/*
494
* Finish any work left after the socket connection is
495
* established. In the normal http case, it's a NO-OP. Subclass
496
* may need to override this. An example is Https, where for
497
* direct connection to the origin server, ssl handshake needs to
498
* be done; for proxy tunneling, the socket needs to be converted
499
* into an SSL socket before ssl handshake can take place.
500
*/
501
public void afterConnect() throws IOException, UnknownHostException {
502
// NO-OP. Needs to be overwritten by HttpsClient
503
}
504
505
/*
506
* call openServer in a privileged block
507
*/
508
private synchronized void privilegedOpenServer(final InetSocketAddress server)
509
throws IOException
510
{
511
try {
512
java.security.AccessController.doPrivileged(
513
new java.security.PrivilegedExceptionAction<Void>() {
514
public Void run() throws IOException {
515
openServer(server.getHostString(), server.getPort());
516
return null;
517
}
518
});
519
} catch (java.security.PrivilegedActionException pae) {
520
throw (IOException) pae.getException();
521
}
522
}
523
524
/*
525
* call super.openServer
526
*/
527
private void superOpenServer(final String proxyHost,
528
final int proxyPort)
529
throws IOException, UnknownHostException
530
{
531
super.openServer(proxyHost, proxyPort);
532
}
533
534
/*
535
*/
536
protected synchronized void openServer() throws IOException {
537
538
SecurityManager security = System.getSecurityManager();
539
540
if (security != null) {
541
security.checkConnect(host, port);
542
}
543
544
if (keepingAlive) { // already opened
545
return;
546
}
547
548
if (url.getProtocol().equals("http") ||
549
url.getProtocol().equals("https") ) {
550
551
if ((proxy != null) && (proxy.type() == Proxy.Type.HTTP)) {
552
sun.net.www.URLConnection.setProxiedHost(host);
553
privilegedOpenServer((InetSocketAddress) proxy.address());
554
usingProxy = true;
555
return;
556
} else {
557
// make direct connection
558
openServer(host, port);
559
usingProxy = false;
560
return;
561
}
562
563
} else {
564
/* we're opening some other kind of url, most likely an
565
* ftp url.
566
*/
567
if ((proxy != null) && (proxy.type() == Proxy.Type.HTTP)) {
568
sun.net.www.URLConnection.setProxiedHost(host);
569
privilegedOpenServer((InetSocketAddress) proxy.address());
570
usingProxy = true;
571
return;
572
} else {
573
// make direct connection
574
super.openServer(host, port);
575
usingProxy = false;
576
return;
577
}
578
}
579
}
580
581
public String getURLFile() throws IOException {
582
583
String fileName;
584
585
/**
586
* proxyDisabled is set by subclass HttpsClient!
587
*/
588
if (usingProxy && !proxyDisabled) {
589
// Do not use URLStreamHandler.toExternalForm as the fragment
590
// should not be part of the RequestURI. It should be an
591
// absolute URI which does not have a fragment part.
592
StringBuffer result = new StringBuffer(128);
593
result.append(url.getProtocol());
594
result.append(":");
595
if (url.getAuthority() != null && url.getAuthority().length() > 0) {
596
result.append("//");
597
result.append(url.getAuthority());
598
}
599
if (url.getPath() != null) {
600
result.append(url.getPath());
601
}
602
if (url.getQuery() != null) {
603
result.append('?');
604
result.append(url.getQuery());
605
}
606
607
fileName = result.toString();
608
} else {
609
fileName = url.getFile();
610
611
if ((fileName == null) || (fileName.length() == 0)) {
612
fileName = "/";
613
} else if (fileName.charAt(0) == '?') {
614
/* HTTP/1.1 spec says in 5.1.2. about Request-URI:
615
* "Note that the absolute path cannot be empty; if
616
* none is present in the original URI, it MUST be
617
* given as "/" (the server root)." So if the file
618
* name here has only a query string, the path is
619
* empty and we also have to add a "/".
620
*/
621
fileName = "/" + fileName;
622
}
623
}
624
625
if (fileName.indexOf('\n') == -1)
626
return fileName;
627
else
628
throw new java.net.MalformedURLException("Illegal character in URL");
629
}
630
631
/**
632
* @deprecated
633
*/
634
@Deprecated
635
public void writeRequests(MessageHeader head) {
636
requests = head;
637
requests.print(serverOutput);
638
serverOutput.flush();
639
}
640
641
public void writeRequests(MessageHeader head,
642
PosterOutputStream pos) throws IOException {
643
requests = head;
644
requests.print(serverOutput);
645
poster = pos;
646
if (poster != null)
647
poster.writeTo(serverOutput);
648
serverOutput.flush();
649
}
650
651
public void writeRequests(MessageHeader head,
652
PosterOutputStream pos,
653
boolean streaming) throws IOException {
654
this.streaming = streaming;
655
writeRequests(head, pos);
656
}
657
658
/** Parse the first line of the HTTP request. It usually looks
659
something like: "HTTP/1.0 <number> comment\r\n". */
660
661
public boolean parseHTTP(MessageHeader responses, ProgressSource pi, HttpURLConnection httpuc)
662
throws IOException {
663
/* If "HTTP/*" is found in the beginning, return true. Let
664
* HttpURLConnection parse the mime header itself.
665
*
666
* If this isn't valid HTTP, then we don't try to parse a header
667
* out of the beginning of the response into the responses,
668
* and instead just queue up the output stream to it's very beginning.
669
* This seems most reasonable, and is what the NN browser does.
670
*/
671
672
try {
673
serverInput = serverSocket.getInputStream();
674
if (capture != null) {
675
serverInput = new HttpCaptureInputStream(serverInput, capture);
676
}
677
serverInput = new BufferedInputStream(serverInput);
678
return (parseHTTPHeader(responses, pi, httpuc));
679
} catch (SocketTimeoutException stex) {
680
// We don't want to retry the request when the app. sets a timeout
681
// but don't close the server if timeout while waiting for 100-continue
682
if (ignoreContinue) {
683
closeServer();
684
}
685
throw stex;
686
} catch (IOException e) {
687
closeServer();
688
cachedHttpClient = false;
689
if (!failedOnce && requests != null) {
690
failedOnce = true;
691
if (getRequestMethod().equals("CONNECT")
692
|| streaming
693
|| (httpuc.getRequestMethod().equals("POST")
694
&& !retryPostProp)) {
695
// do not retry the request
696
} else {
697
// try once more
698
openServer();
699
if (needsTunneling()) {
700
MessageHeader origRequests = requests;
701
httpuc.doTunneling();
702
requests = origRequests;
703
}
704
afterConnect();
705
writeRequests(requests, poster);
706
return parseHTTP(responses, pi, httpuc);
707
}
708
}
709
throw e;
710
}
711
712
}
713
714
private boolean parseHTTPHeader(MessageHeader responses, ProgressSource pi, HttpURLConnection httpuc)
715
throws IOException {
716
/* If "HTTP/*" is found in the beginning, return true. Let
717
* HttpURLConnection parse the mime header itself.
718
*
719
* If this isn't valid HTTP, then we don't try to parse a header
720
* out of the beginning of the response into the responses,
721
* and instead just queue up the output stream to it's very beginning.
722
* This seems most reasonable, and is what the NN browser does.
723
*/
724
725
keepAliveConnections = -1;
726
keepAliveTimeout = 0;
727
728
boolean ret = false;
729
byte[] b = new byte[8];
730
731
try {
732
int nread = 0;
733
serverInput.mark(10);
734
while (nread < 8) {
735
int r = serverInput.read(b, nread, 8 - nread);
736
if (r < 0) {
737
break;
738
}
739
nread += r;
740
}
741
String keep=null;
742
String authenticate=null;
743
ret = b[0] == 'H' && b[1] == 'T'
744
&& b[2] == 'T' && b[3] == 'P' && b[4] == '/' &&
745
b[5] == '1' && b[6] == '.';
746
serverInput.reset();
747
if (ret) { // is valid HTTP - response started w/ "HTTP/1."
748
responses.parseHeader(serverInput);
749
750
// we've finished parsing http headers
751
// check if there are any applicable cookies to set (in cache)
752
CookieHandler cookieHandler = httpuc.getCookieHandler();
753
if (cookieHandler != null) {
754
URI uri = ParseUtil.toURI(url);
755
// NOTE: That cast from Map shouldn't be necessary but
756
// a bug in javac is triggered under certain circumstances
757
// So we do put the cast in as a workaround until
758
// it is resolved.
759
if (uri != null)
760
cookieHandler.put(uri, responses.getHeaders());
761
}
762
763
/* decide if we're keeping alive:
764
* This is a bit tricky. There's a spec, but most current
765
* servers (10/1/96) that support this differ in dialects.
766
* If the server/client misunderstand each other, the
767
* protocol should fall back onto HTTP/1.0, no keep-alive.
768
*/
769
if (usingProxy) { // not likely a proxy will return this
770
keep = responses.findValue("Proxy-Connection");
771
authenticate = responses.findValue("Proxy-Authenticate");
772
}
773
if (keep == null) {
774
keep = responses.findValue("Connection");
775
authenticate = responses.findValue("WWW-Authenticate");
776
}
777
778
// 'disableKeepAlive' starts with the value false.
779
// It can transition from false to true, but once true
780
// it stays true.
781
// If cacheNTLMProp is false, and disableKeepAlive is false,
782
// then we need to examine the response headers to figure out
783
// whether we are doing NTLM authentication. If we do NTLM,
784
// and cacheNTLMProp is false, than we can't keep this connection
785
// alive: we will switch disableKeepAlive to true.
786
boolean canKeepAlive = !disableKeepAlive;
787
if (canKeepAlive && (cacheNTLMProp == false || cacheSPNEGOProp == false)
788
&& authenticate != null) {
789
authenticate = authenticate.toLowerCase(Locale.US);
790
if (cacheNTLMProp == false) {
791
canKeepAlive &= !authenticate.startsWith("ntlm ");
792
}
793
if (cacheSPNEGOProp == false) {
794
canKeepAlive &= !authenticate.startsWith("negotiate ");
795
canKeepAlive &= !authenticate.startsWith("kerberos ");
796
}
797
}
798
disableKeepAlive |= !canKeepAlive;
799
800
if (keep != null && keep.toLowerCase(Locale.US).equals("keep-alive")) {
801
/* some servers, notably Apache1.1, send something like:
802
* "Keep-Alive: timeout=15, max=1" which we should respect.
803
*/
804
if (disableKeepAlive) {
805
keepAliveConnections = 1;
806
} else {
807
HeaderParser p = new HeaderParser(
808
responses.findValue("Keep-Alive"));
809
/* default should be larger in case of proxy */
810
keepAliveConnections = p.findInt("max", usingProxy?50:5);
811
keepAliveTimeout = p.findInt("timeout", usingProxy?60:5);
812
}
813
} else if (b[7] != '0') {
814
/*
815
* We're talking 1.1 or later. Keep persistent until
816
* the server says to close.
817
*/
818
if (keep != null || disableKeepAlive) {
819
/*
820
* The only Connection token we understand is close.
821
* Paranoia: if there is any Connection header then
822
* treat as non-persistent.
823
*/
824
keepAliveConnections = 1;
825
} else {
826
keepAliveConnections = 5;
827
}
828
}
829
} else if (nread != 8) {
830
if (!failedOnce && requests != null) {
831
failedOnce = true;
832
if (getRequestMethod().equals("CONNECT")
833
|| streaming
834
|| (httpuc.getRequestMethod().equals("POST")
835
&& !retryPostProp)) {
836
// do not retry the request
837
} else {
838
closeServer();
839
cachedHttpClient = false;
840
openServer();
841
if (needsTunneling()) {
842
MessageHeader origRequests = requests;
843
httpuc.doTunneling();
844
requests = origRequests;
845
}
846
afterConnect();
847
writeRequests(requests, poster);
848
return parseHTTP(responses, pi, httpuc);
849
}
850
}
851
throw new SocketException("Unexpected end of file from server");
852
} else {
853
// we can't vouche for what this is....
854
responses.set("Content-type", "unknown/unknown");
855
}
856
} catch (IOException e) {
857
throw e;
858
}
859
860
int code = -1;
861
try {
862
String resp;
863
resp = responses.getValue(0);
864
/* should have no leading/trailing LWS
865
* expedite the typical case by assuming it has
866
* form "HTTP/1.x <WS> 2XX <mumble>"
867
*/
868
int ind;
869
ind = resp.indexOf(' ');
870
while(resp.charAt(ind) == ' ')
871
ind++;
872
code = Integer.parseInt(resp.substring(ind, ind + 3));
873
} catch (Exception e) {}
874
875
if (code == HTTP_CONTINUE && ignoreContinue) {
876
responses.reset();
877
return parseHTTPHeader(responses, pi, httpuc);
878
}
879
880
long cl = -1;
881
882
/*
883
* Set things up to parse the entity body of the reply.
884
* We should be smarter about avoid pointless work when
885
* the HTTP method and response code indicate there will be
886
* no entity body to parse.
887
*/
888
String te = responses.findValue("Transfer-Encoding");
889
if (te != null && te.equalsIgnoreCase("chunked")) {
890
serverInput = new ChunkedInputStream(serverInput, this, responses);
891
892
/*
893
* If keep alive not specified then close after the stream
894
* has completed.
895
*/
896
if (keepAliveConnections <= 1) {
897
keepAliveConnections = 1;
898
keepingAlive = false;
899
} else {
900
keepingAlive = !disableKeepAlive;
901
}
902
failedOnce = false;
903
} else {
904
905
/*
906
* If it's a keep alive connection then we will keep
907
* (alive if :-
908
* 1. content-length is specified, or
909
* 2. "Not-Modified" or "No-Content" responses - RFC 2616 states that
910
* 204 or 304 response must not include a message body.
911
*/
912
String cls = responses.findValue("content-length");
913
if (cls != null) {
914
try {
915
cl = Long.parseLong(cls);
916
} catch (NumberFormatException e) {
917
cl = -1;
918
}
919
}
920
String requestLine = requests.getKey(0);
921
922
if ((requestLine != null &&
923
(requestLine.startsWith("HEAD"))) ||
924
code == HttpURLConnection.HTTP_NOT_MODIFIED ||
925
code == HttpURLConnection.HTTP_NO_CONTENT) {
926
cl = 0;
927
}
928
929
if (keepAliveConnections > 1 &&
930
(cl >= 0 ||
931
code == HttpURLConnection.HTTP_NOT_MODIFIED ||
932
code == HttpURLConnection.HTTP_NO_CONTENT)) {
933
keepingAlive = !disableKeepAlive;
934
failedOnce = false;
935
} else if (keepingAlive) {
936
/* Previously we were keeping alive, and now we're not. Remove
937
* this from the cache (but only here, once) - otherwise we get
938
* multiple removes and the cache count gets messed up.
939
*/
940
keepingAlive=false;
941
}
942
}
943
944
/* wrap a KeepAliveStream/MeteredStream around it if appropriate */
945
946
if (cl > 0) {
947
// In this case, content length is well known, so it is okay
948
// to wrap the input stream with KeepAliveStream/MeteredStream.
949
950
if (pi != null) {
951
// Progress monitor is enabled
952
pi.setContentType(responses.findValue("content-type"));
953
}
954
955
// If disableKeepAlive == true, the client will not be returned
956
// to the cache. But we still need to use a keepalive stream to
957
// allow the multi-message authentication exchange on the connection
958
boolean useKeepAliveStream = isKeepingAlive() || disableKeepAlive;
959
if (useKeepAliveStream) {
960
// Wrap KeepAliveStream if keep alive is enabled.
961
logFinest("KeepAlive stream used: " + url);
962
serverInput = new KeepAliveStream(serverInput, pi, cl, this);
963
failedOnce = false;
964
}
965
else {
966
serverInput = new MeteredStream(serverInput, pi, cl);
967
}
968
}
969
else if (cl == -1) {
970
// In this case, content length is unknown - the input
971
// stream would simply be a regular InputStream or
972
// ChunkedInputStream.
973
974
if (pi != null) {
975
// Progress monitoring is enabled.
976
977
pi.setContentType(responses.findValue("content-type"));
978
979
// Wrap MeteredStream for tracking indeterministic
980
// progress, even if the input stream is ChunkedInputStream.
981
serverInput = new MeteredStream(serverInput, pi, cl);
982
}
983
else {
984
// Progress monitoring is disabled, and there is no
985
// need to wrap an unknown length input stream.
986
987
// ** This is an no-op **
988
}
989
}
990
else {
991
if (pi != null)
992
pi.finishTracking();
993
}
994
995
return ret;
996
}
997
998
public synchronized InputStream getInputStream() {
999
return serverInput;
1000
}
1001
1002
public OutputStream getOutputStream() {
1003
return serverOutput;
1004
}
1005
1006
@Override
1007
public String toString() {
1008
return getClass().getName()+"("+url+")";
1009
}
1010
1011
public final boolean isKeepingAlive() {
1012
return getHttpKeepAliveSet() && keepingAlive;
1013
}
1014
1015
public void setCacheRequest(CacheRequest cacheRequest) {
1016
this.cacheRequest = cacheRequest;
1017
}
1018
1019
CacheRequest getCacheRequest() {
1020
return cacheRequest;
1021
}
1022
1023
String getRequestMethod() {
1024
if (requests != null) {
1025
String requestLine = requests.getKey(0);
1026
if (requestLine != null) {
1027
return requestLine.split("\\s+")[0];
1028
}
1029
}
1030
return "";
1031
}
1032
1033
@Override
1034
protected void finalize() throws Throwable {
1035
// This should do nothing. The stream finalizer will
1036
// close the fd.
1037
}
1038
1039
public void setDoNotRetry(boolean value) {
1040
// failedOnce is used to determine if a request should be retried.
1041
failedOnce = value;
1042
}
1043
1044
public void setIgnoreContinue(boolean value) {
1045
ignoreContinue = value;
1046
}
1047
1048
/* Use only on connections in error. */
1049
@Override
1050
public void closeServer() {
1051
try {
1052
keepingAlive = false;
1053
serverSocket.close();
1054
} catch (Exception e) {}
1055
}
1056
1057
/**
1058
* @return the proxy host being used for this client, or null
1059
* if we're not going through a proxy
1060
*/
1061
public String getProxyHostUsed() {
1062
if (!usingProxy) {
1063
return null;
1064
} else {
1065
return ((InetSocketAddress)proxy.address()).getHostString();
1066
}
1067
}
1068
1069
/**
1070
* @return the proxy port being used for this client. Meaningless
1071
* if getProxyHostUsed() gives null.
1072
*/
1073
public int getProxyPortUsed() {
1074
if (usingProxy)
1075
return ((InetSocketAddress)proxy.address()).getPort();
1076
return -1;
1077
}
1078
}
1079
1080