Skip to content

Commit acb4078

Browse files
authored
Merge pull request #8 from appwrite/copilot/add-support-for-all-requests
2 parents a611aa7 + ef1f96f commit acb4078

6 files changed

Lines changed: 460 additions & 1 deletion

File tree

.github/workflows/tests.yml

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
name: Tests
2+
3+
on:
4+
push:
5+
branches: [main, master]
6+
pull_request:
7+
branches: [main, master]
8+
9+
permissions:
10+
contents: read
11+
12+
jobs:
13+
test:
14+
runs-on: ubuntu-latest
15+
16+
steps:
17+
- uses: actions/checkout@v4
18+
19+
- name: Set up Python
20+
uses: actions/setup-python@v5
21+
with:
22+
python-version: '3.11'
23+
24+
- name: Install dependencies
25+
run: |
26+
python -m pip install --upgrade pip
27+
pip install flask pytest
28+
29+
- name: Run tests
30+
run: |
31+
pytest test_app.py -v

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
__pycache__/
2+
*.pyc
3+
*.pyo
4+
.pytest_cache/

Dockerfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ USER catcher
2525
WORKDIR /home/catcher
2626

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

2930
EXPOSE 5000
3031

README.md

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,59 @@ Refer [docs](https://docs.docker.com/) for general documentation and guides for
2828
docker run appwrite/requestcatcher
2929
```
3030

31+
### API Endpoints
32+
33+
The RequestCatcher exposes the following endpoints:
34+
35+
| Endpoint | Method | Description |
36+
|----------|--------|-------------|
37+
| `/__last_request__` | GET | Returns the last captured request |
38+
| `/__all_requests__` | GET | Returns all captured requests (useful for parallel test runs) |
39+
| `/__find_request__` | GET | Find requests matching header/body values (see below) |
40+
| `/__clear__` | POST, DELETE | Clears all captured requests |
41+
| `/*` | Any | Captures any request made to any path |
42+
43+
#### Example: Get All Requests
44+
45+
```bash
46+
curl http://localhost:5000/__all_requests__
47+
```
48+
49+
#### Example: Find Specific Request
50+
51+
Find requests by header value:
52+
```bash
53+
curl "http://localhost:5000/__find_request__?header_X-Custom-Id=abc123"
54+
```
55+
56+
Find requests by body content:
57+
```bash
58+
curl "http://localhost:5000/__find_request__?body=test-event"
59+
```
60+
61+
Find requests by HTTP method:
62+
```bash
63+
curl "http://localhost:5000/__find_request__?method=POST"
64+
```
65+
66+
Combine multiple filters:
67+
```bash
68+
curl "http://localhost:5000/__find_request__?method=POST&header_Content-Type=application/json"
69+
```
70+
71+
#### Example: Clear Requests
72+
73+
```bash
74+
curl -X POST http://localhost:5000/__clear__
75+
```
76+
3177
### Environment Variables
3278

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

3585
### Build
3686

app.py

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
from collections import deque
2+
from datetime import datetime
3+
from flask import Flask, request, jsonify
4+
from os import environ
5+
6+
app = Flask('HTTP Request Catcher')
7+
8+
# Maximum number of requests to store in history (configurable via environment variable)
9+
MAX_REQUEST_HISTORY = int(environ.get('MAX_REQUEST_HISTORY', 1000))
10+
11+
last_request = None
12+
all_requests = deque(maxlen=MAX_REQUEST_HISTORY)
13+
14+
15+
@app.route('/__last_request__', methods=['GET'])
16+
def get_last_request():
17+
return jsonify(last_request), 200
18+
19+
20+
@app.route('/__all_requests__', methods=['GET'])
21+
def get_all_requests():
22+
return jsonify(list(all_requests)), 200
23+
24+
25+
@app.route('/__find_request__', methods=['GET'])
26+
def find_request():
27+
"""
28+
Find requests matching header or body values.
29+
Query parameters:
30+
- header_<name>=<value>: Match requests with specific header value
31+
- body=<value>: Match requests containing this value in body
32+
- method=<value>: Match requests with specific HTTP method
33+
- url=<value>: Match requests with URL containing this value
34+
"""
35+
matches = []
36+
37+
for req in all_requests:
38+
match = True
39+
40+
for key, value in request.args.items():
41+
if key.startswith('header_'):
42+
header_name = key[7:] # Remove 'header_' prefix
43+
req_headers = {k.lower(): v for k, v in req['headers'].items()}
44+
if req_headers.get(header_name.lower()) != value:
45+
match = False
46+
break
47+
elif key == 'body':
48+
if value not in req.get('data', ''):
49+
match = False
50+
break
51+
elif key == 'method':
52+
if req.get('method', '').upper() != value.upper():
53+
match = False
54+
break
55+
elif key == 'url':
56+
if value not in req.get('url', ''):
57+
match = False
58+
break
59+
60+
if match:
61+
matches.append(req)
62+
63+
return jsonify(matches), 200
64+
65+
66+
@app.route('/__clear__', methods=['POST', 'DELETE'])
67+
def clear_requests():
68+
global last_request
69+
last_request = None
70+
all_requests.clear()
71+
return '', 204
72+
73+
74+
@app.route('/', defaults={'path': ''}, methods=['PUT', 'POST', 'GET', 'HEAD', 'DELETE', 'PATCH', 'OPTIONS'])
75+
@app.route('/<path:path>', methods=['PUT', 'POST', 'GET', 'HEAD', 'DELETE', 'PATCH', 'OPTIONS'])
76+
def catch(path):
77+
global last_request
78+
79+
last_request = {
80+
'method': request.method,
81+
'data': request.data.decode('utf-8'),
82+
'headers': dict(request.headers),
83+
'url': request.url,
84+
'time': datetime.now().isoformat(),
85+
}
86+
all_requests.append(last_request)
87+
88+
return '', 200
89+
90+
91+
if __name__ == '__main__':
92+
app.run(host='0.0.0.0', port=5000)

0 commit comments

Comments
 (0)