2017-11-11 198 views
0

由於Bottle Web框架,我有一個在Python中實現的HTTP/JSON Restful服務器。我想將Gzip發送給客戶端的數據。Httpie無法解碼我的Bottle API Gzipped響應

#!/usr/bin/env python 
# -*- coding: utf-8 -*- 
# 
# curl -H "Content-Type: application/json" -X POST -d '{"key1": 1, "key2": 2}' http://localhost:6789/post 
# 

from bottle import run, request, post, route, response 
from zlib import compress 
import json 

data = {'my': 'json'} 


@post('/post') 
def api_post(): 
    global data 
    data = json.loads(request.body.read()) 
    return(data) 


@route('/get') 
def api_get(): 
    global data 
    response.headers['Content-Encoding'] = 'identity' 
    return(json.dumps(data).encode('utf-8')) 


@route('/getgzip') 
def api_get_gzip(): 
    global data 
    if 'gzip' in request.headers.get('Accept-Encoding', ''): 
     response.headers['Content-Encoding'] = 'gzip' 
     ret = compress(json.dumps(data).encode('utf-8')) 
    else: 
     response.headers['Content-Encoding'] = 'identity' 
     ret = json.dumps(data).encode('utf-8') 
    return(ret) 


run(host='localhost', port=6789, debug=True) 

當我測試用捲曲的服務器,結果是好(如果我使用--compressed選項標籤):

$ curl -H "Accept-encoding: gzip, deflated" -v --compressed http://localhost:6789/getgzip 
* Trying 127.0.0.1... 
* Connected to localhost (127.0.0.1) port 6789 (#0) 
> GET /getgzip HTTP/1.1 
> Host: localhost:6789 
> User-Agent: curl/7.47.0 
> Accept: */* 
> Accept-encoding: gzip, deflated 
> 
* HTTP 1.0, assume close after body 
< HTTP/1.0 200 OK 
< Date: Sun, 12 Nov 2017 09:09:09 GMT 
< Server: WSGIServer/0.1 Python/2.7.12 
< Content-Length: 22 
< Content-Encoding: gzip 
< Content-Type: text/html; charset=UTF-8 
< 
* Closing connection 0 
{"my": "json"} 

但不能與HTTPie(或Firefox或Chrome .. 。):

$ http http://localhost:6789/getgzipHTTP/1.0 200 OK 
Content-Encoding: gzip 
Content-Length: 22 
Content-Type: text/html; charset=UTF-8 
Date: Sun, 12 Nov 2017 09:10:10 GMT 
Server: WSGIServer/0.1 Python/2.7.12 

http: error: ContentDecodingError: ('Received response with content-encoding: gzip, but failed to decode it.', error('Error -3 while decompressing: incorrect header check',)) 

任何想法?

回答

1

我Nicolargo,

根據Httpie的文檔,默認編碼設置爲Accept-Encoding: gzip, deflate但你所使用的實現Lempel–Ziv–Welch Compression Algorithm(Gzip已基於DEFLATE Algorithm)的zlib模塊的compress Python函數。

或者,根據Bottle(https://bottlepy.org/docs/dev/recipes.html#gzip-compression-in-bottle)的文檔,您將需要一個自定義中間件來執行gzip壓縮(請參閱此處的示例:http://svn.cherrypy.org/tags/cherrypy-2.1.1/cherrypy/lib/filter/gzipfilter.py)。

編輯:

zlib模塊的compress功能做執行的gzip兼容的壓縮。

我認爲它與數據的header更相關(如錯誤提及)。在http://svn.cherrypy.org/tags/cherrypy-2.1.1/cherrypy/lib/filter/gzipfilter.py有一個使用write_gzip_header也許你可以試試這個。

0

感謝Guillaume的編輯部分,它現在可以同時使用Httpie和Curl。

下面是完整的代碼:

#!/usr/bin/env python 
# -*- coding: utf-8 -*- 
# 
# curl -H "Content-Type: application/json" -X POST -d '{"key1": 1, "key2": 2}' http://localhost:6789/post 
# 

from bottle import run, request, post, route, response 
import zlib 
import json 
import struct 
import time 

data = {'my': 'json'} 


@post('/post') 
def api_post(): 
    global data 
    data = json.loads(request.body.read()) 
    return(data) 


@route('/get') 
def api_get(): 
    global data 
    response.headers['Content-Encoding'] = 'identity' 
    return(json.dumps(data).encode('utf-8')) 


@route('/getgzip') 
def api_get_gzip(): 
    global data 
    ret = json.dumps(data).encode('utf-8') 
    if 'gzip' in request.headers.get('Accept-Encoding', ''): 
     response.headers['Content-Encoding'] = 'gzip' 
     ret = gzip_body(ret) 
    else: 
     response.headers['Content-Encoding'] = 'identity' 
    return(ret) 


def write_gzip_header(): 
    header = '\037\213'  # magic header 
    header += '\010'   # compression method 
    header += '\0' 
    header += struct.pack("<L", long(time.time())) 
    header += '\002' 
    header += '\377' 
    return header 


def write_gzip_trailer(crc, size): 
    footer = struct.pack("<l", crc) 
    footer += struct.pack("<L", size & 0xFFFFFFFFL) 
    return footer 


def gzip_body(body, compress_level=6): 
    yield gzip_header() 
    crc = zlib.crc32("") 
    size = 0 
    zobj = zlib.compressobj(compress_level, 
          zlib.DEFLATED, -zlib.MAX_WBITS, 
          zlib.DEF_MEM_LEVEL, 0) 
    size += len(data) 
    crc = zlib.crc32(data, crc) 
    yield zobj.compress(data) 
    yield zobj.flush() 
    yield gzip_trailer(crc, size) 


run(host='localhost', port=6789, debug=True) 

這是一個有點複雜,但它做的工作......