View Javadoc
1   /*
2    * Copyright (C) 2010, Google Inc.
3    * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
4    * and other copyright owners as documented in the project's IP log.
5    *
6    * This program and the accompanying materials are made available
7    * under the terms of the Eclipse Distribution License v1.0 which
8    * accompanies this distribution, is reproduced below, and is
9    * available at http://www.eclipse.org/org/documents/edl-v10.php
10   *
11   * All rights reserved.
12   *
13   * Redistribution and use in source and binary forms, with or
14   * without modification, are permitted provided that the following
15   * conditions are met:
16   *
17   * - Redistributions of source code must retain the above copyright
18   *   notice, this list of conditions and the following disclaimer.
19   *
20   * - Redistributions in binary form must reproduce the above
21   *   copyright notice, this list of conditions and the following
22   *   disclaimer in the documentation and/or other materials provided
23   *   with the distribution.
24   *
25   * - Neither the name of the Eclipse Foundation, Inc. nor the
26   *   names of its contributors may be used to endorse or promote
27   *   products derived from this software without specific prior
28   *   written permission.
29   *
30   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
31   * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
32   * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
33   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
34   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
35   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
36   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
37   * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
38   * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
39   * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
40   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
41   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
42   * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
43   */
44  
45  package org.eclipse.jgit.util;
46  
47  import java.io.IOException;
48  import java.io.UnsupportedEncodingException;
49  import java.net.ConnectException;
50  import java.net.Proxy;
51  import java.net.ProxySelector;
52  import java.net.URISyntaxException;
53  import java.net.URL;
54  import java.net.URLEncoder;
55  import java.security.KeyManagementException;
56  import java.security.NoSuchAlgorithmException;
57  import java.security.cert.X509Certificate;
58  import java.text.MessageFormat;
59  
60  import javax.net.ssl.HostnameVerifier;
61  import javax.net.ssl.SSLSession;
62  import javax.net.ssl.TrustManager;
63  import javax.net.ssl.X509TrustManager;
64  
65  import org.eclipse.jgit.internal.JGitText;
66  import org.eclipse.jgit.transport.http.HttpConnection;
67  
68  /** Extra utilities to support usage of HTTP. */
69  public class HttpSupport {
70  	/** The {@code GET} HTTP method. */
71  	public static final String METHOD_GET = "GET"; //$NON-NLS-1$
72  
73  	/** The {@code HEAD} HTTP method.
74  	 * @since 4.3 */
75  	public static final String METHOD_HEAD = "HEAD"; //$NON-NLS-1$
76  
77  	/** The {@code POST} HTTP method.
78  	 * @since 4.3 */
79  	public static final String METHOD_PUT = "PUT"; //$NON-NLS-1$
80  
81  	/** The {@code POST} HTTP method. */
82  	public static final String METHOD_POST = "POST"; //$NON-NLS-1$
83  
84  	/** The {@code Cache-Control} header. */
85  	public static final String HDR_CACHE_CONTROL = "Cache-Control"; //$NON-NLS-1$
86  
87  	/** The {@code Pragma} header. */
88  	public static final String HDR_PRAGMA = "Pragma"; //$NON-NLS-1$
89  
90  	/** The {@code User-Agent} header. */
91  	public static final String HDR_USER_AGENT = "User-Agent"; //$NON-NLS-1$
92  
93  	/**
94  	 * The {@code Server} header.
95  	 * @since 4.0
96  	 */
97  	public static final String HDR_SERVER = "Server"; //$NON-NLS-1$
98  
99  	/** The {@code Date} header. */
100 	public static final String HDR_DATE = "Date"; //$NON-NLS-1$
101 
102 	/** The {@code Expires} header. */
103 	public static final String HDR_EXPIRES = "Expires"; //$NON-NLS-1$
104 
105 	/** The {@code ETag} header. */
106 	public static final String HDR_ETAG = "ETag"; //$NON-NLS-1$
107 
108 	/** The {@code If-None-Match} header. */
109 	public static final String HDR_IF_NONE_MATCH = "If-None-Match"; //$NON-NLS-1$
110 
111 	/** The {@code Last-Modified} header. */
112 	public static final String HDR_LAST_MODIFIED = "Last-Modified"; //$NON-NLS-1$
113 
114 	/** The {@code If-Modified-Since} header. */
115 	public static final String HDR_IF_MODIFIED_SINCE = "If-Modified-Since"; //$NON-NLS-1$
116 
117 	/** The {@code Accept} header. */
118 	public static final String HDR_ACCEPT = "Accept"; //$NON-NLS-1$
119 
120 	/** The {@code Content-Type} header. */
121 	public static final String HDR_CONTENT_TYPE = "Content-Type"; //$NON-NLS-1$
122 
123 	/** The {@code Content-Length} header. */
124 	public static final String HDR_CONTENT_LENGTH = "Content-Length"; //$NON-NLS-1$
125 
126 	/** The {@code Content-Encoding} header. */
127 	public static final String HDR_CONTENT_ENCODING = "Content-Encoding"; //$NON-NLS-1$
128 
129 	/** The {@code Content-Range} header. */
130 	public static final String HDR_CONTENT_RANGE = "Content-Range"; //$NON-NLS-1$
131 
132 	/** The {@code Accept-Ranges} header. */
133 	public static final String HDR_ACCEPT_RANGES = "Accept-Ranges"; //$NON-NLS-1$
134 
135 	/** The {@code If-Range} header. */
136 	public static final String HDR_IF_RANGE = "If-Range"; //$NON-NLS-1$
137 
138 	/** The {@code Range} header. */
139 	public static final String HDR_RANGE = "Range"; //$NON-NLS-1$
140 
141 	/** The {@code Accept-Encoding} header. */
142 	public static final String HDR_ACCEPT_ENCODING = "Accept-Encoding"; //$NON-NLS-1$
143 
144 	/** The {@code gzip} encoding value for {@link #HDR_ACCEPT_ENCODING}. */
145 	public static final String ENCODING_GZIP = "gzip"; //$NON-NLS-1$
146 
147 	/** The standard {@code text/plain} MIME type. */
148 	public static final String TEXT_PLAIN = "text/plain"; //$NON-NLS-1$
149 
150 	/** The {@code Authorization} header. */
151 	public static final String HDR_AUTHORIZATION = "Authorization"; //$NON-NLS-1$
152 
153 	/** The {@code WWW-Authenticate} header. */
154 	public static final String HDR_WWW_AUTHENTICATE = "WWW-Authenticate"; //$NON-NLS-1$
155 
156 	/**
157 	 * URL encode a value string into an output buffer.
158 	 *
159 	 * @param urlstr
160 	 *            the output buffer.
161 	 * @param key
162 	 *            value which must be encoded to protected special characters.
163 	 */
164 	public static void encode(final StringBuilder urlstr, final String key) {
165 		if (key == null || key.length() == 0)
166 			return;
167 		try {
168 			urlstr.append(URLEncoder.encode(key, "UTF-8")); //$NON-NLS-1$
169 		} catch (UnsupportedEncodingException e) {
170 			throw new RuntimeException(JGitText.get().couldNotURLEncodeToUTF8, e);
171 		}
172 	}
173 
174 	/**
175 	 * Get the HTTP response code from the request.
176 	 * <p>
177 	 * Roughly the same as <code>c.getResponseCode()</code> but the
178 	 * ConnectException is translated to be more understandable.
179 	 *
180 	 * @param c
181 	 *            connection the code should be obtained from.
182 	 * @return r HTTP status code, usually 200 to indicate success. See
183 	 *         {@link HttpConnection} for other defined constants.
184 	 * @throws IOException
185 	 *             communications error prevented obtaining the response code.
186 	 * @since 3.3
187 	 */
188 	public static int response(final HttpConnection c) throws IOException {
189 		try {
190 			return c.getResponseCode();
191 		} catch (ConnectException ce) {
192 			final URL url = c.getURL();
193 			final String host = (url == null) ? "<null>" : url.getHost(); //$NON-NLS-1$
194 			// The standard J2SE error message is not very useful.
195 			//
196 			if ("Connection timed out: connect".equals(ce.getMessage())) //$NON-NLS-1$
197 				throw new ConnectException(MessageFormat.format(JGitText.get().connectionTimeOut, host));
198 			throw new ConnectException(ce.getMessage() + " " + host); //$NON-NLS-1$
199 		}
200 	}
201 
202 	/**
203 	 * Get the HTTP response code from the request.
204 	 * <p>
205 	 * Roughly the same as <code>c.getResponseCode()</code> but the
206 	 * ConnectException is translated to be more understandable.
207 	 *
208 	 * @param c
209 	 *            connection the code should be obtained from.
210 	 * @return r HTTP status code, usually 200 to indicate success. See
211 	 *         {@link HttpConnection} for other defined constants.
212 	 * @throws IOException
213 	 *             communications error prevented obtaining the response code.
214 	 */
215 	public static int response(final java.net.HttpURLConnection c)
216 			throws IOException {
217 		try {
218 			return c.getResponseCode();
219 		} catch (ConnectException ce) {
220 			final URL url = c.getURL();
221 			final String host = (url == null) ? "<null>" : url.getHost(); //$NON-NLS-1$
222 			// The standard J2SE error message is not very useful.
223 			//
224 			if ("Connection timed out: connect".equals(ce.getMessage())) //$NON-NLS-1$
225 				throw new ConnectException(MessageFormat.format(
226 						JGitText.get().connectionTimeOut, host));
227 			throw new ConnectException(ce.getMessage() + " " + host); //$NON-NLS-1$
228 		}
229 	}
230 
231 	/**
232 	 * Determine the proxy server (if any) needed to obtain a URL.
233 	 *
234 	 * @param proxySelector
235 	 *            proxy support for the caller.
236 	 * @param u
237 	 *            location of the server caller wants to talk to.
238 	 * @return proxy to communicate with the supplied URL.
239 	 * @throws ConnectException
240 	 *             the proxy could not be computed as the supplied URL could not
241 	 *             be read. This failure should never occur.
242 	 */
243 	public static Proxy proxyFor(final ProxySelector proxySelector, final URL u)
244 			throws ConnectException {
245 		try {
246 			return proxySelector.select(u.toURI()).get(0);
247 		} catch (URISyntaxException e) {
248 			final ConnectException err;
249 			err = new ConnectException(MessageFormat.format(JGitText.get().cannotDetermineProxyFor, u));
250 			err.initCause(e);
251 			throw err;
252 		}
253 	}
254 
255 	/**
256 	 * Disable SSL and hostname verification for given HTTP connection
257 	 *
258 	 * @param conn
259 	 * @throws IOException
260 	 * @since 4.3
261 	 */
262 	public static void disableSslVerify(HttpConnection conn)
263 			throws IOException {
264 		final TrustManager[] trustAllCerts = new TrustManager[] {
265 				new DummyX509TrustManager() };
266 		try {
267 			conn.configure(null, trustAllCerts, null);
268 			conn.setHostnameVerifier(new DummyHostnameVerifier());
269 		} catch (KeyManagementException e) {
270 			throw new IOException(e.getMessage());
271 		} catch (NoSuchAlgorithmException e) {
272 			throw new IOException(e.getMessage());
273 		}
274 	}
275 
276 	private static class DummyX509TrustManager implements X509TrustManager {
277 		public X509Certificate[] getAcceptedIssuers() {
278 			return null;
279 		}
280 
281 		public void checkClientTrusted(X509Certificate[] certs,
282 				String authType) {
283 			// no check
284 		}
285 
286 		public void checkServerTrusted(X509Certificate[] certs,
287 				String authType) {
288 			// no check
289 		}
290 	}
291 
292 	private static class DummyHostnameVerifier implements HostnameVerifier {
293 		public boolean verify(String hostname, SSLSession session) {
294 			// always accept
295 			return true;
296 		}
297 	}
298 
299 	private HttpSupport() {
300 		// Utility class only.
301 	}
302 }