79 import java.util.TimeZone; |
79 import java.util.TimeZone; |
80 import java.net.MalformedURLException; |
80 import java.net.MalformedURLException; |
81 import java.nio.ByteBuffer; |
81 import java.nio.ByteBuffer; |
82 import java.util.Objects; |
82 import java.util.Objects; |
83 import java.util.Properties; |
83 import java.util.Properties; |
|
84 import java.util.concurrent.locks.ReentrantLock; |
|
85 |
84 import static sun.net.www.protocol.http.AuthScheme.BASIC; |
86 import static sun.net.www.protocol.http.AuthScheme.BASIC; |
85 import static sun.net.www.protocol.http.AuthScheme.DIGEST; |
87 import static sun.net.www.protocol.http.AuthScheme.DIGEST; |
86 import static sun.net.www.protocol.http.AuthScheme.NTLM; |
88 import static sun.net.www.protocol.http.AuthScheme.NTLM; |
87 import static sun.net.www.protocol.http.AuthScheme.NEGOTIATE; |
89 import static sun.net.www.protocol.http.AuthScheme.NEGOTIATE; |
88 import static sun.net.www.protocol.http.AuthScheme.KERBEROS; |
90 import static sun.net.www.protocol.http.AuthScheme.KERBEROS; |
350 /* Headers and request method cannot be changed |
352 /* Headers and request method cannot be changed |
351 * once this flag is set in :- |
353 * once this flag is set in :- |
352 * - getOutputStream() |
354 * - getOutputStream() |
353 * - getInputStream()) |
355 * - getInputStream()) |
354 * - connect() |
356 * - connect() |
355 * Access synchronized on this. |
357 * Access synchronized on connectionLock. |
356 */ |
358 */ |
357 private boolean connecting = false; |
359 private boolean connecting = false; |
358 |
360 |
359 /* The following two fields are only used with Digest Authentication */ |
361 /* The following two fields are only used with Digest Authentication */ |
360 String domain; /* The list of authentication domains */ |
362 String domain; /* The list of authentication domains */ |
381 |
383 |
382 String serverAuthKey, proxyAuthKey; |
384 String serverAuthKey, proxyAuthKey; |
383 |
385 |
384 /* Progress source */ |
386 /* Progress source */ |
385 protected ProgressSource pi; |
387 protected ProgressSource pi; |
|
388 |
|
389 /* Lock */ |
|
390 final ReentrantLock connectionLock = new ReentrantLock(); |
386 |
391 |
387 /* all the response headers we get back */ |
392 /* all the response headers we get back */ |
388 private MessageHeader responses; |
393 private MessageHeader responses; |
389 /* the stream _from_ the server */ |
394 /* the stream _from_ the server */ |
390 private InputStream inputStream = null; |
395 private InputStream inputStream = null; |
511 public void authObj(Object authObj) { |
516 public void authObj(Object authObj) { |
512 this.authObj = authObj; |
517 this.authObj = authObj; |
513 } |
518 } |
514 |
519 |
515 @Override |
520 @Override |
516 public synchronized void setAuthenticator(Authenticator auth) { |
521 public void setAuthenticator(Authenticator auth) { |
517 if (connecting || connected) { |
522 connectionLock.lock(); |
518 throw new IllegalStateException( |
523 try { |
519 "Authenticator must be set before connecting"); |
524 if (connecting || connected) { |
520 } |
525 throw new IllegalStateException( |
521 authenticator = Objects.requireNonNull(auth); |
526 "Authenticator must be set before connecting"); |
522 authenticatorKey = AuthenticatorKeys.getKey(authenticator); |
527 } |
|
528 authenticator = Objects.requireNonNull(auth); |
|
529 authenticatorKey = AuthenticatorKeys.getKey(authenticator); |
|
530 } finally { |
|
531 connectionLock.unlock(); |
|
532 } |
523 } |
533 } |
524 |
534 |
525 public String getAuthenticatorKey() { |
535 public String getAuthenticatorKey() { |
526 String k = authenticatorKey; |
536 String k = authenticatorKey; |
527 if (k == null) return AuthenticatorKeys.getKey(authenticator); |
537 if (k == null) return AuthenticatorKeys.getKey(authenticator); |
680 String.valueOf(fixedContentLength)); |
695 String.valueOf(fixedContentLength)); |
681 } |
696 } |
682 } |
697 } |
683 } else if (poster != null) { |
698 } else if (poster != null) { |
684 /* add Content-Length & POST/PUT data */ |
699 /* add Content-Length & POST/PUT data */ |
|
700 // safe to synchronize on poster: this is |
|
701 // a simple subclass of ByteArrayOutputStream |
685 synchronized (poster) { |
702 synchronized (poster) { |
686 /* close it, so no more data can be added */ |
703 /* close it, so no more data can be added */ |
687 poster.close(); |
704 poster.close(); |
688 requests.set("Content-Length", |
705 requests.set("Content-Length", |
689 String.valueOf(poster.size())); |
706 String.valueOf(poster.size())); |
1321 * Disallowed: |
1344 * Disallowed: |
1322 * - get input, [read input,] get output, [write output] |
1345 * - get input, [read input,] get output, [write output] |
1323 */ |
1346 */ |
1324 |
1347 |
1325 @Override |
1348 @Override |
1326 public synchronized OutputStream getOutputStream() throws IOException { |
1349 public OutputStream getOutputStream() throws IOException { |
1327 connecting = true; |
1350 connectionLock.lock(); |
1328 SocketPermission p = URLtoSocketPermission(this.url); |
1351 try { |
1329 |
1352 connecting = true; |
1330 if (p != null) { |
1353 SocketPermission p = URLtoSocketPermission(this.url); |
1331 try { |
1354 |
1332 return AccessController.doPrivilegedWithCombiner( |
1355 if (p != null) { |
1333 new PrivilegedExceptionAction<>() { |
1356 try { |
1334 public OutputStream run() throws IOException { |
1357 return AccessController.doPrivilegedWithCombiner( |
1335 return getOutputStream0(); |
1358 new PrivilegedExceptionAction<>() { |
1336 } |
1359 public OutputStream run() throws IOException { |
1337 }, null, p |
1360 return getOutputStream0(); |
1338 ); |
1361 } |
1339 } catch (PrivilegedActionException e) { |
1362 }, null, p |
1340 throw (IOException) e.getException(); |
1363 ); |
1341 } |
1364 } catch (PrivilegedActionException e) { |
1342 } else { |
1365 throw (IOException) e.getException(); |
1343 return getOutputStream0(); |
1366 } |
1344 } |
1367 } else { |
1345 } |
1368 return getOutputStream0(); |
1346 |
1369 } |
1347 private synchronized OutputStream getOutputStream0() throws IOException { |
1370 } finally { |
|
1371 connectionLock.unlock(); |
|
1372 } |
|
1373 } |
|
1374 |
|
1375 private OutputStream getOutputStream0() throws IOException { |
|
1376 assert connectionLock.isHeldByCurrentThread(); |
1348 try { |
1377 try { |
1349 if (!doOutput) { |
1378 if (!doOutput) { |
1350 throw new ProtocolException("cannot write to a URLConnection" |
1379 throw new ProtocolException("cannot write to a URLConnection" |
1351 + " if doOutput=false - call setDoOutput(true)"); |
1380 + " if doOutput=false - call setDoOutput(true)"); |
1352 } |
1381 } |
1432 private void setCookieHeader() throws IOException { |
1461 private void setCookieHeader() throws IOException { |
1433 if (cookieHandler != null) { |
1462 if (cookieHandler != null) { |
1434 // we only want to capture the user defined Cookies once, as |
1463 // we only want to capture the user defined Cookies once, as |
1435 // they cannot be changed by user code after we are connected, |
1464 // they cannot be changed by user code after we are connected, |
1436 // only internally. |
1465 // only internally. |
1437 synchronized (this) { |
1466 if (setUserCookies) { |
1438 if (setUserCookies) { |
1467 // we should only reach here when called from |
1439 int k = requests.getKey("Cookie"); |
1468 // writeRequest, which in turn is only called by |
1440 if (k != -1) |
1469 // getInputStream0 |
1441 userCookies = requests.getValue(k); |
1470 assert connectionLock.isHeldByCurrentThread(); |
1442 k = requests.getKey("Cookie2"); |
1471 int k = requests.getKey("Cookie"); |
1443 if (k != -1) |
1472 if (k != -1) |
1444 userCookies2 = requests.getValue(k); |
1473 userCookies = requests.getValue(k); |
1445 setUserCookies = false; |
1474 k = requests.getKey("Cookie2"); |
1446 } |
1475 if (k != -1) |
1447 } |
1476 userCookies2 = requests.getValue(k); |
|
1477 setUserCookies = false; |
|
1478 } |
1448 |
1479 |
1449 // remove old Cookie header before setting new one. |
1480 // remove old Cookie header before setting new one. |
1450 requests.remove("Cookie"); |
1481 requests.remove("Cookie"); |
1451 requests.remove("Cookie2"); |
1482 requests.remove("Cookie2"); |
1452 |
1483 |
1499 |
1530 |
1500 } // end of getting cookies |
1531 } // end of getting cookies |
1501 } |
1532 } |
1502 |
1533 |
1503 @Override |
1534 @Override |
1504 public synchronized InputStream getInputStream() throws IOException { |
1535 public InputStream getInputStream() throws IOException { |
1505 connecting = true; |
1536 connectionLock.lock(); |
1506 SocketPermission p = URLtoSocketPermission(this.url); |
1537 try { |
1507 |
1538 connecting = true; |
1508 if (p != null) { |
1539 SocketPermission p = URLtoSocketPermission(this.url); |
1509 try { |
1540 |
1510 return AccessController.doPrivilegedWithCombiner( |
1541 if (p != null) { |
1511 new PrivilegedExceptionAction<>() { |
1542 try { |
1512 public InputStream run() throws IOException { |
1543 return AccessController.doPrivilegedWithCombiner( |
1513 return getInputStream0(); |
1544 new PrivilegedExceptionAction<>() { |
1514 } |
1545 public InputStream run() throws IOException { |
1515 }, null, p |
1546 return getInputStream0(); |
1516 ); |
1547 } |
1517 } catch (PrivilegedActionException e) { |
1548 }, null, p |
1518 throw (IOException) e.getException(); |
1549 ); |
1519 } |
1550 } catch (PrivilegedActionException e) { |
1520 } else { |
1551 throw (IOException) e.getException(); |
1521 return getInputStream0(); |
1552 } |
|
1553 } else { |
|
1554 return getInputStream0(); |
|
1555 } |
|
1556 } finally { |
|
1557 connectionLock.unlock(); |
1522 } |
1558 } |
1523 } |
1559 } |
1524 |
1560 |
1525 @SuppressWarnings("empty-statement") |
1561 @SuppressWarnings("empty-statement") |
1526 private synchronized InputStream getInputStream0() throws IOException { |
1562 private InputStream getInputStream0() throws IOException { |
1527 |
1563 |
|
1564 assert connectionLock.isHeldByCurrentThread(); |
1528 if (!doInput) { |
1565 if (!doInput) { |
1529 throw new ProtocolException("Cannot read from URLConnection" |
1566 throw new ProtocolException("Cannot read from URLConnection" |
1530 + " if doInput=false (call setDoInput(true))"); |
1567 + " if doInput=false (call setDoInput(true))"); |
1531 } |
1568 } |
1532 |
1569 |
3164 * Sets request property. If a property with the key already |
3210 * Sets request property. If a property with the key already |
3165 * exists, overwrite its value with the new value. |
3211 * exists, overwrite its value with the new value. |
3166 * @param value the value to be set |
3212 * @param value the value to be set |
3167 */ |
3213 */ |
3168 @Override |
3214 @Override |
3169 public synchronized void setRequestProperty(String key, String value) { |
3215 public void setRequestProperty(String key, String value) { |
3170 if (connected || connecting) |
3216 connectionLock.lock(); |
3171 throw new IllegalStateException("Already connected"); |
3217 try { |
3172 if (key == null) |
3218 if (connected || connecting) |
3173 throw new NullPointerException ("key is null"); |
3219 throw new IllegalStateException("Already connected"); |
3174 |
3220 if (key == null) |
3175 if (isExternalMessageHeaderAllowed(key, value)) { |
3221 throw new NullPointerException("key is null"); |
3176 requests.set(key, value); |
3222 |
3177 if (!key.equalsIgnoreCase("Content-Type")) { |
3223 if (isExternalMessageHeaderAllowed(key, value)) { |
3178 userHeaders.set(key, value); |
3224 requests.set(key, value); |
3179 } |
3225 if (!key.equalsIgnoreCase("Content-Type")) { |
|
3226 userHeaders.set(key, value); |
|
3227 } |
|
3228 } |
|
3229 } finally { |
|
3230 connectionLock.unlock(); |
3180 } |
3231 } |
3181 } |
3232 } |
3182 |
3233 |
3183 MessageHeader getUserSetHeaders() { |
3234 MessageHeader getUserSetHeaders() { |
3184 return userHeaders; |
3235 return userHeaders; |
3190 * existing values associated with the same key. |
3241 * existing values associated with the same key. |
3191 * |
3242 * |
3192 * @param key the keyword by which the request is known |
3243 * @param key the keyword by which the request is known |
3193 * (e.g., "<code>accept</code>"). |
3244 * (e.g., "<code>accept</code>"). |
3194 * @param value the value associated with it. |
3245 * @param value the value associated with it. |
3195 * @see #getRequestProperties(java.lang.String) |
3246 * @see #getRequestProperty(java.lang.String) |
3196 * @since 1.4 |
3247 * @since 1.4 |
3197 */ |
3248 */ |
3198 @Override |
3249 @Override |
3199 public synchronized void addRequestProperty(String key, String value) { |
3250 public void addRequestProperty(String key, String value) { |
3200 if (connected || connecting) |
3251 connectionLock.lock(); |
3201 throw new IllegalStateException("Already connected"); |
3252 try { |
3202 if (key == null) |
3253 if (connected || connecting) |
3203 throw new NullPointerException ("key is null"); |
3254 throw new IllegalStateException("Already connected"); |
3204 |
3255 if (key == null) |
3205 if (isExternalMessageHeaderAllowed(key, value)) { |
3256 throw new NullPointerException("key is null"); |
3206 requests.add(key, value); |
3257 |
3207 if (!key.equalsIgnoreCase("Content-Type")) { |
3258 if (isExternalMessageHeaderAllowed(key, value)) { |
|
3259 requests.add(key, value); |
|
3260 if (!key.equalsIgnoreCase("Content-Type")) { |
3208 userHeaders.add(key, value); |
3261 userHeaders.add(key, value); |
3209 } |
3262 } |
|
3263 } |
|
3264 } finally { |
|
3265 connectionLock.unlock(); |
3210 } |
3266 } |
3211 } |
3267 } |
3212 |
3268 |
3213 // |
3269 // |
3214 // Set a property for authentication. This can safely disregard |
3270 // Set a property for authentication. This can safely disregard |
3215 // the connected test. |
3271 // the connected test. |
3216 // |
3272 // |
3217 public void setAuthenticationProperty(String key, String value) { |
3273 public void setAuthenticationProperty(String key, String value) { |
3218 checkMessageHeader(key, value); |
3274 // use lock here to avoid the need for external synchronization |
3219 requests.set(key, value); |
3275 // in AuthenticationInfo subclasses |
|
3276 connectionLock.lock(); |
|
3277 try { |
|
3278 checkMessageHeader(key, value); |
|
3279 requests.set(key, value); |
|
3280 } finally { |
|
3281 connectionLock.unlock(); |
|
3282 } |
3220 } |
3283 } |
3221 |
3284 |
3222 @Override |
3285 @Override |
3223 public synchronized String getRequestProperty (String key) { |
3286 public String getRequestProperty (String key) { |
3224 if (key == null) { |
3287 connectionLock.lock(); |
3225 return null; |
3288 try { |
3226 } |
3289 if (key == null) { |
3227 |
|
3228 // don't return headers containing security sensitive information |
|
3229 for (int i=0; i < EXCLUDE_HEADERS.length; i++) { |
|
3230 if (key.equalsIgnoreCase(EXCLUDE_HEADERS[i])) { |
|
3231 return null; |
3290 return null; |
3232 } |
3291 } |
3233 } |
3292 |
3234 if (!setUserCookies) { |
3293 // don't return headers containing security sensitive information |
3235 if (key.equalsIgnoreCase("Cookie")) { |
3294 for (int i = 0; i < EXCLUDE_HEADERS.length; i++) { |
3236 return userCookies; |
3295 if (key.equalsIgnoreCase(EXCLUDE_HEADERS[i])) { |
3237 } |
3296 return null; |
3238 if (key.equalsIgnoreCase("Cookie2")) { |
3297 } |
3239 return userCookies2; |
3298 } |
3240 } |
3299 if (!setUserCookies) { |
3241 } |
3300 if (key.equalsIgnoreCase("Cookie")) { |
3242 return requests.findValue(key); |
3301 return userCookies; |
|
3302 } |
|
3303 if (key.equalsIgnoreCase("Cookie2")) { |
|
3304 return userCookies2; |
|
3305 } |
|
3306 } |
|
3307 return requests.findValue(key); |
|
3308 } finally { |
|
3309 connectionLock.unlock(); |
|
3310 } |
3243 } |
3311 } |
3244 |
3312 |
3245 /** |
3313 /** |
3246 * Returns an unmodifiable Map of general request |
3314 * Returns an unmodifiable Map of general request |
3247 * properties for this connection. The Map keys |
3315 * properties for this connection. The Map keys |
3253 * @return a Map of the general request properties for this connection. |
3321 * @return a Map of the general request properties for this connection. |
3254 * @throws IllegalStateException if already connected |
3322 * @throws IllegalStateException if already connected |
3255 * @since 1.4 |
3323 * @since 1.4 |
3256 */ |
3324 */ |
3257 @Override |
3325 @Override |
3258 public synchronized Map<String, List<String>> getRequestProperties() { |
3326 public Map<String, List<String>> getRequestProperties() { |
3259 if (connected) |
3327 connectionLock.lock(); |
3260 throw new IllegalStateException("Already connected"); |
3328 try { |
3261 |
3329 if (connected) |
3262 // exclude headers containing security-sensitive info |
3330 throw new IllegalStateException("Already connected"); |
3263 if (setUserCookies) { |
3331 |
3264 return requests.getHeaders(EXCLUDE_HEADERS); |
3332 // exclude headers containing security-sensitive info |
3265 } |
3333 if (setUserCookies) { |
3266 /* |
3334 return requests.getHeaders(EXCLUDE_HEADERS); |
3267 * The cookies in the requests message headers may have |
3335 } |
3268 * been modified. Use the saved user cookies instead. |
3336 /* |
3269 */ |
3337 * The cookies in the requests message headers may have |
3270 Map<String, List<String>> userCookiesMap = null; |
3338 * been modified. Use the saved user cookies instead. |
3271 if (userCookies != null || userCookies2 != null) { |
3339 */ |
3272 userCookiesMap = new HashMap<>(); |
3340 Map<String, List<String>> userCookiesMap = null; |
3273 if (userCookies != null) { |
3341 if (userCookies != null || userCookies2 != null) { |
3274 userCookiesMap.put("Cookie", Arrays.asList(userCookies)); |
3342 userCookiesMap = new HashMap<>(); |
3275 } |
3343 if (userCookies != null) { |
3276 if (userCookies2 != null) { |
3344 userCookiesMap.put("Cookie", Arrays.asList(userCookies)); |
3277 userCookiesMap.put("Cookie2", Arrays.asList(userCookies2)); |
3345 } |
3278 } |
3346 if (userCookies2 != null) { |
3279 } |
3347 userCookiesMap.put("Cookie2", Arrays.asList(userCookies2)); |
3280 return requests.filterAndAddHeaders(EXCLUDE_HEADERS2, userCookiesMap); |
3348 } |
|
3349 } |
|
3350 return requests.filterAndAddHeaders(EXCLUDE_HEADERS2, userCookiesMap); |
|
3351 } finally { |
|
3352 connectionLock.unlock(); |
|
3353 } |
3281 } |
3354 } |
3282 |
3355 |
3283 @Override |
3356 @Override |
3284 public void setConnectTimeout(int timeout) { |
3357 public void setConnectTimeout(int timeout) { |
3285 if (timeout < 0) |
3358 if (timeout < 0) |
3438 * @param readlimit the maximum limit of bytes that can be read before |
3511 * @param readlimit the maximum limit of bytes that can be read before |
3439 * the mark position becomes invalid. |
3512 * the mark position becomes invalid. |
3440 * @see java.io.FilterInputStream#in |
3513 * @see java.io.FilterInputStream#in |
3441 * @see java.io.FilterInputStream#reset() |
3514 * @see java.io.FilterInputStream#reset() |
3442 */ |
3515 */ |
|
3516 // safe to use synchronized here: super method is synchronized too |
3443 @Override |
3517 @Override |
3444 public synchronized void mark(int readlimit) { |
3518 public synchronized void mark(int readlimit) { |
3445 super.mark(readlimit); |
3519 super.mark(readlimit); |
3446 if (cacheRequest != null) { |
3520 if (cacheRequest != null) { |
3447 marked = true; |
3521 marked = true; |