EMMA Coverage Report (generated Wed Oct 26 13:47:17 EDT 2011)
[all classes][org.jclouds.http]

COVERAGE SUMMARY FOR SOURCE FILE [HttpUtils.java]

nameclass, %method, %block, %line, %
HttpUtils.java100% (1/1)52%  (17/33)40%  (444/1103)43%  (68.1/160)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class HttpUtils100% (1/1)52%  (17/33)40%  (444/1103)43%  (68.1/160)
attemptToParseSizeAndRangeFromHeaders (HttpMessage): Long 0%   (0/1)0%   (0/28)0%   (0/6)
copy (ContentMetadata, MutableContentMetadata): void 0%   (0/1)0%   (0/25)0%   (0/7)
createBaseEndpointFor (URI): URI 0%   (0/1)0%   (0/42)0%   (0/3)
getContentHeadersFromMetadata (ContentMetadata): Multimap 0%   (0/1)0%   (0/67)0%   (0/14)
getMaxConnectionsPerHost (): int 0%   (0/1)0%   (0/3)0%   (0/1)
getProxyPassword (): String 0%   (0/1)0%   (0/3)0%   (0/1)
getProxyPort (): Integer 0%   (0/1)0%   (0/3)0%   (0/1)
getProxyUser (): String 0%   (0/1)0%   (0/3)0%   (0/1)
logMessage (Logger, HttpMessage, String): void 0%   (0/1)0%   (0/203)0%   (0/20)
parseEndPoint (String): URI 0%   (0/1)0%   (0/83)0%   (0/8)
relaxHostname (): boolean 0%   (0/1)0%   (0/3)0%   (0/1)
replaceHostInEndPoint (URI, String): URI 0%   (0/1)0%   (0/8)0%   (0/1)
trustAllCerts (): boolean 0%   (0/1)0%   (0/3)0%   (0/1)
valueOrEmpty (Collection): String 0%   (0/1)0%   (0/13)0%   (0/1)
valueOrEmpty (String): String 0%   (0/1)0%   (0/6)0%   (0/1)
valueOrEmpty (byte []): String 0%   (0/1)0%   (0/7)0%   (0/1)
logRequest (Logger, HttpRequest, String): void 100% (1/1)17%  (4/24)50%  (2/4)
logResponse (Logger, HttpResponse, String): void 100% (1/1)17%  (4/24)50%  (2/4)
toByteArrayOrNull (PayloadEnclosing): byte [] 100% (1/1)18%  (5/28)25%  (2/8)
wirePayloadIfEnabled (Wire, HttpMessage): void 100% (1/1)20%  (4/20)38%  (1.5/4)
closeClientButKeepContentStream (PayloadEnclosing): byte [] 100% (1/1)26%  (7/27)32%  (2.6/8)
checkRequestHasContentLengthOrChunkedEncoding (HttpMessage, String): void 100% (1/1)64%  (14/22)88%  (2.6/3)
createUri (String): URI 100% (1/1)82%  (158/193)81%  (21/26)
HttpUtils (int, int, int, int): void 100% (1/1)86%  (24/28)98%  (7.8/8)
checkRequestHasRequiredProperties (HttpRequest): void 100% (1/1)91%  (128/141)94%  (7.6/8)
getConnectionTimeout (): int 100% (1/1)100% (3/3)100% (1/1)
getMaxConnections (): int 100% (1/1)100% (3/3)100% (1/1)
getProxyHost (): String 100% (1/1)100% (3/3)100% (1/1)
getSocketOpenTimeout (): int 100% (1/1)100% (3/3)100% (1/1)
releasePayload (HttpMessage): void 100% (1/1)100% (7/7)100% (3/3)
returnValueOnCodeOrNull (Exception, Object, Predicate): Object 100% (1/1)100% (29/29)100% (4/4)
sortAndConcatHeadersIntoString (Multimap): String 100% (1/1)100% (45/45)100% (7/7)
useSystemProxies (): boolean 100% (1/1)100% (3/3)100% (1/1)

1/**
2 * Licensed to jclouds, Inc. (jclouds) under one or more
3 * contributor license agreements.  See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership.  jclouds licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License.  You may obtain a copy of the License at
9 *
10 *   http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied.  See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19package org.jclouds.http;
20 
21import static com.google.common.base.Preconditions.checkArgument;
22import static com.google.common.base.Preconditions.checkState;
23import static com.google.common.base.Throwables.getCausalChain;
24import static com.google.common.base.Throwables.propagate;
25import static com.google.common.collect.Iterables.filter;
26import static com.google.common.collect.Iterables.get;
27import static com.google.common.collect.Iterables.size;
28import static com.google.common.collect.Lists.newArrayList;
29import static com.google.common.io.ByteStreams.toByteArray;
30import static com.google.common.io.Closeables.closeQuietly;
31import static javax.ws.rs.core.HttpHeaders.CONTENT_ENCODING;
32import static javax.ws.rs.core.HttpHeaders.CONTENT_LANGUAGE;
33import static javax.ws.rs.core.HttpHeaders.CONTENT_LENGTH;
34import static javax.ws.rs.core.HttpHeaders.CONTENT_TYPE;
35import static org.jclouds.util.Patterns.PATTERN_THAT_BREAKS_URI;
36import static org.jclouds.util.Patterns.URI_PATTERN;
37 
38import java.io.IOException;
39import java.io.InputStream;
40import java.net.URI;
41import java.util.Collection;
42import java.util.List;
43import java.util.Map.Entry;
44import java.util.regex.Matcher;
45 
46import javax.inject.Named;
47import javax.inject.Singleton;
48import javax.ws.rs.core.HttpHeaders;
49 
50import org.jclouds.Constants;
51import org.jclouds.crypto.CryptoStreams;
52import org.jclouds.io.ContentMetadata;
53import org.jclouds.io.InputSuppliers;
54import org.jclouds.io.MutableContentMetadata;
55import org.jclouds.io.Payload;
56import org.jclouds.io.PayloadEnclosing;
57import org.jclouds.io.Payloads;
58import org.jclouds.logging.Logger;
59import org.jclouds.logging.internal.Wire;
60import org.jclouds.util.Strings2;
61 
62import com.google.common.base.Joiner;
63import com.google.common.base.Predicate;
64import com.google.common.base.Splitter;
65import com.google.common.collect.ImmutableMultimap;
66import com.google.common.collect.ImmutableMultimap.Builder;
67import com.google.common.collect.Multimap;
68import com.google.common.collect.SortedSetMultimap;
69import com.google.common.collect.TreeMultimap;
70import com.google.inject.Inject;
71 
72/**
73 * @author Adrian Cole
74 */
75@Singleton
76public class HttpUtils {
77 
78   @Inject(optional = true)
79   @Named(Constants.PROPERTY_RELAX_HOSTNAME)
80   private boolean relaxHostname = false;
81 
82   @Inject(optional = true)
83   @Named(Constants.PROPERTY_PROXY_SYSTEM)
84   private boolean systemProxies = System.getProperty("java.net.useSystemProxies") != null ? Boolean
85         .parseBoolean(System.getProperty("java.net.useSystemProxies")) : false;
86 
87   private final int globalMaxConnections;
88   private final int globalMaxConnectionsPerHost;
89   private final int connectionTimeout;
90   private final int soTimeout;
91   @Inject(optional = true)
92   @Named(Constants.PROPERTY_PROXY_HOST)
93   private String proxyHost;
94   @Inject(optional = true)
95   @Named(Constants.PROPERTY_PROXY_PORT)
96   private Integer proxyPort;
97   @Inject(optional = true)
98   @Named(Constants.PROPERTY_PROXY_USER)
99   private String proxyUser;
100   @Inject(optional = true)
101   @Named(Constants.PROPERTY_PROXY_PASSWORD)
102   private String proxyPassword;
103   @Inject(optional = true)
104   @Named(Constants.PROPERTY_TRUST_ALL_CERTS)
105   private boolean trustAllCerts;
106 
107   @Inject
108   public HttpUtils(@Named(Constants.PROPERTY_CONNECTION_TIMEOUT) int connectionTimeout,
109         @Named(Constants.PROPERTY_SO_TIMEOUT) int soTimeout,
110         @Named(Constants.PROPERTY_MAX_CONNECTIONS_PER_CONTEXT) int globalMaxConnections,
111         @Named(Constants.PROPERTY_MAX_CONNECTIONS_PER_HOST) int globalMaxConnectionsPerHost) {
112      this.soTimeout = soTimeout;
113      this.connectionTimeout = connectionTimeout;
114      this.globalMaxConnections = globalMaxConnections;
115      this.globalMaxConnectionsPerHost = globalMaxConnectionsPerHost;
116   }
117 
118   /**
119    * @see org.jclouds.Constants.PROPERTY_PROXY_HOST
120    */
121   public String getProxyHost() {
122      return proxyHost;
123   }
124 
125   /**
126    * @see org.jclouds.Constants.PROPERTY_PROXY_PORT
127    */
128   public Integer getProxyPort() {
129      return proxyPort;
130   }
131 
132   /**
133    * @see org.jclouds.Constants.PROPERTY_PROXY_USER
134    */
135   public String getProxyUser() {
136      return proxyUser;
137   }
138 
139   /**
140    * @see org.jclouds.Constants.PROPERTY_PROXY_PASSWORD
141    */
142   public String getProxyPassword() {
143      return proxyPassword;
144   }
145 
146   public int getSocketOpenTimeout() {
147      return soTimeout;
148   }
149 
150   public int getConnectionTimeout() {
151      return connectionTimeout;
152   }
153 
154   public boolean relaxHostname() {
155      return relaxHostname;
156   }
157 
158   public boolean trustAllCerts() {
159      return trustAllCerts;
160   }
161 
162   public boolean useSystemProxies() {
163      return systemProxies;
164   }
165 
166   public int getMaxConnections() {
167      return globalMaxConnections;
168   }
169 
170   public int getMaxConnectionsPerHost() {
171      return globalMaxConnectionsPerHost;
172   }
173 
174   /**
175    * keys to the map are only used for socket information, not path. In this case, you should
176    * remove any path or query details from the URI.
177    */
178   public static URI createBaseEndpointFor(URI endpoint) {
179      if (endpoint.getPort() == -1) {
180         return URI.create(String.format("%s://%s", endpoint.getScheme(), endpoint.getHost()));
181      } else {
182         return URI.create(String.format("%s://%s:%d", endpoint.getScheme(), endpoint.getHost(), endpoint.getPort()));
183      }
184   }
185 
186   public static Multimap<String, String> getContentHeadersFromMetadata(ContentMetadata md) {
187       Builder<String, String> builder = ImmutableMultimap.builder();
188      if (md.getContentType() != null)
189         builder.put(HttpHeaders.CONTENT_TYPE, md.getContentType());
190      if (md.getContentDisposition() != null)
191         builder.put("Content-Disposition", md.getContentDisposition());
192      if (md.getContentEncoding() != null)
193         builder.put(HttpHeaders.CONTENT_ENCODING, md.getContentEncoding());
194      if (md.getContentLanguage() != null)
195         builder.put(HttpHeaders.CONTENT_LANGUAGE, md.getContentLanguage());
196      if (md.getContentLength() != null)
197         builder.put(HttpHeaders.CONTENT_LENGTH, md.getContentLength() + "");
198      if (md.getContentMD5() != null)
199         builder.put("Content-MD5", CryptoStreams.base64(md.getContentMD5()));
200      return builder.build();
201   }
202 
203   public static byte[] toByteArrayOrNull(PayloadEnclosing response) {
204      if (response.getPayload() != null) {
205         InputStream input = response.getPayload().getInput();
206         try {
207            return toByteArray(input);
208         } catch (IOException e) {
209            propagate(e);
210         } finally {
211            closeQuietly(input);
212         }
213      }
214      return null;
215   }
216 
217   /**
218    * Content stream may need to be read. However, we should always close the http stream.
219    * 
220    * @throws IOException
221    */
222   public static byte[] closeClientButKeepContentStream(PayloadEnclosing response) {
223      byte[] returnVal = toByteArrayOrNull(response);
224      if (returnVal != null && !response.getPayload().isRepeatable()) {
225         Payload newPayload = Payloads.newByteArrayPayload(returnVal);
226         MutableContentMetadata fromMd = response.getPayload().getContentMetadata();
227         MutableContentMetadata toMd = newPayload.getContentMetadata();
228         copy(fromMd, toMd);
229         response.setPayload(newPayload);
230      }
231      return returnVal;
232   }
233 
234   public static void copy(ContentMetadata fromMd, MutableContentMetadata toMd) {
235      toMd.setContentLength(fromMd.getContentLength());
236      toMd.setContentMD5(fromMd.getContentMD5());
237      toMd.setContentType(fromMd.getContentType());
238      toMd.setContentDisposition(fromMd.getContentDisposition());
239      toMd.setContentEncoding(fromMd.getContentEncoding());
240      toMd.setContentLanguage(fromMd.getContentLanguage());
241   }
242 
243   public static URI parseEndPoint(String hostHeader) {
244      URI redirectURI = URI.create(hostHeader);
245      String scheme = redirectURI.getScheme();
246 
247      checkState(redirectURI.getScheme().startsWith("http"),
248            String.format("header %s didn't parse an http scheme: [%s]", hostHeader, scheme));
249      int port = redirectURI.getPort() > 0 ? redirectURI.getPort() : redirectURI.getScheme().equals("https") ? 443 : 80;
250      String host = redirectURI.getHost();
251      checkState(host.indexOf('/') == -1,
252            String.format("header %s didn't parse an http host correctly: [%s]", hostHeader, host));
253      URI endPoint = URI.create(String.format("%s://%s:%d", scheme, host, port));
254      return endPoint;
255   }
256 
257   public static URI replaceHostInEndPoint(URI endPoint, String host) {
258      return URI.create(endPoint.toString().replace(endPoint.getHost(), host));
259   }
260 
261   /**
262    * Used to extract the URI and authentication data from a String. Note that the java URI class
263    * breaks, if there are special characters like '/' present. Otherwise, we wouldn't need this
264    * class, and we could simply use URI.create("uri").getUserData(); Also, URI breaks if there are
265    * curly braces.
266    * 
267    */
268   public static URI createUri(String uriPath) {
269      List<String> onQuery = newArrayList(Splitter.on('?').split(uriPath));
270      if (onQuery.size() == 2) {
271         onQuery.add(Strings2.urlEncode(onQuery.remove(1), '=', '&'));
272         uriPath = Joiner.on('?').join(onQuery);
273      }
274      if (uriPath.indexOf('@') != 1) {
275         List<String> parts = newArrayList(Splitter.on('@').split(uriPath));
276         String path = parts.remove(parts.size() - 1);
277         if (parts.size() > 1) {
278            parts = newArrayList(Strings2.urlEncode(Joiner.on('@').join(parts), '/', ':'));
279         }
280         parts.add(Strings2.urlEncode(path, '/', ':'));
281         uriPath = Joiner.on('@').join(parts);
282      } else {
283         List<String> parts = newArrayList(Splitter.on('/').split(uriPath));
284         String path = parts.remove(parts.size() - 1);
285         parts.add(Strings2.urlEncode(path, ':'));
286         uriPath = Joiner.on('/').join(parts);
287      }
288 
289      if (PATTERN_THAT_BREAKS_URI.matcher(uriPath).matches()) {
290         // Compile and use regular expression
291         Matcher matcher = URI_PATTERN.matcher(uriPath);
292         if (matcher.find()) {
293            String scheme = matcher.group(1);
294            String rest = matcher.group(4);
295            String identity = matcher.group(2);
296            String key = matcher.group(3);
297            return URI.create(String.format("%s://%s:%s@%s", scheme, Strings2.urlEncode(identity),
298                  Strings2.urlEncode(key), rest));
299         } else {
300            throw new IllegalArgumentException("bad syntax");
301         }
302      } else {
303         return URI.create(uriPath);
304      }
305   }
306 
307   public void logRequest(Logger logger, HttpRequest request, String prefix) {
308      if (logger.isDebugEnabled()) {
309         logger.debug("%s %s", prefix, request.getRequestLine().toString());
310         logMessage(logger, request, prefix);
311      }
312   }
313 
314   private void logMessage(Logger logger, HttpMessage message, String prefix) {
315      for (Entry<String, String> header : message.getHeaders().entries()) {
316         if (header.getKey() != null)
317            logger.debug("%s %s: %s", prefix, header.getKey(), header.getValue());
318      }
319      if (message.getPayload() != null) {
320         if (message.getPayload().getContentMetadata().getContentType() != null)
321            logger.debug("%s %s: %s", prefix, CONTENT_TYPE, message.getPayload().getContentMetadata().getContentType());
322         if (message.getPayload().getContentMetadata().getContentLength() != null)
323            logger.debug("%s %s: %s", prefix, CONTENT_LENGTH, message.getPayload().getContentMetadata()
324                  .getContentLength());
325         if (message.getPayload().getContentMetadata().getContentMD5() != null)
326            try {
327               logger.debug(
328                     "%s %s: %s",
329                     prefix,
330                     "Content-MD5",
331                     CryptoStreams.base64Encode(InputSuppliers.of(message.getPayload().getContentMetadata()
332                           .getContentMD5())));
333            } catch (IOException e) {
334               logger.warn(e, " error getting md5 for %s", message);
335            }
336         if (message.getPayload().getContentMetadata().getContentDisposition() != null)
337            logger.debug("%s %s: %s", prefix, "Content-Disposition", message.getPayload().getContentMetadata()
338                  .getContentDisposition());
339         if (message.getPayload().getContentMetadata().getContentEncoding() != null)
340            logger.debug("%s %s: %s", prefix, CONTENT_ENCODING, message.getPayload().getContentMetadata()
341                  .getContentEncoding());
342         if (message.getPayload().getContentMetadata().getContentLanguage() != null)
343            logger.debug("%s %s: %s", prefix, CONTENT_LANGUAGE, message.getPayload().getContentMetadata()
344                  .getContentLanguage());
345      }
346   }
347 
348   public void logResponse(Logger logger, HttpResponse response, String prefix) {
349      if (logger.isDebugEnabled()) {
350         logger.debug("%s %s", prefix, response.getStatusLine().toString());
351         logMessage(logger, response, prefix);
352      }
353   }
354 
355   public static String sortAndConcatHeadersIntoString(Multimap<String, String> headers) {
356      StringBuffer buffer = new StringBuffer();
357      SortedSetMultimap<String, String> sortedMap = TreeMultimap.create();
358      sortedMap.putAll(headers);
359      for (Entry<String, String> header : sortedMap.entries()) {
360         if (header.getKey() != null)
361            buffer.append(String.format("%s: %s\n", header.getKey(), header.getValue()));
362      }
363      return buffer.toString();
364   }
365 
366   public void checkRequestHasRequiredProperties(HttpRequest message) {
367      checkArgument(
368            message.getPayload() == null || message.getFirstHeaderOrNull(CONTENT_TYPE) == null,
369            "configuration error please use request.getPayload().getContentMetadata().setContentType(value) as opposed to adding a content type header: "
370                  + message);
371      checkArgument(
372            message.getPayload() == null || message.getFirstHeaderOrNull(CONTENT_LENGTH) == null,
373            "configuration error please use request.getPayload().getContentMetadata().setContentLength(value) as opposed to adding a content length header: "
374                  + message);
375      checkArgument(
376            message.getPayload() == null || message.getPayload().getContentMetadata().getContentLength() != null
377                  || "chunked".equalsIgnoreCase(message.getFirstHeaderOrNull("Transfer-Encoding")),
378            "either chunked encoding must be set on the http request or contentlength set on the payload: " + message);
379      checkArgument(
380            message.getPayload() == null || message.getFirstHeaderOrNull("Content-MD5") == null,
381            "configuration error please use request.getPayload().getContentMetadata().setContentMD5(value) as opposed to adding a content md5 header: "
382                  + message);
383      checkArgument(
384            message.getPayload() == null || message.getFirstHeaderOrNull("Content-Disposition") == null,
385            "configuration error please use request.getPayload().getContentMetadata().setContentDisposition(value) as opposed to adding a content disposition header: "
386                  + message);
387      checkArgument(
388            message.getPayload() == null || message.getFirstHeaderOrNull(CONTENT_ENCODING) == null,
389            "configuration error please use request.getPayload().getContentMetadata().setContentEncoding(value) as opposed to adding a content encoding header: "
390                  + message);
391      checkArgument(
392            message.getPayload() == null || message.getFirstHeaderOrNull(CONTENT_LANGUAGE) == null,
393            "configuration error please use request.getPayload().getContentMetadata().setContentLanguage(value) as opposed to adding a content language header: "
394                  + message);
395   }
396 
397   public static void releasePayload(HttpMessage from) {
398      if (from.getPayload() != null)
399         from.getPayload().release();
400   }
401 
402   public String valueOrEmpty(String in) {
403      return in != null ? in : "";
404   }
405 
406   public String valueOrEmpty(byte[] md5) {
407      return md5 != null ? CryptoStreams.base64(md5) : "";
408   }
409 
410   public String valueOrEmpty(Collection<String> collection) {
411      return (collection != null && collection.size() >= 1) ? collection.iterator().next() : "";
412   }
413 
414   public static Long attemptToParseSizeAndRangeFromHeaders(HttpMessage from) throws HttpException {
415      String contentRange = from.getFirstHeaderOrNull("Content-Range");
416      if (contentRange == null && from.getPayload() != null) {
417         return from.getPayload().getContentMetadata().getContentLength();
418      } else if (contentRange != null) {
419         return Long.parseLong(contentRange.substring(contentRange.lastIndexOf('/') + 1));
420      }
421      return null;
422   }
423 
424   public static void checkRequestHasContentLengthOrChunkedEncoding(HttpMessage request, String message) {
425      boolean chunked = "chunked".equals(request.getFirstHeaderOrNull("Transfer-Encoding"));
426      checkArgument(request.getPayload() == null || chunked
427            || request.getPayload().getContentMetadata().getContentLength() != null, message);
428   }
429 
430   public static void wirePayloadIfEnabled(Wire wire, HttpMessage request) {
431      if (request.getPayload() != null && wire.enabled()) {
432         wire.output(request);
433         checkRequestHasContentLengthOrChunkedEncoding(request,
434               "After wiring, the request has neither chunked encoding nor content length: " + request);
435      }
436   }
437 
438   public static <T> T returnValueOnCodeOrNull(Exception from, T value, Predicate<Integer> codePredicate) {
439      Iterable<HttpResponseException> throwables = filter(getCausalChain(from), HttpResponseException.class);
440      if (size(throwables) >= 1 && get(throwables, 0).getResponse() != null
441            && codePredicate.apply(get(throwables, 0).getResponse().getStatusCode())) {
442         return value;
443      }
444      return null;
445   }
446}

[all classes][org.jclouds.http]
EMMA 2.0.5312 (C) Vladimir Roubtsov