Browse Source

Fix NIP-11 caching and export streaming issues (v0.46.2)

- Fix Content-Type header being set on request instead of response
- Add Vary: Accept header to prevent browser/CDN caching NIP-11 for HTML
- Add periodic flushing during export for HTTP streaming (every 100 events)
- Add initial flush after headers to start streaming immediately
- Add X-Content-Type-Options: nosniff to prevent browser buffering

Files modified:
- app/handle-relayinfo.go: Fix header and add Vary: Accept
- app/server.go: Add initial flush and nosniff header for export
- pkg/database/export.go: Add periodic and final flushing for streaming

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
main
woikos 1 week ago
parent
commit
ea7bc75fac
No known key found for this signature in database
  1. 3
      app/handle-relayinfo.go
  2. 6
      app/server.go
  3. 37
      pkg/database/export.go
  4. 2
      pkg/version/version

3
app/handle-relayinfo.go

@ -37,7 +37,8 @@ type ExtendedRelayInfo struct { @@ -37,7 +37,8 @@ type ExtendedRelayInfo struct {
// Informer interface implementation or predefined server configuration. It
// returns this document as a JSON response to the client.
func (s *Server) HandleRelayInfo(w http.ResponseWriter, r *http.Request) {
r.Header.Set("Content-Type", "application/json")
w.Header().Set("Content-Type", "application/json")
w.Header().Set("Vary", "Accept")
log.D.Ln("handling relay information document")
var info *relayinfo.T
nips := []relayinfo.NIP{

6
app/server.go

@ -738,6 +738,12 @@ func (s *Server) handleExport(w http.ResponseWriter, r *http.Request) { @@ -738,6 +738,12 @@ func (s *Server) handleExport(w http.ResponseWriter, r *http.Request) {
w.Header().Set(
"Content-Disposition", "attachment; filename=\""+filename+"\"",
)
w.Header().Set("X-Content-Type-Options", "nosniff")
// Flush headers to start streaming immediately
if flusher, ok := w.(http.Flusher); ok {
flusher.Flush()
}
// Stream export
s.DB.Export(s.Ctx, w, pks...)

37
pkg/database/export.go

@ -17,6 +17,11 @@ import ( @@ -17,6 +17,11 @@ import (
"git.mleku.dev/mleku/nostr/utils/units"
)
// Flusher interface for HTTP streaming
type flusher interface {
Flush()
}
// Export the complete database of stored events to an io.Writer in line structured minified
// JSON. Supports both legacy and compact event formats.
func (d *D) Export(c context.Context, w io.Writer, pubkeys ...[]byte) {
@ -24,11 +29,18 @@ func (d *D) Export(c context.Context, w io.Writer, pubkeys ...[]byte) { @@ -24,11 +29,18 @@ func (d *D) Export(c context.Context, w io.Writer, pubkeys ...[]byte) {
evB := make([]byte, 0, units.Mb)
evBuf := bytes.NewBuffer(evB)
// Get flusher for HTTP streaming if available
var f flusher
if fl, ok := w.(flusher); ok {
f = fl
}
// Performance tracking
startTime := time.Now()
var eventCount, bytesWritten int64
lastLogTime := startTime
const logInterval = 5 * time.Second
const flushInterval = 100 // Flush every N events
log.I.F("export: starting export operation")
@ -109,6 +121,11 @@ func (d *D) Export(c context.Context, w io.Writer, pubkeys ...[]byte) { @@ -109,6 +121,11 @@ func (d *D) Export(c context.Context, w io.Writer, pubkeys ...[]byte) {
eventCount++
ev.Free()
// Flush periodically for HTTP streaming
if f != nil && eventCount%flushInterval == 0 {
f.Flush()
}
// Progress logging every logInterval
if time.Since(lastLogTime) >= logInterval {
elapsed := time.Since(startTime)
@ -169,6 +186,11 @@ func (d *D) Export(c context.Context, w io.Writer, pubkeys ...[]byte) { @@ -169,6 +186,11 @@ func (d *D) Export(c context.Context, w io.Writer, pubkeys ...[]byte) {
eventCount++
ev.Free()
// Flush periodically for HTTP streaming
if f != nil && eventCount%flushInterval == 0 {
f.Flush()
}
// Progress logging every logInterval
if time.Since(lastLogTime) >= logInterval {
elapsed := time.Since(startTime)
@ -186,6 +208,11 @@ func (d *D) Export(c context.Context, w io.Writer, pubkeys ...[]byte) { @@ -186,6 +208,11 @@ func (d *D) Export(c context.Context, w io.Writer, pubkeys ...[]byte) {
return
}
// Final flush
if f != nil {
f.Flush()
}
// Final export summary
elapsed := time.Since(startTime)
eventsPerSec := float64(eventCount) / elapsed.Seconds()
@ -244,6 +271,11 @@ func (d *D) Export(c context.Context, w io.Writer, pubkeys ...[]byte) { @@ -244,6 +271,11 @@ func (d *D) Export(c context.Context, w io.Writer, pubkeys ...[]byte) {
eventCount++
ev.Free()
// Flush periodically for HTTP streaming
if f != nil && eventCount%flushInterval == 0 {
f.Flush()
}
// Progress logging every logInterval
if time.Since(lastLogTime) >= logInterval {
elapsed := time.Since(startTime)
@ -261,6 +293,11 @@ func (d *D) Export(c context.Context, w io.Writer, pubkeys ...[]byte) { @@ -261,6 +293,11 @@ func (d *D) Export(c context.Context, w io.Writer, pubkeys ...[]byte) {
}
}
// Final flush
if f != nil {
f.Flush()
}
// Final export summary for pubkey export
elapsed := time.Since(startTime)
eventsPerSec := float64(eventCount) / elapsed.Seconds()

2
pkg/version/version

@ -1 +1 @@ @@ -1 +1 @@
v0.46.1
v0.46.2

Loading…
Cancel
Save