Skip to content

Commit c026eb6

Browse files
committed
initial
0 parents  commit c026eb6

File tree

12 files changed

+965
-0
lines changed

12 files changed

+965
-0
lines changed

.dockerignore

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
node_modules
2+
Dockerfile*
3+
docker-compose*
4+
.dockerignore
5+
.git
6+
.gitignore
7+
README.md
8+
LICENSE
9+
.vscode
10+
Makefile
11+
helm-charts
12+
.env
13+
.editorconfig
14+
.idea
15+
coverage*

.github/workflows/test.yml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
name: Test
2+
3+
on:
4+
push:
5+
branches: [ master, main ]
6+
pull_request:
7+
branches: [ master, main ]
8+
9+
jobs:
10+
test:
11+
runs-on: ubuntu-latest
12+
13+
steps:
14+
- uses: actions/checkout@v4
15+
16+
- name: Setup Bun
17+
uses: oven-sh/setup-bun@v2
18+
with:
19+
bun-version: latest
20+
21+
- name: Install dependencies
22+
run: bun install
23+
24+
- name: Run tests
25+
run: bun test

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# deps
2+
node_modules/

Dockerfile

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
FROM oven/bun:alpine AS build
2+
3+
WORKDIR /app
4+
5+
COPY bun.lock package.json ./
6+
7+
RUN bun install --frozen-lockfile --production --verbose
8+
9+
COPY . .
10+
11+
RUN bun build --target bun --compile --minify --sourcemap ./src/index.ts --outfile resp-proxy
12+
13+
FROM oven/bun:alpine AS runner
14+
15+
# Required
16+
ENV LISTEN_PORT="6379"
17+
ENV TARGET_HOST=""
18+
ENV TARGET_PORT=""
19+
20+
# Optional
21+
ENV LISTEN_HOST="127.0.0.1"
22+
ENV TIMEOUT=""
23+
ENV ENABLE_LOGGING="false"
24+
ENV API_PORT="3000"
25+
26+
#Default proxy port
27+
EXPOSE 6379
28+
#Default api port
29+
EXPOSE 3000
30+
31+
WORKDIR /app
32+
33+
COPY --from=build /app/resp-proxy .
34+
35+
ENTRYPOINT ["./resp-proxy"]

README.md

Lines changed: 237 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,237 @@
1+
# Redis RESP Proxy
2+
3+
[![Test](https://github.com/redis-developer/cae-resp-proxy/actions/workflows/test.yml/badge.svg)](https://github.com/redis-developer/cae-resp-proxy/actions/workflows/test.yml)
4+
5+
A Redis protocol proxy with HTTP API for response injection.
6+
7+
## Features
8+
9+
- **Redis Transparent Protocol Proxy**: Forwards Redis connections from clients to target Redis servers
10+
- **HTTP API**: RESTful API for managing connections and sending arbitrary responses to clients
11+
- **Connection Management**: Track active connections, view statistics, and close connections
12+
- **Data Encoding Support**: Send data in base64 or raw binary format
13+
- **Docker Support**: Containerized deployment with environment variable configuration
14+
- **Real-time Stats**: Monitor active connections and proxy statistics
15+
16+
## Quick Start
17+
18+
### Prerequisites
19+
- Docker or [Bun](https://bun.sh/) runtime
20+
- Running Redis server (target)
21+
22+
### Basic Usage
23+
24+
#### Docker (Recommended)
25+
26+
The easiest way to get started is with Docker :
27+
28+
29+
```bash
30+
# Build the docker image first
31+
docker build -t resp-proxy .
32+
```
33+
34+
35+
36+
```bash
37+
# Run with Docker - connects to Redis on host
38+
docker run -d \
39+
-p 6379:6379 \ # the proxy will listen for incoming connections on this port
40+
-p 3000:3000 \ # the rest api will listen for http requests on this port
41+
-e TARGET_HOST=host.docker.internal \ #<-- redis server host ( the proxy target )
42+
-e TARGET_PORT=6380 \ # redis server port
43+
-e LISTEN_PORT=6379 \ # proxy listen port
44+
-e API_PORT = 3000 \ # rest api port
45+
resp-proxy
46+
```
47+
48+
### Local Development
49+
50+
```bash
51+
# Install dependencies
52+
bun install
53+
54+
# Start the proxy with CLI arguments
55+
bun run proxy --listenPort=6379 --targetHost=localhost --targetPort=6380
56+
57+
# Or use the dev mode with hot reload
58+
bun run dev --listenPort=6379 --targetHost=localhost --targetPort=6380
59+
```
60+
61+
#### Environment Variables
62+
You can use env variables instead of cli arguments.
63+
64+
```bash
65+
# Set required environment variables
66+
export LISTEN_PORT=6379
67+
export TARGET_HOST=localhost
68+
export TARGET_PORT=6380
69+
70+
# Optional variables
71+
export LISTEN_HOST=127.0.0.1
72+
export TIMEOUT=30000
73+
export ENABLE_LOGGING=true
74+
export API_PORT=3000
75+
76+
# Start the proxy
77+
bun run proxy
78+
```
79+
80+
## Configuration
81+
82+
### Required Parameters
83+
84+
| Parameter | CLI Flag | Environment Variable | Description |
85+
|-----------|----------|---------------------|-------------|
86+
| Target Host | `--targetHost` | `TARGET_HOST` | Redis server hostname/IP |
87+
| Target Port | `--targetPort` | `TARGET_PORT` | Redis server port |
88+
89+
### Optional Parameters
90+
91+
| Parameter | CLI Flag | Environment Variable | Default | Description |
92+
|-----------|----------|---------------------|---------|-------------|
93+
| Listen Port | `--listenPort` | `LISTEN_PORT` | `6379` | Port for Redis clients to connect to |
94+
| Listen Host | `--listenHost` | `LISTEN_HOST` | `127.0.0.1` | Host interface to bind to |
95+
| Timeout | `--timeout` | `TIMEOUT` | - | Connection timeout (ms) |
96+
| Enable Logging | `--enableLogging` | `ENABLE_LOGGING` | `false` | Verbose logging |
97+
| API Port | `--apiPort` | `API_PORT` | `3000` | HTTP API port |
98+
99+
## HTTP API Reference
100+
101+
The proxy provides a REST API for managing connections and sending arbitrary responses.
102+
103+
104+
### Endpoints
105+
106+
107+
#### Get Statistics
108+
```http
109+
GET /stats
110+
```
111+
Returns detailed proxy statistics including active connections, total connections, and connection details.
112+
113+
**Response:**
114+
```json
115+
{
116+
"activeConnections": 2,
117+
"totalConnections": 5,
118+
"connections": [
119+
{
120+
"id": "conn_123",
121+
"clientAddress": "127.0.0.1:54321",
122+
"connectedAt": "2024-01-01T10:00:00Z"
123+
}
124+
]
125+
}
126+
```
127+
128+
#### Get Active Connections
129+
```http
130+
GET /connections
131+
```
132+
Returns list of active connection IDs.
133+
134+
**Response:**
135+
```json
136+
{
137+
"connectionIds": ["conn_123", "conn_456"]
138+
}
139+
```
140+
141+
#### Send Data to Specific Client
142+
```http
143+
POST /send-to-client/{connectionId}?encoding={base64|raw}
144+
```
145+
Send Redis protocol data to a specific client connection.
146+
147+
**Parameters:**
148+
- `connectionId` (path): Target connection ID
149+
- `encoding` (query): Data encoding format (`base64` or `raw`, default: `base64`)
150+
151+
**Body:** Raw data or base64-encoded data
152+
153+
**Example:**
154+
```bash
155+
# Send PING command (base64 encoded)
156+
curl -X POST "http://localhost:3000/send-to-client/conn_123?encoding=base64" \
157+
-d "KjENCiQ0DQpQSU5HDQo="
158+
159+
# Send raw binary data
160+
curl -X POST "http://localhost:3000/send-to-client/conn_123?encoding=raw" \
161+
--data-binary "*1\r\n$4\r\nPING\r\n"
162+
```
163+
164+
**Response:**
165+
```json
166+
{
167+
"success": true,
168+
"connectionId": "conn_123"
169+
}
170+
```
171+
172+
#### Send Data to Multiple Clients
173+
```http
174+
POST /send-to-clients?connectionIds={id1,id2}&encoding={base64|raw}
175+
```
176+
Send data to multiple specific client connections.
177+
178+
**Parameters:**
179+
- `connectionIds` (query): Comma-separated list of connection IDs
180+
- `encoding` (query): Data encoding format (`base64` or `raw`, default: `base64`)
181+
182+
**Example:**
183+
```bash
184+
curl -X POST "http://localhost:3000/send-to-clients?connectionIds=conn_123,conn_456&encoding=base64" \
185+
-d "KjENCiQ0DQpQSU5HDQo="
186+
```
187+
188+
#### Send Data to All Clients
189+
```http
190+
POST /send-to-all-clients?encoding={base64|raw}
191+
```
192+
Broadcast data to all active client connections.
193+
194+
**Example:**
195+
```bash
196+
curl -X POST "http://localhost:3000/send-to-all-clients?encoding=base64" \
197+
-d "KjENCiQ0DQpQSU5HDQo="
198+
```
199+
200+
#### Close Connection
201+
```http
202+
DELETE /connections/{connectionId}
203+
```
204+
Forcefully close a specific client connection.
205+
206+
**Response:**
207+
```json
208+
{
209+
"success": true,
210+
"connectionId": "conn_123"
211+
}
212+
```
213+
214+
## Use Cases
215+
216+
### Testing Redis Applications
217+
Intercept and inspect Redis commands during development and testing.
218+
219+
### Response Injection
220+
Send custom Redis responses to specific clients for testing scenarios.
221+
222+
### Protocol Analysis
223+
Analyze Redis protocol communication patterns.
224+
225+
## Development
226+
227+
### Running Tests
228+
229+
```bash
230+
bun test
231+
```
232+
233+
### Development Mode
234+
235+
```bash
236+
bun run dev --targetHost=localhost --targetPort=6380
237+
```

bun.lock

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
{
2+
"lockfileVersion": 1,
3+
"workspaces": {
4+
"": {
5+
"name": "resp-proxy",
6+
"dependencies": {
7+
"@hono/zod-validator": "^0.7.2",
8+
"hono": "^4.8.5",
9+
"redis-monorepo": "github:redis/node-redis",
10+
"zod": "^4.0.8",
11+
},
12+
"devDependencies": {
13+
"@types/bun": "latest",
14+
"redis": "^5.6.1",
15+
},
16+
},
17+
},
18+
"packages": {
19+
"@hono/zod-validator": ["@hono/[email protected]", "", { "peerDependencies": { "hono": ">=3.9.0", "zod": "^3.25.0 || ^4.0.0" } }, "sha512-ub5eL/NeZ4eLZawu78JpW/J+dugDAYhwqUIdp9KYScI6PZECij4Hx4UsrthlEUutqDDhPwRI0MscUfNkvn/mqQ=="],
20+
21+
"@redis/bloom": ["@redis/[email protected]", "", { "peerDependencies": { "@redis/client": "^5.6.1" } }, "sha512-5/22U76IMEfn6TeZ+uvjXspHw+ykBF0kpBa8xouzeHaQMXs/auqBUOEYzU2VKYDvnd2RSpPTyIg82oB7PpUgLg=="],
22+
23+
"@redis/client": ["@redis/[email protected]", "", { "dependencies": { "cluster-key-slot": "1.1.2" } }, "sha512-bWHmSFIJ5w1Y4aHsYs46XMDHKQsBHFRhNcllYaBxz2Zl+lu+gbm5yI9BqxvKh48bLTs/Wx1Kns0gN2WIasE8MA=="],
24+
25+
"@redis/json": ["@redis/[email protected]", "", { "peerDependencies": { "@redis/client": "^5.6.1" } }, "sha512-cTggVzPIVuiFeXcEcnTRiUzV7rmUvM9KUYxWiHyjsAVACTEUe4ifKkvzrij0H/z3ammU5tfGACffDB3olBwtVA=="],
26+
27+
"@redis/search": ["@redis/[email protected]", "", { "peerDependencies": { "@redis/client": "^5.6.1" } }, "sha512-+eOjx8O2YoKygjqkLpTHqcAq0zKLjior+ee2tRBx/3RSf1+OHxiC9Y6NstshQpvB1XHqTw9n7+f0+MsRJZrp0g=="],
28+
29+
"@redis/time-series": ["@redis/[email protected]", "", { "peerDependencies": { "@redis/client": "^5.6.1" } }, "sha512-sd3q4jMJdoSO2akw1L9NrdFI1JJ6zeMgMUoTh4a34p9sY3AnOI4aDLCecy8L2IcPAP1oNR3TbLFJiCJDQ35QTA=="],
30+
31+
"@types/bun": ["@types/[email protected]", "", { "dependencies": { "bun-types": "1.2.19" } }, "sha512-d9ZCmrH3CJ2uYKXQIUuZ/pUnTqIvLDS0SK7pFmbx8ma+ziH/FRMoAq5bYpRG7y+w1gl+HgyNZbtqgMq4W4e2Lg=="],
32+
33+
"@types/node": ["@types/[email protected]", "", { "dependencies": { "undici-types": "~7.8.0" } }, "sha512-ut5FthK5moxFKH2T1CUOC6ctR67rQRvvHdFLCD2Ql6KXmMuCrjsSsRI9UsLCm9M18BMwClv4pn327UvB7eeO1w=="],
34+
35+
"@types/react": ["@types/[email protected]", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-AwAfQ2Wa5bCx9WP8nZL2uMZWod7J7/JSplxbTmBQ5ms6QpqNYm672H0Vu9ZVKVngQ+ii4R/byguVEUZQyeg44g=="],
36+
37+
"bun-types": ["[email protected]", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-uAOTaZSPuYsWIXRpj7o56Let0g/wjihKCkeRqUBhlLVM/Bt+Fj9xTo+LhC1OV1XDaGkz4hNC80et5xgy+9KTHQ=="],
38+
39+
"cluster-key-slot": ["[email protected]", "", {}, "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA=="],
40+
41+
"csstype": ["[email protected]", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="],
42+
43+
"hono": ["[email protected]", "", {}, "sha512-Up2cQbtNz1s111qpnnECdTGqSIUIhZJMLikdKkshebQSEBcoUKq6XJayLGqSZWidiH0zfHRCJqFu062Mz5UuRA=="],
44+
45+
"redis": ["[email protected]", "", { "dependencies": { "@redis/bloom": "5.6.1", "@redis/client": "5.6.1", "@redis/json": "5.6.1", "@redis/search": "5.6.1", "@redis/time-series": "5.6.1" } }, "sha512-O9DwAvcBm/lrlkGE0A6gNBtUdA8J9oD9njeLYlLzmm+MGTR7nd7VkpspfXqeXFg3gm89zldDqckyaHhXfhY80g=="],
46+
47+
"redis-monorepo": ["redis-monorepo@github:redis/node-redis#e96db0d", {}, "redis-node-redis-e96db0d"],
48+
49+
"undici-types": ["[email protected]", "", {}, "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw=="],
50+
51+
"zod": ["[email protected]", "", {}, "sha512-+MSh9cZU9r3QKlHqrgHMTSr3QwMGv4PLfR0M4N/sYWV5/x67HgXEhIGObdBkpnX8G78pTgWnIrBL2lZcNJOtfg=="],
52+
}
53+
}

package.json

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"name": "resp-proxy",
3+
"scripts": {
4+
"proxy": "bun run src/index.ts",
5+
"dev": "bun run --hot src/index.ts",
6+
"test": "bun test **/*.test.ts"
7+
},
8+
"dependencies": {
9+
"@hono/zod-validator": "^0.7.2",
10+
"hono": "^4.8.5",
11+
"redis-monorepo": "github:redis/node-redis",
12+
"zod": "^4.0.8"
13+
},
14+
"devDependencies": {
15+
"@types/bun": "latest",
16+
"redis": "^5.6.1"
17+
}
18+
}

0 commit comments

Comments
 (0)