1 |
/* Request.java -- |
/* Request.java -- |
2 |
Copyright (C) 2004 Free Software Foundation, Inc. |
Copyright (C) 2004, 2005 Free Software Foundation, Inc. |
3 |
|
|
4 |
This file is part of GNU Classpath. |
This file is part of GNU Classpath. |
5 |
|
|
40 |
|
|
41 |
import gnu.java.net.BASE64; |
import gnu.java.net.BASE64; |
42 |
import gnu.java.net.LineInputStream; |
import gnu.java.net.LineInputStream; |
|
import gnu.java.net.protocol.http.event.RequestEvent; |
|
43 |
|
|
44 |
import java.io.IOException; |
import java.io.IOException; |
45 |
import java.io.InputStream; |
import java.io.InputStream; |
99 |
protected int requestBodyNegotiationThreshold; |
protected int requestBodyNegotiationThreshold; |
100 |
|
|
101 |
/** |
/** |
|
* The response body reader. |
|
|
*/ |
|
|
protected ResponseBodyReader responseBodyReader; |
|
|
|
|
|
/** |
|
102 |
* Map of response header handlers. |
* Map of response header handlers. |
103 |
*/ |
*/ |
104 |
protected Map responseHeaderHandlers; |
protected Map responseHeaderHandlers; |
230 |
} |
} |
231 |
|
|
232 |
/** |
/** |
|
* Sets the response body reader. |
|
|
* @param responseBodyReader the handler to receive notifications of |
|
|
* response body content |
|
|
*/ |
|
|
public void setResponseBodyReader(ResponseBodyReader responseBodyReader) |
|
|
{ |
|
|
this.responseBodyReader = responseBodyReader; |
|
|
} |
|
|
|
|
|
/** |
|
233 |
* Sets a callback handler to be invoked for the specified header name. |
* Sets a callback handler to be invoked for the specified header name. |
234 |
* @param name the header name |
* @param name the header name |
235 |
* @param handler the handler to receive the value for the header |
* @param handler the handler to receive the value for the header |
308 |
do |
do |
309 |
{ |
{ |
310 |
retry = false; |
retry = false; |
|
// Send request |
|
|
connection.fireRequestEvent(RequestEvent.REQUEST_SENDING, this); |
|
311 |
|
|
312 |
// Get socket output and input streams |
// Get socket output and input streams |
313 |
OutputStream out = connection.getOutputStream(); |
OutputStream out = connection.getOutputStream(); |
314 |
LineInputStream in = |
|
|
new LineInputStream(connection.getInputStream()); |
|
315 |
// Request line |
// Request line |
316 |
String requestUri = path; |
String requestUri = path; |
317 |
if (connection.isUsingProxy() && |
if (connection.isUsingProxy() && |
350 |
count += len; |
count += len; |
351 |
} |
} |
352 |
while (len > -1 && count < contentLength); |
while (len > -1 && count < contentLength); |
|
out.write(CRLF.getBytes(US_ASCII)); |
|
353 |
} |
} |
354 |
out.flush(); |
out.flush(); |
|
// Sent event |
|
|
connection.fireRequestEvent(RequestEvent.REQUEST_SENT, this); |
|
355 |
// Get response |
// Get response |
356 |
response = readResponse(in); |
while(true) |
357 |
int sc = response.getCode(); |
{ |
358 |
if (sc == 401 && authenticator != null) |
response = readResponse(connection.getInputStream()); |
359 |
{ |
int sc = response.getCode(); |
360 |
if (authenticate(response, attempts++)) |
if (sc == 401 && authenticator != null) |
361 |
{ |
{ |
362 |
retry = true; |
if (authenticate(response, attempts++)) |
363 |
} |
{ |
364 |
} |
retry = true; |
365 |
else if (sc == 100 && expectingContinue) |
} |
366 |
{ |
} |
367 |
requestHeaders.remove("Expect"); |
else if (sc == 100) |
368 |
setHeader("Content-Length", Integer.toString(contentLength)); |
{ |
369 |
expectingContinue = false; |
if (expectingContinue) |
370 |
retry = true; |
{ |
371 |
} |
requestHeaders.remove("Expect"); |
372 |
|
setHeader("Content-Length", |
373 |
|
Integer.toString(contentLength)); |
374 |
|
expectingContinue = false; |
375 |
|
retry = true; |
376 |
|
} |
377 |
|
else |
378 |
|
{ |
379 |
|
// A conforming server can send an unsoliceted |
380 |
|
// Continue response but *should* not (RFC 2616 |
381 |
|
// sec 8.2.3). Ignore the bogus Continue |
382 |
|
// response and get the real response that |
383 |
|
// should follow |
384 |
|
continue; |
385 |
|
} |
386 |
|
} |
387 |
|
break; |
388 |
|
} |
389 |
} |
} |
390 |
while (retry); |
while (retry); |
391 |
} |
} |
397 |
return response; |
return response; |
398 |
} |
} |
399 |
|
|
400 |
Response readResponse(LineInputStream in) |
Response readResponse(InputStream in) |
401 |
throws IOException |
throws IOException |
402 |
{ |
{ |
403 |
String line; |
String line; |
404 |
int len; |
int len; |
405 |
|
|
406 |
// Read response status line |
// Read response status line |
407 |
line = in.readLine(); |
LineInputStream lis = new LineInputStream(in); |
408 |
|
|
409 |
|
line = lis.readLine(); |
410 |
if (line == null) |
if (line == null) |
411 |
{ |
{ |
412 |
throw new ProtocolException("Peer closed connection"); |
throw new ProtocolException("Peer closed connection"); |
435 |
String message = line.substring(end + 1, len - 1); |
String message = line.substring(end + 1, len - 1); |
436 |
// Read response headers |
// Read response headers |
437 |
Headers responseHeaders = new Headers(); |
Headers responseHeaders = new Headers(); |
438 |
responseHeaders.parse(in); |
responseHeaders.parse(lis); |
439 |
notifyHeaderHandlers(responseHeaders); |
notifyHeaderHandlers(responseHeaders); |
440 |
// Construct response |
InputStream body = null; |
441 |
int codeClass = code / 100; |
|
|
Response ret = new Response(majorVersion, minorVersion, code, |
|
|
codeClass, message, responseHeaders); |
|
442 |
switch (code) |
switch (code) |
443 |
{ |
{ |
444 |
|
case 100: |
445 |
case 204: |
case 204: |
446 |
case 205: |
case 205: |
447 |
case 304: |
case 304: |
448 |
break; |
break; |
449 |
default: |
default: |
450 |
// Does response body reader want body? |
body = createResponseBodyStream(responseHeaders, majorVersion, |
451 |
boolean notify = (responseBodyReader != null); |
minorVersion, in); |
|
if (notify) |
|
|
{ |
|
|
if (!responseBodyReader.accept(this, ret)) |
|
|
{ |
|
|
notify = false; |
|
|
} |
|
|
} |
|
|
readResponseBody(ret, in, notify); |
|
452 |
} |
} |
453 |
|
|
454 |
|
// Construct response |
455 |
|
Response ret = new Response(majorVersion, minorVersion, code, |
456 |
|
message, responseHeaders, body); |
457 |
return ret; |
return ret; |
458 |
} |
} |
459 |
|
|
479 |
} |
} |
480 |
} |
} |
481 |
|
|
482 |
void readResponseBody(Response response, InputStream in, |
private InputStream createResponseBodyStream(Headers responseHeaders, |
483 |
boolean notify) |
int majorVersion, |
484 |
|
int minorVersion, |
485 |
|
InputStream in) |
486 |
throws IOException |
throws IOException |
487 |
{ |
{ |
488 |
byte[] buffer = new byte[4096]; |
long contentLength = -1; |
|
int contentLength = -1; |
|
489 |
Headers trailer = null; |
Headers trailer = null; |
490 |
|
|
491 |
String transferCoding = response.getHeader("Transfer-Encoding"); |
// Persistent connections are the default in HTTP/1.1 |
492 |
|
boolean doClose = "close".equalsIgnoreCase(getHeader("Connection")) || |
493 |
|
"close".equalsIgnoreCase(responseHeaders.getValue("Connection")) || |
494 |
|
(connection.majorVersion == 1 && connection.minorVersion == 0) || |
495 |
|
(majorVersion == 1 && minorVersion == 0); |
496 |
|
|
497 |
|
String transferCoding = responseHeaders.getValue("Transfer-Encoding"); |
498 |
if ("chunked".equalsIgnoreCase(transferCoding)) |
if ("chunked".equalsIgnoreCase(transferCoding)) |
499 |
{ |
{ |
500 |
trailer = new Headers(); |
in = new LimitedLengthInputStream(in, -1, false, connection, doClose); |
501 |
in = new ChunkedInputStream(in, trailer); |
|
502 |
|
in = new ChunkedInputStream(in, responseHeaders); |
503 |
} |
} |
504 |
else |
else |
505 |
{ |
{ |
506 |
contentLength = response.getIntHeader("Content-Length"); |
contentLength = responseHeaders.getLongValue("Content-Length"); |
507 |
|
|
508 |
|
if (contentLength < 0) |
509 |
|
doClose = true; // No Content-Length, must close. |
510 |
|
|
511 |
|
in = new LimitedLengthInputStream(in, contentLength, |
512 |
|
contentLength >= 0, |
513 |
|
connection, doClose); |
514 |
} |
} |
515 |
String contentCoding = response.getHeader("Content-Encoding"); |
String contentCoding = responseHeaders.getValue("Content-Encoding"); |
516 |
if (contentCoding != null && !"identity".equals(contentCoding)) |
if (contentCoding != null && !"identity".equals(contentCoding)) |
517 |
{ |
{ |
518 |
if ("gzip".equals(contentCoding)) |
if ("gzip".equals(contentCoding)) |
529 |
contentCoding); |
contentCoding); |
530 |
} |
} |
531 |
} |
} |
532 |
|
return in; |
|
// Persistent connections are the default in HTTP/1.1 |
|
|
boolean doClose = "close".equalsIgnoreCase(getHeader("Connection")) || |
|
|
"close".equalsIgnoreCase(response.getHeader("Connection")) || |
|
|
(connection.majorVersion == 1 && connection.minorVersion == 0) || |
|
|
(response.majorVersion == 1 && response.minorVersion == 0); |
|
|
|
|
|
int count = contentLength; |
|
|
int len = (count > -1) ? count : buffer.length; |
|
|
len = (len > buffer.length) ? buffer.length : len; |
|
|
while (len > -1) |
|
|
{ |
|
|
len = in.read(buffer, 0, len); |
|
|
if (len < 0) |
|
|
{ |
|
|
// EOF |
|
|
connection.closeConnection(); |
|
|
break; |
|
|
} |
|
|
if (notify) |
|
|
{ |
|
|
responseBodyReader.read(buffer, 0, len); |
|
|
} |
|
|
if (count > -1) |
|
|
{ |
|
|
count -= len; |
|
|
if (count < 1) |
|
|
{ |
|
|
if (doClose) |
|
|
{ |
|
|
connection.closeConnection(); |
|
|
} |
|
|
break; |
|
|
} |
|
|
} |
|
|
} |
|
|
if (notify) |
|
|
{ |
|
|
responseBodyReader.close(); |
|
|
} |
|
|
if (trailer != null) |
|
|
{ |
|
|
response.getHeaders().putAll(trailer); |
|
|
notifyHeaderHandlers(trailer); |
|
|
} |
|
533 |
} |
} |
534 |
|
|
535 |
boolean authenticate(Response response, int attempts) |
boolean authenticate(Response response, int attempts) |
649 |
{ |
{ |
650 |
int len = text.length(); |
int len = text.length(); |
651 |
String key = null; |
String key = null; |
652 |
StringBuffer buf = new StringBuffer(); |
StringBuilder buf = new StringBuilder(); |
653 |
Properties ret = new Properties(); |
Properties ret = new Properties(); |
654 |
boolean inQuote = false; |
boolean inQuote = false; |
655 |
for (int i = 0; i < len; i++) |
for (int i = 0; i < len; i++) |
702 |
{ |
{ |
703 |
int nc = connection.getNonceCount(nonce); |
int nc = connection.getNonceCount(nonce); |
704 |
String hex = Integer.toHexString(nc); |
String hex = Integer.toHexString(nc); |
705 |
StringBuffer buf = new StringBuffer(); |
StringBuilder buf = new StringBuilder(); |
706 |
for (int i = 8 - hex.length(); i > 0; i--) |
for (int i = 8 - hex.length(); i > 0; i--) |
707 |
{ |
{ |
708 |
buf.append('0'); |
buf.append('0'); |
773 |
|
|
774 |
int len = text.length(); |
int len = text.length(); |
775 |
String attr = null; |
String attr = null; |
776 |
StringBuffer buf = new StringBuffer(); |
StringBuilder buf = new StringBuilder(); |
777 |
boolean inQuote = false; |
boolean inQuote = false; |
778 |
for (int i = 0; i <= len; i++) |
for (int i = 0; i <= len; i++) |
779 |
{ |
{ |