こんにちは。
今日は、@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)
というメソッドは、ポインタを設定するオプションoption
にnull
値を設定するメソッドです。しかし、alias tt
のリストを見ると、noprogress
が含まれています。
noprogress
はcURLのドキュメントを見る限り、0
か1
のlong
型を設定するオプションのはずなので、Curl.clear(CurlOption.noprogress)
は誤りです。
この誤りにより、HTTP.dup()
の呼び出しでCurl.dup()
が呼び出され、Curl.clear(CurlOption.noprogress)
によりcurl_easy_setopt(curlHandle, CURLOPT_NOPROGRESS, null)
されます。null
は整数では0
ですから、CURLOPT_NOPROGRESS
に0
が設定されてしまい、結果的にプログレスメータが表示されてしまいます。
問題の解決方法
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投げてもらえますか?
よろしくお願いします。