Update swift lib for adding the support of Openstack Identity v3 API
Signed-off-by: Li Wenquan <wenquan.li@hp.com>
This commit is contained in:
parent
3fb42a1502
commit
3ff9f9b9cc
7 changed files with 434 additions and 39 deletions
2
Godeps/Godeps.json
generated
2
Godeps/Godeps.json
generated
|
@ -87,7 +87,7 @@
|
|||
},
|
||||
{
|
||||
"ImportPath": "github.com/ncw/swift",
|
||||
"Rev": "021f1ecdb0940ce5c64ce0e27928d9680f85f291"
|
||||
"Rev": "22c8fa9fb5ba145b4d4e2cebb027e84b1a7b1296"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/yvasiyarov/go-metrics",
|
||||
|
|
21
Godeps/_workspace/src/github.com/ncw/swift/README.md
generated
vendored
21
Godeps/_workspace/src/github.com/ncw/swift/README.md
generated
vendored
|
@ -34,6 +34,7 @@ Here is a short example from the docs
|
|||
UserName: "user",
|
||||
ApiKey: "key",
|
||||
AuthUrl: "auth_url",
|
||||
Domain: "domain", // Name of the domain (v3 auth only)
|
||||
Tenant: "tenant", // Name of the tenant (v2 auth only)
|
||||
}
|
||||
// Authenticate
|
||||
|
@ -69,6 +70,25 @@ And optionally these if using v2 authentication
|
|||
export SWIFT_TENANT='TenantName'
|
||||
export SWIFT_TENANT_ID='TenantId'
|
||||
|
||||
And optionally these if using v3 authentication
|
||||
|
||||
export SWIFT_TENANT='TenantName'
|
||||
export SWIFT_TENANT_ID='TenantId'
|
||||
export SWIFT_API_DOMAIN_ID='domain id'
|
||||
export SWIFT_API_DOMAIN='domain name'
|
||||
|
||||
And optionally this if you want to skip server certificate validation
|
||||
|
||||
export SWIFT_AUTH_INSECURE=1
|
||||
|
||||
And optionally this to configure the connect channel timeout, in seconds
|
||||
|
||||
export SWIFT_CONNECTION_CHANNEL_TIMEOUT=60
|
||||
|
||||
And optionally this to configure the data channel timeout, in seconds
|
||||
|
||||
export SWIFT_DATA_CHANNEL_TIMEOUT=60
|
||||
|
||||
Then run the tests with `go test`
|
||||
|
||||
License
|
||||
|
@ -105,3 +125,4 @@ Contributors
|
|||
- lsowen <lsowen@s1network.com>
|
||||
- Sylvain Baubeau <sbaubeau@redhat.com>
|
||||
- Chris Kastorff <encryptio@gmail.com>
|
||||
- Dai HaoJun <haojun.dai@hp.com>
|
||||
|
|
6
Godeps/_workspace/src/github.com/ncw/swift/auth.go
generated
vendored
6
Godeps/_workspace/src/github.com/ncw/swift/auth.go
generated
vendored
|
@ -29,7 +29,9 @@ type Authenticator interface {
|
|||
func newAuth(c *Connection) (Authenticator, error) {
|
||||
AuthVersion := c.AuthVersion
|
||||
if AuthVersion == 0 {
|
||||
if strings.Contains(c.AuthUrl, "v2") {
|
||||
if strings.Contains(c.AuthUrl, "v3") {
|
||||
AuthVersion = 3
|
||||
} else if strings.Contains(c.AuthUrl, "v2") {
|
||||
AuthVersion = 2
|
||||
} else if strings.Contains(c.AuthUrl, "v1") {
|
||||
AuthVersion = 1
|
||||
|
@ -47,6 +49,8 @@ func newAuth(c *Connection) (Authenticator, error) {
|
|||
// this is just an optimization.
|
||||
useApiKey: len(c.ApiKey) >= 32,
|
||||
}, nil
|
||||
case 3:
|
||||
return &v3Auth{}, nil
|
||||
}
|
||||
return nil, newErrorf(500, "Auth Version %d not supported", AuthVersion)
|
||||
}
|
||||
|
|
207
Godeps/_workspace/src/github.com/ncw/swift/auth_v3.go
generated
vendored
Normal file
207
Godeps/_workspace/src/github.com/ncw/swift/auth_v3.go
generated
vendored
Normal file
|
@ -0,0 +1,207 @@
|
|||
package swift
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
v3AuthMethodToken = "token"
|
||||
v3AuthMethodPassword = "password"
|
||||
v3InterfacePublic = "public"
|
||||
v3InterfaceInternal = "internal"
|
||||
v3InterfaceAdmin = "admin"
|
||||
v3CatalogTypeObjectStore = "object-store"
|
||||
)
|
||||
|
||||
// V3 Authentication request
|
||||
// http://docs.openstack.org/developer/keystone/api_curl_examples.html
|
||||
// http://developer.openstack.org/api-ref-identity-v3.html
|
||||
type v3AuthRequest struct {
|
||||
Auth struct {
|
||||
Identity struct {
|
||||
Methods []string `json:"methods"`
|
||||
Password *v3AuthPassword `json:"password,omitempty"`
|
||||
Token *v3AuthToken `json:"token,omitempty"`
|
||||
} `json:"identity"`
|
||||
Scope *v3Scope `json:"scope,omitempty"`
|
||||
} `json:"auth"`
|
||||
}
|
||||
|
||||
type v3Scope struct {
|
||||
Project *v3Project `json:"project,omitempty"`
|
||||
Domain *v3Domain `json:"domain,omitempty"`
|
||||
}
|
||||
|
||||
type v3Domain struct {
|
||||
Id string `json:"id,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
}
|
||||
|
||||
type v3Project struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
Id string `json:"id,omitempty"`
|
||||
Domain *v3Domain `json:"domain,omitempty"`
|
||||
}
|
||||
|
||||
type v3User struct {
|
||||
Domain *v3Domain `json:"domain,omitempty"`
|
||||
Id string `json:"id,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Password string `json:"password,omitempty"`
|
||||
}
|
||||
|
||||
type v3AuthToken struct {
|
||||
Id string `json:"id"`
|
||||
}
|
||||
|
||||
type v3AuthPassword struct {
|
||||
User v3User `json:"user"`
|
||||
}
|
||||
|
||||
// V3 Authentication response
|
||||
type v3AuthResponse struct {
|
||||
Token struct {
|
||||
Expires_At, Issued_At string
|
||||
Methods []string
|
||||
Roles []map[string]string
|
||||
|
||||
Project struct {
|
||||
Domain struct {
|
||||
Id, Name string
|
||||
}
|
||||
Id, Name string
|
||||
}
|
||||
|
||||
Catalog []struct {
|
||||
Id, Namem, Type string
|
||||
Endpoints []struct {
|
||||
Id, Region_Id, Url, Region, Interface string
|
||||
}
|
||||
}
|
||||
|
||||
User struct {
|
||||
Id, Name string
|
||||
Domain struct {
|
||||
Id, Name string
|
||||
Links struct {
|
||||
Self string
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Audit_Ids []string
|
||||
}
|
||||
}
|
||||
|
||||
type v3Auth struct {
|
||||
Auth *v3AuthResponse
|
||||
Headers http.Header
|
||||
}
|
||||
|
||||
func (auth *v3Auth) Request(c *Connection) (*http.Request, error) {
|
||||
|
||||
var v3i interface{}
|
||||
|
||||
v3 := v3AuthRequest{}
|
||||
|
||||
if c.UserName == "" {
|
||||
v3.Auth.Identity.Methods = []string{v3AuthMethodToken}
|
||||
v3.Auth.Identity.Token = &v3AuthToken{Id: c.ApiKey}
|
||||
} else {
|
||||
v3.Auth.Identity.Methods = []string{v3AuthMethodPassword}
|
||||
v3.Auth.Identity.Password = &v3AuthPassword{
|
||||
User: v3User{
|
||||
Name: c.UserName,
|
||||
Password: c.ApiKey,
|
||||
},
|
||||
}
|
||||
|
||||
var domain *v3Domain
|
||||
|
||||
if c.Domain != "" {
|
||||
domain = &v3Domain{Name: c.Domain}
|
||||
} else if c.DomainId != "" {
|
||||
domain = &v3Domain{Id: c.DomainId}
|
||||
}
|
||||
v3.Auth.Identity.Password.User.Domain = domain
|
||||
}
|
||||
|
||||
if c.TenantId != "" || c.Tenant != "" {
|
||||
|
||||
v3.Auth.Scope = &v3Scope{Project: &v3Project{}}
|
||||
|
||||
if c.TenantId != "" {
|
||||
v3.Auth.Scope.Project.Id = c.TenantId
|
||||
} else if c.Tenant != "" {
|
||||
v3.Auth.Scope.Project.Name = c.Tenant
|
||||
var defaultDomain v3Domain
|
||||
if c.Domain != "" {
|
||||
defaultDomain = v3Domain{Name: "Default"}
|
||||
} else if c.DomainId != "" {
|
||||
defaultDomain = v3Domain{Id: "Default"}
|
||||
}
|
||||
v3.Auth.Scope.Project.Domain = &defaultDomain
|
||||
}
|
||||
}
|
||||
|
||||
v3i = v3
|
||||
|
||||
body, err := json.Marshal(v3i)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
url := c.AuthUrl
|
||||
if !strings.HasSuffix(url, "/") {
|
||||
url += "/"
|
||||
}
|
||||
url += "tokens"
|
||||
req, err := http.NewRequest("POST", url, bytes.NewBuffer(body))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
return req, nil
|
||||
}
|
||||
|
||||
func (auth *v3Auth) Response(resp *http.Response) error {
|
||||
auth.Auth = &v3AuthResponse{}
|
||||
auth.Headers = resp.Header
|
||||
err := readJson(resp, auth.Auth)
|
||||
return err
|
||||
}
|
||||
|
||||
func (auth *v3Auth) endpointUrl(Type string, Internal bool) string {
|
||||
for _, catalog := range auth.Auth.Token.Catalog {
|
||||
if catalog.Type == Type {
|
||||
for _, endpoint := range catalog.Endpoints {
|
||||
if Internal {
|
||||
if endpoint.Interface == v3InterfaceInternal {
|
||||
return endpoint.Url
|
||||
}
|
||||
} else {
|
||||
if endpoint.Interface == v3InterfacePublic {
|
||||
return endpoint.Url
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (auth *v3Auth) StorageUrl(Internal bool) string {
|
||||
return auth.endpointUrl(v3CatalogTypeObjectStore, Internal)
|
||||
}
|
||||
|
||||
func (auth *v3Auth) Token() string {
|
||||
return auth.Headers.Get("X-Subject-Token")
|
||||
}
|
||||
|
||||
func (auth *v3Auth) CdnUrl() string {
|
||||
return ""
|
||||
}
|
2
Godeps/_workspace/src/github.com/ncw/swift/swift.go
generated
vendored
2
Godeps/_workspace/src/github.com/ncw/swift/swift.go
generated
vendored
|
@ -80,6 +80,8 @@ const (
|
|||
type Connection struct {
|
||||
// Parameters - fill these in before calling Authenticate
|
||||
// They are all optional except UserName, ApiKey and AuthUrl
|
||||
Domain string // User's domain name
|
||||
DomainId string // User's domain Id
|
||||
UserName string // UserName for api
|
||||
ApiKey string // Key for api access
|
||||
AuthUrl string // Auth URL
|
||||
|
|
217
Godeps/_workspace/src/github.com/ncw/swift/swift_test.go
generated
vendored
217
Godeps/_workspace/src/github.com/ncw/swift/swift_test.go
generated
vendored
|
@ -16,22 +16,24 @@ import (
|
|||
"archive/tar"
|
||||
"bytes"
|
||||
"crypto/md5"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"github.com/ncw/swift"
|
||||
"github.com/ncw/swift/swifttest"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ncw/swift"
|
||||
"github.com/ncw/swift/swifttest"
|
||||
)
|
||||
|
||||
var (
|
||||
c swift.Connection
|
||||
c *swift.Connection
|
||||
srv *swifttest.SwiftServer
|
||||
m1 = swift.Metadata{"Hello": "1", "potato-Salad": "2"}
|
||||
m2 = swift.Metadata{"hello": "", "potato-salad": ""}
|
||||
|
@ -54,36 +56,106 @@ const (
|
|||
|
||||
type someTransport struct{ http.Transport }
|
||||
|
||||
func TestTransport(t *testing.T) {
|
||||
func makeConnection() (*swift.Connection, error) {
|
||||
var err error
|
||||
|
||||
UserName := os.Getenv("SWIFT_API_USER")
|
||||
ApiKey := os.Getenv("SWIFT_API_KEY")
|
||||
AuthUrl := os.Getenv("SWIFT_AUTH_URL")
|
||||
|
||||
Insecure := os.Getenv("SWIFT_AUTH_INSECURE")
|
||||
ConnectionChannelTimeout := os.Getenv("SWIFT_CONNECTION_CHANNEL_TIMEOUT")
|
||||
DataChannelTimeout := os.Getenv("SWIFT_DATA_CHANNEL_TIMEOUT")
|
||||
|
||||
if UserName == "" || ApiKey == "" || AuthUrl == "" {
|
||||
if srv != nil {
|
||||
srv.Close()
|
||||
}
|
||||
srv, err = swifttest.NewSwiftServer("localhost")
|
||||
if err != nil {
|
||||
t.Fatal("Failed to create server", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
UserName = "swifttest"
|
||||
ApiKey = "swifttest"
|
||||
AuthUrl = srv.AuthURL
|
||||
}
|
||||
tr := &someTransport{Transport: http.Transport{MaxIdleConnsPerHost: 2048}}
|
||||
ct := swift.Connection{
|
||||
|
||||
transport := &http.Transport{
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
MaxIdleConnsPerHost: 2048,
|
||||
}
|
||||
if Insecure == "1" {
|
||||
transport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
|
||||
}
|
||||
|
||||
c := swift.Connection{
|
||||
UserName: UserName,
|
||||
ApiKey: ApiKey,
|
||||
AuthUrl: AuthUrl,
|
||||
Tenant: os.Getenv("SWIFT_TENANT"),
|
||||
TenantId: os.Getenv("SWIFT_TENANT_ID"),
|
||||
Transport: tr,
|
||||
Transport: transport,
|
||||
ConnectTimeout: 60 * time.Second,
|
||||
Timeout: 60 * time.Second,
|
||||
}
|
||||
err = ct.Authenticate()
|
||||
|
||||
var timeout int64
|
||||
if ConnectionChannelTimeout != "" {
|
||||
timeout, err = strconv.ParseInt(ConnectionChannelTimeout, 10, 32)
|
||||
if err == nil {
|
||||
c.ConnectTimeout = time.Duration(timeout) * time.Second
|
||||
}
|
||||
}
|
||||
|
||||
if DataChannelTimeout != "" {
|
||||
timeout, err = strconv.ParseInt(DataChannelTimeout, 10, 32)
|
||||
if err == nil {
|
||||
c.Timeout = time.Duration(timeout) * time.Second
|
||||
}
|
||||
}
|
||||
|
||||
return &c, nil
|
||||
}
|
||||
|
||||
func isV3Api() bool {
|
||||
AuthUrl := os.Getenv("SWIFT_AUTH_URL")
|
||||
return strings.Contains(AuthUrl, "v3")
|
||||
}
|
||||
|
||||
func TestTransport(t *testing.T) {
|
||||
var err error
|
||||
|
||||
c, err = makeConnection()
|
||||
if err != nil {
|
||||
t.Fatal("Failed to create server", err)
|
||||
}
|
||||
|
||||
tr := &someTransport{
|
||||
Transport: http.Transport{
|
||||
MaxIdleConnsPerHost: 2048,
|
||||
},
|
||||
}
|
||||
|
||||
Insecure := os.Getenv("SWIFT_AUTH_INSECURE")
|
||||
|
||||
if Insecure == "1" {
|
||||
tr.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
|
||||
}
|
||||
|
||||
c.Transport = tr
|
||||
|
||||
if isV3Api() {
|
||||
c.Tenant = os.Getenv("SWIFT_TENANT")
|
||||
c.Domain = os.Getenv("SWIFT_API_DOMAIN")
|
||||
} else {
|
||||
c.Tenant = os.Getenv("SWIFT_TENANT")
|
||||
c.TenantId = os.Getenv("SWIFT_TENANT_ID")
|
||||
}
|
||||
|
||||
err = c.Authenticate()
|
||||
if err != nil {
|
||||
t.Fatal("Auth failed", err)
|
||||
}
|
||||
if !ct.Authenticated() {
|
||||
if !c.Authenticated() {
|
||||
t.Fatal("Not authenticated")
|
||||
}
|
||||
if srv != nil {
|
||||
|
@ -92,27 +164,116 @@ func TestTransport(t *testing.T) {
|
|||
}
|
||||
|
||||
// The following Test functions are run in order - this one must come before the others!
|
||||
func TestAuthenticate(t *testing.T) {
|
||||
func TestV1V2Authenticate(t *testing.T) {
|
||||
var err error
|
||||
UserName := os.Getenv("SWIFT_API_USER")
|
||||
ApiKey := os.Getenv("SWIFT_API_KEY")
|
||||
AuthUrl := os.Getenv("SWIFT_AUTH_URL")
|
||||
if UserName == "" || ApiKey == "" || AuthUrl == "" {
|
||||
srv, err = swifttest.NewSwiftServer("localhost")
|
||||
|
||||
if isV3Api() {
|
||||
return
|
||||
}
|
||||
|
||||
c, err = makeConnection()
|
||||
if err != nil {
|
||||
t.Fatal("Failed to create server", err)
|
||||
}
|
||||
UserName = "swifttest"
|
||||
ApiKey = "swifttest"
|
||||
AuthUrl = srv.AuthURL
|
||||
|
||||
c.Tenant = os.Getenv("SWIFT_TENANT")
|
||||
c.TenantId = os.Getenv("SWIFT_TENANT_ID")
|
||||
|
||||
err = c.Authenticate()
|
||||
if err != nil {
|
||||
t.Fatal("Auth failed", err)
|
||||
}
|
||||
c = swift.Connection{
|
||||
UserName: UserName,
|
||||
ApiKey: ApiKey,
|
||||
AuthUrl: AuthUrl,
|
||||
Tenant: os.Getenv("SWIFT_TENANT"),
|
||||
TenantId: os.Getenv("SWIFT_TENANT_ID"),
|
||||
if !c.Authenticated() {
|
||||
t.Fatal("Not authenticated")
|
||||
}
|
||||
}
|
||||
|
||||
func TestV3AuthenticateWithDomainNameAndTenantId(t *testing.T) {
|
||||
var err error
|
||||
if !isV3Api() {
|
||||
return
|
||||
}
|
||||
|
||||
c, err = makeConnection()
|
||||
if err != nil {
|
||||
t.Fatal("Failed to create server", err)
|
||||
}
|
||||
|
||||
c.TenantId = os.Getenv("SWIFT_TENANT_ID")
|
||||
c.Domain = os.Getenv("SWIFT_API_DOMAIN")
|
||||
|
||||
err = c.Authenticate()
|
||||
if err != nil {
|
||||
t.Fatal("Auth failed", err)
|
||||
}
|
||||
if !c.Authenticated() {
|
||||
t.Fatal("Not authenticated")
|
||||
}
|
||||
}
|
||||
|
||||
func TestV3AuthenticateWithDomainIdAndTenantId(t *testing.T) {
|
||||
var err error
|
||||
|
||||
if !isV3Api() {
|
||||
return
|
||||
}
|
||||
|
||||
c, err = makeConnection()
|
||||
if err != nil {
|
||||
t.Fatal("Failed to create server", err)
|
||||
}
|
||||
|
||||
c.TenantId = os.Getenv("SWIFT_TENANT_ID")
|
||||
c.DomainId = os.Getenv("SWIFT_API_DOMAIN_ID")
|
||||
|
||||
err = c.Authenticate()
|
||||
if err != nil {
|
||||
t.Fatal("Auth failed", err)
|
||||
}
|
||||
if !c.Authenticated() {
|
||||
t.Fatal("Not authenticated")
|
||||
}
|
||||
}
|
||||
|
||||
func TestV3AuthenticateWithDomainNameAndTenantName(t *testing.T) {
|
||||
var err error
|
||||
|
||||
if !isV3Api() {
|
||||
return
|
||||
}
|
||||
|
||||
c, err = makeConnection()
|
||||
if err != nil {
|
||||
t.Fatal("Failed to create server", err)
|
||||
}
|
||||
|
||||
c.Tenant = os.Getenv("SWIFT_TENANT")
|
||||
c.Domain = os.Getenv("SWIFT_API_DOMAIN")
|
||||
|
||||
err = c.Authenticate()
|
||||
if err != nil {
|
||||
t.Fatal("Auth failed", err)
|
||||
}
|
||||
if !c.Authenticated() {
|
||||
t.Fatal("Not authenticated")
|
||||
}
|
||||
}
|
||||
|
||||
func TestV3AuthenticateWithDomainIdAndTenantName(t *testing.T) {
|
||||
var err error
|
||||
|
||||
if !isV3Api() {
|
||||
return
|
||||
}
|
||||
|
||||
c, err = makeConnection()
|
||||
if err != nil {
|
||||
t.Fatal("Failed to create server", err)
|
||||
}
|
||||
|
||||
c.Tenant = os.Getenv("SWIFT_TENANT")
|
||||
c.DomainId = os.Getenv("SWIFT_API_DOMAIN_ID")
|
||||
|
||||
err = c.Authenticate()
|
||||
if err != nil {
|
||||
t.Fatal("Auth failed", err)
|
||||
|
|
10
Godeps/_workspace/src/github.com/ncw/swift/swifttest/server.go
generated
vendored
10
Godeps/_workspace/src/github.com/ncw/swift/swifttest/server.go
generated
vendored
|
@ -444,7 +444,7 @@ func (objr objectResource) get(a *action) interface{} {
|
|||
if start >= cursor+length {
|
||||
continue
|
||||
}
|
||||
segments = append(segments, bytes.NewReader(obj.data[max(0, start - cursor):]))
|
||||
segments = append(segments, bytes.NewReader(obj.data[max(0, start-cursor):]))
|
||||
cursor += length
|
||||
}
|
||||
}
|
||||
|
@ -452,7 +452,7 @@ func (objr objectResource) get(a *action) interface{} {
|
|||
if end == -1 {
|
||||
end = size
|
||||
}
|
||||
reader = io.LimitReader(io.MultiReader(segments...), int64(end - start))
|
||||
reader = io.LimitReader(io.MultiReader(segments...), int64(end-start))
|
||||
} else {
|
||||
if end == -1 {
|
||||
end = len(obj.data)
|
||||
|
@ -461,7 +461,7 @@ func (objr objectResource) get(a *action) interface{} {
|
|||
reader = bytes.NewReader(obj.data[start:end])
|
||||
}
|
||||
|
||||
h.Set("Content-Length", fmt.Sprint(end - start))
|
||||
h.Set("Content-Length", fmt.Sprint(end-start))
|
||||
h.Set("ETag", hex.EncodeToString(etag))
|
||||
h.Set("Last-Modified", obj.mtime.Format(http.TimeFormat))
|
||||
|
||||
|
@ -519,7 +519,7 @@ func (objr objectResource) put(a *action) interface{} {
|
|||
|
||||
var content_type string
|
||||
if content_type = a.req.Header.Get("Content-Type"); content_type == "" {
|
||||
content_type := mime.TypeByExtension(obj.name)
|
||||
content_type = mime.TypeByExtension(obj.name)
|
||||
if content_type == "" {
|
||||
content_type = "application/octet-stream"
|
||||
}
|
||||
|
@ -880,6 +880,6 @@ func NewSwiftServer(address string) (*SwiftServer, error) {
|
|||
return server, nil
|
||||
}
|
||||
|
||||
func (srv SwiftServer) Close() {
|
||||
func (srv *SwiftServer) Close() {
|
||||
srv.Listener.Close()
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue