fix double / on requests to root
[project/uclient.git] / uclient.c
1 #include <libubox/ustream-ssl.h>
2 #include "uclient.h"
3 #include "uclient-utils.h"
4 #include "uclient-backend.h"
5
6 static struct uclient_url *uclient_get_url(const char *url_str)
7 {
8         static const struct uclient_backend *backends[] = {
9                 &uclient_backend_http,
10         };
11
12         const struct uclient_backend *backend;
13         const char * const *prefix = NULL;
14         struct uclient_url *url;
15         char *url_buf, *next;
16         int i;
17
18         for (i = 0; i < ARRAY_SIZE(backends); i++) {
19                 int prefix_len = 0;
20
21                 for (prefix = backends[i]->prefix; *prefix; prefix++) {
22                         prefix_len = strlen(*prefix);
23
24                         if (!strncmp(url_str, *prefix, prefix_len))
25                                 break;
26                 }
27
28                 if (!*prefix)
29                         continue;
30
31                 url_str += prefix_len;
32                 backend = backends[i];
33                 break;
34         }
35
36         if (!*prefix)
37                 return NULL;
38
39         url = calloc_a(sizeof(*url), &url_buf, strlen(url_str) + 1);
40         url->backend = backend;
41         strcpy(url_buf, url_str);
42
43         next = strchr(url_buf, '/');
44         if (next) {
45                 *next = 0;
46                 url->location = next + 1;
47         } else {
48                 url->location = "";
49         }
50
51         url->host = url_buf;
52         next = strchr(url_buf, '@');
53         if (next) {
54                 *next = 0;
55                 url->host = next + 1;
56
57                 if (uclient_urldecode(url_buf, url_buf, false) < 0)
58                         goto free;
59
60                 url->auth = url_buf;
61         }
62
63         /* Literal IPv6 address */
64         if (*url->host == '[') {
65                 url->host++;
66                 next = strrchr(url->host, ']');
67                 if (!next)
68                         goto free;
69
70                 *(next++) = 0;
71                 if (*next == ':')
72                         url->port = next + 1;
73         } else {
74                 next = strrchr(url->host, ':');
75                 if (next)
76                         url->port = next + 1;
77         }
78
79         return url;
80
81 free:
82         free(url);
83         return NULL;
84 }
85
86 struct uclient *uclient_new(const char *url_str, const struct uclient_cb *cb)
87 {
88         struct uclient *cl;
89         struct uclient_url *url;
90
91         url = uclient_get_url(url_str);
92         if (!url)
93                 return NULL;
94
95         cl = url->backend->alloc();
96         if (!cl)
97                 return NULL;
98
99         cl->backend = url->backend;
100         cl->cb = cb;
101         cl->url = url;
102
103         return cl;
104 }
105
106 int uclient_connect_url(struct uclient *cl, const char *url_str)
107 {
108         struct uclient_url *url = cl->url;
109         const struct uclient_backend *backend = cl->backend;
110
111         if (url_str) {
112                 url = uclient_get_url(url_str);
113                 if (!url)
114                         return -1;
115
116                 if (url->backend != cl->backend)
117                         return -1;
118
119                 free(cl->url);
120                 cl->url = url;
121
122                 if (backend->update_url)
123                         backend->update_url(cl);
124         }
125
126         return backend->connect(cl);
127 }
128
129 void uclient_free(struct uclient *cl)
130 {
131         struct uclient_url *url = cl->url;
132
133         if (cl->backend->free)
134                 cl->backend->free(cl);
135         else
136                 free(cl);
137
138         free(url);
139 }
140
141 int uclient_write(struct uclient *cl, char *buf, int len)
142 {
143         if (!cl->backend->write)
144                 return -1;
145
146         return cl->backend->write(cl, buf, len);
147 }
148
149 int uclient_request(struct uclient *cl)
150 {
151         if (!cl->backend->request)
152                 return -1;
153
154         return cl->backend->request(cl);
155 }
156
157 int uclient_read(struct uclient *cl, char *buf, int len)
158 {
159         if (!cl->backend->read)
160                 return -1;
161
162         return cl->backend->read(cl, buf, len);
163 }
164
165 static void __uclient_backend_change_state(struct uloop_timeout *timeout)
166 {
167         struct uclient *cl = container_of(timeout, struct uclient, timeout);
168
169         if (cl->error && cl->cb->error)
170                 cl->cb->error(cl);
171         else if (cl->eof && cl->cb->data_eof)
172                 cl->cb->data_eof(cl);
173 }
174
175 static void uclient_backend_change_state(struct uclient *cl)
176 {
177         cl->timeout.cb = __uclient_backend_change_state;
178         uloop_timeout_set(&cl->timeout, 1);
179 }
180
181 void uclient_backend_set_error(struct uclient *cl)
182 {
183         if (cl->error)
184                 return;
185
186         cl->error = true;
187         uclient_backend_change_state(cl);
188 }
189
190 void __hidden uclient_backend_set_eof(struct uclient *cl)
191 {
192         if (cl->eof || cl->error)
193                 return;
194
195         cl->eof = true;
196         uclient_backend_change_state(cl);
197 }
198
199 void __hidden uclient_backend_reset_state(struct uclient *cl)
200 {
201         cl->error = false;
202         cl->eof = false;
203         uloop_timeout_cancel(&cl->timeout);
204 }