Dillo v3.2.0-180-g355fbd0a
Loading...
Searching...
No Matches
http.c
Go to the documentation of this file.
1/*
2 * File: http.c
3 *
4 * Copyright (C) 2000-2007 Jorge Arellano Cid <jcid@dillo.org>
5 * Copyright (C) 2024-2026 Rodrigo Arias Mallo <rodarima@gmail.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 3 of the License, or
10 * (at your option) any later version.
11 */
12
18#include "config.h"
19
20#include <unistd.h>
21#include <errno.h> /* for errno */
22#include <stdlib.h>
23#include <fcntl.h>
24#include <assert.h>
25#include <sys/socket.h> /* for lots of socket stuff */
26#include <netinet/in.h> /* for ntohl and stuff */
27#include <arpa/inet.h> /* for inet_ntop */
28
29#include "IO.h"
30#include "iowatch.hh"
31#include "tls.h"
32#include "Url.h"
33#include "../msg.h"
34#include "../klist.h"
35#include "../dns.h"
36#include "../web.hh"
37#include "../cookies.h"
38#include "../auth.h"
39#include "../prefs.h"
40#include "../misc.h"
41#include "dlib/dlib.h" /* dIsdigit */
42
43#include "../uicmd.hh"
44
45/* Used to send a message to the bw's status bar */
46#define MSG_BW(web, root, ...) \
47D_STMT_START { \
48 if (a_Web_valid((web)) && (!(root) || (web)->flags & WEB_RootUrl)) \
49 a_UIcmd_set_msg((web)->bw, __VA_ARGS__); \
50} D_STMT_END
51
52#define _MSG_BW(web, root, ...)
53
54static const int HTTP_SOCKET_USE_PROXY = 0x1;
55static const int HTTP_SOCKET_QUEUED = 0x2;
56static const int HTTP_SOCKET_TO_BE_FREED = 0x4;
57static const int HTTP_SOCKET_TLS = 0x8;
58static const int HTTP_SOCKET_IOWATCH_ACTIVE = 0x10;
59
60/* 'web' is just a reference (no need to deallocate it here). */
61typedef struct {
62 int SockFD;
63 uint_t flags;
64 DilloWeb *web; /* reference to client's web structure */
65 DilloUrl *url;
66 Dlist *addr_list; /* Holds the DNS answer */
67 int addr_list_idx;
68 ChainLink *Info; /* Used for CCC asynchronous operations */
69 char *connected_to; /* Used for per-server connection limit */
70 uint_t connect_port;
71 Dstr *https_proxy_reply;
72} SocketData_t;
73
74/* Data structures and functions to queue sockets that need to be
75 * delayed due to the per host connection limit.
76 */
77typedef struct {
78 char *host;
79 uint_t port;
80 bool_t https;
81
82 int active_conns;
83 int running_the_queue;
84 Dlist *queue;
85} Server_t;
86
87typedef struct {
88 int fd;
89 int skey;
90} FdMapEntry_t;
91
92static void Http_socket_enqueue(Server_t *srv, SocketData_t* sock);
93static Server_t *Http_server_get(const char *host, uint_t port, bool_t https);
94static void Http_server_remove(Server_t *srv);
95static void Http_connect_socket(ChainLink *Info);
96static char *Http_get_connect_str(const DilloUrl *url);
97static void Http_send_query(SocketData_t *S);
98static void Http_socket_free(int SKey);
99
100/*
101 * Local data
102 */
103static Klist_t *ValidSocks = NULL; /* Active sockets list. It holds pointers to
104 * SocketData_t structures. */
105static DilloUrl *HTTP_Proxy = NULL;
106static char *HTTP_Proxy_Auth_base64 = NULL;
107static char *HTTP_Language_hdr = NULL;
109
110/* TODO: If fd_map will stick around in its present form (FDs and SocketData_t)
111 * then consider whether having both this and ValidSocks is necessary.
112 */
113static Dlist *fd_map;
114
118int a_Http_init(void)
119{
120 char *env_proxy = getenv("http_proxy");
121
123 dStrconcat("Accept-Language: ", prefs.http_language, "\r\n", NULL) :
124 dStrdup("");
125
126 if (env_proxy && strlen(env_proxy))
127 HTTP_Proxy = a_Url_new(env_proxy, NULL);
130
131/* This allows for storing the proxy password in "user:passwd" format
132 * in dillorc, but as this constitutes a security problem, it was disabled.
133 *
134 if (HTTP_Proxy && prefs.http_proxyuser && strchr(prefs.http_proxyuser, ':'))
135 HTTP_Proxy_Auth_base64 = a_Misc_encode_base64(prefs.http_proxyuser);
136 */
137
138 servers = dList_new(5);
139 fd_map = dList_new(20);
140
141 return 0;
142}
143
149{
150 return (HTTP_Proxy_Auth_base64 ? 1 : 0);
151}
152
156void a_Http_set_proxy_passwd(const char *str)
157{
158 char *http_proxyauth = dStrconcat(prefs.http_proxyuser, ":", str, NULL);
160 dFree(http_proxyauth);
161}
162
167static int Http_sock_new(void)
168{
169 SocketData_t *S = dNew0(SocketData_t, 1);
170 S->SockFD = -1;
171 return a_Klist_insert(&ValidSocks, S);
172}
173
177static int Http_fd_map_cmp(const void *v1, const void *v2)
178{
179 int fd = VOIDP2INT(v2);
180 const FdMapEntry_t *e = v1;
181
182 return (fd != e->fd);
183}
184
185static void Http_fd_map_add_entry(SocketData_t *sd)
186{
187 FdMapEntry_t *e = dNew0(FdMapEntry_t, 1);
188 e->fd = sd->SockFD;
189 e->skey = VOIDP2INT(sd->Info->LocalKey);
190
192 MSG_ERR("FD ENTRY ALREADY FOUND FOR %d\n", e->fd);
193 assert(0);
194 }
195
197}
198
202static void Http_fd_map_remove_entry(int fd)
203{
205
206 if (data) {
208 dFree(data);
209 } else {
210 MSG("FD ENTRY NOT FOUND FOR %d\n", fd);
211 }
212}
213
214void a_Http_connect_done(int fd, bool_t success)
215{
216 SocketData_t *sd;
217 FdMapEntry_t *fme = dList_find_custom(fd_map, INT2VOIDP(fd),
219
220 if (fme && (sd = a_Klist_get_data(ValidSocks, fme->skey))) {
221 ChainLink *info = sd->Info;
222 bool_t valid_web = a_Web_valid(sd->web);
223
224 if (success && valid_web) {
225 a_Chain_bfcb(OpSend, info, &sd->SockFD, "FD");
226 Http_send_query(sd);
227 } else {
228 if (valid_web)
229 MSG_BW(sd->web, 1, "Could not establish connection.");
230 MSG("fd %d is done and failed\n", sd->SockFD);
231 dClose(fd);
232 Http_socket_free(VOIDP2INT(info->LocalKey)); /* free sd */
233 a_Chain_bfcb(OpAbort, info, NULL, "Both");
234 dFree(info);
235 }
236 } else {
237 MSG("**** but no luck with fme %p or sd\n", (void *) fme);
238 }
239}
240
241static void Http_socket_activate(Server_t *srv, SocketData_t *sd)
242{
243 dList_remove(srv->queue, sd);
244 sd->flags &= ~HTTP_SOCKET_QUEUED;
245 srv->active_conns++;
246 sd->connected_to = srv->host;
247}
248
249static void Http_connect_queued_sockets(Server_t *srv)
250{
251 SocketData_t *sd;
252 int i;
253
254 srv->running_the_queue++;
255
256 for (i = 0;
257 (i < dList_length(srv->queue) &&
258 srv->active_conns < prefs.http_max_conns);
259 i++) {
260 sd = dList_nth_data(srv->queue, i);
261
262 if (sd->flags & HTTP_SOCKET_TO_BE_FREED) {
263 dList_remove(srv->queue, sd);
264 dFree(sd);
265 i--;
266 } else {
267 int connect_ready = TLS_CONNECT_READY;
268
269 if (sd->flags & HTTP_SOCKET_TLS)
270 connect_ready = a_Tls_connect_ready(sd->url);
271
272 if (connect_ready == TLS_CONNECT_NEVER || !a_Web_valid(sd->web)) {
273 int SKey = VOIDP2INT(sd->Info->LocalKey);
274
275 Http_socket_free(SKey);
276 } else if (connect_ready == TLS_CONNECT_READY) {
277 i--;
278 Http_socket_activate(srv, sd);
279 Http_connect_socket(sd->Info);
280 }
281 }
282 }
283
284 _MSG("Queue http%s://%s:%u len %d\n", srv->https ? "s" : "", srv->host,
285 srv->port, dList_length(srv->queue));
286
287 if (--srv->running_the_queue == 0) {
288 if (srv->active_conns == 0)
290 }
291}
292
296static void Http_socket_free(int SKey)
297{
298 SocketData_t *S;
299
300 if ((S = a_Klist_get_data(ValidSocks, SKey))) {
302
303 if (S->flags & HTTP_SOCKET_IOWATCH_ACTIVE) {
304 S->flags &= ~HTTP_SOCKET_IOWATCH_ACTIVE;
305 a_IOwatch_remove_fd(S->SockFD, -1);
306 dClose(S->SockFD);
307 }
308 dStr_free(S->https_proxy_reply, 1);
309
310 if (S->flags & HTTP_SOCKET_QUEUED) {
311 S->flags |= HTTP_SOCKET_TO_BE_FREED;
312 a_Url_free(S->url);
313 } else {
314 if (S->SockFD != -1)
315 Http_fd_map_remove_entry(S->SockFD);
317 if (S->connected_to) {
318 a_Tls_close_by_fd(S->SockFD);
319
320 Server_t *srv = Http_server_get(S->connected_to, S->connect_port,
321 (S->flags & HTTP_SOCKET_TLS));
322 srv->active_conns--;
324 }
325 a_Url_free(S->url);
326 dFree(S);
327 }
328 }
329}
330
335static char *Http_get_referer(const DilloUrl *url)
336{
337 char *referer = NULL;
338
339 if (!strcmp(prefs.http_referer, "host")) {
340 referer = dStrconcat("Referer: ", URL_SCHEME(url), "://",
341 URL_AUTHORITY(url), "/", "\r\n", NULL);
342 } else if (!strcmp(prefs.http_referer, "path")) {
343 referer = dStrconcat("Referer: ", URL_SCHEME(url), "://",
344 URL_AUTHORITY(url),
345 URL_PATH_(url) ? URL_PATH(url) : "/", "\r\n", NULL);
346 }
347 if (!referer)
348 referer = dStrdup("");
349 _MSG("http, referer='%s'\n", referer);
350 return referer;
351}
352
357{
358 Dstr *dstr;
359
360 if (URL_FLAGS(url) & URL_MultipartEnc) {
361 _MSG("submitting multipart/form-data!\n");
362 dstr = dStr_new("multipart/form-data; boundary=\"");
363 if (URL_DATA(url)->len > 2) {
364 /* boundary lines have "--" prepended. Skip that. */
365 const char *start = URL_DATA(url)->str + 2;
366 char *eol = strchr(start, '\r');
367 if (eol)
368 dStr_append_l(dstr, start, eol - start);
369 } else {
370 /* Zero parts; arbitrary boundary */
371 dStr_append_c(dstr, '0');
372 }
373 dStr_append_c(dstr,'"');
374 } else {
375 dstr = dStr_new("application/x-www-form-urlencoded");
376 }
377 return dstr;
378}
379
383static Dstr *Http_make_query_str(DilloWeb *web, bool_t use_proxy, bool_t use_tls)
384{
385 char *ptr, *cookies, *referer, *auth;
386 const DilloUrl *url = web->url;
387 Dstr *query = dStr_new(""),
388 *request_uri = dStr_new(""),
389 *proxy_auth = dStr_new("");
390
391 /* BUG: dillo doesn't actually understand application/xml yet */
392 const char *accept_hdr_value =
393 web->flags & WEB_Image ? "image/png,image/*;q=0.8,*/*;q=0.5" :
394 web->flags & WEB_Stylesheet ? "text/css,*/*;q=0.1" :
395 "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8";
396
397 const char *connection_hdr_val =
398 (prefs.http_persistent_conns == TRUE) ? "keep-alive" : "close";
399
400 if (use_proxy && !use_tls) {
401 dStr_sprintfa(request_uri, "%s%s",
402 URL_STR(url),
403 (URL_PATH_(url) || URL_QUERY_(url)) ? "" : "/");
404 if ((ptr = strrchr(request_uri->str, '#')))
405 dStr_truncate(request_uri, ptr - request_uri->str);
407 dStr_sprintf(proxy_auth, "Proxy-Authorization: Basic %s\r\n",
409 } else {
410 dStr_sprintfa(request_uri, "%s%s%s",
411 URL_PATH_(url) ? URL_PATH(url) : "/",
412 URL_QUERY_(url) ? "?" : "",
413 URL_QUERY(url));
414 }
415
416 cookies = a_Cookies_get_query(url, web->requester, web->flags & WEB_RootUrl);
417 auth = a_Auth_get_auth_str(url, request_uri->str);
418 referer = Http_get_referer(url);
419 if (URL_FLAGS(url) & URL_Post) {
420 Dstr *content_type = Http_make_content_type(url);
422 query,
423 "POST %s HTTP/1.1\r\n"
424 "Host: %s\r\n"
425 "User-Agent: %s\r\n"
426 "Accept: %s\r\n"
427 "%s" /* language */
428 "Accept-Encoding: gzip, deflate"
429#ifdef ENABLE_BROTLI
430 ", br"
431#endif
432 "\r\n"
433 "%s" /* auth */
434 "DNT: 1\r\n"
435 "%s" /* proxy auth */
436 "%s" /* referer */
437 "Connection: %s\r\n"
438 "Content-Type: %s\r\n"
439 "Content-Length: %ld\r\n"
440 "%s" /* cookies */
441 "\r\n",
442 request_uri->str, URL_AUTHORITY(url), prefs.http_user_agent,
443 accept_hdr_value, HTTP_Language_hdr, auth ? auth : "",
444 proxy_auth->str, referer, connection_hdr_val, content_type->str,
445 (long)URL_DATA(url)->len, cookies);
446 dStr_append_l(query, URL_DATA(url)->str, URL_DATA(url)->len);
447 dStr_free(content_type, TRUE);
448 } else {
450 query,
451 "GET %s HTTP/1.1\r\n"
452 "Host: %s\r\n"
453 "User-Agent: %s\r\n"
454 "Accept: %s\r\n"
455 "%s" /* language */
456 "Accept-Encoding: gzip, deflate"
457#ifdef ENABLE_BROTLI
458 ", br"
459#endif
460 "\r\n"
461 "%s" /* auth */
462 "DNT: 1\r\n"
463 "%s" /* proxy auth */
464 "%s" /* referer */
465 "Connection: %s\r\n"
466 "%s" /* cache control */
467 "%s" /* cookies */
468 "\r\n",
469 request_uri->str, URL_AUTHORITY(url), prefs.http_user_agent,
470 accept_hdr_value, HTTP_Language_hdr, auth ? auth : "",
471 proxy_auth->str, referer, connection_hdr_val,
472 (URL_FLAGS(url) & URL_E2EQuery) ?
473 "Pragma: no-cache\r\nCache-Control: no-cache\r\n" : "",
474 cookies);
475 }
476 dFree(referer);
477 dFree(cookies);
478 dFree(auth);
479
480 dStr_free(request_uri, TRUE);
481 dStr_free(proxy_auth, TRUE);
482 _MSG("Query: {%s}\n", dStr_printable(query, 8192));
483 return query;
484}
485
489static void Http_send_query(SocketData_t *S)
490{
491 Dstr *query;
492 DataBuf *dbuf;
493
494 /* Create the query */
495 query = Http_make_query_str(S->web,
496 S->flags & HTTP_SOCKET_USE_PROXY,
497 S->flags & HTTP_SOCKET_TLS);
498 dbuf = a_Chain_dbuf_new(query->str, query->len, 0);
499
500 MSG_BW(S->web, 1, "Sending query%s...",
501 S->flags & HTTP_SOCKET_USE_PROXY ? " through proxy" : "");
502
503 if (prefs.trace_http)
504 MSG(">>> sending HTTP:\n%s\n", dStr_printable(query, 8192));
505
506 /* send query */
507 a_Chain_bcb(OpSend, S->Info, dbuf, NULL);
508 dFree(dbuf);
509 dStr_free(query, 1);
510}
511
515static void Http_connect_tls(ChainLink *info)
516{
517 int SKey = VOIDP2INT(info->LocalKey);
518 SocketData_t *S = a_Klist_get_data(ValidSocks, SKey);
519
520 if (S->flags & HTTP_SOCKET_USE_PROXY) {
521 char *connect_str = Http_get_connect_str(S->url);
522 DataBuf *dbuf = a_Chain_dbuf_new(connect_str, strlen(connect_str), 0);
523
524 MSG_BW(S->web, 1, "Tunnel secure connection through proxy...");
525 a_Chain_bfcb(OpSend, info, &S->SockFD, "FD");
526 S->https_proxy_reply = dStr_new(NULL);
527 a_Chain_bcb(OpSend, info, dbuf, NULL);
528
529 dFree(dbuf);
530 dFree(connect_str);
531 } else {
532 MSG_BW(S->web, 1, "Secure connection negotiation...");
533 a_Tls_connect(S->SockFD, S->url);
534 }
535}
536
540static void Http_connect_socket_cb(int fd, void *data)
541{
542 int SKey = VOIDP2INT(data);
543 SocketData_t *S = a_Klist_get_data(ValidSocks, SKey);
544
545 if (S) {
546 int ret, connect_ret;
547 uint_t connect_ret_size = sizeof(connect_ret);
548
549 a_IOwatch_remove_fd(fd, -1);
550 S->flags &= ~HTTP_SOCKET_IOWATCH_ACTIVE;
551
552 ret = getsockopt(S->SockFD, SOL_SOCKET, SO_ERROR, &connect_ret,
553 &connect_ret_size);
554
555 if (ret < 0 || connect_ret != 0) {
556 if (ret < 0) {
557 MSG("Http_connect_socket_cb getsockopt ERROR: %s.\n",
558 dStrerror(errno));
559 } else {
560 MSG("Http_connect_socket_cb connect ERROR: %s.\n",
561 dStrerror(connect_ret));
562 }
563 MSG("Http_connect_socket() will try another IP address.\n");
564 S->addr_list_idx++;
565 Http_connect_socket(S->Info);
566 } else if (S->flags & HTTP_SOCKET_TLS) {
567 Http_connect_tls(S->Info);
568 } else {
569 a_Http_connect_done(S->SockFD, TRUE);
570 }
571 }
572}
573
579{
580 DilloHost *dh;
581 SocketData_t *S = a_Klist_get_data(ValidSocks, VOIDP2INT(Info->LocalKey));
582
583 for (; (dh = dList_nth_data(S->addr_list, S->addr_list_idx));
584 S->addr_list_idx++) {
585#ifdef ENABLE_IPV6
586 struct sockaddr_in6 name;
587#else
588 struct sockaddr_in name;
589#endif
590 socklen_t socket_len = 0;
591
592 if (S->addr_list_idx > 0 && S->SockFD >= 0) {
593 /* clean up the previous one that failed */
594 Http_fd_map_remove_entry(S->SockFD);
595 dClose(S->SockFD);
596 }
597 if ((S->SockFD = socket(dh->af, SOCK_STREAM, IPPROTO_TCP)) < 0) {
598 MSG("Http_connect_socket socket() ERROR: %s\n", dStrerror(errno));
599 continue;
600 }
602
603 /* set NONBLOCKING and close on exec. */
604 fcntl(S->SockFD, F_SETFL, O_NONBLOCK | fcntl(S->SockFD, F_GETFL));
605 fcntl(S->SockFD, F_SETFD, FD_CLOEXEC | fcntl(S->SockFD, F_GETFD));
606
607 /* Some OSes require this... */
608 memset(&name, 0, sizeof(name));
609 /* Set remaining parms. */
610 switch (dh->af) {
611 case AF_INET:
612 {
613 struct sockaddr_in *sin = (struct sockaddr_in *)&name;
614 socket_len = sizeof(struct sockaddr_in);
615 sin->sin_family = dh->af;
616 sin->sin_port = htons(S->connect_port);
617 memcpy(&sin->sin_addr, dh->data, (size_t)dh->alen);
618 if (a_Web_valid(S->web) && (S->web->flags & WEB_RootUrl))
619 MSG("Connecting to %s:%u\n", inet_ntoa(sin->sin_addr),
620 S->connect_port);
621 break;
622 }
623#ifdef ENABLE_IPV6
624 case AF_INET6:
625 {
626 char buf[128];
627 struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&name;
628 socket_len = sizeof(struct sockaddr_in6);
629 sin6->sin6_family = dh->af;
630 sin6->sin6_port = htons(S->connect_port);
631 memcpy(&sin6->sin6_addr, dh->data, dh->alen);
632 inet_ntop(dh->af, dh->data, buf, sizeof(buf));
633 if (a_Web_valid(S->web) && (S->web->flags & WEB_RootUrl))
634 MSG("Connecting to [%s]:%u\n", buf, S->connect_port);
635 break;
636 }
637#endif
638 } /* switch */
639 MSG_BW(S->web, 1, "Contacting host...");
640
641 if (connect(S->SockFD, (struct sockaddr *)&name, socket_len) == 0) {
642 /* probably never succeeds immediately on any system */
643 if (S->flags & HTTP_SOCKET_TLS) {
644 Http_connect_tls(Info);
645 break;
646 } else {
647 a_Http_connect_done(S->SockFD, TRUE);
648 break;
649 }
650 } else {
651 if (errno == EINPROGRESS) {
653 Info->LocalKey);
654 S->flags |= HTTP_SOCKET_IOWATCH_ACTIVE;
655 break;
656 } else {
657 MSG("Http_connect_socket connect ERROR: %s\n", dStrerror(errno));
658 MSG("We will try another IP address.\n");
659 }
660 }
661 } /* for */
662
663 if (S->addr_list_idx >= dList_length(S->addr_list) ) {
664 MSG("Http_connect_socket ran out of IP addrs to try.\n");
665 a_Http_connect_done(S->SockFD, FALSE);
666 }
667}
668
673static int Http_must_use_proxy(const char *hostname)
674{
675 char *np, *p, *tok;
676 int ret = 0;
677
678 if (HTTP_Proxy) {
679 ret = 1;
680 if (prefs.no_proxy) {
681 size_t host_len = strlen(hostname);
682
683 np = dStrdup(prefs.no_proxy);
684 for (p = np; (tok = dStrsep(&p, " ")); ) {
685 int start = host_len - strlen(tok);
686
687 if (start >= 0 && dStrAsciiCasecmp(hostname + start, tok) == 0) {
688 /* no_proxy token is suffix of host string */
689 ret = 0;
690 break;
691 }
692 }
693 dFree(np);
694 }
695 }
696 _MSG("Http_must_use_proxy: %s\n %s\n", hostname, ret ? "YES":"NO");
697 return ret;
698}
699
703static char *Http_get_connect_str(const DilloUrl *url)
704{
705 Dstr *dstr;
706 const char *auth1;
707 int auth_len;
708 char *auth2, *proxy_auth, *retstr;
709
711
712 dstr = dStr_new("");
713 auth1 = URL_AUTHORITY(url);
714 auth_len = strlen(auth1);
715 if (auth_len > 0 && !dIsdigit(auth1[auth_len - 1]))
716 /* if no port number, add HTTPS port */
717 auth2 = dStrconcat(auth1, ":443", NULL);
718 else
719 auth2 = dStrdup(auth1);
720 proxy_auth = HTTP_Proxy_Auth_base64 ?
721 dStrconcat ("Proxy-Authorization: Basic ",
722 HTTP_Proxy_Auth_base64, "\r\n", NULL) :
723 dStrdup("");
725 dstr,
726 "CONNECT %s HTTP/1.1\r\n"
727 "Host: %s\r\n"
728 "%s"
729 "\r\n",
730 auth2,
731 auth2,
732 proxy_auth);
733
734 dFree(auth2);
735 dFree(proxy_auth);
736 retstr = dstr->str;
737 dStr_free(dstr, 0);
738 return retstr;
739}
740
746static void Http_dns_cb(int Status, Dlist *addr_list, void *data)
747{
748 int SKey = VOIDP2INT(data);
749 bool_t clean_up = TRUE;
750 SocketData_t *S;
751 Server_t *srv;
752
753 S = a_Klist_get_data(ValidSocks, SKey);
754 if (S) {
755 const char *host = URL_HOST((S->flags & HTTP_SOCKET_USE_PROXY) ?
756 HTTP_Proxy : S->url);
757 if (a_Web_valid(S->web)) {
758 if (Status == 0 && addr_list) {
759
760 /* Successful DNS answer; save the IP */
761 S->addr_list = addr_list;
762 S->addr_list_idx = 0;
763 clean_up = FALSE;
764 srv = Http_server_get(host, S->connect_port,
765 (S->flags & HTTP_SOCKET_TLS));
766 Http_socket_enqueue(srv, S);
768 } else {
769 /* DNS wasn't able to resolve the hostname */
770 MSG_BW(S->web, 0, "ERROR: DNS can't resolve %s", host);
771 }
772 }
773 if (clean_up) {
774 ChainLink *info = S->Info;
775
776 Http_socket_free(SKey);
777 a_Chain_bfcb(OpAbort, info, NULL, "Both");
778 dFree(info);
779 }
780 }
781}
782
790static int Http_get(ChainLink *Info, void *Data1)
791{
792 SocketData_t *S;
793 char *hostname;
794 const DilloUrl *url;
795
797 /* Reference Web data */
798 S->web = Data1;
799 /* Reference Info data */
800 S->Info = Info;
801
802 /* Proxy support */
803 if (Http_must_use_proxy(URL_HOST(S->web->url))) {
804 url = HTTP_Proxy;
806 } else {
807 url = S->web->url;
808 }
809 hostname = dStrdup(URL_HOST(url));
810 S->connect_port = URL_PORT(url);
811 S->url = a_Url_dup(S->web->url);
812 if (!dStrAsciiCasecmp(URL_SCHEME(S->url), "https"))
813 S->flags |= HTTP_SOCKET_TLS;
814
815 /* Let the user know what we'll do */
816 MSG_BW(S->web, 1, "DNS resolving %s", hostname);
817
818 /* Let the DNS engine resolve the hostname, and when done,
819 * we'll try to connect the socket from the callback function */
820 a_Dns_resolve(hostname, Http_dns_cb, Info->LocalKey);
821
822 dFree(hostname);
823 return 0;
824}
825
832static bool_t Http_socket_reuse_compatible(SocketData_t *old,
833 SocketData_t *new)
834{
835 /*
836 * If we are using TLS through a proxy, we need to ensure that old and new
837 * are going through to the same host:port.
838 */
839 if (a_Web_valid(new->web) &&
840 ((old->flags & HTTP_SOCKET_TLS) == 0 ||
841 (old->flags & HTTP_SOCKET_USE_PROXY) == 0 ||
842 ((URL_PORT(old->url) == URL_PORT(new->url)) &&
843 !dStrAsciiCasecmp(URL_HOST(old->url), URL_HOST(new->url)))))
844 return TRUE;
845 return FALSE;
846}
847
852static void Http_socket_reuse(int SKey)
853{
854 SocketData_t *new_sd, *old_sd = a_Klist_get_data(ValidSocks, SKey);
855
856 if (old_sd) {
857 Server_t *srv = Http_server_get(old_sd->connected_to,
858 old_sd->connect_port,
859 (old_sd->flags & HTTP_SOCKET_TLS));
860 int i, n = dList_length(srv->queue);
861
862 for (i = 0; i < n; i++) {
863 new_sd = dList_nth_data(srv->queue, i);
864
865 if (!(new_sd->flags & HTTP_SOCKET_TO_BE_FREED) &&
866 Http_socket_reuse_compatible(old_sd, new_sd)) {
867 const bool_t success = TRUE;
868
869 new_sd->SockFD = old_sd->SockFD;
870
871 old_sd->connected_to = NULL;
872 srv->active_conns--;
873 Http_socket_free(SKey);
874
875 _MSG("Reusing fd %d for %s\n",new_sd->SockFD,URL_STR(new_sd->url));
876 Http_socket_activate(srv, new_sd);
877 Http_fd_map_add_entry(new_sd);
878 a_Http_connect_done(new_sd->SockFD, success);
879 return;
880 }
881 }
882 /* Free the connection before closing the file descriptor, so more data
883 * can be written. */
884 int old_fd = old_sd->SockFD;
885 Http_socket_free(SKey);
886 dClose(old_fd);
887 }
888}
889
893void a_Http_ccc(int Op, int Branch, int Dir, ChainLink *Info,
894 void *Data1, void *Data2)
895{
896 int SKey = VOIDP2INT(Info->LocalKey);
897 SocketData_t *sd;
898 DataBuf *dbuf;
899
900 dReturn_if_fail( a_Chain_check("a_Http_ccc", Op, Branch, Dir, Info) );
901
902 if (Branch == 1) {
903 if (Dir == BCK) {
904 /* HTTP query branch */
905 switch (Op) {
906 case OpStart:
907 /* ( Data1 = Web ) */
908 SKey = Http_sock_new();
909 Info->LocalKey = INT2VOIDP(SKey);
910 /* link IO */
912 a_Chain_bcb(OpStart, Info, NULL, NULL);
913 /* async. connection */
914 Http_get(Info, Data1);
915 break;
916 case OpEnd:
917 /* finished the HTTP query branch */
918 a_Chain_bcb(OpEnd, Info, NULL, NULL);
919 dFree(Info);
920 break;
921 case OpAbort:
922 _MSG("ABORT 1B\n");
923 Http_socket_free(SKey);
924 a_Chain_bcb(OpAbort, Info, NULL, NULL);
925 dFree(Info);
926 break;
927 default:
928 MSG_WARN("Unused CCC 1B Op %d\n", Op);
929 break;
930 }
931 } else { /* 1 FWD */
932 /* HTTP send-query status branch */
933 switch (Op) {
934 case OpAbort:
935 _MSG("ABORT 1F\n");
936 if ((sd = a_Klist_get_data(ValidSocks, SKey)))
937 MSG_BW(sd->web, 1, "Can't get %s", URL_STR(sd->url));
938 Http_socket_free(SKey);
939 a_Chain_fcb(OpAbort, Info, NULL, "Both");
940 dFree(Info);
941 break;
942 default:
943 MSG_WARN("Unused CCC 1F Op %d\n", Op);
944 break;
945 }
946 }
947 } else if (Branch == 2) {
948 if (Dir == FWD) {
949 sd = a_Klist_get_data(ValidSocks, SKey);
950 assert(sd);
951 /* Receiving from server */
952 switch (Op) {
953 case OpSend:
954 if (sd->https_proxy_reply) {
955 dbuf = Data1;
956 dStr_append(sd->https_proxy_reply, dbuf->Buf);
957 if (strstr(sd->https_proxy_reply->str, "\r\n\r\n")) {
958 if (sd->https_proxy_reply->len >= 12 &&
959 sd->https_proxy_reply->str[9] == '2') {
960 /* e.g. "HTTP/1.1 200 Connection established[...]" */
961 MSG("CONNECT through proxy succeeded. Reply:\n%s\n",
962 sd->https_proxy_reply->str);
963 dStr_free(sd->https_proxy_reply, 1);
964 sd->https_proxy_reply = NULL;
965 MSG_BW(sd->web, 1, "Secure connection negotiation...");
966 a_Tls_connect(sd->SockFD, sd->url);
967 } else {
968 MSG_BW(sd->web, 1, "Can't connect through proxy to %s",
969 URL_HOST(sd->url));
970 MSG("CONNECT through proxy failed. Server sent:\n%s\n",
971 sd->https_proxy_reply->str);
972 Http_socket_free(SKey);
973 a_Chain_bfcb(OpAbort, Info, NULL, "Both");
974 dFree(Info);
975 }
976 }
977 } else {
978 /* Data1 = dbuf */
979 a_Chain_fcb(OpSend, Info, Data1, "send_page_2eof");
980 }
981 break;
982 case OpEnd:
983 if (sd->https_proxy_reply) {
984 MSG("CONNECT through proxy failed. "
985 "Full reply not received:\n%s\n",
986 sd->https_proxy_reply->len ? sd->https_proxy_reply->str :
987 "(nothing)");
988 Http_socket_free(SKey);
989 a_Chain_bfcb(OpAbort, Info, NULL, "Both");
990 } else {
991 Http_socket_free(SKey);
992 a_Chain_fcb(OpEnd, Info, NULL, NULL);
993 }
994 dFree(Info);
995 break;
996 case OpAbort:
997 if (sd->https_proxy_reply) {
998 MSG("CONNECT through proxy failed. "
999 "Full reply not received:\n%s\n",
1000 sd->https_proxy_reply->len ? sd->https_proxy_reply->str :
1001 "(nothing)");
1002 }
1003 Http_socket_free(SKey);
1004 a_Chain_fcb(OpAbort, Info, NULL, "Both");
1005 dFree(Info);
1006 break;
1007 default:
1008 MSG_WARN("Unused CCC 2F Op %d\n", Op);
1009 break;
1010 }
1011 } else { /* 2 BCK */
1012 switch (Op) {
1013 case OpStart:
1014 a_Chain_link_new(Info, a_Http_ccc, BCK, a_IO_ccc, 2, 2);
1015 a_Chain_bcb(OpStart, Info, NULL, NULL); /* IORead */
1016 break;
1017 case OpSend:
1018 if (Data2) {
1019 if (!strcmp(Data2, "FD")) {
1020 int fd = *(int*)Data1;
1021 FdMapEntry_t *fme = dList_find_custom(fd_map, INT2VOIDP(fd),
1023 Info->LocalKey = INT2VOIDP(fme->skey);
1024 a_Chain_bcb(OpSend, Info, Data1, Data2);
1025 } else if (!strcmp(Data2, "reply_complete")) {
1026 a_Chain_bfcb(OpEnd, Info, NULL, NULL);
1027 Http_socket_reuse(SKey);
1028 dFree(Info);
1029 }
1030 }
1031 break;
1032 case OpAbort:
1033 Http_socket_free(SKey);
1034 a_Chain_bcb(OpAbort, Info, NULL, NULL);
1035 dFree(Info);
1036 break;
1037 default:
1038 MSG_WARN("Unused CCC 2B Op %d\n", Op);
1039 break;
1040 }
1041 }
1042 }
1043}
1044
1049static void Http_socket_enqueue(Server_t *srv, SocketData_t* sock)
1050{
1051 assert(~sock->flags & HTTP_SOCKET_QUEUED);
1052
1053 sock->flags |= HTTP_SOCKET_QUEUED;
1054
1055 if ((sock->web->flags & WEB_Image) == 0) {
1056 int i, n = dList_length(srv->queue);
1057
1058 for (i = 0; i < n; i++) {
1059 SocketData_t *curr = dList_nth_data(srv->queue, i);
1060
1061 if (a_Web_valid(curr->web) && (curr->web->flags & WEB_Image)) {
1062 dList_insert_pos(srv->queue, sock, i);
1063 return;
1064 }
1065 }
1066 }
1067 dList_append(srv->queue, sock);
1068}
1069
1070static Server_t *Http_server_get(const char *host, uint_t port, bool_t https)
1071{
1072 int i;
1073 Server_t *srv;
1074
1075 for (i = 0; i < dList_length(servers); i++) {
1076 srv = (Server_t*) dList_nth_data(servers, i);
1077
1078 if (port == srv->port && https == srv->https &&
1079 !dStrAsciiCasecmp(host, srv->host))
1080 return srv;
1081 }
1082
1083 srv = dNew0(Server_t, 1);
1084 srv->queue = dList_new(10);
1085 srv->running_the_queue = 0;
1086 srv->host = dStrdup(host);
1087 srv->port = port;
1088 srv->https = https;
1089 dList_append(servers, srv);
1090
1091 return srv;
1092}
1093
1094static void Http_server_remove(Server_t *srv)
1095{
1096 SocketData_t *sd;
1097
1098 while ((sd = dList_nth_data(srv->queue, 0))) {
1099 dList_remove_fast(srv->queue, sd);
1100 sd->flags &= ~HTTP_SOCKET_QUEUED;
1101 if (sd->flags & HTTP_SOCKET_TO_BE_FREED)
1102 dFree(sd);
1103 }
1104 dList_free(srv->queue);
1106 dFree(srv->host);
1107 dFree(srv);
1108}
1109
1111{
1112 Server_t *srv;
1113 SocketData_t *sd;
1114
1115 while (dList_length(servers) > 0) {
1116 srv = (Server_t*) dList_nth_data(servers, 0);
1117 while ((sd = dList_nth_data(srv->queue, 0))) {
1118 dList_remove_fast(srv->queue, sd);
1119 sd->flags &= ~HTTP_SOCKET_QUEUED;
1120 if (sd->flags & HTTP_SOCKET_TO_BE_FREED)
1121 dFree(sd);
1122 }
1123 Http_server_remove(srv);
1124 }
1126}
1127
1128static void Http_fd_map_remove_all(void)
1129{
1130 FdMapEntry_t *fme;
1131 int i, n = dList_length(fd_map);
1132
1133 for (i = 0; i < n; i++) {
1134 fme = (FdMapEntry_t *) dList_nth_data(fd_map, i);
1135 dFree(fme);
1136 }
1138}
1139
void a_IO_ccc(int Op, int Branch, int Dir, ChainLink *Info, void *Data1, void *Data2)
CCC function for the IO module.
Definition IO.c:359
char * a_Auth_get_auth_str(const DilloUrl *url, const char *request_uri)
Return the authorization header for an HTTP query.
Definition auth.c:471
#define _MSG(...)
Definition bookmarks.c:44
#define MSG(...)
Definition bookmarks.c:45
int a_Chain_bcb(int Op, ChainLink *Info, void *Data1, void *Data2)
Issue the backward callback of the 'Info' link.
Definition chain.c:126
DataBuf * a_Chain_dbuf_new(void *buf, int size, int code)
Allocate and initialize a new DataBuf structure.
Definition chain.c:171
ChainLink * a_Chain_link_new(ChainLink *AInfo, ChainFunction_t AFunc, int Direction, ChainFunction_t BFunc, int AtoB_branch, int BtoA_branch)
Create a new link from module A to module B.
Definition chain.c:55
int a_Chain_check(char *FuncStr, int Op, int Branch, int Dir, ChainLink *Info)
Check whether the CCC is operative.
Definition chain.c:186
int a_Chain_bfcb(int Op, ChainLink *Info, void *Data1, void *Data2)
Issue the backward callback of the 'Info' link and then the forward callback (used for OpAbort and Op...
Definition chain.c:150
int a_Chain_fcb(int Op, ChainLink *Info, void *Data1, void *Data2)
Issue the forward callback of the 'Info' link.
Definition chain.c:103
#define OpAbort
Definition chain.h:17
#define FWD
Definition chain.h:29
#define OpStart
Definition chain.h:13
#define BCK
Definition chain.h:30
#define OpSend
Definition chain.h:14
#define OpEnd
Definition chain.h:16
unsigned int uint_t
Definition d_size.h:20
unsigned char bool_t
Definition d_size.h:21
char * dStrconcat(const char *s1,...)
Concatenate a NULL-terminated list of strings.
Definition dlib.c:101
char * dStrsep(char **orig, const char *delim)
strsep() implementation
Definition dlib.c:158
void dFree(void *mem)
Definition dlib.c:67
int dStrAsciiCasecmp(const char *s1, const char *s2)
Definition dlib.c:202
void dStr_sprintfa(Dstr *ds, const char *format,...)
Printf-like function that appends.
Definition dlib.c:463
void dStr_append(Dstr *ds, const char *s)
Append a C string to a Dstr.
Definition dlib.c:315
void dList_insert_pos(Dlist *lp, void *data, int pos0)
Insert an element at a given position [0 based].
Definition dlib.c:603
char * dStrdup(const char *s)
Definition dlib.c:76
Dlist * dList_new(int size)
Create a new empty list.
Definition dlib.c:575
int dList_length(Dlist *lp)
For completing the ADT.
Definition dlib.c:640
void * dList_nth_data(Dlist *lp, int n0)
Return the nth data item, NULL when not found or 'n0' is out of range.
Definition dlib.c:689
void dList_remove_fast(Dlist *lp, const void *data)
Remove a data item without preserving order.
Definition dlib.c:650
void dStr_free(Dstr *ds, int all)
Free a dillo string.
Definition dlib.c:336
int dClose(int fd)
Close a FD handling EINTR.
Definition dlib.c:978
void dStr_append_l(Dstr *ds, const char *s, int l)
Append a C string to a Dstr (providing length).
Definition dlib.c:307
void dStr_append_c(Dstr *ds, int c)
Append one character.
Definition dlib.c:348
void dStr_sprintf(Dstr *ds, const char *format,...)
Printf-like function.
Definition dlib.c:449
Dstr * dStr_new(const char *s)
Create a new string.
Definition dlib.c:324
void dList_append(Dlist *lp, void *data)
Append a data item to the list.
Definition dlib.c:624
void dList_free(Dlist *lp)
Free a list (not its elements)
Definition dlib.c:591
void * dList_find_custom(Dlist *lp, const void *data, dCompareFunc func)
Search a data item using a custom function.
Definition dlib.c:731
const char * dStr_printable(Dstr *in, int maxlen)
Return a printable representation of the provided Dstr, limited to a length of roughly maxlen.
Definition dlib.c:512
void dStr_truncate(Dstr *ds, int len)
Truncate a Dstr to be 'len' bytes long.
Definition dlib.c:367
void dList_remove(Dlist *lp, const void *data)
Definition dlib.c:668
#define dStrerror
Definition dlib.h:124
#define dReturn_if_fail(expr)
Definition dlib.h:101
static int dIsdigit(unsigned char c)
Definition dlib.h:50
#define dNew0(type, count)
Definition dlib.h:80
#define dReturn_val_if_fail(expr, val)
Definition dlib.h:105
#define VOIDP2INT(p)
Definition dlib.h:72
#define TRUE
Definition dlib.h:36
#define FALSE
Definition dlib.h:32
#define INT2VOIDP(i)
Definition dlib.h:73
void a_Dns_resolve(const char *hostname, DnsCallback_t cb_func, void *cb_data)
Return the IP for the given hostname using a callback.
Definition dns.c:362
#define MSG_ERR(...)
Definition dpid_common.h:23
void a_Http_set_proxy_passwd(const char *str)
Activate entered proxy password for HTTP.
Definition http.c:156
static char * HTTP_Language_hdr
Definition http.c:107
static Dlist * fd_map
Definition http.c:113
int a_Http_init(void)
Initialize proxy vars and Accept-Language header.
Definition http.c:118
static int Http_get(ChainLink *Info, void *Data1)
Asynchronously create a new http connection for 'Url'.
Definition http.c:790
static Dstr * Http_make_query_str(DilloWeb *web, bool_t use_proxy, bool_t use_tls)
Make the http query string.
Definition http.c:383
static int Http_fd_map_cmp(const void *v1, const void *v2)
Compare by FD.
Definition http.c:177
static Klist_t * ValidSocks
Definition http.c:103
static void Http_fd_map_remove_all(void)
Definition http.c:1128
#define MSG_BW(web, root,...)
Definition http.c:46
static bool_t Http_socket_reuse_compatible(SocketData_t *old, SocketData_t *new)
Can the old socket's fd be reused for the new socket?.
Definition http.c:832
static const int HTTP_SOCKET_TO_BE_FREED
Definition http.c:56
static void Http_server_remove(Server_t *srv)
Definition http.c:1094
static void Http_fd_map_add_entry(SocketData_t *sd)
Definition http.c:185
static const int HTTP_SOCKET_QUEUED
Definition http.c:55
static void Http_socket_reuse(int SKey)
If any entry in the socket data queue can reuse our connection, set it up and send off a new query.
Definition http.c:852
static void Http_connect_socket(ChainLink *Info)
This function is called after the DNS succeeds in solving a hostname.
Definition http.c:578
static void Http_send_query(SocketData_t *S)
Create and submit the HTTP query to the IO engine.
Definition http.c:489
static int Http_must_use_proxy(const char *hostname)
Test proxy settings and check the no_proxy domains list.
Definition http.c:673
static char * Http_get_referer(const DilloUrl *url)
Make the HTTP header's Referer line according to preferences (default is "host" i....
Definition http.c:335
static void Http_socket_free(int SKey)
Free SocketData_t struct.
Definition http.c:296
static DilloUrl * HTTP_Proxy
Definition http.c:105
static void Http_socket_enqueue(Server_t *srv, SocketData_t *sock)
Add socket data to the queue.
Definition http.c:1049
static Dlist * servers
Definition http.c:108
void a_Http_connect_done(int fd, bool_t success)
Definition http.c:214
static void Http_socket_activate(Server_t *srv, SocketData_t *sd)
Definition http.c:241
static int Http_sock_new(void)
Create and init a new SocketData_t struct, insert into ValidSocks, and return a primary key for it.
Definition http.c:167
static void Http_dns_cb(int Status, Dlist *addr_list, void *data)
Callback function for the DNS resolver.
Definition http.c:746
static char * Http_get_connect_str(const DilloUrl *url)
Return a new string for the request used to tunnel HTTPS through a proxy.
Definition http.c:703
static void Http_connect_tls(ChainLink *info)
Prepare an HTTPS connection.
Definition http.c:515
static void Http_fd_map_remove_entry(int fd)
Remove and free entry from fd_map.
Definition http.c:202
static char * HTTP_Proxy_Auth_base64
Definition http.c:106
void a_Http_freeall(void)
Deallocate memory used by http module.
Definition http.c:1144
int a_Http_proxy_auth(void)
Tell whether the proxy auth is already set (user:password).
Definition http.c:148
static Server_t * Http_server_get(const char *host, uint_t port, bool_t https)
Definition http.c:1070
static void Http_connect_socket_cb(int fd, void *data)
connect() couldn't complete before, but now it's ready, so let's try again.
Definition http.c:540
static const int HTTP_SOCKET_USE_PROXY
Definition http.c:54
static const int HTTP_SOCKET_TLS
Definition http.c:57
static Dstr * Http_make_content_type(const DilloUrl *url)
Generate Content-Type header value for a POST query.
Definition http.c:356
void a_Http_ccc(int Op, int Branch, int Dir, ChainLink *Info, void *Data1, void *Data2)
CCC function for the HTTP module.
Definition http.c:893
static void Http_connect_queued_sockets(Server_t *srv)
Definition http.c:249
static void Http_servers_remove_all(void)
Definition http.c:1110
static const int HTTP_SOCKET_IOWATCH_ACTIVE
Definition http.c:58
void a_IOwatch_add_fd(int fd, int when, Fl_FD_Handler Callback, void *usr_data=0)
Hook a Callback for a certain activities in a FD.
Definition iowatch.cc:22
void a_IOwatch_remove_fd(int fd, int when)
Remove a Callback for a given FD (or just remove some events)
Definition iowatch.cc:32
#define DIO_WRITE
Definition iowatch.hh:8
void a_Klist_remove(Klist_t *Klist, int Key)
Remove data by Key.
Definition klist.c:86
void * a_Klist_get_data(Klist_t *Klist, int Key)
Return the data pointer for a given Key (or NULL if not found)
Definition klist.c:43
void a_Klist_free(Klist_t **KlistPtr)
Free a Klist.
Definition klist.c:110
int a_Klist_insert(Klist_t **Klist, void *Data)
Insert a data pointer and return a key for it.
Definition klist.c:56
#define MSG_WARN(...)
Definition msg.h:26
char * a_Misc_encode_base64(const char *in)
Encodes string using base64 encoding.
Definition misc.c:426
DilloPrefs prefs
Global Data.
Definition prefs.c:33
char * a_Cookies_get_query(const DilloUrl *query_url, const DilloUrl *requester, int is_root_url)
Return a string containing cookie data for an HTTP query.
Definition cookies.c:185
A convenience data structure for passing data chunks between nodes.
Definition chain.h:52
char * Buf
Definition chain.h:53
char data[DILLO_ADDR_MAX]
Definition dns.h:27
int alen
Definition dns.h:26
int af
Definition dns.h:25
bool_t http_persistent_conns
Definition prefs.h:105
char * http_user_agent
Definition prefs.h:47
int32_t http_max_conns
Definition prefs.h:43
char * no_proxy
Definition prefs.h:48
char * http_proxyuser
Definition prefs.h:45
DilloUrl * http_proxy
Definition prefs.h:44
bool_t trace_http
Definition prefs.h:124
char * http_referer
Definition prefs.h:46
char * http_language
Definition prefs.h:42
Definition url.h:88
int flags
Definition url.h:98
Definition dlib.h:161
Definition dlib.h:131
Dstr_char_t * str
Definition dlib.h:134
int len
Definition dlib.h:133
int flags
Additional info.
Definition web.hh:29
DilloUrl * url
Requested URL.
Definition web.hh:25
DilloUrl * requester
URL that caused this request, or < NULL if user-initiated.
Definition web.hh:26
void a_Tls_reset_server_state(const DilloUrl *url)
Definition tls.c:131
void a_Tls_connect(int fd, const DilloUrl *url)
Definition tls.c:144
int a_Tls_connect_ready(const DilloUrl *url)
The purpose here is to permit a single initial connection to a server.
Definition tls.c:84
void a_Tls_close_by_fd(int fd)
Definition tls.c:157
#define TLS_CONNECT_NEVER
Definition tls.h:30
#define TLS_CONNECT_READY
Definition tls.h:32
void a_Url_free(DilloUrl *url)
Free a DilloUrl.
Definition url.c:207
DilloUrl * a_Url_new(const char *url_str, const char *base_url)
Transform (and resolve) an URL string into the respective DilloURL.
Definition url.c:370
DilloUrl * a_Url_dup(const DilloUrl *ori)
Duplicate a Url structure.
Definition url.c:476
#define URL_PATH(u)
Definition url.h:72
#define URL_E2EQuery
Definition url.h:35
#define URL_QUERY_(u)
Definition url.h:52
#define URL_FLAGS(u)
Definition url.h:79
#define URL_DATA(u)
Definition url.h:77
#define URL_MultipartEnc
Definition url.h:42
#define URL_STR(u)
Definition url.h:76
#define URL_QUERY(u)
Definition url.h:73
#define URL_SCHEME(u)
Definition url.h:70
#define URL_PATH_(u)
Definition url.h:51
#define URL_AUTHORITY(u)
Definition url.h:71
#define URL_Post
Definition url.h:33
#define URL_PORT(u)
Definition url.h:78
#define URL_HOST(u)
Definition url.h:75
int a_Web_valid(DilloWeb *web)
Validate a DilloWeb pointer.
Definition web.cc:144
#define WEB_Stylesheet
Definition web.hh:18
#define WEB_Image
Definition web.hh:17
#define WEB_RootUrl
Definition web.hh:16