1 // Copyright 2022 The Chromium Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 package org.chromium.net.apihelpers; 6 7 import org.chromium.net.UrlResponseInfo; 8 9 import java.nio.charset.Charset; 10 import java.util.List; 11 import java.util.Map; 12 13 /** 14 * A specialization of {@link InMemoryTransformCronetCallback} that interprets the response body as 15 * a string. 16 * 17 * <p>The charset used to decode the string is derived from the {@code Content-Type} header. 18 */ 19 public abstract class StringCronetCallback extends InMemoryTransformCronetCallback<String> { 20 private static final String CONTENT_TYPE_HEADER_NAME = "Content-Type"; 21 StringCronetCallback()22 StringCronetCallback() {} 23 24 @Override addCompletionListener( CronetRequestCompletionListener<? super String> listener)25 public StringCronetCallback addCompletionListener( 26 CronetRequestCompletionListener<? super String> listener) { 27 super.addCompletionListener(listener); 28 return this; 29 } 30 31 @Override // Override to return the subtype transformBodyBytes(UrlResponseInfo info, byte[] bodyBytes)32 protected String transformBodyBytes(UrlResponseInfo info, byte[] bodyBytes) { 33 return new String(bodyBytes, getCharsetFromHeaders(info)); 34 } 35 getCharsetFromHeaders(UrlResponseInfo info)36 private Charset getCharsetFromHeaders(UrlResponseInfo info) { 37 List<String> contentTypeHeaders = info.getAllHeaders().get(CONTENT_TYPE_HEADER_NAME); 38 39 String charset = null; 40 41 for (String header : contentTypeHeaders) { 42 ContentTypeParametersParser parser = new ContentTypeParametersParser(header); 43 44 while (parser.hasMore()) { 45 Map.Entry<String, String> parameter; 46 try { 47 parameter = parser.getNextParameter(); 48 } catch (ContentTypeParametersParser.ContentTypeParametersParserException e) { 49 // Malformed header, continue with the next one 50 break; 51 } 52 String parameterName = parameter.getKey(); 53 String parameterValue = parameter.getValue(); 54 55 if (charset != null && !parameterValue.equalsIgnoreCase(charset)) { 56 throw new IllegalArgumentException( 57 "Multiple charsets provided: " + parameterValue + " and " + charset); 58 } 59 charset = parameterValue; 60 } 61 } 62 63 if (charset != null) { 64 return Charset.forName(charset); 65 } else { 66 // Always UTF-8 on Android 67 return Charset.defaultCharset(); 68 } 69 } 70 } 71