github.com/AlpineAIO/wails/v2@v2.0.0-beta.32.0.20240505041856-1047a8fa5fef/pkg/assetserver/webview/request_windows.go (about) 1 //go:build windows 2 // +build windows 3 4 package webview 5 6 import ( 7 "fmt" 8 "io" 9 "net/http" 10 "strings" 11 12 "github.com/wailsapp/go-webview2/pkg/edge" 13 ) 14 15 // NewRequest creates as new WebViewRequest for chromium. This Method must be called from the Main-Thread! 16 func NewRequest(env *edge.ICoreWebView2Environment, args *edge.ICoreWebView2WebResourceRequestedEventArgs, invokeSync func(fn func())) (Request, error) { 17 req, err := args.GetRequest() 18 if err != nil { 19 return nil, fmt.Errorf("GetRequest failed: %s", err) 20 } 21 defer req.Release() 22 23 r := &request{ 24 invokeSync: invokeSync, 25 } 26 27 code := http.StatusInternalServerError 28 r.response, err = env.CreateWebResourceResponse(nil, code, http.StatusText(code), "") 29 if err != nil { 30 return nil, fmt.Errorf("CreateWebResourceResponse failed: %s", err) 31 } 32 33 if err := args.PutResponse(r.response); err != nil { 34 r.finishResponse() 35 return nil, fmt.Errorf("PutResponse failed: %s", err) 36 } 37 38 r.deferral, err = args.GetDeferral() 39 if err != nil { 40 r.finishResponse() 41 return nil, fmt.Errorf("GetDeferral failed: %s", err) 42 } 43 44 r.url, r.urlErr = req.GetUri() 45 r.method, r.methodErr = req.GetMethod() 46 r.header, r.headerErr = getHeaders(req) 47 48 if content, err := req.GetContent(); err != nil { 49 r.bodyErr = err 50 } else if content != nil { 51 // It is safe to access Content from another Thread: https://learn.microsoft.com/en-us/microsoft-edge/webview2/concepts/threading-model#thread-safety 52 r.body = &iStreamReleaseCloser{stream: content} 53 } 54 55 return r, nil 56 } 57 58 var _ Request = &request{} 59 60 type request struct { 61 response *edge.ICoreWebView2WebResourceResponse 62 deferral *edge.ICoreWebView2Deferral 63 64 url string 65 urlErr error 66 67 method string 68 methodErr error 69 70 header http.Header 71 headerErr error 72 73 body io.ReadCloser 74 bodyErr error 75 rw *responseWriter 76 77 invokeSync func(fn func()) 78 } 79 80 func (r *request) URL() (string, error) { 81 return r.url, r.urlErr 82 } 83 84 func (r *request) Method() (string, error) { 85 return r.method, r.methodErr 86 } 87 88 func (r *request) Header() (http.Header, error) { 89 return r.header, r.headerErr 90 } 91 92 func (r *request) Body() (io.ReadCloser, error) { 93 return r.body, r.bodyErr 94 } 95 96 func (r *request) Response() ResponseWriter { 97 if r.rw != nil { 98 return r.rw 99 } 100 101 r.rw = &responseWriter{req: r} 102 return r.rw 103 } 104 105 func (r *request) Close() error { 106 var errs []error 107 if r.body != nil { 108 if err := r.body.Close(); err != nil { 109 errs = append(errs, err) 110 } 111 r.body = nil 112 } 113 114 if err := r.Response().Finish(); err != nil { 115 errs = append(errs, err) 116 } 117 118 return combineErrs(errs) 119 } 120 121 // finishResponse must be called on the main-thread 122 func (r *request) finishResponse() error { 123 var errs []error 124 if r.response != nil { 125 if err := r.response.Release(); err != nil { 126 errs = append(errs, err) 127 } 128 r.response = nil 129 } 130 if r.deferral != nil { 131 if err := r.deferral.Complete(); err != nil { 132 errs = append(errs, err) 133 } 134 135 if err := r.deferral.Release(); err != nil { 136 errs = append(errs, err) 137 } 138 r.deferral = nil 139 } 140 return combineErrs(errs) 141 } 142 143 type iStreamReleaseCloser struct { 144 stream *edge.IStream 145 closed bool 146 } 147 148 func (i *iStreamReleaseCloser) Read(p []byte) (int, error) { 149 if i.closed { 150 return 0, io.ErrClosedPipe 151 } 152 return i.stream.Read(p) 153 } 154 155 func (i *iStreamReleaseCloser) Close() error { 156 if i.closed { 157 return nil 158 } 159 i.closed = true 160 return i.stream.Release() 161 } 162 163 func getHeaders(req *edge.ICoreWebView2WebResourceRequest) (http.Header, error) { 164 header := http.Header{} 165 headers, err := req.GetHeaders() 166 if err != nil { 167 return nil, fmt.Errorf("GetHeaders Error: %s", err) 168 } 169 defer headers.Release() 170 171 headersIt, err := headers.GetIterator() 172 if err != nil { 173 return nil, fmt.Errorf("GetIterator Error: %s", err) 174 } 175 defer headersIt.Release() 176 177 for { 178 has, err := headersIt.HasCurrentHeader() 179 if err != nil { 180 return nil, fmt.Errorf("HasCurrentHeader Error: %s", err) 181 } 182 if !has { 183 break 184 } 185 186 name, value, err := headersIt.GetCurrentHeader() 187 if err != nil { 188 return nil, fmt.Errorf("GetCurrentHeader Error: %s", err) 189 } 190 191 header.Set(name, value) 192 if _, err := headersIt.MoveNext(); err != nil { 193 return nil, fmt.Errorf("MoveNext Error: %s", err) 194 } 195 } 196 197 // WebView2 has problems when a request returns a 304 status code and the WebView2 is going to hang for other 198 // requests including IPC calls. 199 // So prevent 304 status codes by removing the headers that are used in combinationwith caching. 200 header.Del("If-Modified-Since") 201 header.Del("If-None-Match") 202 return header, nil 203 } 204 205 func combineErrs(errs []error) error { 206 // TODO use Go1.20 errors.Join 207 if len(errs) == 0 { 208 return nil 209 } 210 211 errStrings := make([]string, len(errs)) 212 for i, err := range errs { 213 errStrings[i] = err.Error() 214 } 215 216 return fmt.Errorf(strings.Join(errStrings, "\n")) 217 }