Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

client_http_run_once example: use NEAT_RUN_ONCE #147

Closed
wants to merge 2 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
1 change: 1 addition & 0 deletions examples/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ LIST(APPEND neat_programs
client.c
client_multi.c
client_http_get.c
client_http_run_once.c
client_https_get.c
server_chargen.c
server_daytime.c
Expand Down
202 changes: 202 additions & 0 deletions examples/client_http_run_once.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <poll.h>
#include "../neat.h"

/**********************************************************************

HTTP-GET client in neat
* connect to HOST and send GET request
* write response to stdout

client_http_get [OPTIONS] HOST
-u : URI
-n : number of requests/flows

**********************************************************************/

static uint32_t config_rcv_buffer_size = 65536;
static uint32_t config_max_flows = 50;
static char request[512];
static const char *request_tail = "HTTP/1.0\r\nUser-agent: libneat\r\nConnection: close\r\n\r\n";

static neat_error_code
on_error(struct neat_flow_operations *opCB)
{
fprintf(stderr, "%s\n", __func__);
exit(EXIT_FAILURE);
}

static neat_error_code
on_readable(struct neat_flow_operations *opCB)
{
// data is available to read
unsigned char buffer[config_rcv_buffer_size];
uint32_t bytes_read = 0;
neat_error_code code;

code = neat_read(opCB->ctx, opCB->flow, buffer, config_rcv_buffer_size, &bytes_read, NULL, 0);
if (code == NEAT_ERROR_WOULD_BLOCK) {
return 0;
} else if (code != NEAT_OK) {
return on_error(opCB);
}

if (!bytes_read) { // eof
int *numstreams = opCB->userData;
(*numstreams)--; /* one stream less */
fflush(stdout);
opCB->on_readable = NULL; // do not read more
neat_set_operations(opCB->ctx, opCB->flow, opCB);
neat_stop_event_loop(opCB->ctx);
} else if (bytes_read > 0) {
fwrite(buffer, sizeof(char), bytes_read, stdout);
}
return 0;
}

static neat_error_code
on_writable(struct neat_flow_operations *opCB)
{
neat_error_code code;
code = neat_write(opCB->ctx, opCB->flow, (const unsigned char *)request, strlen(request), NULL, 0);
if (code != NEAT_OK) {
return on_error(opCB);
}
opCB->on_writable = NULL;
neat_set_operations(opCB->ctx, opCB->flow, opCB);
return 0;
}

static neat_error_code
on_connected(struct neat_flow_operations *opCB)
{
// now we can start writing
opCB->on_readable = on_readable;
opCB->on_writable = on_writable;
neat_set_operations(opCB->ctx, opCB->flow, opCB);
return 0;
}

int
main(int argc, char *argv[])
{
struct neat_ctx *ctx = NULL;
struct neat_flow *flows[config_max_flows];
struct neat_flow_operations ops[config_max_flows];
uint64_t prop = 0;
int result = 0;
int arg = 0;
uint32_t num_flows = 1;
uint32_t i = 0;
int backend_fd;
int streams_going = 0;
result = EXIT_SUCCESS;

memset(&ops, 0, sizeof(ops));
memset(flows, 0, sizeof(flows));

snprintf(request, sizeof(request), "GET %s %s", "/", request_tail);

while ((arg = getopt(argc, argv, "u:n:")) != -1) {
switch(arg) {
case 'u':
snprintf(request, sizeof(request), "GET %s %s", optarg, request_tail);
break;
case 'n':
num_flows = strtoul (optarg, NULL, 0);
if (num_flows > config_max_flows) {
num_flows = config_max_flows;
}
fprintf(stderr, "%s - option - number of flows: %d\n", __func__, num_flows);
break;
default:
fprintf(stderr, "usage: client_http_get [OPTIONS] HOST\n");
goto cleanup;
break;
}
}

if (optind + 1 != argc) {
fprintf(stderr, "usage: client_http_get [OPTIONS] HOST\n");
goto cleanup;
}

printf("%d flows - requesting: %s\n", num_flows, request);

if ((ctx = neat_init_ctx()) == NULL) {
fprintf(stderr, "could not initialize context\n");
result = EXIT_FAILURE;
goto cleanup;
}


for (i = 0; i < num_flows; i++) {
if ((flows[i] = neat_new_flow(ctx)) == NULL) {
fprintf(stderr, "could not initialize context\n");
result = EXIT_FAILURE;
goto cleanup;
}

neat_get_property(ctx, flows[i], &prop);
prop |= NEAT_PROPERTY_OPTIONAL_SECURITY;
prop |= NEAT_PROPERTY_RETRANSMISSIONS_REQUIRED;
neat_set_property(ctx, flows[i], prop);

ops[i].on_connected = on_connected;
ops[i].on_error = on_error;
ops[i].userData = &streams_going;
streams_going++;
neat_set_operations(ctx, flows[i], &(ops[i]));

// wait for on_connected or on_error to be invoked
if (neat_open(ctx, flows[i], argv[argc - 1], 80, NULL, 0) != NEAT_OK) {
fprintf(stderr, "Could not open flow\n");
result = EXIT_FAILURE;
} else {
fprintf(stderr, "Opened flow %d\n", i);
}
}

/* Get the underlying single file descriptor from libuv. Wait on this
descriptor to become readable to know when to ask NEAT to run another
loop ONCE on everything that it might have to work on. */

backend_fd = neat_get_backend_fd(ctx);

/* kick off the event loop first */
neat_start_event_loop(ctx, NEAT_RUN_ONCE);

while (streams_going) {
struct pollfd fds[1] = {
{
.fd = backend_fd,
.events = POLLERR | POLLIN | POLLHUP,
.revents = 0,
}
};
int rc = poll(fds, 1, 1000);

if (rc > 0) {
/* there's stuff to do */
neat_start_event_loop(ctx, NEAT_RUN_NOWAIT);
}
else {
fprintf(stderr, "Waiting on %d streams...\n", streams_going);
neat_start_event_loop(ctx, NEAT_RUN_NOWAIT);
}

}
cleanup:
for (i = 0; i < num_flows; i++) {
if (flows[i] != NULL) {
neat_free_flow(flows[i]);
}
}
if (ctx != NULL) {
neat_free_ctx(ctx);
}
exit(result);
}