Skip to content
This repository was archived by the owner on Nov 6, 2022. It is now read-only.

Enhance parsertrace to use an URL as input #162

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
203 changes: 152 additions & 51 deletions contrib/parsertrace.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,78 +22,119 @@
* IN THE SOFTWARE.
*/

/* Dump what the parser finds to stdout as it happen */
/* Dump what the parser finds to stderr as it happen, body go to stdout */

#define _GNU_SOURCE 1
#include "http_parser.h"
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <unistd.h>
#include <string.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <netdb.h>
#include <fcntl.h>

struct timeval start;
unsigned long long timestamp() {
struct timeval stop;
gettimeofday(&stop, NULL);
return (stop.tv_sec - start.tv_sec)*1000 + (stop.tv_usec - start.tv_usec)/1000;
}

int log_event(const char *fmt, ...) {
va_list ap;
char *nfmt;
va_start(ap, fmt);
if (asprintf(&nfmt, "%s %6llu ms: %s %s\n",
isatty(STDERR_FILENO)?"\033[1;33m**":"\n**", timestamp(),
fmt, isatty(STDERR_FILENO)?"\033[0m":"**\n") == -1) {
vfprintf(stderr, fmt, ap);
fprintf(stderr, "\n");
} else {
vfprintf(stderr, nfmt, ap);
free(nfmt);
}
fflush(stderr);
return 0;
}

int on_message_begin(http_parser* _) {
(void)_;
printf("\n***MESSAGE BEGIN***\n\n");
return 0;
return log_event("MESSAGE BEGIN");
}

int on_headers_complete(http_parser* _) {
(void)_;
printf("\n***HEADERS COMPLETE***\n\n");
return 0;
return log_event("HEADERS COMPLETE");
}

int on_message_complete(http_parser* _) {
(void)_;
printf("\n***MESSAGE COMPLETE***\n\n");
return 0;
return log_event("MESSAGE COMPLETE");
}

int on_chunk_begin(http_parser* _) {
(void)_;
return log_event("CHUNK BEGIN");
}

int on_chunk_complete(http_parser* _) {
(void)_;
return log_event("CHUNK COMPLETE");
}

int on_url(http_parser* _, const char* at, size_t length) {
(void)_;
printf("Url: %.*s\n", (int)length, at);
return 0;
return log_event("URL: %.*s", (int)length, at);
}

int on_header_field(http_parser* _, const char* at, size_t length) {
(void)_;
printf("Header field: %.*s\n", (int)length, at);
return 0;
return log_event("Header field: %.*s", (int)length, at);
}

int on_header_value(http_parser* _, const char* at, size_t length) {
(void)_;
printf("Header value: %.*s\n", (int)length, at);
return 0;
return log_event("Header value: %.*s", (int)length, at);
}

int on_body(http_parser* _, const char* at, size_t length) {
(void)_;
printf("Body: %.*s\n", (int)length, at);
return 0;
log_event("<body> (size=%zu)", length);
printf("%.*s", (int)length, at);
return log_event("</body>");
}

void usage(const char* name) {
fprintf(stderr,
"Usage: %s $type $filename\n"
" %s $url\n"
" type: -x, where x is one of {r,b,q}\n"
" parses file as a Response, reQuest, or Both\n",
name);
name, name);
exit(EXIT_FAILURE);
}

