Skip to content

Instantly share code, notes, and snippets.

@podhmo
Last active August 13, 2018 15:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save podhmo/14aa242415ec2592eeebb71c9f821f43 to your computer and use it in GitHub Desktop.
Save podhmo/14aa242415ec2592eeebb71c9f821f43 to your computer and use it in GitHub Desktop.
$ make server&
[1] 27748
python server.py --port 5000
$ make proxy&
[2] 27754
python proxy.py --port 5001
$ make client
http :5001/api/foo
127.0.0.1 - - [14/Aug/2018 00:12:29] "GET / HTTP/1.1" 200 28
127.0.0.1 - - [14/Aug/2018 00:12:29] "GET /api/foo HTTP/1.1" 200 36
HTTP/1.0 200 OK
Content-Length: 36
Content-type: application/json; charset=utf-8
Date: Mon, 13 Aug 2018 15:12:29 GMT
Server: WSGIServer/0.2 CPython/3.7.0

{
    "age": "**20**",
    "name": "**foo**"
}
server:
python server.py --port 5000
proxy:
python proxy.py --port 5001
client:
http :5001/api/foo
client2:
echo '{"foo": "boo"}' | http --json POST :5001 headerX:headerV qsK==qsV
client3:
http --form POST :5001 headerX:headerV qsK==qsV foo=boo
import typing as t
import typing_extensions as tx
import json
from wsgiref.simple_server import make_server
import requests
class Request:
def __init__(self, environ):
self.environ = environ
@property
def wsgi_input(self):
return self.environ["wsgi.input"]
@property
def path(self):
return self.environ["PATH_INFO"]
@property
def method(self):
return self.environ["REQUEST_METHOD"]
@property
def query_string(self):
return self.environ.get("QUERY_STRING")
@property
def headers(self):
environ = self.environ
return {k[5:].replace("_", "-"): environ[k] for k in environ if k.startswith("HTTP_")}
@property
def content_type(self):
return self.environ.get("CONTENT_TYPE")
@property
def content_length(self):
v = self.environ.get("CONTENT_LENGTH") or None
if v is None:
return None
return int(v)
@property
def data(self):
if not self.content_length:
return None
return self.wsgi_input.read(self.content_length)
class Response(tx.Protocol):
status_code: int
reason: str
headers: t.Dict[str, str]
@property
def content(self) -> bytes:
...
def json(self) -> dict:
...
class Proxy:
def __init__(
self,
request: t.Callable[[Request], Response],
response: t.Callable[[Response], bytes],
):
self.request = request
self.response = response
def __call__(
self,
environ: dict,
start_response: t.Callable[[str, t.List[t.Tuple[str, str]]], None],
) -> None:
response = self.request(Request(environ))
if response.status_code == 200:
content = self.response(response)
else:
content = response.content
start_response(f'{response.status_code} {response.reason}', list(response.headers.items()))
return [content]
def main(port=4444):
def request(req: Request) -> Response:
url = f"http://localhost:5000{req.path}"
if req.query_string:
url = f"{url}?{req.query_string}"
return requests.request(req.method, url, data=req.data, headers=req.headers)
def response(res: Response) -> bytes:
if not res.headers.get("Content-Type", "").lstrip().startswith("application/json"):
return res.content
res.headers.pop("Content-Length", None)
body = res.json()
for k in list(body.keys()):
body[k] = f"**{body[k]}**"
return json.dumps(body).encode("utf-8")
proxy = Proxy(request, response)
with make_server('', port, proxy) as httpd:
httpd.serve_forever()
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("--port", type=int, default=4444)
args = parser.parse_args()
main(port=args.port)
requests
typing-extensions
import json
from wsgiref.simple_server import make_server
def app(environ, start_response):
status = '200 OK'
headers = [('Content-type', 'application/json; charset=utf-8')]
start_response(status, headers)
data = {
"name": "foo",
"age": "20",
}
return [json.dumps(data).encode("utf-8")]
def main(port=4445):
with make_server('', port, app) as httpd:
httpd.serve_forever()
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("--port", type=int, default=4444)
args = parser.parse_args()
main(port=args.port)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment