Skip to content

Instantly share code, notes, and snippets.

@Ekliptor
Created April 3, 2021 10:29
Show Gist options
  • Save Ekliptor/0dfe2552191537efed12db010d7eccf5 to your computer and use it in GitHub Desktop.
Save Ekliptor/0dfe2552191537efed12db010d7eccf5 to your computer and use it in GitHub Desktop.
Go Web Assembly compile server
func (api *CompileAPI) ServeHTTP(w http.ResponseWriter, r *http.Request) {
defer r.Body.Close()
// Get code from params
type parameters struct {
Code string `json:"code" validate:"required"`
}
decoder := json.NewDecoder(r.Body)
params := parameters{}
err := decoder.Decode(&params)
if err != nil {
api.respondWithError(w, http.StatusInternalServerError, "Couldn't decode parameters")
return
}
params.Code = strings.TrimSpace(params.Code)
// create file system location for compilation path
usr, err := user.Current()
if err != nil {
api.respondWithError(w, http.StatusInternalServerError, "Couldn't get system user")
return
}
workingDir := filepath.Join(usr.HomeDir, ".wasm", uuid.New().String())
err = os.MkdirAll(workingDir, os.ModePerm)
if err != nil {
api.respondWithError(w, http.StatusInternalServerError, "Couldn't create directory for compilation")
return
}
defer func() {
err = os.RemoveAll(workingDir)
if err != nil {
api.respondWithError(w, http.StatusInternalServerError, "Couldn't clean up code from compilation")
return
}
}()
f, err := os.Create(filepath.Join(workingDir, "main.go"))
if err != nil {
api.respondWithError(w, http.StatusInternalServerError, "Couldn't create code file for compilation")
return
}
defer f.Close()
dat := []byte(params.Code)
_, err = f.Write(dat)
if err != nil {
api.respondWithError(w, http.StatusInternalServerError, "Couldn't write code to file for compilation")
return
}
// compile the wasm
const outputBinary = "main.wasm"
os.Setenv("GOOS", "js")
os.Setenv("GOARCH", "wasm")
os.Setenv("GO111MODULE", "off") // otherwise it tries to load the main module of our project's go.mod file
cmd := exec.Command("go", "build", "-o", outputBinary)
cmd.Dir = workingDir
stderr, err := cmd.StderrPipe()
if err != nil {
api.respondWithError(w, http.StatusInternalServerError, err.Error())
return
}
if err := cmd.Start(); err != nil {
api.respondWithError(w, http.StatusInternalServerError, err.Error())
return
}
stdErr, err := ioutil.ReadAll(stderr)
if err != nil {
api.respondWithError(w, http.StatusInternalServerError, err.Error())
return
}
stdErrString := string(stdErr)
if stdErrString != "" {
parts := strings.Split(stdErrString, workingDir)
if len(parts) < 2 {
api.respondWithError(w, http.StatusInternalServerError, stdErrString)
return
}
api.respondWithError(w, http.StatusBadRequest, parts[1])
return
}
if err := cmd.Wait(); err != nil {
api.respondWithError(w, http.StatusInternalServerError, err.Error())
return
}
// return WebAssembly binary as HTTP response
dat, err = ioutil.ReadFile(filepath.Join(workingDir, outputBinary))
if err != nil {
api.respondWithError(w, http.StatusInternalServerError, err.Error())
return
}
w.Write(dat)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment