libfetch: support TCP_CORK

Unfortunately libfetch operates on raw sockets and is sending
each HTTP request line using separate syscall which causes the
HTTP request to be sent as multiple packets over the wire in most
configurations. This is not good for performance, but can also
cause subtle breakage if there's DPI firewall that does not get
the Host header.

Incidentally, it seems that on BSDs libfetch already sets
TCP_NOPUSH optimize the packetization. This commit adds same
logic for using TCP_CORK if available. When using TCP_CORK
there is no requirement to set TCP_NODELAY as uncorking will
also cause immediate send. Keep TCP_NODELAY in the fallback
codepaths.

Long term, it might make sense to replace or rewrite libfetch
to use application level buffering.
cute-signatures
Timo Teräs 2020-02-18 15:21:35 +02:00
parent 3694dc5fa2
commit 271047cc93
1 changed files with 22 additions and 23 deletions

View File

@ -107,6 +107,8 @@
#define HTTP_ERROR(xyz) ((xyz) > 400 && (xyz) < 599) #define HTTP_ERROR(xyz) ((xyz) > 400 && (xyz) < 599)
static int val_yes = 1, val_no = 0;
static int http_cmd(conn_t *, const char *, ...) LIBFETCH_PRINTFLIKE(2, 3); static int http_cmd(conn_t *, const char *, ...) LIBFETCH_PRINTFLIKE(2, 3);
/***************************************************************************** /*****************************************************************************
@ -294,21 +296,20 @@ static void
http_closefn(void *v) http_closefn(void *v)
{ {
struct httpio *io = (struct httpio *)v; struct httpio *io = (struct httpio *)v;
conn_t *conn = io->conn;
if (io->keep_alive) { if (io->keep_alive) {
int val; #if defined(TCP_CORK)
setsockopt(conn->sd, IPPROTO_TCP, TCP_CORK, &val_yes, sizeof val_yes);
val = 0; #else
setsockopt(io->conn->sd, IPPROTO_TCP, TCP_NODELAY, &val, setsockopt(conn->sd, IPPROTO_TCP, TCP_NODELAY, &val_no, sizeof val_no);
sizeof(val));
fetch_cache_put(io->conn, fetch_close);
#if defined(TCP_NOPUSH) && !defined(__APPLE__) #if defined(TCP_NOPUSH) && !defined(__APPLE__)
val = 1; setsockopt(conn->sd, IPPROTO_TCP, TCP_NOPUSH, &val_yes, sizeof val_yes);
setsockopt(io->conn->sd, IPPROTO_TCP, TCP_NOPUSH, &val,
sizeof(val));
#endif #endif
#endif
fetch_cache_put(conn, fetch_close);
} else { } else {
fetch_close(io->conn); fetch_close(conn);
} }
free(io->buf); free(io->buf);
@ -688,9 +689,6 @@ http_connect(struct url *URL, struct url *purl, const char *flags, int *cached)
hdr_t h; hdr_t h;
const char *p; const char *p;
int af, verbose; int af, verbose;
#if defined(TCP_NOPUSH) && !defined(__APPLE__)
int val;
#endif
*cached = 0; *cached = 0;
@ -752,9 +750,10 @@ http_connect(struct url *URL, struct url *purl, const char *flags, int *cached)
goto ouch; goto ouch;
} }
#if defined(TCP_NOPUSH) && !defined(__APPLE__) #if defined(TCP_CORK)
val = 1; setsockopt(conn->sd, IPPROTO_TCP, TCP_CORK, &val_yes, sizeof val_yes);
setsockopt(conn->sd, IPPROTO_TCP, TCP_NOPUSH, &val, sizeof(val)); #elif defined(TCP_NOPUSH) && !defined(__APPLE__)
setsockopt(conn->sd, IPPROTO_TCP, TCP_NOPUSH, &val_yes, sizeof val_yes);
#endif #endif
return (conn); return (conn);
@ -838,7 +837,7 @@ http_request(struct url *URL, const char *op, struct url_stat *us,
struct url *url, *new; struct url *url, *new;
int chunked, direct, if_modified_since, need_auth, noredirect, nocache; int chunked, direct, if_modified_since, need_auth, noredirect, nocache;
int keep_alive, verbose, cached; int keep_alive, verbose, cached;
int e, i, n, val; int e, i, n;
off_t offset, clength, length, size; off_t offset, clength, length, size;
time_t mtime; time_t mtime;
const char *p; const char *p;
@ -972,14 +971,14 @@ http_request(struct url *URL, const char *op, struct url_stat *us,
* be compatible with such configurations, fiddle with socket * be compatible with such configurations, fiddle with socket
* options to force the pending data to be written. * options to force the pending data to be written.
*/ */
#if defined(TCP_CORK)
setsockopt(conn->sd, IPPROTO_TCP, TCP_CORK, &val_no, sizeof val_no);
#else
#if defined(TCP_NOPUSH) && !defined(__APPLE__) #if defined(TCP_NOPUSH) && !defined(__APPLE__)
val = 0; setsockopt(conn->sd, IPPROTO_TCP, TCP_NOPUSH, &val_no, sizeof val_no);
setsockopt(conn->sd, IPPROTO_TCP, TCP_NOPUSH, &val, #endif
sizeof(val)); setsockopt(conn->sd, IPPROTO_TCP, TCP_NODELAY, &val_yes, sizeof val_yes);
#endif #endif
val = 1;
setsockopt(conn->sd, IPPROTO_TCP, TCP_NODELAY, &val,
sizeof(val));
/* get reply */ /* get reply */
switch (http_get_reply(conn)) { switch (http_get_reply(conn)) {