use chunked data for POST, set HTTP version to 1.1
[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 };