github.com/FUSIONFoundation/efsn@v3.6.2-0.20200916075423-dbb5dd5d2cc7+incompatible/swarm/api/http/response.go (about) 1 // Copyright 2017 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package http 18 19 import ( 20 "encoding/json" 21 "fmt" 22 "html/template" 23 "net/http" 24 "strings" 25 "time" 26 27 "github.com/FusionFoundation/efsn/log" 28 "github.com/FusionFoundation/efsn/metrics" 29 "github.com/FusionFoundation/efsn/swarm/api" 30 ) 31 32 var ( 33 htmlCounter = metrics.NewRegisteredCounter("api.http.errorpage.html.count", nil) 34 jsonCounter = metrics.NewRegisteredCounter("api.http.errorpage.json.count", nil) 35 plaintextCounter = metrics.NewRegisteredCounter("api.http.errorpage.plaintext.count", nil) 36 ) 37 38 type ResponseParams struct { 39 Msg template.HTML 40 Code int 41 Timestamp string 42 template *template.Template 43 Details template.HTML 44 } 45 46 // ShowMultipleChoices is used when a user requests a resource in a manifest which results 47 // in ambiguous results. It returns a HTML page with clickable links of each of the entry 48 // in the manifest which fits the request URI ambiguity. 49 // For example, if the user requests bzz:/<hash>/read and that manifest contains entries 50 // "readme.md" and "readinglist.txt", a HTML page is returned with this two links. 51 // This only applies if the manifest has no default entry 52 func ShowMultipleChoices(w http.ResponseWriter, r *http.Request, list api.ManifestList) { 53 log.Debug("ShowMultipleChoices", "ruid", GetRUID(r.Context()), "uri", GetURI(r.Context())) 54 msg := "" 55 if list.Entries == nil { 56 RespondError(w, r, "Could not resolve", http.StatusInternalServerError) 57 return 58 } 59 requestUri := strings.TrimPrefix(r.RequestURI, "/") 60 61 uri, err := api.Parse(requestUri) 62 if err != nil { 63 RespondError(w, r, "Bad Request", http.StatusBadRequest) 64 } 65 66 uri.Scheme = "bzz-list" 67 msg += fmt.Sprintf("Disambiguation:<br/>Your request may refer to multiple choices.<br/>Click <a class=\"orange\" href='"+"/"+uri.String()+"'>here</a> if your browser does not redirect you within 5 seconds.<script>setTimeout(\"location.href='%s';\",5000);</script><br/>", "/"+uri.String()) 68 RespondTemplate(w, r, "error", msg, http.StatusMultipleChoices) 69 } 70 71 func RespondTemplate(w http.ResponseWriter, r *http.Request, templateName, msg string, code int) { 72 log.Debug("RespondTemplate", "ruid", GetRUID(r.Context()), "uri", GetURI(r.Context())) 73 respond(w, r, &ResponseParams{ 74 Code: code, 75 Msg: template.HTML(msg), 76 Timestamp: time.Now().Format(time.RFC1123), 77 template: TemplatesMap[templateName], 78 }) 79 } 80 81 func RespondError(w http.ResponseWriter, r *http.Request, msg string, code int) { 82 log.Debug("RespondError", "ruid", GetRUID(r.Context()), "uri", GetURI(r.Context()), "code", code) 83 RespondTemplate(w, r, "error", msg, code) 84 } 85 86 func respond(w http.ResponseWriter, r *http.Request, params *ResponseParams) { 87 88 w.WriteHeader(params.Code) 89 90 if params.Code >= 400 { 91 w.Header().Del("Cache-Control") 92 w.Header().Del("ETag") 93 } 94 95 acceptHeader := r.Header.Get("Accept") 96 // this cannot be in a switch since an Accept header can have multiple values: "Accept: */*, text/html, application/xhtml+xml, application/xml;q=0.9, */*;q=0.8" 97 if strings.Contains(acceptHeader, "application/json") { 98 if err := respondJSON(w, r, params); err != nil { 99 RespondError(w, r, "Internal server error", http.StatusInternalServerError) 100 } 101 } else if strings.Contains(acceptHeader, "text/html") { 102 respondHTML(w, r, params) 103 } else { 104 respondPlaintext(w, r, params) //returns nice errors for curl 105 } 106 } 107 108 func respondHTML(w http.ResponseWriter, r *http.Request, params *ResponseParams) { 109 htmlCounter.Inc(1) 110 log.Debug("respondHTML", "ruid", GetRUID(r.Context())) 111 err := params.template.Execute(w, params) 112 if err != nil { 113 log.Error(err.Error()) 114 } 115 } 116 117 func respondJSON(w http.ResponseWriter, r *http.Request, params *ResponseParams) error { 118 jsonCounter.Inc(1) 119 log.Debug("respondJSON", "ruid", GetRUID(r.Context())) 120 w.Header().Set("Content-Type", "application/json") 121 return json.NewEncoder(w).Encode(params) 122 } 123 124 func respondPlaintext(w http.ResponseWriter, r *http.Request, params *ResponseParams) error { 125 plaintextCounter.Inc(1) 126 log.Debug("respondPlaintext", "ruid", GetRUID(r.Context())) 127 w.Header().Set("Content-Type", "text/plain") 128 strToWrite := "Code: " + fmt.Sprintf("%d", params.Code) + "\n" 129 strToWrite += "Message: " + string(params.Msg) + "\n" 130 strToWrite += "Timestamp: " + params.Timestamp + "\n" 131 _, err := w.Write([]byte(strToWrite)) 132 return err 133 }