Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
0e97e6b
Add HTTP/2 support implementation
renatillas Aug 28, 2025
cb41e7e
Refactor HTTP/2 code to use functional approaches over pattern matching
renatillas Aug 28, 2025
1cad8c2
Refactor to use Gleam's 'use' syntax instead of deep nesting
renatillas Aug 28, 2025
025eecf
Remove debug logging statements and clean up unused imports
renatillas Aug 28, 2025
9ab5a0a
Remove unnecessary comments that clutter the code
renatillas Aug 28, 2025
518a11c
Remove problematic test_http2.sh script and update documentation
renatillas Aug 28, 2025
9bfe1a4
Remove certificate files from repository and ignore them
renatillas Aug 29, 2025
4ed364f
Revert "Remove unnecessary comments that clutter the code"
renatillas Aug 29, 2025
d66cd0d
Revert "Remove debug logging statements and clean up unused imports"
renatillas Aug 29, 2025
8eb52bb
Revert "Refactor to use Gleam's 'use' syntax instead of deep nesting"
renatillas Aug 29, 2025
7959711
Revert "Refactor HTTP/2 code to use functional approaches over patter…
renatillas Aug 29, 2025
b7847db
Refactoring
renatillas Aug 29, 2025
880f7aa
Fix compilation errors in HTTP/2 refactoring
renatillas Aug 29, 2025
f883eda
Remove aliasing
renatillas Aug 29, 2025
3468d3d
Merge pull request #1 from renatillas/feature/http2-implementation
renatillas Aug 29, 2025
d48d12a
remove clutter
renatillas Aug 29, 2025
82cf4d7
Fix issues
renatillas Sep 2, 2025
68050a2
Refactor
renatillas Sep 2, 2025
669e428
Fix frame shadowing
renatillas Sep 2, 2025
594d16f
Assert failures dont crash
renatillas Sep 2, 2025
51476f9
add to commit missing change
renatillas Sep 2, 2025
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
6 changes: 6 additions & 0 deletions examples/http2/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
*.beam
*.ez
/build
erl_crash.dump
*.crt
*.key
274 changes: 274 additions & 0 deletions examples/http2/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,274 @@
# HTTP/2 Comprehensive Example for Mist

This example demonstrates all HTTP/2 capabilities supported by the Mist web server, including **full H2C (HTTP/2 cleartext) upgrade support**.

## Features Demonstrated

### Core HTTP/2 Features
- ✅ **H2C Upgrade**: Complete HTTP/1.1 → HTTP/2 upgrade mechanism
- ✅ **Multiplexing**: Multiple requests/responses over a single connection
- ✅ **Header Compression (HPACK)**: Efficient header encoding
- ✅ **Flow Control**: Window-based flow control for streams
- ✅ **Binary Framing**: Binary protocol instead of text-based HTTP/1.1
- ✅ **Stream Prioritization**: Request prioritization (client-dependent)

### Configuration Options
- Custom max concurrent streams
- Configurable initial window size
- Adjustable max frame size
- Optional max header list size

## Running the Example

### 1. Install Dependencies
```bash
cd examples/http2
gleam deps download
gleam build
```

### 2. Start the Server
```bash
gleam run
```

This starts two servers:
- **HTTP/2 (h2c)** on http://localhost:9080 - HTTP/2 over cleartext with H2C upgrade
- **HTTP/2 (TLS)** on https://localhost:8443 - HTTP/2 over TLS (requires certificates)

### 3. Generate TLS Certificates (for HTTPS)
```bash
./generate_certs.sh
```

## Available Endpoints

| Method | Path | Description | HTTP/2 Feature Tested |
|--------|------|-------------|----------------------|
| GET | `/` | Server info and capabilities | Basic connection |
| GET/POST | `/echo` | Echo request details | Header inspection |
| GET | `/stream` | Streaming response | Chunked data |
| GET | `/large` | 100KB response | Flow control |
| GET | `/headers` | Response with 50+ headers | HPACK compression |
| GET | `/delay/{seconds}` | Delayed response (max 5s) | Multiplexing |
| GET | `/status/{code}` | Return specific status | Status handling |
| GET/POST | `/json` | JSON response/echo | Content types |
| GET | `/binary` | Binary data download | Binary frames |
| GET | `/metrics` | Server metrics | Monitoring |

## Testing

### Automated Test Suite

**Unit Tests** (Gleam):
```bash
gleam test
```

**H2C Upgrade Integration Tests**:
```bash
./test_h2c_upgrade.sh
```

**Basic HTTP/2 Features Tests**:
```bash
./test_working_features.sh
```

### Manual Testing Examples

#### 1. Basic HTTP/2 Request
```bash
# H2C upgrade (HTTP/1.1 → HTTP/2)
curl --http2 -v http://localhost:9080/

# Direct HTTP/2 (prior knowledge)
curl --http2-prior-knowledge -v http://localhost:9080/

# HTTP/2 over TLS (accept self-signed cert)
curl --http2 -k -v https://localhost:8443/
```

#### 2. Test Multiplexing
```bash
# Send 5 parallel requests with different delays
# Should complete in ~3 seconds (not 9 seconds sequentially)
curl --http2-prior-knowledge --parallel --parallel-max 5 \
http://localhost:9080/delay/1 \
http://localhost:9080/delay/2 \
http://localhost:9080/delay/3 \
http://localhost:9080/delay/1 \
http://localhost:9080/delay/2
```

#### 3. Test Flow Control
```bash
# Download large response
curl --http2-prior-knowledge -v http://localhost:9080/large > /dev/null

# Watch for flow control frames in verbose output
```

#### 4. Test HPACK Compression
```bash
# Get response with many headers
curl --http2-prior-knowledge -I http://localhost:9080/headers

# Headers are compressed using HPACK
```

#### 5. Test Different Content Types
```bash
# JSON response
curl --http2-prior-knowledge http://localhost:9080/json | jq .

# Binary data
curl --http2-prior-knowledge --output data.bin http://localhost:9080/binary

# Server-sent events style
curl --http2-prior-knowledge http://localhost:9080/stream
```

#### 6. POST Request with Data
```bash
# Echo POST data
curl --http2-prior-knowledge -X POST -d '{"test": "data"}' \
-H "Content-Type: application/json" \
http://localhost:9080/echo
```

## Advanced Testing with nghttp2

If you have nghttp2 tools installed:

### Installation
```bash
# macOS
brew install nghttp2

# Ubuntu/Debian
apt-get install nghttp2-client

# From source
git clone https://github.com/nghttp2/nghttp2.git
cd nghttp2
./configure && make && sudo make install
```

### nghttp2 Testing Commands

#### Detailed Protocol Information
```bash
# See detailed HTTP/2 frames
nghttp -v http://localhost:9080/

# With custom settings
nghttp -v --window-bits=20 --max-concurrent-streams=100 \
http://localhost:9080/
```

#### Performance Testing with h2load
```bash
# Basic load test
h2load -n 1000 -c 10 -m 100 http://localhost:9080/

# Test with multiple URIs
h2load -n 1000 -c 10 -m 50 \
http://localhost:9080/ \
http://localhost:9080/json \
http://localhost:9080/metrics

# Extended test with timing
h2load -n 10000 -c 100 -m 10 --duration=30 \
http://localhost:9080/
```

## Browser Testing

Modern browsers automatically negotiate HTTP/2 when available.

1. Open Chrome/Firefox/Safari
2. Open Developer Tools (F12)
3. Go to Network tab
4. Visit http://localhost:9080/
5. Check the "Protocol" column - should show "h2" for HTTP/2

### Chrome Specific
- chrome://net-internals/#http2 - View active HTTP/2 sessions
- chrome://net-internals/#events - See detailed protocol events

### Firefox Specific
- about:networking#http2 - View HTTP/2 connections

## Configuration Guide

### Server Configuration
```gleam
handler
|> mist.new()
|> mist.with_http2() // Enable with defaults
|> mist.http2_max_concurrent_streams(1000) // Max parallel streams
|> mist.http2_initial_window_size(1_048_576) // 1MB flow control window
|> mist.http2_max_frame_size(32_768) // 32KB max frame
|> mist.http2_max_header_list_size(16_384) // 16KB header limit
|> mist.start
```

