feat(webclient): add request context and HTML navigation handling

- Add context to HTTP requests in JSRequestToHTTPRequest, storing the request mode for downstream use - Implement IsHTMLContentType function to properly parse and check for HTML content types - Modify proxy ServeHTTP to read and log HTML bodies for navigation mode requests, ensuring proper handling of HTML responses

lemon-mint committed Oct 31, 2025 at 13:38 UTC 79ceefeb334ff64b89d93d3dda2db9a4d2cf754d
2 files changed +40 -3
cmd/webclient/httpjs/http_js.go
+5 -1
@@ -3,6 +3,7 @@ package httpjs
3 import (
4 "bufio"
5 "bytes"
6 + "context"
7 "errors"
8 "io"
9 "net/http"
@@ -365,8 +366,11 @@ func JSRequestToHTTPRequest(jsReq js.Value) (*http.Request, error) {
366 bodyReader = bytes.NewReader([]byte{})
367 }
368
369 + ctx := context.Background()
370 + ctx = context.WithValue(ctx, "http.request.mode", jsReq.Get("mode").String())
371 +
372 // Create HTTP request
369 - httpReq, err := http.NewRequest(method, url, bodyReader)
373 + httpReq, err := http.NewRequestWithContext(ctx, method, url, bodyReader)
374 if err != nil {
375 return nil, err
376 }
cmd/webclient/main_js.go
+35 -2
@@ -3,6 +3,7 @@ package main
3 import (
4 "context"
5 "io"
6 + "mime"
7 "net"
8 "net/http"
9 "os"
@@ -44,6 +45,24 @@ var client = &http.Client{
45 type Proxy struct {
46 }
47
48 +// IsHTMLContentType checks if the Content-Type header indicates HTML content
49 +// It properly handles media type parsing with parameters like charset
50 +func IsHTMLContentType(contentType string) bool {
51 + if contentType == "" {
52 + return false
53 + }
54 +
55 + // Parse the media type and parameters
56 + mediaType, _, err := mime.ParseMediaType(contentType)
57 + if err != nil {
58 + // If parsing fails, do a simple case-insensitive check for "text/html"
59 + return strings.HasPrefix(strings.ToLower(contentType), "text/html")
60 + }
61 +
62 + // Check if the media type is HTML
63 + return mediaType == "text/html"
64 +}
65 +
66 func (p *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
67 log.Info().Msgf("Proxying request to %s", r.URL.String())
68
@@ -69,8 +88,22 @@ func (p *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
88 for key, value := range resp.Header {
89 w.Header()[key] = value
90 }
72 - w.WriteHeader(resp.StatusCode)
73 - io.Copy(w, resp.Body)
91 +
92 + if r.Context().Value("http.request.mode").(string) == "navigate" &&
93 + IsHTMLContentType(resp.Header.Get("Content-Type")) {
94 + body, err := io.ReadAll(resp.Body)
95 + if err != nil {
96 + w.WriteHeader(http.StatusBadGateway)
97 + w.Write([]byte("502: Failed to read response body"))
98 + return
99 + }
100 + log.Debug().Msgf("HTML content received: %s", body)
101 + w.WriteHeader(resp.StatusCode)
102 + w.Write(body)
103 + } else {
104 + w.WriteHeader(resp.StatusCode)
105 + io.Copy(w, resp.Body)
106 + }
107 }
108
109 func main() {