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