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  }