go go restful static server, mighty restful static server!
This commit is contained in:
parent
9c507ae68e
commit
dd971fcc72
|
@ -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
|
||||||
|
|
|
@ -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)"
|
||||||
|
|
|
@ -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'
|
||||||
|
|
|
@ -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>
|
|
@ -0,0 +1,3 @@
|
||||||
|
"use strict"
|
||||||
|
|
||||||
|
console.log("Hello, World!");
|
Loading…
Reference in New Issue