refactor: improve streaming response error handling and shorten derived IDs
- Add wroteHeaderChan to streamingResponseWriter for synchronization - Ensure Bad Gateway error is written if upstream handler fails to set headers - Truncate HMAC-SHA256 hash to 16 bytes in DeriveID for shorter base32-encoded IDs (reduces length from ~51 to ~26 characters)
lemon-mint committed
Nov 3, 2025 at 16:07 UTC
7e8c94784bec5b5c74b5ef49cfe98f7318b8b4e2
2 files changed
+21
-9
cmd/webclient/httpjs/http_js.go
+19
-8
@@ -429,7 +429,8 @@ func HTTPResponseToJSResponse(httpResp *http.Response) js.Value {
429
jsOptions.Set("headers", jsHeaders)
430
431
// Create and return JS Response
432
- return _Response.New(jsBody, jsOptions)
432
+ jsResp := _Response.New(jsBody, jsOptions)
433
+ return jsResp
434
}
435
436
// ServeHTTPAsyncWithStreaming handles an HTTP request asynchronously and returns a streaming JS Response
@@ -451,9 +452,10 @@ func ServeHTTPAsyncWithStreaming(handler http.Handler, jsReq js.Value) js.Value
452
453
// Create custom ResponseWriter that writes to pipe
454
respWriter := &streamingResponseWriter{
454
- pipeWriter: pw,
455
- header: make(http.Header),
456
- statusCode: 200,
455
+ pipeWriter: pw,
456
+ header: make(http.Header),
457
+ statusCode: 200,
458
+ wroteHeaderChan: make(chan struct{}, 1),
459
}
460
461
// Serve HTTP in goroutine
@@ -468,8 +470,15 @@ func ServeHTTPAsyncWithStreaming(handler http.Handler, jsReq js.Value) js.Value
470
}()
471
472
handler.ServeHTTP(respWriter, httpReq)
473
+
474
+ if !respWriter.wroteHeader {
475
+ respWriter.WriteHeader(http.StatusBadGateway)
476
+ http.Error(respWriter, "Bad Gateway\n\nUpstream server error", http.StatusBadGateway)
477
+ }
478
}()
479
480
+ <-respWriter.wroteHeaderChan
481
+
482
// Create http.Response with streaming body
483
httpResp := &http.Response{
484
StatusCode: respWriter.statusCode,
@@ -489,10 +498,11 @@ func ServeHTTPAsyncWithStreaming(handler http.Handler, jsReq js.Value) js.Value
498
499
// streamingResponseWriter implements http.ResponseWriter for streaming responses
500
type streamingResponseWriter struct {
492
- pipeWriter *io.PipeWriter
493
- header http.Header
494
- statusCode int
495
- wroteHeader bool
501
+ pipeWriter *io.PipeWriter
502
+ header http.Header
503
+ statusCode int
504
+ wroteHeader bool
505
+ wroteHeaderChan chan struct{}
506
}
507
508
func (w *streamingResponseWriter) Header() http.Header {
@@ -510,5 +520,6 @@ func (w *streamingResponseWriter) WriteHeader(statusCode int) {
520
if !w.wroteHeader {
521
w.statusCode = statusCode
522
w.wroteHeader = true
523
+ close(w.wroteHeaderChan)
524
}
525
}
portal/core/cryptoops/sig.go
+2
-1
@@ -15,7 +15,8 @@ var _base32_encoding = base32.NewEncoding("ABCDEFGHIJKLMNOPQRSTUVWXYZ234567").Wi
15
func DeriveID(publickey ed25519.PublicKey) string {
16
h := hmac.New(sha256.New, _id_magic)
17
h.Write(publickey)
18
- return _base32_encoding.EncodeToString(h.Sum(nil))
18
+ hash := h.Sum(nil)
19
+ return _base32_encoding.EncodeToString(hash[:16])
20
}
21
22
type Credential struct {