Update

현재는 크롬 데이터 세이버의 동작 방식이 바뀌어 (protobuf + signature) 이 글의 내용은 더 이상 동작하지 않습니다.

데이터 세이버는 구글에서 제작한 크롬 브라우저의 확장 프로그램으로 크롬 웹 스토어에서는 다음과 같이 소개하고 있습니다.

Google 서버를 통해 방문하는 페이지를 최적화하여 데이터 사용량을 줄이세요.

데이터 세이버의 기본 동작은 아주 단순한데, 구글 압축 서버(Google compress server)가 프록시(proxy)가 되어 서버가 보낸 데이터(주로 이미지)를 중간에서 압축하여 유저에게 보내는 방식입니다.

아마 실제로는 캐싱도 해서 효율을 높이고 있을 것 같긴 한데, 기본적인 동작 메커니즘은 압축입니다.

데이터 세이버를 껐을 때

데이터 세이버를 켰을 때

데이터 세이버를 켠 채 ssl을 사용하지 않는 사이트로 접속해보면 Remote Address의 IP가 달라져 있는 것을 확인할 수 있습니다.


구글 압축 서버에 직접 연결하기

데이버 세이버의 동작 방식이 단순히 구글 서버를 프록시로 사용하는 것이기 때문에, 우리는 굳이 데이터 세이버에 의존할 필요가 없습니다. 크롬 외의 다른 브라우저에서도 쉽게 같은 효과를 얻을 수 있고, 또는 브라우저 없이도 사용할 수 있습니다.

실제로 Firefox의 addon으로 일반 유저가 제작한 Data saver proxy for Firefox가 존재합니다.

우리도 직접 연결을 해봅시다.

1
2
$ ping compress.googlezip.net
PING compress.googlezip.net (172.217.161.44) 56(84) bytes of data.

데이터 세이버를 켰을 때의 Remote Address인 172.217.161.44는 compress.googlezip.net이라는 도메인을 가리킵니다.

해당 도메인을 프록시로 사용하여 리퀘스트를 보내보도록 합시다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
import requests

proxies = {
    'http': 'compress.googlezip.net',
    'https': 'compress.googlezip.net',
}

r = requests.get('http://www.bbc.com/', proxies=proxies)

print(r.status_code)
print(r.content)

# 502
# ...(omitted)...This page cannot be loaded using Chrome Data Saver. Try reloading the page...(omitted)...

해당 주소를 프록실로 사용해서 리퀘스트를 보내보면, 서버에서 502 Bad Gateway 에러를 뱉어내는 것을 확인할 수 있습니다. 어떻게 동작하는 지 알아낼 필요가 있겠습니다.

구글 코드 아카이브를 찾아보면 datacompressionproxy라는 레포지토리를 확인할 수 있습니다. compress.googlezip.net 서버가 아마도 이것을 사용할 것이라고 추정할 수 있습니다.

직접 코드를 분석해볼 수도 있지만, 좀 더 찾아보면 운이 좋게도 이미 다른 사람이 해당 코드를 분석해서 별도로 활용할 수 있는 구현체를 만들어놓은 것을 확인할 수 있습니다.

Chrome-Data-Compression-Proxy-Standalone-Python

위 코드 저장소에서 코드를 살펴보면, Chrome-Proxy라는 헤더에 고정된 authvalue에 해시값을 포함한 특정 값을 심어서 보내야 한다는 것을 알 수 있습니다. 아래는 위 저장소에서 가져온 코드입니다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
from hashlib import md5
import random
import time
import requests


# code from https://github.com/cnbeining/Chrome-Data-Compression-Proxy-Standalone-Python/blob/master/google.py
def get_long_int():
    return str(random.randint(100000000, 999999999))


# code from https://github.com/cnbeining/Chrome-Data-Compression-Proxy-Standalone-Python/blob/master/google.py
def get_google_header():
    authValue = 'ac4500dd3b7579186c1b0620614fdb1f7d61f944'
    timestamp = str(int(time.time()))
    return 'ps=' + timestamp + '-' + get_long_int() + '-' + get_long_int() + '-' + get_long_int() + ', sid=' + md5((timestamp + authValue + timestamp).encode('utf-8')).hexdigest() + ', b=2403, p=61, c=win'

이제 다시 리퀘스트를 보내봅시다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
def get(url, headers={}, **kwargs):

    proxy_headers = {
        'Chrome-Proxy': get_google_header(),
    }

    # ...omitted...

    path_idx = url.find('/')
      host = url[:path_idx]
      path = url[path_idx:]

    proxy_headers.update({'Host': host})

    # ...omitted...

    return requests.get('http://compress.googlezip.net:80{path}'.format(path=path),
                        headers=proxy_headers, **kwargs)

요청을 보내고자 하는 url의 Host부분을 헤더의 Host 값으로 적어주었습니다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
import requests
from google_compression_proxy import get as compress_get

http_image_url = 'http://ichef.bbci.co.uk/wwfeatures/wm/live/624_351/images/live/p0/4v/jy/p04vjy8l.jpg'
https_image_url = 'https://upload.wikimedia.org/wikipedia/commons/thumb/d/d4/Siberian_Tiger_by_Malene_Th.jpg/450px-Siberian_Tiger_by_Malene_Th.jpg'

r1 = requests.get(http_image_url)
r2 = compress_get(http_image_url)

print('Original image size:', len(r1.content))
print('Compressed image size:', len(r2.content))
# Original image size: 63816
# Compressed image size: 33361

r1 = requests.get(https_image_url)
r2 = compress_get(https_image_url)

print('Original image size:', len(r1.content))
print('Compressed image size:', len(r2.content))
# Original image size: 93198
# Compressed image size: 93198

이미지 url에 대해서 리퀘스트를 보내본 결과입니다.

http 요청에 대해서는 확실하게 이미지의 용량이 줄어든 것을 확인할 수 있고, https 요청에 대해서는 압축을 하지 못하는 것도 확인할 수 있습니다.

샘플 코드를 포함한 전체 코드는 여기에서 볼 수 있습니다.

Reference

https://github.com/cnbeining/Chrome-Data-Compression-Proxy-Standalone-Python

https://code.google.com/archive/p/datacompressionproxy/