5 #include <libubox/ustream.h>
6 #include <libubox/ustream-ssl.h>
7 #include <libubox/usock.h>
8 #include <libubox/blobmsg.h>
11 #include "uclient-utils.h"
12 #include "uclient-backend.h"
14 static struct ustream_ssl_ctx
*ssl_ctx
;
31 HTTP_STATE_HEADERS_SENT
,
32 HTTP_STATE_REQUEST_DONE
,
33 HTTP_STATE_RECV_HEADERS
,
38 static const char * const request_types
[__REQ_MAX
] = {
49 struct ustream_fd ufd
;
50 struct ustream_ssl ussl
;
54 bool connection_close
;
55 enum request_type req_type
;
56 enum http_state state
;
58 enum auth_type auth_type
;
64 struct blob_buf headers
;
74 static const char * const uclient_http_prefix
[] = {
75 [PREFIX_HTTP
] = "http://",
76 [PREFIX_HTTPS
] = "https://",
80 static int uclient_do_connect(struct uclient_http
*uh
, const char *port
)
85 port
= uh
->uc
.url
->port
;
87 fd
= usock(USOCK_TCP
| USOCK_NONBLOCK
, uh
->uc
.url
->host
, port
);
91 ustream_fd_init(&uh
->ufd
, fd
);
95 static void uclient_http_disconnect(struct uclient_http
*uh
)
101 ustream_free(&uh
->ussl
.stream
);
102 ustream_free(&uh
->ufd
.stream
);
103 close(uh
->ufd
.fd
.fd
);
107 static void uclient_http_free_url_state(struct uclient
*cl
)
109 struct uclient_http
*uh
= container_of(cl
, struct uclient_http
, uc
);
111 uh
->auth_type
= AUTH_TYPE_UNKNOWN
;
114 uclient_http_disconnect(uh
);
117 static void uclient_notify_eof(struct uclient_http
*uh
)
119 struct ustream
*us
= uh
->us
;
122 if (!us
->eof
&& !us
->write_error
)
125 if (ustream_pending_data(us
, false))
129 uclient_backend_set_eof(&uh
->uc
);
131 if (uh
->connection_close
)
132 uclient_http_disconnect(uh
);
135 static void uclient_http_reset_state(struct uclient_http
*uh
)
137 uclient_backend_reset_state(&uh
->uc
);
138 uh
->read_chunked
= -1;
139 uh
->content_length
= -1;
141 uh
->connection_close
= false;
142 uh
->state
= HTTP_STATE_INIT
;
144 if (uh
->auth_type
== AUTH_TYPE_UNKNOWN
&& !uh
->uc
.url
->auth
)
145 uh
->auth_type
= AUTH_TYPE_NONE
;
148 static void uclient_http_init_request(struct uclient_http
*uh
)
150 uclient_http_reset_state(uh
);
151 blob_buf_init(&uh
->meta
, 0);
154 static enum auth_type
155 uclient_http_update_auth_type(struct uclient_http
*uh
)
158 return AUTH_TYPE_NONE
;
160 if (!strncasecmp(uh
->auth_str
, "basic", 5))
161 return AUTH_TYPE_BASIC
;
163 return AUTH_TYPE_NONE
;
166 static void uclient_http_process_headers(struct uclient_http
*uh
)
169 HTTP_HDR_TRANSFER_ENCODING
,
171 HTTP_HDR_CONTENT_LENGTH
,
175 static const struct blobmsg_policy hdr_policy
[__HTTP_HDR_MAX
] = {
176 #define hdr(_name) { .name = _name, .type = BLOBMSG_TYPE_STRING }
177 [HTTP_HDR_TRANSFER_ENCODING
] = hdr("transfer-encoding"),
178 [HTTP_HDR_CONNECTION
] = hdr("connection"),
179 [HTTP_HDR_CONTENT_LENGTH
] = hdr("content-length"),
180 [HTTP_HDR_AUTH
] = hdr("www-authenticate"),
183 struct blob_attr
*tb
[__HTTP_HDR_MAX
];
184 struct blob_attr
*cur
;
186 blobmsg_parse(hdr_policy
, __HTTP_HDR_MAX
, tb
, blob_data(uh
->meta
.head
), blob_len(uh
->meta
.head
));
188 cur
= tb
[HTTP_HDR_TRANSFER_ENCODING
];
189 if (cur
&& strstr(blobmsg_data(cur
), "chunked"))
190 uh
->read_chunked
= 0;
192 cur
= tb
[HTTP_HDR_CONNECTION
];
193 if (cur
&& strstr(blobmsg_data(cur
), "close"))
194 uh
->connection_close
= true;
196 cur
= tb
[HTTP_HDR_CONTENT_LENGTH
];
198 uh
->content_length
= strtoul(blobmsg_data(cur
), NULL
, 10);
200 cur
= tb
[HTTP_HDR_AUTH
];
203 uh
->auth_str
= strdup(blobmsg_data(cur
));
206 uh
->auth_type
= uclient_http_update_auth_type(uh
);
210 uclient_http_add_auth_header(struct uclient_http
*uh
)
212 struct uclient_url
*url
= uh
->uc
.url
;
219 auth_len
= strlen(url
->auth
);
223 auth_buf
= alloca(base64_len(auth_len
) + 1);
224 base64_encode(url
->auth
, auth_len
, auth_buf
);
225 ustream_printf(uh
->us
, "Authorization: Basic %s\r\n", auth_buf
);
229 uclient_http_send_headers(struct uclient_http
*uh
)
231 struct uclient_url
*url
= uh
->uc
.url
;
232 struct blob_attr
*cur
;
233 enum request_type req_type
= uh
->req_type
;
236 if (uh
->state
>= HTTP_STATE_HEADERS_SENT
)
239 if (uh
->auth_type
== AUTH_TYPE_UNKNOWN
)
242 ustream_printf(uh
->us
,
243 "%s /%s HTTP/1.1\r\n"
245 request_types
[req_type
],
246 url
->location
, url
->host
);
248 blobmsg_for_each_attr(cur
, uh
->headers
.head
, rem
)
249 ustream_printf(uh
->us
, "%s: %s\n", blobmsg_name(cur
), (char *) blobmsg_data(cur
));
251 if (uh
->req_type
== REQ_POST
)
252 ustream_printf(uh
->us
, "Transfer-Encoding: chunked\r\n");
254 uclient_http_add_auth_header(uh
);
256 ustream_printf(uh
->us
, "\r\n");
259 static void uclient_http_headers_complete(struct uclient_http
*uh
)
261 enum auth_type auth_type
= uh
->auth_type
;
263 uh
->state
= HTTP_STATE_RECV_DATA
;
264 uh
->uc
.meta
= uh
->meta
.head
;
265 uclient_http_process_headers(uh
);
267 if (auth_type
== AUTH_TYPE_UNKNOWN
) {
268 uclient_http_init_request(uh
);
269 uclient_http_send_headers(uh
);
270 uh
->state
= HTTP_STATE_REQUEST_DONE
;
274 if (uh
->uc
.cb
->header_done
)
275 uh
->uc
.cb
->header_done(&uh
->uc
);
277 if (uh
->req_type
== REQ_HEAD
) {
279 uclient_notify_eof(uh
);
283 static void uclient_parse_http_line(struct uclient_http
*uh
, char *data
)
288 if (uh
->state
== HTTP_STATE_REQUEST_DONE
) {
289 uh
->state
= HTTP_STATE_RECV_HEADERS
;
294 uclient_http_headers_complete(uh
);
298 sep
= strchr(data
, ':');
304 for (name
= data
; *name
; name
++)
305 *name
= tolower(*name
);
308 while (isspace(*sep
))
311 blobmsg_add_string(&uh
->meta
, name
, sep
);
314 static void __uclient_notify_read(struct uclient_http
*uh
)
316 struct uclient
*uc
= &uh
->uc
;
320 if (uh
->state
< HTTP_STATE_REQUEST_DONE
)
323 data
= ustream_get_read_buf(uh
->us
, &len
);
327 if (uh
->state
< HTTP_STATE_RECV_DATA
) {
332 sep
= strstr(data
, "\r\n");
336 /* Check for multi-line HTTP headers */
341 if (isspace(sep
[2]) && sep
[2] != '\r') {
349 cur_len
= sep
+ 2 - data
;
350 uclient_parse_http_line(uh
, data
);
351 ustream_consume(uh
->us
, cur_len
);
354 data
= ustream_get_read_buf(uh
->us
, &len
);
355 } while (data
&& uh
->state
< HTTP_STATE_RECV_DATA
);
361 if (uh
->state
== HTTP_STATE_RECV_DATA
&& uc
->cb
->data_read
)
362 uc
->cb
->data_read(uc
);
365 static void uclient_notify_read(struct ustream
*us
, int bytes
)
367 struct uclient_http
*uh
= container_of(us
, struct uclient_http
, ufd
.stream
);
369 __uclient_notify_read(uh
);
372 static void uclient_notify_state(struct ustream
*us
)
374 struct uclient_http
*uh
= container_of(us
, struct uclient_http
, ufd
.stream
);
376 uclient_notify_eof(uh
);
379 static int uclient_setup_http(struct uclient_http
*uh
)
381 struct ustream
*us
= &uh
->ufd
.stream
;
385 us
->string_data
= true;
386 us
->notify_state
= uclient_notify_state
;
387 us
->notify_read
= uclient_notify_read
;
389 ret
= uclient_do_connect(uh
, "80");
396 static void uclient_ssl_notify_read(struct ustream
*us
, int bytes
)
398 struct uclient_http
*uh
= container_of(us
, struct uclient_http
, ussl
.stream
);
400 __uclient_notify_read(uh
);
403 static void uclient_ssl_notify_state(struct ustream
*us
)
405 struct uclient_http
*uh
= container_of(us
, struct uclient_http
, ussl
.stream
);
407 uclient_notify_eof(uh
);
410 static int uclient_setup_https(struct uclient_http
*uh
)
412 struct ustream
*us
= &uh
->ussl
.stream
;
418 ret
= uclient_do_connect(uh
, "443");
423 ssl_ctx
= ustream_ssl_context_new(false);
425 us
->string_data
= true;
426 us
->notify_state
= uclient_ssl_notify_state
;
427 us
->notify_read
= uclient_ssl_notify_read
;
428 ustream_ssl_init(&uh
->ussl
, &uh
->ufd
.stream
, ssl_ctx
, false);
433 static int uclient_http_connect(struct uclient
*cl
)
435 struct uclient_http
*uh
= container_of(cl
, struct uclient_http
, uc
);
437 uclient_http_init_request(uh
);
442 uh
->ssl
= cl
->url
->prefix
== PREFIX_HTTPS
;
445 return uclient_setup_https(uh
);
447 return uclient_setup_http(uh
);
450 static struct uclient
*uclient_http_alloc(void)
452 struct uclient_http
*uh
;
454 uh
= calloc_a(sizeof(*uh
));
455 blob_buf_init(&uh
->headers
, 0);
460 static void uclient_http_free(struct uclient
*cl
)
462 struct uclient_http
*uh
= container_of(cl
, struct uclient_http
, uc
);
464 uclient_http_free_url_state(cl
);
465 blob_buf_free(&uh
->headers
);
466 blob_buf_free(&uh
->meta
);
471 uclient_http_set_request_type(struct uclient
*cl
, const char *type
)
473 struct uclient_http
*uh
= container_of(cl
, struct uclient_http
, uc
);
476 if (cl
->backend
!= &uclient_backend_http
)
479 if (uh
->state
> HTTP_STATE_INIT
)
482 for (i
= 0; i
< ARRAY_SIZE(request_types
); i
++) {
483 if (strcmp(request_types
[i
], type
) != 0)
494 uclient_http_reset_headers(struct uclient
*cl
, const char *name
, const char *value
)
496 struct uclient_http
*uh
= container_of(cl
, struct uclient_http
, uc
);
498 blob_buf_init(&uh
->headers
, 0);
504 uclient_http_set_header(struct uclient
*cl
, const char *name
, const char *value
)
506 struct uclient_http
*uh
= container_of(cl
, struct uclient_http
, uc
);
508 if (cl
->backend
!= &uclient_backend_http
)
511 if (uh
->state
> HTTP_STATE_INIT
)
514 blobmsg_add_string(&uh
->headers
, name
, value
);
519 uclient_http_send_data(struct uclient
*cl
, char *buf
, unsigned int len
)
521 struct uclient_http
*uh
= container_of(cl
, struct uclient_http
, uc
);
523 if (uh
->state
>= HTTP_STATE_REQUEST_DONE
)
526 uclient_http_send_headers(uh
);
528 ustream_printf(uh
->us
, "%X\r\n", len
);
529 ustream_write(uh
->us
, buf
, len
, false);
530 ustream_printf(uh
->us
, "\r\n");
536 uclient_http_request_done(struct uclient
*cl
)
538 struct uclient_http
*uh
= container_of(cl
, struct uclient_http
, uc
);
540 if (uh
->state
>= HTTP_STATE_REQUEST_DONE
)
543 uclient_http_send_headers(uh
);
544 uh
->state
= HTTP_STATE_REQUEST_DONE
;
550 uclient_http_read(struct uclient
*cl
, char *buf
, unsigned int len
)
552 struct uclient_http
*uh
= container_of(cl
, struct uclient_http
, uc
);
554 char *data
, *data_end
;
556 if (uh
->state
< HTTP_STATE_RECV_DATA
|| !uh
->us
)
559 data
= ustream_get_read_buf(uh
->us
, &read_len
);
560 if (!data
|| !read_len
)
563 data_end
= data
+ read_len
;
566 if (uh
->read_chunked
== 0) {
569 if (data
[0] == '\r' && data
[1] == '\n') {
574 sep
= strstr(data
, "\r\n");
579 uh
->read_chunked
= strtoul(data
, NULL
, 16);
581 read_len
+= sep
+ 2 - data
;
584 if (!uh
->read_chunked
)
588 if (len
> data_end
- data
)
589 len
= data_end
- data
;
591 if (uh
->read_chunked
>= 0) {
592 if (len
> uh
->read_chunked
)
593 len
= uh
->read_chunked
;
595 uh
->read_chunked
-= len
;
596 } else if (uh
->content_length
>= 0) {
597 if (len
> uh
->content_length
)
598 len
= uh
->content_length
;
600 uh
->content_length
-= len
;
601 if (!uh
->content_length
)
607 memcpy(buf
, data
, len
);
611 ustream_consume(uh
->us
, read_len
);
613 uclient_notify_eof(uh
);
618 const struct uclient_backend uclient_backend_http __hidden
= {
619 .prefix
= uclient_http_prefix
,
621 .alloc
= uclient_http_alloc
,
622 .free
= uclient_http_free
,
623 .connect
= uclient_http_connect
,
624 .update_url
= uclient_http_free_url_state
,
626 .read
= uclient_http_read
,
627 .write
= uclient_http_send_data
,
628 .request
= uclient_http_request_done
,