Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
name: Tests

on:
push:
branches: [main, master]
pull_request:
branches: [main, master]

permissions:
contents: read

jobs:
test:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install flask pytest

- name: Run tests
run: |
pytest test_app.py -v
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
__pycache__/
*.pyc
*.pyo
.pytest_cache/
1 change: 1 addition & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ USER catcher
WORKDIR /home/catcher

COPY --chown=catcher:catcher --from=build /root/http-request-catcher /home/catcher
COPY --chown=catcher:catcher app.py /home/catcher/app.py

EXPOSE 5000

Expand Down
52 changes: 51 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,59 @@ Refer [docs](https://docs.docker.com/) for general documentation and guides for
docker run appwrite/requestcatcher
```

### API Endpoints

The RequestCatcher exposes the following endpoints:

| Endpoint | Method | Description |
|----------|--------|-------------|
| `/__last_request__` | GET | Returns the last captured request |
| `/__all_requests__` | GET | Returns all captured requests (useful for parallel test runs) |
| `/__find_request__` | GET | Find requests matching header/body values (see below) |
| `/__clear__` | POST, DELETE | Clears all captured requests |
| `/*` | Any | Captures any request made to any path |

#### Example: Get All Requests

```bash
curl http://localhost:5000/__all_requests__
```

#### Example: Find Specific Request

Find requests by header value:
```bash
curl "http://localhost:5000/__find_request__?header_X-Custom-Id=abc123"
```

Find requests by body content:
```bash
curl "http://localhost:5000/__find_request__?body=test-event"
```

Find requests by HTTP method:
```bash
curl "http://localhost:5000/__find_request__?method=POST"
```

Combine multiple filters:
```bash
curl "http://localhost:5000/__find_request__?method=POST&header_Content-Type=application/json"
```

#### Example: Clear Requests

```bash
curl -X POST http://localhost:5000/__clear__
```

### Environment Variables

This container supports all environment variables supplied by the original smarterdm/http-request-catcher Docker image.
| Variable | Default | Description |
|----------|---------|-------------|
| `MAX_REQUEST_HISTORY` | 1000 | Maximum number of requests to store in history. Older requests are automatically removed when this limit is exceeded. |

This container also supports all environment variables supplied by the original smarterdm/http-request-catcher Docker image.

### Build

Expand Down
92 changes: 92 additions & 0 deletions app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
from collections import deque
from datetime import datetime
from flask import Flask, request, jsonify
from os import environ

app = Flask('HTTP Request Catcher')

# Maximum number of requests to store in history (configurable via environment variable)
MAX_REQUEST_HISTORY = int(environ.get('MAX_REQUEST_HISTORY', 1000))

last_request = None
all_requests = deque(maxlen=MAX_REQUEST_HISTORY)


@app.route('/__last_request__', methods=['GET'])
def get_last_request():
return jsonify(last_request), 200


@app.route('/__all_requests__', methods=['GET'])
def get_all_requests():
return jsonify(list(all_requests)), 200


@app.route('/__find_request__', methods=['GET'])
def find_request():
"""
Find requests matching header or body values.
Query parameters:
- header_<name>=<value>: Match requests with specific header value
- body=<value>: Match requests containing this value in body
- method=<value>: Match requests with specific HTTP method
- url=<value>: Match requests with URL containing this value
"""
matches = []

for req in all_requests:
match = True

for key, value in request.args.items():
if key.startswith('header_'):
header_name = key[7:] # Remove 'header_' prefix
req_headers = {k.lower(): v for k, v in req['headers'].items()}
if req_headers.get(header_name.lower()) != value:
match = False
break
elif key == 'body':
if value not in req.get('data', ''):
match = False
break
elif key == 'method':
if req.get('method', '').upper() != value.upper():
match = False
break
elif key == 'url':
if value not in req.get('url', ''):
match = False
break

if match:
matches.append(req)

return jsonify(matches), 200


@app.route('/__clear__', methods=['POST', 'DELETE'])
def clear_requests():
global last_request
last_request = None
all_requests.clear()
return '', 204


@app.route('/', defaults={'path': ''}, methods=['PUT', 'POST', 'GET', 'HEAD', 'DELETE', 'PATCH', 'OPTIONS'])
@app.route('/<path:path>', methods=['PUT', 'POST', 'GET', 'HEAD', 'DELETE', 'PATCH', 'OPTIONS'])
def catch(path):
global last_request

last_request = {
'method': request.method,
'data': request.data.decode('utf-8'),
'headers': dict(request.headers),
'url': request.url,
'time': datetime.now().isoformat(),
}
all_requests.append(last_request)

return '', 200


if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
Loading