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 	/**
145 	 * The {@code Location} header.
146 	 * @since 4.7
147 	 */
148 	public static final String HDR_LOCATION = "Location"; //$NON-NLS-1$
149 
150 	/** The {@code gzip} encoding value for {@link #HDR_ACCEPT_ENCODING}. */
151 	public static final String ENCODING_GZIP = "gzip"; //$NON-NLS-1$
152 
153 	/**
154 	 * The {@code x-gzip} encoding value for {@link #HDR_ACCEPT_ENCODING}.
155 	 * @since 4.6
156 	 */
157 	public static final String ENCODING_X_GZIP = "x-gzip"; //$NON-NLS-1$
158 
159 	/** The standard {@code text/plain} MIME type. */
160 	public static final String TEXT_PLAIN = "text/plain"; //$NON-NLS-1$
161 
162 	/** The {@code Authorization} header. */
163 	public static final String HDR_AUTHORIZATION = "Authorization"; //$NON-NLS-1$
164 
165 	/** The {@code WWW-Authenticate} header. */
166 	public static final String HDR_WWW_AUTHENTICATE = "WWW-Authenticate"; //$NON-NLS-1$
167 
168 	/**
169 	 * URL encode a value string into an output buffer.
170 	 *
171 	 * @param urlstr
172 	 *            the output buffer.
173 	 * @param key
174 	 *            value which must be encoded to protected special characters.
175 	 */
176 	public static void encode(final StringBuilder urlstr, final String key) {
177 		if (key == null || key.length() == 0)
178 			return;
179 		try {
180 			urlstr.append(URLEncoder.encode(key, "UTF-8")); //$NON-NLS-1$
181 		} catch (UnsupportedEncodingException e) {
182 			throw new RuntimeException(JGitText.get().couldNotURLEncodeToUTF8, e);
183 		}
184 	}
185 
186 	/**
187 	 * Get the HTTP response code from the request.
188 	 * <p>
189 	 * Roughly the same as <code>c.getResponseCode()</code> but the
190 	 * ConnectException is translated to be more understandable.
191 	 *
192 	 * @param c
193 	 *            connection the code should be obtained from.
194 	 * @return r HTTP status code, usually 200 to indicate success. See
195 	 *         {@link HttpConnection} for other defined constants.
196 	 * @throws IOException
197 	 *             communications error prevented obtaining the response code.
198 	 * @since 3.3
199 	 */
200 	public static int response(final HttpConnection c) throws IOException {
201 		try {
202 			return c.getResponseCode();
203 		} catch (ConnectException ce) {
204 			final URL url = c.getURL();
205 			final String host = (url == null) ? "<null>" : url.getHost(); //$NON-NLS-1$
206 			// The standard J2SE error message is not very useful.
207 			//
208 			if ("Connection timed out: connect".equals(ce.getMessage())) //$NON-NLS-1$
209 				throw new ConnectException(MessageFormat.format(JGitText.get().connectionTimeOut, host));
210 			throw new ConnectException(ce.getMessage() + " " + host); //$NON-NLS-1$
211 		}
212 	}
213 
214 	/**
215 	 * Get the HTTP response code from the request.
216 	 * <p>
217 	 * Roughly the same as <code>c.getResponseCode()</code> but the
218 	 * ConnectException is translated to be more understandable.
219 	 *
220 	 * @param c
221 	 *            connection the code should be obtained from.
222 	 * @return r HTTP status code, usually 200 to indicate success. See
223 	 *         {@link HttpConnection} for other defined constants.
224 	 * @throws IOException
225 	 *             communications error prevented obtaining the response code.
226 	 */
227 	public static int response(final java.net.HttpURLConnection c)
228 			throws IOException {
229 		try {
230 			return c.getResponseCode();
231 		} catch (ConnectException ce) {
232 			final URL url = c.getURL();
233 			final String host = (url == null) ? "<null>" : url.getHost(); //$NON-NLS-1$
234 			// The standard J2SE error message is not very useful.
235 			//
236 			if ("Connection timed out: connect".equals(ce.getMessage())) //$NON-NLS-1$
237 				throw new ConnectException(MessageFormat.format(
238 						JGitText.get().connectionTimeOut, host));
239 			throw new ConnectException(ce.getMessage() + " " + host); //$NON-NLS-1$
240 		}
241 	}
242 
243 	/**
244 	 * Extract a HTTP header from the response.
245 	 *
246 	 * @param c
247 	 *            connection the header should be obtained from.
248 	 * @param headerName
249 	 *            the header name
250 	 * @return the header value
251 	 * @throws IOException
252 	 *             communications error prevented obtaining the header.
253 	 * @since 4.7
254 	 */
255 	public static String responseHeader(final HttpConnection c,
256 			final String headerName) throws IOException {
257 		return c.getHeaderField(headerName);
258 	}
259 
260 	/**
261 	 * Determine the proxy server (if any) needed to obtain a URL.
262 	 *
263 	 * @param proxySelector
264 	 *            proxy support for the caller.
265 	 * @param u
266 	 *            location of the server caller wants to talk to.
267 	 * @return proxy to communicate with the supplied URL.
268 	 * @throws ConnectException
269 	 *             the proxy could not be computed as the supplied URL could not
270 	 *             be read. This failure should never occur.
271 	 */
272 	public static Proxy proxyFor(final ProxySelector proxySelector, final URL u)
273 			throws ConnectException {
274 		try {
275 			return proxySelector.select(u.toURI()).get(0);
276 		} catch (URISyntaxException e) {
277 			final ConnectException err;
278 			err = new ConnectException(MessageFormat.format(JGitText.get().cannotDetermineProxyFor, u));
279 			err.initCause(e);
280 			throw err;
281 		}
282 	}
283 
284 	/**
285 	 * Disable SSL and hostname verification for given HTTP connection
286 	 *
287 	 * @param conn
288 	 * @throws IOException
289 	 * @since 4.3
290 	 */
291 	public static void disableSslVerify(HttpConnection conn)
292 			throws IOException {
293 		final TrustManager[] trustAllCerts = new TrustManager[] {
294 				new DummyX509TrustManager() };
295 		try {
296 			conn.configure(null, trustAllCerts, null);
297 			conn.setHostnameVerifier(new DummyHostnameVerifier());
298 		} catch (KeyManagementException e) {
299 			throw new IOException(e.getMessage());
300 		} catch (NoSuchAlgorithmException e) {
301 			throw new IOException(e.getMessage());
302 		}
303 	}
304 
305 	private static class DummyX509TrustManager implements X509TrustManager {
306 		@Override
307 		public X509Certificate[] getAcceptedIssuers() {
308 			return null;
309 		}
310 
311 		@Override
312 		public void checkClientTrusted(X509Certificate[] certs,
313 				String authType) {
314 			// no check
315 		}
316 
317 		@Override
318 		public void checkServerTrusted(X509Certificate[] certs,
319 				String authType) {
320 			// no check
321 		}
322 	}
323 
324 	private static class DummyHostnameVerifier implements HostnameVerifier {
325 		@Override
326 		public boolean verify(String hostname, SSLSession session) {
327 			// always accept
328 			return true;
329 		}
330 	}
331 
332 	private HttpSupport() {
333 		// Utility class only.
334 	}
335 }