github.com/vugu/vugu@v0.3.5/devutil/handlers.go (about) 1 package devutil 2 3 import ( 4 "bytes" 5 "io" 6 "io/ioutil" 7 "log" 8 "net/http" 9 "os" 10 "sync" 11 "time" 12 ) 13 14 // Compiler is implemented by WasmCompiler and TinygoCompiler. 15 type Compiler interface { 16 Execute() (outpath string, err error) 17 } 18 19 // WasmExecJSer is implemented by WasmCompiler and TinygoCompiler. 20 type WasmExecJSer interface { 21 WasmExecJS() (contents io.Reader, err error) 22 } 23 24 // MainWasmHandler calls WasmCompiler.Build and responds with the resulting .wasm file. 25 type MainWasmHandler struct { 26 wc Compiler 27 } 28 29 // NewMainWasmHandler returns an initialized MainWasmHandler. 30 func NewMainWasmHandler(wc Compiler) *MainWasmHandler { 31 return &MainWasmHandler{ 32 wc: wc, 33 } 34 } 35 36 // ServeHTTP implements http.Handler. 37 func (h *MainWasmHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { 38 39 outpath, err := h.wc.Execute() 40 if err != nil { 41 log.Printf("MainWasmHandler: Execute error:\n%v", err) 42 w.Header().Set("Content-Type", "text/plain; charset=utf-8") 43 http.Error(w, "MainWasmHandler: Execute error:\n"+err.Error(), 500) 44 return 45 } 46 defer os.Remove(outpath) 47 48 w.Header().Set("Content-Type", "application/wasm") 49 50 f, err := os.Open(outpath) 51 if err != nil { 52 log.Printf("MainWasmHandler: File open error:\n%v", err) 53 w.Header().Set("Content-Type", "text/plain; charset=utf-8") 54 http.Error(w, "MainWasmHandler: File open error:\n"+err.Error(), 500) 55 return 56 } 57 defer f.Close() 58 st, err := f.Stat() 59 if err != nil { 60 log.Printf("MainWasmHandler: File stat error:\n%v", err) 61 w.Header().Set("Content-Type", "text/plain; charset=utf-8") 62 http.Error(w, "MainWasmHandler: File stat error:\n"+err.Error(), 500) 63 return 64 } 65 66 http.ServeContent(w, r, r.URL.Path, st.ModTime(), f) 67 68 } 69 70 // WasmExecJSHandler calls WasmCompiler.WasmExecJS and responds with the resulting .js file. 71 // WasmCompiler.WasmExecJS will only be called the first time and subsequent times 72 // will return the same result from memory. (We're going to assume that you'll restart 73 // whatever process this is running in when upgrading your Go version.) 74 type WasmExecJSHandler struct { 75 wc WasmExecJSer 76 77 rwmu sync.RWMutex 78 content []byte 79 modTime time.Time 80 } 81 82 // NewWasmExecJSHandler returns an initialized WasmExecJSHandler. 83 func NewWasmExecJSHandler(wc WasmExecJSer) *WasmExecJSHandler { 84 return &WasmExecJSHandler{ 85 wc: wc, 86 } 87 } 88 89 // ServeHTTP implements http.Handler. 90 func (h *WasmExecJSHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { 91 92 h.rwmu.RLock() 93 content := h.content 94 modTime := h.modTime 95 h.rwmu.RUnlock() 96 97 if content == nil { 98 99 h.rwmu.Lock() 100 defer h.rwmu.Unlock() 101 102 rd, err := h.wc.WasmExecJS() 103 if err != nil { 104 log.Printf("error getting wasm_exec.js: %v", err) 105 http.Error(w, "error getting wasm_exec.js: "+err.Error(), 500) 106 return 107 } 108 109 b, err := ioutil.ReadAll(rd) 110 if err != nil { 111 log.Printf("error reading wasm_exec.js: %v", err) 112 http.Error(w, "error reading wasm_exec.js: "+err.Error(), 500) 113 return 114 } 115 116 h.content = b 117 content = h.content 118 h.modTime = time.Now() 119 modTime = h.modTime 120 121 } 122 123 w.Header().Set("Content-Type", "text/javascript") 124 http.ServeContent(w, r, r.URL.Path, modTime, bytes.NewReader(content)) 125 }