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  }