diff --git a/README.md b/README.md index 29acb87..f67e9ca 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,7 @@ Rudimentary go chat server as a fun project. ```bash git clone https://git.coolaj86.com/coolaj86/chat.go.git go get gopkg.in/yaml.v2 +go get github.com/emicklei/go-restful ``` # Usage diff --git a/chatserver.go b/chatserver.go index 87ee671..4608843 100644 --- a/chatserver.go +++ b/chatserver.go @@ -16,19 +16,22 @@ import ( "net/http" "net/url" "os" + "path" "strconv" "strings" "sync" "time" + "github.com/emicklei/go-restful" "gopkg.in/yaml.v2" ) // I'm not sure how to pass nested structs, so I de-nested this. // TODO: Learn if passing nested structs is desirable? type Conf struct { - Port uint `yaml:"port,omitempty"` - Mailer ConfMailer + Port uint `yaml:"port,omitempty"` + Mailer ConfMailer + RootPath string `yaml:"root_path,omitempty"` } type ConfMailer struct { Url string `yaml:"url,omitempty"` @@ -456,8 +459,34 @@ func sendAuthCode(cnf ConfMailer, to string) (string, error) { return code, nil } +type myServer struct { + chans chan bufferedConn + net.Listener +} + +func (m *myServer) Accept() (net.Conn, error) { + bufConn := <-m.chans + return bufConn, nil +} + +func newMyServer(l net.Listener) *myServer { + return &myServer{make(chan bufferedConn), l} +} + var config Conf +func serveStatic(req *restful.Request, resp *restful.Response) { + actual := path.Join(config.RootPath, req.PathParameter("subpath")) + fmt.Printf("serving %s ... (from %s)\n", actual, req.PathParameter("subpath")) + http.ServeFile( + resp.ResponseWriter, + req.Request, + actual) +} +func serveHello(req *restful.Request, resp *restful.Response) { + fmt.Fprintf(resp, "{\"msg\":\"hello\"}") +} + func main() { flag.Usage = usage port := flag.Uint("telnet-port", 0, "tcp telnet chat port") @@ -474,6 +503,10 @@ func main() { if nil != err { config = Conf{} } + if "" == config.RootPath { + // TODO Embed the public dir at the default + config.RootPath = "./public" + } myRawConns := make(map[bufferedConn]bool) firstMsgs = make(chan myMsg, 128) @@ -516,12 +549,38 @@ func main() { newConns <- conn } }() + + // Learning by Example + // https://github.com/emicklei/go-restful/blob/master/examples/restful-multi-containers.go + // https://github.com/emicklei/go-restful/blob/master/examples/restful-basic-authentication.go + // https://github.com/emicklei/go-restful/blob/master/examples/restful-serve-static.go + // https://github.com/emicklei/go-restful/blob/master/examples/restful-pre-post-filters.go + container := restful.NewContainer() + + wsStatic := new(restful.WebService) + wsStatic.Path("/") + wsStatic.Route(wsStatic.GET("/").To(serveStatic)) + wsStatic.Route(wsStatic.GET("/{subpath:*}").To(serveStatic)) + container.Add(wsStatic) + + wsApi := new(restful.WebService) + wsApi.Path("/api") + wsApi.Route(wsApi.GET("/api/hello").To(serveHello)) /* - server := &http.Server{ - Addr: addr, - Handler: &myHandler{}, - } + ws.Route(ws.POST("/api/authn").To(createAuth)) + ws.Route(ws.POST("/api/authn/{email}").To(createAuth)) + ws.Route(ws.GET("/api").Filter(basicAuthenticate).To(hello2)) */ + container.Add(wsApi) + + server := &http.Server{ + Addr: addr, + Handler: container, + } + myHttpServer := newMyServer(sock) + go func() { + server.Serve(myHttpServer) + }() // Main event loop handling most access to shared data for { @@ -553,8 +612,9 @@ func main() { go handleSorted(bufConn) //case msg := <- myRooms["general"]: //delete(myRooms["general"], bufConn) - //case bufConn := <-newHttpClient: - //server.Serve(bufConn) + case bufConn := <-newHttpClient: + // this will be Accept()ed immediately by restful + myHttpServer.chans <- bufConn case msg := <-myMsgs: t := msg.receivedAt tf := "%d-%02d-%02d %02d:%02d:%02d (%s)" diff --git a/config.sample.yml b/config.sample.yml index 7b74fab..78dcfda 100644 --- a/config.sample.yml +++ b/config.sample.yml @@ -1,4 +1,5 @@ port: 4080 +root_path: ./public mailer: service: 'mailgun' url: 'https://api.mailgun.net/v3/example.com/messages' diff --git a/public/index.html b/public/index.html new file mode 100644 index 0000000..1a6dc11 --- /dev/null +++ b/public/index.html @@ -0,0 +1,26 @@ + + +
# Ask for an auth code (swap sub)
+curl -X POST https://localhost:4080/api/session \
+ -H 'Content-Type: application/json; charset=utf-8' \
+ -d '{"sub":"jon@example.com"}'
+
+# Validate auth code (swap session id, sub, and otp)
+curl -X POST https://localhost:4080/api/session/xyz \
+ -H 'Content-Type: application/json; charset=utf-8' \
+ -d '{"sub":"jon@example.com","otp":"secret123"}'
+
+# Post a message (swap api-token)
+curl -X POST https://localhost:4080/api/rooms/general \
+ -H 'Authorization: Bearer api-token' \
+ -H 'Content-Type: application/json; charset=utf-8' \
+ -d '{"msg":"hello"}'
+
+# Get a room's messages (swap api-token, since unix-epoch)
+curl https://localhost:4080/api/rooms/general?since=0 \
+ -H 'Authorization: Bearer api-token'
+
+
+
diff --git a/public/js/app.js b/public/js/app.js
new file mode 100644
index 0000000..9867863
--- /dev/null
+++ b/public/js/app.js
@@ -0,0 +1,3 @@
+"use strict"
+
+console.log("Hello, World!");