Skip to content
This repository has been archived by the owner on Mar 1, 2018. It is now read-only.

Commit

Permalink
Automatically decompress client bodies.
Browse files Browse the repository at this point in the history
Signed-off-by: Laura <l@veriny.tf>
  • Loading branch information
Fuyukai committed Jul 3, 2017
1 parent 39bb2ce commit ab796d2
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 4 deletions.
4 changes: 3 additions & 1 deletion docs/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ Kyoukai Changelog

Here you can see the list of changes between each Kyoukai release.

Version 2.x.x
Version 2.2.1
-------------

- Add the ability to override the context class created.
Expand All @@ -14,6 +14,8 @@ Version 2.x.x

- Explicitly handle :class:`werkzeug.exceptions.BadRequestKeyError` in app processing.

- Decompress client body data when a Content-Encoding is detected in the httptools backend.

Version 2.2.0
-------------

Expand Down
2 changes: 1 addition & 1 deletion kyoukai/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
from kyoukai.asphalt import HTTPRequestContext
from kyoukai.blueprint import Blueprint

__version__ = "2.2.0"
__version__ = "2.2.1"

logger = logging.getLogger("Kyoukai")

Expand Down
62 changes: 60 additions & 2 deletions kyoukai/backends/httptools_.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@
"""
import asyncio
import base64
import gzip
import logging
import traceback
import warnings
import zlib
from io import BytesIO

import httptools
Expand All @@ -20,32 +22,53 @@
CRITICAL_ERROR_TEXT = """HTTP/1.0 500 INTERNAL SERVER ERROR
Server: Kyoukai
X-Powered-By: Kyoukai
X-HTTP-Backend: httptools
Content-Type: text/html; charset=utf-8
Content-Length: 310
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<title>Critical Server Error</title>
<h1>Critical Server Error</h1>
<p>An unrecoverable error has occurred within Kyoukai.
If you are the developer, please report this at <a href="https://github.com/SunDwarf/Kyoukai">the Kyoukai issue
tracker.</a>
If you are the developer, please report this at <a href="https://github.com/SunDwarf/Kyoukai">the
Kyoukai issue tracker.</a>
""".replace("\n", "\r\n")

HTTP_SWITCHING_PROTOCOLS = """HTTP/1.1 101 SWITCHING PROTOCOLS
Connection: Upgrade
Upgrade: h2c
Server: Kyoukai
X-Powered-By: Kyoukai
X-HTTP-Backend: httptools
Content-Length: 0
""".replace("\n", "\r\n")

HTTP_TOO_BIG = """HTTP/1.1 413 PAYLOAD TOO LARGE
Server: Kyoukai
X-Powered-By: Kyoukai
X-HTTP-Backend: httptools
Content-Length: 0
""".replace("\n", "\r\n")

HTTP_INVALID_COMPRESSION = """HTTP/1.1 400 BAD REQUEST
Server: Kyoukai
X-Powered-By: Kyoukai
X-HTTP-Backend: httptools
Content-Length: 25
Invalid compressed data
""".replace("\n", "\r\n")

PROTOCOL_CLASS = "KyoukaiProtocol"


class KyoukaiProtocol(asyncio.Protocol): # pragma: no cover
"""
The base protocol for Kyoukai using httptools for a HTTP/1.0 or HTTP/1.1 interface.
"""
MAX_BODY_SIZE = 12 * 1024 * 1024

def __init__(self, component, parent_context: Context,
server_ip: str, server_port: int):
Expand Down Expand Up @@ -138,6 +161,10 @@ def on_body(self, body: bytes):
:param body: The body text.
"""
self.body.write(body)
if self.body.tell() >= self.MAX_BODY_SIZE:
# write a "too big" message
self.write(HTTP_TOO_BIG)
self.close()

def on_url(self, url: bytes):
"""
Expand Down Expand Up @@ -338,6 +365,37 @@ async def _wait(self):
version = self.parser.get_http_version()
method = self.parser.get_method().decode()

for header, value in self.headers:
# check if a content-encoding has been passed
if header == "Content-Encoding" and body is not None:
# no special encoding
if value == "identity":
pass

# gzip, decompress as such
elif value == "gzip":
self.logger.debug("Decoding body data as gzip.")
try:
decompressed_data = gzip.decompress(body.read())
except zlib.error:
self.write(HTTP_INVALID_COMPRESSION)
self.close()
return

body = BytesIO(decompressed_data)

# deflate, decompress as such
elif value == "deflate":
z = zlib.decompressobj(6, zlib.DEFLATED, -zlib.MAX_WBITS, zlib.DEF_MEM_LEVEL, 0)
try:
decompressed_data = z.decompress(body.read())
except zlib.error:
self.write(HTTP_INVALID_COMPRESSION)
self.close()
return
else:
self.logger.error("Unknown Content-Encoding sent by client: {}".format(value))

new_environ = to_wsgi_environment(headers=self.headers, method=method, path=self.full_url,
http_version=version, body=body)

Expand Down

0 comments on commit ab796d2

Please sign in to comment.