github.com/bazelbuild/rules_webtesting@v0.2.0/go/wsl/upload/upload.go (about) 1 // Copyright 2018 Google Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Package files handles file uploads and downloads. 16 package upload 17 18 import ( 19 "archive/zip" 20 "bytes" 21 "encoding/base64" 22 "encoding/json" 23 "io" 24 "io/ioutil" 25 "net/http" 26 "os" 27 "path/filepath" 28 "strings" 29 30 "github.com/bazelbuild/rules_webtesting/go/httphelper" 31 ) 32 33 type Uploader struct { 34 Root string 35 } 36 37 func (u *Uploader) ServeHTTP(w http.ResponseWriter, r *http.Request) { 38 if r.Method == http.MethodGet || r.Method == http.MethodDelete { 39 errorResponse(w, http.StatusMethodNotAllowed, 9, "unknown method", "file deletion and fetching is not allowed") 40 return 41 } 42 43 reqJSON := struct { 44 File *string `json:"file"` 45 }{} 46 47 if err := json.NewDecoder(r.Body).Decode(&reqJSON); err != nil { 48 errorResponse(w, http.StatusBadRequest, 13, "invalid argument", err.Error()) 49 return 50 } 51 52 if reqJSON.File == nil { 53 errorResponse(w, http.StatusBadRequest, 13, "invalid argument", "file field is required in request") 54 return 55 } 56 57 buffer := &bytes.Buffer{} 58 59 if _, err := io.Copy(buffer, base64.NewDecoder(base64.StdEncoding, strings.NewReader(*reqJSON.File))); err != nil { 60 errorResponse(w, http.StatusBadRequest, 13, "invalid argument", err.Error()) 61 return 62 } 63 64 dir, err := ioutil.TempDir(u.Root, "") 65 if err != nil { 66 errorResponse(w, http.StatusInternalServerError, 13, "unknown error", err.Error()) 67 return 68 } 69 70 files, err := unzipFiles(dir, buffer.Bytes()) 71 if err != nil { 72 f, err := uploadFile(dir, buffer.Bytes()) 73 if err != nil { 74 errorResponse(w, http.StatusInternalServerError, 13, "unknown error", err.Error()) 75 return 76 } 77 files = append(files, f) 78 } 79 80 respJSON := map[string]interface{}{ 81 "status": 0, 82 } 83 84 if len(files) == 1 { 85 respJSON["value"] = files[0] 86 } else { 87 respJSON["value"] = files 88 } 89 90 w.Header().Set("Content-Type", "application/json; charset=utf-8") 91 httphelper.SetDefaultResponseHeaders(w.Header()) 92 w.WriteHeader(http.StatusOK) 93 json.NewEncoder(w).Encode(respJSON) 94 } 95 96 func unzipFiles(dir string, contents []byte) ([]string, error) { 97 unzipped, err := zip.NewReader(bytes.NewReader(contents), int64(len(contents))) 98 if err != nil { 99 return nil, err 100 } 101 102 var files []string 103 104 for _, file := range unzipped.File { 105 path, err := unzipFile(dir, file) 106 if err != nil { 107 return nil, err 108 } 109 files = append(files, path) 110 } 111 112 return files, nil 113 } 114 115 func unzipFile(dir string, file *zip.File) (string, error) { 116 path := filepath.Join(dir, file.FileHeader.Name) 117 in, err := file.Open() 118 if err != nil { 119 return "", err 120 } 121 defer in.Close() 122 123 out, err := os.Create(path) 124 if err != nil { 125 return "", err 126 } 127 defer out.Close() 128 129 if _, err := io.Copy(out, in); err != nil { 130 return "", err 131 } 132 133 return path, nil 134 } 135 136 func uploadFile(dir string, contents []byte) (string, error) { 137 out, err := ioutil.TempFile(dir, "") 138 if err != nil { 139 return "", err 140 } 141 defer out.Close() 142 143 if _, err := out.Write(contents); err != nil { 144 return "", err 145 } 146 147 return out.Name(), nil 148 } 149 150 func errorResponse(w http.ResponseWriter, httpStatus, status int, err, message string) { 151 w.Header().Set("Content-Type", "application/json; charset=utf-8") 152 httphelper.SetDefaultResponseHeaders(w.Header()) 153 w.WriteHeader(httpStatus) 154 155 respJSON := map[string]interface{}{ 156 "status": status, 157 "value": map[string]interface{}{ 158 "error": err, 159 "message": message, 160 }, 161 } 162 163 json.NewEncoder(w).Encode(respJSON) 164 }