gopkg.in/goose.v2@v2.0.1/testservices/swiftservice/service_http.go (about) 1 // Swift double testing service - HTTP API implementation 2 3 package swiftservice 4 5 import ( 6 "bytes" 7 "crypto/md5" 8 "encoding/json" 9 "fmt" 10 "io/ioutil" 11 "net/http" 12 "net/url" 13 "strconv" 14 "strings" 15 "time" 16 ) 17 18 // verbatim real Swift responses 19 const ( 20 notFoundResponse = `404 Not Found 21 22 The resource could not be found. 23 24 25 ` 26 createdResponse = `201 Created 27 28 29 30 31 ` 32 acceptedResponse = `202 Accepted 33 34 The request is accepted for processing. 35 36 37 ` 38 ) 39 40 // handleContainers processes HTTP requests for container management. 41 func (s *Swift) handleContainers(container string, w http.ResponseWriter, r *http.Request) { 42 var err error 43 w.Header().Set("Content-Type", "text/html; charset=UTF-8") 44 exists := s.HasContainer(container) 45 if !exists && r.Method != "PUT" { 46 w.WriteHeader(http.StatusNotFound) 47 w.Write([]byte(notFoundResponse)) 48 return 49 } 50 switch r.Method { 51 case "GET": 52 urlParams, err := url.ParseQuery(r.URL.RawQuery) 53 if err != nil { 54 w.WriteHeader(http.StatusInternalServerError) 55 w.Write([]byte(err.Error())) 56 return 57 } 58 params := make(map[string]string, len(urlParams)) 59 for k := range urlParams { 60 params[k] = urlParams.Get(k) 61 } 62 contents, err := s.ListContainer(container, params) 63 var objdata []byte 64 if err == nil { 65 objdata, err = json.Marshal(contents) 66 } 67 if err != nil { 68 w.WriteHeader(http.StatusInternalServerError) 69 w.Write([]byte(err.Error())) 70 } else { 71 w.WriteHeader(http.StatusOK) 72 w.Header().Set("Content-Type", "application/json; charset=UF-8") 73 w.Write([]byte(objdata)) 74 } 75 case "DELETE": 76 if err = s.RemoveContainer(container); err != nil { 77 w.WriteHeader(http.StatusInternalServerError) 78 w.Write([]byte(err.Error())) 79 } else { 80 w.Header().Set("Content-Length", "0") 81 w.WriteHeader(http.StatusNoContent) 82 } 83 case "HEAD": 84 urlParams, err := url.ParseQuery(r.URL.RawQuery) 85 if err != nil { 86 w.WriteHeader(http.StatusInternalServerError) 87 w.Write([]byte(err.Error())) 88 return 89 } 90 params := make(map[string]string, len(urlParams)) 91 for k := range urlParams { 92 params[k] = urlParams.Get(k) 93 } 94 _, err = s.ListContainer(container, params) 95 if err != nil { 96 w.WriteHeader(http.StatusInternalServerError) 97 w.Write([]byte(err.Error())) 98 } else { 99 w.WriteHeader(http.StatusOK) 100 w.Header().Set("Content-Type", "application/json; charset=UF-8") 101 } 102 case "PUT": 103 if exists { 104 w.WriteHeader(http.StatusAccepted) 105 w.Write([]byte(acceptedResponse)) 106 } else { 107 if err = s.AddContainer(container); err != nil { 108 w.WriteHeader(http.StatusInternalServerError) 109 w.Write([]byte(err.Error())) 110 } else { 111 w.WriteHeader(http.StatusCreated) 112 w.Write([]byte(createdResponse)) 113 } 114 } 115 case "POST": 116 // [sodre]: we don't implement changing ACLs, so this always succeeds. 117 w.WriteHeader(http.StatusAccepted) 118 w.Write([]byte(createdResponse)) 119 default: 120 panic("not implemented request type: " + r.Method) 121 } 122 } 123 124 // handleObjects processes HTTP requests for object management. 125 func (s *Swift) handleObjects(container, object string, w http.ResponseWriter, r *http.Request) { 126 var err error 127 w.Header().Set("Content-Type", "text/html; charset=UTF-8") 128 if exists := s.HasContainer(container); !exists { 129 w.WriteHeader(http.StatusNotFound) 130 w.Write([]byte(notFoundResponse)) 131 return 132 } 133 objdata, err := s.GetObject(container, object) 134 if err != nil && r.Method != "PUT" { 135 w.WriteHeader(http.StatusNotFound) 136 w.Write([]byte(notFoundResponse)) 137 return 138 } 139 exists := err == nil 140 switch r.Method { 141 case "GET": 142 // Note: even though the real Swift service does not 143 // quote the Etag header value, we need to quote it here 144 // because http.ServeContent requires the quotes, and 145 // this means that we can have a naive client that puts 146 // the exact Etag value in (for example) an If-Match 147 // header and it will work with both the real Swift and 148 // the local mock server. Note also that the HTTP 149 // standard does require the Etag value to be quoted; 150 // see https://tools.ietf.org/html/rfc7232#section-2.3 151 // TODO maintain modification time. 152 w.Header().Set("Etag", fmt.Sprintf(`"%x"`, md5.Sum(objdata))) 153 http.ServeContent(w, r, object, time.Now(), bytes.NewReader(objdata)) 154 case "DELETE": 155 if err = s.RemoveObject(container, object); err != nil { 156 w.WriteHeader(http.StatusInternalServerError) 157 w.Write([]byte(err.Error())) 158 } else { 159 w.Header().Set("Content-Length", "0") 160 w.WriteHeader(http.StatusNoContent) 161 } 162 case "HEAD": 163 w.Header().Set("Content-Length", strconv.Itoa(len(objdata))) 164 w.Header().Set("Etag", fmt.Sprintf(`"%x"`, md5.Sum(objdata))) 165 w.WriteHeader(http.StatusOK) 166 case "PUT": 167 bodydata, err := ioutil.ReadAll(r.Body) 168 if err != nil { 169 w.WriteHeader(http.StatusInternalServerError) 170 w.Write([]byte(err.Error())) 171 return 172 } 173 if exists { 174 err = s.RemoveObject(container, object) 175 if err != nil { 176 w.WriteHeader(http.StatusInternalServerError) 177 w.Write([]byte(err.Error())) 178 } 179 } 180 if err = s.AddObject(container, object, bodydata); err != nil { 181 w.WriteHeader(http.StatusInternalServerError) 182 w.Write([]byte(err.Error())) 183 } else { 184 w.WriteHeader(http.StatusCreated) 185 w.Write([]byte(createdResponse)) 186 } 187 default: 188 panic("not implemented request type: " + r.Method) 189 } 190 } 191 192 // ServeHTTP is the main entry point in the HTTP request processing. 193 func (s *Swift) ServeHTTP(w http.ResponseWriter, r *http.Request) { 194 // TODO(wallyworld) - 2013-02-11 bug=1121682 195 // we need to support container ACLs so we can have pubic containers. 196 // For public containers, the token is not required to access the files. For now, if the request 197 // does not provide a token, we will let it through and assume a public container is being accessed. 198 token := r.Header.Get("X-Auth-Token") 199 _, err := s.IdentityService.FindUser(token) 200 if err != nil && s.FallbackIdentityService != nil { 201 _, err = s.FallbackIdentityService.FindUser(token) 202 } 203 if token != "" && err != nil { 204 w.WriteHeader(http.StatusUnauthorized) 205 return 206 } 207 path := strings.TrimRight(r.URL.Path, "/") 208 path = strings.Trim(path, "/") 209 parts := strings.SplitN(path, "/", 4) 210 if len(parts) > 2 { 211 parts = parts[2:] 212 if len(parts) == 1 { 213 container := parts[0] 214 s.handleContainers(container, w, r) 215 } else if len(parts) == 2 { 216 container := parts[0] 217 object := parts[1] 218 s.handleObjects(container, object, w, r) 219 } 220 } else { 221 panic("not implemented request: " + r.URL.Path) 222 } 223 } 224 225 // setupHTTP attaches all the needed handlers to provide the HTTP API. 226 func (s *Swift) SetupHTTP(mux *http.ServeMux) { 227 mux.Handle("/", s) 228 } 229 230 func (s *Swift) Stop() { 231 // noop 232 }