From c63b1f1981ee6985b4294d021b6f34ce178a94b6 Mon Sep 17 00:00:00 2001 From: stffabi Date: Mon, 28 Feb 2022 09:14:03 +0100 Subject: [PATCH] [v2] Consolidate processRequest, improve logging and error handling (#1158) --- .../desktop/common/process_request.go | 55 +++++++++++++++++++ .../frontend/desktop/common/uri_translate.go | 32 ++++++++--- .../frontend/desktop/darwin/frontend.go | 39 +++---------- .../frontend/desktop/linux/frontend.go | 45 ++++----------- .../frontend/desktop/windows/frontend.go | 46 ++++++---------- 5 files changed, 114 insertions(+), 103 deletions(-) create mode 100644 v2/internal/frontend/desktop/common/process_request.go diff --git a/v2/internal/frontend/desktop/common/process_request.go b/v2/internal/frontend/desktop/common/process_request.go new file mode 100644 index 000000000..d0a278571 --- /dev/null +++ b/v2/internal/frontend/desktop/common/process_request.go @@ -0,0 +1,55 @@ +package common + +import ( + "fmt" + "net/http" + "os" + "strings" + + "github.com/wailsapp/wails/v2/internal/frontend/assetserver" +) + +type RequestRespone struct { + Body []byte + MimeType string + StatusCode int +} + +func (r RequestRespone) StatusText() string { + return http.StatusText(r.StatusCode) +} + +func (r RequestRespone) String() string { + return fmt.Sprintf("Body: '%s', StatusCode: %d", string(r.Body), r.StatusCode) +} + +func ProcessRequest(uri string, assets *assetserver.DesktopAssetServer, expectedScheme string, expectedHosts ...string) (RequestRespone, error) { + // Translate URI to file + file, err := translateUriToFile(uri, expectedScheme, expectedHosts...) + if err != nil { + if err == ErrUnexpectedHost { + body := fmt.Sprintf("expected host one of \"%s\"", strings.Join(expectedHosts, ",")) + return textResponse(body, http.StatusInternalServerError), err + } + + return RequestRespone{StatusCode: http.StatusInternalServerError}, err + } + + content, mimeType, err := assets.Load(file) + if err != nil { + statusCode := http.StatusInternalServerError + if os.IsNotExist(err) { + statusCode = http.StatusNotFound + } + return RequestRespone{StatusCode: statusCode}, err + } + + return RequestRespone{Body: content, MimeType: mimeType, StatusCode: http.StatusOK}, nil +} + +func textResponse(body string, statusCode int) RequestRespone { + if body == "" { + return RequestRespone{StatusCode: statusCode} + } + return RequestRespone{Body: []byte(body), MimeType: "text/plain;charset=UTF-8", StatusCode: statusCode} +} diff --git a/v2/internal/frontend/desktop/common/uri_translate.go b/v2/internal/frontend/desktop/common/uri_translate.go index 4cfd78824..75370408b 100644 --- a/v2/internal/frontend/desktop/common/uri_translate.go +++ b/v2/internal/frontend/desktop/common/uri_translate.go @@ -1,20 +1,34 @@ package common -import "net/url" +import ( + "fmt" + "net/url" +) -func TranslateUriToFile(uri string, expectedScheme string, expectedHost string) (file string, match bool, err error) { +var ErrUnexpectedScheme = fmt.Errorf("unexpected scheme") +var ErrUnexpectedHost = fmt.Errorf("unexpected host") + +func translateUriToFile(uri string, expectedScheme string, expectedHosts ...string) (file string, err error) { url, err := url.Parse(uri) if err != nil { - return "", false, err + return "", err } - if url.Scheme != expectedScheme || url.Host != expectedHost { - return "", false, nil + if url.Scheme != expectedScheme { + return "", ErrUnexpectedScheme } - filePath := url.Path - if filePath == "" { - filePath = "/" + for _, expectedHost := range expectedHosts { + if url.Host != expectedHost { + continue + } + + filePath := url.Path + if filePath == "" { + filePath = "/" + } + return filePath, nil } - return filePath, true, nil + + return "", ErrUnexpectedHost } diff --git a/v2/internal/frontend/desktop/darwin/frontend.go b/v2/internal/frontend/desktop/darwin/frontend.go index 56d8fd557..cd5adb94b 100644 --- a/v2/internal/frontend/desktop/darwin/frontend.go +++ b/v2/internal/frontend/desktop/darwin/frontend.go @@ -16,10 +16,8 @@ import "C" import ( "context" "encoding/json" - "fmt" "html/template" "log" - "os" "strconv" "unsafe" @@ -280,40 +278,21 @@ func (f *Frontend) ExecJS(js string) { func (f *Frontend) processRequest(r *request) { uri := C.GoString(r.url) - var _contents []byte - var _mimetype string - - // Translate URI to file - file, match, err := common.TranslateUriToFile(uri, "wails", "wails") - if err == nil { - if !match { - // This should never happen on darwin, because we get only called for wails:// - panic("Unexpected host for request on wails:// scheme") - } - - // Load file from asset store - _contents, _mimetype, err = f.assets.Load(file) - } - - statusCode := 200 + res, err := common.ProcessRequest(uri, f.assets, "wails", "wails") if err != nil { - if os.IsNotExist(err) { - statusCode = 404 - } else { - err = fmt.Errorf("Error processing request %s: %w", uri, err) - f.logger.Error(err.Error()) - statusCode = 500 - } + f.logger.Error("Error processing request '%s': %s (HttpResponse=%s)", uri, err, res) } - var data unsafe.Pointer - if _contents != nil { - data = unsafe.Pointer(&_contents[0]) + var content unsafe.Pointer + var contentLen int + if _contents := res.Body; _contents != nil { + content = unsafe.Pointer(&_contents[0]) + contentLen = len(_contents) } - mimetype := C.CString(_mimetype) + mimetype := C.CString(res.MimeType) defer C.free(unsafe.Pointer(mimetype)) - C.ProcessURLResponse(r.ctx, r.url, C.int(statusCode), mimetype, data, C.int(len(_contents))) + C.ProcessURLResponse(r.ctx, r.url, C.int(res.StatusCode), mimetype, content, C.int(contentLen)) } //func (f *Frontend) processSystemEvent(message string) { diff --git a/v2/internal/frontend/desktop/linux/frontend.go b/v2/internal/frontend/desktop/linux/frontend.go index 4b465173e..f431a66e8 100644 --- a/v2/internal/frontend/desktop/linux/frontend.go +++ b/v2/internal/frontend/desktop/linux/frontend.go @@ -14,8 +14,8 @@ import "C" import ( "context" "encoding/json" - "fmt" "log" + "net/http" "os" "strconv" "text/template" @@ -292,46 +292,23 @@ func (f *Frontend) processRequest(request unsafe.Pointer) { uri := C.webkit_uri_scheme_request_get_uri(req) goURI := C.GoString(uri) - file, match, err := common.TranslateUriToFile(goURI, "wails", "") + res, err := common.ProcessRequest(goURI, f.assets, "wails", "", "null") if err != nil { - // TODO Handle errors - return - } else if !match { - file, match, err = common.TranslateUriToFile(goURI, "wails", "null") - if err != nil { - // TODO Handle errors - return - } else if !match { - // This should never happen on linux, because we get only called for wails:// - panic("Unexpected host for request on wails:// scheme") - } + f.logger.Error("Error processing request '%s': %s (HttpResponse=%s)", goURI, err, res) } - // Load file from asset store - content, mimeType, err := f.assets.Load(file) - - // TODO How to return 404/500 errors to webkit? - if err != nil { - if os.IsNotExist(err) { - message := C.CString("File not found") - gerr := C.g_error_new_literal(C.g_quark_from_string(message), C.int(404), message) - C.webkit_uri_scheme_request_finish_error(req, gerr) - C.g_error_free(gerr) - C.free(unsafe.Pointer(message)) - } else { - err = fmt.Errorf("Error processing request %s: %v", uri, err) - message := C.CString("Internal Error") - gerr := C.g_error_new_literal(C.g_quark_from_string(message), C.int(500), message) - C.webkit_uri_scheme_request_finish_error(req, gerr) - C.g_error_free(gerr) - C.free(unsafe.Pointer(message)) - } + if code := res.StatusCode; code != http.StatusOK { + message := C.CString(res.StatusText()) + gerr := C.g_error_new_literal(C.g_quark_from_string(message), C.int(code), message) + C.webkit_uri_scheme_request_finish_error(req, gerr) + C.g_error_free(gerr) + C.free(unsafe.Pointer(message)) return } - cContent := C.CString(string(content)) + cContent := C.CString(string(res.Body)) defer C.free(unsafe.Pointer(cContent)) - cMimeType := C.CString(mimeType) + cMimeType := C.CString(res.MimeType) defer C.free(unsafe.Pointer(cMimeType)) cLen := C.long(C.strlen(cContent)) stream := C.g_memory_input_stream_new_from_data( diff --git a/v2/internal/frontend/desktop/windows/frontend.go b/v2/internal/frontend/desktop/windows/frontend.go index 0ce901706..8fc48e598 100644 --- a/v2/internal/frontend/desktop/windows/frontend.go +++ b/v2/internal/frontend/desktop/windows/frontend.go @@ -8,7 +8,6 @@ import ( "encoding/json" "fmt" "log" - "os" "runtime" "strconv" "strings" @@ -353,46 +352,32 @@ func (f *Frontend) processRequest(req *edge.ICoreWebView2WebResourceRequest, arg //Get the request uri, _ := req.GetUri() - var content []byte - var mimeType string - - // Translate URI to file - file, match, err := common.TranslateUriToFile(uri, "file", "wails") - if err == nil { - if !match { - // In this case we should let the WebView2 handle the request with it's default handler - return - } - - // Load file from asset store - content, mimeType, err = f.assets.Load(file) - } - - statusCode := 200 - reasonPhrase := "OK" - if err != nil { - if os.IsNotExist(err) { - statusCode = 404 - reasonPhrase = "Not Found" - } else { - err = fmt.Errorf("Error processing request %s: %w", uri, err) - f.logger.Error(err.Error()) - statusCode = 500 - reasonPhrase = "Internal Server Error" - } + res, err := common.ProcessRequest(uri, f.assets, "file", "wails") + if err == common.ErrUnexpectedScheme { + // In this case we should let the WebView2 handle the request with its default handler + return + } else if err == common.ErrUnexpectedHost { + // This means file:// to something other than wails, should we prevent this? + // Maybe we should introduce an AllowList for explicitly allowing schemes and hosts, this could also be interesting + // for all other platforms to improve security. + return // Let WebView2 handle the request with its default handler + } else if err != nil { + f.logger.Error("Error processing request '%s': %s (HttpResponse=%s)", uri, err, res) } headers := []string{} - if mimeType != "" { + if mimeType := res.MimeType; mimeType != "" { headers = append(headers, "Content-Type: "+mimeType) } + content := res.Body if content != nil && f.servingFromDisk { headers = append(headers, "Pragma: no-cache") } env := f.chromium.Environment() - response, err := env.CreateWebResourceResponse(content, statusCode, reasonPhrase, strings.Join(headers, "\n")) + response, err := env.CreateWebResourceResponse(content, res.StatusCode, res.StatusText(), strings.Join(headers, "\n")) if err != nil { + f.logger.Error("CreateWebResourceResponse Error: %s", err) return } defer response.Release() @@ -400,6 +385,7 @@ func (f *Frontend) processRequest(req *edge.ICoreWebView2WebResourceRequest, arg // Send response back err = args.PutResponse(response) if err != nil { + f.logger.Error("PutResponse Error: %s", err) return } }