Add curl example (#42)

make -j8 o//examples/curl.com
    o//examples/curl.com http://justine.lol/ape.html
This commit is contained in:
Justine Tunney 2021-02-18 19:20:41 -08:00
parent 667ab245fe
commit 2c15efc249
29 changed files with 208 additions and 153 deletions

107
examples/curl.c Normal file
View file

@ -0,0 +1,107 @@
#if 0
/*─────────────────────────────────────────────────────────────────╗
To the extent possible under law, Justine Tunney has waived
all copyright and related or neighboring rights to this file,
as it is written in the following disclaimers:
http://unlicense.org/ │
http://creativecommons.org/publicdomain/zero/1.0/ │
*/
#endif
#include "libc/bits/safemacros.h"
#include "libc/dns/dns.h"
#include "libc/fmt/conv.h"
#include "libc/fmt/fmt.h"
#include "libc/log/check.h"
#include "libc/log/log.h"
#include "libc/macros.h"
#include "libc/runtime/gc.h"
#include "libc/runtime/runtime.h"
#include "libc/sock/sock.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/af.h"
#include "libc/sysv/consts/ai.h"
#include "libc/sysv/consts/ipproto.h"
#include "libc/sysv/consts/shut.h"
#include "libc/sysv/consts/sock.h"
#include "libc/x/x.h"
#include "net/http/uri.h"
/**
* @fileoverview Downloads HTTP URL to stdout.
*
* make -j8 o//examples/curl.com
* o//examples/curl.com http://justine.lol/ape.html
*/
int main(int argc, char *argv[]) {
int sock;
ssize_t rc;
unsigned long need;
struct UriSlice path;
size_t i, got, toto, msglen;
char buf[1500], host[256], port[7];
const char *url, *msg, *pathstr, *crlfcrlf, *contentlength;
struct UriSlice us[16];
struct Uri u = {.segs.p = us, .segs.n = ARRAYLEN(us)};
struct addrinfo *addr, *addrs;
struct addrinfo hints = {.ai_family = AF_INET,
.ai_socktype = SOCK_STREAM,
.ai_protocol = IPPROTO_TCP,
.ai_flags = AI_NUMERICSERV};
if (argc != 2) {
fprintf(stderr, "USAGE: %s URL\n", argv[0]);
exit(1);
}
url = argv[1];
CHECK_NE(-1, uriparse(&u, url, strlen(url)), "BAD URL: %`'s", url);
CHECK_EQ(kUriSchemeHttp, urischeme(u.scheme, url));
urislice2cstr(host, sizeof(host), u.host, url, "127.0.0.1");
urislice2cstr(port, sizeof(port), u.port, url, "80");
path = uripath(&u);
pathstr = path.n ? url + path.i : "/";
msg = gc(xstrcat("GET ", pathstr,
" HTTP/1.1\r\n"
"Host: ",
host,
"\r\n"
"Connection: close\r\n"
"Content-Length: 0\r\n"
"Accept: text/plain; */*\r\n"
"Accept-Encoding: identity\r\n"
"User-Agent: github.com/jart/cosmopolitan\r\n"
"\r\n"));
msglen = strlen(msg);
CHECK_EQ(EAI_SUCCESS, getaddrinfo(host, port, &hints, &addrs));
for (addr = addrs; addr; addr = addr->ai_next) {
CHECK_NE(-1, (sock = socket(addr->ai_family, addr->ai_socktype,
addr->ai_protocol)));
CHECK_NE(-1, connect(sock, addr->ai_addr, addr->ai_addrlen));
CHECK_EQ(msglen, write(sock, msg, msglen));
shutdown(sock, SHUT_WR);
buf[0] = '\0';
CHECK_NE(-1, (rc = read(sock, buf, sizeof(buf))));
got = rc;
CHECK(startswith(buf, "HTTP/1.1 200"), "%`'.*s", got, buf);
CHECK_NOTNULL((crlfcrlf = memmem(buf, got, "\r\n\r\n", 4)));
need =
strtol((char *)firstnonnull(
firstnonnull(
memmem(buf, crlfcrlf - buf, "\r\nContent-Length: ", 18),
memmem(buf, crlfcrlf - buf, "\r\ncontent-length: ", 18)),
"\r\nContent-Length: -1") +
18,
NULL, 10);
got = MIN(got - (crlfcrlf + 4 - buf), need);
CHECK_EQ(got, write(1, crlfcrlf + 4, got));
for (toto = got; toto < need; toto += got) {
CHECK_NE(-1, (rc = read(sock, buf, sizeof(buf))));
if (!(got = rc)) exit(18);
got = MIN(got, need - toto);
CHECK_EQ(got, write(1, buf, got));
}
LOGIFNEG1(close(sock));
break;
}
return 0;
}

View file

@ -40,6 +40,7 @@ EXAMPLES_DIRECTDEPS = \
LIBC_ALG \ LIBC_ALG \
LIBC_BITS \ LIBC_BITS \
LIBC_CALLS \ LIBC_CALLS \
LIBC_DNS \
LIBC_FMT \ LIBC_FMT \
LIBC_INTRIN \ LIBC_INTRIN \
LIBC_LOG \ LIBC_LOG \
@ -64,6 +65,7 @@ EXAMPLES_DIRECTDEPS = \
LIBC_UNICODE \ LIBC_UNICODE \
LIBC_X \ LIBC_X \
LIBC_ZIPOS \ LIBC_ZIPOS \
NET_HTTP \
THIRD_PARTY_COMPILER_RT \ THIRD_PARTY_COMPILER_RT \
THIRD_PARTY_DLMALLOC \ THIRD_PARTY_DLMALLOC \
THIRD_PARTY_GDTOA \ THIRD_PARTY_GDTOA \

View file

@ -1,15 +1,34 @@
#ifndef COSMOPOLITAN_LIBC_DNS_DNS_H_ #ifndef COSMOPOLITAN_LIBC_DNS_DNS_H_
#define COSMOPOLITAN_LIBC_DNS_DNS_H_ #define COSMOPOLITAN_LIBC_DNS_DNS_H_
#if !(__ASSEMBLER__ + __LINKER__ + 0) #include "libc/dns/resolvconf.h"
COSMOPOLITAN_C_START_ #include "libc/sock/sock.h"
#define DNS_PORT 53 #define DNS_PORT 53
#define DNS_NAME_MAX 253 #define DNS_NAME_MAX 253
#define DNS_LABEL_MAX 63 #define DNS_LABEL_MAX 63
struct sockaddr; #define EAI_SUCCESS 0
struct sockaddr_in; #define EAI_BADFLAGS -1
struct ResolvConf; #define EAI_NONAME -2
#define EAI_AGAIN -3
#define EAI_FAIL -4
#define EAI_NODATA -5
#define EAI_FAMILY -6
#define EAI_SOCKTYPE -7
#define EAI_SERVICE -8
#define EAI_ADDRFAMILY -9
#define EAI_MEMORY -10
#define EAI_OVERFLOW -12
#define EAI_SYSTEM -11
#define EAI_ALLDONE -103
#define EAI_CANCELED -101
#define EAI_IDN_ENCODE -105
#define EAI_INPROGRESS -100
#define EAI_INTR -104
#define EAI_NOTCANCELED -102
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
struct addrinfo { struct addrinfo {
int32_t ai_flags; /* AI_XXX */ int32_t ai_flags; /* AI_XXX */

View file

@ -17,30 +17,51 @@
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/dns/dns.h" #include "libc/dns/dns.h"
#include "libc/sysv/consts/eai.h"
/** /**
* Turns getaddrinfo() return code into string. * Turns getaddrinfo() return code into string.
*/ */
const char *eai2str(int code) { const char *eai2str(int code) {
if (code == EAI_ADDRFAMILY) return "ADDRFAMILY"; switch (code) {
if (code == EAI_AGAIN) return "AGAIN"; case EAI_ADDRFAMILY:
if (code == EAI_ALLDONE) return "ALLDONE"; return "ADDRFAMILY";
if (code == EAI_BADFLAGS) return "BADFLAGS"; case EAI_AGAIN:
if (code == EAI_CANCELED) return "CANCELED"; return "AGAIN";
if (code == EAI_FAIL) return "FAIL"; case EAI_ALLDONE:
if (code == EAI_FAMILY) return "FAMILY"; return "ALLDONE";
if (code == EAI_IDN_ENCODE) return "ENCODE"; case EAI_BADFLAGS:
if (code == EAI_INPROGRESS) return "INPROGRESS"; return "BADFLAGS";
if (code == EAI_INTR) return "INTR"; case EAI_CANCELED:
if (code == EAI_MEMORY) return "MEMORY"; return "CANCELED";
if (code == EAI_NODATA) return "NODATA"; case EAI_FAIL:
if (code == EAI_NONAME) return "NONAME"; return "FAIL";
if (code == EAI_NOTCANCELED) return "NOTCANCELED"; case EAI_FAMILY:
if (code == EAI_OVERFLOW) return "OVERFLOW"; return "FAMILY";
if (code == EAI_SERVICE) return "SERVICE"; case EAI_IDN_ENCODE:
if (code == EAI_SOCKTYPE) return "SOCKTYPE"; return "ENCODE";
if (code == EAI_SUCCESS) return "SUCCESS"; case EAI_INPROGRESS:
if (code == EAI_SYSTEM) return "SYSTEM"; return "INPROGRESS";
case EAI_INTR:
return "INTR";
case EAI_MEMORY:
return "MEMORY";
case EAI_NODATA:
return "NODATA";
case EAI_NONAME:
return "NONAME";
case EAI_NOTCANCELED:
return "NOTCANCELED";
case EAI_OVERFLOW:
return "OVERFLOW";
case EAI_SERVICE:
return "SERVICE";
case EAI_SOCKTYPE:
return "SOCKTYPE";
case EAI_SUCCESS:
return "SUCCESS";
case EAI_SYSTEM:
return "SYSTEM";
default:
return "???"; return "???";
} }
}

View file

@ -27,7 +27,6 @@
#include "libc/str/str.h" #include "libc/str/str.h"
#include "libc/sysv/consts/af.h" #include "libc/sysv/consts/af.h"
#include "libc/sysv/consts/ai.h" #include "libc/sysv/consts/ai.h"
#include "libc/sysv/consts/eai.h"
#include "libc/sysv/consts/inaddr.h" #include "libc/sysv/consts/inaddr.h"
#include "libc/sysv/errfuns.h" #include "libc/sysv/errfuns.h"

View file

@ -1,11 +1,10 @@
#ifndef COSMOPOLITAN_LIBC_DNS_RESOLVCONF_H_ #ifndef COSMOPOLITAN_LIBC_DNS_RESOLVCONF_H_
#define COSMOPOLITAN_LIBC_DNS_RESOLVCONF_H_ #define COSMOPOLITAN_LIBC_DNS_RESOLVCONF_H_
#include "libc/sock/sock.h"
#include "libc/stdio/stdio.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0) #if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_ COSMOPOLITAN_C_START_
struct FILE;
struct sockaddr_in;
struct Nameservers { struct Nameservers {
size_t i, n; size_t i, n;
struct sockaddr_in *p; struct sockaddr_in *p;

View file

@ -1833,29 +1833,6 @@ syscon gai AI_NUMERICSERV 0x0400 0x1000 8 0x10 0x10 8
syscon gai AI_ALL 0x10 0x0100 0x0100 0 0 0x0100 syscon gai AI_ALL 0x10 0x0100 0x0100 0 0 0x0100
syscon gai AI_V4MAPPED 8 0x0800 0x0800 0 0 0x0800 syscon gai AI_V4MAPPED 8 0x0800 0x0800 0 0 0x0800
# getaddrinfo() return codes
#
# group name GNU/Systemd XNU's Not UNIX FreeBSD OpenBSD NetBSD XENIX Commentary
syscon eai EAI_SUCCESS 0 0 0 0 0 0
syscon eai EAI_BADFLAGS -1 3 3 -1 -1 0x2726
syscon eai EAI_NONAME -2 8 8 -2 -2 0x2af9
syscon eai EAI_AGAIN -3 2 2 -3 -3 0x2afa
syscon eai EAI_FAIL -4 4 4 -4 -4 0x2afb
syscon eai EAI_FAMILY -6 5 5 -6 -6 0x273f
syscon eai EAI_MEMORY -10 6 6 -10 -10 0x2747
syscon eai EAI_SERVICE -8 9 9 -8 -8 0x277d
syscon eai EAI_SOCKTYPE -7 10 10 -7 -7 0x273c
syscon eai EAI_NODATA -5 7 0 -5 -5 0x2af9
syscon eai EAI_OVERFLOW -12 14 14 -14 -14 -12
syscon eai EAI_SYSTEM -11 11 11 -11 -11 -11
syscon eai EAI_ADDRFAMILY -9 1 0 -9 -9 -9
syscon eai EAI_ALLDONE -103 -103 -103 -103 -103 -103 # copying from linux
syscon eai EAI_CANCELED -101 -101 -101 -101 -101 -101 # copying from linux
syscon eai EAI_IDN_ENCODE -105 -105 -105 -105 -105 -105 # copying from linux
syscon eai EAI_INPROGRESS -100 -100 -100 -100 -100 -100 # copying from linux
syscon eai EAI_INTR -104 -104 -104 -104 -104 -104 # copying from linux
syscon eai EAI_NOTCANCELED -102 -102 -102 -102 -102 -102 # copying from linux
syscon misc BLK_BYTECOUNT 2 2 2 2 2 0 # unix consensus syscon misc BLK_BYTECOUNT 2 2 2 2 2 0 # unix consensus
syscon misc BLK_EOF 0x40 0x40 0x40 0x40 0x40 0 # unix consensus syscon misc BLK_EOF 0x40 0x40 0x40 0x40 0x40 0 # unix consensus
syscon misc BLK_EOR 0x80 0x80 0x80 0x80 0x80 0 # unix consensus syscon misc BLK_EOR 0x80 0x80 0x80 0x80 0x80 0 # unix consensus

View file

@ -1,2 +0,0 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon eai,EAI_ADDRFAMILY,-9,1,0,-9,-9,-9

View file

@ -1,2 +0,0 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon eai,EAI_AGAIN,-3,2,2,-3,-3,0x2afa

View file

@ -1,2 +0,0 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon eai,EAI_ALLDONE,-103,-103,-103,-103,-103,-103

View file

@ -1,2 +0,0 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon eai,EAI_BADFLAGS,-1,3,3,-1,-1,0x2726

View file

@ -1,2 +0,0 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon eai,EAI_CANCELED,-101,-101,-101,-101,-101,-101

View file

@ -1,2 +0,0 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon eai,EAI_FAIL,-4,4,4,-4,-4,0x2afb

View file

@ -1,2 +0,0 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon eai,EAI_FAMILY,-6,5,5,-6,-6,0x273f

View file

@ -1,2 +0,0 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon eai,EAI_IDN_ENCODE,-105,-105,-105,-105,-105,-105

View file

@ -1,2 +0,0 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon eai,EAI_INPROGRESS,-100,-100,-100,-100,-100,-100

View file

@ -1,2 +0,0 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon eai,EAI_INTR,-104,-104,-104,-104,-104,-104

View file

@ -1,2 +0,0 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon eai,EAI_MEMORY,-10,6,6,-10,-10,0x2747

View file

@ -1,2 +0,0 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon eai,EAI_NODATA,-5,7,0,-5,-5,0x2af9

View file

@ -1,2 +0,0 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon eai,EAI_NONAME,-2,8,8,-2,-2,0x2af9

View file

@ -1,2 +0,0 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon eai,EAI_NOTCANCELED,-102,-102,-102,-102,-102,-102

View file

@ -1,2 +0,0 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon eai,EAI_OVERFLOW,-12,14,14,-14,-14,-12

View file

@ -1,2 +0,0 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon eai,EAI_SERVICE,-8,9,9,-8,-8,0x277d

View file

@ -1,2 +0,0 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon eai,EAI_SOCKTYPE,-7,10,10,-7,-7,0x273c

View file

@ -1,2 +0,0 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon eai,EAI_SUCCESS,0,0,0,0,0,0

View file

@ -1,2 +0,0 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon eai,EAI_SYSTEM,-11,11,11,-11,-11,-11

View file

@ -1,49 +0,0 @@
#ifndef COSMOPOLITAN_LIBC_SYSV_CONSTS_EAI_H_
#define COSMOPOLITAN_LIBC_SYSV_CONSTS_EAI_H_
#include "libc/runtime/symbolic.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
extern const long EAI_ADDRFAMILY;
extern const long EAI_AGAIN;
extern const long EAI_ALLDONE;
extern const long EAI_BADFLAGS;
extern const long EAI_CANCELED;
extern const long EAI_FAIL;
extern const long EAI_FAMILY;
extern const long EAI_IDN_ENCODE;
extern const long EAI_INPROGRESS;
extern const long EAI_INTR;
extern const long EAI_MEMORY;
extern const long EAI_NODATA;
extern const long EAI_NONAME;
extern const long EAI_NOTCANCELED;
extern const long EAI_OVERFLOW;
extern const long EAI_SERVICE;
extern const long EAI_SOCKTYPE;
extern const long EAI_SYSTEM;
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#define EAI_ADDRFAMILY SYMBOLIC(EAI_ADDRFAMILY)
#define EAI_AGAIN SYMBOLIC(EAI_AGAIN)
#define EAI_ALLDONE SYMBOLIC(EAI_ALLDONE)
#define EAI_BADFLAGS SYMBOLIC(EAI_BADFLAGS)
#define EAI_CANCELED SYMBOLIC(EAI_CANCELED)
#define EAI_FAIL SYMBOLIC(EAI_FAIL)
#define EAI_FAMILY SYMBOLIC(EAI_FAMILY)
#define EAI_IDN_ENCODE SYMBOLIC(EAI_IDN_ENCODE)
#define EAI_INPROGRESS SYMBOLIC(EAI_INPROGRESS)
#define EAI_INTR SYMBOLIC(EAI_INTR)
#define EAI_MEMORY SYMBOLIC(EAI_MEMORY)
#define EAI_NODATA SYMBOLIC(EAI_NODATA)
#define EAI_NONAME SYMBOLIC(EAI_NONAME)
#define EAI_NOTCANCELED SYMBOLIC(EAI_NOTCANCELED)
#define EAI_OVERFLOW SYMBOLIC(EAI_OVERFLOW)
#define EAI_SERVICE SYMBOLIC(EAI_SERVICE)
#define EAI_SOCKTYPE SYMBOLIC(EAI_SOCKTYPE)
#define EAI_SUCCESS LITERALLY(0)
#define EAI_SYSTEM SYMBOLIC(EAI_SYSTEM)
#endif /* COSMOPOLITAN_LIBC_SYSV_CONSTS_EAI_H_ */

View file

@ -16,19 +16,30 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/macros.h"
#include "libc/str/str.h" #include "libc/str/str.h"
#include "net/http/uri.h" #include "net/http/uri.h"
/* TODO(jart): Unescape */
char *urislice2cstr(char *buf, size_t size, struct UriSlice slice, char *urislice2cstr(char *buf, size_t size, struct UriSlice slice,
const char *uristr, const char *defaultval) { const char *uristr, const char *defaultval) {
/* TODO(jart): Unescape */ size_t n;
if (slice.n && slice.n + 1 < size) { const char *p;
memcpy(buf, uristr + slice.i, slice.n); if (size) {
if (slice.n) {
p = uristr + slice.i;
n = slice.n;
} else if (defaultval) { } else if (defaultval) {
memcpy(buf, defaultval, (slice.n = strlen(defaultval))); p = defaultval;
n = strlen(defaultval);
} else { } else {
slice.n = 0; p = NULL;
n = 0;
}
n = MIN(n, size - 1);
memcpy(buf, p, n);
buf[n] = '\0';
} }
buf[slice.n] = '\0';
return buf; return buf;
} }

View file

@ -31,14 +31,21 @@
#include "tool/decode/lib/socknames.h" #include "tool/decode/lib/socknames.h"
void lookup(const char *name) { void lookup(const char *name) {
int rc;
struct addrinfo hints = (struct addrinfo){.ai_family = AF_INET, struct addrinfo hints = (struct addrinfo){.ai_family = AF_INET,
.ai_socktype = SOCK_STREAM, .ai_socktype = SOCK_STREAM,
.ai_protocol = IPPROTO_TCP, .ai_protocol = IPPROTO_TCP,
.ai_flags = AI_NUMERICSERV}; .ai_flags = AI_NUMERICSERV};
struct addrinfo *addrs = NULL; struct addrinfo *addrs = NULL;
if (getaddrinfo(name, "80", &hints, &addrs) == -1) { switch ((rc = getaddrinfo(name, "80", &hints, &addrs))) {
case EAI_SUCCESS:
break;
case EAI_SYSTEM:
perror("getaddrinfo"); perror("getaddrinfo");
exit(1); exit(1);
default:
fprintf(stderr, "getaddrinfo failed: %d (EAI_%s)\n", rc, eai2str(rc));
exit(1);
} }
if (addrs) { if (addrs) {
for (struct addrinfo *addr = addrs; addr; addr = addr->ai_next) { for (struct addrinfo *addr = addrs; addr; addr = addr->ai_next) {