diff options
author | Wolfy-J <[email protected]> | 2018-05-29 20:31:27 +0300 |
---|---|---|
committer | Wolfy-J <[email protected]> | 2018-05-29 20:31:27 +0300 |
commit | 130da89a94f044fafed584ad5430b59991f317c7 (patch) | |
tree | b6fac7a256b6089eb04ff6bacfbd138ada9382bc | |
parent | e45daa3bbfd5e95889d00ba3cf9ff6c95101bcb2 (diff) |
updates for the server support
-rw-r--r-- | cmd/rr-php/cmd/serve.go | 22 | ||||
-rw-r--r-- | server.go | 6 | ||||
-rw-r--r-- | server/http.go | 159 |
3 files changed, 175 insertions, 12 deletions
diff --git a/cmd/rr-php/cmd/serve.go b/cmd/rr-php/cmd/serve.go index cac830ac..dc15b6df 100644 --- a/cmd/rr-php/cmd/serve.go +++ b/cmd/rr-php/cmd/serve.go @@ -18,9 +18,10 @@ import ( "github.com/spf13/cobra" "github.com/spiral/roadrunner" "os/exec" - "log" "time" "github.com/sirupsen/logrus" + "github.com/spiral/roadrunner/server" + "net/http" ) func init() { @@ -31,20 +32,20 @@ func init() { } func serveHandler(cmd *cobra.Command, args []string) { - r := roadrunner.NewRouter( + rr := roadrunner.NewRouter( func() *exec.Cmd { return exec.Command("php", "/Users/wolfy-j/Projects/phpapp/webroot/index.php", "rr", "pipes") }, roadrunner.NewPipeFactory(), ) - err := r.Configure(roadrunner.Config{ + err := rr.Configure(roadrunner.Config{ NumWorkers: 1, AllocateTimeout: time.Minute, DestroyTimeout: time.Minute, }) - r.Observe(func(event int, ctx interface{}) { + rr.Observe(func(event int, ctx interface{}) { logrus.Info(ctx) }) @@ -52,10 +53,13 @@ func serveHandler(cmd *cobra.Command, args []string) { panic(err) } - for i := 0; i < 10; i++ { - r.Exec(&roadrunner.Payload{}) - } - - log.Print(r.Workers()) + logrus.Info("serving") + http.ListenAndServe(":8080", server.NewHTTP( + server.HTTPConfig{ + ServeStatic: true, + Root: "/Users/wolfy-j/Projects/phpapp/webroot", + }, + rr, + )) } @@ -43,7 +43,7 @@ func NewRouter(cmd func() *exec.Cmd, factory Factory) *Server { } } -// Configure configures underlying pool and destroys it's previous version if any +// Configure configures underlying pool and destroys it's previous version if any. func (r *Server) Configure(cfg Config) error { r.mu.Lock() previous := r.pool @@ -58,8 +58,8 @@ func (r *Server) Configure(cfg Config) error { r.mu.Lock() - r.pool.Observe(r.poolObserver) r.cfg, r.pool = cfg, pool + r.pool.Observe(r.poolObserver) r.mu.Unlock() @@ -135,7 +135,7 @@ func (r *Server) throw(event int, ctx interface{}) { } } -// Observe pool events +// Observe pool events. func (r *Server) poolObserver(event int, ctx interface{}) { // bypassing to user specified observer r.throw(event, ctx) diff --git a/server/http.go b/server/http.go new file mode 100644 index 00000000..3d8855a4 --- /dev/null +++ b/server/http.go @@ -0,0 +1,159 @@ +package server + +import ( + "github.com/spiral/roadrunner" + "net/http" + "strings" + "path" + "github.com/sirupsen/logrus" + "os" + "path/filepath" + "encoding/json" +) + +var ( + excludeFiles = []string{".php", ".htaccess"} +) + +type Request struct { + Protocol string `json:"protocol"` + Uri string `json:"uri"` + Method string `json:"method"` + Headers http.Header `json:"headers"` + Cookies map[string]string `json:"cookies"` + RawQuery string `json:"rawQuery"` +} + +// Configures http rr +type HTTPConfig struct { + // ServeStatic enables static file serving from desired root directory. + ServeStatic bool + + // Root directory, required when ServeStatic set to true. + Root string +} + +// HTTP serves http connections to underlying PHP application using PSR-7 protocol. Context will include request headers, +// parsed files and query, payload will include parsed form data (if any) - todo: do we need to do that?. +type HTTP struct { + cfg HTTPConfig + root http.Dir + rr *roadrunner.Server +} + +func NewHTTP(cfg HTTPConfig, server *roadrunner.Server) *HTTP { + h := &HTTP{cfg: cfg, rr: server} + if cfg.ServeStatic { + h.root = http.Dir(h.cfg.Root) + } + + return h +} + +// ServeHTTP serve using PSR-7 requests passed to underlying application. +func (h *HTTP) ServeHTTP(w http.ResponseWriter, r *http.Request) () { + if h.cfg.ServeStatic && h.serveStatic(w, r) { + // serving static files + return + } + + // WHAT TO PUT TO BODY? + p, err := h.buildPayload(r) + rsp, err := h.rr.Exec(p) + + if err != nil { + w.Write([]byte(err.Error())) + return + } + + // wrapping the response + + w.Header().Add("content-type", "text/html;charset=UTF-8") + w.Write(rsp.Body) +} + +// serveStatic attempts to serve static file and returns true in case of success, will return false in case if file not +// found, not allowed or on read error. +func (h *HTTP) serveStatic(w http.ResponseWriter, r *http.Request) bool { + fpath := r.URL.Path + if !strings.HasPrefix(fpath, "/") { + fpath = "/" + fpath + } + fpath = path.Clean(fpath) + + if isForbidden(fpath) { + logrus.Warningf("attempt to access forbidden file %s", fpath) + return false + } + + f, err := h.root.Open(fpath) + if err != nil { + if !os.IsNotExist(err) { + // rr or access error + logrus.Error(err) + } + + return false + } + defer f.Close() + + d, err := f.Stat() + if err != nil { + // rr error + logrus.Error(err) + return false + } + + if d.IsDir() { + // we are not serving directories + return false + } + + http.ServeContent(w, r, d.Name(), d.ModTime(), f) + return true +} + +// todo: add files support +func (h *HTTP) buildPayload(r *http.Request) (*roadrunner.Payload, error) { + request := Request{ + Protocol: r.Proto, + Uri: r.URL.String(), + Method: r.Method, + Headers: r.Header, + Cookies: make(map[string]string), + RawQuery: r.URL.RawQuery, + } + + r.ParseMultipartForm(1000000000) + + logrus.Print(r.MultipartForm.Value) + logrus.Print(r.MultipartForm.File["kkk"][0].Header) + logrus.Print(r.MultipartForm.File["kkk"][0].Filename) + logrus.Print(r.Form) + + // cookies + for _, c := range r.Cookies() { + request.Cookies[c.Name] = c.Value + } + + data, _ := json.Marshal(request) + + logrus.Info(string(data)) + + return &roadrunner.Payload{ + Context: data, + Body: []byte("lol"), + }, nil +} + +// isForbidden returns true if file has forbidden extension. +func isForbidden(path string) bool { + ext := strings.ToLower(filepath.Ext(path)) + for _, exl := range excludeFiles { + if ext == exl { + return true + } + } + + return false +} |