LoginSignup
0
0

More than 5 years have passed since last update.

std.net.curlのプログレスメータが出る問題について

Posted at

こんにちは。
今日は、@alpha_kai_NETさんも報告されているstd.net.curlのプログレスメータの問題について、問題の原因と対策について話します。

また最後に書いていますが、どうかこの記事を見た人、報告お願いします。

問題の概要

次のコードをコンパイルして実行してみてください。標準出力にcurlの進捗状況を表すプログレスメータが出力されると思います。

import std.net.curl;

void main()
{
    auto http = HTTP();
    http = http.dup();      // ここを消すと無くなる
    get("dlang.org", http);
}

このようにHTTP.dup()によりインスタンスを複製した場合、プログレスメータが表示されてしまいます。

そのため、byLineAsyncなどのように内部で.dup()を呼び出している標準ライブラリの関数を使うと、なぜかコンソールにプログレスメータが表示されてしまいます。

問題の詳細

問題の原因はstd.net.curl.Curl.dup()メソッドのこの行です。以下に.dup()メソッドを引用させてもらいます。

/**
   Duplicate this handle.
   The new handle will have all options set as the one it was duplicated
   from. An exception to this is that all options that cannot be shared
   across threads are reset thereby making it safe to use the duplicate
   in a new thread.
*/
Curl dup()
{
    Curl copy;
    copy.handle = curl_easy_duphandle(handle);
    copy.stopped = false;

    with (CurlOption) {
        auto tt = TypeTuple!(file, writefunction, writeheader,
                             headerfunction, infile,
                             readfunction, ioctldata, ioctlfunction,
                             seekdata, seekfunction, sockoptdata,
                             sockoptfunction, opensocketdata,
                             opensocketfunction, noprogress,
                             progressdata, progressfunction,
                             debugdata, debugfunction,
                             interleavedata,
                             interleavefunction, chunk_data,
                             chunk_bgn_function, chunk_end_function,
                             fnmatch_data, fnmatch_function,
                             cookiejar, postfields);
        foreach(option; tt)
            copy.clear(option);
    }

    // The options are only supported by libcurl when it has been built
    // against certain versions of OpenSSL - if your libcurl uses an old
    // OpenSSL, or uses an entirely different SSL engine, attempting to
    // clear these normally will raise an exception
    copy.clearIfSupported(CurlOption.ssl_ctx_function);
    copy.clearIfSupported(CurlOption.ssh_keydata);

    // Enable for curl version > 7.21.7
    static if (LIBCURL_VERSION_MAJOR >= 7 &&
               LIBCURL_VERSION_MINOR >= 21 &&
               LIBCURL_VERSION_PATCH >= 7)
    {
        copy.clear(CurlOption.closesocketdata);
        copy.clear(CurlOption.closesocketfunction);
    }

    copy.set(CurlOption.nosignal, 1);

    // copy.clear(CurlOption.ssl_ctx_data); Let ssl function be shared
    // copy.clear(CurlOption.ssh_keyfunction); Let key function be shared

    /*
      Allow sharing of conv functions
      copy.clear(CurlOption.conv_to_network_function);
      copy.clear(CurlOption.conv_from_network_function);
      copy.clear(CurlOption.conv_from_utf8_function);
    */

    return copy;
}

with文内が問題の原因です。Curl.clear(option)というメソッドは、ポインタを設定するオプションoptionnull値を設定するメソッドです。しかし、alias ttのリストを見ると、noprogressが含まれています。

noprogresscURLのドキュメントを見る限り、01long型を設定するオプションのはずなので、Curl.clear(CurlOption.noprogress)は誤りです。

この誤りにより、HTTP.dup()の呼び出しでCurl.dup()が呼び出され、Curl.clear(CurlOption.noprogress)によりcurl_easy_setopt(curlHandle, CURLOPT_NOPROGRESS, null)されます。nullは整数では0ですから、CURLOPT_NOPROGRESS0が設定されてしまい、結果的にプログレスメータが表示されてしまいます。

問題の解決方法

alias ttのオプションのリストからnoprogressを消すことで解決できます。

もし、Phobosのコードの改変を待てない場合は次のような型をstd.net.curl.HTTPの代わりに使うことでその場しのぎをすることができます。

struct BugFixedHTTP
{
    static BugFixedHTTP opCall(const(char)[] url)
    {
        return BugFixedHTTP(HTTP(url));
    }


    static BugFixedHTTP opCall()
    {
        return BugFixedHTTP(HTTP());
    }


    static BugFixedHTTP opCall(HTTP http)
    {
        //this.http = http;
        BugFixedHTTP bfhttp;
        bfhttp.http = http;
        return bfhttp;
    }


    BugFixedHTTP dup()
    {
        HTTP conn = http.dup;
        conn.handle.set(CurlOption.noprogress, 1);
        return BugFixedHTTP(conn);
    }


    string encoding() @property 
    {
        return http.tupleof[0].charset;
    }


    HTTP http;
    alias http this;
}

さいごに

この記事を書いた理由は僕にはこの内容を英語を用いて説明する実力が無いためです。訓練だと思えば良いのでしょうが、どうしても自信がないので、誰か代わりにBugzillaに報告してGitHubにPull Request投げてもらえますか?

よろしくお願いします。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0