blob: cfab23d774059cd9beac29aa4afcc3ce9b8ebb06 [file] [edit]
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <errno.h>
#include "./eutils.h"
#include "internal.h"
#include "evhtp/evhtp.h"
struct reply_ {
evhtp_request_t * request;
FILE * file_desc;
struct evbuffer * buffer;
};
/* This function is called each time the client has been sent
* all outstanding data. We use this to send the next part of
* the file in a chunk at 128 byte increments.
*
* When there is no more data to be read from the file, this
* will send the final chunked reply and free our struct reply_.
*/
static evhtp_res
http__send_chunk_(evhtp_connection_t * conn, void * arg)
{
struct reply_ * reply = (struct reply_ *)arg;
char buf[128];
size_t bytes_read;
/* try to read 128 bytes from the file pointer */
bytes_read = fread(buf, 1, sizeof(buf), reply->file_desc);
log_info("Sending %zu bytes", bytes_read);
if (bytes_read > 0) {
/* add our data we read from the file into our reply buffer */
evbuffer_add(reply->buffer, buf, bytes_read);
/* send the reply buffer as a http chunked message */
evhtp_send_reply_chunk(reply->request, reply->buffer);
/* we can now drain our reply buffer as to not be a resource
* hog.
*/
evbuffer_drain(reply->buffer, bytes_read);
}
/* check if we have read everything from the file */
if (feof(reply->file_desc)) {
log_info("Sending last chunk");
/* now that we have read everything from the file, we must
* first unset our on_write hook, then inform evhtp to send
* this message as the final chunk.
*/
evhtp_connection_unset_hook(conn, evhtp_hook_on_write);
evhtp_send_reply_chunk_end(reply->request);
/* we can now free up our little reply_ structure */
{
fclose(reply->file_desc);
evhtp_safe_free(reply->buffer, evbuffer_free);
evhtp_safe_free(reply, free);
}
}
return EVHTP_RES_OK;
}
static evhtp_res
http__conn_fini_(struct evhtp_connection * c, void * arg)
{
log_info("hi");
return EVHTP_RES_OK;
}
/* This function is called when a request has been fully received.
*
* This function assumes the `arg` value is the filename that was
* passed via `evhtp_set_gencb` in `main`.
*
* 1. open the file
* 2. create a `struct reply_`
* 3. create an evbuffer that we will write into.
* 4. set a hook to call the function `http__send_chunk_` each
* time all data has been sent from the previous write call.
* 5. start the chunked stream via `evhtp_send_reply_chunk_start`
*/
static void
http__callback_(evhtp_request_t * req, void * arg)
{
const char * filename = arg;
FILE * file_desc;
struct reply_ * reply;
evhtp_assert(arg != NULL);
/* open up the file as passed to us via evhtp_set_gencb */
file_desc = fopen(filename, "r");
evhtp_assert(file_desc != NULL);
/* create our little internal reply structure which will
* be used by `http__send_chunk_`
*/
reply = mm__alloc_(struct reply_, {
req,
file_desc,
evbuffer_new()
});
/* here we set a connection hook of the type `evhtp_hook_on_write`
*
* this will execute the function `http__send_chunk_` each time
* all data has been written to the client.
*/
evhtp_connection_set_hook(req->conn,
evhtp_hook_on_write,
http__send_chunk_, reply);
/* set a hook to be called when the client disconnects */
evhtp_connection_set_hook(req->conn,
evhtp_hook_on_connection_fini,
http__conn_fini_, NULL);
/* we do not have to start sending data from the file from here -
* this function will write data to the client, thus when finished,
* will call our `http__send_chunk_` callback.
*/
evhtp_send_reply_chunk_start(req, EVHTP_RES_OK);
}
int
main(int argc, char ** argv)
{
evhtp_t * htp;
struct event_base * evbase;
if (argc < 2) {
printf("Usage: %s <file>\n", argv[0]);
exit(EXIT_FAILURE);
}
evbase = event_base_new();
evhtp_alloc_assert(evbase);
htp = evhtp_new(evbase, NULL);
evhtp_alloc_assert(htp);
/* here we set our default request response callback, the argument
* that is passed will be the filename we want to stream to the
* client in chunked form.
*/
evhtp_set_gencb(htp, http__callback_, strdup(argv[1]));
log_info("curl http://127.0.0.1:%d/", bind__sock_port0_(htp));
event_base_loop(evbase, 0);
return 0;
}