summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--server/http.go135
-rw-r--r--server/psr7/files.go58
-rw-r--r--server/psr7/post.go45
-rw-r--r--server/psr7/request.go101
-rw-r--r--server/psr7/response.go21
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)
+}