not give error if hashes are prefixed with 0x (#277)

* ethersphere/go-ethereum#182:
- added a case error struct that contains information about certain error cases in which we would like to output more information to the client
- added a validation method that iterates and adds the information that is stored in the error cases

* PR Requested changes

* Merge conflicts

* linting
This commit is contained in:
Elad Nachmias 2018-03-05 11:12:14 +01:00 committed by Balint Gabor
parent 19f0c2de7f
commit 5b8a06bfde
5 changed files with 87 additions and 9 deletions

3
.gitignore vendored
View file

@ -34,6 +34,9 @@ profile.cov
# IdeaIDE # IdeaIDE
.idea .idea
# VS Code
.vscode
# dashboard # dashboard
/dashboard/assets/flow-typed /dashboard/assets/flow-typed
/dashboard/assets/node_modules /dashboard/assets/node_modules

View file

@ -35,6 +35,7 @@ import (
//templateMap holds a mapping of an HTTP error code to a template //templateMap holds a mapping of an HTTP error code to a template
var templateMap map[int]*template.Template var templateMap map[int]*template.Template
var caseErrors []CaseError
//metrics variables //metrics variables
var ( var (
@ -51,6 +52,13 @@ type ResponseParams struct {
Details template.HTML Details template.HTML
} }
//a custom error case struct that would be used to store validators and
//additional error info to display with client responses.
type CaseError struct {
Validator func(*Request) bool
Msg func(*Request) string
}
//we init the error handling right on boot time, so lookup and http response is fast //we init the error handling right on boot time, so lookup and http response is fast
func init() { func init() {
initErrHandling() initErrHandling()
@ -74,6 +82,29 @@ func initErrHandling() {
//assign formatted HTML to the code //assign formatted HTML to the code
templateMap[code] = template.Must(template.New(fmt.Sprintf("%d", code)).Parse(tname)) templateMap[code] = template.Must(template.New(fmt.Sprintf("%d", code)).Parse(tname))
} }
caseErrors = []CaseError{
{
Validator: func(r *Request) bool { return r.uri != nil && r.uri.Addr != "" && strings.HasPrefix(r.uri.Addr, "0x") },
Msg: func(r *Request) string {
uriCopy := r.uri
uriCopy.Addr = strings.TrimPrefix(uriCopy.Addr, "0x")
return fmt.Sprintf(`The requested hash seems to be prefixed with '0x'. You will be redirected to the correct URL within 5 seconds.<br/>
Please click <a href='%[1]s'>here</a> if your browser does not redirect you.<script>setTimeout("location.href='%[1]s';",5000);</script>`, "/"+uriCopy.String())
},
}}
}
//ValidateCaseErrors is a method that process the request object through certain validators
//that assert if certain conditions are met for further information to log as an error
func ValidateCaseErrors(r *Request) string {
for _, err := range caseErrors {
if err.Validator(r) {
return err.Msg(r)
}
}
return ""
} }
//ShowMultipeChoices is used when a user requests a resource in a manifest which results //ShowMultipeChoices is used when a user requests a resource in a manifest which results
@ -111,7 +142,9 @@ func ShowMultipleChoices(w http.ResponseWriter, req *Request, list api.ManifestL
//The function just takes a string message which will be displayed in the error page. //The function just takes a string message which will be displayed in the error page.
//The code is used to evaluate which template will be displayed //The code is used to evaluate which template will be displayed
//(and return the correct HTTP status code) //(and return the correct HTTP status code)
func Respond(w http.ResponseWriter, req *Request, msg string, code int) { func Respond(w http.ResponseWriter, req *Request, msg string, code int) {
additionalMessage := ValidateCaseErrors(req)
switch code { switch code {
case http.StatusInternalServerError: case http.StatusInternalServerError:
log.Output(msg, log.LvlError, 3, "ruid", req.ruid, "code", code) log.Output(msg, log.LvlError, 3, "ruid", req.ruid, "code", code)
@ -122,6 +155,7 @@ func Respond(w http.ResponseWriter, req *Request, msg string, code int) {
respond(w, &req.Request, &ResponseParams{ respond(w, &req.Request, &ResponseParams{
Code: code, Code: code,
Msg: msg, Msg: msg,
Details: template.HTML(additionalMessage),
Timestamp: time.Now().Format(time.RFC1123), Timestamp: time.Now().Format(time.RFC1123),
template: getTemplate(code), template: getTemplate(code),
}) })

View file

@ -168,6 +168,11 @@ func GetGenericErrorPage() string {
{{.Msg}} {{.Msg}}
</td> </td>
</tr> </tr>
<tr>
<td class="value">
{{.Details}}
</td>
</tr>
<tr> <tr>
<td class="key"> <td class="key">
@ -342,6 +347,12 @@ func GetNotFoundErrorPage() string {
{{.Msg}} {{.Msg}}
</td> </td>
</tr> </tr>
<tr>
<td class="value">
{{.Details}}
</td>
</tr>
<tr> <tr>
<td class="key"> <td class="key">

View file

@ -18,12 +18,13 @@ package http_test
import ( import (
"encoding/json" "encoding/json"
"golang.org/x/net/html"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"strings" "strings"
"testing" "testing"
"golang.org/x/net/html"
"github.com/ethereum/go-ethereum/swarm/testutil" "github.com/ethereum/go-ethereum/swarm/testutil"
) )
@ -96,8 +97,37 @@ func Test500Page(t *testing.T) {
defer resp.Body.Close() defer resp.Body.Close()
respbody, err = ioutil.ReadAll(resp.Body) respbody, err = ioutil.ReadAll(resp.Body)
if resp.StatusCode != 500 || !strings.Contains(string(respbody), "500") { if resp.StatusCode != 404 {
t.Fatalf("Invalid Status Code received, expected 500, got %d", resp.StatusCode) t.Fatalf("Invalid Status Code received, expected 404, got %d", resp.StatusCode)
}
_, err = html.Parse(strings.NewReader(string(respbody)))
if err != nil {
t.Fatalf("HTML validation failed for error page returned!")
}
}
func Test500PageWith0xHashPrefix(t *testing.T) {
srv := testutil.NewTestSwarmServer(t)
defer srv.Close()
var resp *http.Response
var respbody []byte
url := srv.URL + "/bzz:/0xthisShouldFailWith500CodeAndAHelpfulMessage"
resp, err := http.Get(url)
if err != nil {
t.Fatalf("Request failed: %v", err)
}
defer resp.Body.Close()
respbody, err = ioutil.ReadAll(resp.Body)
if resp.StatusCode != 404 {
t.Fatalf("Invalid Status Code received, expected 404, got %d", resp.StatusCode)
}
if !strings.Contains(string(respbody), "The requested hash seems to be prefixed with") {
t.Fatalf("Did not receive the expected error message")
} }
_, err = html.Parse(strings.NewReader(string(respbody))) _, err = html.Parse(strings.NewReader(string(respbody)))
@ -127,8 +157,8 @@ func TestJsonResponse(t *testing.T) {
defer resp.Body.Close() defer resp.Body.Close()
respbody, err = ioutil.ReadAll(resp.Body) respbody, err = ioutil.ReadAll(resp.Body)
if resp.StatusCode != 500 { if resp.StatusCode != 404 {
t.Fatalf("Invalid Status Code received, expected 500, got %d", resp.StatusCode) t.Fatalf("Invalid Status Code received, expected 404, got %d", resp.StatusCode)
} }
if !isJSON(string(respbody)) { if !isJSON(string(respbody)) {

View file

@ -474,7 +474,7 @@ func (s *Server) HandleGet(w http.ResponseWriter, r *Request) {
key, err := s.api.Resolve(r.uri) key, err := s.api.Resolve(r.uri)
if err != nil { if err != nil {
getFail.Inc(1) getFail.Inc(1)
Respond(w, r, fmt.Sprintf("cannot resolve %s: %s", r.uri.Addr, err), http.StatusInternalServerError) Respond(w, r, fmt.Sprintf("cannot resolve %s: %s", r.uri.Addr, err), http.StatusNotFound)
return return
} }
@ -558,7 +558,7 @@ func (s *Server) HandleGetFiles(w http.ResponseWriter, r *Request) {
key, err := s.api.Resolve(r.uri) key, err := s.api.Resolve(r.uri)
if err != nil { if err != nil {
getFilesFail.Inc(1) getFilesFail.Inc(1)
Respond(w, r, fmt.Sprintf("cannot resolve %s: %s", r.uri.Addr, err), http.StatusInternalServerError) Respond(w, r, fmt.Sprintf("cannot resolve %s: %s", r.uri.Addr, err), http.StatusNotFound)
return return
} }
@ -631,7 +631,7 @@ func (s *Server) HandleGetList(w http.ResponseWriter, r *Request) {
key, err := s.api.Resolve(r.uri) key, err := s.api.Resolve(r.uri)
if err != nil { if err != nil {
getListFail.Inc(1) getListFail.Inc(1)
Respond(w, r, fmt.Sprintf("cannot resolve %s: %s", r.uri.Addr, err), http.StatusInternalServerError) Respond(w, r, fmt.Sprintf("cannot resolve %s: %s", r.uri.Addr, err), http.StatusNotFound)
return return
} }
@ -735,7 +735,7 @@ func (s *Server) HandleGetFile(w http.ResponseWriter, r *Request) {
key, err := s.api.Resolve(r.uri) key, err := s.api.Resolve(r.uri)
if err != nil { if err != nil {
getFileFail.Inc(1) getFileFail.Inc(1)
Respond(w, r, fmt.Sprintf("cannot resolve %s: %s", r.uri.Addr, err), http.StatusInternalServerError) Respond(w, r, fmt.Sprintf("cannot resolve %s: %s", r.uri.Addr, err), http.StatusNotFound)
return return
} }