114 #ifndef DISABLE_QHTTPCLIENT
123 #include <sys/types.h>
124 #include <sys/socket.h>
125 #include <netinet/in.h>
126 #include <netinet/tcp.h>
127 #include <arpa/inet.h>
129 #ifdef ENABLE_OPENSSL
130 #include "openssl/ssl.h"
131 #include "openssl/err.h"
134 #include "qinternal.h"
135 #include "utilities/qio.h"
136 #include "utilities/qstring.h"
137 #include "utilities/qsocket.h"
138 #include "containers/qlisttbl.h"
139 #include "containers/qgrow.h"
140 #include "extensions/qhttpclient.h"
142 #ifndef _DOXYGEN_SKIP
144 static bool open_(qhttpclient_t *client);
145 static bool setssl(qhttpclient_t *client);
146 static void settimeout(qhttpclient_t *client,
int timeoutms);
147 static void setkeepalive(qhttpclient_t *client,
bool keepalive);
148 static void setuseragent(qhttpclient_t *client,
const char *agentname);
150 static bool head(qhttpclient_t *client,
const char *uri,
int *rescode,
151 qlisttbl_t *reqheaders, qlisttbl_t *resheaders);
152 static bool get(qhttpclient_t *client,
const char *uri,
int fd, off_t *savesize,
153 int *rescode, qlisttbl_t *reqheaders, qlisttbl_t *resheaders,
154 bool (*callback)(
void *userdata, off_t recvbytes),
156 static bool put(qhttpclient_t *client,
const char *uri,
int fd, off_t length,
157 int *rescode, qlisttbl_t *reqheaders, qlisttbl_t *resheaders,
158 bool (*callback)(
void *userdata, off_t sentbytes),
160 static void *
cmd(qhttpclient_t *client,
const char *method,
const char *uri,
161 void *data,
size_t size,
int *rescode,
size_t *contentslength,
162 qlisttbl_t *reqheaders, qlisttbl_t *resheaders);
164 static bool sendrequest(qhttpclient_t *client,
const char *method,
165 const char *uri, qlisttbl_t *reqheaders);
166 static int readresponse(qhttpclient_t *client, qlisttbl_t *resheaders,
167 off_t *contentlength);
169 static ssize_t
gets_(qhttpclient_t *client,
char *buf,
size_t bufsize);
170 static ssize_t
read_(qhttpclient_t *client,
void *buf,
size_t nbytes);
171 static ssize_t
write_(qhttpclient_t *client,
const void *buf,
size_t nbytes);
172 static off_t
recvfile(qhttpclient_t *client,
int fd, off_t nbytes);
173 static off_t
sendfile_(qhttpclient_t *client,
int fd, off_t nbytes);
175 static bool _close(qhttpclient_t *client);
176 static void _free(qhttpclient_t *client);
179 static bool _set_socket_option(
int socket);
180 static bool _parse_uri(
const char *uri,
bool *protocol,
char *hostname,
181 size_t namesize,
int *port);
188 #define HTTP_NO_RESPONSE (0)
189 #define HTTP_CODE_CONTINUE (100)
190 #define HTTP_CODE_OK (200)
191 #define HTTP_CODE_CREATED (201)
192 #define HTTP_CODE_NO_CONTENT (204)
193 #define HTTP_CODE_MULTI_STATUS (207)
194 #define HTTP_CODE_MOVED_TEMPORARILY (302)
195 #define HTTP_CODE_NOT_MODIFIED (304)
196 #define HTTP_CODE_BAD_REQUEST (400)
197 #define HTTP_CODE_FORBIDDEN (403)
198 #define HTTP_CODE_NOT_FOUND (404)
199 #define HTTP_CODE_METHOD_NOT_ALLOWED (405)
200 #define HTTP_CODE_REQUEST_TIME_OUT (408)
201 #define HTTP_CODE_REQUEST_URI_TOO_LONG (414)
202 #define HTTP_CODE_INTERNAL_SERVER_ERROR (500)
203 #define HTTP_CODE_NOT_IMPLEMENTED (501)
204 #define HTTP_CODE_SERVICE_UNAVAILABLE (503)
206 #define HTTP_PROTOCOL_11 "HTTP/1.1"
211 #define SET_TCP_LINGER_TIMEOUT (15)
212 #define SET_TCP_NODELAY (1)
213 #define MAX_SHUTDOWN_WAIT (100)
214 #define MAX_ATOMIC_DATA_SIZE (32 * 1024)
216 #ifdef ENABLE_OPENSSL
246 bool ishttps =
false;
248 if (port == 0 || strstr(hostname,
"://") != NULL) {
249 if (_parse_uri(destname, &ishttps, hostname,
sizeof(hostname), &port)
251 DEBUG(
"Can't parse URI %s", destname);
255 DEBUG(
"https: %d, hostname: %s, port:%d\n", ishttps, hostname, port);
257 qstrcpy(hostname,
sizeof(hostname), destname);
261 struct sockaddr_in addr;
267 qhttpclient_t *client = (qhttpclient_t *) malloc(
sizeof(qhttpclient_t));
270 memset((
void *) client, 0,
sizeof(qhttpclient_t));
275 memcpy((
void *) &client->addr, (
void *) &addr,
sizeof(client->addr));
276 client->hostname = strdup(hostname);
285 client->open =
open_;
295 client->gets =
gets_;
296 client->read =
read_;
302 client->free =
_free;
323 static bool setssl(qhttpclient_t *client) {
324 #ifdef ENABLE_OPENSSL
325 static bool initialized =
false;
327 if (client->socket >= 0) {
333 if (initialized ==
false) {
335 SSL_load_error_strings();
340 if (client->ssl == NULL) {
341 client->ssl = malloc(
sizeof(
struct SslConn));
342 if (client->ssl == NULL)
return false;
343 memset(client->ssl, 0,
sizeof(
struct SslConn));
363 static void settimeout(qhttpclient_t *client,
int timeoutms) {
366 client->timeoutms = timeoutms;
381 client->keepalive = keepalive;
394 static void setuseragent(qhttpclient_t *client,
const char *useragent) {
395 if (client->useragent != NULL)
396 free(client->useragent);
397 client->useragent = strdup(useragent);
417 static bool open_(qhttpclient_t *client) {
418 if (client->socket >= 0) {
426 int sockfd = socket(AF_INET, SOCK_STREAM, 0);
428 DEBUG(
"sockfd creation failed.");
434 if (client->timeoutms > 0) {
435 sockflag = fcntl(sockfd, F_GETFL, 0);
436 fcntl(sockfd, F_SETFL, sockflag | O_NONBLOCK);
440 int status = connect(sockfd, (
struct sockaddr *) &client->addr,
441 sizeof(client->addr));
443 && (errno != EINPROGRESS
445 DEBUG(
"connection failed. (%d)", errno);
451 if (client->timeoutms > 0) {
452 fcntl(sockfd, F_SETFL, sockflag);
456 client->socket = sockfd;
459 _set_socket_option(sockfd);
461 #ifdef ENABLE_OPENSSL
463 if (client->ssl != NULL) {
465 struct SslConn *ssl = client->ssl;
466 ssl->ctx = SSL_CTX_new(SSLv23_client_method());
467 if (ssl->ctx == NULL) {
468 DEBUG(
"OpenSSL: %s", ERR_reason_error_string(ERR_get_error()));
474 ssl->ssl = SSL_new(ssl->ctx);
475 if (ssl->ssl == NULL) {
476 DEBUG(
"OpenSSL: %s", ERR_reason_error_string(ERR_get_error()));
482 if (SSL_set_fd(ssl->ssl, client->socket) != 1) {
483 DEBUG(
"OpenSSL: %s", ERR_reason_error_string(ERR_get_error()));
489 SSL_set_connect_state(ssl->ssl);
492 if (SSL_connect(ssl->ssl) != 1) {
493 DEBUG(
"OpenSSL: %s", ERR_reason_error_string(ERR_get_error()));
498 DEBUG(
"ssl initialized");
553 static bool head(qhttpclient_t *client,
const char *uri,
int *rescode,
554 qlisttbl_t *reqheaders, qlisttbl_t *resheaders) {
561 bool freeReqHeaders =
false;
562 if (reqheaders == NULL) {
564 QLISTTBL_UNIQUE | QLISTTBL_CASEINSENSITIVE);
565 freeReqHeaders =
true;
569 reqheaders->putstr(reqheaders,
"Accept",
"*/*");
572 bool sendret =
sendrequest(client,
"HEAD", uri, reqheaders);
573 if (freeReqHeaders ==
true)
574 reqheaders->free(reqheaders);
575 if (sendret ==
false) {
588 if (
read_(client, NULL, clength) != clength) {
594 if (client->keepalive ==
false || client->connclose ==
true) {
598 if (resno == HTTP_CODE_OK)
689 static bool get(qhttpclient_t *client,
const char *uri,
int fd, off_t *savesize,
690 int *rescode, qlisttbl_t *reqheaders, qlisttbl_t *resheaders,
691 bool (*callback)(
void *userdata, off_t recvbytes),
697 if (savesize != NULL)
701 bool freeReqHeaders =
false;
702 if (reqheaders == NULL) {
704 QLISTTBL_UNIQUE | QLISTTBL_CASEINSENSITIVE);
705 freeReqHeaders =
true;
709 reqheaders->putstr(reqheaders,
"Accept",
"*/*");
712 bool sendret =
sendrequest(client,
"GET", uri, reqheaders);
713 if (freeReqHeaders ==
true)
714 reqheaders->free(reqheaders);
715 if (sendret ==
false) {
727 if (resno != HTTP_CODE_OK) {
730 if (
read_(client, NULL, clength) != clength) {
736 if (client->keepalive ==
false || client->connclose ==
true) {
744 if (callback != NULL && callback(userdata, recv) ==
false) {
750 while (recv < clength) {
751 unsigned int recvsize;
752 if (clength - recv < MAX_ATOMIC_DATA_SIZE) {
753 recvsize = clength - recv;
755 recvsize = MAX_ATOMIC_DATA_SIZE;
758 ssize_t ret =
recvfile(client, fd, recvsize);
762 if (savesize != NULL)
765 if (callback != NULL) {
766 if (callback(userdata, recv) ==
false) {
773 if (recv != clength) {
778 }
else if (clength == -1) {
779 bool completed =
false;
783 if (
gets_(client, buf,
sizeof(buf)) <= 0)
787 unsigned int recvsize;
788 sscanf(buf,
"%x", &recvsize);
796 ssize_t ret =
recvfile(client, fd, recvsize);
800 DEBUG(
"%zd %zd", recv, ret);
801 if (savesize != NULL)
806 if (
gets_(client, buf,
sizeof(buf)) <= 0)
810 if (recvsize > 0 && callback != NULL
811 && callback(userdata, recv) ==
false) {
815 }
while (completed ==
false);
817 if (completed ==
false) {
818 DEBUG(
"Broken pipe. %jd/chunked, errno=%d", recv, errno);
825 if (client->keepalive ==
false || client->connclose ==
true) {
918 static bool put(qhttpclient_t *client,
const char *uri,
int fd, off_t length,
919 int *rescode, qlisttbl_t *reqheaders, qlisttbl_t *resheaders,
920 bool (*callback)(
void *userdata, off_t sentbytes),
928 bool freeReqHeaders =
false;
929 if (reqheaders == NULL) {
931 QLISTTBL_UNIQUE | QLISTTBL_CASEINSENSITIVE);
932 freeReqHeaders =
true;
936 reqheaders->putstrf(reqheaders,
"Content-Length",
"%jd", length);
937 reqheaders->putstr(reqheaders,
"Expect",
"100-continue");
940 bool sendret =
sendrequest(client,
"PUT", uri, reqheaders);
941 if (freeReqHeaders ==
true) {
942 reqheaders->free(reqheaders);
945 if (sendret ==
false) {
952 DEBUG(
"timed out %d", client->timeoutms);
960 if (resno != HTTP_CODE_CONTINUE) {
965 if (
read_(client, NULL, clength) != clength) {
971 if (client->keepalive ==
false || client->connclose ==
true) {
979 if (callback != NULL) {
980 if (callback(userdata, sent) ==
false) {
986 while (sent < length) {
988 if (length - sent < MAX_ATOMIC_DATA_SIZE)
989 sendsize = length - sent;
991 sendsize = MAX_ATOMIC_DATA_SIZE;
993 ssize_t ret =
sendfile_(client, fd, sendsize);
998 if (callback != NULL) {
999 if (callback(userdata, sent) ==
false) {
1006 if (sent != length) {
1011 if (callback != NULL) {
1012 if (callback(userdata, sent) ==
false) {
1022 if (rescode != NULL)
1025 if (resno == HTTP_NO_RESPONSE) {
1031 if (
read_(client, NULL, clength) != clength) {
1037 if (client->keepalive ==
false || client->connclose ==
true) {
1041 if (resno == HTTP_CODE_CREATED)
1091 static void *
cmd(qhttpclient_t *client,
const char *method,
const char *uri,
1092 void *data,
size_t size,
int *rescode,
size_t *contentslength,
1093 qlisttbl_t *reqheaders, qlisttbl_t *resheaders) {
1096 if (rescode != NULL)
1098 if (contentslength != NULL)
1099 *contentslength = 0;
1102 bool freeReqHeaders =
false;
1103 if (reqheaders == NULL && data != NULL && size > 0) {
1105 QLISTTBL_UNIQUE | QLISTTBL_CASEINSENSITIVE);
1106 reqheaders->putstrf(reqheaders,
"Content-Length",
"%jd", size);
1107 freeReqHeaders =
true;
1110 bool sendret =
sendrequest(client, method, uri, reqheaders);
1111 if (freeReqHeaders ==
true) {
1112 reqheaders->free(reqheaders);
1115 if (sendret ==
false) {
1121 if (data != NULL && size > 0) {
1122 ssize_t written =
write_(client, data, size);
1123 if (written != size) {
1131 int resno =
readresponse(client, resheaders, &clength);
1132 if (rescode != NULL)
1134 if (contentslength != NULL)
1135 *contentslength = clength;
1138 void *content = NULL;
1140 content = malloc(clength + 1);
1141 if (content != NULL) {
1142 if (
read_(client, content, clength) == clength) {
1143 *(
char *) (content + clength) =
'\0';
1152 content = strdup(
"");
1156 if (client->keepalive ==
false || client->connclose ==
true) {
1187 const char *uri, qlisttbl_t *reqheaders) {
1188 if (
open_(client) ==
false) {
1193 bool freeReqHeaders =
false;
1194 if (reqheaders == NULL) {
1196 QLISTTBL_UNIQUE | QLISTTBL_CASEINSENSITIVE);
1197 if (reqheaders == NULL)
1199 freeReqHeaders =
true;
1203 if (reqheaders->get(reqheaders,
"Host", NULL,
false) == NULL) {
1204 reqheaders->putstrf(reqheaders,
"Host",
"%s:%d", client->hostname,
1207 if (reqheaders->get(reqheaders,
"User-Agent", NULL,
false) == NULL) {
1208 reqheaders->putstr(reqheaders,
"User-Agent", client->useragent);
1210 if (reqheaders->get(reqheaders,
"Connection", NULL,
false) == NULL) {
1212 reqheaders,
"Connection",
1213 (client->keepalive ==
true) ?
"Keep-Alive" :
"close");
1217 qgrow_t *outBuf =
qgrow(0);
1222 outBuf->addstrf(outBuf,
"%s %s %s\r\n", method, uri,
1227 memset((
void *) &obj, 0,
sizeof(obj));
1228 reqheaders->lock(reqheaders);
1229 while (reqheaders->getnext(reqheaders, &obj, NULL,
false) ==
true) {
1230 outBuf->addstrf(outBuf,
"%s: %s\r\n", obj.name, (
char *) obj.data);
1232 reqheaders->unlock(reqheaders);
1234 outBuf->addstrf(outBuf,
"\r\n");
1238 char *
final = outBuf->toarray(outBuf, &towrite);
1239 ssize_t written = 0;
1240 if (
final != NULL) {
1241 written =
write_(client,
final, towrite);
1246 outBuf->free(outBuf);
1247 if (freeReqHeaders ==
true)
1248 reqheaders->free(reqheaders);
1250 if (written > 0 && written == towrite)
1286 off_t *contentlength) {
1287 if (contentlength != NULL) {
1293 if (
gets_(client, buf,
sizeof(buf)) <= 0)
1294 return HTTP_NO_RESPONSE;
1297 if (strncmp(buf,
"HTTP/", CONST_STRLEN(
"HTTP/")))
1298 return HTTP_NO_RESPONSE;
1299 char *tmp = strstr(buf,
" ");
1301 return HTTP_NO_RESPONSE;
1302 int rescode = atoi(tmp + 1);
1304 return HTTP_NO_RESPONSE;
1307 while (
gets_(client, buf,
sizeof(buf)) > 0) {
1313 char *value = strstr(buf,
":");
1314 if (value != NULL) {
1323 if (resheaders != NULL) {
1324 resheaders->putstr(resheaders, name, value);
1328 if (!strcasecmp(name,
"Connection")) {
1329 if (!strcasecmp(value,
"close")) {
1330 client->connclose =
true;
1334 else if (contentlength != NULL && *contentlength == 0) {
1335 if (!strcasecmp(name,
"Content-Length")) {
1336 *contentlength = atoll(value);
1339 else if (!strcasecmp(name,
"Transfer-Encoding")
1340 && !strcasecmp(value,
"chunked")) {
1341 *contentlength = -1;
1364 static ssize_t
gets_(qhttpclient_t *client,
char *buf,
size_t bufsize) {
1365 #ifdef ENABLE_OPENSSL
1366 if (client->ssl == NULL) {
1367 return qio_gets(client->socket, buf, bufsize, client->timeoutms);
1369 if (bufsize <= 1)
return -1;
1371 struct SslConn *ssl = client->ssl;
1372 ssize_t readcnt = 0;
1375 for (ptr = buf; readcnt < (bufsize - 1); ptr++) {
1381 int rsize = SSL_read(ssl->ssl, ptr, 1);
1383 unsigned long sslerr = ERR_get_error();
1384 if (sslerr == SSL_ERROR_WANT_READ) {
1388 DEBUG(
"OpenSSL: %s (%d)",
1389 ERR_reason_error_string(sslerr), rsize);
1394 if (*ptr ==
'\r') ptr--;
1395 else if (*ptr ==
'\n')
break;
1399 DEBUG(
"SSL_read: %s (%zd)", buf, readcnt);
1401 if (readcnt > 0)
return readcnt;
1405 return qio_gets(client->socket, buf, bufsize, client->timeoutms);
1428 static ssize_t
read_(qhttpclient_t *client,
void *buf,
size_t nbytes) {
1429 #ifdef ENABLE_OPENSSL
1430 if (client->ssl == NULL) {
1431 return qio_read(client->socket, buf, nbytes, client->timeoutms);
1433 if (nbytes == 0)
return 0;
1435 struct SslConn *ssl = client->ssl;
1437 while (total < nbytes) {
1444 rsize = SSL_read(ssl->ssl, buf + total, nbytes - total);
1447 int toread = nbytes - total;
1448 if (toread >
sizeof(trash)) toread =
sizeof(trash);
1449 rsize = SSL_read(ssl->ssl, trash, toread);
1452 DEBUG(
"OpenSSL: %s (%d)",
1453 ERR_reason_error_string(ERR_get_error()), rsize);
1454 unsigned long sslerr = ERR_get_error();
1455 if (sslerr == SSL_ERROR_WANT_READ) {
1464 DEBUG(
"SSL_read: %zd", total);
1465 if (total > 0)
return total;
1469 return qio_read(client->socket, buf, nbytes, client->timeoutms);
1482 static ssize_t
write_(qhttpclient_t *client,
const void *buf,
size_t nbytes) {
1483 #ifdef ENABLE_OPENSSL
1484 if (client->ssl == NULL) {
1485 return qio_write(client->socket, buf, nbytes, -1);
1487 if (nbytes == 0)
return 0;
1489 struct SslConn *ssl = client->ssl;
1491 while (total < nbytes) {
1493 int wsize = SSL_write(ssl->ssl, buf + total, nbytes - total);
1495 DEBUG(
"OpenSSL: %s (%d)",
1496 ERR_reason_error_string(ERR_get_error()), wsize);
1497 unsigned long sslerr = ERR_get_error();
1498 if (sslerr == SSL_ERROR_WANT_WRITE) {
1507 DEBUG(
"SSL_write: %zd/%zu", total, nbytes);
1508 if (total > 0)
return total;
1512 return qio_write(client->socket, buf, nbytes, -1);
1526 static off_t
recvfile(qhttpclient_t *client,
int fd, off_t nbytes) {
1530 unsigned char buf[MAX_ATOMIC_DATA_SIZE];
1533 while (total < nbytes) {
1535 if (nbytes - total <=
sizeof(buf))
1536 chunksize = nbytes - total;
1538 chunksize =
sizeof(buf);
1541 ssize_t rsize =
read_(client, buf, chunksize);
1546 ssize_t wsize =
qio_write(fd, buf, rsize, -1);
1547 DEBUG(
"FILE write: %zd", wsize);
1552 if (rsize != wsize) {
1553 DEBUG(
"size mismatch. read:%zd, write:%zd", rsize, wsize);
1572 static off_t
sendfile_(qhttpclient_t *client,
int fd, off_t nbytes) {
1576 unsigned char buf[MAX_ATOMIC_DATA_SIZE];
1579 while (total < nbytes) {
1581 if (nbytes - total <=
sizeof(buf))
1582 chunksize = nbytes - total;
1584 chunksize =
sizeof(buf);
1587 ssize_t rsize =
qio_read(fd, buf, chunksize, -1);
1588 DEBUG(
"FILE read: %zd", rsize);
1593 ssize_t wsize =
write_(client, buf, rsize);
1598 if (rsize != wsize) {
1599 DEBUG(
"size mismatch. read:%zd, write:%zd", rsize, wsize);
1621 if (client->socket < 0)
1624 #ifdef ENABLE_OPENSSL
1626 if (client->ssl != NULL) {
1627 struct SslConn *ssl = client->ssl;
1629 if (ssl->ssl != NULL) {
1630 SSL_shutdown(ssl->ssl);
1635 if (ssl->ctx != NULL) {
1636 SSL_CTX_free(ssl->ctx);
1643 if (client->ssl == NULL && MAX_SHUTDOWN_WAIT >= 0
1644 && shutdown(client->socket, SHUT_WR) == 0) {
1646 while (
qio_read(client->socket, buf,
sizeof(buf), MAX_SHUTDOWN_WAIT) > 0);
1650 close(client->socket);
1651 client->socket = -1;
1652 client->connclose =
false;
1670 static void _free(qhttpclient_t *client) {
1671 if (client->socket >= 0) {
1672 client->close(client);
1675 if (client->ssl != NULL)
1677 if (client->hostname != NULL)
1678 free(client->hostname);
1679 if (client->useragent != NULL)
1680 free(client->useragent);
1685 #ifndef _DOXYGEN_SKIP
1686 static bool _set_socket_option(
int socket) {
1690 if (SET_TCP_LINGER_TIMEOUT > 0) {
1693 li.l_linger = SET_TCP_LINGER_TIMEOUT;
1694 if (setsockopt(socket, SOL_SOCKET, SO_LINGER, &li,
1695 sizeof(
struct linger)) < 0) {
1701 if (SET_TCP_NODELAY > 0) {
1702 int so_tcpnodelay = 1;
1703 if (setsockopt(socket, IPPROTO_TCP, TCP_NODELAY, &so_tcpnodelay,
1704 sizeof(so_tcpnodelay)) < 0) {
1712 static bool _parse_uri(
const char *uri,
bool *protocol,
char *hostname,
1713 size_t namesize,
int *port) {
1715 if (!strncasecmp(uri,
"http://", CONST_STRLEN(
"http://"))) {
1718 }
else if (!strncasecmp(uri,
"https://", CONST_STRLEN(
"https://"))) {
1725 char *t1 = strstr(uri,
"://");
1727 char *t2 = strstr(t1,
"/");
1729 t2 = (
char *) uri + strlen(uri);
1731 if (t2 - t1 + 1 > namesize)
1733 qstrncpy(hostname, namesize, t1, t2 - t1);
1735 t1 = strstr(hostname,
":");
1738 *port = atoi(t1 + 1);
static bool _close(qhttpclient_t *client)
qhttpclient->close(): Closes the connection.
static void setkeepalive(qhttpclient_t *client, bool keepalive)
qhttpclient->setkeepalive(): Sets KEEP-ALIVE feature on/off.
static ssize_t gets_(qhttpclient_t *client, char *buf, size_t bufsize)
qhttpclient->gets(): Reads a text line from a HTTP/HTTPS stream.
static void settimeout(qhttpclient_t *client, int timeoutms)
qhttpclient->settimeout(): Sets connection wait timeout.
ssize_t qio_gets(int fd, char *buf, size_t bufsize, int timeoutms)
Read a line from a file descriptor into the buffer pointed to until either a terminating newline or E...
static bool sendrequest(qhttpclient_t *client, const char *method, const char *uri, qlisttbl_t *reqheaders)
qhttpclient->sendrequest(): Sends a HTTP request to the remote host.
bool qsocket_get_addr(struct sockaddr_in *addr, const char *hostname, int port)
Convert hostname to sockaddr_in structure.
qlisttbl_t * qlisttbl(int options)
Create a new Q_LIST linked-list container.
char * qstrncpy(char *dst, size_t size, const char *src, size_t nbytes)
Copy src string to dst no more than n bytes.
static off_t sendfile_(qhttpclient_t *client, int fd, off_t nbytes)
qhttpclient->sendfile(): Send file data to a HTTP/HTTPS stream.
static void * cmd(qhttpclient_t *client, const char *method, const char *uri, void *data, size_t size, int *rescode, size_t *contentslength, qlisttbl_t *reqheaders, qlisttbl_t *resheaders)
qhttpclient->cmd(): Sends a custom request(method) to the remote host and reads it's response...
static bool put(qhttpclient_t *client, const char *uri, int fd, off_t length, int *rescode, qlisttbl_t *reqheaders, qlisttbl_t *resheaders, bool(*callback)(void *userdata, off_t sentbytes), void *userdata)
qhttpclient->put(): Uploads a file to the remote host using PUT method.
char * qstrtrim(char *str)
Remove white spaces(including CR, LF) from head and tail of the string.
static void setuseragent(qhttpclient_t *client, const char *useragent)
qhttpclient->setuseragent(): Sets user-agent string.
static ssize_t write_(qhttpclient_t *client, const void *buf, size_t nbytes)
qhttpclient->write(): Writes data to a HTTP/HTTPS stream.
static void _free(qhttpclient_t *client)
qhttpclient->free(): Free object.
char * qstrcpy(char *dst, size_t size, const char *src)
Copy src string to dst.
static bool setssl(qhttpclient_t *client)
qhttpclient->setssl(): Sets connection to HTTPS connection
static int readresponse(qhttpclient_t *client, qlisttbl_t *resheaders, off_t *contentlength)
qhttpclient->readresponse(): Reads HTTP response header from the remote host.
ssize_t qio_read(int fd, void *buf, size_t nbytes, int timeoutms)
Read from a file descriptor.
qhttpclient_t * qhttpclient(const char *destname, int port)
Initialize & create new HTTP client.
qgrow_t * qgrow(int options)
Initialize grow.
int qio_wait_writable(int fd, int timeoutms)
Test & wait until the file descriptor is ready for writing.
ssize_t qio_write(int fd, const void *buf, size_t nbytes, int timeoutms)
Write to a file descriptor.
static off_t recvfile(qhttpclient_t *client, int fd, off_t nbytes)
qhttpclient->recvfile(): Reads data from a HTTP/HTTPS stream and save into a file descriptor...
static ssize_t read_(qhttpclient_t *client, void *buf, size_t nbytes)
qhttpclient->read(): Reads data from a HTTP/HTTPS stream.
int qio_wait_readable(int fd, int timeoutms)
Test & wait until the file descriptor has readable data.
static bool head(qhttpclient_t *client, const char *uri, int *rescode, qlisttbl_t *reqheaders, qlisttbl_t *resheaders)
qhttpclient->head(): Sends a HEAD request.
static bool open_(qhttpclient_t *client)
qhttpclient->open(): Opens a connection to the remote host.