lib/httputils/handle.go (64 lines of code) (raw):

// Copyright 2019 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package httputils import ( "net/http" "strings" "google.golang.org/grpc/status" /* copybara-comment */ "github.com/golang/protobuf/proto" /* copybara-comment */ glog "github.com/golang/glog" /* copybara-comment */ ) // To create a HTTP handler from a gRPC handler: // // func (h *FooHTTPHandler) GetFoo(w http.ResponseWriter, r *http.Request) { // req := &fpb.GetFooRequest{Name: r.RequestURI} // resp, err := fooServer.GetFoo(r.Context(), req) // if err != nil { // httputils.WriteError(w, err) // } // WriteResp(w, resp) // } // WriteResp writes an protobuf message to the response. func WriteResp(w http.ResponseWriter, m proto.Message) { WriteCorsHeaders(w) w.Header().Set("Content-Type", "application/json") // w.Header().Set("Cache-Control", "no-store") // w.Header().Set("Pragma", "no-cache") if err := EncodeJSONPB(w, m); err != nil { glog.Errorf("EncodeJSONPB() failed: %v", err) http.Error(w, "encoding the response failed", http.StatusInternalServerError) return } } // WriteNonProtoResp writes a reponse. // For protobuf message responses use WriteResp(w, resp) instead. func WriteNonProtoResp(w http.ResponseWriter, resp interface{}) { WriteCorsHeaders(w) w.Header().Set("Content-Type", "application/json") if err := EncodeJSON(w, resp); err != nil { glog.Errorf("EncodeJSON() failed: %v", err) http.Error(w, "encoding the response failed", http.StatusInternalServerError) return } } // WriteError writes an error to the response. // Does nothing if status is nil. func WriteError(w http.ResponseWriter, err error) { if err == nil { return } glog.InfoDepth(1, err) st := status.Convert(err) w.WriteHeader(HTTPStatus(st.Code())) WriteResp(w, st.Proto()) } func addPreventClickjackingHeader(w http.ResponseWriter, additionalCSP *CSP) { csp := mergeCSP(pageCSP, additionalCSP) csp.addToHeader(w) // X-Frame-Options for browser compatibility w.Header().Set("X-Frame-Options", "SAMEORIGIN") } // WriteHTMLResp writes a "text/html" type string to the ResponseWriter. func WriteHTMLResp(w http.ResponseWriter, b string, additionalCSP *CSP) { addPreventClickjackingHeader(w, additionalCSP) w.Header().Set("Content-Type", "text/html") w.Write([]byte(b)) } // WriteRedirect writes a redirect to the provider URL. // If the provided URL is relative, it will be relative to the request URL. func WriteRedirect(w http.ResponseWriter, r *http.Request, redirect string) { // url = strings.ReplaceAll(url, "%2526", "&") // url = strings.ReplaceAll(url, "%253F", "?") WriteCorsHeaders(w) http.Redirect(w, r, redirect, http.StatusSeeOther) } // WriteCorsHeaders writes CORS headers (https://www.w3.org/TR/cors) to the response. func WriteCorsHeaders(w http.ResponseWriter) { w.Header().Set("Access-Control-Allow-Origin", "*") w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Origin, Accept, Authorization, X-Link-Authorization") w.Header().Set("Access-Control-Allow-Methods", "GET,POST,PUT,PATCH,DELETE,OPTIONS") } // RedirectHTMLPage retuns the HTML page generated by http.Redirect. // This is copied from http package. func RedirectHTMLPage(dst string) string { return `<a href="` + HTMLReplacer.Replace(dst) + `">See Other</a>.` + "\n\n" } // HTMLReplacer escape URL parameters for HTML. // This is copied from http package. var HTMLReplacer = strings.NewReplacer( "&", "&amp;", "<", "&lt;", ">", "&gt;", `"`, "&#34;", "'", "&#39;", )