| 27 */ |
27 */ |
| 28 |
28 |
| 29 #include "httpclient.h" |
29 #include "httpclient.h" |
| 30 |
30 |
| 31 #include <cx/buffer.h> |
31 #include <cx/buffer.h> |
| |
32 #include <cx/string.h> |
| 32 #include <stdlib.h> |
33 #include <stdlib.h> |
| 33 #include <string.h> |
34 #include <string.h> |
| |
35 #include <errno.h> |
| 34 |
36 |
| 35 static int client_connected(EventHandler *ev, Event *event); |
37 static int client_connected(EventHandler *ev, Event *event); |
| |
38 static int client_io(EventHandler *ev, Event *event); |
| |
39 |
| |
40 static int client_send_request(HttpClient *client); |
| 36 |
41 |
| 37 HttpClient* http_client_new(EventHandler *ev) { |
42 HttpClient* http_client_new(EventHandler *ev) { |
| 38 CxMempool *mp = cxMempoolCreate(32, CX_MEMPOOL_TYPE_PURE); |
43 CxMempool *mp = cxMempoolCreate(32, CX_MEMPOOL_TYPE_PURE); |
| 39 if(!mp) { |
44 if(!mp) { |
| 40 return NULL; |
45 return NULL; |
| 50 cxMempoolFree(mp); |
55 cxMempoolFree(mp); |
| 51 return NULL; |
56 return NULL; |
| 52 } |
57 } |
| 53 |
58 |
| 54 memset(client, 0, sizeof(HttpClient)); |
59 memset(client, 0, sizeof(HttpClient)); |
| |
60 client->ev = ev; |
| 55 client->request_headers = req_headers; |
61 client->request_headers = req_headers; |
| 56 client->response_headers = resp_headers; |
62 client->response_headers = resp_headers; |
| |
63 |
| |
64 client->buffer.maxsize = HTTP_CLIENT_BUFFER_SIZE; |
| |
65 client->buffer.inbuf = malloc(HTTP_CLIENT_BUFFER_SIZE); |
| |
66 HttpParser *parser = http_parser_new2(1, &client->buffer, resp_headers); |
| |
67 if(!parser || !client->buffer.inbuf) { |
| |
68 http_client_free(client); |
| |
69 return NULL; |
| |
70 } |
| |
71 client->parser = parser; |
| 57 |
72 |
| 58 return client; |
73 return client; |
| 59 } |
74 } |
| 60 |
75 |
| 61 void http_client_free(HttpClient *client) { |
76 void http_client_free(HttpClient *client) { |
| 62 cxMempoolFree(client->mp); |
77 cxMempoolFree(client->mp); |
| 63 header_array_free(client->request_headers); |
78 header_array_free(client->request_headers); |
| |
79 http_parser_free(client->parser); |
| |
80 free(client->buffer.inbuf); |
| 64 free(client->addr); |
81 free(client->addr); |
| 65 free(client->method); |
82 free(client->method); |
| 66 free(client->uri); |
83 free(client->uri); |
| 67 free(client); |
84 free(client); |
| 68 } |
85 } |
| 102 memcpy(newvalue, str, len); |
119 memcpy(newvalue, str, len); |
| 103 newvalue[len] = 0; |
120 newvalue[len] = 0; |
| 104 *ptr = newvalue; |
121 *ptr = newvalue; |
| 105 } else { |
122 } else { |
| 106 *ptr = NULL; |
123 *ptr = NULL; |
| 107 return 0; |
124 } |
| 108 } |
125 return 0; |
| 109 } |
126 } |
| 110 |
127 |
| 111 int http_client_set_method_len(HttpClient *client, const char *method, size_t len) { |
128 int http_client_set_method_len(HttpClient *client, const char *method, size_t len) { |
| 112 return client_set_str(&client->method, method, len); |
129 return client_set_str(&client->method, method, len); |
| 113 } |
130 } |
| 147 } |
164 } |
| 148 if (fcntl(socketfd, F_SETFL, flags | O_NONBLOCK) != 0) { |
165 if (fcntl(socketfd, F_SETFL, flags | O_NONBLOCK) != 0) { |
| 149 close(socketfd); |
166 close(socketfd); |
| 150 return 1; |
167 return 1; |
| 151 } |
168 } |
| |
169 client->socketfd = socketfd; |
| 152 |
170 |
| 153 client->writeev.cookie = client; |
171 client->writeev.cookie = client; |
| 154 client->writeev.fn = client_connected; |
172 client->writeev.fn = client_connected; |
| 155 |
173 |
| 156 int ret = ev_pollout(client->ev, socketfd, &client->writeev); |
174 int ret = 1; |
| |
175 if(connect(socketfd, client->addr, client->addrlen)) { |
| |
176 int err = errno; |
| |
177 if(err == EINPROGRESS) { |
| |
178 ret = ev_pollout(client->ev, socketfd, &client->writeev); |
| |
179 } else { |
| |
180 log_ereport(LOG_FAILURE, "http-client-start: connect failed: %s", strerror(err)); |
| |
181 } |
| |
182 } else { |
| |
183 // TODO: call client_connected directly |
| |
184 } |
| |
185 |
| 157 if(ret) { |
186 if(ret) { |
| 158 close(socketfd); |
187 close(socketfd); |
| 159 } |
188 } |
| 160 return ret; |
189 return ret; |
| 161 } |
190 } |
| 195 HttpClient *client = event->cookie; |
224 HttpClient *client = event->cookie; |
| 196 if(create_req_buffer(client)) { |
225 if(create_req_buffer(client)) { |
| 197 // TODO: set error |
226 // TODO: set error |
| 198 return 0; // end |
227 return 0; // end |
| 199 } |
228 } |
| 200 |
229 event->fn = client_io; |
| 201 |
230 |
| 202 return 1; |
231 return client_io(ev, event); |
| 203 } |
232 } |
| 204 |
233 |
| 205 |
234 static int client_io(EventHandler *ev, Event *event) { |
| |
235 HttpClient *client = event->cookie; |
| |
236 if(client->req_buffer_pos < client->req_buffer_len) { |
| |
237 if(client_send_request(client)) { |
| |
238 if(client->error) { |
| |
239 return 0; // TODO: set error |
| |
240 } |
| |
241 return 1; |
| |
242 } |
| |
243 } |
| |
244 |
| |
245 // make sure to receive read-ready events in the future |
| |
246 event->events |= EVENT_POLLIN; |
| |
247 |
| |
248 |
| |
249 char *buffer; |
| |
250 size_t nbytes; |
| |
251 if(client->header_complete) { |
| |
252 buffer = client->buffer.inbuf; |
| |
253 nbytes = client->buffer.maxsize; |
| |
254 } else { |
| |
255 buffer = client->buffer.inbuf + client->buffer.pos; |
| |
256 nbytes = client->buffer.maxsize - client->buffer.cursize; |
| |
257 } |
| |
258 |
| |
259 |
| |
260 ssize_t r; |
| |
261 while((r = read(client->socketfd, buffer, nbytes)) > 0) { |
| |
262 client->buffer.cursize += r; |
| |
263 if(!client->header_complete) { |
| |
264 switch(http_parser_process(client->parser)) { |
| |
265 case 0: { // finish |
| |
266 if(!http_parser_validate(client->parser)) { |
| |
267 client->error = 1; |
| |
268 return 0; |
| |
269 } |
| |
270 |
| |
271 client->header_complete = 1; |
| |
272 if(client->response_start) { |
| |
273 cxmutstr msg = client->parser->msg; |
| |
274 char t = msg.ptr[msg.length]; |
| |
275 msg.ptr[msg.length] = 0; |
| |
276 int ret = client->response_start(client, client->parser->status, msg.ptr, client->response_start_userdata); |
| |
277 msg.ptr[msg.length] = t; |
| |
278 |
| |
279 // TODO: check ret |
| |
280 } |
| |
281 break; |
| |
282 } |
| |
283 case 1: { // need more data |
| |
284 continue; |
| |
285 } |
| |
286 case 2: { // error |
| |
287 client->error = 1; |
| |
288 return 0; |
| |
289 } |
| |
290 } |
| |
291 } |
| |
292 |
| |
293 // header complete |
| |
294 |
| |
295 char *out = client->buffer.inbuf + client->buffer.pos; |
| |
296 size_t len = client->buffer.cursize - client->buffer.pos; |
| |
297 |
| |
298 if(client->response_body_write) { |
| |
299 int ret = client->response_body_write(client, out, len, client->response_body_write_userdata); |
| |
300 // TODO: check ret |
| |
301 } |
| |
302 |
| |
303 client->buffer.pos = 0; |
| |
304 client->buffer.cursize = 0; |
| |
305 } |
| |
306 |
| |
307 |
| |
308 return 0; |
| |
309 } |
| |
310 |
| |
311 static int client_send_request(HttpClient *client) { |
| |
312 size_t nbytes = client->req_buffer_len - client->req_buffer_pos; |
| |
313 ssize_t w = write(client->socketfd, client->req_buffer + client->req_buffer_pos, nbytes); |
| |
314 if(w <= 0) { |
| |
315 if(errno != EAGAIN) { |
| |
316 // TODO: log correct host |
| |
317 log_ereport(LOG_VERBOSE, "http-client %s - %s: write failed: %s", "localhost", client->uri, strerror(errno)); |
| |
318 client->error = 1; |
| |
319 } |
| |
320 return 1; |
| |
321 } |
| |
322 |
| |
323 client->req_buffer_pos += w; |
| |
324 |
| |
325 return client->req_buffer_pos < client->req_buffer_len; |
| |
326 } |