k8s.io/apiserver@v0.31.1/pkg/endpoints/discovery/aggregated/etag.go (about) 1 /* 2 Copyright 2022 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package aggregated 18 19 import ( 20 "crypto/sha512" 21 "encoding/json" 22 "fmt" 23 "net/http" 24 "strconv" 25 26 "k8s.io/apimachinery/pkg/runtime" 27 "k8s.io/apimachinery/pkg/runtime/schema" 28 "k8s.io/apiserver/pkg/endpoints/handlers/responsewriters" 29 ) 30 31 // This file exposes helper functions used for calculating the E-Tag header 32 // used in discovery endpoint responses 33 34 // Attaches Cache-Busting functionality to an endpoint 35 // - Sets ETag header to provided hash 36 // - Replies with 304 Not Modified, if If-None-Match header matches hash 37 // 38 // hash should be the value of calculateETag on object. If hash is empty, then 39 // the object is simply serialized without E-Tag functionality 40 func ServeHTTPWithETag( 41 object runtime.Object, 42 hash string, 43 targetGV schema.GroupVersion, 44 serializer runtime.NegotiatedSerializer, 45 w http.ResponseWriter, 46 req *http.Request, 47 ) { 48 // ETag must be enclosed in double quotes: 49 // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag 50 quotedHash := strconv.Quote(hash) 51 w.Header().Set("ETag", quotedHash) 52 w.Header().Set("Vary", "Accept") 53 w.Header().Set("Cache-Control", "public") 54 55 // If Request includes If-None-Match and matches hash, reply with 304 56 // Otherwise, we delegate to the handler for actual content 57 // 58 // According to documentation, An Etag within an If-None-Match 59 // header will be enclosed within double quotes: 60 // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/If-None-Match#directives 61 if clientCachedHash := req.Header.Get("If-None-Match"); quotedHash == clientCachedHash { 62 w.WriteHeader(http.StatusNotModified) 63 return 64 } 65 66 responsewriters.WriteObjectNegotiated( 67 serializer, 68 DiscoveryEndpointRestrictions, 69 targetGV, 70 w, 71 req, 72 http.StatusOK, 73 object, 74 true, 75 ) 76 } 77 78 func calculateETag(resources interface{}) (string, error) { 79 serialized, err := json.Marshal(resources) 80 if err != nil { 81 return "", err 82 } 83 84 return fmt.Sprintf("%X", sha512.Sum512(serialized)), nil 85 }