Skip to content

Commit 09a7a76

Browse files
committedMar 2, 2019
curl
1 parent 0f28ef0 commit 09a7a76

39 files changed

+3659
-537
lines changed
 

‎example/curl/curl.cc

Lines changed: 283 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,283 @@
1+
#include "httpcurl.h"
2+
3+
static void dummy(const std::shared_ptr<Channel> &)
4+
{
5+
6+
}
7+
8+
Request::Request(Curl *owner, const char *url, const char *data, size_t len)
9+
: owner(owner),
10+
curl(curl_easy_init())
11+
{
12+
setopt(CURLOPT_URL, url);
13+
setopt(CURLOPT_WRITEFUNCTION, &Request::writeData);
14+
setopt(CURLOPT_WRITEDATA, this);
15+
setopt(CURLOPT_HEADERFUNCTION, &Request::headerData);
16+
setopt(CURLOPT_HEADERDATA, this);
17+
setopt(CURLOPT_PRIVATE, this);
18+
setopt(CURLOPT_USERAGENT, "curl");
19+
setopt(CURLOPT_POST, 1L);
20+
setopt(CURLOPT_POSTFIELDS, data);
21+
setopt(CURLOPT_POSTFIELDSIZE, len);
22+
23+
LOG_DEBUG << curl << " " << url;
24+
curl_multi_add_handle(owner->getCurlm(), curl);
25+
}
26+
27+
Request::Request(Curl *owner, const char *url)
28+
: owner(owner),
29+
curl(curl_easy_init())
30+
{
31+
setopt(CURLOPT_URL, url);
32+
setopt(CURLOPT_WRITEFUNCTION, &Request::writeData);
33+
setopt(CURLOPT_WRITEDATA, this);
34+
setopt(CURLOPT_HEADERFUNCTION, &Request::headerData);
35+
setopt(CURLOPT_HEADERDATA, this);
36+
setopt(CURLOPT_PRIVATE, this);
37+
setopt(CURLOPT_USERAGENT, "curl");
38+
39+
LOG_DEBUG << curl << " " << url;
40+
curl_multi_add_handle(owner->getCurlm(), curl);
41+
}
42+
43+
Request::~Request()
44+
{
45+
assert(!channel || channel->isNoneEvent());
46+
curl_multi_remove_handle(owner->getCurlm(), curl);
47+
curl_easy_cleanup(curl);
48+
}
49+
50+
void Request::headerOnly()
51+
{
52+
setopt(CURLOPT_NOBODY, 1);
53+
}
54+
55+
void Request::setRange(const std::string_view range)
56+
{
57+
setopt(CURLOPT_RANGE, range.data());
58+
}
59+
60+
const char *Request::getEffectiveUrl()
61+
{
62+
const char *p = nullptr;
63+
curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &p);
64+
return p;
65+
}
66+
67+
const char *Request::getRedirectUrl()
68+
{
69+
const char *p = nullptr;
70+
curl_easy_getinfo(curl, CURLINFO_REDIRECT_URL, &p);
71+
return p;
72+
}
73+
74+
int Request::getResponseCode()
75+
{
76+
long code = 0;
77+
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code);
78+
return static_cast<int>(code);
79+
}
80+
81+
Channel *Request::setChannel(int fd)
82+
{
83+
assert(channel.get() == nullptr);
84+
channel.reset(new Channel(owner->getLoop(), fd));
85+
channel->setTie(shared_from_this());
86+
return channel.get();
87+
}
88+
89+
void Request::removeChannel()
90+
{
91+
channel->disableAll();
92+
channel->remove();
93+
owner->getLoop()->queueInLoop(std::bind(dummy, channel));
94+
channel.reset();
95+
}
96+
97+
void Request::done(int code)
98+
{
99+
if (doneCb)
100+
{
101+
doneCb(this, code);
102+
}
103+
}
104+
105+
void Request::dataCallback(const char *buffer, int len)
106+
{
107+
if (dataCb)
108+
{
109+
dataCb(this, buffer, len);
110+
}
111+
}
112+
113+
void Request::headerCallback(const char *buffer, int len)
114+
{
115+
if (headerCb)
116+
{
117+
headerCb(buffer, len);
118+
}
119+
}
120+
121+
size_t Request::writeData(char *buffer, size_t size, size_t nmemb, void *userp)
122+
{
123+
assert(size == 1);
124+
Request *req = static_cast<Request*>(userp);
125+
req->dataCallback(buffer, static_cast<int>(nmemb));
126+
return nmemb;
127+
}
128+
129+
size_t Request::headerData(char *buffer, size_t size, size_t nmemb, void *userp)
130+
{
131+
assert(size == 1);
132+
Request *req = static_cast<Request*>(userp);
133+
req->headerCallback(buffer, static_cast<int>(nmemb));
134+
return nmemb;
135+
}
136+
137+
// ==================================================================
138+
139+
void Curl::initialize(Option opt)
140+
{
141+
curl_global_init(opt == kCURLnossl ? CURL_GLOBAL_NOTHING : CURL_GLOBAL_SSL);
142+
}
143+
144+
int Curl::socketCallback(CURL *c, int fd, int what, void *userp, void *socketp)
145+
{
146+
Curl *curl = static_cast<Curl*>(userp);
147+
const char *whatstr[]={ "none", "IN", "OUT", "INOUT", "REMOVE" };
148+
LOG_DEBUG << "Curl::socketCallback [" << curl << "] - fd = " << fd
149+
<< " what = " << whatstr[what];
150+
Request *req = nullptr;
151+
curl_easy_getinfo(c, CURLINFO_PRIVATE, &req);
152+
assert(req->getCurl() == c);
153+
if (what == CURL_POLL_REMOVE)
154+
{
155+
Channel *ch = static_cast<Channel*>(socketp);
156+
assert(req->getChannel() == ch);
157+
req->removeChannel();
158+
ch = nullptr;
159+
curl_multi_assign(curl->curlm, fd, ch);
160+
}
161+
else
162+
{
163+
Channel *ch = static_cast<Channel*>(socketp);
164+
if (!ch)
165+
{
166+
ch = req->setChannel(fd);
167+
ch->setReadCallback(std::bind(&Curl::onRead, curl, fd));
168+
ch->setWriteCallback(std::bind(&Curl::onWrite, curl, fd));
169+
ch->enableReading();
170+
curl_multi_assign(curl->curlm, fd, ch);
171+
LOG_TRACE << "new channel for fd=" << fd;
172+
}
173+
174+
assert(req->getChannel() == ch);
175+
// update
176+
if (what & CURL_POLL_OUT)
177+
{
178+
ch->enableWriting();
179+
}
180+
else
181+
{
182+
ch->disableWriting();
183+
}
184+
}
185+
return 0;
186+
}
187+
188+
int Curl::timerCallback(CURLM *curlm, long ms, void *userp)
189+
{
190+
Curl *curl = static_cast<Curl*>(userp);
191+
LOG_DEBUG << curl << " " << ms << " ms";
192+
curl->loop->runAfter(static_cast<int>(ms)/1000.0, false, std::bind(&Curl::onTimer, curl));
193+
return 0;
194+
}
195+
196+
Curl::Curl(EventLoop *loop)
197+
: loop(loop),
198+
curlm(curl_multi_init()),
199+
runningHandles(0),
200+
prevRunningHandles(0)
201+
{
202+
curl_multi_setopt(curlm, CURLMOPT_SOCKETFUNCTION, &Curl::socketCallback);
203+
curl_multi_setopt(curlm, CURLMOPT_SOCKETDATA, this);
204+
curl_multi_setopt(curlm, CURLMOPT_TIMERFUNCTION, &Curl::timerCallback);
205+
curl_multi_setopt(curlm, CURLMOPT_TIMERDATA, this);
206+
}
207+
208+
Curl::~Curl()
209+
{
210+
curl_multi_cleanup(curlm);
211+
}
212+
213+
RequestPtr Curl::getUrl(std::string_view url)
214+
{
215+
RequestPtr req(new Request(this, url.data()));
216+
return req;
217+
}
218+
219+
RequestPtr Curl::getUrl(std::string_view url, std::string_view body)
220+
{
221+
RequestPtr req(new Request(this, url.data(), body.data(), body.size()));
222+
return req;
223+
}
224+
225+
void Curl::onTimer()
226+
{
227+
CURLMcode rc = CURLM_OK;
228+
do
229+
{
230+
LOG_TRACE;
231+
rc = curl_multi_socket_action(curlm, CURL_SOCKET_TIMEOUT, 0, &runningHandles);
232+
LOG_TRACE << rc << " " << runningHandles;
233+
} while (rc == CURLM_CALL_MULTI_PERFORM);
234+
checkFinish();
235+
}
236+
237+
void Curl::onRead(int fd)
238+
{
239+
CURLMcode rc = CURLM_OK;
240+
do
241+
{
242+
LOG_TRACE << fd;
243+
rc = curl_multi_socket_action(curlm, fd, CURL_POLL_IN, &runningHandles);
244+
LOG_TRACE << fd << " " << rc << " " << runningHandles;
245+
} while (rc == CURLM_CALL_MULTI_PERFORM);
246+
checkFinish();
247+
}
248+
249+
void Curl::onWrite(int fd)
250+
{
251+
CURLMcode rc = CURLM_OK;
252+
do
253+
{
254+
LOG_TRACE << fd;
255+
rc = curl_multi_socket_action(curlm, fd, CURL_POLL_OUT, &runningHandles);
256+
LOG_TRACE << fd << " " << rc << " " << runningHandles;
257+
} while (rc == CURLM_CALL_MULTI_PERFORM);
258+
checkFinish();
259+
}
260+
261+
void Curl::checkFinish()
262+
{
263+
if (prevRunningHandles > runningHandles || runningHandles == 0)
264+
{
265+
CURLMsg *msg = nullptr;
266+
int left = 0;
267+
while ( (msg = curl_multi_info_read(curlm, &left)) != nullptr)
268+
{
269+
if (msg->msg == CURLMSG_DONE)
270+
{
271+
CURL *c = msg->easy_handle;
272+
CURLcode res = msg->data.result;
273+
Request *req = nullptr;
274+
curl_easy_getinfo(c, CURLINFO_PRIVATE, &req);
275+
assert(req->getCurl() == c);
276+
LOG_TRACE << req << " done";
277+
req->done(res);
278+
}
279+
}
280+
}
281+
prevRunningHandles = runningHandles;
282+
}
283+

0 commit comments

Comments
 (0)
Please sign in to comment.