Skip to content

Commit

Permalink
docs: update comments and README [ci skip]
Browse files Browse the repository at this point in the history
Signed-off-by: Gaukas Wang <[email protected]>
  • Loading branch information
gaukas committed Jun 6, 2024
1 parent 6aa7674 commit 5f4cf92
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 12 deletions.
31 changes: 25 additions & 6 deletions modcaddy/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,26 +12,26 @@ You will need to use [xcaddy](https://github.com/caddyserver/xcaddy) to rebuild

It is worth noting that some web browsers may not choose to switch to QUIC protocol in localhost environment, which may result in the QUIC Client Initial Packet not being sent and therefore not being captured/analyzed.

### Build
## Build

```bash
xcaddy build --with github.com/gaukas/clienthellod/modcaddy
```

#### When build locally with changes
### When build locally with changes

```bash
xcaddy build --with github.com/gaukas/clienthellod/modcaddy --with github.com/gaukas/clienthellod/=./
```

### Caddyfile
## sample Caddyfile

A sample Caddyfile is provided below.

```Caddyfile
{
# debug # for debugging purpose
# https_port 443 # currently, QUIC listener works only on port 443, otherwise you need to make changes to the code
# https_port 443 # currently, QUIC listener works only on port 443, otherwise you need to make changes to the code
order clienthellod before file_server # make sure it hits handler before file_server
clienthellod { # app (reservoir)
tls_ttl 10s
Expand All @@ -45,7 +45,6 @@ A sample Caddyfile is provided below.
}
tls
}
# protocols h3
}
}
Expand All @@ -70,4 +69,24 @@ A sample Caddyfile is provided below.
root /var/www/html
}
}
```
```

## Known issues

### QUIC can't be fingerprinted when web browser chooses H2 not H3

Under certain network condition or configurations, a web browser may decide not to switch to QUIC protocol even when the server advertises the support for QUIC. This issue is more likely to happen under the scenarios with low-latency such as in localhost/intranet.

There is no trivial solution to this issue, as there seems to be no way to force the web browser to use QUIC.

### QUIC fingerprint missing for the first request

It is possible that a client sends both H2-over-TCP (TLS) and H3-over-UDP (QUIC) for the first time requesting a web page and decide to render the response from H2-over-TCP (TLS). In this case, the QUIC Client Initial Packet might be not yet recorded.

Reloading the page might help by fetching the cached QUIC fingerprint if it is captured and not yet expired.

### Fingerprint gone after reloading/refreshing the web page

Some web browsers may decide to reuse the existing unclosed connection for new HTTP requests instead of establishing a new one by sending a new TLS Client Hello or QUIC Initial Packet(s). In which case, no new fingerprint will be captured and if the old fingerprint is expired or otherwise removed, the fingerprint will be gone and nothing will be displayed.

Forcing the web browser to establish a new connection by closing the existing connection, opening a new tab, or use different domain names every time might help.
23 changes: 17 additions & 6 deletions modcaddy/handler/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,11 +119,17 @@ func (h *Handler) serveTLS(wr http.ResponseWriter, req *http.Request, next caddy
return next.ServeHTTP(wr, req)
}

// write JSON to response
// Properly set the Content-Type header
wr.Header().Set("Content-Type", "application/json")

// Close the HTTP connection after sending the response
//
// HTTP/1.X only. Forbidden in HTTP/2 (RFC 9113 Section 8.2.2)
// and HTTP/3 (RFC 9114 Section 4.2)
if req.ProtoMajor == 1 {
wr.Header().Set("Connection", "close") // HTTP/1 only. Forbidden in HTTP/2, HTTP/3
wr.Header().Set("Connection", "close")
}

_, err = wr.Write(b)
if err != nil {
h.logger.Error("failed to write response", zap.Error(err))
Expand All @@ -136,7 +142,6 @@ func (h *Handler) serveTLS(wr http.ResponseWriter, req *http.Request, next caddy
// reservoir and writing it to the response.
func (h *Handler) serveQUIC(wr http.ResponseWriter, req *http.Request, next caddyhttp.Handler) error { // skipcq: GO-W1029
var from string

if req.ProtoMajor == 3 {
from = req.RemoteAddr
} else {
Expand All @@ -159,7 +164,7 @@ func (h *Handler) serveQUIC(wr http.ResponseWriter, req *http.Request, next cadd
// get the client hello from the reservoir
qfp, err := h.reservoir.QUICFingerprinter().PeekAwait(from)
if err != nil {
h.logger.Error(fmt.Sprintf("Unable to fetch QUIC fingerprint sent by %s: %v", req.RemoteAddr, err))
h.logger.Debug(fmt.Sprintf("Unable to fetch QUIC fingerprint sent by %s: %v", req.RemoteAddr, err))
return next.ServeHTTP(wr, req)
}

Expand Down Expand Up @@ -187,11 +192,17 @@ func (h *Handler) serveQUIC(wr http.ResponseWriter, req *http.Request, next cadd
return next.ServeHTTP(wr, req)
}

// write JSON to response
// Properly set the Content-Type header
wr.Header().Set("Content-Type", "application/json")

// Close the HTTP connection after sending the response
//
// HTTP/1.X only. Forbidden in HTTP/2 (RFC 9113 Section 8.2.2)
// and HTTP/3 (RFC 9114 Section 4.2)
if req.ProtoMajor == 1 {
wr.Header().Set("Connection", "close") // HTTP/1 only. Forbidden in HTTP/2, HTTP/3
wr.Header().Set("Connection", "close")
}

_, err = wr.Write(b)
if err != nil {
h.logger.Error("failed to write response", zap.Error(err))
Expand Down

0 comments on commit 5f4cf92

Please sign in to comment.