Co-authored-by: matst80 <mats.tornberg@gmail.com> Reviewed-on: https://git.tornberg.me/mats/go-cart-actor/pulls/4 Co-authored-by: Mats Törnberg <mats@tornberg.me> Co-committed-by: Mats Törnberg <mats@tornberg.me>
70 lines
1.7 KiB
Go
70 lines
1.7 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/sha256"
|
|
_ "embed"
|
|
"encoding/hex"
|
|
"net/http"
|
|
"sync"
|
|
)
|
|
|
|
// openapi_embed.go: Provides embedded OpenAPI spec and helper to mount handler.
|
|
|
|
//go:embed openapi.json
|
|
var openapiJSON []byte
|
|
|
|
var (
|
|
openapiOnce sync.Once
|
|
openapiETag string
|
|
)
|
|
|
|
// initOpenAPIMetadata computes immutable metadata for the embedded spec.
|
|
func initOpenAPIMetadata() {
|
|
sum := sha256.Sum256(openapiJSON)
|
|
openapiETag = `W/"` + hex.EncodeToString(sum[:8]) + `"` // weak ETag with first 8 bytes
|
|
}
|
|
|
|
// ServeEmbeddedOpenAPI serves the embedded OpenAPI JSON spec at /openapi.json.
|
|
// It supports GET and HEAD and implements basic ETag caching.
|
|
func ServeEmbeddedOpenAPI(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodGet && r.Method != http.MethodHead {
|
|
w.Header().Set("Allow", "GET, HEAD")
|
|
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
|
|
openapiOnce.Do(initOpenAPIMetadata)
|
|
|
|
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
|
w.Header().Set("Cache-Control", "no-cache")
|
|
w.Header().Set("Access-Control-Allow-Origin", "*")
|
|
w.Header().Set("ETag", openapiETag)
|
|
|
|
if match := r.Header.Get("If-None-Match"); match != "" {
|
|
if bytes.Contains([]byte(match), []byte(openapiETag)) {
|
|
w.WriteHeader(http.StatusNotModified)
|
|
return
|
|
}
|
|
}
|
|
|
|
if r.Method == http.MethodHead {
|
|
w.WriteHeader(http.StatusOK)
|
|
return
|
|
}
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
_, _ = w.Write(openapiJSON)
|
|
}
|
|
|
|
// Optional: function to access raw spec bytes programmatically.
|
|
func OpenAPISpecBytes() []byte {
|
|
return openapiJSON
|
|
}
|
|
|
|
// Optional: function to access current ETag.
|
|
func OpenAPIETag() string {
|
|
openapiOnce.Do(initOpenAPIMetadata)
|
|
return openapiETag
|
|
}
|