Skip to content

Commit 7130d8a

Browse files
authored
add httpbin extension (#25)
1 parent fb04197 commit 7130d8a

30 files changed

+3865
-0
lines changed

httpbin/Makefile

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
VENV_BIN = python3 -m venv
2+
VENV_DIR ?= .venv
3+
VENV_ACTIVATE = $(VENV_DIR)/bin/activate
4+
VENV_RUN = . $(VENV_ACTIVATE)
5+
6+
venv: $(VENV_ACTIVATE)
7+
8+
$(VENV_ACTIVATE): setup.py setup.cfg
9+
test -d .venv || $(VENV_BIN) .venv
10+
$(VENV_RUN); pip install --upgrade pip setuptools plux wheel
11+
$(VENV_RUN); pip install --upgrade black isort pyproject-flake8 flake8-black flake8-isort
12+
$(VENV_RUN); pip install -e .
13+
touch $(VENV_DIR)/bin/activate
14+
15+
clean:
16+
rm -rf .venv/
17+
rm -rf build/
18+
rm -rf .eggs/
19+
rm -rf *.egg-info/
20+
21+
lint: venv
22+
$(VENV_RUN); python -m pflake8 --show-source
23+
24+
format: venv
25+
$(VENV_RUN); python -m isort .; python -m black .
26+
27+
install: venv
28+
$(VENV_RUN); python setup.py develop
29+
30+
dist: venv
31+
$(VENV_RUN); python setup.py sdist bdist_wheel
32+
33+
publish: clean-dist venv dist
34+
$(VENV_RUN); pip install --upgrade twine; twine upload dist/*
35+
36+
clean-dist: clean
37+
rm -rf dist/
38+
39+
.PHONY: clean clean-dist dist install publish

httpbin/README.md

+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
LocalStack httpbin extension
2+
===============================
3+
4+
A simple HTTP Request & Response Service directly in LocalStack
5+
using [httpbin](https://github.com/postmanlabs/httpbin).
6+
Get the full httpbin experience directly in LocalStack without connecting to httpbin.org!
7+
8+
The httpbin API is served through the hostname `http://httpbin.localhost.localstack.cloud:4566`.
9+
10+
## Install
11+
12+
Install the extension by running:
13+
14+
```bash
15+
localstack extensions install localstack-extension-httpbin
16+
```
17+
18+
## Usage
19+
20+
Opening http://httpbin.localhost.localstack.cloud:4566 in the browser will show you the flasgger UI:
21+
![Screenshot at 2023-07-27 14-33-03](https://github.com/localstack/localstack-extensions/assets/3996682/68442f91-13b8-4308-8f04-966340cff082)
22+
23+
And you can call the API endpoints just as you would httpbin.org.
24+
![Screenshot at 2023-07-27 14-34-15](https://github.com/localstack/localstack-extensions/assets/3996682/bebe444a-d6f9-4953-87ef-cca79daa00e8)
25+
26+
## Development
27+
28+
### Install local development version
29+
30+
To install the extension into localstack in developer mode, you will need Python 3.10, and create a virtual
31+
environment in the extensions project.
32+
33+
In the newly generated project, simply run
34+
35+
```bash
36+
make install
37+
```
38+
39+
Then, to enable the extension for LocalStack, run
40+
41+
```bash
42+
localstack extensions dev enable .
43+
```
44+
45+
You can then start LocalStack with `EXTENSION_DEV_MODE=1` to load all enabled extensions:
46+
47+
```bash
48+
EXTENSION_DEV_MODE=1 localstack start
49+
```
50+
51+
## Licensing
52+
53+
* httpbin is licensed under the ISC license: https://github.com/postmanlabs/httpbin/blob/master/LICENSE
54+
* The httpbin source code is vendored with this extension, slight modifications were made to make it
55+
compatible with the latest Python and Werkzeug version.
56+
The modifications retain the ISC license
57+
* The extension code is licensed under the Apache 2.0 License
+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
name = "localstack_httpbin"
+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import logging
2+
from typing import Optional
3+
4+
from localstack import config, constants
5+
from localstack.config import get_edge_url
6+
from localstack.extensions.api import Extension, http
7+
from localstack.utils.net import get_free_tcp_port
8+
9+
from localstack_httpbin.server import HttpbinServer
10+
11+
LOG = logging.getLogger(__name__)
12+
13+
14+
class HttpbinExtension(Extension):
15+
name = "httpbin"
16+
17+
hostname_prefix = "httpbin."
18+
19+
server: Optional[HttpbinServer]
20+
21+
def __init__(self):
22+
self.server = None
23+
24+
def on_extension_load(self):
25+
level = logging.DEBUG if config.DEBUG else logging.INFO
26+
logging.getLogger("localstack_httpbin").setLevel(level=level)
27+
logging.getLogger("httpbin").setLevel(level=level)
28+
29+
def on_platform_start(self):
30+
from localstack_httpbin.vendor.httpbin import core
31+
core.template['host'] = f"{self.get_public_hostname()}:{config.get_edge_port_http()}"
32+
33+
self.server = HttpbinServer(get_free_tcp_port())
34+
LOG.debug("starting httpbin on %s", self.server.url)
35+
self.server.start()
36+
37+
def get_public_hostname(self) -> str:
38+
# FIXME: reconcile with LOCALSTACK_HOST, but this should be accessible via the host
39+
return f"{self.hostname_prefix}{constants.LOCALHOST_HOSTNAME}"
40+
41+
def on_platform_ready(self):
42+
LOG.info("Serving httpbin on %s", get_edge_url(localstack_hostname=self.get_public_hostname()))
43+
44+
def on_platform_shutdown(self):
45+
if self.server:
46+
self.server.shutdown()
47+
48+
def update_gateway_routes(self, router: http.Router[http.RouteHandler]):
49+
endpoint = http.ProxyHandler(forward_base_url=self.server.url)
50+
51+
router.add("/", host=f"{self.hostname_prefix}<host>", endpoint=endpoint)
52+
router.add("/<path:path>", host=f"{self.hostname_prefix}<host>", endpoint=endpoint)

httpbin/localstack_httpbin/server.py

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import logging
2+
3+
from localstack.utils.run import ShellCommandThread
4+
from localstack.utils.serving import Server
5+
from localstack.utils.threads import TMP_THREADS
6+
7+
8+
class HttpbinServer(Server):
9+
logger = logging.getLogger("httpbin")
10+
11+
def do_start_thread(self):
12+
thread = ShellCommandThread(
13+
[
14+
"/opt/code/localstack/.venv/bin/python",
15+
"-m",
16+
"localstack_httpbin.vendor.httpbin.core",
17+
"--port",
18+
str(self.port),
19+
],
20+
log_listener=self._log_listener,
21+
)
22+
TMP_THREADS.append(thread)
23+
thread.start()
24+
return thread
25+
26+
def _log_listener(self, line, **_kwargs):
27+
self.logger.debug(line.rstrip())
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
"""Vendored libraries"""
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# -*- coding: utf-8 -*-
2+
3+
from .core import *

0 commit comments

Comments
 (0)