8ff2e0ae20fde3e03e30049e37de87495c8279be
[project/uclient.git] / uclient-http.c
1 #include <stdio.h>
2 #include <ctype.h>
3 #include <unistd.h>
4
5 #include <libubox/ustream.h>
6 #include <libubox/ustream-ssl.h>
7 #include <libubox/usock.h>
8 #include <libubox/blobmsg.h>
9
10 #include "uclient.h"
11 #include "uclient-utils.h"
12 #include "uclient-backend.h"
13
14 static struct ustream_ssl_ctx *ssl_ctx;
15
16 enum request_type {
17 REQ_GET,
18 REQ_HEAD,
19 REQ_POST,
20 __REQ_MAX
21 };
22
23 enum http_state {
24 HTTP_STATE_INIT,
25 HTTP_STATE_HEADERS_SENT,
26 HTTP_STATE_REQUEST_DONE,
27 HTTP_STATE_RECV_HEADERS,
28 HTTP_STATE_RECV_DATA,
29 HTTP_STATE_ERROR,
30 };
31
32 static const char * const request_types[__REQ_MAX] = {
33 [REQ_GET] = "GET",
34 [REQ_HEAD] = "HEAD",
35 [REQ_POST] = "POST",
36 };
37
38 struct uclient_http {
39 struct uclient uc;
40
41 struct ustream *us;
42
43 struct ustream_fd ufd;
44 struct ustream_ssl ussl;
45
46 bool ssl;
47 enum request_type req_type;
48 enum http_state state;
49
50 struct blob_buf headers;
51 struct blob_buf meta;
52 };
53
54 enum {
55 PREFIX_HTTP,
56 PREFIX_HTTPS,
57 __PREFIX_MAX,
58 };
59
60 static const char * const uclient_http_prefix[] = {
61 [PREFIX_HTTP] = "http://",
62 [PREFIX_HTTPS] = "https://",
63 [__PREFIX_MAX] = NULL
64 };
65
66 static int uclient_do_connect(struct uclient_http *uh, const char *port)
67 {
68 int fd;
69
70 if (uh->uc.url->port)
71 port = uh->uc.url->port;
72
73 fd = usock(USOCK_TCP | USOCK_NONBLOCK, uh->uc.url->host, port);
74 if (fd < 0)
75 return -1;
76
77 ustream_fd_init(&uh->ufd, fd);
78 return 0;
79 }
80
81 static void uclient_notify_eof(struct uclient_http *uh)
82 {
83 struct ustream *us = uh->us;
84
85 if (!us->eof && !us->write_error)
86 return;
87
88 if (ustream_pending_data(us, false))
89 return;
90
91 uclient_backend_set_eof(&uh->uc);
92 }
93
94 static void uclient_parse_http_line(struct uclient_http *uh, char *data)
95 {
96 char *name;
97 char *sep;
98
99 if (uh->state == HTTP_STATE_REQUEST_DONE) {
100 uh->state = HTTP_STATE_RECV_HEADERS;
101 return;
102 }
103
104 if (!*data) {
105 uh->state = HTTP_STATE_RECV_DATA;
106 uh->uc.meta = uh->meta.head;
107 if (uh->uc.cb->header_done)
108 uh->uc.cb->header_done(&uh->uc);
109 return;
110 }
111
112 sep = strchr(data, ':');
113 if (!sep)
114 return;
115
116 *(sep++) = 0;
117
118 for (name = data; *name; name++)
119 *name = tolower(*name);
120
121 name = data;
122 while (isspace(*sep))
123 sep++;
124
125 blobmsg_add_string(&uh->meta, name, sep);
126 }
127
128 static void __uclient_notify_read(struct uclient_http *uh)
129 {
130 struct uclient *uc = &uh->uc;
131 char *data;
132 int len;
133
134 if (uh->state < HTTP_STATE_REQUEST_DONE)
135 return;
136
137 data = ustream_get_read_buf(uh->us, &len);
138 if (!data || !len)
139 return;
140
141 if (uh->state < HTTP_STATE_RECV_DATA) {
142 char *sep;
143 int cur_len;
144
145 do {
146 sep = strstr(data, "\r\n");
147 if (!sep)
148 break;
149
150 /* Check for multi-line HTTP headers */
151 if (sep > data) {
152 if (!sep[2])
153 return;
154
155 if (isspace(sep[2]) && sep[2] != '\r') {
156 sep[0] = ' ';
157 sep[1] = ' ';
158 continue;
159 }
160 }
161
162 *sep = 0;
163 cur_len = sep + 2 - data;
164 uclient_parse_http_line(uh, data);
165 ustream_consume(uh->us, cur_len);
166 len -= cur_len;
167
168 data = ustream_get_read_buf(uh->us, &len);
169 } while (uh->state < HTTP_STATE_RECV_DATA);
170
171 if (!len)
172 return;
173 }
174
175 if (uh->state == HTTP_STATE_RECV_DATA && uc->cb->data_read)
176 uc->cb->data_read(uc);
177 }
178
179 static void uclient_notify_read(struct ustream *us, int bytes)
180 {
181 struct uclient_http *uh = container_of(us, struct uclient_http, ufd.stream);
182
183 __uclient_notify_read(uh);
184 }
185
186 static void uclient_notify_state(struct ustream *us)
187 {
188 struct uclient_http *uh = container_of(us, struct uclient_http, ufd.stream);
189
190 uclient_notify_eof(uh);
191 }
192
193 static int uclient_setup_http(struct uclient_http *uh)
194 {
195 struct ustream *us = &uh->ufd.stream;
196 int ret;
197
198 uh->us = us;
199 us->string_data = true;
200 us->notify_state = uclient_notify_state;
201 us->notify_read = uclient_notify_read;
202
203 ret = uclient_do_connect(uh, "80");
204 if (ret)
205 return ret;
206
207 return 0;
208 }
209
210 static void uclient_ssl_notify_read(struct ustream *us, int bytes)
211 {
212 struct uclient_http *uh = container_of(us, struct uclient_http, ussl.stream);
213
214 __uclient_notify_read(uh);
215 }
216
217 static void uclient_ssl_notify_state(struct ustream *us)
218 {
219 struct uclient_http *uh = container_of(us, struct uclient_http, ussl.stream);
220
221 uclient_notify_eof(uh);
222 }
223
224 static int uclient_setup_https(struct uclient_http *uh)
225 {
226 struct ustream *us = &uh->ussl.stream;
227 int ret;
228
229 uh->ssl = true;
230 uh->us = us;
231
232 ret = uclient_do_connect(uh, "443");
233 if (ret)
234 return ret;
235
236 if (!ssl_ctx)
237 ssl_ctx = ustream_ssl_context_new(false);
238
239 us->string_data = true;
240 us->notify_state = uclient_ssl_notify_state;
241 us->notify_read = uclient_ssl_notify_read;
242 ustream_ssl_init(&uh->ussl, &uh->ufd.stream, ssl_ctx, false);
243
244 return 0;
245 }
246
247 static void uclient_http_disconnect(struct uclient_http *uh)
248 {
249 uclient_backend_reset_state(&uh->uc);
250
251 if (!uh->us)
252 return;
253
254 if (uh->ssl)
255 ustream_free(&uh->ussl.stream);
256 ustream_free(&uh->ufd.stream);
257 close(uh->ufd.fd.fd);
258 uh->us = NULL;
259 }
260
261 static int uclient_http_connect(struct uclient *cl)
262 {
263 struct uclient_http *uh = container_of(cl, struct uclient_http, uc);
264
265 uclient_http_disconnect(uh);
266 blob_buf_init(&uh->meta, 0);
267
268 uh->ssl = cl->url->prefix == PREFIX_HTTPS;
269 uh->state = HTTP_STATE_INIT;
270
271 if (uh->ssl)
272 return uclient_setup_https(uh);
273 else
274 return uclient_setup_http(uh);
275 }
276
277 static struct uclient *uclient_http_alloc(void)
278 {
279 struct uclient_http *uh;
280
281 uh = calloc_a(sizeof(*uh));
282 blob_buf_init(&uh->headers, 0);
283
284 return &uh->uc;
285 }
286
287 static void uclient_http_free(struct uclient *cl)
288 {
289 struct uclient_http *uh = container_of(cl, struct uclient_http, uc);
290
291 uclient_http_disconnect(uh);
292 blob_buf_free(&uh->headers);
293 blob_buf_free(&uh->meta);
294 free(uh);
295 }
296
297 int
298 uclient_http_set_request_type(struct uclient *cl, const char *type)
299 {
300 struct uclient_http *uh = container_of(cl, struct uclient_http, uc);
301 int i;
302
303 if (cl->backend != &uclient_backend_http)
304 return -1;
305
306 if (uh->state > HTTP_STATE_INIT)
307 return -1;
308
309 for (i = 0; i < ARRAY_SIZE(request_types); i++) {
310 if (strcmp(request_types[i], type) != 0)
311 continue;
312
313 uh->req_type = i;
314 return 0;
315 }
316
317 return -1;
318 }
319
320 int
321 uclient_http_reset_headers(struct uclient *cl, const char *name, const char *value)
322 {
323 struct uclient_http *uh = container_of(cl, struct uclient_http, uc);
324
325 blob_buf_init(&uh->headers, 0);
326
327 return 0;
328 }
329
330 int
331 uclient_http_set_header(struct uclient *cl, const char *name, const char *value)
332 {
333 struct uclient_http *uh = container_of(cl, struct uclient_http, uc);
334
335 if (cl->backend != &uclient_backend_http)
336 return -1;
337
338 if (uh->state > HTTP_STATE_INIT)
339 return -1;
340
341 blobmsg_add_string(&uh->headers, name, value);
342 return 0;
343 }
344
345 #define ustream_printf(us, ...) do { \
346 fprintf(stderr, "send: " __VA_ARGS__); \
347 ustream_printf(us, __VA_ARGS__); \
348 } while (0)
349
350
351 static void
352 uclient_http_send_headers(struct uclient_http *uh)
353 {
354 struct uclient_url *url = uh->uc.url;
355 struct blob_attr *cur;
356 int rem;
357
358 if (uh->state >= HTTP_STATE_HEADERS_SENT)
359 return;
360
361 ustream_printf(uh->us,
362 "%s /%s HTTP/1.1\r\n"
363 "Host: %s\r\n"
364 "Connection: close\r\n",
365 request_types[uh->req_type],
366 url->location, url->host);
367
368 blobmsg_for_each_attr(cur, uh->headers.head, rem)
369 ustream_printf(uh->us, "%s: %s\n", blobmsg_name(cur), (char *) blobmsg_data(cur));
370
371 if (url->auth) {
372 int auth_len = strlen(url->auth);
373 char *auth_buf;
374
375 if (auth_len > 512)
376 return;
377
378 auth_buf = alloca(base64_len(auth_len) + 1);
379 base64_encode(url->auth, auth_len, auth_buf);
380 ustream_printf(uh->us, "Authorization: Basic %s\r\n", auth_buf);
381 }
382
383 if (uh->req_type == REQ_POST)
384 ustream_printf(uh->us, "Transfer-Encoding: chunked\r\n");
385
386 ustream_printf(uh->us, "\r\n");
387 }
388
389 static int
390 uclient_http_send_data(struct uclient *cl, char *buf, unsigned int len)
391 {
392 struct uclient_http *uh = container_of(cl, struct uclient_http, uc);
393
394 if (uh->state >= HTTP_STATE_REQUEST_DONE)
395 return -1;
396
397 uclient_http_send_headers(uh);
398
399 ustream_printf(uh->us, "%X\r\n", len);
400 ustream_write(uh->us, buf, len, false);
401 ustream_printf(uh->us, "\r\n");
402
403 return len;
404 }
405
406 static int
407 uclient_http_request_done(struct uclient *cl)
408 {
409 struct uclient_http *uh = container_of(cl, struct uclient_http, uc);
410
411 if (uh->state >= HTTP_STATE_REQUEST_DONE)
412 return -1;
413
414 uclient_http_send_headers(uh);
415 uh->state = HTTP_STATE_REQUEST_DONE;
416
417 return 0;
418 }
419
420 static int
421 uclient_http_read(struct uclient *cl, char *buf, unsigned int len)
422 {
423 struct uclient_http *uh = container_of(cl, struct uclient_http, uc);
424 int data_len;
425 char *data;
426
427 if (uh->state < HTTP_STATE_RECV_DATA)
428 return 0;
429
430 data = ustream_get_read_buf(uh->us, &data_len);
431 if (!data || !data_len)
432 return 0;
433
434 if (len > data_len)
435 len = data_len;
436
437 memcpy(buf, data, len);
438 ustream_consume(uh->us, len);
439 uclient_notify_eof(uh);
440
441 return len;
442 }
443
444 const struct uclient_backend uclient_backend_http __hidden = {
445 .prefix = uclient_http_prefix,
446
447 .alloc = uclient_http_alloc,
448 .free = uclient_http_free,
449 .connect = uclient_http_connect,
450
451 .read = uclient_http_read,
452 .write = uclient_http_send_data,
453 .request = uclient_http_request_done,
454 };