### Configuration Parameters

| Parameter | Default | Description | Recommendation |
|-----------|---------|-------------|----------------|
| max_concurrent_streams | 100 | Max parallel requests | 100-1000 for typical servers |
| initial_window_size | 65,535 | Flow control window (bytes) | 65KB-2MB depending on bandwidth |
| max_frame_size | 16,384 | Max HTTP/2 frame size | 16KB-1MB (16KB is standard) |
| max_header_list_size | None | Max header size | 8KB-16KB for most applications |

## Monitoring and Debugging

### Server Metrics Endpoint
```bash
curl --http2-prior-knowledge http://localhost:9080/metrics | jq .
```

### Logging
The server logs HTTP/2 events. Set log level in your application:
```gleam
logging.configure()
logging.set_level(logging.Debug)
```

### Common Issues

1. **Connection not upgrading to HTTP/2**
- Ensure client supports HTTP/2
- Check with `curl --http2 -v` for protocol negotiation
- For h2c (cleartext), client must send upgrade header

2. **TLS certificate errors**
- Generate certificates with `./generate_certs.sh`
- Use `-k` flag with curl to accept self-signed certs
- For production, use proper certificates

3. **Performance issues**
- Adjust window sizes for your bandwidth
- Increase max_concurrent_streams for high load
- Monitor with h2load for bottlenecks

## HTTP/2 vs HTTP/1.1 Comparison

| Feature | HTTP/1.1 | HTTP/2 |
|---------|----------|---------|
| Protocol | Text | Binary |
| Multiplexing | No (uses pipelining) | Yes |
| Header Compression | No | Yes (HPACK) |
| Server Push | No | Yes (disabled in this example) |
| Flow Control | TCP only | Stream + Connection level |
| Connections Needed | Multiple | Single |

## Further Resources

