@@ -98,6 +98,7 @@ type CustomHTTPClient struct {
9898 shutdownOnce sync.Once
9999 shutdownDone chan struct {}
100100 shutdownResult FinalizeResult
101+ pendingExchanges map [* exchangeState ]struct {}
101102
102103 CDXDedupeTotalBytes * atomic.Int64
103104 DoppelgangerDedupeTotalBytes * atomic.Int64
@@ -131,8 +132,16 @@ func (c *CustomHTTPClient) Shutdown(ctx context.Context) (FinalizeResult, error)
131132 c .shutdownOnce .Do (func () {
132133 c .lifecycleMu .Lock ()
133134 c .closing = true
135+ pending := make ([]* exchangeState , 0 , len (c .pendingExchanges ))
136+ for state := range c .pendingExchanges {
137+ pending = append (pending , state )
138+ }
134139 c .lifecycleMu .Unlock ()
135- go c .runShutdown ()
140+ stopTransportEarly := false
141+ for _ , state := range pending {
142+ stopTransportEarly = state .closeForShutdown () || stopTransportEarly
143+ }
144+ go c .runShutdown (stopTransportEarly )
136145 })
137146 select {
138147 case <- c .shutdownDone :
@@ -144,13 +153,17 @@ func (c *CustomHTTPClient) Shutdown(ctx context.Context) (FinalizeResult, error)
144153 }
145154}
146155
147- func (c * CustomHTTPClient ) runShutdown () {
156+ func (c * CustomHTTPClient ) runShutdown (stopTransportEarly bool ) {
148157 defer close (c .shutdownDone )
149158 var wg sync.WaitGroup
150- if c .protoClient != nil {
159+ if stopTransportEarly && c .protoClient != nil {
151160 c .protoClient .Shutdown ()
152161 }
153162 c .WaitGroup .Wait ()
163+ c .compatWG .Wait ()
164+ if ! stopTransportEarly && c .protoClient != nil {
165+ c .protoClient .Shutdown ()
166+ }
154167
155168 close (c .WARCWriter )
156169
@@ -171,7 +184,6 @@ func (c *CustomHTTPClient) runShutdown() {
171184 finalizedFiles = append (finalizedFiles , result .FinalizedFiles ... )
172185 }
173186
174- c .compatWG .Wait ()
175187 close (c .ErrChan )
176188
177189 if c .randomLocalIP {
@@ -197,14 +209,14 @@ func (c *CustomHTTPClient) Do(req *http.Request) (*http.Response, error) {
197209 }
198210 c .compatWG .Add (1 )
199211 c .lifecycleMu .Unlock ()
200- exchange , err := c .Start (req )
212+ exchange , err := c .start (req , exchangeCommit )
201213 if exchange == nil {
202214 c .compatWG .Done ()
203215 return nil , err
204216 }
205217 go func () {
206218 defer c .compatWG .Done ()
207- result , _ := exchange .Wait (context .Background ())
219+ result , _ := exchange .wait (context .Background ())
208220 var archiveErrs []error
209221 for _ , attempt := range result .Attempts {
210222 archiveErrs = append (archiveErrs , attempt .Err )
@@ -214,16 +226,20 @@ func (c *CustomHTTPClient) Do(req *http.Request) (*http.Response, error) {
214226 return
215227 }
216228 select {
217- case c .ErrChan <- & Error {Err : archiveErr , Func : "Exchange.Wait " }:
229+ case c .ErrChan <- & Error {Err : archiveErr , Func : "Exchange.Commit " }:
218230 default :
219231 }
220232 }()
221233 return exchange .Response , err
222234}
223235
224- // Start executes req and returns an Exchange whose Wait method reports the
225- // durable archival result independently from receiving response headers .
236+ // Start executes req and returns an Exchange that the caller must finish with
237+ // Commit or Discard after inspecting the response.
226238func (c * CustomHTTPClient ) Start (req * http.Request ) (* Exchange , error ) {
239+ return c .start (req , exchangeUndecided )
240+ }
241+
242+ func (c * CustomHTTPClient ) start (req * http.Request , decision exchangeDecision ) (* Exchange , error ) {
227243 if req == nil {
228244 return nil , errors .New ("warc: nil request" )
229245 }
@@ -245,18 +261,26 @@ func (c *CustomHTTPClient) Start(req *http.Request) (*Exchange, error) {
245261 return nil , errors .New ("warc: feedback channel must be buffered" )
246262 }
247263 }
248- state := newExchangeState (c , feedback )
264+ state := newExchangeState (c , feedback , decision )
265+ c .pendingExchanges [state ] = struct {}{}
249266 ctx := context .WithValue (req .Context (), exchangeContextKey {}, state )
250267 req = req .Clone (ctx )
251268 req .URL .Scheme = strings .ToLower (req .URL .Scheme )
252269 c .WaitGroup .Add (1 )
253270 c .lifecycleMu .Unlock ()
254271 resp , err := c .protoClient .Do (ctx , req )
255272 c .WaitGroup .Done ()
273+ state .attachResponse (resp )
256274 exchange := & Exchange {Response : resp , state : state }
257275 return exchange , err
258276}
259277
278+ func (c * CustomHTTPClient ) unregisterExchange (state * exchangeState ) {
279+ c .lifecycleMu .Lock ()
280+ delete (c .pendingExchanges , state )
281+ c .lifecycleMu .Unlock ()
282+ }
283+
260284func (c * CustomHTTPClient ) Get (url string ) (* http.Response , error ) {
261285 req , err := http .NewRequest (http .MethodGet , url , nil )
262286 if err != nil {
@@ -303,6 +327,7 @@ func NewWARCWritingHTTPClient(HTTPClientSettings HTTPClientSettings) (httpClient
303327 }
304328 httpClient = new (CustomHTTPClient )
305329 httpClient .shutdownDone = make (chan struct {})
330+ httpClient .pendingExchanges = make (map [* exchangeState ]struct {})
306331
307332 httpClient .DataTotal = & DataTotal
308333
0 commit comments