Skip to content

Instantly share code, notes, and snippets.

@dyoder
Created July 7, 2010 16:30
Show Gist options
  • Save dyoder/466918 to your computer and use it in GitHub Desktop.
Save dyoder/466918 to your computer and use it in GitHub Desktop.
/* A simple store using libmicrohttp */
#include <stdint.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdarg.h>
#include <sys/socket.h>
#include <microhttpd.h>
#include <string.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
/* URL processing */
const char *
validate_url (const char *url)
{
/* URLs must not have any slashes, that's our rule */
return index(url + 1, '/') ? 0 : url + 1;
}
/* Accept all connections */
int
accept_policy (void *cls, const struct sockaddr *addr, socklen_t addrlen)
{
return 1;
}
/* Queue a simple response */
int
queue_response (struct MHD_Connection *connection, unsigned int code, char *msg)
{
return MHD_queue_response (connection, code,
MHD_create_response_from_data (strlen (msg),
msg, 0, 0));
}
/* Callback for GET to read the next chunk of the file */
int
get_reader (void *cls, uint64_t pos, char *buf, int max)
{
int *fd = cls;
int nread;
nread = read (*fd, buf, max);
if (nread == 0) /* EOF becomes termination code */
nread = -1;
return nread;
}
/* release resources from GET */
void
cleanup_reader (void *cls)
{
int *fd = cls;
close (*fd);
free (fd);
}
/* GET just fetches the file */
int
dispatch_get (struct MHD_Connection *connection, const char *url)
{
int fd, *cls;
fd = open (url, O_RDONLY, 0);
if (fd < 0)
return queue_response (connection, MHD_HTTP_NOT_FOUND,
"Resource not found");
cls = malloc (sizeof *cls);
*cls = fd;
return MHD_queue_response (connection, MHD_HTTP_OK,
MHD_create_response_from_callback
(MHD_SIZE_UNKNOWN,1024, get_reader, cls,
cleanup_reader));
}
struct upload_closure
{
struct MHD_Response *response;
int fd;
};
/* uploading for PUT and POST */
int
continue_upload (struct MHD_Connection *connection,
const char *upload_data, size_t *upload_data_size,
struct upload_closure *c)
{
int nwritten;
nwritten = write (c->fd, upload_data, *upload_data_size);
if (nwritten < 0)
return 0;
else
{
*upload_data_size -= nwritten;
return 1;
}
}
int
begin_upload (struct MHD_Connection *connection, int fd,
const char *upload_data, size_t *upload_data_size,
void **con_cls)
{
struct upload_closure *c;
c = malloc (sizeof *c);
if (c)
{
char *msg = "Upload successful";
c->response = MHD_create_response_from_data (strlen (msg), msg, 0, 0);
c->fd = fd;
*con_cls = c;
return 1;
}
else
return 0;
}
void
completion_callback (void *cls,
struct MHD_Connection *connection,
void **con_cls,
enum MHD_RequestTerminationCode toe)
{
struct upload_closure *c = *con_cls;
if (c)
{
close (c->fd);
if (c->response)
MHD_destroy_response (c->response);
free (c);
}
}
/* POST handling */
void
add_post_location_header (struct MHD_Connection *connection,
struct upload_closure *c,
char *name)
{
const char *host;
char *newurl;
host = MHD_lookup_connection_value (connection, MHD_HEADER_KIND, "Host");
newurl = alloca (strlen (host) + 100);
sprintf (newurl, "http://%s/%s", host, name);
MHD_add_response_header (c->response, "Location", newurl);
}
int
dispatch_post (struct MHD_Connection *connection, const char *url,
const char *upload_data, size_t *upload_data_size,
void **con_cls)
{
int fd;
struct upload_closure *c = *con_cls;
char name[7];
if (*url)
return queue_response (connection, MHD_HTTP_NOT_FOUND,
"Improper POST name");
if (c)
{
if (*upload_data_size)
return continue_upload (connection, upload_data, upload_data_size, c);
else
if (MHD_queue_response (connection, MHD_HTTP_CREATED, c->response))
{
c->response = 0;
return 1;
}
else
return 0;
}
else
{
strcpy (name, "XXXXXX");
fd = mkstemp (name);
if (fd < 0)
return queue_response (connection, MHD_HTTP_INTERNAL_SERVER_ERROR,
"Cannot create new resource");
else
{
if (begin_upload (connection, fd,
upload_data, upload_data_size, con_cls))
{
add_post_location_header (connection, *con_cls, name);
return 1;
}
else
return 0;
}
}
}
int
dispatch_put (struct MHD_Connection *connection, const char *url,
const char *upload_data, size_t *upload_data_size,
void **con_cls)
{
int fd;
struct upload_closure *c = *con_cls;
if (c)
{
if (*upload_data_size)
return continue_upload (connection, upload_data, upload_data_size, c);
else
if (MHD_queue_response (connection, MHD_HTTP_OK, c->response))
{
c->response = 0;
return 1;
}
else
return 0;
}
else
{
fd = open (url, O_WRONLY|O_TRUNC, 0666);
if (fd < 0)
return queue_response (connection, MHD_HTTP_NOT_FOUND,
"Resource not found");
else
return begin_upload (connection, fd, upload_data, upload_data_size,
con_cls);
}
}
/* Access handler */
int
access_handler (void *cls, struct MHD_Connection *connection,
const char *url, const char *method, const char *version,
const char *upload_data, size_t *upload_data_size,
void **con_cls)
{
url = validate_url (url);
if (!url)
return queue_response (connection, MHD_HTTP_NOT_FOUND,
"Improper resource name");
/* Dispatch based on method */
if (!strcmp ("GET", method))
return dispatch_get (connection, url);
else if (!strcmp ("PUT", method))
return dispatch_put (connection, url,
upload_data, upload_data_size, con_cls);
else if (!strcmp ("POST", method))
return dispatch_post (connection, url,
upload_data, upload_data_size, con_cls);
else
return queue_response (connection, MHD_HTTP_NOT_IMPLEMENTED,
"Method not understood");
}
/* Main program */
main (int argc, char *argv)
{
struct MHD_Daemon *daemon;
daemon = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY,
2020,
accept_policy, 0,
access_handler, 0,
MHD_OPTION_NOTIFY_COMPLETED,
completion_callback, 0,
MHD_OPTION_END);
if (daemon)
pause ();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment