I have a problem when i activate gzip on WS using retrofit 1.4.1 and okhttp 1.3.0.
RequestInterceptor requestInterceptor = new RequestInterceptor() {
You need have to use Okhttp also made by square which supports grip. Don't know if you have create a custom instance or if it's enabled by default you should check the docs for that.
After running into a similar issue (in my case, without adding any Accept-Encoding header, it would occasionally fail to un-gzip the response, leaving also the Content-Encoding: gzip header in it, crashing the JSON parser), and with no clear way around this, I manually enabled gzip for Retrofit by creating the delegated Client implementation below. It works great, except that you probably should not use it for very large (e.g. > 250KB) responses, as they are first copied into a byte array.
public class GzippedClient implements Client {
private Client wrappedClient;
public GzippedClient(Client wrappedClient) {
this.wrappedClient = wrappedClient;
}
@Override
public Response execute(Request request) throws IOException {
Response response = wrappedClient.execute(request);
boolean gzipped = false;
for (Header h : response.getHeaders()) {
if (h.getName() != null && h.getName().toLowerCase().equals("content-encoding") && h.getValue() != null && h.getValue().toLowerCase().equals("gzip")) {
gzipped = true;
break;
}
}
Response r = null;
if (gzipped) {
InputStream is = null;
ByteArrayOutputStream bos = null;
try {
is = new BufferedInputStream(new GZIPInputStream(response.getBody().in()));
bos = new ByteArrayOutputStream();
int b;
while ((b = is.read()) != -1) {
bos.write(b);
}
TypedByteArray body = new TypedByteArray(response.getBody().mimeType(), bos.toByteArray());
r = new Response(response.getUrl(), response.getStatus(), response.getReason(), response.getHeaders(), body);
} finally {
if (is != null) {
is.close();
}
if (bos != null) {
bos.close();
}
}
} else {
r = response;
}
return r;
}
}
You will also have to add an Accept-Encoding header to your requests, e.g. by using a RequestInterceptor
requestFacade.addHeader("Accept-Encoding", "gzip");
Finally, you have to wrap your existing Client into this new GzippedClient, like so:
restBuilder.setClient(new GzippedClient(new OkClient(okHttpClient)));
That's it. Now your data will be gzipped.
EDIT: It seems that in OkHttp version 1.5.1, a bug (https://github.com/square/okhttp/pull/632) seems to have been fixed related to the transparent gzipping which may (or may not) have been the source of my initial issue. If so, the occasional failure to un-gzip may no longer occur, though it happened rarely enough that I cannot confirm this yet. Either way, if you want to rely on your own, rather than the transparent adding/removing of headers and gzipping, then the solution described will work.
If you check HttpEngine in OkHttp library, then you can find below code, which means if you manually add "Accept-Encoding":"gzip" header into request, then un-zip is your responsibility.
/**
* True if this client added an "Accept-Encoding: gzip" header field and is
* therefore responsible for also decompressing the transfer stream.
*/
private boolean transparentGzip;
so if you manually add "Accept-Encoding":"gzip" header, then after getting response, do un-zip like below.
private static String readContentFromTypedInput(TypedInput typedInput){
InputStreamReader isr = null;
BufferedReader br = null;
char[] cbuf = new char[512];
StringWriter stringWriter = new StringWriter();
try {
final InputStream in = typedInput.in();
boolean isGzipped = GZipper.isGzippped(in);
if(isGzipped){
return new String(GZipper.doUnZip(in));
}
isr = new InputStreamReader(in);
br = new BufferedReader(isr);
while((br.read(cbuf))!= -1){
stringWriter.write(cbuf);
}
} catch (IOException e) {
throw new InvalidTestCaseException("failed read received content.", e);
} finally{
try{
if(br != null) br.close();
}catch(IOException e){
//ignore
}
}
return stringWriter.toString().trim();
}
GZipper.java
public class GZipper{
public static final String DEFAULT_CHARSET = "utf-8";
private static final int BYTE_BLOCK_LENGTH = 1024;
public static byte[] doZip(final String message){
if(message == null || message.isEmpty()){
throw new SystemFailedException("Fail to zip - given message is null or empty");
}
byte[] gzippped = null;
try {
gzippped = doZip(message.getBytes(DEFAULT_CHARSET));
} catch (Throwable e) {
throw new SystemFailedException(e.getMessage(), e);
}
return gzippped;
}
public static byte[] doZip(final byte[] unzippedMessageByte){
validate(unzippedMessageByte, "Fail to zip - given bytes is null or empty");
ByteArrayInputStream is = null;
ByteArrayOutputStream bos = null;
GZIPOutputStream gzip_os = null;
byte[] compressedBytes = null;
try{
is = new ByteArrayInputStream(unzippedMessageByte);
bos = new ByteArrayOutputStream();
gzip_os = new GZIPOutputStream(bos);
copy(is, gzip_os);
gzip_os.finish();
compressedBytes = bos.toByteArray();
}catch(IOException e){
throw new SystemFailedException(e.getMessage(), e);
}finally{
try{
if(is != null){is.close();}
if(gzip_os != null){gzip_os.close();}
if(bos != null){bos.close();}
}catch(IOException e){
//ignore
}
}
return compressedBytes;
}
public static String doUnZipToString(final byte[] gzippedMessage){
validate(gzippedMessage, "Fail to unzip - given bytes is null or empty");
byte[] gzippped = null;
String unzippedMessage = null;
try {
gzippped = doUnZip(gzippedMessage);
unzippedMessage = new String(gzippped, DEFAULT_CHARSET);
} catch (Throwable e) {
throw new SystemFailedException(e.getMessage(), e);
}
return unzippedMessage;
}
private static void validate(final byte[] bytes, String failedMessage) {
if(bytes == null || bytes.length == 0){
throw new SystemFailedException(failedMessage);
}
}
public static byte[] doUnZip(InputStream in) {
if(!(in instanceof ByteArrayInputStream)){
try {
return doUnZip(IOUtils.toByteArray(in));
} catch (IOException e) {
throw new SystemFailedException(e.getMessage(), e);
}
}
ByteArrayOutputStream bos = null;
InputStream gzip_is = null;
byte[] bytes = null;
try{
bos = new ByteArrayOutputStream();
gzip_is = new GZIPInputStream(in);
copy(gzip_is,bos);
bytes = bos.toByteArray();
}catch(IOException e){
throw new SystemFailedException(e.getMessage(), e);
}finally{
try{
if(gzip_is != null) gzip_is.close();
if(bos != null) bos.close();
}catch(IOException e){
//ignore
}
}
return bytes;
}
public static byte[] doUnZip(final byte[] zippedMessage){
validate(zippedMessage, "Fail to unzip - given bytes is null or empty");
ByteArrayInputStream is = null;
try{
is = new ByteArrayInputStream(zippedMessage);
return doUnZip(is);
}finally{
try{
if(is != null) is.close();
}catch(IOException e){
//ignore
}
}
}
public static String doUnZip(File file){
validate(file);
GZIPInputStream gzipInputStream = null;
StringWriter writer = null;
String result = "";
try{
byte[] buffer = new byte[BYTE_BLOCK_LENGTH];
gzipInputStream = new GZIPInputStream(new FileInputStream(file));
writer = new StringWriter();
while((gzipInputStream.read(buffer)) > 0){
writer.write(new String(buffer));
writer.flush();
}
result = writer.toString();
}catch(IOException e){
//do something to handle exception
}
finally{
try{
if(writer != null){writer.close();}
if(gzipInputStream != null){gzipInputStream.close();}
}catch(IOException e){
//ignore
}
}
return result;
}
private static void validate(File file) {
if(file==null || !file.exists()){
throw new SystemFailedException("Fail to unzip - file is not exist");
}
}
private static void copy(InputStream in, OutputStream out)throws IOException {
byte[] buf = new byte[BYTE_BLOCK_LENGTH];
int len = -1;
while ((len = in.read(buf, 0, buf.length)) != -1) {
out.write(buf, 0, len);
}
}
public static boolean isGzipped(byte[] input){
return isGzippped(new ByteArrayInputStream(input));
}
public static boolean isGzippped(InputStream in){
boolean markSupported = in.markSupported();
boolean result = false;
try {
if(markSupported){
in.mark(0);
result = (readUShort(in) == GZIPInputStream.GZIP_MAGIC);
in.reset();
}
} catch (Exception e) {
result = false;
}
return result;
}
private static int readUShort(InputStream in) throws IOException {
int b = readUByte(in);
return ((int)readUByte(in) << 8) | b;
}
/*
* Reads unsigned byte.
*/
private static int readUByte(InputStream in) throws IOException {
int b = in.read();
if (b == -1) {
throw new EOFException();
}
if (b < -1 || b > 255) {
b = 0;
}
return b;
}
}
Just omit the accept-encoding
header from your code. OkHttp will add its own accept-encoding
header, and if the server responds with gzip then OkHttp will silently unzip it for you.