github.com/codingfuture/orig-energi3@v0.8.4/swarm/api/http/response.go (about) 1 // Copyright 2018 The Energi Core Authors 2 // Copyright 2018 The go-ethereum Authors 3 // This file is part of the Energi Core library. 4 // 5 // The Energi Core library is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Lesser General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // The Energi Core library is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Lesser General Public License for more details. 14 // 15 // You should have received a copy of the GNU Lesser General Public License 16 // along with the Energi Core library. If not, see <http://www.gnu.org/licenses/>. 17 18 package http 19 20 import ( 21 "encoding/json" 22 "fmt" 23 "html/template" 24 "net/http" 25 "strings" 26 "time" 27 28 "github.com/ethereum/go-ethereum/log" 29 "github.com/ethereum/go-ethereum/metrics" 30 "github.com/ethereum/go-ethereum/swarm/api" 31 ) 32 33 var ( 34 htmlCounter = metrics.NewRegisteredCounter("api.http.errorpage.html.count", nil) 35 jsonCounter = metrics.NewRegisteredCounter("api.http.errorpage.json.count", nil) 36 plaintextCounter = metrics.NewRegisteredCounter("api.http.errorpage.plaintext.count", nil) 37 ) 38 39 type ResponseParams struct { 40 Msg template.HTML 41 Code int 42 Timestamp string 43 template *template.Template 44 Details template.HTML 45 } 46 47 // ShowMultipleChoices is used when a user requests a resource in a manifest which results 48 // in ambiguous results. It returns a HTML page with clickable links of each of the entry 49 // in the manifest which fits the request URI ambiguity. 50 // For example, if the user requests bzz:/<hash>/read and that manifest contains entries 51 // "readme.md" and "readinglist.txt", a HTML page is returned with this two links. 52 // This only applies if the manifest has no default entry 53 func ShowMultipleChoices(w http.ResponseWriter, r *http.Request, list api.ManifestList) { 54 log.Debug("ShowMultipleChoices", "ruid", GetRUID(r.Context()), "uri", GetURI(r.Context())) 55 msg := "" 56 if list.Entries == nil { 57 respondError(w, r, "Could not resolve", http.StatusInternalServerError) 58 return 59 } 60 requestUri := strings.TrimPrefix(r.RequestURI, "/") 61 62 uri, err := api.Parse(requestUri) 63 if err != nil { 64 respondError(w, r, "Bad Request", http.StatusBadRequest) 65 } 66 67 uri.Scheme = "bzz-list" 68 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()) 69 respondTemplate(w, r, "error", msg, http.StatusMultipleChoices) 70 } 71 72 func respondTemplate(w http.ResponseWriter, r *http.Request, templateName, msg string, code int) { 73 log.Debug("respondTemplate", "ruid", GetRUID(r.Context()), "uri", GetURI(r.Context())) 74 respond(w, r, &ResponseParams{ 75 Code: code, 76 Msg: template.HTML(msg), 77 Timestamp: time.Now().Format(time.RFC1123), 78 template: TemplatesMap[templateName], 79 }) 80 } 81 82 func respondError(w http.ResponseWriter, r *http.Request, msg string, code int) { 83 log.Info("respondError", "ruid", GetRUID(r.Context()), "uri", GetURI(r.Context()), "code", code) 84 respondTemplate(w, r, "error", msg, code) 85 } 86 87 func respond(w http.ResponseWriter, r *http.Request, params *ResponseParams) { 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.Info("respondHTML", "ruid", GetRUID(r.Context()), "code", params.Code) 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.Info("respondJSON", "ruid", GetRUID(r.Context()), "code", params.Code) 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.Info("respondPlaintext", "ruid", GetRUID(r.Context()), "code", params.Code) 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 }