ネットワーク(cURL)
ネットワークモジュール、特に std.net.curl の使い方についてまとめます。
HTTP通信などができます。
Examples:
HTTPでGET
import std.net.curl; import std.array: appender; import std.algorithm: countUntil; // ごく単純なGET auto contents1 = get("https://dlang.org"); // 細かな設定を行うGET auto client = HTTP(); // こんな感じでいろいろクライアントの設定を行う // (ちなみにこの設定はリダイレクトを30回まで行う設定) client.maxRedirects = 30; // get関数の2番目の引数に指定する auto contents2 = get("https://dlang.org", client); assert(contents2 == contents1); // 生データをGET auto contents3 = appender!(char[])(); client = HTTP(); client.url = "https://dlang.org"; // データが分割して送られてくるのをコールバックでハンドリング client.onReceive = (void[] buf) { contents3 ~= cast(byte[])buf; return buf.length; }; // GET client.method = HTTP.Method.get; // ここでネットワークと接続 client.perform(); // getで得たcontents1~2と、contents3の生データの内容は同一ではありません。 // getで得たものは、文字コードのエンコードが行われています。 // このため、エンコーダがデフォルトで存在していないShift_JISなどの // 文字コードのWEBページをGETしようとすると例外が発生します。 /* ※ 一致するかどうかが不明のため ここではコンパイルできるかどうかだけチェックします */ static assert(__traits(compiles, assert(contents1 == cast(const char[])contents3.data)));
Examples:
独自のHeaderを付けてGETリクエストを送る方法
外部APIを呼ぶ場合など、AuthorizationヘッダーでBearerトークンを渡す例です。
import std.net.curl : get, HTTP; const token = "0123456789"; // HTTPオブジェクトを作り、addRequestHeaderを呼ぶことで任意のヘッダーが付けられます auto http = HTTP(); http.addRequestHeader("Authorization", "Bearer " ~ token); auto content = get("http://httpbin.org/headers", http); // 以下、応答からリクエスト内容を検証します。 import std.json : parseJSON; const response = parseJSON(content).object(); const headers = response["headers"].object(); assert(headers["Authorization"].str == "Bearer " ~ token);
Examples:
www-form-urlencoding 形式でデータをPOSTする方法
import std.net.curl : post; // post 関数と第2引数にデータを指定して呼び出します。 // 第2引数にはForm形式のデータとして連想配列を指定でき、連想配列のキーと値がそのままペアでエンコードされます auto content = post("http://httpbin.org/anything", ["username" : "user", "password" : "pass"]); // 以下、応答からリクエスト内容を検証します。 import std.json : parseJSON; const response = parseJSON(content).object(); assert(response["method"].str == "POST"); const headers = response["headers"].object(); assert(headers["Content-Type"].str == "application/x-www-form-urlencoded"); const forms = response["form"].object(); assert(forms["username"].str == "user"); assert(forms["password"].str == "pass");
Examples:
JSON 形式のデータをPOSTする方法
import std.net.curl : post, HTTP; import std.json : JSONValue, toJSON; // HTTPヘッダーに application/json を設定します。 auto http = HTTP(); http.addRequestHeader("Content-Type", "application/json"); // 送信内容は文字列とするため、オブジェクトは文字列に一度変換します。 const request = JSONValue([ "name": JSONValue("Item"), "value": JSONValue(1.5) ]); const data = toJSON(request); // あとは通常のPOST操作と同じく、URL、データ、ヘッダーなどの情報をつけて post 関数を呼び出します。 auto content = post("http://httpbin.org/anything", data, http); // 以下、応答からリクエスト内容を検証します。 import std.json : parseJSON; const response = parseJSON(content).object(); const headers = response["headers"].object(); assert(headers["Content-Type"].str == "application/json"); const json = response["json"].object(); assert(json["name"].str == "Item"); assert(json["value"].floating == 1.5);
Examples:
文字コードの話
import std.net.curl; import std.array: appender; import std.exception: assertThrown; import std.algorithm: countUntil; // 試しに今どき珍しいEUC-JPを使用しているSeesaa WikiをGET! get("https://wiki.seesaa.jp/").assertThrown(); auto client = HTTP(); auto contents = appender!(char[])(); client.url = "https://wiki.seesaa.jp/"; client.onReceive = (void[] buf) { contents ~= cast(byte[])buf; return buf.length; }; client.perform(); auto buf = contents.data; // 生データならEUC-JPでもちゃんと受信できます。 // UTF-8ではないので、バイト列にEUC-JPが含まれるか検索してみます。 assert(countUntil(cast(const ubyte[])buf, cast(const ubyte[])"charset=EUC-JP"c) < buf.length); version (Windows) { // Windowsだとこのようにして文字コードを変換できます。 import std.windows.charset; // EUC-JPだと例外を投げるような文字列の検索も buf.countUntil("</body>").assertThrown; contents ~= "\0"; buf = contents.data; // 文字コード変換(EUC-JP(20932)からUTF-8)することで auto contents2 = fromMBSz(cast(immutable char*)buf.ptr, 20_932); // 検索できるようになります assert(contents2.countUntil("</body>") < contents2.length); } // TODO: Linuxなどでの文字コード変換方法 // TODO: std.encodingへの文字コード変換器登録方法
Examples:
認証付きProxyを通す
import etc.c.curl: CurlAuth, CurlOption, CurlProxy; import std.net.curl; auto client = HTTP(); // Proxyのホスト名を指定します client.proxy = "proxy.host.name"; // Proxyのポート番号を指定します client.proxyPort = 8080; // Proxyの種類を指定します。デフォルトはhttpです。 client.proxyType = CurlProxy.http; // Proxyの認証データのユーザー名を指定します。 client.setProxyAuthentication("username", "password"); // 認証方式を指定する場合はこう client.handle.set(CurlOption.proxyauth, CurlAuth.basic); // あとはgetするなど、ほかの操作と同じです /* ※ ここでは実在のURLやパスワードを指定していないので、 コンパイルできるかどうかだけチェックします */ static assert(__traits(compiles, get("http://hogehoge.net", client)));
Examples:
クライアント認証する
CurlOption.sslcertなどのオプションを使用します。
- WindowsではDMDやLDCにOpenSSLがリンクされていないlibcurl.dllが使われているようですが
クライアント認証にはOpenSSLが必要となりますので、OpenSSLがリンクされたlibcurl.dllを使用する必要があります。
- さらに、cURLの公式で配布している64bitのDLLは libcurl-x64.dll という名称ですが、std.net.curlが使用するDLLの名称はlibcurl.dllまたはcurl.dll固定です。 このため、ファイル名を変更する必要があります。
- また、OpenSSLのDLLlibcrypto-1_1-x64.dll、libssl-1_1-x64.dllなども用意します。
import etc.c.curl: CurlAuth, CurlOption, CurlProxy; import std.net.curl; auto client = HTTP(); // CA証明書のPEM形式ファイルを指定します client.caInfo = "cacert.pem"; // クライアント証明書に暗号化された秘密鍵がついている場合 { // 秘密鍵付きのクライアント証明書のPEM形式ファイルを指定します client.handle.set(CurlOption.sslcert, "clcert.pem"); // 形式はPEMです(デフォルトはPEM) client.handle.set(CurlOption.sslcerttype, "PEM"); // クライアント証明書に秘密鍵がついている場合、パスワードを指定します client.handle.set(CurlOption.keypasswd, "password"); } // クライアント証明書に秘密鍵が含まれておらず // 別途暗号化された秘密鍵を指定する場合 { // クライアント証明書のPEM形式ファイルを指定します client.handle.set(CurlOption.sslcert, "clcert.pem"); // クライアント証明書の形式を指定(デフォルトはPEM) client.handle.set(CurlOption.sslcerttype, "PEM"); // 暗号化された秘密鍵のPEM形式ファイルを指定します client.handle.set(CurlOption.sslkey, "key.pem"); // 秘密鍵の形式を指定(デフォルトはPEM) client.handle.set(CurlOption.sslkeytype, "PEM"); // 暗号化した秘密鍵を指定した場合、パスワードを指定 client.handle.set(CurlOption.keypasswd, "password"); } // クライアント証明書に秘密鍵が含まれておらず // 復号化された秘密鍵を指定する場合 { // クライアント証明書のPEM形式ファイルを指定します client.handle.set(CurlOption.sslcert, "clcert.pem"); // クライアント証明書の形式を指定(デフォルトはPEM) client.handle.set(CurlOption.sslcerttype, "PEM"); // 復号化された秘密鍵のPEM形式ファイルを指定します client.handle.set(CurlOption.sslkey, "decrypted-key.pem"); // 秘密鍵の形式を指定(デフォルトはPEM) client.handle.set(CurlOption.sslkeytype, "PEM"); // 復号化された秘密鍵を指定した場合、パスワードは不要 //client.handle.set(CurlOption.keypasswd, "不要"); } // あとはgetするなど、ほかの操作と同じです /* ※ ここでは実在のファイルを指定していないので、 コンパイルできるかどうかだけチェックします */ static assert(__traits(compiles, get("http://hogehoge.net", client)));