0001: /*
0002: * ====================================================================
0003: * Copyright (c) 2004-2008 TMate Software Ltd. All rights reserved.
0004: *
0005: * This software is licensed as described in the file COPYING, which
0006: * you should have received as part of this distribution. The terms
0007: * are also available at http://svnkit.com/license.html
0008: * If newer versions of this license are posted there, you may use a
0009: * newer version instead, at your option.
0010: * ====================================================================
0011: */
0012: package org.tmatesoft.svn.core.internal.io.dav.http;
0013:
0014: import java.io.BufferedInputStream;
0015: import java.io.BufferedOutputStream;
0016: import java.io.ByteArrayInputStream;
0017: import java.io.EOFException;
0018: import java.io.File;
0019: import java.io.IOException;
0020: import java.io.InputStream;
0021: import java.io.OutputStream;
0022: import java.io.UnsupportedEncodingException;
0023: import java.net.HttpURLConnection;
0024: import java.net.Socket;
0025: import java.net.SocketTimeoutException;
0026: import java.net.UnknownHostException;
0027: import java.security.cert.CertificateException;
0028: import java.text.ParseException;
0029: import java.util.Collection;
0030: import java.util.zip.GZIPInputStream;
0031:
0032: import javax.net.ssl.SSLHandshakeException;
0033: import javax.xml.parsers.FactoryConfigurationError;
0034: import javax.xml.parsers.ParserConfigurationException;
0035: import javax.xml.parsers.SAXParser;
0036: import javax.xml.parsers.SAXParserFactory;
0037:
0038: import org.tmatesoft.svn.core.SVNCancelException;
0039: import org.tmatesoft.svn.core.SVNErrorCode;
0040: import org.tmatesoft.svn.core.SVNErrorMessage;
0041: import org.tmatesoft.svn.core.SVNException;
0042: import org.tmatesoft.svn.core.SVNURL;
0043: import org.tmatesoft.svn.core.auth.ISVNAuthenticationManager;
0044: import org.tmatesoft.svn.core.auth.ISVNProxyManager;
0045: import org.tmatesoft.svn.core.auth.ISVNSSLManager;
0046: import org.tmatesoft.svn.core.auth.SVNAuthentication;
0047: import org.tmatesoft.svn.core.auth.SVNPasswordAuthentication;
0048: import org.tmatesoft.svn.core.auth.SVNSSLAuthentication;
0049: import org.tmatesoft.svn.core.internal.io.dav.handlers.DAVErrorHandler;
0050: import org.tmatesoft.svn.core.internal.util.SVNSocketFactory;
0051: import org.tmatesoft.svn.core.internal.wc.SVNCancellableOutputStream;
0052: import org.tmatesoft.svn.core.internal.wc.SVNErrorManager;
0053: import org.tmatesoft.svn.core.internal.wc.SVNFileUtil;
0054: import org.tmatesoft.svn.core.io.SVNRepository;
0055: import org.xml.sax.EntityResolver;
0056: import org.xml.sax.InputSource;
0057: import org.xml.sax.SAXException;
0058: import org.xml.sax.SAXNotRecognizedException;
0059: import org.xml.sax.SAXNotSupportedException;
0060: import org.xml.sax.SAXParseException;
0061: import org.xml.sax.helpers.DefaultHandler;
0062:
0063: /**
0064: * @version 1.1.1
0065: * @author TMate Software Ltd.
0066: */
0067: class HTTPConnection implements IHTTPConnection {
0068:
0069: private static final DefaultHandler DEFAULT_SAX_HANDLER = new DefaultHandler();
0070: private static EntityResolver NO_ENTITY_RESOLVER = new EntityResolver() {
0071: public InputSource resolveEntity(String publicId,
0072: String systemId) throws SAXException, IOException {
0073: return new InputSource(
0074: new ByteArrayInputStream(new byte[0]));
0075: }
0076: };
0077:
0078: private static final int DEFAULT_HTTP_TIMEOUT = 3600 * 1000;
0079:
0080: private static SAXParserFactory ourSAXParserFactory;
0081: private byte[] myBuffer;
0082: private SAXParser mySAXParser;
0083: private SVNURL myHost;
0084: private OutputStream myOutputStream;
0085: private InputStream myInputStream;
0086: private Socket mySocket;
0087: private SVNRepository myRepository;
0088: private boolean myIsSecured;
0089: private boolean myIsProxied;
0090: private SVNAuthentication myLastValidAuth;
0091: private HTTPAuthentication myChallengeCredentials;
0092: private HTTPAuthentication myProxyAuthentication;
0093: private boolean myIsSpoolResponse;
0094: private ISVNSSLManager mySSLManager;
0095: private String myCharset;
0096: private boolean myIsSpoolAll;
0097: private File mySpoolDirectory;
0098:
0099: public HTTPConnection(SVNRepository repository, String charset,
0100: File spoolDirectory, boolean spoolAll) throws SVNException {
0101: myRepository = repository;
0102: myCharset = charset;
0103: myHost = repository.getLocation().setPath("", false);
0104: myIsSecured = "https".equalsIgnoreCase(myHost.getProtocol());
0105: myIsSpoolAll = spoolAll;
0106: mySpoolDirectory = spoolDirectory;
0107: }
0108:
0109: public SVNURL getHost() {
0110: return myHost;
0111: }
0112:
0113: private void connect(ISVNSSLManager sslManager) throws IOException,
0114: SVNException {
0115: SVNURL location = myRepository.getLocation();
0116: if (mySocket == null
0117: || SVNSocketFactory.isSocketStale(mySocket)) {
0118: close();
0119: String host = location.getHost();
0120: int port = location.getPort();
0121:
0122: ISVNAuthenticationManager authManager = myRepository
0123: .getAuthenticationManager();
0124: ISVNProxyManager proxyAuth = authManager != null ? authManager
0125: .getProxyManager(location)
0126: : null;
0127: if (proxyAuth != null && proxyAuth.getProxyHost() != null) {
0128: mySocket = SVNSocketFactory.createPlainSocket(proxyAuth
0129: .getProxyHost(), proxyAuth.getProxyPort());
0130: if (myProxyAuthentication == null) {
0131: myProxyAuthentication = new HTTPBasicAuthentication(
0132: proxyAuth.getProxyUserName(), proxyAuth
0133: .getProxyPassword(), myCharset);
0134: }
0135: myIsProxied = true;
0136: if (myIsSecured) {
0137: HTTPRequest connectRequest = new HTTPRequest(
0138: myCharset);
0139: connectRequest.setConnection(this );
0140: connectRequest.initCredentials(
0141: myProxyAuthentication, "CONNECT", host
0142: + ":" + port);
0143: connectRequest
0144: .setProxyAuthentication(myProxyAuthentication
0145: .authenticate());
0146: connectRequest.setForceProxyAuth(true);
0147: connectRequest.dispatch("CONNECT", host + ":"
0148: + port, null, 0, 0, null);
0149: HTTPStatus status = connectRequest.getStatus();
0150: if (status.getCode() == HttpURLConnection.HTTP_OK) {
0151: myInputStream = null;
0152: myOutputStream = null;
0153: mySocket = SVNSocketFactory.createSSLSocket(
0154: sslManager, host, port, mySocket);
0155: proxyAuth.acknowledgeProxyContext(true, null);
0156: return;
0157: }
0158: SVNURL proxyURL = SVNURL.parseURIEncoded("http://"
0159: + proxyAuth.getProxyHost() + ":"
0160: + proxyAuth.getProxyPort());
0161: SVNErrorMessage err = SVNErrorMessage.create(
0162: SVNErrorCode.RA_DAV_REQUEST_FAILED,
0163: "{0} request failed on ''{1}''",
0164: new Object[] { "CONNECT", proxyURL });
0165: proxyAuth.acknowledgeProxyContext(false, err);
0166: SVNErrorManager.error(err, connectRequest
0167: .getErrorMessage());
0168: }
0169: } else {
0170: myIsProxied = false;
0171: myProxyAuthentication = null;
0172: mySocket = myIsSecured ? SVNSocketFactory
0173: .createSSLSocket(sslManager, host, port)
0174: : SVNSocketFactory
0175: .createPlainSocket(host, port);
0176: }
0177: long timeout = myRepository.getAuthenticationManager() != null ? myRepository
0178: .getAuthenticationManager().getHTTPTimeout(
0179: myRepository)
0180: : DEFAULT_HTTP_TIMEOUT;
0181: if (timeout < 0) {
0182: timeout = DEFAULT_HTTP_TIMEOUT;
0183: }
0184: mySocket.setSoTimeout((int) timeout);
0185: }
0186: }
0187:
0188: public void readHeader(HTTPRequest request) throws IOException {
0189: InputStream is = myRepository.getDebugLog().createLogStream(
0190: getInputStream());
0191: try {
0192: HTTPStatus status = HTTPParser.parseStatus(is, myCharset);
0193: HTTPHeader header = HTTPHeader.parseHeader(is, myCharset);
0194: request.setStatus(status);
0195: request.setResponseHeader(header);
0196: } catch (ParseException e) {
0197: // in case of parse exception:
0198: // try to read remaining and log it.
0199: String line = HTTPParser.readLine(is, myCharset);
0200: while (line != null && line.length() > 0) {
0201: line = HTTPParser.readLine(is, myCharset);
0202: }
0203: throw new IOException(e.getMessage());
0204: } finally {
0205: myRepository.getDebugLog().flushStream(is);
0206: }
0207: }
0208:
0209: public SVNErrorMessage readError(HTTPRequest request,
0210: String method, String path) {
0211: DAVErrorHandler errorHandler = new DAVErrorHandler();
0212: try {
0213: readData(request, method, path, errorHandler);
0214: } catch (IOException e) {
0215: return null;
0216: }
0217: return errorHandler.getErrorMessage();
0218: }
0219:
0220: public void sendData(byte[] body) throws IOException {
0221: try {
0222: getOutputStream().write(body, 0, body.length);
0223: getOutputStream().flush();
0224: } finally {
0225: myRepository.getDebugLog().flushStream(getOutputStream());
0226: }
0227: }
0228:
0229: public void sendData(InputStream source, long length)
0230: throws IOException {
0231: try {
0232: byte[] buffer = getBuffer();
0233: while (length > 0) {
0234: int read = source.read(buffer, 0, (int) Math.min(
0235: buffer.length, length));
0236: length -= read;
0237: if (read > 0) {
0238: getOutputStream().write(buffer, 0, read);
0239: } else {
0240: break;
0241: }
0242: }
0243: getOutputStream().flush();
0244: } finally {
0245: myRepository.getDebugLog().flushStream(getOutputStream());
0246: }
0247: }
0248:
0249: public SVNAuthentication getLastValidCredentials() {
0250: return myLastValidAuth;
0251: }
0252:
0253: public void clearAuthenticationCache() {
0254: myLastValidAuth = null;
0255: mySSLManager = null;
0256: }
0257:
0258: public HTTPStatus request(String method, String path,
0259: HTTPHeader header, StringBuffer body, int ok1, int ok2,
0260: OutputStream dst, DefaultHandler handler)
0261: throws SVNException {
0262: return request(method, path, header, body, ok1, ok2, dst,
0263: handler, null);
0264: }
0265:
0266: public HTTPStatus request(String method, String path,
0267: HTTPHeader header, StringBuffer body, int ok1, int ok2,
0268: OutputStream dst, DefaultHandler handler,
0269: SVNErrorMessage context) throws SVNException {
0270: byte[] buffer = null;
0271: if (body != null) {
0272: try {
0273: buffer = body.toString().getBytes("UTF-8");
0274: } catch (UnsupportedEncodingException e) {
0275: buffer = body.toString().getBytes();
0276: }
0277: }
0278: return request(method, path, header,
0279: buffer != null ? new ByteArrayInputStream(buffer)
0280: : null, ok1, ok2, dst, handler, context);
0281: }
0282:
0283: public HTTPStatus request(String method, String path,
0284: HTTPHeader header, InputStream body, int ok1, int ok2,
0285: OutputStream dst, DefaultHandler handler)
0286: throws SVNException {
0287: return request(method, path, header, body, ok1, ok2, dst,
0288: handler, null);
0289: }
0290:
0291: public HTTPStatus request(String method, String path,
0292: HTTPHeader header, InputStream body, int ok1, int ok2,
0293: OutputStream dst, DefaultHandler handler,
0294: SVNErrorMessage context) throws SVNException {
0295: if ("".equals(path) || path == null) {
0296: path = "/";
0297: }
0298:
0299: // 1. prompt for ssl client cert if needed, if cancelled - throw cancellation exception.
0300: ISVNSSLManager sslManager = mySSLManager != null ? mySSLManager
0301: : promptSSLClientCertificate(true, false);
0302: String sslRealm = "<" + myHost.getProtocol() + "://"
0303: + myHost.getHost() + ":" + myHost.getPort() + ">";
0304: SVNAuthentication httpAuth = myLastValidAuth;
0305: boolean isAuthForced = myRepository.getAuthenticationManager() != null ? myRepository
0306: .getAuthenticationManager().isAuthenticationForced()
0307: : false;
0308: if (httpAuth == null && isAuthForced) {
0309: httpAuth = myRepository.getAuthenticationManager()
0310: .getFirstAuthentication(
0311: ISVNAuthenticationManager.PASSWORD,
0312: sslRealm, null);
0313: myChallengeCredentials = new HTTPBasicAuthentication(
0314: (SVNPasswordAuthentication) httpAuth, myCharset);
0315: }
0316: String realm = null;
0317:
0318: // 2. create request instance.
0319: HTTPRequest request = new HTTPRequest(myCharset);
0320: request.setConnection(this );
0321: request.setKeepAlive(true);
0322: request.setRequestBody(body);
0323: request.setResponseHandler(handler);
0324: request.setResponseStream(dst);
0325:
0326: SVNErrorMessage err = null;
0327:
0328: while (true) {
0329: HTTPStatus status = null;
0330: try {
0331: err = null;
0332: connect(sslManager);
0333: request.reset();
0334: request.setProxied(myIsProxied);
0335: request.setSecured(myIsSecured);
0336: if (myProxyAuthentication != null) {
0337: request.initCredentials(myProxyAuthentication,
0338: method, path);
0339: request
0340: .setProxyAuthentication(myProxyAuthentication
0341: .authenticate());
0342: }
0343: if (httpAuth != null && myChallengeCredentials != null) {
0344: request.initCredentials(myChallengeCredentials,
0345: method, path);
0346: String authResponse = myChallengeCredentials
0347: .authenticate();
0348: request.setAuthentication(authResponse);
0349: }
0350: request.dispatch(method, path, header, ok1, ok2,
0351: context);
0352: status = request.getStatus();
0353: } catch (SSLHandshakeException ssl) {
0354: myRepository.getDebugLog().info(ssl);
0355: if (ssl.getCause() instanceof CertificateException
0356: && ssl.getCause().getCause() instanceof SVNCancelException) {
0357: SVNErrorManager.cancel(ssl.getCause().getCause()
0358: .getMessage());
0359: }
0360: if (sslManager != null) {
0361: close();
0362: SVNSSLAuthentication sslAuth = sslManager
0363: .getClientAuthentication();
0364: if (sslAuth != null) {
0365: SVNErrorMessage sslErr = SVNErrorMessage
0366: .create(
0367: SVNErrorCode.RA_NOT_AUTHORIZED,
0368: "SSL handshake failed: ''{0}''",
0369: ssl.getMessage());
0370: myRepository.getAuthenticationManager()
0371: .acknowledgeAuthentication(false,
0372: ISVNAuthenticationManager.SSL,
0373: sslRealm, sslErr, sslAuth);
0374: }
0375: sslManager = promptSSLClientCertificate(
0376: sslAuth == null, true);
0377: continue;
0378: }
0379: err = SVNErrorMessage.create(
0380: SVNErrorCode.RA_DAV_REQUEST_FAILED, ssl);
0381: } catch (UnknownHostException ioe) {
0382: myRepository.getDebugLog().info(ioe);
0383: err = SVNErrorMessage.create(
0384: SVNErrorCode.RA_DAV_REQUEST_FAILED, ioe);
0385: } catch (SocketTimeoutException timeout) {
0386: myRepository.getDebugLog().info(timeout);
0387: err = SVNErrorMessage.create(
0388: SVNErrorCode.RA_DAV_REQUEST_FAILED,
0389: "timed out waiting for server");
0390: } catch (SVNCancellableOutputStream.IOCancelException cancel) {
0391: myRepository.getDebugLog().info(cancel);
0392: SVNErrorManager.cancel(cancel.getMessage());
0393: } catch (IOException e) {
0394: myRepository.getDebugLog().info(e);
0395: if (sslManager != null) {
0396: close();
0397: SVNSSLAuthentication sslAuth = sslManager
0398: .getClientAuthentication();
0399: if (sslAuth != null) {
0400: SVNErrorMessage sslErr = SVNErrorMessage
0401: .create(
0402: SVNErrorCode.RA_NOT_AUTHORIZED,
0403: "SSL handshake failed: ''{0}''",
0404: e.getMessage());
0405: myRepository.getAuthenticationManager()
0406: .acknowledgeAuthentication(false,
0407: ISVNAuthenticationManager.SSL,
0408: sslRealm, sslErr, sslAuth);
0409: }
0410: sslManager = promptSSLClientCertificate(
0411: sslAuth == null, true);
0412: continue;
0413: }
0414: err = SVNErrorMessage.create(
0415: SVNErrorCode.RA_DAV_REQUEST_FAILED, e
0416: .getMessage());
0417: } catch (SVNException e) {
0418: myRepository.getDebugLog().info(e);
0419: // force connection close on SVNException
0420: // (could be thrown by user's auth manager methods).
0421: close();
0422: throw e;
0423: } finally {
0424: finishResponse(request);
0425: }
0426: if (err != null) {
0427: close();
0428: if (sslManager != null) {
0429: sslManager.acknowledgeSSLContext(false, err);
0430: }
0431: break;
0432: }
0433: if (sslManager != null) {
0434: sslManager.acknowledgeSSLContext(true, null);
0435: SVNSSLAuthentication sslAuth = sslManager
0436: .getClientAuthentication();
0437: if (sslAuth != null) {
0438: mySSLManager = sslManager;
0439: myRepository.getAuthenticationManager()
0440: .acknowledgeAuthentication(true,
0441: ISVNAuthenticationManager.SSL,
0442: sslRealm, null, sslAuth);
0443: }
0444: }
0445:
0446: if (status.getCode() == HttpURLConnection.HTTP_FORBIDDEN) {
0447: myLastValidAuth = null;
0448: close();
0449: err = request.getErrorMessage();
0450: } else if (myIsProxied
0451: && status.getCode() == HttpURLConnection.HTTP_PROXY_AUTH) {
0452: Collection proxyAuthHeaders = request
0453: .getResponseHeader().getHeaderValues(
0454: HTTPHeader.PROXY_AUTHENTICATE_HEADER);
0455: boolean retry = false;
0456: if (!HTTPAuthentication
0457: .isSchemeSupportedByServer(
0458: myProxyAuthentication
0459: .getAuthenticationScheme(),
0460: proxyAuthHeaders)) {
0461: retry = true;
0462: }
0463:
0464: try {
0465: myProxyAuthentication = HTTPAuthentication
0466: .parseAuthParameters(proxyAuthHeaders,
0467: myProxyAuthentication, myCharset);
0468: } catch (SVNException svne) {
0469: myRepository.getDebugLog().info(svne);
0470: err = svne.getErrorMessage();
0471: break;
0472: }
0473:
0474: if (retry) {
0475: close();
0476: continue;
0477: }
0478:
0479: if (myProxyAuthentication instanceof HTTPNTLMAuthentication) {
0480: HTTPNTLMAuthentication ntlmProxyAuth = (HTTPNTLMAuthentication) myProxyAuthentication;
0481: if (ntlmProxyAuth.isInType3State()) {
0482: continue;
0483: }
0484: }
0485:
0486: err = SVNErrorMessage.create(SVNErrorCode.CANCELLED,
0487: "HTTP proxy authorization cancelled");
0488: SVNURL location = myRepository.getLocation();
0489: ISVNAuthenticationManager authManager = myRepository
0490: .getAuthenticationManager();
0491: ISVNProxyManager proxyManager = authManager != null ? authManager
0492: .getProxyManager(location)
0493: : null;
0494: if (proxyManager != null) {
0495: proxyManager.acknowledgeProxyContext(false, err);
0496: }
0497: close();
0498:
0499: break;
0500: } else if (status.getCode() == HttpURLConnection.HTTP_UNAUTHORIZED) {
0501: Collection authHeaderValues = request
0502: .getResponseHeader().getHeaderValues(
0503: HTTPHeader.AUTHENTICATE_HEADER);
0504: if (authHeaderValues == null
0505: || authHeaderValues.size() == 0) {
0506: err = request.getErrorMessage();
0507: status.setError(SVNErrorMessage.create(
0508: SVNErrorCode.RA_DAV_REQUEST_FAILED, err
0509: .getMessageTemplate(), err
0510: .getRelatedObjects()));
0511: if ("LOCK".equalsIgnoreCase(method)) {
0512: status
0513: .getError()
0514: .setChildErrorMessage(
0515: SVNErrorMessage
0516: .create(
0517: SVNErrorCode.UNSUPPORTED_FEATURE,
0518: "Probably you are trying to lock file in repository that only allows anonymous access"));
0519: }
0520: SVNErrorManager.error(status.getError());
0521: return status;
0522: }
0523:
0524: //we should work around a situation when a server
0525: //does not support Basic authentication while we're
0526: //forcing it, credentials should not be immediately
0527: //thrown away
0528: boolean skip = false;
0529: isAuthForced = myRepository.getAuthenticationManager() != null ? myRepository
0530: .getAuthenticationManager()
0531: .isAuthenticationForced()
0532: : false;
0533: if (isAuthForced) {
0534: if (httpAuth != null
0535: && myChallengeCredentials != null
0536: && !HTTPAuthentication
0537: .isSchemeSupportedByServer(
0538: myChallengeCredentials
0539: .getAuthenticationScheme(),
0540: authHeaderValues)) {
0541: skip = true;
0542: }
0543: }
0544:
0545: try {
0546: myChallengeCredentials = HTTPAuthentication
0547: .parseAuthParameters(authHeaderValues,
0548: myChallengeCredentials, myCharset);
0549: } catch (SVNException svne) {
0550: err = svne.getErrorMessage();
0551: break;
0552: }
0553:
0554: myChallengeCredentials.setChallengeParameter(
0555: "methodname", method);
0556: myChallengeCredentials.setChallengeParameter("uri",
0557: path);
0558:
0559: if (skip) {
0560: close();
0561: continue;
0562: }
0563:
0564: if (myChallengeCredentials instanceof HTTPNTLMAuthentication) {
0565: HTTPNTLMAuthentication ntlmAuth = (HTTPNTLMAuthentication) myChallengeCredentials;
0566: if (ntlmAuth.isInType3State()) {
0567: continue;
0568: }
0569: } else if (myChallengeCredentials instanceof HTTPDigestAuthentication) {
0570: // continue (retry once) if previous request was acceppted?
0571: if (myLastValidAuth != null) {
0572: myLastValidAuth = null;
0573: continue;
0574: }
0575: }
0576:
0577: myLastValidAuth = null;
0578: close();
0579:
0580: ISVNAuthenticationManager authManager = myRepository
0581: .getAuthenticationManager();
0582: if (authManager == null) {
0583: err = request.getErrorMessage();
0584: break;
0585: }
0586:
0587: realm = myChallengeCredentials
0588: .getChallengeParameter("realm");
0589: realm = realm == null ? "" : " " + realm;
0590: realm = "<" + myHost.getProtocol() + "://"
0591: + myHost.getHost() + ":" + myHost.getPort()
0592: + ">" + realm;
0593: if (httpAuth == null) {
0594: httpAuth = authManager.getFirstAuthentication(
0595: ISVNAuthenticationManager.PASSWORD, realm,
0596: myRepository.getLocation());
0597: } else {
0598: authManager.acknowledgeAuthentication(false,
0599: ISVNAuthenticationManager.PASSWORD, realm,
0600: request.getErrorMessage(), httpAuth);
0601: httpAuth = authManager.getNextAuthentication(
0602: ISVNAuthenticationManager.PASSWORD, realm,
0603: myRepository.getLocation());
0604: }
0605: if (httpAuth == null) {
0606: err = SVNErrorMessage.create(
0607: SVNErrorCode.CANCELLED,
0608: "HTTP authorization cancelled");
0609: break;
0610: }
0611: myChallengeCredentials
0612: .setCredentials((SVNPasswordAuthentication) httpAuth);
0613: continue;
0614: } else if (status.getCode() == HttpURLConnection.HTTP_MOVED_PERM
0615: || status.getCode() == HttpURLConnection.HTTP_MOVED_TEMP) {
0616: close();
0617: String newLocation = request
0618: .getResponseHeader()
0619: .getFirstHeaderValue(HTTPHeader.LOCATION_HEADER);
0620: if (newLocation == null) {
0621: err = request.getErrorMessage();
0622: break;
0623: }
0624: int hostIndex = newLocation.indexOf("://");
0625: if (hostIndex > 0) {
0626: hostIndex += 3;
0627: hostIndex = newLocation.indexOf("/", hostIndex);
0628: }
0629: if (hostIndex > 0 && hostIndex < newLocation.length()) {
0630: String newPath = newLocation.substring(hostIndex);
0631: if (newPath.endsWith("/")
0632: && !newPath.endsWith("//")
0633: && !path.endsWith("/")
0634: && newPath.substring(0,
0635: newPath.length() - 1).equals(path)) {
0636: path += "//";
0637: continue;
0638: }
0639: }
0640: err = request.getErrorMessage();
0641: } else if (request.getErrorMessage() != null) {
0642: err = request.getErrorMessage();
0643: }
0644: if (err != null) {
0645: break;
0646: }
0647:
0648: if (myIsProxied) {
0649: SVNURL location = myRepository.getLocation();
0650: ISVNAuthenticationManager authManager = myRepository
0651: .getAuthenticationManager();
0652: ISVNProxyManager proxyManager = authManager != null ? authManager
0653: .getProxyManager(location)
0654: : null;
0655: if (proxyManager != null) {
0656: proxyManager.acknowledgeProxyContext(true, null);
0657: }
0658: }
0659:
0660: if (httpAuth != null && realm != null
0661: && myRepository.getAuthenticationManager() != null) {
0662: myRepository.getAuthenticationManager()
0663: .acknowledgeAuthentication(true,
0664: ISVNAuthenticationManager.PASSWORD,
0665: realm, null, httpAuth);
0666: }
0667: myLastValidAuth = httpAuth;
0668: status.setHeader(request.getResponseHeader());
0669: return status;
0670: }
0671: // force close on error that was not processed before.
0672: // these are errors that has no relation to http status (processing error or cancellation).
0673: close();
0674: if (err != null
0675: && err.getErrorCode().getCategory() != SVNErrorCode.RA_DAV_CATEGORY
0676: && err.getErrorCode() != SVNErrorCode.UNSUPPORTED_FEATURE) {
0677: SVNErrorManager.error(err);
0678: }
0679: // err2 is another default context...
0680: myRepository.getDebugLog().info(err.getMessage());
0681: myRepository.getDebugLog().info(new Exception());
0682:
0683: SVNErrorMessage err2 = SVNErrorMessage.create(
0684: SVNErrorCode.RA_DAV_REQUEST_FAILED,
0685: "{0} request failed on ''{1}''", new Object[] { method,
0686: path });
0687: SVNErrorManager.error(err2, err);
0688: return null;
0689: }
0690:
0691: private ISVNSSLManager promptSSLClientCertificate(
0692: boolean firstAuth, boolean onError) throws SVNException {
0693: SVNURL location = myRepository.getLocation();
0694: ISVNAuthenticationManager authManager = myRepository
0695: .getAuthenticationManager();
0696: ISVNSSLManager sslManager = null;
0697: SVNSSLAuthentication sslAuth = null;
0698: String sslRealm = "<" + location.getProtocol() + "://"
0699: + location.getHost() + ":" + location.getPort() + ">";
0700: if (myIsSecured) {
0701: sslManager = authManager != null ? authManager
0702: .getSSLManager(location) : null;
0703: }
0704: if (authManager != null
0705: && sslManager != null
0706: && (onError || sslManager.isClientCertPromptRequired() || (firstAuth && sslManager
0707: .getClientCertLoadingError() != null))) {
0708: // prompt if there is error or prompt has been forced.
0709: while (true) {
0710: if (firstAuth) {
0711: sslAuth = (SVNSSLAuthentication) authManager
0712: .getFirstAuthentication(
0713: ISVNAuthenticationManager.SSL,
0714: sslRealm, location);
0715: } else {
0716: sslAuth = (SVNSSLAuthentication) authManager
0717: .getNextAuthentication(
0718: ISVNAuthenticationManager.SSL,
0719: sslRealm, location);
0720: }
0721: if (sslAuth == null) {
0722: SVNErrorManager
0723: .cancel("SSL authentication with client certificate cancelled");
0724: }
0725: // this will set error.
0726: sslManager.setClientAuthentication(sslAuth);
0727: if (sslManager.getClientCertLoadingError() != null) {
0728: sslManager
0729: .acknowledgeSSLContext(
0730: false,
0731: SVNErrorMessage
0732: .create(
0733: SVNErrorCode.RA_NOT_AUTHORIZED,
0734: sslManager
0735: .getClientCertLoadingError()
0736: .getMessage()));
0737: // prompt again.
0738: continue;
0739: }
0740: break;
0741: }
0742: }
0743: return sslManager;
0744: }
0745:
0746: public SVNErrorMessage readData(HTTPRequest request,
0747: OutputStream dst) throws IOException {
0748: InputStream stream = createInputStream(request
0749: .getResponseHeader(), getInputStream());
0750: byte[] buffer = getBuffer();
0751: boolean willCloseConnection = false;
0752: try {
0753: while (true) {
0754: int count = stream.read(buffer);
0755: if (count <= 0) {
0756: break;
0757: }
0758: if (dst != null) {
0759: dst.write(buffer, 0, count);
0760: }
0761: }
0762: } catch (IOException e) {
0763: willCloseConnection = true;
0764: if (e.getCause() instanceof SVNException) {
0765: return ((SVNException) e.getCause()).getErrorMessage();
0766: }
0767: throw e;
0768: } finally {
0769: if (!willCloseConnection) {
0770: SVNFileUtil.closeFile(stream);
0771: }
0772: myRepository.getDebugLog().flushStream(stream);
0773: }
0774: return null;
0775: }
0776:
0777: public SVNErrorMessage readData(HTTPRequest request, String method,
0778: String path, DefaultHandler handler) throws IOException {
0779: InputStream is = null;
0780: SpoolFile tmpFile = null;
0781: SVNErrorMessage err = null;
0782: try {
0783: if (myIsSpoolResponse || myIsSpoolAll) {
0784: OutputStream dst = null;
0785: try {
0786: tmpFile = new SpoolFile(mySpoolDirectory);
0787: dst = tmpFile.openForWriting();
0788: dst = new SVNCancellableOutputStream(dst,
0789: myRepository.getCanceller());
0790: // this will exhaust http stream anyway.
0791: err = readData(request, dst);
0792: SVNFileUtil.closeFile(dst);
0793: dst = null;
0794: if (err != null) {
0795: return err;
0796: }
0797: // this stream always have to be closed.
0798: is = tmpFile.openForReading();
0799: } finally {
0800: SVNFileUtil.closeFile(dst);
0801: }
0802: } else {
0803: is = createInputStream(request.getResponseHeader(),
0804: getInputStream());
0805: }
0806: // this will not close is stream.
0807: err = readData(is, method, path, handler);
0808: } catch (IOException e) {
0809: throw e;
0810: } finally {
0811: if (myIsSpoolResponse || myIsSpoolAll) {
0812: // always close spooled stream.
0813: SVNFileUtil.closeFile(is);
0814: } else if (err == null
0815: && !hasToCloseConnection(request
0816: .getResponseHeader())) {
0817: // exhaust stream if connection is not about to be closed.
0818: SVNFileUtil.closeFile(is);
0819: }
0820: if (tmpFile != null) {
0821: try {
0822: tmpFile.delete();
0823: } catch (SVNException e) {
0824: throw new IOException(e.getMessage());
0825: }
0826: }
0827: myIsSpoolResponse = false;
0828: }
0829: return err;
0830: }
0831:
0832: private SVNErrorMessage readData(InputStream is, String method,
0833: String path, DefaultHandler handler)
0834: throws FactoryConfigurationError,
0835: UnsupportedEncodingException, IOException {
0836: try {
0837: if (mySAXParser == null) {
0838: mySAXParser = getSAXParserFactory().newSAXParser();
0839: }
0840: XMLReader reader = new XMLReader(is);
0841: while (!reader.isClosed()) {
0842: org.xml.sax.XMLReader xmlReader = mySAXParser
0843: .getXMLReader();
0844: xmlReader.setContentHandler(handler);
0845: xmlReader.setDTDHandler(handler);
0846: xmlReader.setErrorHandler(handler);
0847: xmlReader.setEntityResolver(NO_ENTITY_RESOLVER);
0848: xmlReader.parse(new InputSource(reader));
0849: }
0850: } catch (SAXException e) {
0851: if (e instanceof SAXParseException) {
0852: if (handler instanceof DAVErrorHandler) {
0853: // failed to read svn-specific error, return null.
0854: return null;
0855: }
0856: } else if (e.getException() instanceof SVNException) {
0857: return ((SVNException) e.getException())
0858: .getErrorMessage();
0859: } else if (e.getCause() instanceof SVNException) {
0860: return ((SVNException) e.getCause()).getErrorMessage();
0861: }
0862: return SVNErrorMessage
0863: .create(
0864: SVNErrorCode.RA_DAV_REQUEST_FAILED,
0865: "Processing {0} request response failed: {1} ({2}) ",
0866: new Object[] { method, e.getMessage(), path });
0867: } catch (ParserConfigurationException e) {
0868: return SVNErrorMessage
0869: .create(
0870: SVNErrorCode.RA_DAV_REQUEST_FAILED,
0871: "XML parser configuration error while processing {0} request response: {1} ({2}) ",
0872: new Object[] { method, e.getMessage(), path });
0873: } catch (EOFException e) {
0874: // skip it.
0875: } finally {
0876: if (mySAXParser != null) {
0877: // to avoid memory leaks when connection is cached.
0878: org.xml.sax.XMLReader xmlReader = null;
0879: try {
0880: xmlReader = mySAXParser.getXMLReader();
0881: } catch (SAXException e) {
0882: }
0883: if (xmlReader != null) {
0884: xmlReader.setContentHandler(DEFAULT_SAX_HANDLER);
0885: xmlReader.setDTDHandler(DEFAULT_SAX_HANDLER);
0886: xmlReader.setErrorHandler(DEFAULT_SAX_HANDLER);
0887: xmlReader.setEntityResolver(NO_ENTITY_RESOLVER);
0888: }
0889: }
0890: myRepository.getDebugLog().flushStream(is);
0891: }
0892: return null;
0893: }
0894:
0895: public void skipData(HTTPRequest request) throws IOException {
0896: if (hasToCloseConnection(request.getResponseHeader())) {
0897: return;
0898: }
0899: InputStream is = createInputStream(request.getResponseHeader(),
0900: getInputStream());
0901: while (is.skip(2048) > 0)
0902: ;
0903: }
0904:
0905: public void close() {
0906: if (mySocket != null) {
0907: if (myInputStream != null) {
0908: try {
0909: myInputStream.close();
0910: } catch (IOException e) {
0911: }
0912: }
0913: if (myOutputStream != null) {
0914: try {
0915: myOutputStream.flush();
0916: } catch (IOException e) {
0917: }
0918: }
0919: if (myOutputStream != null) {
0920: try {
0921: myOutputStream.close();
0922: } catch (IOException e) {
0923: }
0924: }
0925: try {
0926: mySocket.close();
0927: } catch (IOException e) {
0928: }
0929: mySocket = null;
0930: myOutputStream = null;
0931: myInputStream = null;
0932: }
0933: }
0934:
0935: private byte[] getBuffer() {
0936: if (myBuffer == null) {
0937: myBuffer = new byte[32 * 1024];
0938: }
0939: return myBuffer;
0940: }
0941:
0942: private InputStream getInputStream() throws IOException {
0943: if (myInputStream == null) {
0944: if (mySocket == null) {
0945: return null;
0946: }
0947: myInputStream = new BufferedInputStream(mySocket
0948: .getInputStream(), 2048);
0949: }
0950: return myInputStream;
0951: }
0952:
0953: private OutputStream getOutputStream() throws IOException {
0954: if (myOutputStream == null) {
0955: if (mySocket == null) {
0956: return null;
0957: }
0958: myOutputStream = new BufferedOutputStream(mySocket
0959: .getOutputStream(), 2048);
0960: myOutputStream = myRepository.getDebugLog()
0961: .createLogStream(myOutputStream);
0962: }
0963: return myOutputStream;
0964: }
0965:
0966: private void finishResponse(HTTPRequest request) {
0967: if (myOutputStream != null) {
0968: try {
0969: myOutputStream.flush();
0970: } catch (IOException ex) {
0971: }
0972: }
0973: HTTPHeader header = request != null ? request
0974: .getResponseHeader() : null;
0975: if (hasToCloseConnection(header)) {
0976: close();
0977: }
0978: }
0979:
0980: private static boolean hasToCloseConnection(HTTPHeader header) {
0981: if (header == null
0982: || "close"
0983: .equalsIgnoreCase(header
0984: .getFirstHeaderValue(HTTPHeader.CONNECTION_HEADER))
0985: || "close"
0986: .equalsIgnoreCase(header
0987: .getFirstHeaderValue(HTTPHeader.PROXY_CONNECTION_HEADER))) {
0988: return true;
0989: }
0990: return false;
0991: }
0992:
0993: private InputStream createInputStream(HTTPHeader readHeader,
0994: InputStream is) throws IOException {
0995: if ("chunked"
0996: .equalsIgnoreCase(readHeader
0997: .getFirstHeaderValue(HTTPHeader.TRANSFER_ENCODING_HEADER))) {
0998: is = new ChunkedInputStream(is, myCharset);
0999: } else if (readHeader
1000: .getFirstHeaderValue(HTTPHeader.CONTENT_LENGTH_HEADER) != null) {
1001: is = new FixedSizeInputStream(is, Long.parseLong(readHeader
1002: .getFirstHeaderValue(
1003: HTTPHeader.CONTENT_LENGTH_HEADER)
1004: .toString()));
1005: } else if (!hasToCloseConnection(readHeader)) {
1006: // no content length and no valid transfer-encoding!
1007: // consider as empty response.
1008:
1009: // but only when there is no "Connection: close" or "Proxy-Connection: close" header,
1010: // in that case just return "is".
1011: // skipData will not read that as it should also analyze "close" instruction.
1012:
1013: // return empty stream.
1014: // and force connection close? (not to read garbage on the next request).
1015: is = new FixedSizeInputStream(is, 0);
1016: // this will force connection to close.
1017: readHeader.setHeaderValue(HTTPHeader.CONNECTION_HEADER,
1018: "close");
1019: }
1020:
1021: if ("gzip"
1022: .equals(readHeader
1023: .getFirstHeaderValue(HTTPHeader.CONTENT_ENCODING_HEADER))) {
1024: is = new GZIPInputStream(is);
1025: }
1026: return myRepository.getDebugLog().createLogStream(is);
1027: }
1028:
1029: private static synchronized SAXParserFactory getSAXParserFactory()
1030: throws FactoryConfigurationError {
1031: if (ourSAXParserFactory == null) {
1032: ourSAXParserFactory = SAXParserFactory.newInstance();
1033: try {
1034: ourSAXParserFactory.setFeature(
1035: "http://xml.org/sax/features/namespaces", true);
1036: } catch (SAXNotRecognizedException e) {
1037: } catch (SAXNotSupportedException e) {
1038: } catch (ParserConfigurationException e) {
1039: }
1040: try {
1041: ourSAXParserFactory
1042: .setFeature(
1043: "http://xml.org/sax/features/validation",
1044: false);
1045: } catch (SAXNotRecognizedException e) {
1046: } catch (SAXNotSupportedException e) {
1047: } catch (ParserConfigurationException e) {
1048: }
1049: try {
1050: ourSAXParserFactory
1051: .setFeature(
1052: "http://apache.org/xml/features/nonvalidating/load-external-dtd",
1053: false);
1054: } catch (SAXNotRecognizedException e) {
1055: } catch (SAXNotSupportedException e) {
1056: } catch (ParserConfigurationException e) {
1057: }
1058: ourSAXParserFactory.setNamespaceAware(true);
1059: ourSAXParserFactory.setValidating(false);
1060: }
1061: return ourSAXParserFactory;
1062: }
1063:
1064: public void setSpoolResponse(boolean spoolResponse) {
1065: myIsSpoolResponse = spoolResponse;
1066: }
1067:
1068: }
|