httpダウンロードに非アクティブタイムアウトを実装するにはどうすればよいですか

SwiftD:

私は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秒間変更されなかったときに名前付きエラーをスローしました。名前付きエラーを既存のエラー処理で拾い、そこからダウンロードを再試行する方がはるかに簡単でした。

私はおそらく私のアプローチでバックグラウンドで少なくとも1つのgoroutineをクラッシュさせます(後でこれをいくつかのシグナリングで修正する可能性があります)が、これは実行時間の短いアプリケーション(そのインストーラー)であるため、これは許容可能です(少なくとも許容可能)。

JimB:

手動でコピーを行うことは特に難しくありません。適切に実装する方法がわからない場合は、ioパッケージから数十行でコピーして、ニーズに合わせて変更します(ErrShortWritestdライブラリのio.Writerの実装が正しいと想定できるため、この句は削除しました)

これは、コピーコンテキストのような関数で、キャンセルコンテキストとアイドルタイムアウトパラメーターも必要です。読み取りが成功するたびに、キャンセルゴルーチンに続行して新しいタイマーを開始するように通知します。

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の呼び出し中にタイマーが発生した場合、とにかくキャンセルするのに十分近かったためですが、このコードが将来拡張される場合に備えて、コードを慣用的にすることはしばしば良いことです。

この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。

侵害の場合は、連絡してください[email protected]

編集
0

コメントを追加

0

関連記事

分類Dev

Flinkでキー付きウィンドウタイムアウトを実装するにはどうすればよいですか?

分類Dev

ドロップダウンアイテムにサブドローダウンアイテムを追加するにはどうすればよいですか?

分類Dev

ボタンでカウントダウンタイマーをアクティブにするにはどうすればよいですか?

分類Dev

GNOMEでアクティブなウィンドウのタイトルバーの色を変更するにはどうすればよいですか?

分類Dev

カスタムキーボードレイアウトをアクティブ化するにはどうすればよいですか

分類Dev

RxJava間隔/無限カウンターをKotlinFlowでリアクティブに実装するにはどうすればよいですか?

分類Dev

ブートストラップのドロップダウンアイテムに幅を設定するにはどうすればよいですか?

分類Dev

プラグイン内でMavenアーティファクトをダウンロードするにはどうすればよいですか?

分類Dev

AngularJS:Cookieに保存されているタイマーカウントダウンを使用して、ページをしばらく非アクティブにするにはどうすればよいですか?

分類Dev

EclipseMarsでマルチウィンドウパースペクティブレイアウトを保存するにはどうすればよいですか

分類Dev

ダウンロード時にリクエストからメディアタイプを取得するにはどうすればよいですか?

分類Dev

Google App Scriptのカスタムダイアログウィンドウからデータを取得するにはどうすればよいですか?

分類Dev

コンテンツにContentDispositionアタッチメントがある場合、ウィンドウロードコールバックを実装するにはどうすればよいですか?

分類Dev

ファイルをダウンロードした後にアクティビティを開始するにはどうすればよいですか?

分類Dev

ウェブサイトにクライアント側のTwitterログインを実装するにはどうすればよいですか?

分類Dev

スクリプトを使用してアーカイブをダウンロードおよび抽出するにはどうすればよいですか(後続のコマンドにダウンロードされたファイルを確実に指定するにはどうすればよいですか)。

分類Dev

タイムアウト付きのRunnableを実装するにはどうすればよいですか?

分類Dev

タイムアウト機能を実装するにはどうすればよいですか?

分類Dev

ブートストラップドロップダウン:ボタンに値を表示するようにアクティブリンクを設定するにはどうすればよいですか?

分類Dev

Bootstrapのアイテムメニューにサブメニュードロップダウンを作成するにはどうすればよいですか?

分類Dev

タブレイアウトアクティビティにアイコンを追加するにはどうすればよいですか

分類Dev

[選択]> [オプション]ドロップダウンでアイテムを非表示にするにはどうすればよいですか?

分類Dev

クロスライダーでブラウザがアクティブかどうかを確認するにはどうすればよいですか?

分類Dev

WPF Windowsテンプレートに基づいたウィンドウにアイテムを追加するにはどうすればよいですか?

