gpt4 book ai didi

http - 如何在 http 下载上实现不活动超时

转载 作者:IT王子 更新时间:2023-10-29 00:45:59 24 4
gpt4 key购买 nike

我一直在阅读有关 HTTP 请求可用的各种超时的信息,它们似乎都是请求总时间的硬截止日期。

我正在运行一个 http 下载,我不想在初始握手之后实现硬超时,因为我对我的用户连接一无所知,并且不想在慢速连接时超时。我最理想的是在一段时间不活动后超时(x 秒内没有下载任何内容)。有什么方法可以内置执行此操作,还是我必须根据说明文件来中断?

工作代码有点难以隔离,但我认为这些是相关部分,还有另一个循环统计文件以提供进度,但我需要重构一下以使用它来中断下载:

// httspClientOnNetInterface returns an http client using the named network interface, (via proxy if passed)
func HttpsClientOnNetInterface(interfaceIP []byte, httpsProxy *Proxy) (*http.Client, error) {

log.Printf("Got IP addr : %s\n", string(interfaceIP))
// create address for the dialer
tcpAddr := &net.TCPAddr{
IP: interfaceIP,
}

// create the dialer & transport
netDialer := net.Dialer{
LocalAddr: tcpAddr,
}

var proxyURL *url.URL
var err error

if httpsProxy != nil {
proxyURL, err = url.Parse(httpsProxy.String())
if err != nil {
return nil, fmt.Errorf("Error parsing proxy connection string: %s", err)
}
}

httpTransport := &http.Transport{
Dial: netDialer.Dial,
Proxy: http.ProxyURL(proxyURL),
}

httpClient := &http.Client{
Transport: httpTransport,
}

return httpClient, nil
}

/*
StartDownloadWithProgress will initiate a download from a remote url to a local file,
providing download progress information
*/
func StartDownloadWithProgress(interfaceIP []byte, httpsProxy *Proxy, srcURL, dstFilepath string) (*Download, error) {

// start an http client on the selected net interface
httpClient, err := HttpsClientOnNetInterface(interfaceIP, httpsProxy)
if err != nil {
return nil, err
}

// grab the header
headResp, err := httpClient.Head(srcURL)
if err != nil {
log.Printf("error on head request (download size): %s", err)
return nil, err
}

// pull out total size
size, err := strconv.Atoi(headResp.Header.Get("Content-Length"))
if err != nil {
headResp.Body.Close()
return nil, err
}
headResp.Body.Close()

errChan := make(chan error)
doneChan := make(chan struct{})

// spawn the download process
go func(httpClient *http.Client, srcURL, dstFilepath string, errChan chan error, doneChan chan struct{}) {
resp, err := httpClient.Get(srcURL)
if err != nil {
errChan <- err
return
}
defer resp.Body.Close()

// create the file
outFile, err := os.Create(dstFilepath)
if err != nil {
errChan <- err
return
}
defer outFile.Close()

log.Println("starting copy")
// copy to file as the response arrives
_, err = io.Copy(outFile, resp.Body)

// return err
if err != nil {
log.Printf("\n Download Copy Error: %s \n", err.Error())
errChan <- err
return
}

doneChan <- struct{}{}

return
}(httpClient, srcURL, dstFilepath, errChan, doneChan)

// return Download
return (&Download{
updateFrequency: time.Microsecond * 500,
total: size,
errRecieve: errChan,
doneRecieve: doneChan,
filepath: dstFilepath,
}).Start(), nil
}

更新感谢所有参与其中的人。

我接受了 JimB 的回答,因为它似乎是一种完全可行的方法,比我选择的解决方案更通用(并且可能对任何在这里找到方法的人更有用)。

在我的例子中,我已经有一个监控文件大小的循环,所以当它在 x 秒内没有改变时,我抛出了一个命名错误。通过我现有的错误处理并从那里重试下载,我更容易找到命名错误。

我可能会用我的方法在后台崩溃至少一个 goroutine(我稍后可能会通过一些信号修复这个问题)但是因为这是一个短时间运行的应用程序(它是一个安装程序)所以这是可以接受的(至少可以容忍)

最佳答案

手动复制并不是特别困难。如果您不确定如何正确实现它,只需复制和修改 io 包中的几十行代码即可满足您的需要(我只删除了 ErrShortWrite 子句,因为我们可以假设标准库 io.Writer 实现是正确的)

这是一个类似复制工作的函数,它还带有一个取消上下文和一个空闲超时参数。每次成功读取时,它都会向取消 goroutine 发出信号以继续并启动新的计时器。

func idleTimeoutCopy(dst io.Writer, src io.Reader, timeout time.Duration,
ctx context.Context, cancel context.CancelFunc) (written int64, err error) {
read := make(chan int)
go func() {
for {
select {
case <-ctx.Done():
return
case <-time.After(timeout):
cancel()
case <-read:
}
}
}()

buf := make([]byte, 32*1024)
for {
nr, er := src.Read(buf)
if nr > 0 {
read <- nr
nw, ew := dst.Write(buf[0:nr])
written += int64(nw)
if ew != nil {
err = ew
break
}
}
if er != nil {
if er != io.EOF {
err = er
}
break
}
}
return written, err
}

虽然为了简洁起见我使用了 time.After,但重用 Timer 效率更高。这意味着要注意使用正确的重置模式,因为 Reset 函数的返回值已损坏:

    t := time.NewTimer(timeout)
for {
select {
case <-ctx.Done():
return
case <-t.C:
cancel()
case <-read:
if !t.Stop() {
<-t.C
}
t.Reset(timeout)
}
}

您可以在这里完全跳过调用 Stop ,因为在我看来,如果定时器在调用 Reset 时触发,那么无论如何它都足够接近取消,但是让代码是惯用的通常是好的,以防万一此代码将在未来扩展。

关于http - 如何在 http 下载上实现不活动超时,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46789554/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com