breakout server/main.go
parent
c47d2d7456
commit
68f8e931e7
|
|
@ -0,0 +1,22 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"os"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Port int
|
||||
Root string
|
||||
}
|
||||
|
||||
func NewConfig() (Config, error) {
|
||||
var c Config
|
||||
fs := flag.NewFlagSet(os.Args[0], flag.ContinueOnError)
|
||||
|
||||
fs.IntVar(&c.Port, "p", 8080, "port")
|
||||
fs.StringVar(&c.Root, "r", "/tmp/", "static file root")
|
||||
|
||||
err := fs.Parse(os.Args[1:])
|
||||
return c, err
|
||||
}
|
||||
|
|
@ -2,18 +2,13 @@ package main
|
|||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/coder/websocket"
|
||||
"golang.org/x/time/rate"
|
||||
)
|
||||
|
||||
|
|
@ -27,19 +22,19 @@ func main() {
|
|||
}
|
||||
|
||||
func run(ctx context.Context) error {
|
||||
fs := flag.NewFlagSet(os.Args[0], flag.ContinueOnError)
|
||||
port := fs.Int("p", 8080, "port")
|
||||
if err := fs.Parse(os.Args[1:]); err != nil {
|
||||
config, err := NewConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
S := &S{
|
||||
ctx: ctx,
|
||||
limiter: rate.NewLimiter(10, 10),
|
||||
config: config,
|
||||
}
|
||||
|
||||
s := &http.Server{
|
||||
Addr: fmt.Sprintf(":%d", *port),
|
||||
Addr: fmt.Sprintf(":%d", config.Port),
|
||||
Handler: S,
|
||||
}
|
||||
|
||||
|
|
@ -50,96 +45,10 @@ func run(ctx context.Context) error {
|
|||
s.Shutdown(ctx)
|
||||
}()
|
||||
|
||||
log.Println("listening on", *port)
|
||||
log.Println("listening on", config.Port)
|
||||
if err := s.ListenAndServe(); err != nil && ctx.Err() == nil {
|
||||
return err
|
||||
}
|
||||
log.Println("shut down")
|
||||
return nil
|
||||
}
|
||||
|
||||
type S struct {
|
||||
ctx context.Context
|
||||
limiter *rate.Limiter
|
||||
}
|
||||
|
||||
func (s *S) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
if err := s.serveHTTP(w, r); err != nil {
|
||||
log.Println(r.URL.Path, "//", err.Error(), r.Header)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *S) serveHTTP(w http.ResponseWriter, r *http.Request) error {
|
||||
if isV1(r) || isWS(r) {
|
||||
return s.serveAPI(w, r)
|
||||
}
|
||||
return s.serveStatic(w, r)
|
||||
}
|
||||
|
||||
func isV1(r *http.Request) bool {
|
||||
return strings.HasPrefix(r.URL.Path, "/v1/")
|
||||
}
|
||||
|
||||
func isWS(r *http.Request) bool {
|
||||
return r.URL.Path == "/ws" || strings.HasPrefix(r.URL.Path, "/ws/")
|
||||
}
|
||||
|
||||
func (s *S) serveStatic(w http.ResponseWriter, r *http.Request) error {
|
||||
return fmt.Errorf("not impl static")
|
||||
}
|
||||
|
||||
func (s *S) serveAPI(w http.ResponseWriter, r *http.Request) error {
|
||||
if err := s.injectContext(w, r); err == io.EOF {
|
||||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||||
return nil
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if isWS(r) {
|
||||
return s.serveWS(w, r)
|
||||
} else if isV1(r) {
|
||||
return s.serveV1(w, r)
|
||||
}
|
||||
|
||||
http.NotFound(w, r)
|
||||
return nil
|
||||
}
|
||||
|
||||
type Session struct {
|
||||
ID string
|
||||
}
|
||||
|
||||
func (s *S) injectContext(w http.ResponseWriter, r *http.Request) error {
|
||||
id, err := r.Cookie("uuid")
|
||||
if err != nil || id.Value == "" {
|
||||
return io.EOF
|
||||
}
|
||||
ctx := r.Context()
|
||||
ctx = context.WithValue(ctx, "session", Session{
|
||||
ID: id.Value,
|
||||
})
|
||||
*r = *r.WithContext(ctx)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *S) serveWS(httpw http.ResponseWriter, httpr *http.Request) error {
|
||||
ctx := httpr.Context()
|
||||
|
||||
c, err := websocket.Accept(httpw, httpr, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer c.CloseNow()
|
||||
|
||||
if err := c.Write(ctx, 1, []byte("hello world")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return fmt.Errorf("not impl")
|
||||
}
|
||||
|
||||
func (s *S) serveV1(w http.ResponseWriter, r *http.Request) error {
|
||||
return fmt.Errorf("not impl: v1")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,105 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/coder/websocket"
|
||||
"golang.org/x/time/rate"
|
||||
)
|
||||
|
||||
type S struct {
|
||||
ctx context.Context
|
||||
limiter *rate.Limiter
|
||||
config Config
|
||||
}
|
||||
|
||||
func (s *S) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
if err := s.serveHTTP(w, r); err != nil {
|
||||
log.Println(s.Session(r.Context()), "//", r.URL.Path, "//", err.Error())
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *S) serveHTTP(w http.ResponseWriter, r *http.Request) error {
|
||||
if isV1(r) || isWS(r) {
|
||||
return s.serveAPI(w, r)
|
||||
}
|
||||
return s.serveStatic(w, r)
|
||||
}
|
||||
|
||||
func isV1(r *http.Request) bool {
|
||||
return strings.HasPrefix(r.URL.Path, "/v1/")
|
||||
}
|
||||
|
||||
func isWS(r *http.Request) bool {
|
||||
return r.URL.Path == "/ws" || strings.HasPrefix(r.URL.Path, "/ws/")
|
||||
}
|
||||
|
||||
func (s *S) serveStatic(w http.ResponseWriter, r *http.Request) error {
|
||||
return fmt.Errorf("not impl static")
|
||||
}
|
||||
|
||||
func (s *S) serveAPI(w http.ResponseWriter, r *http.Request) error {
|
||||
if err := s.injectContext(w, r); err == io.EOF {
|
||||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||||
return nil
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if isWS(r) {
|
||||
return s.serveWS(w, r)
|
||||
} else if isV1(r) {
|
||||
return s.serveV1(w, r)
|
||||
}
|
||||
|
||||
http.NotFound(w, r)
|
||||
return nil
|
||||
}
|
||||
|
||||
type Session struct {
|
||||
ID string
|
||||
}
|
||||
|
||||
func (s *S) injectContext(w http.ResponseWriter, r *http.Request) error {
|
||||
id, err := r.Cookie("uuid")
|
||||
if err != nil || id.Value == "" {
|
||||
return io.EOF
|
||||
}
|
||||
ctx := r.Context()
|
||||
ctx = context.WithValue(ctx, "session", Session{
|
||||
ID: id.Value,
|
||||
})
|
||||
*r = *r.WithContext(ctx)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *S) Session(ctx context.Context) Session {
|
||||
v, _ := ctx.Value("session").(Session)
|
||||
return v
|
||||
}
|
||||
|
||||
func (s *S) serveWS(httpw http.ResponseWriter, httpr *http.Request) error {
|
||||
ctx := httpr.Context()
|
||||
|
||||
c, err := websocket.Accept(httpw, httpr, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer c.CloseNow()
|
||||
|
||||
if err := c.Write(ctx, 1, []byte("hello world")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return fmt.Errorf("not impl")
|
||||
}
|
||||
|
||||
func (s *S) serveV1(w http.ResponseWriter, r *http.Request) error {
|
||||
return fmt.Errorf("not impl: v1")
|
||||
}
|
||||
Loading…
Reference in New Issue