I'm using http.Client
for the client-side implementation of a long-poll:
resp, err := client.Post(url, "application/json", bytes.NewBuffer(jsonPostBytes))
if err != nil {
panic(err)
}
defer resp.Body.Close()
var results []*ResponseMessage
err = json.NewDecoder(resp.Body).Decode(&results) // code blocks here on long-poll
Is there a standard way to pre-empt/cancel the request from the client-side?
I imagine that calling resp.Body.Close()
would do it, but I'd have to call that from another goroutine, as the client is normally already blocked in reading the response of the long-poll.
I know that there is a way to set a timeout via http.Transport
, but my app logic need to do the cancellation based on a user action, not just a timeout.
Nope, client.Post is a handy wrapper for 90% of use-cases where request cancellation is not needed.
Probably it will be enough simply to reimplement your client to get access to underlying Transport object, which has CancelRequest() function.
Just a quick example:
package main
import (
"log"
"net/http"
"time"
)
func main() {
req, _ := http.NewRequest("GET", "http://google.com", nil)
tr := &http.Transport{} // TODO: copy defaults from http.DefaultTransport
client := &http.Client{Transport: tr}
c := make(chan error, 1)
go func() {
resp, err := client.Do(req)
// handle response ...
_ = resp
c <- err
}()
// Simulating user cancel request channel
user := make(chan struct{}, 0)
go func() {
time.Sleep(100 * time.Millisecond)
user <- struct{}{}
}()
for {
select {
case <-user:
log.Println("Cancelling request")
tr.CancelRequest(req)
case err := <-c:
log.Println("Client finished:", err)
return
}
}
}