http://nginx.org/en/docs/http/ngx_http_gzip_module.html
gzip on;
gzip_min_length 1000;
gzip_types text/plain application/xml application/json;
간단히 세줄 추가로 deno+oak 서버에서 application/json 형식의 응답을 gzip으로 압축하여 클라이언트로 내려주었다.
Android에서 gzip 핸들링
요청 헤더에 Accetp-Encodig: gzip 추가 (by OkHttp3)
// "Accept-Encoding: gzip" 으로 gzip을 처리할 수 있음을 서버에 전달
val body = RequestBody.create(JSON_CONTENT_TYPE, jsonObject.toString(INDENT))
val request = Request.Builder()
.url(getUrl(path))
.header("Content-Type", "application/json; charset=utf-8")
.header("Accept-Encoding", "gzip")
.post(body)
.build()
val response = httpClient.newCall(request).execute()
// "Content-Encoding: gzip" 응답에 gzip으로 압축된 컨텐츠인지 체크하여 unzip 처리
var bodyString: String?
// gzip 디코딩
if (response.header("Content-Encoding") == "gzip" ||
response.header("content-encoding") == "gzip"
) {
val responseBody = response.body() ?: throw IOException("body is null")
BufferedInputStream(GZIPInputStream(responseBody.byteStream())).use { input ->
ByteArrayOutputStream().use { baos ->
val ba = ByteArray(1024)
while (true) {
val len = input.read(ba)
if (len == -1) break
baos.write(ba, 0, len)
}
bodyString = String(baos.toByteArray())
}
}
} else {
bodyString = response.body()?.string()
}
Android에서 요청시 압축하여 보내기
// data 압축
val data: ByteArray = jsonObject.toString().toByteArray()
val arr = ByteArrayOutputStream()
val zipper: OutputStream = GZIPOutputStream(arr)
zipper.write(data)
zipper.close()
val body = RequestBody.create(JSON_CONTENT_TYPE, arr.toByteArray())
// "Content-Encoding: gzip" 추가
val request = Request.Builder()
.url(getUrl(path))
.header("Content-Type", "application/json; charset=utf-8")
.header("Content-Encoding", "gzip")
.header("Accept-Encoding", "gzip")
.post(body)
.build()
val response = httpClient.newCall(request).execute()
이 방법으로 gzip 인코딩하여 요청할 수 있으나, 안타깝게도 deno + oak 서버에서 gzip 인코딩 요청을 지원하지 않는다.
Cpp에서 gzip 핸들링
https://zlib.net/ 에서 최신 소스를 받는다. Visual studio 2019 프로젝트에 추가하고, Precompiled header 사용하지 않도록 설정
https://github.com/mapbox/gzip-hpp zlip를 쉽게 사용할 수 있도록 hpp로만 구현된 wrapper 코드 다운로드, hpp이므로 프로젝트의 구성파일에 추가할 필요는 없음
gzip 인지 판단하기
https://docs.microsoft.com/ko-kr/windows/win32/wininet/retrieving-http-headers 에서 참고 HttpClient로 WinInet을 사용하기 때문에..
bool HasGzipHeader(HINTERNET hHttp, LPCSTR headerKey)
{
DWORD dwSize = 20;
LPVOID lpOutBuffer = new char[dwSize];
StringCchPrintfA((LPSTR)lpOutBuffer, dwSize, headerKey);
retry:
if (!HttpQueryInfoA(hHttp, HTTP_QUERY_CUSTOM,
(LPVOID)lpOutBuffer, &dwSize, NULL))
{
if (GetLastError() == ERROR_HTTP_HEADER_NOT_FOUND)
{
// Code to handle the case where the header isn't available.
delete[] lpOutBuffer;
return false;
}
else
{
// Check for an insufficient buffer.
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
{
// Allocate the necessary buffer.
delete[] lpOutBuffer;
dwSize = dwSize * 2;
lpOutBuffer = new char[dwSize];
// Rewrite the header name in the buffer.
StringCchPrintfA((LPSTR)lpOutBuffer, dwSize, headerKey);
// Retry the call.
goto retry;
}
else
{
// Error handling code.
delete[] lpOutBuffer;
return false;
}
}
}
bool ret = (memcmp(lpOutBuffer, "gzip\0", 5) == 0);
delete[] lpOutBuffer;
return ret;
}
bool isGzipContents = HasGzipHeader(hRequest, "Content-Encoding") || HasGzipHeader(hRequest, "content-encoding");
msdn 샘플소스에서 실수가 있는 듯..
프로젝트가 Unicode 프로젝트이므로 HttpQueryInfoA 를 사용해야 한다.
ERROR_INSUFFICIENT_BUFFER 일때 dwSize = dwSize * 2 를 빠트린듯하다.
gzip 압축해제
샘플 코드에서 두 줄만 가져와서 간단히 구현됨
std::string resultBody;
BOOL ret = _CallHttpRequest(resultCode, url.data(), resultBody, postData.data(), &requestParams);
if (resultCode.hasGzipHeader) {
const char* compressed_pointer = resultBody.data();
std::string result = gzip::decompress(compressed_pointer, resultBody.size());
}