go go restful static server, mighty restful static server!

This commit is contained in:
AJ ONeal 2018-07-31 01:44:45 -06:00
parent 9c507ae68e
commit dd971fcc72
5 changed files with 99 additions and 8 deletions

View File

@ -7,6 +7,7 @@ Rudimentary go chat server as a fun project.
```bash ```bash
git clone https://git.coolaj86.com/coolaj86/chat.go.git git clone https://git.coolaj86.com/coolaj86/chat.go.git
go get gopkg.in/yaml.v2 go get gopkg.in/yaml.v2
go get github.com/emicklei/go-restful
``` ```
# Usage # Usage

View File

@ -16,19 +16,22 @@ import (
"net/http" "net/http"
"net/url" "net/url"
"os" "os"
"path"
"strconv" "strconv"
"strings" "strings"
"sync" "sync"
"time" "time"
"github.com/emicklei/go-restful"
"gopkg.in/yaml.v2" "gopkg.in/yaml.v2"
) )
// I'm not sure how to pass nested structs, so I de-nested this. // I'm not sure how to pass nested structs, so I de-nested this.
// TODO: Learn if passing nested structs is desirable? // TODO: Learn if passing nested structs is desirable?
type Conf struct { type Conf struct {
Port uint `yaml:"port,omitempty"` Port uint `yaml:"port,omitempty"`
Mailer ConfMailer Mailer ConfMailer
RootPath string `yaml:"root_path,omitempty"`
} }
type ConfMailer struct { type ConfMailer struct {
Url string `yaml:"url,omitempty"` Url string `yaml:"url,omitempty"`
@ -456,8 +459,34 @@ func sendAuthCode(cnf ConfMailer, to string) (string, error) {
return code, nil 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 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() { func main() {
flag.Usage = usage flag.Usage = usage
port := flag.Uint("telnet-port", 0, "tcp telnet chat port") port := flag.Uint("telnet-port", 0, "tcp telnet chat port")
@ -474,6 +503,10 @@ func main() {
if nil != err { if nil != err {
config = Conf{} config = Conf{}
} }
if "" == config.RootPath {
// TODO Embed the public dir at the default
config.RootPath = "./public"
}
myRawConns := make(map[bufferedConn]bool) myRawConns := make(map[bufferedConn]bool)
firstMsgs = make(chan myMsg, 128) firstMsgs = make(chan myMsg, 128)
@ -516,12 +549,38 @@ func main() {
newConns <- conn 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{ ws.Route(ws.POST("/api/authn").To(createAuth))
Addr: addr, ws.Route(ws.POST("/api/authn/{email}").To(createAuth))
Handler: &myHandler{}, 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 // Main event loop handling most access to shared data
for { for {
@ -553,8 +612,9 @@ func main() {
go handleSorted(bufConn) go handleSorted(bufConn)
//case msg := <- myRooms["general"]: //case msg := <- myRooms["general"]:
//delete(myRooms["general"], bufConn) //delete(myRooms["general"], bufConn)
//case bufConn := <-newHttpClient: case bufConn := <-newHttpClient:
//server.Serve(bufConn) // this will be Accept()ed immediately by restful
myHttpServer.chans <- bufConn
case msg := <-myMsgs: case msg := <-myMsgs:
t := msg.receivedAt t := msg.receivedAt
tf := "%d-%02d-%02d %02d:%02d:%02d (%s)" tf := "%d-%02d-%02d %02d:%02d:%02d (%s)"

View File

@ -1,4 +1,5 @@
port: 4080 port: 4080
root_path: ./public
mailer: mailer:
service: 'mailgun' service: 'mailgun'
url: 'https://api.mailgun.net/v3/example.com/messages' url: 'https://api.mailgun.net/v3/example.com/messages'

26
public/index.html Normal file
View File

@ -0,0 +1,26 @@
<!DOCTYPE html>
<html>
<head><title>Sample Chat</title></head>
<body>
<pre><code># 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'
</code></pre>
</body>
</html>

3
public/js/app.js Normal file
View File

@ -0,0 +1,3 @@
"use strict"
console.log("Hello, World!");