Last active
August 12, 2020 10:50
-
-
Save dcollien/7301248 to your computer and use it in GitHub Desktop.
C program for a libevent server receiving JSON requests and replying with JSON responses over HTTP
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <sys/types.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <unistd.h> | |
#include <string.h> | |
#include <event.h> | |
#include <evhttp.h> | |
#include <signal.h> | |
#include <jansson.h> | |
#define HEADER_BUFFER_SIZE 1024 | |
#define ERROR_RESPONSE_SIZE 1024 | |
void brokenPipe(int signum); | |
void jsonRequestHandler(struct evhttp_request *request, void *arg); | |
void sendErrorResponse(struct evhttp_request *request, char *errorText); | |
void sendJSONResponse(struct evhttp_request *request, json_t *requestJSON, struct event_base *base); | |
int main(int argc, char **argv) { | |
short http_port = 8000; | |
const char *http_addr = "127.0.0.1"; | |
struct evhttp *http_server = NULL; | |
if (argc > 1) { | |
http_addr = argv[1]; | |
if (argc > 2) { | |
http_port = atoi(argv[2]); | |
} | |
} | |
// don't exit on broken pipe (just fail with message) | |
signal(SIGPIPE, brokenPipe); | |
// new event base for async stuff | |
struct event_base *base = event_base_new(); | |
// create a new libevent http server | |
http_server = evhttp_new(base); | |
// bind to a particular address and port (can be specified via argvs) | |
evhttp_bind_socket(http_server, http_addr, http_port); | |
// set jsonRequestHandler as a callback for all requests (no others are caught specifically) | |
evhttp_set_gencb(http_server, jsonRequestHandler, (void *)base); | |
fprintf(stderr, "Server started on %s port %d\n", http_addr, http_port); | |
event_base_dispatch(base); | |
evhttp_free(http_server); | |
event_base_free(base); | |
fprintf(stderr, "Server Died\n"); | |
return EXIT_SUCCESS; | |
} | |
void brokenPipe(int signum) { | |
fprintf(stderr, "Broken Pipe (%d)\n", signum); | |
} | |
void jsonRequestHandler(struct evhttp_request *request, void *arg) { | |
struct event_base *base = (struct event_base *)arg; | |
// Request | |
struct evbuffer *requestBuffer; | |
size_t requestLen; | |
char *requestDataBuffer; | |
json_t *requestJSON; | |
json_error_t error; | |
// Error buffer | |
char errorText[ERROR_RESPONSE_SIZE]; | |
// Process Request | |
requestBuffer = evhttp_request_get_input_buffer(request); | |
requestLen = evbuffer_get_length(requestBuffer); | |
requestDataBuffer = (char *)malloc(sizeof(char) * requestLen); | |
memset(requestDataBuffer, 0, requestLen); | |
evbuffer_copyout(requestBuffer, requestDataBuffer, requestLen); | |
printf("%s\n", evhttp_request_uri(request)); | |
requestJSON = json_loadb(requestDataBuffer, requestLen, 0, &error); | |
free(requestDataBuffer); | |
if (requestJSON == NULL) { | |
snprintf(errorText, ERROR_RESPONSE_SIZE, "Input error: on line %d: %s\n", error.line, error.text); | |
sendErrorResponse(request, errorText); | |
} else { | |
// Debug out | |
requestDataBuffer = json_dumps(requestJSON, JSON_INDENT(3)); | |
printf("%s\n", requestDataBuffer); | |
free(requestDataBuffer); | |
sendJSONResponse(request, requestJSON, base); | |
json_decref(requestJSON); | |
} | |
return; | |
} | |
void sendErrorResponse(struct evhttp_request *request, char *errorText) { | |
// Reponse | |
char responseHeader[HEADER_BUFFER_SIZE]; | |
size_t responseLen; | |
struct evbuffer *responseBuffer; | |
responseLen = strlen(errorText); | |
responseBuffer = evbuffer_new(); | |
// content length to string | |
sprintf(responseHeader, "%d", (int)responseLen); | |
evhttp_add_header(request->output_headers, "Content-Type", "text/plain"); | |
evhttp_add_header(request->output_headers, "Content-Length", responseHeader); | |
evbuffer_add(responseBuffer, errorText, responseLen); | |
evhttp_send_reply(request, 400, "Bad JSON", responseBuffer); | |
evbuffer_free(responseBuffer); | |
} | |
void sendJSONResponse(struct evhttp_request *request, json_t *requestJSON, struct event_base *base) { | |
// Reponse | |
json_t *responseJSON; | |
json_t *message; | |
char responseHeader[HEADER_BUFFER_SIZE]; | |
char *responseData; | |
int responseLen; | |
struct evbuffer *responseBuffer; | |
// Create JSON response data | |
responseJSON = json_object(); | |
message = json_string("Hello World"); | |
json_object_set_new(responseJSON, "message", message); | |
// dump JSON to buffer and store response length as string | |
responseData = json_dumps(responseJSON, JSON_INDENT(3)); | |
responseLen = strlen(responseData); | |
snprintf(responseHeader, HEADER_BUFFER_SIZE, "%d", (int)responseLen); | |
json_decref(responseJSON); | |
// create a response buffer to send reply | |
responseBuffer = evbuffer_new(); | |
// add appropriate headers | |
evhttp_add_header(request->output_headers, "Content-Type", "application/json"); | |
evhttp_add_header(request->output_headers, "Content-Length", responseHeader); | |
evbuffer_add(responseBuffer, responseData, responseLen); | |
// send the reply | |
evhttp_send_reply(request, 200, "OK", responseBuffer); | |
evbuffer_free(responseBuffer); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment