mhttp: implement MListenAndServe, using new mctx/mrun/mcfg framework

This commit is contained in:
Brian Picciano 2019-01-24 19:23:04 -05:00
parent a0a531cdd2
commit ef2e76de45
2 changed files with 71 additions and 20 deletions

View File

@ -10,35 +10,47 @@ import (
"net/url" "net/url"
"strings" "strings"
"github.com/mediocregopher/mediocre-go-lib/m" "github.com/mediocregopher/mediocre-go-lib/mctx"
"github.com/mediocregopher/mediocre-go-lib/mcfg" "github.com/mediocregopher/mediocre-go-lib/merr"
"github.com/mediocregopher/mediocre-go-lib/mlog" "github.com/mediocregopher/mediocre-go-lib/mlog"
"github.com/mediocregopher/mediocre-go-lib/mnet" "github.com/mediocregopher/mediocre-go-lib/mnet"
"github.com/mediocregopher/mediocre-go-lib/mrun"
) )
// CfgServer initializes and returns an *http.Server which will initialize on // MListenAndServe returns an http.Server which will be initialized and have
// the Start hook. This also sets up config params for configuring the address // ListenAndServe called on it (asynchronously) when the start event is
// being listened on. // triggered on ctx (see mrun.Start). The Server will have Shutdown called on it
func CfgServer(cfg *mcfg.Cfg, h http.Handler) *http.Server { // when the stop event is triggered on ctx (see mrun.Stop).
cfg = cfg.Child("http") //
addr := cfg.ParamString("addr", ":0", "Address to listen on. Default is any open port") // This function automatically handles setting up configuration parameters via
// mcfg. The default listen address is ":0".
func MListenAndServe(ctx mctx.Context, h http.Handler) *http.Server {
ctx = mctx.ChildOf(ctx, "http")
listener := mnet.MListen(ctx, "tcp", "")
listener.NoCloseOnStop = true // http.Server.Shutdown will do this
logger := mlog.From(ctx).WithKV(listener)
srv := http.Server{Handler: h} srv := http.Server{Handler: h}
cfg.Start.Then(func(ctx context.Context) error { mrun.OnStart(ctx, func(mctx.Context) error {
srv.Addr = *addr srv.Addr = listener.Addr().String()
kv := mlog.KV{"addr": *addr} mrun.Thread(ctx, func() error {
log := m.Log(cfg, kv) if err := srv.Serve(listener); err != http.ErrServerClosed {
log.Info("listening") logger.Error("error serving listener", merr.KV(err))
go func() { return err
err := srv.ListenAndServe() }
// TODO the listening log should happen here, somehow, now that we return nil
// know the actual address being listened on })
log.Fatal("http server fataled", mlog.ErrKV(err))
}()
return nil return nil
}) })
// TODO shutdown logic mrun.OnStop(ctx, func(innerCtx mctx.Context) error {
logger.Info("shutting down server")
if err := srv.Shutdown(context.Context(innerCtx)); err != nil {
return err
}
return mrun.Wait(ctx, innerCtx.Done())
})
return &srv return &srv
} }

View File

@ -1,12 +1,51 @@
package mhttp package mhttp
import ( import (
"bytes"
"io"
"io/ioutil"
"net/http"
"net/http/httptest" "net/http/httptest"
. "testing" . "testing"
"github.com/mediocregopher/mediocre-go-lib/mctx"
"github.com/mediocregopher/mediocre-go-lib/mlog"
"github.com/mediocregopher/mediocre-go-lib/mrun"
"github.com/mediocregopher/mediocre-go-lib/mtest/massert" "github.com/mediocregopher/mediocre-go-lib/mtest/massert"
) )
func TestMListenAndServe(t *T) {
ctx := mctx.ChildOf(mctx.New(), "test")
mlog.CtxSet(ctx, mlog.From(ctx).WithMaxLevel(mlog.DebugLevel))
h := http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
io.Copy(rw, r.Body)
})
srv := MListenAndServe(ctx, h)
if err := mrun.Start(ctx); err != nil {
t.Fatal(err)
}
body := bytes.NewBufferString("HELLO")
resp, err := http.Post("http://"+srv.Addr, "text/plain", body)
if err != nil {
t.Fatal(err)
}
defer resp.Body.Close()
respBody, err := ioutil.ReadAll(resp.Body)
if err != nil {
t.Fatal(err)
} else if string(respBody) != "HELLO" {
t.Fatalf("unexpected respBody: %q", respBody)
}
if err := mrun.Stop(ctx); err != nil {
t.Fatal(err)
}
}
func TestAddXForwardedFor(t *T) { func TestAddXForwardedFor(t *T) {
assertXFF := func(prev []string, ipStr, expected string) massert.Assertion { assertXFF := func(prev []string, ipStr, expected string) massert.Assertion {
r := httptest.NewRequest("GET", "/", nil) r := httptest.NewRequest("GET", "/", nil)