diff options
Diffstat (limited to 'http')
-rw-r--r-- | http/fs_config.go | 29 | ||||
-rw-r--r-- | http/fs_config_test.go | 15 | ||||
-rw-r--r-- | http/parse.go | 147 | ||||
-rw-r--r-- | http/response.go | 54 | ||||
-rw-r--r-- | http/uploads.go | 130 |
5 files changed, 0 insertions, 375 deletions
diff --git a/http/fs_config.go b/http/fs_config.go deleted file mode 100644 index de5b1389..00000000 --- a/http/fs_config.go +++ /dev/null @@ -1,29 +0,0 @@ -package http - -import ( - "strings" - "path" -) - -// FsConfig describes file location and controls access to them. -type FsConfig struct { - // Dir contains name of directory to control access to. - Dir string - - // Forbid specifies list of file extensions which are forbidden for access. - // Example: .php, .exe, .bat, .htaccess and etc. - Forbid []string -} - -// Forbid must return true if file extension is not allowed for the upload. -func (cfg FsConfig) Forbids(filename string) bool { - ext := strings.ToLower(path.Ext(filename)) - - for _, v := range cfg.Forbid { - if ext == v { - return true - } - } - - return false -} diff --git a/http/fs_config_test.go b/http/fs_config_test.go deleted file mode 100644 index 05f568e5..00000000 --- a/http/fs_config_test.go +++ /dev/null @@ -1,15 +0,0 @@ -package http - -import ( - "testing" - "github.com/stretchr/testify/assert" -) - -func TestFsConfig_Forbids(t *testing.T) { - cfg := FsConfig{Forbid: []string{".php"}} - - assert.True(t, cfg.Forbids("index.php")) - assert.True(t, cfg.Forbids("index.PHP")) - assert.True(t, cfg.Forbids("phpadmin/index.bak.php")) - assert.False(t, cfg.Forbids("index.html")) -} diff --git a/http/parse.go b/http/parse.go deleted file mode 100644 index 898f39a1..00000000 --- a/http/parse.go +++ /dev/null @@ -1,147 +0,0 @@ -package http - -import ( - "strings" - "net/http" - "os" -) - -const maxLevel = 127 - -type dataTree map[string]interface{} -type fileTree map[string]interface{} - -// parseData parses incoming request body into data tree. -func parseData(r *http.Request) (dataTree, error) { - data := make(dataTree) - for k, v := range r.PostForm { - data.push(k, v) - } - - for k, v := range r.MultipartForm.Value { - data.push(k, v) - } - - return data, nil -} - -// pushes value into data tree. -func (d dataTree) push(k string, v []string) { - 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 dataTree) mount(i []string, v []string) { - 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.(dataTree).mount(i[1:], v) - } - - d[i[0]] = make(dataTree) - d[i[0]].(dataTree).mount(i[1:], v) -} - -// parse incoming dataTree request into JSON (including multipart form dataTree) -func parseUploads(r *http.Request, cfg *FsConfig) (*Uploads, error) { - u := &Uploads{ - cfg: cfg, - 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, NewUpload(f)) - } - - u.list = append(u.list, files...) - u.tree.push(k, files) - } - - return u, nil -} - -// 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 -} - -// pushes new file upload into it's proper place. -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) -} diff --git a/http/response.go b/http/response.go deleted file mode 100644 index dd092353..00000000 --- a/http/response.go +++ /dev/null @@ -1,54 +0,0 @@ -package http - -import ( - "encoding/json" - "github.com/spiral/roadrunner" - "net/http" - "io" -) - -// Response handles PSR7 response logic. -type Response struct { - // Status contains response status. - Status int `json:"status"` - - // Headers contains list of response headers. - Headers map[string][]string `json:"headers"` - - // associated body payload. - body interface{} -} - -// NewResponse creates new response based on given roadrunner payload. -func NewResponse(p *roadrunner.Payload) (*Response, error) { - r := &Response{body: p.Body} - if err := json.Unmarshal(p.Context, r); err != nil { - return nil, err - } - - return r, nil -} - -// Write writes response headers, status and body into ResponseWriter. -func (r *Response) Write(w http.ResponseWriter) error { - for k, v := range r.Headers { - for _, h := range v { - w.Header().Add(k, h) - - } - } - - w.WriteHeader(r.Status) - - if data, ok := r.body.([]byte); ok { - w.Write(data) - } - - if rc, ok := r.body.(io.Reader); ok { - if _, err := io.Copy(w, rc); err != nil { - return err - } - } - - return nil -} diff --git a/http/uploads.go b/http/uploads.go deleted file mode 100644 index cdd3e52c..00000000 --- a/http/uploads.go +++ /dev/null @@ -1,130 +0,0 @@ -package http - -import ( - "encoding/json" - "os" - "sync" - "mime/multipart" - "io/ioutil" - "io" -) - -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 - - // Forbid file extension. - UploadErrorExtension = 7 -) - -// tree manages uploaded files tree and temporary files. -type Uploads struct { - // associated temp directory and forbidden extensions. - cfg *FsConfig - - // 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. -func (u *Uploads) Open() error { - var wg sync.WaitGroup - for _, f := range u.list { - wg.Add(1) - go func(f *FileUpload) { - defer wg.Done() - f.Open(u.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) - } - } -} - -// FileUpload represents singular file NewUpload. -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 -} - -// NewUpload wraps net/http upload into PRS-7 compatible structure. -func NewUpload(f *multipart.FileHeader) *FileUpload { - return &FileUpload{ - Name: f.Filename, - MimeType: f.Header.Get("Content-Type"), - Error: UploadErrorOK, - header: f, - } -} - -func (f *FileUpload) Open(cfg *FsConfig) error { - if cfg.Forbids(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.Dir, "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 -} |