Skip to content

Commit

Permalink
Multiple HTTP writes (#125)
Browse files Browse the repository at this point in the history
* Fix for #111.
* Bump version.
  • Loading branch information
mindflayer committed Oct 9, 2020
1 parent ef2d1d5 commit e723252
Show file tree
Hide file tree
Showing 8 changed files with 79 additions and 20 deletions.
6 changes: 5 additions & 1 deletion .isort.cfg
@@ -1,2 +1,6 @@
[settings]
known_third_party = aiohttp,async_timeout,decorator,gevent,mock,pook,pytest,redis,requests,setuptools,six,sure,urllib3
multi_line_output=3
include_trailing_comma=True
force_grid_wrap=0
use_parentheses=True
line_length=88
12 changes: 4 additions & 8 deletions .pre-commit-config.yaml
@@ -1,12 +1,13 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v3.1.0
rev: v3.2.0
hooks:
- id: check-yaml
args: ['--unsafe']
- id: check-json
- id: end-of-file-fixer
- id: trailing-whitespace
# - id: no-commit-to-branch
- id: no-commit-to-branch
- id: pretty-format-json
args: ['--autofix']

Expand All @@ -16,13 +17,8 @@ repos:
- id: autoflake
args: ['--in-place', '--remove-all-unused-imports', '--remove-unused-variable']

- repo: https://github.com/asottile/seed-isort-config
rev: v2.2.0
hooks:
- id: seed-isort-config

- repo: https://github.com/timothycrosley/isort
rev: 4.3.21
rev: 5.5.2
hooks:
- id: isort

Expand Down
3 changes: 3 additions & 0 deletions README.rst
Expand Up @@ -20,6 +20,9 @@ A socket mock framework
-------------------------
for all kinds of socket *animals*, web-clients included - with gevent/asyncio/SSL support

...and then MicroPython's *urequest* (*mocket >= 3.9.1*)


Versioning
==========
Starting from *3.7.0*, Mocket major version will follow the same numbering pattern as Python's and therefore indicate the most recent Python version that is supported.
Expand Down
6 changes: 3 additions & 3 deletions mocket/__init__.py
@@ -1,10 +1,10 @@
try:
# Py2
from mocket import mocketize, Mocket, MocketEntry, Mocketizer
from mocket import Mocket, MocketEntry, Mocketizer, mocketize
except ImportError:
# Py3
from mocket.mocket import mocketize, Mocket, MocketEntry, Mocketizer
from mocket.mocket import Mocket, MocketEntry, Mocketizer, mocketize

__all__ = ("mocketize", "Mocket", "MocketEntry", "Mocketizer")

__version__ = "3.9.0"
__version__ = "3.9.1"
4 changes: 2 additions & 2 deletions mocket/mocket.py
Expand Up @@ -38,7 +38,7 @@
hasher = xxh32 or hashlib.md5

try: # pragma: no cover
from urllib3.contrib.pyopenssl import inject_into_urllib3, extract_from_urllib3
from urllib3.contrib.pyopenssl import extract_from_urllib3, inject_into_urllib3

pyopenssl_override = True
except ImportError:
Expand Down Expand Up @@ -386,7 +386,7 @@ def send(self, data, *args, **kwargs): # pragma: no cover
else:
req = Mocket.last_request()
if hasattr(req, "add_data"):
req.add_data(decode_from_bytes(data))
req.add_data(data)
self._entry = entry
return len(data)

Expand Down
15 changes: 12 additions & 3 deletions mocket/mockhttp.py
Expand Up @@ -31,6 +31,7 @@

class Request:
parser = None
_body = None

def __init__(self, data):
self.parser = HttpParser()
Expand All @@ -39,15 +40,20 @@ def __init__(self, data):
self.method = self.parser.get_method()
self.path = self.parser.get_path()
self.headers = self.parser.get_headers()
self.body = decode_from_bytes(self.parser.recv_body())
self.querystring = parse_qs(
unquote_utf8(self.parser.get_query_string()), keep_blank_values=True
)
if self.querystring:
self.path += "?{}".format(self.parser.get_query_string())

def add_data(self, data):
self.body += data
self.parser.execute(data, len(data))

@property
def body(self):
if self._body is None:
self._body = decode_from_bytes(self.parser.recv_body())
return self._body

def __str__(self):
return "{} - {} - {}".format(self.method, self.path, self.headers)
Expand Down Expand Up @@ -82,7 +88,10 @@ def get_protocol_data(self, str_format_fun_name="capitalize"):
status_code=self.status, status=STATUS[self.status]
)
header_lines = CRLF.join(
("{0}: {1}".format(getattr(k, str_format_fun_name)(), v) for k, v in self.headers.items())
(
"{0}: {1}".format(getattr(k, str_format_fun_name)(), v)
for k, v in self.headers.items()
)
)
return "{0}\r\n{1}\r\n\r\n".format(status_line, header_lines).encode("utf-8")

