Seize the day

POST : Backend study

Nginx에 gzip 적용하기

http://nginx.org/en/docs/http/ngx_http_gzip_module.html

 

Module ngx_http_gzip_module

Module ngx_http_gzip_module The ngx_http_gzip_module module is a filter that compresses responses using the “gzip” method. This often helps to reduce the size of transmitted data by half or even more. When using the SSL/TLS protocol, compressed respons

nginx.org

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());
}
top

posted at

2021. 9. 25. 13:50


CONTENTS

Seize the day
BLOG main image
김대정의 앱 개발 노트와 사는 이야기
RSS 2.0Tattertools
공지
아카이브
최근 글 최근 댓글
카테고리 태그 구름사이트 링크