分類Dev

RPi3のクロスコンパイルアプリケーションのメインウィンドウにボーダーフレームとタイトルバーを表示するにはどうすればよいですか?

分類Dev

ドロップダウンリストを文字インターフェイスに実装するにはどうすればよいですか?

分類Dev

Azure Pipelinesからビルドアーティファクトをダウンロードするにはどうすればよいですか?

分類Dev

Rマークダウンドキュメント全体でアイテムに自動的に番号を付けるにはどうすればよいですか?

分類Dev

Ubuntuアプリディレクトリからソフトウェアをダウンロードするにはどうすればよいですか?

Related 関連記事

  1. 1

    Flinkでキー付きウィンドウタイムアウトを実装するにはどうすればよいですか?

  2. 2

    ドロップダウンアイテムにサブドローダウンアイテムを追加するにはどうすればよいですか?

  3. 3

    ボタンでカウントダウンタイマーをアクティブにするにはどうすればよいですか?

  4. 4

    GNOMEでアクティブなウィンドウのタイトルバーの色を変更するにはどうすればよいですか?

  5. 5

    カスタムキーボードレイアウトをアクティブ化するにはどうすればよいですか

  6. 6

    RxJava間隔/無限カウンターをKotlinFlowでリアクティブに実装するにはどうすればよいですか?

  7. 7

    ブートストラップのドロップダウンアイテムに幅を設定するにはどうすればよいですか?

  8. 8

    プラグイン内でMavenアーティファクトをダウンロードするにはどうすればよいですか?

  9. 9

    AngularJS:Cookieに保存されているタイマーカウントダウンを使用して、ページをしばらく非アクティブにするにはどうすればよいですか?

  10. 10

    EclipseMarsでマルチウィンドウパースペクティブレイアウトを保存するにはどうすればよいですか

  11. 11

    ダウンロード時にリクエストからメディアタイプを取得するにはどうすればよいですか?

  12. 12

    Google App Scriptのカスタムダイアログウィンドウからデータを取得するにはどうすればよいですか?

  13. 13

    コンテンツにContentDispositionアタッチメントがある場合、ウィンドウロードコールバックを実装するにはどうすればよいですか?

  14. 14

    ファイルをダウンロードした後にアクティビティを開始するにはどうすればよいですか?

  15. 15

    ウェブサイトにクライアント側のTwitterログインを実装するにはどうすればよいですか?

  16. 16

    スクリプトを使用してアーカイブをダウンロードおよび抽出するにはどうすればよいですか(後続のコマンドにダウンロードされたファイルを確実に指定するにはどうすればよいですか)。

  17. 17

    タイムアウト付きのRunnableを実装するにはどうすればよいですか?

  18. 18

    タイムアウト機能を実装するにはどうすればよいですか?

  19. 19

    ブートストラップドロップダウン:ボタンに値を表示するようにアクティブリンクを設定するにはどうすればよいですか?

  20. 20

    Bootstrapのアイテムメニューにサブメニュードロップダウンを作成するにはどうすればよいですか?

  21. 21

    タブレイアウトアクティビティにアイコンを追加するにはどうすればよいですか

  22. 22

    [選択]> [オプション]ドロップダウンでアイテムを非表示にするにはどうすればよいですか?

  23. 23

    クロスライダーでブラウザがアクティブかどうかを確認するにはどうすればよいですか?

  24. 24

    WPF Windowsテンプレートに基づいたウィンドウにアイテムを追加するにはどうすればよいですか?

  25. 25

    RPi3のクロスコンパイルアプリケーションのメインウィンドウにボーダーフレームとタイトルバーを表示するにはどうすればよいですか?

  26. 26

    ドロップダウンリストを文字インターフェイスに実装するにはどうすればよいですか?

  27. 27

    Azure Pipelinesからビルドアーティファクトをダウンロードするにはどうすればよいですか?

  28. 28

    Rマークダウンドキュメント全体でアイテムに自動的に番号を付けるにはどうすればよいですか?

  29. 29

    Ubuntuアプリディレクトリからソフトウェアをダウンロードするにはどうすればよいですか?

ホットタグ

アーカイブ