Expand Down
17 changes: 14 additions & 3 deletions mocket/plugins/httpretty/__init__.py
@@ -1,20 +1,31 @@
from sys import version_info

from mocket import Mocket, mocketize
from mocket.compat import byte_type, encode_to_bytes, text_type
from mocket.compat import byte_type, text_type
from mocket.mockhttp import Entry as MocketHttpEntry
from mocket.mockhttp import Request as MocketHttpRequest
from mocket.mockhttp import Response as MocketHttpResponse


def httprettifier_headers(headers):
return {k.lower().replace("_", "-"): v for k, v in headers.items()}


class Request(MocketHttpRequest):
@property
def body(self):
if self._body is None:
self._body = self.parser.recv_body()
return self._body


class Response(MocketHttpResponse):
def get_protocol_data(self, str_format_fun_name="lower"):
if "server" in self.headers and self.headers["server"] == "Python/Mocket":
self.headers["server"] = "Python/HTTPretty"
return super(Response, self).get_protocol_data(str_format_fun_name=str_format_fun_name)
return super(Response, self).get_protocol_data(
str_format_fun_name=str_format_fun_name
)

def set_base_headers(self):
super(Response, self).set_base_headers()
Expand All @@ -27,6 +38,7 @@ def set_extra_headers(self, headers):


class Entry(MocketHttpEntry):
request_cls = Request
response_cls = Response


Expand Down Expand Up @@ -102,7 +114,6 @@ def __init__(self):
def __getattr__(self, name):
if name == "last_request":
last_request = getattr(Mocket, "last_request")()
last_request.body = encode_to_bytes(last_request.body)
return last_request
elif name == "latest_requests":
return getattr(Mocket, "_requests")
Expand Down
36 changes: 36 additions & 0 deletions tests/main/test_http.py
Expand Up @@ -4,6 +4,7 @@
import io
import json
import os
import socket
import tempfile
import time
from unittest import TestCase
Expand Down Expand Up @@ -321,3 +322,38 @@ def test_post_file_object(self):
files = {"content": file_obj}
r = requests.post(url, files=files, data={}, verify=False)
self.assertEqual(r.status_code, 201)

@mocketize
def test_sockets(self):
"""
https://github.com/mindflayer/python-mocket/issues/111
https://gist.github.com/amotl/015ef6b336db55128798d7f1a9a67dea
"""

# Define HTTP conversation.
url = "http://127.0.0.1/api/data"
Entry.single_register(Entry.POST, url)

# Define HTTP url segments and data.
host = "127.0.0.1"
port = 80
method = "POST"
path = "/api/data"
data = json.dumps({"hello": "world"})

# Invoke HTTP request.
address = socket.getaddrinfo(host, port, 0, socket.SOCK_STREAM)[0]
sock = socket.socket(address[0], address[1], address[2])

sock.connect(address[-1])
sock.write("%s %s HTTP/1.0\r\n" % (method, path))
sock.write("Host: %s\r\n" % host)
sock.write("Content-Type: application/json\r\n")
sock.write("Content-Length: %d\r\n" % len(data))
sock.write("Connection: close\r\n\r\n")
sock.write(data)
sock.close()

# Proof that worked.
print(Mocket.last_request().__dict__)
assert Mocket.last_request().body == '{"hello": "world"}'

0 comments on commit e723252

Please sign in to comment.