+ throw new HTTPException("unknown authentication type: " + h.get("AUTHTYPE"));
+ }
+ }
+
+ private void doProxyAuth(Hashtable h0, String method) throws IOException {
+ if (Log.on) Log.log(this, "Proxy AuthChallenge: " + h0.get("proxy-authenticate"));
+ Hashtable h = parseAuthenticationChallenge(h0.get("proxy-authenticate").toString());
+ String style = h.get("AUTHTYPE").toString();
+ String realm = h.get("realm").toString();
+
+ if (!realm.equals("Digest") || Proxy.Authorization.authorization2 == null || !"true".equals(h.get("stale")))
+ Proxy.Authorization.getPassword(realm, style, sock.getInetAddress().getHostAddress(), Proxy.Authorization.authorization);
+
+ if (style.equals("Basic")) {
+ Proxy.Authorization.authorization2 =
+ "Basic " + new String(Base64.encode(Proxy.Authorization.authorization.getBytes("US-ASCII")));
+
+ } else if (style.equals("Digest")) {
+ String A1 = Proxy.Authorization.authorization.substring(0, userInfo.indexOf(':')) + ":" + h.get("realm") + ":" +
+ Proxy.Authorization.authorization.substring(Proxy.Authorization.authorization.indexOf(':') + 1);
+ String A2 = method + ":" + path;
+ Proxy.Authorization.authorization2 =
+ "Digest " +
+ "username=\"" + Proxy.Authorization.authorization.substring(0, Proxy.Authorization.authorization.indexOf(':')) + "\", " +
+ "realm=\"" + h.get("realm") + "\", " +
+ "nonce=\"" + h.get("nonce") + "\", " +
+ "uri=\"" + path + "\", " +
+ (h.get("opaque") == null ? "" : ("opaque=\"" + h.get("opaque") + "\", ")) +
+ "response=\"" + H(H(A1) + ":" + h.get("nonce") + ":" + H(A2)) + "\", " +
+ "algorithm=MD5";
+ }
+ }
+
+
+ // HTTPException ///////////////////////////////////////////////////////////////////////////////////
+
+ static class HTTPException extends IOException { public HTTPException(String s) { super(s); } }
+
+
+ // HTTPInputStream ///////////////////////////////////////////////////////////////////////////////////
+
+ /** An input stream that represents a subset of a longer input stream. Supports HTTP chunking as well */
+ public class HTTPInputStream extends FilterInputStream {
+
+ /** if chunking, the number of bytes remaining in this subset; otherwise the remainder of the chunk */
+ private int length = 0;
+
+ /** this semaphore will be released when the stream is closed */
+ private Semaphore releaseMe = null;
+
+ /** indicates that we have encountered the zero-length terminator chunk */
+ boolean chunkedDone = false;
+
+ /** if we're on the first chunk, we don't pre-read a CRLF */
+ boolean firstChunk = true;
+
+ /** the length of the entire content body; -1 if chunked */
+ private int contentLength = 0;
+ public int getContentLength() { return contentLength; }
+
+ HTTPInputStream(InputStream in, int length, Semaphore releaseMe) throws IOException {
+ super(in);
+ this.releaseMe = releaseMe;
+ this.contentLength = length;
+ this.length = length == -1 ? 0 : length;
+ }
+
+ public boolean markSupported() { return false; }
+ public int read(byte[] b) throws IOException { return read(b, 0, b.length); }
+ public long skip(long n) throws IOException { return read(null, -1, (int)n); }
+ public int available() throws IOException {
+ if (contentLength == -1) return java.lang.Math.min(super.available(), length);
+ return super.available();
+ }
+
+ public int read() throws IOException {
+ byte[] b = new byte[1];
+ int ret = read(b, 0, 1);
+ return ret == -1 ? -1 : b[0] & 0xff;
+ }
+
+ private void readChunk() throws IOException {
+ if (chunkedDone) return;
+ if (!firstChunk) super.skip(2); // CRLF
+ firstChunk = false;
+ String chunkLen = "";
+ while(true) {
+ int i = super.read();
+ if (i == -1) throw new HTTPException("encountered end of stream while reading chunk length");
+
+ // FEATURE: handle chunking extensions
+ if (i == '\r') {
+ super.read(); // LF
+ break;
+ } else {
+ chunkLen += (char)i;
+ }
+ }
+ length = Integer.parseInt(chunkLen.trim(), 16);
+ if (length == 0) chunkedDone = true;
+ }
+
+ public int read(byte[] b, int off, int len) throws IOException {
+ boolean good = false;
+ try {
+ if (length == 0 && contentLength == -1) {
+ readChunk();
+ if (chunkedDone) { good = true; return -1; }
+ } else {
+ if (length == 0) { good = true; return -1; }
+ }
+ if (len > length) len = length;
+ int ret = b == null ? (int)super.skip(len) : super.read(b, off, len);
+ if (ret >= 0) {
+ length -= ret;
+ good = true;
+ }
+ return ret;
+ } finally {
+ if (!good) invalid = true;
+ }
+ }
+
+ public void close() throws IOException {
+ if (contentLength == -1) {
+ while(!chunkedDone) {
+ if (length != 0) skip(length);
+ readChunk();
+ }
+ skip(2);
+ } else {
+ if (length != 0) skip(length);
+ }
+ if (releaseMe != null) releaseMe.release();