Android: support guessing charset when header value is not set

This commit is contained in:
Sefa Ilkimen
2018-02-15 11:10:19 +01:00
parent 2f01d48abb
commit a49bcac81b
2 changed files with 68 additions and 9 deletions
@@ -1945,16 +1945,24 @@ public class HttpRequest {
} }
/** /**
* Get the response body as a {@link String} and set it as the value of the * Get the response body as ByteBuffer and set it as the value of the
* given reference. * given reference.
* *
* @param output * @param output
* @return this request * @return this request
* @throws HttpRequestException * @throws HttpRequestException
*/ */
public HttpRequest body(final AtomicReference<String> output) throws HttpRequestException { public HttpRequest body(final AtomicReference<ByteBuffer> output) throws HttpRequestException {
output.set(body()); final ByteArrayOutputStream outputStream = byteStream();
return this;
try {
copy(buffer(), outputStream);
output.set(ByteBuffer.wrap(outputStream.toByteArray()));
return this;
} catch (IOException e) {
throw new HttpRequestException(e);
}
} }
/** /**
@@ -1971,7 +1979,6 @@ public class HttpRequest {
return this; return this;
} }
/** /**
* Is the response body empty? * Is the response body empty?
* *
@@ -12,6 +12,14 @@ import org.json.JSONObject;
import java.net.SocketTimeoutException; import java.net.SocketTimeoutException;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CodingErrorAction;
import java.nio.charset.MalformedInputException;
import javax.net.ssl.SSLHandshakeException; import javax.net.ssl.SSLHandshakeException;
import java.util.ArrayList; import java.util.ArrayList;
@@ -19,6 +27,7 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import java.util.Iterator; import java.util.Iterator;
import android.text.TextUtils; import android.text.TextUtils;
@@ -28,7 +37,7 @@ import com.github.kevinsawicki.http.HttpRequest.HttpRequestException;
abstract class CordovaHttp { abstract class CordovaHttp {
protected static final String TAG = "CordovaHTTP"; protected static final String TAG = "CordovaHTTP";
protected static final String[] ACCEPTED_CHARSETS = new String[] {HttpRequest.CHARSET_UTF8, HttpRequest.CHARSET_LATIN1}; protected static final String[] ACCEPTED_CHARSETS = new String[] { HttpRequest.CHARSET_UTF8, HttpRequest.CHARSET_LATIN1 };
private static AtomicBoolean sslPinning = new AtomicBoolean(false); private static AtomicBoolean sslPinning = new AtomicBoolean(false);
private static AtomicBoolean acceptAllCerts = new AtomicBoolean(false); private static AtomicBoolean acceptAllCerts = new AtomicBoolean(false);
@@ -231,25 +240,68 @@ abstract class CordovaHttp {
request.uncompress(true); request.uncompress(true);
} }
private CharsetDecoder createCharsetDecoder(final String charsetName) {
return Charset.forName(charsetName).newDecoder()
.onMalformedInput(CodingErrorAction.REPORT)
.onUnmappableCharacter(CodingErrorAction.REPORT);
}
private String decodeBody(AtomicReference<ByteBuffer> rawOutput, String charsetName)
throws CharacterCodingException, MalformedInputException {
if (charsetName == null) {
return tryDecodeByteBuffer(rawOutput);
}
return decodeByteBuffer(rawOutput, charsetName);
}
private String tryDecodeByteBuffer(AtomicReference<ByteBuffer> rawOutput)
throws CharacterCodingException, MalformedInputException {
for (int i = 0; i < ACCEPTED_CHARSETS.length - 1; i++) {
try {
return decodeByteBuffer(rawOutput, ACCEPTED_CHARSETS[i]);
} catch (MalformedInputException e) {
continue;
} catch (CharacterCodingException e) {
continue;
}
}
return decodeBody(rawOutput, ACCEPTED_CHARSETS[ACCEPTED_CHARSETS.length - 1]);
}
private String decodeByteBuffer(AtomicReference<ByteBuffer> rawOutput, String charsetName)
throws CharacterCodingException, MalformedInputException {
return createCharsetDecoder(charsetName).decode(rawOutput.get()).toString();
}
protected void returnResponseObject(HttpRequest request) throws HttpRequestException { protected void returnResponseObject(HttpRequest request) throws HttpRequestException {
try { try {
JSONObject response = new JSONObject(); JSONObject response = new JSONObject();
int code = request.code(); int code = request.code();
String body = request.body(request.charset()); AtomicReference<ByteBuffer> rawOutputReference = new AtomicReference<ByteBuffer>();
request.body(rawOutputReference);
response.put("status", code); response.put("status", code);
response.put("url", request.url().toString()); response.put("url", request.url().toString());
this.addResponseHeaders(request, response); this.addResponseHeaders(request, response);
if (code >= 200 && code < 300) { if (code >= 200 && code < 300) {
response.put("data", body); response.put("data", decodeBody(rawOutputReference, request.charset()));
this.getCallbackContext().success(response); this.getCallbackContext().success(response);
} else { } else {
response.put("error", body); response.put("error", decodeBody(rawOutputReference, request.charset()));
this.getCallbackContext().error(response); this.getCallbackContext().error(response);
} }
} catch(JSONException e) { } catch(JSONException e) {
this.respondWithError("There was an error generating the response"); this.respondWithError("There was an error generating the response");
} catch(MalformedInputException e) {
this.respondWithError("Could not decode response data due to malformed data");
} catch(CharacterCodingException e) {
this.respondWithError("Could not decode response data due to invalid or unknown charset encoding");
} }
} }