@@ -135,7 +135,8 @@ type CouchConnectionDef struct {
135
135
136
136
//CouchInstance represents a CouchDB instance
137
137
type CouchInstance struct {
138
- conf CouchConnectionDef //connection configuration
138
+ conf CouchConnectionDef //connection configuration
139
+ client * http.Client // a client to connect to this instance
139
140
}
140
141
141
142
//CouchDatabase represents a database within a CouchDB instance
@@ -206,6 +207,13 @@ type Base64Attachment struct {
206
207
AttachmentData string `json:"data"`
207
208
}
208
209
210
+ // closeResponseBody discards the body and then closes it to enable returning it to
211
+ // connection pool
212
+ func closeResponseBody (resp * http.Response ) {
213
+ io .Copy (ioutil .Discard , resp .Body ) // discard whatever is remaining of body
214
+ resp .Body .Close ()
215
+ }
216
+
209
217
//CreateConnectionDefinition for a new client connection
210
218
func CreateConnectionDefinition (couchDBAddress , username , password string , maxRetries ,
211
219
maxRetriesOnStartup int , requestTimeout time.Duration ) (* CouchConnectionDef , error ) {
@@ -264,7 +272,7 @@ func (dbclient *CouchDatabase) CreateDatabaseIfNotExist() (*DBOperationResponse,
264
272
if err != nil {
265
273
return nil , err
266
274
}
267
- defer resp . Body . Close ( )
275
+ defer closeResponseBody ( resp )
268
276
269
277
//Get the response from the create REST call
270
278
dbResponse := & DBOperationResponse {}
@@ -305,7 +313,7 @@ func (dbclient *CouchDatabase) GetDatabaseInfo() (*DBInfo, *DBReturn, error) {
305
313
if err != nil {
306
314
return nil , couchDBReturn , err
307
315
}
308
- defer resp . Body . Close ( )
316
+ defer closeResponseBody ( resp )
309
317
310
318
dbResponse := & DBInfo {}
311
319
json .NewDecoder (resp .Body ).Decode (& dbResponse )
@@ -344,7 +352,7 @@ func (couchInstance *CouchInstance) VerifyCouchConfig() (*ConnectionInfo, *DBRet
344
352
if err != nil {
345
353
return nil , couchDBReturn , fmt .Errorf ("Unable to connect to CouchDB, check the hostname and port: %s" , err .Error ())
346
354
}
347
- defer resp . Body . Close ( )
355
+ defer closeResponseBody ( resp )
348
356
349
357
dbResponse := & ConnectionInfo {}
350
358
errJSON := json .NewDecoder (resp .Body ).Decode (& dbResponse )
@@ -371,7 +379,6 @@ func (couchInstance *CouchInstance) VerifyCouchConfig() (*ConnectionInfo, *DBRet
371
379
}
372
380
373
381
return dbResponse , couchDBReturn , nil
374
-
375
382
}
376
383
377
384
//DropDatabase provides method to drop an existing database
@@ -393,7 +400,7 @@ func (dbclient *CouchDatabase) DropDatabase() (*DBOperationResponse, error) {
393
400
if err != nil {
394
401
return nil , err
395
402
}
396
- defer resp . Body . Close ( )
403
+ defer closeResponseBody ( resp )
397
404
398
405
dbResponse := & DBOperationResponse {}
399
406
json .NewDecoder (resp .Body ).Decode (& dbResponse )
@@ -434,7 +441,7 @@ func (dbclient *CouchDatabase) EnsureFullCommit() (*DBOperationResponse, error)
434
441
logger .Errorf ("Failed to invoke _ensure_full_commit Error: %s\n " , err .Error ())
435
442
return nil , err
436
443
}
437
- defer resp . Body . Close ( )
444
+ defer closeResponseBody ( resp )
438
445
439
446
dbResponse := & DBOperationResponse {}
440
447
json .NewDecoder (resp .Body ).Decode (& dbResponse )
@@ -528,7 +535,7 @@ func (dbclient *CouchDatabase) SaveDoc(id string, rev string, couchDoc *CouchDoc
528
535
if err != nil {
529
536
return "" , err
530
537
}
531
- defer resp . Body . Close ( )
538
+ defer closeResponseBody ( resp )
532
539
533
540
//get the revision and return
534
541
revision , err := getRevisionHeader (resp )
@@ -666,7 +673,7 @@ func (dbclient *CouchDatabase) ReadDoc(id string) (*CouchDoc, string, error) {
666
673
logger .Debugf ("couchDBReturn=%v\n " , couchDBReturn )
667
674
return nil , "" , err
668
675
}
669
- defer resp . Body . Close ( )
676
+ defer closeResponseBody ( resp )
670
677
671
678
//Get the media type from the Content-Type header
672
679
mediaType , params , err := mime .ParseMediaType (resp .Header .Get ("Content-Type" ))
@@ -813,7 +820,7 @@ func (dbclient *CouchDatabase) ReadDocRange(startKey, endKey string, limit, skip
813
820
if err != nil {
814
821
return nil , err
815
822
}
816
- defer resp . Body . Close ( )
823
+ defer closeResponseBody ( resp )
817
824
818
825
if logger .IsEnabledFor (logging .DEBUG ) {
819
826
dump , err2 := httputil .DumpResponse (resp , true )
@@ -918,7 +925,7 @@ func (dbclient *CouchDatabase) DeleteDoc(id, rev string) error {
918
925
}
919
926
return err
920
927
}
921
- defer resp . Body . Close ( )
928
+ defer closeResponseBody ( resp )
922
929
923
930
logger .Debugf ("Exiting DeleteDoc()" )
924
931
@@ -948,7 +955,7 @@ func (dbclient *CouchDatabase) QueryDocuments(query string) (*[]QueryResult, err
948
955
if err != nil {
949
956
return nil , err
950
957
}
951
- defer resp . Body . Close ( )
958
+ defer closeResponseBody ( resp )
952
959
953
960
if logger .IsEnabledFor (logging .DEBUG ) {
954
961
dump , err2 := httputil .DumpResponse (resp , true )
@@ -1034,7 +1041,7 @@ func (dbclient *CouchDatabase) BatchRetrieveIDRevision(keys []string) ([]*DocMet
1034
1041
if err != nil {
1035
1042
return nil , err
1036
1043
}
1037
- defer resp . Body . Close ( )
1044
+ defer closeResponseBody ( resp )
1038
1045
1039
1046
if logger .IsEnabledFor (logging .DEBUG ) {
1040
1047
dump , _ := httputil .DumpResponse (resp , false )
@@ -1129,7 +1136,7 @@ func (dbclient *CouchDatabase) BatchUpdateDocuments(documents []*CouchDoc) ([]*B
1129
1136
if err != nil {
1130
1137
return nil , err
1131
1138
}
1132
- defer resp . Body . Close ( )
1139
+ defer closeResponseBody ( resp )
1133
1140
1134
1141
if logger .IsEnabledFor (logging .DEBUG ) {
1135
1142
dump , _ := httputil .DumpResponse (resp , false )
@@ -1156,7 +1163,9 @@ func (dbclient *CouchDatabase) BatchUpdateDocuments(documents []*CouchDoc) ([]*B
1156
1163
1157
1164
}
1158
1165
1159
- //handleRequest method is a generic http request handler
1166
+ //handleRequest method is a generic http request handler.
1167
+ // if it returns an error, it ensures that the response body is closed, else it is the
1168
+ // callee's responsibility to close response correctly
1160
1169
func (couchInstance * CouchInstance ) handleRequest (method , connectURL string , data []byte , rev string ,
1161
1170
multipartBoundary string , maxRetries int ) (* http.Response , * DBReturn , error ) {
1162
1171
@@ -1170,9 +1179,6 @@ func (couchInstance *CouchInstance) handleRequest(method, connectURL string, dat
1170
1179
//set initial wait duration for retries
1171
1180
waitDuration := retryWaitTime * time .Millisecond
1172
1181
1173
- //get the connection timeout
1174
- requestTimeout := couchInstance .conf .RequestTimeout
1175
-
1176
1182
//attempt the http request for the max number of retries
1177
1183
for attempts := 0 ; attempts < maxRetries ; attempts ++ {
1178
1184
@@ -1225,14 +1231,8 @@ func (couchInstance *CouchInstance) handleRequest(method, connectURL string, dat
1225
1231
logger .Debugf ("HTTP Request: %s" , bytes .Replace (dump , []byte {0x0d , 0x0a }, []byte {0x20 , 0x7c , 0x20 }, - 1 ))
1226
1232
}
1227
1233
1228
- //Create the http client
1229
- client := & http.Client {Timeout : requestTimeout }
1230
-
1231
- transport := & http.Transport {Proxy : http .ProxyFromEnvironment }
1232
- transport .DisableCompression = false
1233
- client .Transport = transport
1234
1234
//Execute http request
1235
- resp , errResp = client .Do (req )
1235
+ resp , errResp = couchInstance . client .Do (req )
1236
1236
1237
1237
//if an error is not detected then drop out of the retry
1238
1238
if errResp == nil && resp != nil && resp .StatusCode < 500 {
@@ -1248,8 +1248,9 @@ func (couchInstance *CouchInstance) handleRequest(method, connectURL string, dat
1248
1248
1249
1249
} else {
1250
1250
1251
- //Read the response body
1251
+ //Read the response body and close it for next attempt
1252
1252
jsonError , err := ioutil .ReadAll (resp .Body )
1253
+ closeResponseBody (resp )
1253
1254
if err != nil {
1254
1255
return nil , nil , err
1255
1256
}
@@ -1283,6 +1284,8 @@ func (couchInstance *CouchInstance) handleRequest(method, connectURL string, dat
1283
1284
//check to see if the status code is 400 or higher
1284
1285
//response codes 4XX and 500 will be treated as errors
1285
1286
if resp .StatusCode >= 400 {
1287
+ // close the response before returning error
1288
+ defer closeResponseBody (resp )
1286
1289
1287
1290
//Read the response body
1288
1291
jsonError , err := ioutil .ReadAll (resp .Body )
0 commit comments