LEFT | RIGHT |
1 /* | 1 /* |
2 * Copyright (c) 2010 Google Inc. | 2 * Copyright (c) 2010 Google Inc. |
3 * | 3 * |
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not u
se this file except | 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not u
se this file except |
5 * in compliance with the License. You may obtain a copy of the License at | 5 * in compliance with the License. You may obtain a copy of the License at |
6 * | 6 * |
7 * http://www.apache.org/licenses/LICENSE-2.0 | 7 * http://www.apache.org/licenses/LICENSE-2.0 |
8 * | 8 * |
9 * Unless required by applicable law or agreed to in writing, software distribut
ed under the License | 9 * Unless required by applicable law or agreed to in writing, software distribut
ed under the License |
10 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY K
IND, either express | 10 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY K
IND, either express |
11 * or implied. See the License for the specific language governing permissions a
nd limitations under | 11 * or implied. See the License for the specific language governing permissions a
nd limitations under |
12 * the License. | 12 * the License. |
13 */ | 13 */ |
14 | 14 |
15 package com.google.api.client.http; | 15 package com.google.api.client.http; |
16 | 16 |
17 import com.google.api.client.util.ArrayValueMap; | |
18 import com.google.api.client.util.ClassInfo; | |
19 import com.google.api.client.util.Data; | |
20 import com.google.api.client.util.FieldInfo; | |
21 import com.google.api.client.util.LoggingInputStream; | 17 import com.google.api.client.util.LoggingInputStream; |
22 import com.google.api.client.util.StringUtils; | 18 import com.google.api.client.util.StringUtils; |
23 import com.google.api.client.util.Types; | |
24 import com.google.common.base.Preconditions; | 19 import com.google.common.base.Preconditions; |
25 | 20 |
26 import java.io.ByteArrayOutputStream; | 21 import java.io.ByteArrayOutputStream; |
27 import java.io.IOException; | 22 import java.io.IOException; |
28 import java.io.InputStream; | 23 import java.io.InputStream; |
29 import java.io.OutputStream; | 24 import java.io.OutputStream; |
30 import java.lang.reflect.Type; | |
31 import java.util.ArrayList; | |
32 import java.util.Arrays; | |
33 import java.util.Collection; | |
34 import java.util.HashMap; | |
35 import java.util.List; | |
36 import java.util.logging.Level; | 25 import java.util.logging.Level; |
37 import java.util.logging.Logger; | 26 import java.util.logging.Logger; |
38 import java.util.regex.Matcher; | 27 import java.util.regex.Matcher; |
39 import java.util.regex.Pattern; | 28 import java.util.regex.Pattern; |
40 import java.util.zip.GZIPInputStream; | 29 import java.util.zip.GZIPInputStream; |
41 | 30 |
42 /** | 31 /** |
43 * HTTP response. | 32 * HTTP response. |
44 * | 33 * |
45 * <p> | 34 * <p> |
46 * Callers should call {@link #disconnect} when the HTTP response object is no l
onger needed. | 35 * Callers should call {@link #disconnect} when the HTTP response object is no l
onger needed. |
47 * Example usage: | 36 * However, {@link #disconnect} does not have to be called if the response strea
m is properly |
| 37 * closed. Example usage: |
48 * </p> | 38 * </p> |
49 * | |
50 * | 39 * |
51 * <pre> | 40 * <pre> |
52 HttpResponse response = request.execute(); | 41 HttpResponse response = request.execute(); |
53 try { | 42 try { |
54 // process the HTTP response object | 43 // process the HTTP response object |
55 } finally { | 44 } finally { |
56 response.disconnect(); | 45 response.disconnect(); |
57 } | 46 } |
58 * </pre> | 47 * </pre> |
59 * | 48 * |
(...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
154 if (statusLine != null) { | 143 if (statusLine != null) { |
155 logbuf.append(statusLine); | 144 logbuf.append(statusLine); |
156 } else { | 145 } else { |
157 logbuf.append(code); | 146 logbuf.append(code); |
158 if (message != null) { | 147 if (message != null) { |
159 logbuf.append(' ').append(message); | 148 logbuf.append(' ').append(message); |
160 } | 149 } |
161 } | 150 } |
162 logbuf.append(StringUtils.LINE_SEPARATOR); | 151 logbuf.append(StringUtils.LINE_SEPARATOR); |
163 } | 152 } |
| 153 |
164 // headers | 154 // headers |
165 int size = response.getHeaderCount(); | 155 headers.fromHttpResponse(response, loggable ? logbuf : null); |
166 Class<? extends HttpHeaders> headersClass = headers.getClass(); | 156 |
167 List<Type> context = Arrays.<Type>asList(headersClass); | |
168 ClassInfo classInfo = ClassInfo.of(headersClass); | |
169 HashMap<String, String> fieldNameMap = HttpHeaders.getFieldNameMap(headersCl
ass); | |
170 ArrayValueMap arrayValueMap = new ArrayValueMap(headers); | |
171 for (int i = 0; i < size; i++) { | |
172 String headerName = response.getHeaderName(i); | |
173 String headerValue = response.getHeaderValue(i); | |
174 if (loggable) { | |
175 logbuf.append(headerName + ": " + headerValue).append(StringUtils.LINE_S
EPARATOR); | |
176 } | |
177 String fieldName = fieldNameMap.get(headerName.toLowerCase()); | |
178 if (fieldName == null) { | |
179 fieldName = headerName; | |
180 } | |
181 // use field information if available | |
182 FieldInfo fieldInfo = classInfo.getFieldInfo(fieldName); | |
183 if (fieldInfo != null) { | |
184 Type type = Data.resolveWildcardTypeOrTypeVariable(context, fieldInfo.ge
tGenericType()); | |
185 // type is now class, parameterized type, or generic array type | |
186 if (Types.isArray(type)) { | |
187 // array that can handle repeating values | |
188 Class<?> rawArrayComponentType = | |
189 Types.getRawArrayComponentType(context, Types.getArrayComponentTyp
e(type)); | |
190 arrayValueMap.put(fieldInfo.getField(), rawArrayComponentType, | |
191 parseValue(rawArrayComponentType, context, headerValue)); | |
192 } else if (Types.isAssignableToOrFrom( | |
193 Types.getRawArrayComponentType(context, type), Iterable.class)) { | |
194 // iterable that can handle repeating values | |
195 @SuppressWarnings("unchecked") | |
196 Collection<Object> collection = (Collection<Object>) fieldInfo.getValu
e(headers); | |
197 if (collection == null) { | |
198 collection = Data.newCollectionInstance(type); | |
199 fieldInfo.setValue(headers, collection); | |
200 } | |
201 Type subFieldType = type == Object.class ? null : Types.getIterablePar
ameter(type); | |
202 collection.add(parseValue(subFieldType, context, headerValue)); | |
203 } else { | |
204 // parse value based on field type | |
205 fieldInfo.setValue(headers, parseValue(type, context, headerValue)); | |
206 } | |
207 } else { | |
208 // store header values in an array list | |
209 @SuppressWarnings("unchecked") | |
210 ArrayList<String> listValue = (ArrayList<String>) headers.get(fieldName)
; | |
211 if (listValue == null) { | |
212 listValue = new ArrayList<String>(); | |
213 headers.set(fieldName, listValue); | |
214 } | |
215 listValue.add(headerValue); | |
216 } | |
217 } | |
218 arrayValueMap.setValues(); | |
219 // log from buffer | 157 // log from buffer |
220 if (loggable) { | 158 if (loggable) { |
221 logger.config(logbuf.toString()); | 159 logger.config(logbuf.toString()); |
222 } | 160 } |
223 } | 161 } |
224 | 162 |
225 /** | 163 /** |
226 * Returns the limit to the content size that will be logged during {@link #ge
tContent()}. | 164 * Returns the limit to the content size that will be logged during {@link #ge
tContent()}. |
227 * | 165 * |
228 * <p> | 166 * <p> |
(...skipping 151 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
380 | 318 |
381 /** | 319 /** |
382 * Returns the HTTP request. | 320 * Returns the HTTP request. |
383 * | 321 * |
384 * @since 1.5 | 322 * @since 1.5 |
385 */ | 323 */ |
386 public HttpRequest getRequest() { | 324 public HttpRequest getRequest() { |
387 return request; | 325 return request; |
388 } | 326 } |
389 | 327 |
390 private static Object parseValue(Type valueType, List<Type> context, String va
lue) { | |
391 Type resolved = Data.resolveWildcardTypeOrTypeVariable(context, valueType); | |
392 return Data.parsePrimitiveValue(resolved, value); | |
393 } | |
394 | |
395 /** | 328 /** |
396 * Returns the content of the HTTP response. | 329 * Returns the content of the HTTP response. |
397 * <p> | 330 * <p> |
398 * The result is cached, so subsequent calls will be fast. | 331 * The result is cached, so subsequent calls will be fast. |
| 332 * <p> |
| 333 * Callers should call {@link InputStream#close} after the returned {@link Inp
utStream} is no |
| 334 * longer needed. Example usage: |
| 335 * |
| 336 * <pre> |
| 337 InputStream is = response.getContent(); |
| 338 try { |
| 339 // Process the input stream.. |
| 340 } finally { |
| 341 is.close(); |
| 342 } |
| 343 * </pre> |
| 344 * <p> |
| 345 * {@link HttpResponse#disconnect} does not have to be called if the content i
s closed. |
399 * | 346 * |
400 * @return input stream content of the HTTP response or {@code null} for none | 347 * @return input stream content of the HTTP response or {@code null} for none |
401 * @throws IOException I/O exception | 348 * @throws IOException I/O exception |
402 */ | 349 */ |
403 public InputStream getContent() throws IOException { | 350 public InputStream getContent() throws IOException { |
404 if (!contentRead) { | 351 if (!contentRead) { |
405 InputStream lowLevelResponseContent = this.response.getContent(); | 352 InputStream lowLevelResponseContent = this.response.getContent(); |
406 if (lowLevelResponseContent != null) { | 353 if (lowLevelResponseContent != null) { |
407 // gzip encoding (wrap content with GZipInputStream) | 354 // Flag used to indicate if an exception is thrown before the content is
successfully |
408 String contentEncoding = this.contentEncoding; | 355 // processed. |
409 if (contentEncoding != null && contentEncoding.contains("gzip")) { | 356 boolean contentProcessed = false; |
410 lowLevelResponseContent = new GZIPInputStream(lowLevelResponseContent)
; | 357 try { |
| 358 // gzip encoding (wrap content with GZipInputStream) |
| 359 String contentEncoding = this.contentEncoding; |
| 360 if (contentEncoding != null && contentEncoding.contains("gzip")) { |
| 361 lowLevelResponseContent = new GZIPInputStream(lowLevelResponseConten
t); |
| 362 } |
| 363 // logging (wrap content with LoggingInputStream) |
| 364 Logger logger = HttpTransport.LOGGER; |
| 365 if (loggingEnabled && logger.isLoggable(Level.CONFIG)) { |
| 366 lowLevelResponseContent = new LoggingInputStream( |
| 367 lowLevelResponseContent, logger, Level.CONFIG, contentLoggingLim
it); |
| 368 } |
| 369 content = lowLevelResponseContent; |
| 370 contentProcessed = true; |
| 371 } finally { |
| 372 if (!contentProcessed) { |
| 373 lowLevelResponseContent.close(); |
| 374 } |
411 } | 375 } |
412 // logging (wrap content with LoggingInputStream) | |
413 Logger logger = HttpTransport.LOGGER; | |
414 if (loggingEnabled && logger.isLoggable(Level.CONFIG)) { | |
415 lowLevelResponseContent = new LoggingInputStream( | |
416 lowLevelResponseContent, logger, Level.CONFIG, contentLoggingLimit
); | |
417 } | |
418 content = lowLevelResponseContent; | |
419 } | 376 } |
420 contentRead = true; | 377 contentRead = true; |
421 } | 378 } |
422 return content; | 379 return content; |
423 } | 380 } |
424 | 381 |
425 /** | 382 /** |
426 * Writes the content of the HTTP response into the given destination output s
tream. | 383 * Writes the content of the HTTP response into the given destination output s
tream. |
427 * | 384 * |
428 * <p> | 385 * <p> |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
466 if (content != null) { | 423 if (content != null) { |
467 content.close(); | 424 content.close(); |
468 } | 425 } |
469 } | 426 } |
470 | 427 |
471 /** | 428 /** |
472 * Close the HTTP response content and disconnect using {@link LowLevelHttpRes
ponse#disconnect()}. | 429 * Close the HTTP response content and disconnect using {@link LowLevelHttpRes
ponse#disconnect()}. |
473 * | 430 * |
474 * <p> | 431 * <p> |
475 * Upgrade warning: since version 1.10 {@link #disconnect} now closes the HTTP
response content | 432 * Upgrade warning: since version 1.10 {@link #disconnect} now closes the HTTP
response content |
476 * input stream. It was not closed by this method prior to version 1.10. | 433 * input stream. This was not done by this method prior to version 1.10. |
477 * </p> | 434 * </p> |
478 * | 435 * |
479 * @since 1.4 | 436 * @since 1.4 |
480 */ | 437 */ |
481 public void disconnect() throws IOException { | 438 public void disconnect() throws IOException { |
482 ignore(); | 439 ignore(); |
483 response.disconnect(); | 440 response.disconnect(); |
484 } | 441 } |
485 | 442 |
486 /** | 443 /** |
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
543 Matcher m = CONTENT_TYPE_PARAM_PATTERN.matcher(contentType); | 500 Matcher m = CONTENT_TYPE_PARAM_PATTERN.matcher(contentType); |
544 while (m.find()) { | 501 while (m.find()) { |
545 if ("charset".equalsIgnoreCase(m.group(1))) { | 502 if ("charset".equalsIgnoreCase(m.group(1))) { |
546 return m.group(2); | 503 return m.group(2); |
547 } | 504 } |
548 } | 505 } |
549 } | 506 } |
550 return "ISO-8859-1"; | 507 return "ISO-8859-1"; |
551 } | 508 } |
552 } | 509 } |
LEFT | RIGHT |