diff options
-rw-r--r-- | server/http.go | 135 | ||||
-rw-r--r-- | server/psr7/files.go | 58 | ||||
-rw-r--r-- | server/psr7/post.go | 45 | ||||
-rw-r--r-- | server/psr7/request.go | 101 | ||||
-rw-r--r-- | server/psr7/response.go | 21 |
5 files changed, 248 insertions, 112 deletions
diff --git a/server/http.go b/server/http.go index 6e9c91e6..63551703 100644 --- a/server/http.go +++ b/server/http.go @@ -2,6 +2,7 @@ package server import ( "github.com/spiral/roadrunner" + "github.com/spiral/roadrunner/server/psr7" "net/http" "strings" "path" @@ -9,22 +10,12 @@ import ( "os" "path/filepath" "encoding/json" - "fmt" ) 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. @@ -42,6 +33,7 @@ type HTTP struct { rr *roadrunner.Server } +// NewHTTP returns new instance of HTTP PSR7 server. func NewHTTP(cfg HTTPConfig, server *roadrunner.Server) *HTTP { h := &HTTP{cfg: cfg, rr: server} if cfg.ServeStatic { @@ -54,22 +46,33 @@ func NewHTTP(cfg HTTPConfig, server *roadrunner.Server) *HTTP { // 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 + // server always attempt to serve static files first return } - // WHAT TO PUT TO BODY? - p, err := h.buildPayload(r) - rsp, err := h.rr.Exec(p) + req, err := psr7.ParseRequest(r) + if err != nil { + w.Write([]byte(err.Error())) //todo: better errors + w.WriteHeader(500) + return + } + defer req.Close() + rsp, err := h.rr.Exec(req.Payload()) if err != nil { - w.Write([]byte(err.Error())) + w.Write([]byte(err.Error())) //todo: better errors + w.WriteHeader(500) return } - // wrapping the response + resp := &psr7.Response{} + if err = json.Unmarshal(rsp.Context, resp); err != nil { + w.Write([]byte(err.Error())) //todo: better errors + w.WriteHeader(500) + return + } - w.Header().Add("content-type", "text/html;charset=UTF-8") + resp.Write(w) w.Write(rsp.Body) } @@ -82,7 +85,7 @@ func (h *HTTP) serveStatic(w http.ResponseWriter, r *http.Request) bool { } fpath = path.Clean(fpath) - if isForbidden(fpath) { + if h.excluded(fpath) { logrus.Warningf("attempt to access forbidden file %s", fpath) return false } @@ -114,39 +117,8 @@ func (h *HTTP) serveStatic(w http.ResponseWriter, r *http.Request) bool { return true } -// todo: add files support -func (h *HTTP) buildPayload(r *http.Request) (*roadrunner.Payload, error) { - request := Request{ - Protocol: r.Proto, - Uri: fmt.Sprintf("%s%s", r.Host, r.URL.String()), - Method: r.Method, - Headers: r.Header, - Cookies: make(map[string]string), - RawQuery: r.URL.RawQuery, - } - - logrus.Print(parseData(r)) - - //logrus.Print(r.MultipartForm.File["kkk"][0].Header) - //logrus.Print(r.MultipartForm.File["kkk"][0].Filename) - - // 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 { +// excluded returns true if file has forbidden extension. +func (h *HTTP) excluded(path string) bool { ext := strings.ToLower(filepath.Ext(path)) for _, exl := range excludeFiles { if ext == exl { @@ -156,64 +128,3 @@ func isForbidden(path string) bool { return false } - -type postData map[string]interface{} - -func (d postData) push(k string, v []string) { - if len(v) == 0 { - // doing nothing - return - } - - chunks := make([]string, 0) - for _, chunk := range strings.Split(k, "[") { - chunks = append(chunks, strings.Trim(chunk, "]")) - } - - d.pushChunk(chunks, v) -} - -func (d postData) pushChunk(k []string, v []string) { - if len(v) == 0 { - return - } - - head := k[0] - tail := k[1:] - if len(k) == 1 { - d[head] = v[0] - return - } - - // unnamed array - if len(tail) == 1 && tail[0] == "" { - d[head] = v - return - } - - if p, ok := d[head]; !ok { - d[head] = make(postData) - d[head].(postData).pushChunk(tail, v) - } else { - p.(postData).pushChunk(tail, v) - } -} - -// parse incoming data request into JSON (including multipart form data) -func parseData(r *http.Request) (*postData, error) { - if r.Method != "POST" && r.Method != "PUT" && r.Method != "PATCH" { - return nil, nil - } - - r.ParseMultipartForm(32 << 20) - - data := make(postData) - for k, v := range r.MultipartForm.Value { - data.push(k, v) - } - - jd, _ := json.Marshal(data) - logrus.Warning(string(jd)) - - return nil, nil -} diff --git a/server/psr7/files.go b/server/psr7/files.go new file mode 100644 index 00000000..31ddfec8 --- /dev/null +++ b/server/psr7/files.go @@ -0,0 +1,58 @@ +package psr7 + +import ( + "mime/multipart" + "strings" + "github.com/sirupsen/logrus" +) + +type fileData map[string]interface{} + +type FileUpload struct { + Name string `json:"name"` + MimeType string `json:"mimetype"` +} + +func (d fileData) push(k string, v []*multipart.FileHeader) { + if len(v) == 0 { + // doing nothing + return + } + + chunks := make([]string, 0) + for _, chunk := range strings.Split(k, "[") { + chunks = append(chunks, strings.Trim(chunk, "]")) + } + + d.pushChunk(chunks, v) +} + +func (d fileData) pushChunk(k []string, v []*multipart.FileHeader) { + logrus.Print(v) + if len(v) == 0 || v[0] == nil { + return + } + + head := k[0] + tail := k[1:] + if len(k) == 1 { + d[head] = FileUpload{ + Name: v[0].Filename, + MimeType: v[0].Header.Get("Content-Type"), + } + return + } + + // unnamed array + if len(tail) == 1 && tail[0] == "" { + d[head] = v + return + } + + if p, ok := d[head]; !ok { + d[head] = make(fileData) + d[head].(fileData).pushChunk(tail, v) + } else { + p.(fileData).pushChunk(tail, v) + } +} diff --git a/server/psr7/post.go b/server/psr7/post.go new file mode 100644 index 00000000..30af7e3a --- /dev/null +++ b/server/psr7/post.go @@ -0,0 +1,45 @@ +package psr7 + +import "strings" + +type postData map[string]interface{} + +func (d postData) push(k string, v []string) { + if len(v) == 0 { + // doing nothing + return + } + + chunks := make([]string, 0) + for _, chunk := range strings.Split(k, "[") { + chunks = append(chunks, strings.Trim(chunk, "]")) + } + + d.pushChunk(chunks, v) +} + +func (d postData) pushChunk(k []string, v []string) { + if len(v) == 0 || v[0] == "" { + return + } + + head := k[0] + tail := k[1:] + if len(k) == 1 { + d[head] = v[0] + return + } + + // unnamed array + if len(tail) == 1 && tail[0] == "" { + d[head] = v + return + } + + if p, ok := d[head]; !ok { + d[head] = make(postData) + d[head].(postData).pushChunk(tail, v) + } else { + p.(postData).pushChunk(tail, v) + } +} diff --git a/server/psr7/request.go b/server/psr7/request.go new file mode 100644 index 00000000..f8dedd8f --- /dev/null +++ b/server/psr7/request.go @@ -0,0 +1,101 @@ +package psr7 + +import ( + "net/http" + "fmt" + "encoding/json" + "github.com/spiral/roadrunner" + "github.com/sirupsen/logrus" +) + +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"` + Uploads fileData `json:"fileUploads"` + + // buffers + postData postData +} + +func ParseRequest(r *http.Request) (req *Request, err error) { + req = &Request{ + Protocol: r.Proto, + Uri: fmt.Sprintf("%s%s", r.Host, r.URL.String()), + Method: r.Method, + Headers: r.Header, + Cookies: make(map[string]string), + RawQuery: r.URL.RawQuery, + } + + for _, c := range r.Cookies() { + req.Cookies[c.Name] = c.Value + } + + if req.HasBody() { + r.ParseMultipartForm(32 << 20) + + if req.postData, err = parseData(r); err != nil { + return nil, err + } + + if req.Uploads, err = parseFiles(r); err != nil { + return nil, err + } + + if req.Uploads != nil { + logrus.Debug("opening files") + } + } + + return req, nil +} + +func (r *Request) Payload() *roadrunner.Payload { + ctx, err := json.Marshal(r) + if err != nil { + panic(err) //todo: change it + } + + // todo: non parseble payloads + body, err := json.Marshal(r.postData) + if err != nil { + panic(err) //todo: change it + } + + return &roadrunner.Payload{Context: ctx, Body: body} +} + +func (r *Request) Close() { + if r.Uploads != nil { + + } +} + +// HasBody returns true if request might include POST data or file uploads. +func (r *Request) HasBody() bool { + return r.Method == "POST" || r.Method == "PUT" || r.Method == "PATCH" +} + +// parse incoming data request into JSON (including multipart form data) +func parseData(r *http.Request) (postData, error) { + data := make(postData) + for k, v := range r.MultipartForm.Value { + data.push(k, v) + } + + return data, nil +} + +// parse incoming data request into JSON (including multipart form data) +func parseFiles(r *http.Request) (fileData, error) { + data := make(fileData) + for k, v := range r.MultipartForm.File { + data.push(k, v) + } + + return data, nil +} diff --git a/server/psr7/response.go b/server/psr7/response.go new file mode 100644 index 00000000..c0d739a7 --- /dev/null +++ b/server/psr7/response.go @@ -0,0 +1,21 @@ +package psr7 + +import ( + "net/http" +) + +type Response struct { + Status int `json:"status"` + Headers map[string][]string `json:"headers"` +} + +func (r *Response) Write(w http.ResponseWriter) { + for k, v := range r.Headers { + for _, h := range v { + w.Header().Add(k, h) + + } + } + + w.WriteHeader(r.Status) +} |