diff --git a/authentication.go b/authentication.go index c37358eb..44c379b1 100644 --- a/authentication.go +++ b/authentication.go @@ -29,6 +29,8 @@ const ( AuthenticationTypeBasic AuthenticationType = iota // AuthenticationTypeJWT uses username+password JWT token based authentication AuthenticationTypeJWT + // AuthenticationTypeRaw uses a raw value for the Authorization header + AuthenticationTypeRaw ) // Authentication implements a kind of authentication. @@ -82,3 +84,31 @@ func (a *userNameAuthentication) Get(property string) string { return "" } } + +// RawAuthentication creates a raw authentication implementation based on the given value for the Authorization header. +func RawAuthentication(value string) Authentication { + return &rawAuthentication{ + value: value, + } +} + +// rawAuthentication implements Raw authentication. +type rawAuthentication struct { + value string +} + +// Returns the type of authentication +func (a *rawAuthentication) Type() AuthenticationType { + return AuthenticationTypeRaw +} + +// Get returns a configuration property of the authentication. +// Supported properties depend on type of authentication. +func (a *rawAuthentication) Get(property string) string { + switch property { + case "value": + return a.value + default: + return "" + } +} diff --git a/client.go b/client.go index 79a76691..84e2131c 100644 --- a/client.go +++ b/client.go @@ -44,6 +44,9 @@ type Client interface { // This function requires ArangoDB 3.1.15 or up. SynchronizeEndpoints(ctx context.Context) error + // Connection returns the connection used by this client + Connection() Connection + // Database functions ClientDatabases diff --git a/client_impl.go b/client_impl.go index afa5ce34..eee16a05 100644 --- a/client_impl.go +++ b/client_impl.go @@ -56,6 +56,11 @@ type client struct { conn Connection } +// Connection returns the connection used by this client +func (c *client) Connection() Connection { + return c.conn +} + // Version returns version information from the connected database server. func (c *client) Version(ctx context.Context) (VersionInfo, error) { req, err := c.conn.NewRequest("GET", "_api/version") diff --git a/connection.go b/connection.go index f4765a28..8b058901 100644 --- a/connection.go +++ b/connection.go @@ -77,6 +77,8 @@ type Request interface { // Written returns true as soon as this request has been written completely to the network. // This does not guarantee that the server has received or processed the request. Written() bool + // Clone creates a new request containing the same data as this request + Clone() Request } // Response represents the response from the server on a given request. diff --git a/http/authentication.go b/http/authentication.go index fdbdb550..76b15c9b 100644 --- a/http/authentication.go +++ b/http/authentication.go @@ -58,6 +58,13 @@ func newJWTAuthentication(userName, password string) httpAuthentication { } } +// newRawAuthentication creates a Raw authentication implementation based on the given value. +func newRawAuthentication(value string) httpAuthentication { + return &basicAuthentication{ + authorizationValue: value, + } +} + // basicAuthentication implements HTTP Basic authentication. type basicAuthentication struct { authorizationValue string diff --git a/http/connection.go b/http/connection.go index 1c9fa448..64aa78f5 100644 --- a/http/connection.go +++ b/http/connection.go @@ -57,6 +57,8 @@ type ConnectionConfig struct { // Otherwise a `TLSConfig` property other than `nil` will overwrite the `TLSClientConfig` // property of `Transport`. Transport http.RoundTripper + // FailOnRedirect; if set, redirect will not be followed, instead the status code is returned as error + FailOnRedirect bool // Cluster configuration settings cluster.ConnectionConfig // ContentType specified type of content encoding to use. @@ -95,12 +97,23 @@ func newHTTPConnection(endpoint string, config ConnectionConfig) (driver.Connect if config.TLSConfig != nil && httpTransport != nil { httpTransport.TLSClientConfig = config.TLSConfig } + httpClient := &http.Client{ + Transport: config.Transport, + } + if config.FailOnRedirect { + httpClient.CheckRedirect = func(req *http.Request, via []*http.Request) error { + return driver.ArangoError{ + HasError: true, + Code: http.StatusFound, + ErrorNum: 0, + ErrorMessage: "Redirect not allowed", + } + } + } c := &httpConnection{ endpoint: *u, contentType: config.ContentType, - client: &http.Client{ - Transport: config.Transport, - }, + client: httpClient, } return c, nil } @@ -278,6 +291,9 @@ func (c *httpConnection) SetAuthentication(auth driver.Authentication) (driver.C userName := auth.Get("username") password := auth.Get("password") httpAuth = newJWTAuthentication(userName, password) + case driver.AuthenticationTypeRaw: + value := auth.Get("value") + httpAuth = newRawAuthentication(value) default: return nil, driver.WithStack(fmt.Errorf("Unsupported authentication type %d", int(auth.Type()))) } diff --git a/http/request_json.go b/http/request_json.go index 386dda4f..712ed3ea 100644 --- a/http/request_json.go +++ b/http/request_json.go @@ -47,6 +47,24 @@ type httpJSONRequest struct { written bool } +// Clone creates a new request containing the same data as this request +func (r *httpJSONRequest) Clone() driver.Request { + clone := *r + clone.q = url.Values{} + for k, v := range r.q { + for _, x := range v { + clone.q.Add(k, x) + } + } + if clone.hdr != nil { + clone.hdr = make(map[string]string) + for k, v := range r.hdr { + clone.hdr[k] = v + } + } + return &clone +} + // SetQuery sets a single query argument of the request. // Any existing query argument with the same key is overwritten. func (r *httpJSONRequest) SetQuery(key, value string) driver.Request { diff --git a/http/request_vpack.go b/http/request_vpack.go index 5c70bc99..d9fdf701 100644 --- a/http/request_vpack.go +++ b/http/request_vpack.go @@ -47,6 +47,24 @@ type httpVPackRequest struct { written bool } +// Clone creates a new request containing the same data as this request +func (r *httpVPackRequest) Clone() driver.Request { + clone := *r + clone.q = url.Values{} + for k, v := range r.q { + for _, x := range v { + clone.q.Add(k, x) + } + } + if clone.hdr != nil { + clone.hdr = make(map[string]string) + for k, v := range r.hdr { + clone.hdr[k] = v + } + } + return &clone +} + // SetQuery sets a single query argument of the request. // Any existing query argument with the same key is overwritten. func (r *httpVPackRequest) SetQuery(key, value string) driver.Request { diff --git a/vst/request.go b/vst/request.go index 50cb0765..c88d8d35 100644 --- a/vst/request.go +++ b/vst/request.go @@ -43,6 +43,24 @@ type vstRequest struct { written bool } +// Clone creates a new request containing the same data as this request +func (r *vstRequest) Clone() driver.Request { + clone := *r + clone.q = url.Values{} + for k, v := range r.q { + for _, x := range v { + clone.q.Add(k, x) + } + } + if clone.hdr != nil { + clone.hdr = make(map[string]string) + for k, v := range r.hdr { + clone.hdr[k] = v + } + } + return &clone +} + // SetQuery sets a single query argument of the request. // Any existing query argument with the same key is overwritten. func (r *vstRequest) SetQuery(key, value string) driver.Request {