http-tunnel-common 0.2.1

Common utilities and types for http-tunnel
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
# HTTP Tunnel

> 🌐 **[English]#english | [δΈ­ζ–‡ζ–‡ζ‘£]./README_CN.md**

A serverless HTTP tunnel built with Rust and AWS Lambda, providing secure access to local development servers through public URLs - similar to ngrok, but fully serverless and self-hosted.

## English

### Overview

HTTP Tunnel allows you to expose local services (like `localhost:3000`) to the internet through a public URL. Perfect for:

- Testing webhooks during local development (Stripe, GitHub, Twilio, etc.)
- Sharing work-in-progress with clients or teammates
- Testing mobile apps against local backends
- Demoing features without deploying
- API development with external services requiring public URLs
- IoT development with callback testing

**Architecture**: Fully serverless (AWS Lambda + API Gateway + DynamoDB) for cost-effective, auto-scaling infrastructure with zero operational overhead.

### Features

- **Serverless Architecture**: Zero operational overhead, pay only for actual usage
- **Secure WebSocket Tunneling**: Encrypted persistent connections (WSS/HTTPS)
- **Automatic Reconnection**: Exponential backoff strategy handles network interruptions gracefully
- **JWT/JWKS Authentication**: Optional token-based authentication with RSA/HMAC support
- **Custom Domains**: Support for custom domain names with ACM certificates
- **Fast & Efficient**: Low-latency request forwarding powered by Rust performance
- **Event-Driven**: Optional DynamoDB Streams + EventBridge for optimized response delivery
- **Load Testing Ready**: Handles concurrent requests with proper timeout handling
- **Multiple HTTP Methods**: Full support for GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS
- **Binary Data Support**: Base64 encoding for binary request/response bodies
- **Open Source**: MIT licensed, fully customizable and auditable

## Quick Start

### Prerequisites

- Rust 1.70+ with cargo
- AWS account with configured credentials
- Node.js 18+ (for infrastructure deployment)
- Pulumi CLI (for infrastructure management)

### Installation

#### Option 1: Build from source

```bash
# Clone the repository
git clone https://github.com/tyrchen/http-tunnel.git
cd http-tunnel

# Build the forwarder agent
cargo build --release --bin ttf

# The binary will be at target/release/ttf
```

#### Option 2: Install via cargo

```bash
cargo install --git https://github.com/tyrchen/http-tunnel --bin ttf
```

### Deploy Infrastructure

```bash
# Deploy AWS infrastructure (Lambda, API Gateway, DynamoDB)
make deploy-infra

# Note the WebSocket endpoint from the output
```

### Start Forwarding

```bash
# Forward local service to the internet (uses default endpoint and port 3000)
ttf

# Or specify custom endpoint and port
ttf --endpoint wss://YOUR_WEBSOCKET_ENDPOINT --local-port 8080

# You'll receive a public URL like: https://abc123.execute-api.us-west-2.amazonaws.com
```

Now any HTTP request to your public URL will be forwarded to your local service.

**Default Configuration**:

- Endpoint: `wss://ws.example.com/dev`
- Local port: `3000`
- Local host: `127.0.0.1`

## Architecture

```
Internet β†’ API Gateway HTTP β†’ Lambda β†’ WebSocket β†’ Local Agent β†’ localhost:3000
                              ↓
                         DynamoDB (state)
```

**Components**:

- **Local Forwarder** (`ttf`): Rust CLI agent running on your machine
- **Lambda Handler**: Serverless functions for WebSocket and HTTP handling
- **API Gateway**: WebSocket API for agent connections, HTTP API for public requests
- **DynamoDB**: Tracks connections and pending requests

For detailed architecture, see [specs/0001-idea.md](specs/0001-idea.md).

## Usage

### Basic Usage

```bash
# Forward localhost:3000 to the internet (uses default endpoint)
ttf

# Or specify custom endpoint
ttf --endpoint wss://YOUR_ENDPOINT
```

### With Custom Port

```bash
# Forward a different local port
ttf --port 8080

# Or use short form
ttf -p 8080
```

### With Custom Domain

```bash
# Use your own domain (requires custom domain setup)
ttf --endpoint wss://ws.yourdomain.com
```

### Environment Variables

```bash
# Override default endpoint via environment variable
export TUNNEL_ENDPOINT=wss://YOUR_CUSTOM_ENDPOINT

# Set authentication token
export TUNNEL_TOKEN=your_jwt_token

# Run with environment configuration
ttf
```

## Project Structure

