1 module dast.net.curl; 2 3 import dast.net.curlapi : C = Curl, CurlInf, curl = curlAPI; 4 public import etc.c.curl : CurlError, CurlOption, CurlProto, CURL, CURLcode; 5 6 struct Curl { 7 C c; 8 alias c this; 9 10 this(CURL* h) { 11 c = C(h); 12 } 13 14 this(C curl) { 15 c = curl; 16 } 17 18 @disable this(); 19 20 @property { 21 void connTimeout(long secs) => set(CurlOption.connecttimeout, secs); 22 23 void timeout(long secs) => set(CurlOption.timeout, secs); 24 25 void nobody(bool v) => set(CurlOption.nobody, v); 26 27 void maxredirs(long v) => set(CurlOption.maxredirs, v); 28 29 long contentLength() => c.get(CurlInf.contentlengthdownload); 30 } 31 32 private: 33 void* ch; 34 } 35 36 struct CurlSet { 37 import etc.c.curl : CurlM, CurlMsg, CURLMsg; 38 39 this(Curl[] cs) { 40 mh = curl.multi_init(); 41 _rc = 1; 42 msgLeft = -1; 43 if (mh) { 44 foreach (c; cs) 45 curl.multi_add_handle(mh, c.handle); 46 } 47 } 48 49 this(this) { 50 ++_rc; 51 } 52 53 ~this() { 54 if (mh) { 55 if (--_rc == 0) 56 curl.multi_cleanup(mh); 57 } 58 } 59 60 @property { 61 auto handle() => mh; 62 63 bool running() => _running > 0; 64 65 bool empty() { 66 if (msgLeft < 0) 67 msg = curl.multi_info_read(mh, &msgLeft); 68 return msgLeft == 0; 69 } 70 } 71 72 auto tryWait(int timeout_ms = 1000) => curl.multi_wait(mh, null, 0, timeout_ms, null); 73 74 auto add(Curl c) => curl.multi_add_handle(mh, c.handle); 75 76 auto remove(Curl c) => curl.multi_remove_handle(mh, c.handle); 77 78 auto perform() => curl.multi_perform(mh, &_running); 79 80 auto run() { 81 while (_running > 0) { 82 auto rc = perform(); 83 if (rc != CurlM.call_multi_perform) 84 return rc; 85 } 86 return CurlM.ok; 87 } 88 89 private: 90 void* mh; 91 CURLMsg* msg; 92 uint _rc; 93 int _running; 94 int msgLeft; 95 } 96 97 version (unittest) private void testFunc(alias f)(in char[] src, string expected) { 98 char[256] buf = void; 99 assert(f(src, buf) == expected.length); 100 assert(buf[0 .. expected.length] == expected); 101 } 102 103 // TODO: @nogc nothrow 104 105 /// URI编码 106 int escapeTo(in char[] src, scope char[] dst) @trusted 107 in (src.length <= int.max) { 108 import etc.c.curl, 109 core.stdc.string; 110 111 dst[0 .. src.length] = src; 112 dst[src.length] = 0; 113 int len = -1; 114 auto p = curl.easy_escape(null, dst.ptr, cast(int)src.length); 115 if (p) { 116 len = cast(int)strlen(p); 117 if (len <= dst.length) 118 dst[0 .. len] = p[0 .. len]; 119 else 120 len = -1; 121 curl.free(p); 122 } 123 return len; 124 } 125 126 @safe unittest { 127 alias test = testFunc!escapeTo; 128 test("ab", "ab"); 129 test("a b", "a%20b"); 130 } 131 132 /// URI解码 133 int unescapeTo(in char[] src, scope char[] dst) @trusted 134 in (src.length <= int.max) 135 in (dst.length > src.length) { 136 import etc.c.curl; 137 138 if (src.ptr !is dst.ptr) 139 dst[0 .. src.length] = src; 140 dst[src.length] = 0; 141 int len = -1; 142 auto p = curl.easy_unescape(null, dst.ptr, cast(int)src.length, &len); 143 if (p) { 144 dst[0 .. len] = p[0 .. len]; 145 curl.free(p); 146 } 147 return len; 148 } 149 150 @safe unittest { 151 alias test = testFunc!unescapeTo; 152 153 test("ab", "ab"); 154 test("a%20b", "a b"); 155 }