int main(int argc, char* argv[]) {
enum http_parser_type file_type;
char data[2 << 16];
enum http_parser_type file_type = HTTP_RESPONSE;

if (argc != 3) {
if (argc != 3 && argc != 2) {
usage(argv[0]);
}

char* type = argv[1];
if (type[0] != '-') {
usage(argv[0]);
}
if (argc == 3) {
char* type = argv[1];
if (type[0] != '-') {
usage(argv[0]);
}

switch (type[1]) {
/* in the case of "-", type[1] will be NUL */
switch (type[1]) {
/* in the case of "-", type[1] will be NUL */
case 'r':
file_type = HTTP_RESPONSE;
break;
Expand All @@ -105,28 +146,81 @@ int main(int argc, char* argv[]) {
break;
default:
usage(argv[0]);
}
}

char* filename = argv[2];
FILE* file = fopen(filename, "r");
if (file == NULL) {
perror("fopen");
return EXIT_FAILURE;
}
int file = -1;

fseek(file, 0, SEEK_END);
long file_length = ftell(file);
if (file_length == -1) {
perror("ftell");
return EXIT_FAILURE;
}
fseek(file, 0, SEEK_SET);
if (argc == 3) {
char* filename = argv[2];
file = (strcmp(filename, "-"))?open(filename, O_RDONLY):STDIN_FILENO;
if (file == -1) {
perror("open");
return EXIT_FAILURE;
}
} else {
char* url = argv[1];
struct http_parser_url u;
if (http_parser_parse_url(url, strlen(url), 0, &u) != 0) {
fprintf(stderr, "Unable to parse %s\n", url);
return EXIT_FAILURE;
}
if ((u.field_set & (1 << UF_SCHEMA)) == 0 ||
(u.field_set & (1 << UF_HOST)) == 0 ||
(u.field_set & (1 << UF_PATH)) == 0 ||
u.field_data[UF_SCHEMA].len != 4 ||
strncmp(url + u.field_data[UF_SCHEMA].off, "http", 4)) {
fprintf(stderr, "Absolute HTTP URL expected\n");
return EXIT_FAILURE;
}

char* data = malloc(file_length);
if (fread(data, 1, file_length, file) != (size_t)file_length) {
fprintf(stderr, "couldn't read entire file\n");
free(data);
return EXIT_FAILURE;
int n;
char *remote; char *port;
remote = strndup(url + u.field_data[UF_HOST].off, u.field_data[UF_HOST].len);
port = u.port?strndup(url + u.field_data[UF_PORT].off, u.field_data[UF_PORT].len):strdup("http");

struct addrinfo *res, *ressave;
struct addrinfo hints = {
.ai_family = AF_UNSPEC,
.ai_socktype = SOCK_STREAM,
.ai_protocol = IPPROTO_TCP
};
if ((n = getaddrinfo(remote, port, &hints, &res)) != 0) {
fprintf(stderr, "unable to get address for %s:%s: %s",
remote, port, gai_strerror(n));
free(remote); free(port);
return EXIT_FAILURE;
}
ressave = res;
do {
int s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
if (s < 0) continue;
if (connect(s, res->ai_addr, res->ai_addrlen) != 0) {
close(s);
continue;
}
file = s;
break;
} while ((res = res->ai_next) != NULL);
freeaddrinfo(ressave);

if (file == -1) {
fprintf(stderr, "unable to connect to %s:%s",
remote, port);
free(remote); free(port);
return EXIT_FAILURE;
}

struct iovec stuff[] = {
{"GET ", 4},
{url + u.field_data[UF_PATH].off,
u.field_data[UF_PATH].len + ((u.field_set & (1 << UF_QUERY))?(1 + u.field_data[UF_QUERY].len):0) },
{" HTTP/1.1\r\nHost: ", 17},
{url + u.field_data[UF_HOST].off, u.field_data[UF_HOST].len},
{"\r\n\r\n", 4}
};
writev(file, stuff, sizeof(stuff)/sizeof(stuff[0]));
shutdown(file, SHUT_WR);
}

http_parser_settings settings;
Expand All @@ -136,21 +230,28 @@ int main(int argc, char* argv[]) {
settings.on_header_field = on_header_field;
settings.on_header_value = on_header_value;
settings.on_headers_complete = on_headers_complete;
settings.on_chunk_begin = on_chunk_begin;
settings.on_body = on_body;
settings.on_chunk_complete = on_chunk_complete;
settings.on_message_complete = on_message_complete;

http_parser parser;
http_parser_init(&parser, file_type);
size_t nparsed = http_parser_execute(&parser, &settings, data, file_length);
free(data);

if (nparsed != (size_t)file_length) {
fprintf(stderr,
"Error: %s (%s)\n",
http_errno_description(HTTP_PARSER_ERRNO(&parser)),
http_errno_name(HTTP_PARSER_ERRNO(&parser)));
return EXIT_FAILURE;
gettimeofday(&start, NULL);

size_t len;
while ((len = read(file, data, sizeof(data))) > 0) {
size_t nparsed = http_parser_execute(&parser, &settings, data, len);
if (nparsed != len) {
fprintf(stderr,
"Error: %s (%s)\n",
http_errno_description(HTTP_PARSER_ERRNO(&parser)),
http_errno_name(HTTP_PARSER_ERRNO(&parser)));
if (file && file != STDIN_FILENO) close(file);
return EXIT_FAILURE;
}
}

if (file && file != STDIN_FILENO) close(file);
return EXIT_SUCCESS;
}
2 changes: 2 additions & 0 deletions http_parser.c
Original file line number Diff line number Diff line change
Expand Up @@ -1782,6 +1782,7 @@ size_t http_parser_execute (http_parser *parser,
parser->state = s_header_field_start;
} else {
parser->state = s_chunk_data;
CALLBACK_NOTIFY(chunk_begin);
}
break;
}
Expand Down Expand Up @@ -1822,6 +1823,7 @@ size_t http_parser_execute (http_parser *parser,
STRICT_CHECK(ch != LF);
parser->nread = 0;
parser->state = s_chunk_size_start;
CALLBACK_NOTIFY(chunk_complete);
break;

default:
Expand Down
4 changes: 4 additions & 0 deletions http_parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,9 @@ enum flags
XX(CB_header_field, "the on_header_field callback failed") \
XX(CB_header_value, "the on_header_value callback failed") \
XX(CB_headers_complete, "the on_headers_complete callback failed") \
XX(CB_chunk_begin, "the on_chunk_begin callback failed") \
XX(CB_body, "the on_body callback failed") \
XX(CB_chunk_complete, "the on_chunk_complete callback failed") \
XX(CB_message_complete, "the on_message_complete callback failed") \
\
/* Parsing-related errors */ \
Expand Down Expand Up @@ -228,7 +230,9 @@ struct http_parser_settings {
http_data_cb on_header_field;
http_data_cb on_header_value;
http_cb on_headers_complete;
http_cb on_chunk_begin;
http_data_cb on_body;
http_cb on_chunk_complete;
http_cb on_message_complete;
};

Expand Down
Loading