[DEPRECATED] Go implementation of plcbundle
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}