```
http-tunnel/
β”œβ”€β”€ apps/
β”‚   β”œβ”€β”€ forwarder/          # Local agent CLI (ttf binary)
β”‚   └── handler/            # AWS Lambda function
β”œβ”€β”€ crates/
β”‚   └── common/             # Shared library (protocol, models, utilities)
β”œβ”€β”€ infra/                  # Pulumi infrastructure as code
β”‚   β”œβ”€β”€ src/                # TypeScript infrastructure modules
β”‚   β”œβ”€β”€ scripts/            # Deployment helper scripts
β”‚   └── README.md           # Infrastructure documentation
β”œβ”€β”€ testapp/                # Example TodoMVC API server for testing
β”‚   β”œβ”€β”€ main.py             # FastAPI application
β”‚   └── pyproject.toml      # Python dependencies
└── specs/                  # Architecture and implementation specs
    β”œβ”€β”€ 0001-idea.md        # Architecture design
    β”œβ”€β”€ 0002-common.md      # Common library spec
    β”œβ”€β”€ 0003-forwarder.md   # Forwarder agent spec
    β”œβ”€β”€ 0004-lambda.md      # Lambda functions spec
    β”œβ”€β”€ 0005-iac.md         # Infrastructure spec
    └── 0006-implementation-plan.md
```

## Development

### Build Commands

```bash
# Build all components
cargo build

# Build forwarder agent only
cargo build --bin ttf

# Build Lambda handler (requires cargo-lambda)
cargo lambda build --release --arm64 --bin handler

# Run tests
cargo test

# Run linter
cargo clippy
```

### Test Application

A sample TodoMVC API server is included in `testapp/` for testing the HTTP tunnel:

```bash
# Run the test app on port 3000
make run-testapp

# The API will be available at http://localhost:3000
# Interactive docs at http://localhost:3000/docs
```

**Test App Features**:

- In-memory CRUD API for todo items
- Pre-loaded with meaningful dummy data
- RESTful endpoints: GET, POST, PUT, DELETE
- Perfect for testing the tunnel forwarding functionality

**Example Usage**:

```bash
# In terminal 1: Start the test app
make run-testapp

# In terminal 2: Start the tunnel forwarder (uses default endpoint and port 3000)
ttf

# In terminal 3: Access your local app via the public tunnel URL
curl https://YOUR_TUNNEL_URL/todos
```

### Infrastructure Commands

```bash
# Preview infrastructure changes
make preview-infra

# Deploy infrastructure
make deploy-infra

# Destroy infrastructure
make destroy-infra
```

### Custom Domain Setup

To use your own domain instead of API Gateway URLs:

1. Configure your domain in `infra/Pulumi.dev.yaml`
2. Set up ACM certificate (see [infra/QUICKSTART_CUSTOM_DOMAIN.md]infra/QUICKSTART_CUSTOM_DOMAIN.md)
3. Deploy infrastructure
4. Configure DNS records

See [infra/README.md](infra/README.md) for detailed instructions.

## How It Works

1. **Agent Connection**: The `ttf` CLI connects to AWS API Gateway WebSocket endpoint
2. **Registration**: Lambda assigns a unique subdomain/connection ID
3. **HTTP Request**: User makes HTTP request to the public URL
4. **Forwarding**: Lambda looks up the connection and forwards request via WebSocket
5. **Local Processing**: Agent receives request and forwards to local service
6. **Response**: Agent sends response back through WebSocket
7. **Completion**: Lambda receives response and returns to original HTTP caller

## Configuration

### Forwarder Configuration

```bash
ttf --help

Options:
  -e, --endpoint <URL>       WebSocket endpoint URL [default: wss://ws.example.com/dev]
  -p, --port <PORT>          Local service port to forward to [default: 3000]
  --host <HOST>              Local service host [default: 127.0.0.1]
  -t, --token <TOKEN>        Authentication token (JWT)
  -v, --verbose              Enable verbose logging
  --connect-timeout <SECS>   Connection timeout in seconds [default: 10]
  --request-timeout <SECS>   Request timeout in seconds [default: 25]
```

**Environment Variables**:

- `TUNNEL_ENDPOINT`: Override default WebSocket endpoint
- `TUNNEL_TOKEN`: Set authentication token

### Infrastructure Configuration

Edit `infra/Pulumi.dev.yaml`:

```yaml
config:
  aws:region: us-west-2
  aws:profile: your-aws-profile
  http-tunnel:environment: dev
  http-tunnel:lambdaArchitecture: arm64
  http-tunnel:lambdaMemorySize: "256"
  http-tunnel:lambdaTimeout: "30"
  http-tunnel:enableCustomDomain: "false"
```

See [infra/README.md](infra/README.md) for all configuration options.

## Cost Estimation

