[DEPRECATED] Go implementation of plcbundle
at main 151 lines 4.2 kB view raw
1package server 2 3import ( 4 "fmt" 5 "net/http" 6 "time" 7 8 "tangled.org/atscan.net/plcbundle-go/bundle" 9) 10 11// getScheme determines the HTTP scheme 12func getScheme(r *http.Request) string { 13 if r.TLS != nil { 14 return "https" 15 } 16 17 if proto := r.Header.Get("X-Forwarded-Proto"); proto != "" { 18 return proto 19 } 20 21 if r.Header.Get("X-Forwarded-Ssl") == "on" { 22 return "https" 23 } 24 25 return "http" 26} 27 28// getWSScheme determines the WebSocket scheme 29func getWSScheme(r *http.Request) string { 30 if getScheme(r) == "https" { 31 return "wss" 32 } 33 return "ws" 34} 35 36// getBaseURL returns the base URL for HTTP 37func getBaseURL(r *http.Request) string { 38 scheme := getScheme(r) 39 host := r.Host 40 return fmt.Sprintf("%s://%s", scheme, host) 41} 42 43// getWSURL returns the base URL for WebSocket 44func getWSURL(r *http.Request) string { 45 scheme := getWSScheme(r) 46 host := r.Host 47 return fmt.Sprintf("%s://%s", scheme, host) 48} 49 50// formatNumber formats numbers with thousand separators 51func formatNumber(n int) string { 52 s := fmt.Sprintf("%d", n) 53 var result []byte 54 for i, c := range s { 55 if i > 0 && (len(s)-i)%3 == 0 { 56 result = append(result, ',') 57 } 58 result = append(result, byte(c)) 59 } 60 return string(result) 61} 62 63func setDIDDocumentHeaders( 64 w http.ResponseWriter, 65 _ *http.Request, 66 did string, 67 resolvedHandle string, 68 result *bundle.ResolveDIDResult, 69 handleResolveTime time.Duration, 70) { 71 // === Identity === 72 w.Header().Set("X-DID", did) 73 74 if resolvedHandle != "" { 75 w.Header().Set("X-Handle-Resolved", resolvedHandle) 76 w.Header().Set("X-Handle-Resolution-Time-Ms", 77 fmt.Sprintf("%.3f", float64(handleResolveTime.Microseconds())/1000.0)) 78 w.Header().Set("X-Request-Type", "handle") 79 } else { 80 w.Header().Set("X-Request-Type", "did") 81 } 82 83 // === Resolution Source & Location === 84 w.Header().Set("X-Resolution-Source", result.Source) 85 86 if result.Source == "bundle" { 87 w.Header().Set("X-Bundle-Number", fmt.Sprintf("%d", result.BundleNumber)) 88 w.Header().Set("X-Bundle-Position", fmt.Sprintf("%d", result.Position)) 89 globalPos := (result.BundleNumber * 10000) + result.Position 90 w.Header().Set("X-Global-Position", fmt.Sprintf("%d", globalPos)) 91 w.Header().Set("X-Pointer", fmt.Sprintf("%d:%d", result.BundleNumber, result.Position)) 92 } else { 93 w.Header().Set("X-Mempool", "true") 94 } 95 96 // === Operation Metadata (from result.LatestOperation) === 97 if result.LatestOperation != nil { 98 op := result.LatestOperation 99 100 w.Header().Set("X-Operation-CID", op.CID) 101 w.Header().Set("X-Operation-Created", op.CreatedAt.Format(time.RFC3339)) 102 103 opAge := time.Since(op.CreatedAt) 104 w.Header().Set("X-Operation-Age-Seconds", fmt.Sprintf("%d", int(opAge.Seconds()))) 105 106 if len(op.RawJSON) > 0 { 107 w.Header().Set("X-Operation-Size", fmt.Sprintf("%d", len(op.RawJSON))) 108 } 109 110 // Nullification status 111 if op.IsNullified() { 112 w.Header().Set("X-Operation-Nullified", "true") 113 if nullCID := op.GetNullifyingCID(); nullCID != "" { 114 w.Header().Set("X-Operation-Nullified-By", nullCID) 115 } 116 } else { 117 w.Header().Set("X-Operation-Nullified", "false") 118 } 119 120 // Standard HTTP headers 121 w.Header().Set("Last-Modified", op.CreatedAt.UTC().Format(http.TimeFormat)) 122 w.Header().Set("ETag", fmt.Sprintf(`"%s"`, op.CID)) 123 } 124 125 // === Performance Metrics === 126 totalTime := handleResolveTime + result.TotalTime 127 w.Header().Set("X-Resolution-Time-Ms", 128 fmt.Sprintf("%.3f", float64(totalTime.Microseconds())/1000.0)) 129 w.Header().Set("X-Mempool-Time-Ms", 130 fmt.Sprintf("%.3f", float64(result.MempoolTime.Microseconds())/1000.0)) 131 132 if result.Source == "bundle" { 133 w.Header().Set("X-Index-Time-Ms", 134 fmt.Sprintf("%.3f", float64(result.IndexTime.Microseconds())/1000.0)) 135 w.Header().Set("X-Load-Time-Ms", 136 fmt.Sprintf("%.3f", float64(result.LoadOpTime.Microseconds())/1000.0)) 137 } 138 139 // === Caching Strategy === 140 if result.Source == "bundle" { 141 // Bundled data: cache 5min, stale-while-revalidate 10min 142 w.Header().Set("Cache-Control", 143 "public, max-age=300, stale-while-revalidate=600, stale-if-error=3600") 144 } else { 145 // Mempool data: cache 1min, stale-while-revalidate 5min 146 w.Header().Set("Cache-Control", 147 "public, max-age=60, stale-while-revalidate=300, stale-if-error=600") 148 } 149 150 w.Header().Set("Vary", "Accept, If-None-Match") 151}