- [HTTP/2 RFC 7540](https://tools.ietf.org/html/rfc7540)
- [HPACK RFC 7541](https://tools.ietf.org/html/rfc7541)
- [nghttp2 Documentation](https://nghttp2.org/documentation/)
- [Chrome HTTP/2 Debugging](https://developers.google.com/web/fundamentals/performance/http2)
- [Mist Documentation](https://github.com/rawhat/mist)
24 changes: 24 additions & 0 deletions examples/http2/generate_certs.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#!/bin/bash

# Generate self-signed certificates for testing HTTP/2 over TLS

echo "Generating self-signed certificates for localhost..."

# Generate private key
openssl genrsa -out localhost.key 4096

# Generate certificate signing request
openssl req -new -key localhost.key -out localhost.csr -subj "/C=US/ST=Test/L=Test/O=Test/CN=localhost"

# Generate self-signed certificate
openssl x509 -req -days 365 -in localhost.csr -signkey localhost.key -out localhost.crt

# Clean up CSR
rm localhost.csr

echo "Certificates generated:"
echo " - localhost.key (private key)"
echo " - localhost.crt (certificate)"
echo ""
echo "These are self-signed certificates for testing only."
echo "In production, use proper certificates from a trusted CA."
15 changes: 15 additions & 0 deletions examples/http2/gleam.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
name = "http2"
version = "1.0.0"
description = "Comprehensive HTTP/2 example for Mist"
target = "erlang"

[dependencies]
gleam_stdlib = ">= 0.44.0 and < 2.0.0"
mist = { path = "../.." }
gleam_http = ">= 4.0.0 and < 5.0.0"
gleam_erlang = ">= 1.0.0 and < 2.0.0"
gleam_otp = ">= 1.0.0 and < 2.0.0"
gleam_json = ">= 3.0.0 and < 4.0.0"

[dev-dependencies]
gleeunit = ">= 1.0.0 and < 2.0.0"
29 changes: 29 additions & 0 deletions examples/http2/manifest.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# This file was generated by Gleam
# You typically do not need to edit this file

packages = [
{ name = "exception", version = "2.1.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "exception", source = "hex", outer_checksum = "329D269D5C2A314F7364BD2711372B6F2C58FA6F39981572E5CA68624D291F8C" },
{ name = "gleam_crypto", version = "1.5.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_crypto", source = "hex", outer_checksum = "50774BAFFF1144E7872814C566C5D653D83A3EBF23ACC3156B757A1B6819086E" },
{ name = "gleam_erlang", version = "1.3.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_erlang", source = "hex", outer_checksum = "1124AD3AA21143E5AF0FC5CF3D9529F6DB8CA03E43A55711B60B6B7B3874375C" },
{ name = "gleam_http", version = "4.1.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_http", source = "hex", outer_checksum = "DD0271B32C356FB684EC7E9F48B1E835D0480168848581F68983C0CC371405D4" },
{ name = "gleam_json", version = "3.0.2", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_json", source = "hex", outer_checksum = "874FA3C3BB6E22DD2BB111966BD40B3759E9094E05257899A7C08F5DE77EC049" },
{ name = "gleam_otp", version = "1.1.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_stdlib"], otp_app = "gleam_otp", source = "hex", outer_checksum = "7987CBEBC8060B88F14575DEF546253F3116EBE2A5DA6FD82F38243FCE97C54B" },
{ name = "gleam_stdlib", version = "0.62.1", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "0080706D3A5A9A36C40C68481D1D231D243AF602E6D2A2BE67BA8F8F4DFF45EC" },
{ name = "gleam_yielder", version = "1.1.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_yielder", source = "hex", outer_checksum = "8E4E4ECFA7982859F430C57F549200C7749823C106759F4A19A78AEA6687717A" },
{ name = "gleeunit", version = "1.6.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "FDC68A8C492B1E9B429249062CD9BAC9B5538C6FBF584817205D0998C42E1DAC" },
{ name = "glisten", version = "8.0.1", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_otp", "gleam_stdlib", "logging", "telemetry"], otp_app = "glisten", source = "hex", outer_checksum = "534BB27C71FB9E506345A767C0D76B17A9E9199934340C975DC003C710E3692D" },
{ name = "gramps", version = "6.0.0", build_tools = ["gleam"], requirements = ["gleam_crypto", "gleam_erlang", "gleam_http", "gleam_stdlib"], otp_app = "gramps", source = "hex", outer_checksum = "8B7195978FBFD30B43DF791A8A272041B81E45D245314D7A41FC57237AA882A0" },
{ name = "hpack_erl", version = "0.3.0", build_tools = ["rebar3"], requirements = [], otp_app = "hpack", source = "hex", outer_checksum = "D6137D7079169D8C485C6962DFE261AF5B9EF60FBC557344511C1E65E3D95FB0" },
{ name = "logging", version = "1.3.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "logging", source = "hex", outer_checksum = "1098FBF10B54B44C2C7FDF0B01C1253CAFACDACABEFB4B0D027803246753E06D" },
{ name = "mist", version = "5.0.3", build_tools = ["gleam"], requirements = ["exception", "gleam_erlang", "gleam_http", "gleam_json", "gleam_otp", "gleam_stdlib", "gleam_yielder", "glisten", "gramps", "hpack_erl", "logging"], source = "local", path = "../.." },
{ name = "telemetry", version = "1.3.0", build_tools = ["rebar3"], requirements = [], otp_app = "telemetry", source = "hex", outer_checksum = "7015FC8919DBE63764F4B4B87A95B7C0996BD539E0D499BE6EC9D7F3875B79E6" },
]

[requirements]
gleam_erlang = { version = ">= 1.0.0 and < 2.0.0" }
gleam_http = { version = ">= 4.0.0 and < 5.0.0" }
gleam_json = { version = ">= 3.0.0 and < 4.0.0" }
gleam_otp = { version = ">= 1.0.0 and < 2.0.0" }
gleam_stdlib = { version = ">= 0.44.0 and < 2.0.0" }
gleeunit = { version = ">= 1.0.0 and < 2.0.0" }
mist = { path = "../.." }
Loading