github.com/google/go-safeweb@v0.0.0-20231219055052-64d8cfc90fbb/safehttp/default_dispatcher.go (about) 1 // Copyright 2020 Google LLC 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // https://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package safehttp 16 17 import ( 18 "encoding/json" 19 "fmt" 20 "io" 21 "net/http" 22 23 "github.com/google/safehtml" 24 "github.com/google/safehtml/template" 25 ) 26 27 // DefaultDispatcher is responsible for writing safe responses. 28 type DefaultDispatcher struct{} 29 30 // Write writes the response to the http.ResponseWriter if it's deemed safe. It 31 // returns a non-nil error if the response is deemed unsafe or if the writing 32 // operation fails. 33 // 34 // For JSONResponses, the underlying object is serialised and written if it's a 35 // valid JSON. 36 // 37 // For TemplateResponses, the parsed template is applied to the provided data 38 // object. If the funcMap is non-nil, its elements override the existing names 39 // to functions mappings in the template. An attempt to define a new name to 40 // function mapping that is not already in the template will result in a panic. 41 // 42 // Write sets the Content-Type accordingly. 43 func (DefaultDispatcher) Write(rw http.ResponseWriter, resp Response) error { 44 switch x := resp.(type) { 45 case JSONResponse: 46 rw.Header().Set("Content-Type", "application/json; charset=utf-8") 47 io.WriteString(rw, ")]}',\n") // Break parsing of JavaScript in order to prevent XSSI. 48 return json.NewEncoder(rw).Encode(x.Data) 49 case *TemplateResponse: 50 t, ok := (x.Template).(*template.Template) 51 if !ok { 52 return fmt.Errorf("%T is not a safe template and it cannot be parsed and written", t) 53 } 54 rw.Header().Set("Content-Type", "text/html; charset=utf-8") 55 if len(x.FuncMap) == 0 { 56 if x.Name == "" { 57 return t.Execute(rw, x.Data) 58 } 59 return t.ExecuteTemplate(rw, x.Name, x.Data) 60 } 61 cloned, err := t.Clone() 62 if err != nil { 63 return err 64 } 65 cloned = cloned.Funcs(x.FuncMap) 66 if x.Name == "" { 67 return cloned.Execute(rw, x.Data) 68 } 69 return cloned.ExecuteTemplate(rw, x.Name, x.Data) 70 case safehtml.HTML: 71 rw.Header().Set("Content-Type", "text/html; charset=utf-8") 72 _, err := io.WriteString(rw, x.String()) 73 return err 74 case FileServerResponse: 75 rw.Header().Set("Content-Type", x.ContentType()) 76 // The http package will take care of writing the file body. 77 return nil 78 case RedirectResponse: 79 http.Redirect(rw, x.Request.req, x.Location, int(x.Code)) 80 return nil 81 case NoContentResponse: 82 rw.WriteHeader(int(StatusNoContent)) 83 return nil 84 default: 85 return fmt.Errorf("%T is not a safe response type and it cannot be written", resp) 86 } 87 } 88 89 // Error writes the error response to the http.ResponseWriter. 90 // 91 // Error sets the Content-Type to "text/plain; charset=utf-8" through calling 92 // WriteTextError. 93 func (DefaultDispatcher) Error(rw http.ResponseWriter, resp ErrorResponse) error { 94 writeTextError(rw, resp) 95 return nil 96 }