So, I tried to write a web server in Golang to fit in with webview/webview, although I currently have an important issue (which is cgo
)...
There are Go language-specific concepts I don't really understand.
- Channel
<-
Will signals works in Windows, which is not POSIX?
And there are things I clearly don't like...
// Scoping of error variables.
// Is there a better naming conventions?
// IMO, these kind of namings are very typo-prone.
_, e1 := f1()
if e1 != nil {
log.Fatal(e1)
}
data, e2 := f1()
if e2 != nil {
log.Fatal(e2)
}
In short, I don't really like Go, but industries and job markets demand it.
I haven't done JSON serialization / deserialization yet. Not to mention middlewares and auth. However, I do cover
req.Query
req.Body
res.Write
- Status codes
package main
import (
"context"
"fmt"
"io/ioutil"
"log"
"net"
"net/http"
"os"
"os/signal"
"syscall"
"time"
)
func main() {
port := os.Getenv("PORT")
if port == "" {
port = "0"
}
listener, err := net.Listen("tcp", "localhost:"+port)
if err != nil {
log.Fatal(err)
}
http.Handle("/", http.FileServer(http.Dir("./dist")))
http.HandleFunc("/api/file", func(w http.ResponseWriter, r *http.Request) {
f := r.URL.Query()["filename"]
if len(f) == 0 {
throwHTTP(&w, fmt.Errorf("filename not supplied"), http.StatusNotFound)
return
}
filename := f[0]
if r.Method == "GET" {
data, eReadFile := ioutil.ReadFile(filename)
if eReadFile != nil {
throwHTTP(&w, eReadFile, http.StatusInternalServerError)
return
}
w.Write(data)
return
} else if r.Method == "PUT" {
data, eReadAll := ioutil.ReadAll(r.Body)
if eReadAll != nil {
throwHTTP(&w, eReadAll, http.StatusInternalServerError)
return
}
eWriteFile := ioutil.WriteFile(filename, data, 0666)
if eWriteFile != nil {
throwHTTP(&w, eWriteFile, http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusCreated)
return
} else if r.Method == "DELETE" {
eRemove := os.Remove(filename)
if eRemove != nil {
throwHTTP(&w, eRemove, http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusCreated)
return
}
throwHTTP(&w, fmt.Errorf("unsupported method"), http.StatusNotFound)
})
go func() {
// True port will be autogenerated
// And Addr() generated accordingly
log.Println("Listening at:", "http://"+listener.Addr().String())
if err := http.Serve(listener, nil); err != http.ErrServerClosed {
log.Fatal(err)
}
}()
// Cleaning up should be done in 10 seconds
onExit(10 * time.Second)
}
func onExit(timeout time.Duration) {
signals := make(chan os.Signal, 1)
signal.Notify(signals, os.Interrupt, syscall.SIGTERM)
<-signals
log.Println("Cleaning up...")
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
// onExit proper
func() {
time.Sleep(2 * time.Second)
}()
if _, ok := ctx.Deadline(); ok {
log.Println("Clean-up finished. Closing...")
// secs := (time.Until(deadline) + time.Second/2) / time.Second
// log.Printf("Clean-up finished %ds before deadline\n", secs)
} else {
log.Fatal(fmt.Sprintf("Clean-up timeout. Not finished within %ds.", timeout/time.Second))
}
}
func throwHTTP(w *http.ResponseWriter, e error, code int) {
http.Error(*w, e.Error(), code)
log.Println(e, code)
}
Tested with cURL's
% PORT=3000 go run .
% curl -i -X PUT --data 'hello' http://127.0.0.1:3000/api/file\?filename\=test.txt
I have another nice way to connect with frontend. You can argue that sending SQL and its parameters might be a better way...
import Loki from 'lokijs'
class LokiRestAdaptor {
loadDatabase (dbname: string, callback: (data: string | null | Error) => void) {
fetch(`/api/file?filename=${encodeURIComponent(dbname)}`)
.then((r) => r.text())
.then((r) => callback(r))
.catch((e) => callback(e))
}
saveDatabase (dbname: string, dbstring: string, callback: (e: Error | null) => void) {
fetch(`/api/file?filename=${encodeURIComponent(dbname)}`, {
method: 'PUT',
body: dbstring
})
.then(() => callback(null))
.catch((e) => callback(e))
}
deleteDatabase (dbname: string, callback: (data: Error | null) => void) {
fetch(`/api/file?filename=${encodeURIComponent(dbname)}`, {
method: 'DELETE'
})
.then(() => callback(null))
.catch((e) => callback(e))
}
}
// eslint-disable-next-line import/no-mutable-exports
export let loki: Loki
export async function initDatabase () {
return new Promise((resolve) => {
loki = new Loki('db.loki', {
adapter: new LokiRestAdaptor(),
autoload: true,
autoloadCallback: () => {
resolve()
},
autosave: true,
autosaveInterval: 4000
})
})
}
Top comments (0)