toaruos/apps/http-get.c
2018-04-25 14:03:29 +09:00

233 lines
4.3 KiB
C

/* vim: ts=4 sw=4 noexpandtab
*
* Small HTTP fetch tool. Not meant to be complete, this is
* written for demo purposes only.
*/
#include <stdio.h>
#include <unistd.h>
#include "../lib/list.c"
#include "../lib/hashmap.c"
#define SIZE 512
struct uri {
char domain[SIZE];
char path[SIZE];
};
void parse_url(char * d, struct uri * r) {
if (strstr(d, "http://") == d) {
d += strlen("http://");
char * s = strstr(d, "/");
if (!s) {
strcpy(r->domain, d);
strcpy(r->path, "");
} else {
*s = 0;
s++;
strcpy(r->domain, d);
strcpy(r->path, s);
}
} else {
fprintf(stderr, "sorry, can't parse %s\n", d);
exit(1);
}
}
static char read_buf[1024];
static size_t available = 0;
static size_t offset = 0;
static size_t read_from = 0;
static char * read_line(FILE * f, char * out, ssize_t len) {
while (len > 0) {
if (available == 0) {
if (offset == 1024) {
offset = 0;
}
size_t r = read(fileno(f), &read_buf[offset], 1024 - offset);
read_from = offset;
available = r;
offset += available;
}
#if 0
fprintf(stderr, "Available: %d\n", available);
fprintf(stderr, "Remaining length: %d\n", len);
fprintf(stderr, "Read from: %d\n", read_from);
fprintf(stderr, "Offset: %d\n", offset);
#endif
if (available == 0) {
return out;
}
while (read_from < offset && len > 0) {
*out = read_buf[read_from];
len--;
read_from++;
available--;
if (*out == '\n') {
return out;
}
out++;
}
}
return out;
}
static size_t read_bytes(FILE * f, char * out, ssize_t len) {
size_t r_out = 0;
while (len > 0) {
if (available == 0) {
if (offset == 1024) {
offset = 0;
}
size_t r = read(fileno(f), &read_buf[offset], 1024 - offset);
read_from = offset;
available = r;
offset += available;
}
if (available == 0) {
return r_out;
}
while (read_from < offset && len > 0) {
*out = read_buf[read_from];
len--;
read_from++;
available--;
out++;
r_out += 1;
}
}
return r_out;
}
void read_http_line(char * buf, FILE * f) {
memset(buf, 0x00, 256);
read_line(f, buf, 255);
char * _r = strchr(buf, '\r');
if (_r) {
*_r = '\0';
}
if (!_r) {
_r = strchr(buf, '\n'); /* that's not right, but, whatever */
if (_r) {
*_r = '\0';
}
}
}
void bad_response(void) {
fprintf(stderr, "Bad response.\n");
exit(1);
}
int main(int argc, char * argv[]) {
if (argc < 2) {
fprintf(stderr, "%s: expected an argument\n", argv[0]);
return 1;
}
struct uri uri;
parse_url(argv[1], &uri);
char file[100];
sprintf(file, "/dev/net/%s", uri.domain);
FILE * f = fopen(file,"r+");
if (!f) {
fprintf(stderr, "%s: connection to %s failed\n", argv[0], uri.domain);
return 1;
}
fprintf(f,
"GET /%s HTTP/1.0\r\n"
"User-Agent: curl/7.35.0\r\n"
"Host: %s\r\n"
"Accept: */*\r\n"
"\r\n", uri.path, uri.domain);
hashmap_t * headers = hashmap_create(10);
/* Parse response */
{
char buf[256];
read_http_line(buf, f);
fprintf(stderr, "[%s]\n", buf);
char * elements[3];
elements[0] = buf;
elements[1] = strchr(elements[0], ' ');
if (!elements[1]) bad_response();
*elements[1] = '\0';
elements[1]++;
elements[2] = strchr(elements[1], ' ');
if (!elements[2]) bad_response();
*elements[2] = '\0';
elements[2]++;
if (strcmp(elements[1], "200")) {
fprintf(stderr, "Bad response code: %s\n", elements[1]);
return 1;
}
}
/* Parse headers */
while (1) {
char buf[256];
read_http_line(buf, f);
if (!*buf) {
fprintf(stderr, "(done with headers)\n");
break;
}
/* Split */
char * name = buf;
char * value = strstr(buf, ": ");
if (!value) bad_response();
*value = '\0';
value += 2;
hashmap_set(headers, name, strdup(value));
}
fprintf(stderr, "Dumping headers.\n");
list_t * hash_keys = hashmap_keys(headers);
foreach(_key, hash_keys) {
char * key = (char *)_key->value;
fprintf(stderr, "[%s] = %s\n", key, (char*)hashmap_get(headers, key));
}
list_free(hash_keys);
free(hash_keys);
/* determine how many bytes we should read now */
if (!hashmap_has(headers, "Content-Length")) {
fprintf(stderr, "Don't know how much to read.\n");
return 1;
}
int bytes_to_read = atoi(hashmap_get(headers, "Content-Length"));
while (bytes_to_read > 0) {
char buf[1024];
size_t r = read_bytes(f, buf, bytes_to_read < 1024 ? bytes_to_read : 1024);
fwrite(buf, 1, r, stdout);
bytes_to_read -= r;
}
return 0;
}