1a11b70d5ec690dbedd9087fb3b25d7d65d865eb4Narayan Kamath/*
2a11b70d5ec690dbedd9087fb3b25d7d65d865eb4Narayan Kamath * Copyright (C) 2014 The Android Open Source Project
3a11b70d5ec690dbedd9087fb3b25d7d65d865eb4Narayan Kamath *
4a11b70d5ec690dbedd9087fb3b25d7d65d865eb4Narayan Kamath * Licensed under the Apache License, Version 2.0 (the "License");
5a11b70d5ec690dbedd9087fb3b25d7d65d865eb4Narayan Kamath * you may not use this file except in compliance with the License.
6a11b70d5ec690dbedd9087fb3b25d7d65d865eb4Narayan Kamath * You may obtain a copy of the License at
7a11b70d5ec690dbedd9087fb3b25d7d65d865eb4Narayan Kamath *
8a11b70d5ec690dbedd9087fb3b25d7d65d865eb4Narayan Kamath *      http://www.apache.org/licenses/LICENSE-2.0
9a11b70d5ec690dbedd9087fb3b25d7d65d865eb4Narayan Kamath *
10a11b70d5ec690dbedd9087fb3b25d7d65d865eb4Narayan Kamath * Unless required by applicable law or agreed to in writing, software
11a11b70d5ec690dbedd9087fb3b25d7d65d865eb4Narayan Kamath * distributed under the License is distributed on an "AS IS" BASIS,
12a11b70d5ec690dbedd9087fb3b25d7d65d865eb4Narayan Kamath * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13a11b70d5ec690dbedd9087fb3b25d7d65d865eb4Narayan Kamath * See the License for the specific language governing permissions and
14a11b70d5ec690dbedd9087fb3b25d7d65d865eb4Narayan Kamath * limitations under the License.
15a11b70d5ec690dbedd9087fb3b25d7d65d865eb4Narayan Kamath */
16a11b70d5ec690dbedd9087fb3b25d7d65d865eb4Narayan Kamath
17a11b70d5ec690dbedd9087fb3b25d7d65d865eb4Narayan Kamathpackage libcore.net.http;
18a11b70d5ec690dbedd9087fb3b25d7d65d865eb4Narayan Kamath
19a11b70d5ec690dbedd9087fb3b25d7d65d865eb4Narayan Kamathimport java.nio.charset.Charset;
20a11b70d5ec690dbedd9087fb3b25d7d65d865eb4Narayan Kamathimport java.nio.charset.IllegalCharsetNameException;
21a11b70d5ec690dbedd9087fb3b25d7d65d865eb4Narayan Kamathimport java.nio.charset.StandardCharsets;
22a11b70d5ec690dbedd9087fb3b25d7d65d865eb4Narayan Kamathimport java.nio.charset.UnsupportedCharsetException;
23a11b70d5ec690dbedd9087fb3b25d7d65d865eb4Narayan Kamathimport java.util.Collections;
24a11b70d5ec690dbedd9087fb3b25d7d65d865eb4Narayan Kamathimport java.util.HashMap;
25a11b70d5ec690dbedd9087fb3b25d7d65d865eb4Narayan Kamathimport java.util.Map;
26a11b70d5ec690dbedd9087fb3b25d7d65d865eb4Narayan Kamath
27a11b70d5ec690dbedd9087fb3b25d7d65d865eb4Narayan Kamath/**
28a11b70d5ec690dbedd9087fb3b25d7d65d865eb4Narayan Kamath * @hide
29a11b70d5ec690dbedd9087fb3b25d7d65d865eb4Narayan Kamath */
30a11b70d5ec690dbedd9087fb3b25d7d65d865eb4Narayan Kamathpublic class ResponseUtils {
31a11b70d5ec690dbedd9087fb3b25d7d65d865eb4Narayan Kamath  /**
32a11b70d5ec690dbedd9087fb3b25d7d65d865eb4Narayan Kamath   * Returns the response charset of a HTTP response based on the {@code Content-Type} of
33a11b70d5ec690dbedd9087fb3b25d7d65d865eb4Narayan Kamath   * the response (see RFC 7230). If the {@code Content-Type} header is missing or invalid,
34a11b70d5ec690dbedd9087fb3b25d7d65d865eb4Narayan Kamath   * the response is assumed to be encoded as {@code UTF-8}. Note that a charset usually
35a11b70d5ec690dbedd9087fb3b25d7d65d865eb4Narayan Kamath   * makes sense only for {@code "text/plain"} and other "text based" responses.
36a11b70d5ec690dbedd9087fb3b25d7d65d865eb4Narayan Kamath   *
37a11b70d5ec690dbedd9087fb3b25d7d65d865eb4Narayan Kamath   * @throws IllegalCharsetNameException if the response specified charset is illegal.
38a11b70d5ec690dbedd9087fb3b25d7d65d865eb4Narayan Kamath   * @throws UnsupportedCharsetException if the response specified charset is unsupported.
39a11b70d5ec690dbedd9087fb3b25d7d65d865eb4Narayan Kamath   */
40a11b70d5ec690dbedd9087fb3b25d7d65d865eb4Narayan Kamath  public static Charset responseCharset(String contentTypeHeader)
41a11b70d5ec690dbedd9087fb3b25d7d65d865eb4Narayan Kamath      throws IllegalCharsetNameException, UnsupportedCharsetException {
42a11b70d5ec690dbedd9087fb3b25d7d65d865eb4Narayan Kamath    Charset responseCharset = StandardCharsets.UTF_8;
43a11b70d5ec690dbedd9087fb3b25d7d65d865eb4Narayan Kamath    if (contentTypeHeader != null) {
44a11b70d5ec690dbedd9087fb3b25d7d65d865eb4Narayan Kamath      Map<String, String> contentTypeParams = parseContentTypeParameters(contentTypeHeader);
45a11b70d5ec690dbedd9087fb3b25d7d65d865eb4Narayan Kamath      String charsetParameter = contentTypeParams.get("charset");
46a11b70d5ec690dbedd9087fb3b25d7d65d865eb4Narayan Kamath      if (charsetParameter != null) {
47a11b70d5ec690dbedd9087fb3b25d7d65d865eb4Narayan Kamath        responseCharset = Charset.forName(charsetParameter);
48a11b70d5ec690dbedd9087fb3b25d7d65d865eb4Narayan Kamath      }
49a11b70d5ec690dbedd9087fb3b25d7d65d865eb4Narayan Kamath    }
50a11b70d5ec690dbedd9087fb3b25d7d65d865eb4Narayan Kamath
51a11b70d5ec690dbedd9087fb3b25d7d65d865eb4Narayan Kamath    return responseCharset;
52a11b70d5ec690dbedd9087fb3b25d7d65d865eb4Narayan Kamath  }
53a11b70d5ec690dbedd9087fb3b25d7d65d865eb4Narayan Kamath
54a11b70d5ec690dbedd9087fb3b25d7d65d865eb4Narayan Kamath  /**
55a11b70d5ec690dbedd9087fb3b25d7d65d865eb4Narayan Kamath   * Parse content-type parameters. The format of this header is roughly :
56a11b70d5ec690dbedd9087fb3b25d7d65d865eb4Narayan Kamath   * {@code type/subtype; param1=value1; param2=value2 ...} where each of the
57a11b70d5ec690dbedd9087fb3b25d7d65d865eb4Narayan Kamath   * parameters are optional. Parsing is lenient, malformed parameters are ignored.
58a11b70d5ec690dbedd9087fb3b25d7d65d865eb4Narayan Kamath   *
59a11b70d5ec690dbedd9087fb3b25d7d65d865eb4Narayan Kamath   * Parameter keys & values are trimmed of whitespace and keys are converted to
60a11b70d5ec690dbedd9087fb3b25d7d65d865eb4Narayan Kamath   * lower case.
61a11b70d5ec690dbedd9087fb3b25d7d65d865eb4Narayan Kamath   */
62a11b70d5ec690dbedd9087fb3b25d7d65d865eb4Narayan Kamath  private static Map<String, String> parseContentTypeParameters(String contentTypeHeader) {
63a11b70d5ec690dbedd9087fb3b25d7d65d865eb4Narayan Kamath    Map<String, String> parameters = Collections.EMPTY_MAP;
64a11b70d5ec690dbedd9087fb3b25d7d65d865eb4Narayan Kamath
65a11b70d5ec690dbedd9087fb3b25d7d65d865eb4Narayan Kamath    String[] fields = contentTypeHeader.split(";");
66a11b70d5ec690dbedd9087fb3b25d7d65d865eb4Narayan Kamath    if (fields.length > 1) {
67a11b70d5ec690dbedd9087fb3b25d7d65d865eb4Narayan Kamath      parameters = new HashMap<>();
68a11b70d5ec690dbedd9087fb3b25d7d65d865eb4Narayan Kamath      // Ignore the first element in the array (the type/subtype).
69a11b70d5ec690dbedd9087fb3b25d7d65d865eb4Narayan Kamath      for (int i = 1; i < fields.length; ++i) {
70a11b70d5ec690dbedd9087fb3b25d7d65d865eb4Narayan Kamath        final String parameter = fields[i];
71a11b70d5ec690dbedd9087fb3b25d7d65d865eb4Narayan Kamath        if (!parameter.isEmpty()) {
72a11b70d5ec690dbedd9087fb3b25d7d65d865eb4Narayan Kamath          final String[] components = parameter.split("=");
73a11b70d5ec690dbedd9087fb3b25d7d65d865eb4Narayan Kamath          if (components.length != 2) {
74a11b70d5ec690dbedd9087fb3b25d7d65d865eb4Narayan Kamath            continue;
75a11b70d5ec690dbedd9087fb3b25d7d65d865eb4Narayan Kamath          }
76a11b70d5ec690dbedd9087fb3b25d7d65d865eb4Narayan Kamath
77a11b70d5ec690dbedd9087fb3b25d7d65d865eb4Narayan Kamath          final String key = components[0].trim().toLowerCase();
78a11b70d5ec690dbedd9087fb3b25d7d65d865eb4Narayan Kamath          final String value = components[1].trim();
79a11b70d5ec690dbedd9087fb3b25d7d65d865eb4Narayan Kamath          if (key.isEmpty() || value.isEmpty()) {
80a11b70d5ec690dbedd9087fb3b25d7d65d865eb4Narayan Kamath            continue;
81a11b70d5ec690dbedd9087fb3b25d7d65d865eb4Narayan Kamath          }
82a11b70d5ec690dbedd9087fb3b25d7d65d865eb4Narayan Kamath
83a11b70d5ec690dbedd9087fb3b25d7d65d865eb4Narayan Kamath          parameters.put(key, value);
84a11b70d5ec690dbedd9087fb3b25d7d65d865eb4Narayan Kamath        }
85a11b70d5ec690dbedd9087fb3b25d7d65d865eb4Narayan Kamath      }
86a11b70d5ec690dbedd9087fb3b25d7d65d865eb4Narayan Kamath    }
87a11b70d5ec690dbedd9087fb3b25d7d65d865eb4Narayan Kamath
88a11b70d5ec690dbedd9087fb3b25d7d65d865eb4Narayan Kamath    return parameters;
89a11b70d5ec690dbedd9087fb3b25d7d65d865eb4Narayan Kamath  }
90a11b70d5ec690dbedd9087fb3b25d7d65d865eb4Narayan Kamath}
91