parent
							
								
									987a69dd05
								
							
						
					
					
						commit
						0737bb7175
					
				|  | @ -86,7 +86,7 @@ | |||
| 		}, | ||||
| 		{ | ||||
| 			"ImportPath": "github.com/ncw/swift", | ||||
| 			"Rev": "ca8cbbde50d4e12dd8ad70b1bd66589ae98efc5c" | ||||
| 			"Rev": "c54732e87b0b283d1baf0a18db689d0aea460ba3" | ||||
| 		}, | ||||
| 		{ | ||||
| 			"ImportPath": "github.com/noahdesu/go-ceph/rados", | ||||
|  |  | |||
|  | @ -132,3 +132,5 @@ Contributors | |||
| - Dai HaoJun <haojun.dai@hp.com> | ||||
| - Hua Wang <wanghua.humble@gmail.com> | ||||
| - Fabian Ruff <fabian@progra.de> | ||||
| - Arturo Reuschenbach Puncernau <reuschenbach@gmail.com> | ||||
| - Petr Kotek <petr.kotek@bigcommerce.com> | ||||
|  |  | |||
|  | @ -156,6 +156,7 @@ func (auth *v2Auth) Request(c *Connection) (*http.Request, error) { | |||
| 		return nil, err | ||||
| 	} | ||||
| 	req.Header.Set("Content-Type", "application/json") | ||||
| 	req.Header.Set("User-Agent", c.UserAgent) | ||||
| 	return req, nil | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -177,6 +177,7 @@ func (auth *v3Auth) Request(c *Connection) (*http.Request, error) { | |||
| 		return nil, err | ||||
| 	} | ||||
| 	req.Header.Set("Content-Type", "application/json") | ||||
| 	req.Header.Set("User-Agent", c.UserAgent) | ||||
| 	return req, nil | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -392,6 +392,26 @@ func (c *Connection) authenticated() bool { | |||
| 	return c.StorageUrl != "" && c.AuthToken != "" | ||||
| } | ||||
| 
 | ||||
| // SwiftInfo contains the JSON object returned by Swift when the /info
 | ||||
| // route is queried. The object contains, among others, the Swift version,
 | ||||
| // the enabled middlewares and their configuration
 | ||||
| type SwiftInfo map[string]interface{} | ||||
| 
 | ||||
| // Discover Swift configuration by doing a request against /info
 | ||||
| func (c *Connection) QueryInfo() (infos SwiftInfo, err error) { | ||||
| 	infoUrl, err := url.Parse(c.StorageUrl) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	infoUrl.Path = path.Join(infoUrl.Path, "..", "..", "info") | ||||
| 	resp, err := http.Get(infoUrl.String()) | ||||
| 	if err == nil { | ||||
| 		err = readJson(resp, &infos) | ||||
| 		return infos, err | ||||
| 	} | ||||
| 	return nil, err | ||||
| } | ||||
| 
 | ||||
| // RequestOpts contains parameters for Connection.storage.
 | ||||
| type RequestOpts struct { | ||||
| 	Container  string | ||||
|  | @ -418,6 +438,10 @@ type RequestOpts struct { | |||
| // resp.Body.Close() must be called on it, unless noResponse is set in
 | ||||
| // which case the body will be closed in this function
 | ||||
| //
 | ||||
| // If "Content-Length" is set in p.Headers it will be used - this can
 | ||||
| // be used to override the default chunked transfer encoding for
 | ||||
| // uploads.
 | ||||
| //
 | ||||
| // This will Authenticate if necessary, and re-authenticate if it
 | ||||
| // receives a 401 error which means the token has expired
 | ||||
| //
 | ||||
|  | @ -433,8 +457,9 @@ func (c *Connection) Call(targetUrl string, p RequestOpts) (resp *http.Response, | |||
| 	var req *http.Request | ||||
| 	for { | ||||
| 		var authToken string | ||||
| 		targetUrl, authToken, err = c.getUrlAndAuthToken(targetUrl, p.OnReAuth) | ||||
| 
 | ||||
| 		if targetUrl, authToken, err = c.getUrlAndAuthToken(targetUrl, p.OnReAuth); err != nil { | ||||
| 			return //authentication failure
 | ||||
| 		} | ||||
| 		var URL *url.URL | ||||
| 		URL, err = url.Parse(targetUrl) | ||||
| 		if err != nil { | ||||
|  | @ -460,18 +485,27 @@ func (c *Connection) Call(targetUrl string, p RequestOpts) (resp *http.Response, | |||
| 		} | ||||
| 		if p.Headers != nil { | ||||
| 			for k, v := range p.Headers { | ||||
| 				req.Header.Add(k, v) | ||||
| 				// Set ContentLength in req if the user passed it in in the headers
 | ||||
| 				if k == "Content-Length" { | ||||
| 					contentLength, err := strconv.ParseInt(v, 10, 64) | ||||
| 					if err != nil { | ||||
| 						return nil, nil, fmt.Errorf("Invalid %q header %q: %v", k, v, err) | ||||
| 					} | ||||
| 					req.ContentLength = contentLength | ||||
| 				} else { | ||||
| 					req.Header.Add(k, v) | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		req.Header.Add("User-Agent", DefaultUserAgent) | ||||
| 		req.Header.Add("User-Agent", c.UserAgent) | ||||
| 		req.Header.Add("X-Auth-Token", authToken) | ||||
| 		resp, err = c.doTimeoutRequest(timer, req) | ||||
| 		if err != nil { | ||||
| 			if p.Operation == "HEAD" || p.Operation == "GET" { | ||||
| 			if (p.Operation == "HEAD" || p.Operation == "GET") && retries > 0 { | ||||
| 				retries-- | ||||
| 				continue | ||||
| 			} | ||||
| 			return | ||||
| 			return nil, nil, err | ||||
| 		} | ||||
| 		// Check to see if token has expired
 | ||||
| 		if resp.StatusCode == 401 && retries > 0 { | ||||
|  | @ -566,7 +600,8 @@ func readJson(resp *http.Response, result interface{}) (err error) { | |||
| // ContainersOpts is options for Containers() and ContainerNames()
 | ||||
| type ContainersOpts struct { | ||||
| 	Limit     int     // For an integer value n, limits the number of results to at most n values.
 | ||||
| 	Marker    string  // Given a string value x, return object names greater in value than the specified marker.
 | ||||
| 	Prefix    string  // Given a string value x, return container names matching the specified prefix.
 | ||||
| 	Marker    string  // Given a string value x, return container names greater in value than the specified marker.
 | ||||
| 	EndMarker string  // Given a string value x, return container names less in value than the specified marker.
 | ||||
| 	Headers   Headers // Any additional HTTP headers - can be nil
 | ||||
| } | ||||
|  | @ -579,6 +614,9 @@ func (opts *ContainersOpts) parse() (url.Values, Headers) { | |||
| 		if opts.Limit > 0 { | ||||
| 			v.Set("limit", strconv.Itoa(opts.Limit)) | ||||
| 		} | ||||
| 		if opts.Prefix != "" { | ||||
| 			v.Set("prefix", opts.Prefix) | ||||
| 		} | ||||
| 		if opts.Marker != "" { | ||||
| 			v.Set("marker", opts.Marker) | ||||
| 		} | ||||
|  |  | |||
|  | @ -65,6 +65,7 @@ func makeConnection() (*swift.Connection, error) { | |||
| 	UserName := os.Getenv("SWIFT_API_USER") | ||||
| 	ApiKey := os.Getenv("SWIFT_API_KEY") | ||||
| 	AuthUrl := os.Getenv("SWIFT_AUTH_URL") | ||||
| 	Region := os.Getenv("SWIFT_REGION_NAME") | ||||
| 
 | ||||
| 	Insecure := os.Getenv("SWIFT_AUTH_INSECURE") | ||||
| 	ConnectionChannelTimeout := os.Getenv("SWIFT_CONNECTION_CHANNEL_TIMEOUT") | ||||
|  | @ -96,6 +97,7 @@ func makeConnection() (*swift.Connection, error) { | |||
| 		UserName:       UserName, | ||||
| 		ApiKey:         ApiKey, | ||||
| 		AuthUrl:        AuthUrl, | ||||
| 		Region:         Region, | ||||
| 		Transport:      transport, | ||||
| 		ConnectTimeout: 60 * time.Second, | ||||
| 		Timeout:        60 * time.Second, | ||||
|  | @ -601,6 +603,45 @@ func TestObjectPutString(t *testing.T) { | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| func TestObjectPut(t *testing.T) { | ||||
| 	headers := swift.Headers{} | ||||
| 
 | ||||
| 	// Set content size incorrectly - should produce an error
 | ||||
| 	headers["Content-Length"] = strconv.FormatInt(CONTENT_SIZE-1, 10) | ||||
| 	contents := bytes.NewBufferString(CONTENTS) | ||||
| 	h, err := c.ObjectPut(CONTAINER, OBJECT, contents, true, CONTENT_MD5, "text/plain", headers) | ||||
| 	if err == nil { | ||||
| 		t.Fatal("Expecting error but didn't get one") | ||||
| 	} | ||||
| 
 | ||||
| 	// Now set content size correctly
 | ||||
| 	contents = bytes.NewBufferString(CONTENTS) | ||||
| 	headers["Content-Length"] = strconv.FormatInt(CONTENT_SIZE, 10) | ||||
| 	h, err = c.ObjectPut(CONTAINER, OBJECT, contents, true, CONTENT_MD5, "text/plain", headers) | ||||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| 
 | ||||
| 	if h["Etag"] != CONTENT_MD5 { | ||||
| 		t.Errorf("Bad Etag want %q got %q", CONTENT_MD5, h["Etag"]) | ||||
| 	} | ||||
| 
 | ||||
| 	// Fetch object info and compare
 | ||||
| 	info, _, err := c.Object(CONTAINER, OBJECT) | ||||
| 	if err != nil { | ||||
| 		t.Error(err) | ||||
| 	} | ||||
| 	if info.ContentType != "text/plain" { | ||||
| 		t.Error("Bad content type", info.ContentType) | ||||
| 	} | ||||
| 	if info.Bytes != CONTENT_SIZE { | ||||
| 		t.Error("Bad length") | ||||
| 	} | ||||
| 	if info.Hash != CONTENT_MD5 { | ||||
| 		t.Error("Bad length") | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func TestObjectEmpty(t *testing.T) { | ||||
| 	err := c.ObjectPutString(CONTAINER, EMPTYOBJECT, "", "") | ||||
| 	if err != nil { | ||||
|  | @ -1493,6 +1534,14 @@ func TestTempUrl(t *testing.T) { | |||
| 		if content, err := ioutil.ReadAll(resp.Body); err != nil || string(content) != CONTENTS { | ||||
| 			t.Error("Bad content", err) | ||||
| 		} | ||||
| 
 | ||||
| 		resp, err := http.Post(tempUrl, "image/jpeg", bytes.NewReader([]byte(CONTENTS))) | ||||
| 		if err != nil { | ||||
| 			t.Fatal("Failed to retrieve file from temporary url") | ||||
| 		} | ||||
| 		if resp.StatusCode != 401 { | ||||
| 			t.Fatal("Expecting server to forbid access to object") | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	resp.Body.Close() | ||||
|  | @ -1500,7 +1549,17 @@ func TestTempUrl(t *testing.T) { | |||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func TestQueryInfo(t *testing.T) { | ||||
| 	infos, err := c.QueryInfo() | ||||
| 	if err != nil { | ||||
| 		t.Log("Server doesn't support querying info") | ||||
| 		return | ||||
| 	} | ||||
| 	if _, ok := infos["swift"]; !ok { | ||||
| 		t.Fatal("No 'swift' section found in configuration") | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func TestContainerDelete(t *testing.T) { | ||||
|  |  | |||
|  | @ -443,7 +443,7 @@ func (objr objectResource) get(a *action) interface{} { | |||
| 			if obj, ok := item.(*object); ok { | ||||
| 				length := len(obj.data) | ||||
| 				size += length | ||||
| 				sum.Write([]byte(components[0] + "/" + obj.name + "\n")) | ||||
| 				sum.Write([]byte(hex.EncodeToString(obj.checksum))) | ||||
| 				if start >= cursor+length { | ||||
| 					continue | ||||
| 				} | ||||
|  | @ -668,24 +668,42 @@ func (s *SwiftServer) serveHTTP(w http.ResponseWriter, req *http.Request) { | |||
| 		panic(notAuthorized()) | ||||
| 	} | ||||
| 
 | ||||
| 	if req.URL.String() == "/info" { | ||||
| 		jsonMarshal(w, &swift.SwiftInfo{ | ||||
| 			"swift": map[string]interface{}{ | ||||
| 				"version": "1.2", | ||||
| 			}, | ||||
| 			"tempurl": map[string]interface{}{ | ||||
| 				"methods": []string{"GET", "HEAD", "PUT"}, | ||||
| 			}, | ||||
| 		}) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	r = s.resourceForURL(req.URL) | ||||
| 
 | ||||
| 	key := req.Header.Get("x-auth-token") | ||||
| 	if key == "" { | ||||
| 		secretKey := "" | ||||
| 		signature := req.URL.Query().Get("temp_url_sig") | ||||
| 		expires := req.URL.Query().Get("temp_url_expires") | ||||
| 	signature := req.URL.Query().Get("temp_url_sig") | ||||
| 	expires := req.URL.Query().Get("temp_url_expires") | ||||
| 	if key == "" && signature != "" && expires != "" { | ||||
| 		accountName, _, _, _ := s.parseURL(req.URL) | ||||
| 		secretKey := "" | ||||
| 		if account, ok := s.Accounts[accountName]; ok { | ||||
| 			secretKey = account.meta.Get("X-Account-Meta-Temp-Url-Key") | ||||
| 		} | ||||
| 
 | ||||
| 		mac := hmac.New(sha1.New, []byte(secretKey)) | ||||
| 		body := fmt.Sprintf("%s\n%s\n%s", req.Method, expires, req.URL.Path) | ||||
| 		mac.Write([]byte(body)) | ||||
| 		expectedSignature := hex.EncodeToString(mac.Sum(nil)) | ||||
| 		get_hmac := func(method string) string { | ||||
| 			mac := hmac.New(sha1.New, []byte(secretKey)) | ||||
| 			body := fmt.Sprintf("%s\n%s\n%s", method, expires, req.URL.Path) | ||||
| 			mac.Write([]byte(body)) | ||||
| 			return hex.EncodeToString(mac.Sum(nil)) | ||||
| 		} | ||||
| 
 | ||||
| 		if signature != expectedSignature { | ||||
| 		if req.Method == "HEAD" { | ||||
| 			if signature != get_hmac("GET") && signature != get_hmac("POST") && signature != get_hmac("PUT") { | ||||
| 				panic(notAuthorized()) | ||||
| 			} | ||||
| 		} else if signature != get_hmac(req.Method) { | ||||
| 			panic(notAuthorized()) | ||||
| 		} | ||||
| 	} else { | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue