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>
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"
144static bool open_(qhttpclient_t *client);
145static bool setssl(qhttpclient_t *client);
146static void settimeout(qhttpclient_t *client,
int timeoutms);
147static void setkeepalive(qhttpclient_t *client,
bool keepalive);
148static void setuseragent(qhttpclient_t *client,
const char *agentname);
150static bool head(qhttpclient_t *client,
const char *uri,
int *rescode,
151 qlisttbl_t *reqheaders, qlisttbl_t *resheaders);
152static 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),
156static 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),
160static 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);
164static bool sendrequest(qhttpclient_t *client,
const char *method,
165 const char *uri, qlisttbl_t *reqheaders);
166static int readresponse(qhttpclient_t *client, qlisttbl_t *resheaders,
167 off_t *contentlength);
169static ssize_t
gets_(qhttpclient_t *client,
char *buf,
size_t bufsize);
170static ssize_t
read_(qhttpclient_t *client,
void *buf,
size_t nbytes);
171static ssize_t
write_(qhttpclient_t *client,
const void *buf,
size_t nbytes);
172static off_t
recvfile(qhttpclient_t *client,
int fd, off_t nbytes);
173static off_t
sendfile_(qhttpclient_t *client,
int fd, off_t nbytes);
175static bool _close(qhttpclient_t *client);
176static void _free(qhttpclient_t *client);
179static bool _set_socket_option(
int socket);
180static 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)
246 bool ishttps =
false;
248 if (port == 0 || strstr(destname,
"://") != 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;
323static bool setssl(qhttpclient_t *client) {
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));
363static void settimeout(qhttpclient_t *client,
int timeoutms) {
366 client->timeoutms = timeoutms;
381 client->keepalive = keepalive;
394static void setuseragent(qhttpclient_t *client,
const char *useragent) {
395 if (client->useragent != NULL)
396 free(client->useragent);
397 client->useragent = strdup(useragent);
417static 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);
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);
491#ifndef OPENSSL_NO_TLSEXT
493 ssl->ssl->tlsext_hostname = client->hostname;
497 if (SSL_connect(ssl->ssl) != 1) {
498 DEBUG(
"OpenSSL: %s", ERR_reason_error_string(ERR_get_error()));
503 DEBUG(
"ssl initialized");
558static bool head(qhttpclient_t *client,
const char *uri,
int *rescode,
559 qlisttbl_t *reqheaders, qlisttbl_t *resheaders) {
566 bool freeReqHeaders =
false;
567 if (reqheaders == NULL) {
569 QLISTTBL_UNIQUE | QLISTTBL_CASEINSENSITIVE);
570 freeReqHeaders =
true;
574 reqheaders->putstr(reqheaders,
"Accept",
"*/*");
577 bool sendret =
sendrequest(client,
"HEAD", uri, reqheaders);
578 if (freeReqHeaders ==
true)
579 reqheaders->free(reqheaders);
580 if (sendret ==
false) {
593 if (
read_(client, NULL, clength) != clength) {
599 if (client->keepalive ==
false || client->connclose ==
true) {
603 if (resno == HTTP_CODE_OK)
694static bool get(qhttpclient_t *client,
const char *uri,
int fd, off_t *savesize,
695 int *rescode, qlisttbl_t *reqheaders, qlisttbl_t *resheaders,
696 bool (*callback)(
void *userdata, off_t recvbytes),
702 if (savesize != NULL)
706 bool freeReqHeaders =
false;
707 if (reqheaders == NULL) {
709 QLISTTBL_UNIQUE | QLISTTBL_CASEINSENSITIVE);
710 freeReqHeaders =
true;
714 reqheaders->putstr(reqheaders,
"Accept",
"*/*");
717 bool sendret =
sendrequest(client,
"GET", uri, reqheaders);
718 if (freeReqHeaders ==
true)
719 reqheaders->free(reqheaders);
720 if (sendret ==
false) {
732 if (resno != HTTP_CODE_OK) {
735 if (
read_(client, NULL, clength) != clength) {
741 if (client->keepalive ==
false || client->connclose ==
true) {
749 if (callback != NULL && callback(userdata, recv) ==
false) {
755 while (recv < clength) {
756 unsigned int recvsize;
757 if (clength - recv < MAX_ATOMIC_DATA_SIZE) {
758 recvsize = clength - recv;
760 recvsize = MAX_ATOMIC_DATA_SIZE;
763 ssize_t ret =
recvfile(client, fd, recvsize);
767 if (savesize != NULL)
770 if (callback != NULL) {
771 if (callback(userdata, recv) ==
false) {
778 if (recv != clength) {
783 }
else if (clength == -1) {
784 bool completed =
false;
788 if (
gets_(client, buf,
sizeof(buf)) <= 0)
792 unsigned int recvsize;
793 if (sscanf(buf,
"%x", &recvsize) != 1) {
804 ssize_t ret =
recvfile(client, fd, recvsize);
808 DEBUG(
"%zd %zd", recv, ret);
809 if (savesize != NULL)
814 if (
gets_(client, buf,
sizeof(buf)) <= 0)
818 if (recvsize > 0 && callback != NULL
819 && callback(userdata, recv) ==
false) {
823 }
while (completed ==
false);
825 if (completed ==
false) {
826 DEBUG(
"Broken pipe. %jd/chunked, errno=%d", recv, errno);
833 if (client->keepalive ==
false || client->connclose ==
true) {
926static bool put(qhttpclient_t *client,
const char *uri,
int fd, off_t length,
927 int *rescode, qlisttbl_t *reqheaders, qlisttbl_t *resheaders,
928 bool (*callback)(
void *userdata, off_t sentbytes),
936 bool freeReqHeaders =
false;
937 if (reqheaders == NULL) {
939 QLISTTBL_UNIQUE | QLISTTBL_CASEINSENSITIVE);
940 freeReqHeaders =
true;
944 reqheaders->putstrf(reqheaders,
"Content-Length",
"%jd", length);
945 reqheaders->putstr(reqheaders,
"Expect",
"100-continue");
948 bool sendret =
sendrequest(client,
"PUT", uri, reqheaders);
949 if (freeReqHeaders ==
true) {
950 reqheaders->free(reqheaders);
953 if (sendret ==
false) {
960 DEBUG(
"timed out %d", client->timeoutms);
968 if (resno != HTTP_CODE_CONTINUE) {
973 if (
read_(client, NULL, clength) != clength) {
979 if (client->keepalive ==
false || client->connclose ==
true) {
987 if (callback != NULL) {
988 if (callback(userdata, sent) ==
false) {
994 while (sent < length) {
996 if (length - sent < MAX_ATOMIC_DATA_SIZE)
997 sendsize = length - sent;
999 sendsize = MAX_ATOMIC_DATA_SIZE;
1001 ssize_t ret =
sendfile_(client, fd, sendsize);
1006 if (callback != NULL) {
1007 if (callback(userdata, sent) ==
false) {
1014 if (sent != length) {
1019 if (callback != NULL) {
1020 if (callback(userdata, sent) ==
false) {
1030 if (rescode != NULL)
1033 if (resno == HTTP_NO_RESPONSE) {
1039 if (
read_(client, NULL, clength) != clength) {
1045 if (client->keepalive ==
false || client->connclose ==
true) {
1049 if (resno == HTTP_CODE_CREATED)
1098static void *
cmd(qhttpclient_t *client,
const char *method,
const char *uri,
1099 void *data,
size_t size,
int *rescode,
size_t *contentslength,
1100 qlisttbl_t *reqheaders, qlisttbl_t *resheaders) {
1103 if (rescode != NULL)
1105 if (contentslength != NULL)
1106 *contentslength = 0;
1109 bool freeReqHeaders =
false;
1110 if (reqheaders == NULL && data != NULL && size > 0) {
1112 QLISTTBL_UNIQUE | QLISTTBL_CASEINSENSITIVE);
1113 reqheaders->putstrf(reqheaders,
"Content-Length",
"%jd", size);
1114 freeReqHeaders =
true;
1117 bool sendret =
sendrequest(client, method, uri, reqheaders);
1118 if (freeReqHeaders ==
true) {
1119 reqheaders->free(reqheaders);
1122 if (sendret ==
false) {
1128 if (data != NULL && size > 0) {
1129 ssize_t written =
write_(client, data, size);
1130 if (written != size) {
1138 int resno =
readresponse(client, resheaders, &clength);
1139 if (rescode != NULL)
1141 if (contentslength != NULL)
1142 *contentslength = clength;
1145 void *content = NULL;
1147 content = malloc(clength + 1);
1148 if (content != NULL) {
1149 if (
read_(client, content, clength) == clength) {
1150 *(
char *) (content + clength) =
'\0';
1159 content = strdup(
"");
1163 if (client->keepalive ==
false || client->connclose ==
true) {
1194 const char *uri, qlisttbl_t *reqheaders) {
1195 if (
open_(client) ==
false) {
1200 bool freeReqHeaders =
false;
1201 if (reqheaders == NULL) {
1203 QLISTTBL_UNIQUE | QLISTTBL_CASEINSENSITIVE);
1204 if (reqheaders == NULL)
1206 freeReqHeaders =
true;
1210 if (reqheaders->get(reqheaders,
"Host", NULL,
false) == NULL) {
1211 reqheaders->putstrf(reqheaders,
"Host",
"%s:%d", client->hostname,
1214 if (reqheaders->get(reqheaders,
"User-Agent", NULL,
false) == NULL) {
1215 reqheaders->putstr(reqheaders,
"User-Agent", client->useragent);
1217 if (reqheaders->get(reqheaders,
"Connection", NULL,
false) == NULL) {
1219 reqheaders,
"Connection",
1220 (client->keepalive ==
true) ?
"Keep-Alive" :
"close");
1224 qgrow_t *outBuf =
qgrow(0);
1229 outBuf->addstrf(outBuf,
"%s %s %s\r\n", method, uri,
1234 memset((
void *) &obj, 0,
sizeof(obj));
1235 reqheaders->lock(reqheaders);
1236 while (reqheaders->getnext(reqheaders, &obj, NULL,
false) ==
true) {
1237 outBuf->addstrf(outBuf,
"%s: %s\r\n", obj.name, (
char *) obj.data);
1239 reqheaders->unlock(reqheaders);
1241 outBuf->addstrf(outBuf,
"\r\n");
1245 char *
final = outBuf->toarray(outBuf, &towrite);
1246 ssize_t written = 0;
1247 if (
final != NULL) {
1248 written =
write_(client,
final, towrite);
1253 outBuf->free(outBuf);
1254 if (freeReqHeaders ==
true)
1255 reqheaders->free(reqheaders);
1257 if (written > 0 && written == towrite)
1293 off_t *contentlength) {
1294 if (contentlength != NULL) {
1300 if (
gets_(client, buf,
sizeof(buf)) <= 0)
1301 return HTTP_NO_RESPONSE;
1304 if (strncmp(buf,
"HTTP/", CONST_STRLEN(
"HTTP/")))
1305 return HTTP_NO_RESPONSE;
1306 char *tmp = strstr(buf,
" ");
1308 return HTTP_NO_RESPONSE;
1309 int rescode = atoi(tmp + 1);
1311 return HTTP_NO_RESPONSE;
1314 while (
gets_(client, buf,
sizeof(buf)) > 0) {
1320 char *value = strstr(buf,
":");
1321 if (value != NULL) {
1330 if (resheaders != NULL) {
1331 resheaders->putstr(resheaders, name, value);
1335 if (!strcasecmp(name,
"Connection")) {
1336 if (!strcasecmp(value,
"close")) {
1337 client->connclose =
true;
1341 else if (contentlength != NULL && *contentlength == 0) {
1342 if (!strcasecmp(name,
"Content-Length")) {
1343 *contentlength = atoll(value);
1346 else if (!strcasecmp(name,
"Transfer-Encoding")
1347 && !strcasecmp(value,
"chunked")) {
1348 *contentlength = -1;
1371static ssize_t
gets_(qhttpclient_t *client,
char *buf,
size_t bufsize) {
1372#ifdef ENABLE_OPENSSL
1373 if (client->ssl == NULL) {
1374 return qio_gets(client->socket, buf, bufsize, client->timeoutms);
1376 if (bufsize <= 1)
return -1;
1378 struct SslConn *ssl = client->ssl;
1379 ssize_t readcnt = 0;
1382 for (ptr = buf; readcnt < (bufsize - 1); ptr++) {
1388 int rsize = SSL_read(ssl->ssl, ptr, 1);
1390 unsigned long sslerr = ERR_get_error();
1391 if (sslerr == SSL_ERROR_WANT_READ) {
1395 DEBUG(
"OpenSSL: %s (%d)",
1396 ERR_reason_error_string(sslerr), rsize);
1401 if (*ptr ==
'\r') ptr--;
1402 else if (*ptr ==
'\n')
break;
1406 DEBUG(
"SSL_read: %s (%zd)", buf, readcnt);
1408 if (readcnt > 0)
return readcnt;
1412 return qio_gets(client->socket, buf, bufsize, client->timeoutms);
1435static ssize_t
read_(qhttpclient_t *client,
void *buf,
size_t nbytes) {
1436#ifdef ENABLE_OPENSSL
1437 if (client->ssl == NULL) {
1438 return qio_read(client->socket, buf, nbytes, client->timeoutms);
1440 if (nbytes == 0)
return 0;
1442 struct SslConn *ssl = client->ssl;
1444 while (total < nbytes) {
1451 rsize = SSL_read(ssl->ssl, buf + total, nbytes - total);
1454 int toread = nbytes - total;
1455 if (toread >
sizeof(trash)) toread =
sizeof(trash);
1456 rsize = SSL_read(ssl->ssl, trash, toread);
1459 DEBUG(
"OpenSSL: %s (%d)",
1460 ERR_reason_error_string(ERR_get_error()), rsize);
1461 unsigned long sslerr = ERR_get_error();
1462 if (sslerr == SSL_ERROR_WANT_READ) {
1471 DEBUG(
"SSL_read: %zd", total);
1472 if (total > 0)
return total;
1476 return qio_read(client->socket, buf, nbytes, client->timeoutms);
1489static ssize_t
write_(qhttpclient_t *client,
const void *buf,
size_t nbytes) {
1490#ifdef ENABLE_OPENSSL
1491 if (client->ssl == NULL) {
1492 return qio_write(client->socket, buf, nbytes, -1);
1494 if (nbytes == 0)
return 0;
1496 struct SslConn *ssl = client->ssl;
1498 while (total < nbytes) {
1500 int wsize = SSL_write(ssl->ssl, buf + total, nbytes - total);
1502 DEBUG(
"OpenSSL: %s (%d)",
1503 ERR_reason_error_string(ERR_get_error()), wsize);
1504 unsigned long sslerr = ERR_get_error();
1505 if (sslerr == SSL_ERROR_WANT_WRITE) {
1514 DEBUG(
"SSL_write: %zd/%zu", total, nbytes);
1515 if (total > 0)
return total;
1519 return qio_write(client->socket, buf, nbytes, -1);
1533static off_t
recvfile(qhttpclient_t *client,
int fd, off_t nbytes) {
1537 unsigned char buf[MAX_ATOMIC_DATA_SIZE];
1540 while (total < nbytes) {
1542 if (nbytes - total <=
sizeof(buf))
1543 chunksize = nbytes - total;
1545 chunksize =
sizeof(buf);
1548 ssize_t rsize =
read_(client, buf, chunksize);
1553 ssize_t wsize =
qio_write(fd, buf, rsize, -1);
1554 DEBUG(
"FILE write: %zd", wsize);
1559 if (rsize != wsize) {
1560 DEBUG(
"size mismatch. read:%zd, write:%zd", rsize, wsize);
1579static off_t
sendfile_(qhttpclient_t *client,
int fd, off_t nbytes) {
1583 unsigned char buf[MAX_ATOMIC_DATA_SIZE];
1586 while (total < nbytes) {
1588 if (nbytes - total <=
sizeof(buf))
1589 chunksize = nbytes - total;
1591 chunksize =
sizeof(buf);
1594 ssize_t rsize =
qio_read(fd, buf, chunksize, -1);
1595 DEBUG(
"FILE read: %zd", rsize);
1600 ssize_t wsize =
write_(client, buf, rsize);
1605 if (rsize != wsize) {
1606 DEBUG(
"size mismatch. read:%zd, write:%zd", rsize, wsize);
1628 if (client->socket < 0)
1631#ifdef ENABLE_OPENSSL
1633 if (client->ssl != NULL) {
1634 struct SslConn *ssl = client->ssl;
1636 if (ssl->ssl != NULL) {
1637 SSL_shutdown(ssl->ssl);
1642 if (ssl->ctx != NULL) {
1643 SSL_CTX_free(ssl->ctx);
1650 if (client->ssl == NULL && MAX_SHUTDOWN_WAIT >= 0
1651 && shutdown(client->socket, SHUT_WR) == 0) {
1653 while (
qio_read(client->socket, buf,
sizeof(buf), MAX_SHUTDOWN_WAIT) > 0);
1657 close(client->socket);
1658 client->socket = -1;
1659 client->connclose =
false;
1677static void _free(qhttpclient_t *client) {
1678 if (client->socket >= 0) {
1679 client->close(client);
1682 if (client->ssl != NULL)
1684 if (client->hostname != NULL)
1685 free(client->hostname);
1686 if (client->useragent != NULL)
1687 free(client->useragent);
1692#ifndef _DOXYGEN_SKIP
1693static bool _set_socket_option(
int socket) {
1697 if (SET_TCP_LINGER_TIMEOUT > 0) {
1700 li.l_linger = SET_TCP_LINGER_TIMEOUT;
1701 if (setsockopt(socket, SOL_SOCKET, SO_LINGER, &li,
1702 sizeof(
struct linger)) < 0) {
1708 if (SET_TCP_NODELAY > 0) {
1709 int so_tcpnodelay = 1;
1710 if (setsockopt(socket, IPPROTO_TCP, TCP_NODELAY, &so_tcpnodelay,
1711 sizeof(so_tcpnodelay)) < 0) {
1719static bool _parse_uri(
const char *uri,
bool *protocol,
char *hostname,
1720 size_t namesize,
int *port) {
1722 if (!strncasecmp(uri,
"http://", CONST_STRLEN(
"http://"))) {
1725 }
else if (!strncasecmp(uri,
"https://", CONST_STRLEN(
"https://"))) {
1732 char *t1 = strstr(uri,
"://");
1734 char *t2 = strstr(t1,
"/");
1736 t2 = (
char *) uri + strlen(uri);
1738 if (t2 - t1 + 1 > namesize)
1740 qstrncpy(hostname, namesize, t1, t2 - t1);
1742 t1 = strstr(hostname,
":");
1745 *port = atoi(t1 + 1);
qgrow_t * qgrow(int options)
Initialize grow.
static void settimeout(qhttpclient_t *client, int timeoutms)
qhttpclient->settimeout(): Sets connection wait timeout.
static bool sendrequest(qhttpclient_t *client, const char *method, const char *uri, qlisttbl_t *reqheaders)
qhttpclient->sendrequest(): Sends an HTTP request to the remote host.
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(): Send a custom request method to the remote host and read the response.
static off_t recvfile(qhttpclient_t *client, int fd, off_t nbytes)
qhttpclient->recvfile(): Reads data from an HTTP/HTTPS stream and saves it to a file descriptor.
static bool setssl(qhttpclient_t *client)
qhttpclient->setssl(): Enable HTTPS for the connection.
static ssize_t read_(qhttpclient_t *client, void *buf, size_t nbytes)
qhttpclient->read(): Reads data from an HTTP/HTTPS stream.
static bool _close(qhttpclient_t *client)
qhttpclient->close(): Closes the connection.
static ssize_t write_(qhttpclient_t *client, const void *buf, size_t nbytes)
qhttpclient->write(): Writes data to an HTTP/HTTPS stream.
static void _free(qhttpclient_t *client)
qhttpclient->free(): Free object.
static void setkeepalive(qhttpclient_t *client, bool keepalive)
qhttpclient->setkeepalive(): Sets KEEP-ALIVE feature on/off.
static int readresponse(qhttpclient_t *client, qlisttbl_t *resheaders, off_t *contentlength)
qhttpclient->readresponse(): Reads HTTP response header from the remote host.
qhttpclient_t * qhttpclient(const char *destname, int port)
Initialize and create a new HTTP client.
static bool open_(qhttpclient_t *client)
qhttpclient->open(): Opens a connection to the remote host.
static bool head(qhttpclient_t *client, const char *uri, int *rescode, qlisttbl_t *reqheaders, qlisttbl_t *resheaders)
qhttpclient->head(): Sends a HEAD request.
static off_t sendfile_(qhttpclient_t *client, int fd, off_t nbytes)
qhttpclient->sendfile(): Sends file data to an HTTP/HTTPS stream.
static ssize_t gets_(qhttpclient_t *client, char *buf, size_t bufsize)
qhttpclient->gets(): Reads a text line from an HTTP/HTTPS stream.
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.
static bool get(qhttpclient_t *client, const char *uri, int fd, off_t *savesize, int *rescode, qlisttbl_t *reqheaders, qlisttbl_t *resheaders, bool(*callback)(void *userdata, off_t recvbytes), void *userdata)
qhttpclient->get(): Downloads a file from the remote host using GET method.
static void setuseragent(qhttpclient_t *client, const char *useragent)
qhttpclient->setuseragent(): Sets user-agent string.
ssize_t qio_write(int fd, const void *buf, size_t nbytes, int timeoutms)
Write to a file descriptor.
int qio_wait_readable(int fd, int timeoutms)
Wait until a file descriptor becomes readable.
ssize_t qio_read(int fd, void *buf, size_t nbytes, int timeoutms)
Read from a file descriptor.
ssize_t qio_gets(int fd, char *buf, size_t bufsize, int timeoutms)
Read a line from a file descriptor into the buffer until a terminating newline or EOF.
int qio_wait_writable(int fd, int timeoutms)
Wait until a file descriptor is ready for writing.
qlisttbl_t * qlisttbl(int options)
Create a new Q_LIST linked-list container.
static bool write_(qlog_t *log, const char *str)
qlog->write(): Log messages
bool qsocket_get_addr(struct sockaddr_in *addr, const char *hostname, int port)
Convert a hostname to a sockaddr_in structure.
char * qstrcpy(char *dst, size_t size, const char *src)
Copy src string to dst.
char * qstrncpy(char *dst, size_t size, const char *src, size_t nbytes)
Copy src string to dst no more than n bytes.
char * qstrtrim(char *str)
Remove whitespace, including CR and LF, from both ends of a string.