1 // Licensed under the Apache License, Version 2.0 (the "License"); you may not 2 // use this file except in compliance with the License. You may obtain a copy of 3 // the License at 4 // 5 // http://www.apache.org/licenses/LICENSE-2.0 6 // 7 // Unless required by applicable law or agreed to in writing, software 8 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 9 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 10 // License for the specific language governing permissions and limitations under 11 // the License. 12 13 package chttp 14 15 import ( 16 "fmt" 17 "io" 18 "net/http" 19 "net/url" 20 21 "github.com/go-kivik/kivik/v4" 22 "github.com/go-kivik/kivik/v4/driver" 23 ) 24 25 // Options are optional parameters which may be sent with a request. 26 type Options struct { 27 // Accept sets the request's Accept header. Defaults to "application/json". 28 // To specify any, use "*/*". 29 Accept string 30 31 // ContentType sets the requests's Content-Type header. Defaults to "application/json". 32 ContentType string 33 34 // ContentLength, if set, sets the ContentLength of the request 35 ContentLength int64 36 37 // Body sets the body of the request. 38 Body io.ReadCloser 39 40 // GetBody is a function to set the body, and can be used on retries. If 41 // set, Body is ignored. 42 GetBody func() (io.ReadCloser, error) 43 44 // JSON is an arbitrary data type which is marshaled to the request's body. 45 // It an error to set both Body and JSON on the same request. When this is 46 // set, ContentType is unconditionally set to 'application/json'. Note that 47 // for large JSON payloads, it can be beneficial to do your own JSON stream 48 // encoding, so that the request can be live on the wire during JSON 49 // encoding. 50 JSON interface{} 51 52 // FullCommit adds the X-Couch-Full-Commit: true header to requests 53 FullCommit bool 54 55 // IfNoneMatch adds the If-None-Match header. The value will be quoted if 56 // it is not already. 57 IfNoneMatch string 58 59 // Query is appended to the exiting url, if present. If the passed url 60 // already contains query parameters, the values in Query are appended. 61 // No merging takes place. 62 Query url.Values 63 64 // Header is a list of default headers to be set on the request. 65 Header http.Header 66 67 // NoGzip disables gzip compression on the request body. 68 NoGzip bool 69 } 70 71 // NewOptions converts a kivik options map into 72 func NewOptions(options driver.Options) *Options { 73 o := &Options{} 74 options.Apply(o) 75 return o 76 } 77 78 type optionNoRequestCompression struct{} 79 80 var _ kivik.Option = optionNoRequestCompression{} 81 82 func (optionNoRequestCompression) Apply(target interface{}) { 83 if client, ok := target.(*Client); ok { 84 client.noGzip = true 85 } 86 } 87 88 func (optionNoRequestCompression) String() string { return "NoRequestCompression" } 89 90 // OptionNoRequestCompression instructs the CouchDB client not to use gzip 91 // compression for request bodies sent to the server. Only honored when passed 92 // to [github.com/go-kivik/kivik/v4.New] or [New]. 93 func OptionNoRequestCompression() kivik.Option { 94 return optionNoRequestCompression{} 95 } 96 97 type optionUserAgent string 98 99 func (a optionUserAgent) Apply(target interface{}) { 100 if client, ok := target.(*Client); ok { 101 client.UserAgents = append(client.UserAgents, string(a)) 102 } 103 } 104 105 func (a optionUserAgent) String() string { 106 return fmt.Sprintf("[UserAgent:%s]", string(a)) 107 } 108 109 // OptionUserAgent may be passed as an option when creating a client object, 110 // to append to the default User-Agent header sent on all requests. 111 func OptionUserAgent(ua string) kivik.Option { 112 return optionUserAgent(ua) 113 } 114 115 type optionFullCommit struct{} 116 117 func (optionFullCommit) Apply(target interface{}) { 118 if o, ok := target.(*Options); ok { 119 o.FullCommit = true 120 } 121 } 122 123 func (optionFullCommit) String() string { 124 return "[FullCommit]" 125 } 126 127 // OptionFullCommit is the option key used to set the `X-Couch-Full-Commit` 128 // header in the request when set to true. 129 func OptionFullCommit() kivik.Option { 130 return optionFullCommit{} 131 } 132 133 type optionIfNoneMatch string 134 135 func (o optionIfNoneMatch) Apply(target interface{}) { 136 if opts, ok := target.(*Options); ok { 137 opts.IfNoneMatch = string(o) 138 } 139 } 140 141 func (o optionIfNoneMatch) String() string { 142 return fmt.Sprintf("[If-None-Match: %s]", string(o)) 143 } 144 145 // OptionIfNoneMatch is an option key to set the `If-None-Match` header on 146 // the request. 147 func OptionIfNoneMatch(value string) kivik.Option { 148 return optionIfNoneMatch(value) 149 } 150 151 // CookieAuth provides CouchDB [Cookie auth]. Cookie Auth is the default 152 // authentication method if credentials are included in the connection URL 153 // passed to [New]. You may also pass this option as an argument to the same 154 // function, if you need to provide your auth credentials outside of the URL. 155 // 156 // [Cookie auth]: http://docs.couchdb.org/en/2.0.0/api/server/authn.html#cookie-authentication 157 func CookieAuth(username, password string) kivik.Option { 158 return &cookieAuth{ 159 Username: username, 160 Password: password, 161 } 162 } 163 164 // BasicAuth provides HTTP Basic Auth for a client. Pass this option to [New] 165 // to use Basic Authentication. 166 func BasicAuth(username, password string) kivik.Option { 167 return &basicAuth{ 168 Username: username, 169 Password: password, 170 } 171 } 172 173 // JWTAuth provides JWT based auth for a client. Pass this option to [New] to 174 // use JWT authentication 175 func JWTAuth(token string) kivik.Option { 176 return &jwtAuth{ 177 Token: token, 178 } 179 } 180 181 // ProxyAuth provides support for CouchDB's [proxy authentication]. Pass this 182 // option to [New] to use proxy authentication. 183 func ProxyAuth(username, secret string, roles []string, headers ...map[string]string) kivik.Option { 184 httpHeader := http.Header{} 185 for _, h := range headers { 186 for k, v := range h { 187 httpHeader.Set(k, v) 188 } 189 } 190 return &proxyAuth{ 191 Username: username, 192 Secret: secret, 193 Roles: roles, 194 Headers: httpHeader, 195 } 196 } 197