Approximate monthly costs (us-west-2 region):

| Service               | Usage                         | Cost            |
| --------------------- | ----------------------------- | --------------- |
| Lambda                | 1M requests, 256MB, 500ms avg | ~$3.00          |
| API Gateway WebSocket | 1M messages                   | ~$1.00          |
| API Gateway HTTP      | 1M requests                   | ~$1.00          |
| DynamoDB              | 1M reads, 100K writes         | ~$0.50          |
| Custom Domains        | 2 domains (optional)          | ~$2.00          |
| **Total**             |                               | **~$5.50-7.50** |

AWS Free Tier may significantly reduce costs for development/testing usage.

## Monitoring

The deployed infrastructure includes CloudWatch logs for:

- WebSocket connection events
- HTTP request forwarding
- Lambda function execution
- Error tracking

Access logs via AWS Console or CLI:

```bash
# View Lambda logs
aws logs tail /aws/lambda/http-tunnel-handler-dev --follow

# View API Gateway logs
aws logs tail /aws/apigateway/http-tunnel-dev --follow
```

## Troubleshooting

### Connection Issues

**Problem**: Agent can't connect to WebSocket endpoint

**Solution**:

1. Verify endpoint URL is correct (should start with `wss://`)
2. Check AWS credentials are configured
3. Ensure infrastructure is deployed (`make deploy-infra`)
4. Check CloudWatch logs for errors

### Request Timeout

**Problem**: HTTP requests timeout waiting for response

**Solution**:

1. Ensure local service is running on specified port
2. Check agent is connected (should show "Connected" in logs)
3. Verify no firewall blocking local connections
4. Check Lambda timeout settings (increase if needed)

### Custom Domain Not Working

**Problem**: Custom domain not resolving or returns errors

**Solution**:

1. Verify ACM certificate is in "ISSUED" status
2. Check DNS records are correctly configured
3. Wait 5-10 minutes for DNS propagation
4. See [infra/CUSTOM_DOMAIN_SETUP.md]infra/CUSTOM_DOMAIN_SETUP.md for detailed troubleshooting

## Documentation

- **[specs/README.md]specs/README.md**: Complete technical specifications
- **[specs/0001-idea.md]specs/0001-idea.md**: Architecture design document
- **[infra/README.md]infra/README.md**: Infrastructure deployment guide
- **[infra/QUICKSTART_CUSTOM_DOMAIN.md]infra/QUICKSTART_CUSTOM_DOMAIN.md**: Custom domain quick setup
- **[infra/CUSTOM_DOMAIN_SETUP.md]infra/CUSTOM_DOMAIN_SETUP.md**: Complete custom domain reference

## Contributing

Contributions are welcome! Please:

1. Fork the repository
2. Create a feature branch
3. Make your changes
4. Add tests for new functionality
5. Ensure all tests pass (`cargo test`)
6. Run linter (`cargo clippy`)
7. Submit a pull request

## Comparison with ngrok

| Feature              | HTTP Tunnel         | ngrok                 |
| -------------------- | ------------------- | --------------------- |
| **Deployment**       | Self-hosted (AWS)   | SaaS                  |
| **Cost**             | Pay AWS costs (~$5) | Free/$10-$35/month    |
| **Custom Domain**    | βœ… Included         | βœ… (paid plans)       |
| **Open Source**      | βœ… MIT License      | ❌ Proprietary        |
| **Data Privacy**     | Your AWS account    | ngrok servers         |
| **Scaling**          | Auto (serverless)   | Managed by ngrok      |
| **Setup Complexity** | Medium (AWS + Rust) | Easy (download & run) |

## Security

- **End-to-end TLS**: All communication encrypted (HTTPS + WSS)
- **Isolated Connections**: Each connection has unique credentials
- **No Persistent Storage**: Request/response data not stored
- **IAM Policies**: Least-privilege access for Lambda functions
- **TTL Cleanup**: Automatic cleanup of stale data

For production use, consider:

- Implementing authentication on the WebSocket connection
- Adding request filtering/validation
- Setting up AWS WAF rules
- Enabling VPC endpoints for Lambda-DynamoDB communication

## License

This project is distributed under the terms of MIT.

See [LICENSE](LICENSE.md) for details.

Copyright 2025 Tyr Chen

## Acknowledgments

Inspired by [ngrok](https://ngrok.com/) and built with:

- [Rust]https://www.rust-lang.org/ - Systems programming language
- [Tokio]https://tokio.rs/ - Async runtime
- [AWS Lambda]https://aws.amazon.com/lambda/ - Serverless compute
- [Pulumi]https://www.pulumi.com/ - Infrastructure as code