diff options
author | Wolfy-J <[email protected]> | 2018-06-06 16:01:06 +0300 |
---|---|---|
committer | Wolfy-J <[email protected]> | 2018-06-06 16:01:06 +0300 |
commit | c0abdf657a0a5de02b63683a60bf13b88895ef79 (patch) | |
tree | b497e98a7c2cf73f7a8377de7566a3f7b4b72a5e /http | |
parent | e863c6cdcf7c318fb251e096bf92812ed98ea03c (diff) |
better server re-configuration
Diffstat (limited to 'http')
-rw-r--r-- | http/request.go | 137 | ||||
-rw-r--r-- | http/uploads.go | 206 |
2 files changed, 0 insertions, 343 deletions
diff --git a/http/request.go b/http/request.go deleted file mode 100644 index fd483744..00000000 --- a/http/request.go +++ /dev/null @@ -1,137 +0,0 @@ -package http - -import ( - "encoding/json" - "fmt" - "github.com/spiral/roadrunner" - "io/ioutil" - "net/http" - "strings" -) - -const ( - defaultMaxMemory = 32 << 20 // 32 MB -) - -// Request maps net/http requests to PSR7 compatible structure and managed state of temporary uploaded files. -type Request struct { - // Protocol includes HTTP protocol version. - Protocol string `json:"protocol"` - - // Method contains name of HTTP method used for the request. - Method string `json:"method"` - - // Uri contains full request Uri with scheme and query. - Uri string `json:"uri"` - - // Headers contains list of request headers. - Headers http.Header `json:"headers"` - - // Cookies contains list of request cookies. - Cookies map[string]string `json:"cookies"` - - // RawQuery contains non parsed query string (to be parsed on php end). - RawQuery string `json:"rawQuery"` - - // Parsed indicates that request body has been parsed on RR end. - Parsed bool `json:"parsed"` - - // Uploads contains list of uploaded files, their names, sized and associations with temporary files. - Uploads *Uploads `json:"uploads"` - - // request body can be parsedData or []byte - body interface{} -} - -// NewRequest creates new PSR7 compatible request using net/http request. -func NewRequest(r *http.Request) (req *Request, err error) { - req = &Request{ - Protocol: r.Proto, - Method: r.Method, - Uri: uri(r), - 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.parsable() { - req.body, err = ioutil.ReadAll(r.Body) - return req, err - } - - if err = r.ParseMultipartForm(defaultMaxMemory); err != nil { - return nil, err - } - - if req.body, err = parsePost(r); err != nil { - return nil, err - } - - if req.Uploads, err = parseUploads(r); err != nil { - return nil, err - } - - req.Parsed = true - return req, nil -} - -// Open moves all uploaded files to temporary directory so it can be given to php later. -func (r *Request) Open(cfg *Config) error { - if r.Uploads == nil { - return nil - } - - return r.Uploads.Open(cfg) -} - -// Close clears all temp file uploads -func (r *Request) Close() { - if r.Uploads == nil { - return - } - - r.Uploads.Clear() -} - -// Payload request marshaled RoadRunner payload based on PSR7 data. Default encode method is JSON. Make sure to open -// files prior to calling this method. -func (r *Request) Payload() (p *roadrunner.Payload, err error) { - p = &roadrunner.Payload{} - - if p.Context, err = json.Marshal(r); err != nil { - return nil, err - } - - if r.Parsed { - if p.Body, err = json.Marshal(r.body); err != nil { - return nil, err - } - } else if r.body != nil { - p.Body = r.body.([]byte) - } - - return p, nil -} - -// parsable returns true if request payload can be parsed (POST dataTree, file tree). -func (r *Request) parsable() bool { - if r.Method != "POST" && r.Method != "PUT" && r.Method != "PATCH" { - return false - } - - ct := r.Headers.Get("content-type") - return strings.Contains(ct, "multipart/form-data") || ct == "application/x-www-form-urlencoded" -} - -// uri fetches full uri from request in a form of string (including https scheme if TLS connection is enabled). -func uri(r *http.Request) string { - if r.TLS != nil { - return fmt.Sprintf("https://%s%s", r.Host, r.URL.String()) - } - - return fmt.Sprintf("http://%s%s", r.Host, r.URL.String()) -} diff --git a/http/uploads.go b/http/uploads.go deleted file mode 100644 index c3b18169..00000000 --- a/http/uploads.go +++ /dev/null @@ -1,206 +0,0 @@ -package http - -import ( - "encoding/json" - "io" - "io/ioutil" - "mime/multipart" - "net/http" - "os" - "strings" - "sync" -) - -const ( - // There is no error, the file uploaded with success. - UploadErrorOK = 0 - - // No file was uploaded. - UploadErrorNoFile = 4 - - // Missing a temporary folder. - UploadErrorNoTmpDir = 5 - - // Failed to write file to disk. - UploadErrorCantWrite = 6 - - // ForbidUploads file extension. - UploadErrorExtension = 7 -) - -// FileUpload represents singular file wrapUpload. -type FileUpload struct { - // Name contains filename specified by the client. - Name string `json:"name"` - - // MimeType contains mime-type provided by the client. - MimeType string `json:"type"` - - // Size of the uploaded file. - Size int64 `json:"size"` - - // Error indicates file upload error (if any). See http://php.net/manual/en/features.file-upload.errors.php - Error int `json:"error"` - - // TempFilename points to temporary file location. - TempFilename string `json:"tmpName"` - - // associated file header - header *multipart.FileHeader -} - -func (f *FileUpload) Open(cfg *Config) error { - if cfg.Forbidden(f.Name) { - f.Error = UploadErrorExtension - return nil - } - - file, err := f.header.Open() - if err != nil { - f.Error = UploadErrorNoFile - return err - } - defer file.Close() - - tmp, err := ioutil.TempFile(cfg.TmpDir, "upload") - if err != nil { - // most likely cause of this issue is missing tmp dir - f.Error = UploadErrorNoTmpDir - return err - } - - f.TempFilename = tmp.Name() - defer tmp.Close() - - if f.Size, err = io.Copy(tmp, file); err != nil { - f.Error = UploadErrorCantWrite - } - - return err -} - -type fileTree map[string]interface{} - -func (d fileTree) push(k string, v []*FileUpload) { - if len(v) == 0 { - // skip empty values - return - } - - indexes := make([]string, 0) - for _, index := range strings.Split(k, "[") { - indexes = append(indexes, strings.Trim(index, "]")) - } - - if len(indexes) <= maxLevel { - d.mount(indexes, v) - } -} - -// mount mounts data tree recursively. -func (d fileTree) mount(i []string, v []*FileUpload) { - if len(v) == 0 { - return - } - - if len(i) == 1 { - // single value context - d[i[0]] = v[0] - return - } - - if len(i) == 2 && i[1] == "" { - // non associated array of elements - d[i[0]] = v - return - } - - if p, ok := d[i[0]]; ok { - p.(fileTree).mount(i[1:], v) - } - - d[i[0]] = make(fileTree) - d[i[0]].(fileTree).mount(i[1:], v) -} - -// tree manages uploaded files tree and temporary files. -type Uploads struct { - // pre processed data tree for Uploads. - tree fileTree - - // flat list of all file Uploads. - list []*FileUpload -} - -// MarshalJSON marshal tree tree into JSON. -func (u *Uploads) MarshalJSON() ([]byte, error) { - return json.Marshal(u.tree) -} - -// Open moves all uploaded files to temp directory, return error in case of issue with temp directory. File errors -// will be handled individually. @todo: do we need it? -func (u *Uploads) Open(cfg *Config) error { - var wg sync.WaitGroup - for _, f := range u.list { - wg.Add(1) - go func(f *FileUpload) { - defer wg.Done() - f.Open(cfg) - }(f) - } - - wg.Wait() - return nil -} - -// Clear deletes all temporary files. -func (u *Uploads) Clear() { - for _, f := range u.list { - if f.TempFilename != "" && exists(f.TempFilename) { - os.Remove(f.TempFilename) - } - } -} - -// parse incoming dataTree request into JSON (including multipart form dataTree) -func parseUploads(r *http.Request) (*Uploads, error) { - u := &Uploads{ - tree: make(fileTree), - list: make([]*FileUpload, 0), - } - - for k, v := range r.MultipartForm.File { - files := make([]*FileUpload, 0, len(v)) - for _, f := range v { - files = append(files, wrapUpload(f)) - } - - u.list = append(u.list, files...) - u.tree.push(k, files) - } - - return u, nil -} - -func wrapUpload(f *multipart.FileHeader) *FileUpload { - return &FileUpload{ - Name: f.Filename, - MimeType: f.Header.Get("Content-Type"), - Error: UploadErrorOK, - header: f, - } -} - -// exists if file exists. -func exists(path string) bool { - _, err := os.Stat(path) - if err == nil { - return true - } - - if os.IsNotExist(err) { - return false - } - - return false -} |