Skip to content

Instantly share code, notes, and snippets.

@paralax
Last active July 7, 2017 18:08
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save paralax/6f57e457b5edd7b11aae2988ae8564a0 to your computer and use it in GitHub Desktop.
Save paralax/6f57e457b5edd7b11aae2988ae8564a0 to your computer and use it in GitHub Desktop.
vtwebd - a very tiny web daemon that servers static content
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "vtweb.h"
int port;
void
sig_hdlr(int sig)
{
switch(sig) {
case SIGPIPE:
break;
}
return;
}
void
usage(char *progname)
{
fprintf(stderr, "%s usage: %s -d docdir -p port -t nthreads\n", progname, progname);
exit(1);
}
void *
thread_start_listen(void *arg)
{
int thread_id = (int)arg;
struct servent *serv;
int n, ns, s, len, one = 1, *status;
char buf[1024];
struct sockaddr_in name;
static int rc;
s = socket(AF_INET, SOCK_STREAM, 0);
if (s < 0) {
fprintf(stderr, "error: socket creation.\n");
return;
}
if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one))) {
fprintf(stderr, "setsockopt SO_REUSEADDR error.\n");
return;
}
if (setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one))) {
fprintf(stderr, "setsockopt SO_REUSEPRT error.\n");
return;
}
memset(&name, 0, sizeof(struct sockaddr_in));
name.sin_family = AF_INET;
name.sin_port = htons(port);
len = sizeof(struct sockaddr_in);
n = INADDR_ANY;
memcpy(&name.sin_addr, &n, sizeof(long));
if (bind(s, (struct sockaddr *) & name, len) < 0) {
fprintf(stderr, "error: socket bind.\n");
return;
}
// printf("started thread %d\n", thread_id);
if (listen(s, 5) < 0) {
fprintf(stderr, "error: listen.\n");
return;
}
if ((ns = accept(s, (struct sockaddr *) & name, &len)) < 0) {
fprintf(stderr, "error: accept.\n");
return;
}
/* get the request, handle it */
while ((n = recv(ns, buf, sizeof(buf), 0)) > 0) {
/* handle the received content here ... */
handle_req(ns, buf);
}
/* cleanup: close sockets, kill thread */
close(ns);
close(s);
pthread_join(threads[thread_id], (void *)&status);
/* respawn the thread .. we're done */
rc = pthread_create(&threads[thread_id], NULL, thread_start_listen, arg);
if (rc) {
printf("ERROR: thread initialization failure.\n");
exit(1);
}
}
int
main(int argc, char *argv[])
{
static int ch, t, nthreads = 32;
struct servent *se;
signal(SIGPIPE, sig_hdlr);
#if 0
if ((se = getservbyname("www", "tcp")) == NULL)
if ((sp = getservbyname ("http", "tcp")) == NULL)
printf("oops ...\n");
printf("se: %s\n", se);
port = se->s_port;
printf("%s\n", port);
#endif
while ((ch = getopt(argc, argv, "d:p:t:h")) != -1) {
switch(ch) {
case 'd':
docdir = optarg;
break;
case 'p':
port = atoi(optarg);
break;
case 't':
nthreads = atoi(optarg);
break;
case 'h':
default:
usage(argv[0]);
break;
}
}
for (; t < nthreads; t++) {
static int rc;
rc = pthread_create(&threads[t], NULL, thread_start_listen, (void *)t);
if (rc) {
printf("ERROR: thread initialization failure.\n");
exit(1);
}
}
pthread_exit(NULL);
}
all: vtwebd
request.o: request.c
gcc -g -O2 -c request.c
vtwebd.o: main.c
gcc -pthread -g -O2 -c main.c
vtwebd: vtwebd.o request.o
gcc -pthread -g -o vtwebd main.o request.o
clean:
rm -f *.o *.core vtwebd
#include <sys/param.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <fcntl.h>
#include <unistd.h>
#include <pthread.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include "vtweb.h"
/*
* http://radio.weblogs.com/0111551/stories/2002/08/15/playWithStringsTheWayCc
* ProgrammersUsedTo.html
*/
int
reverse(char *str)
{
static int x, l;
if (NULL == str)
return -1;
//no string
l = strlen(str) - 1;
//get the string length
if (1 == l)
return 1;
for (x = 0; x < l; x++, l--) {
str[x] ^= str[l]; //triple XOR Trick
str[l] ^= str[x]; //for not using a temp
str[x] ^= str[l];
}
return 0;
}
/* http://www.codecomments.com/C/message377951-3.html */
static int
endswith(char *phrase, char *searchme)
{
reverse(phrase);
reverse(searchme);
if (0 == strncasecmp(phrase, searchme, strlen(phrase))) {
return 1;
} else {
return 0;
}
}
void
err500_error_req(int sock, char *uri)
{
char tmp[65535], header[65535];
snprintf(header, sizeof(header),
"HTTP/1.0 500\nContent-type: text/html\n\n");
write(sock, header, sizeof(header));
snprintf(tmp, sizeof(tmp), "<h1>Error: 500</h1>The file %s was not found on this server.", uri);
write(sock, tmp, strlen(tmp));
return;
}
void
err405_error_req(int sock, char *uri)
{
char tmp[65535], header[65535];
snprintf(header, sizeof(header),
"HTTP/1.0 405\nContent-type: text/html\n\n");
write(sock, header, sizeof(header));
snprintf(tmp, sizeof(tmp), "<h1>Error: 405</h1>Method Not Allowed");
write(sock, tmp, strlen(tmp));
return;
}
void
err404_error_req(int sock, char *uri)
{
char tmp[65535], header[65535];
snprintf(header, sizeof(header),
"HTTP/1.0 404\nContent-type: text/html\n\n");
write(sock, header, sizeof(header));
snprintf(tmp, sizeof(tmp), "<h1>Error: 404</h1>The file %s was not found on this server.", uri);
write(sock, tmp, strlen(tmp));
return;
}
void
err403_error_req(int sock, char *uri)
{
char tmp[65535], header[65535];
snprintf(header, sizeof(header),
"HTTP/1.0 403\nContent-type: text/html\n\n");
write(sock, header, sizeof(header));
snprintf(tmp, sizeof(tmp), "<h1>Error: 403</h1>You do not have permission for this request %s", uri);
write(sock, tmp, strlen(tmp));
return;
}
char *
gettype(char *input)
{
char html[] = ".html";
char htm[] = ".htm";
char gif[] = ".gif";
char jpg[] = ".jpg";
char jpeg[] = ".jpeg";
if (endswith(html, input))
return ("text/html");
else if (endswith(htm, input))
return ("text/html");
else if (endswith(gif, input))
return ("image/gif");
else if (endswith(jpg, input))
return ("image/jpeg");
else if (endswith(jpeg, input))
return ("image/jpeg");
else
return ("text/plain");
}
#define INET6_ADDRSTRLEN 46
//https://stackoverflow.com/questions/2064636/getting-the-source-address-of-an-incoming-socket-connection
void
peerip(int s)
{
socklen_t len;
struct sockaddr_storage addr;
char ipstr[INET6_ADDRSTRLEN], *res;
int port;
len = sizeof addr;
getpeername(s, (struct sockaddr *) & addr, &len);
//deal with both IPv4 and IPv6:
if (addr.ss_family == AF_INET) {
struct sockaddr_in *s = (struct sockaddr_in *) & addr;
port = ntohs(s->sin_port);
inet_ntop(AF_INET, &s->sin_addr, ipstr, sizeof ipstr);
} else {
//AF_INET6
struct sockaddr_in6 *s = (struct sockaddr_in6 *) & addr;
port = ntohs(s->sin6_port);
inet_ntop(AF_INET6, &s->sin6_addr, ipstr, sizeof ipstr);
}
printf("%s ", ipstr);
return;
}
void
log_req(int sock, char *req, int status)
{
time_t rawtime;
struct tm *timeinfo;
char *timestr;
time(&rawtime);
timeinfo = localtime(&rawtime);
timestr = asctime(timeinfo);
timestr[strlen(timestr) - 1] = 0;
peerip(sock);
printf("[%s] \"%s\" %d\n", timestr, req, status);
}
void
handle_req(int sock, char *req)
{
static char *line, *method, *uri, *uritmp, *req_orig;
static char fname[65535]; /* XXX */
char contents[65535]; /* XXX */
static int i, fd, n;
static struct stat sb;
static char header[65535];
line = strtok(req, "\n");
req_orig = strdup(line);
req_orig[strlen(req_orig) - 1] = 0;
method = strtok(line, " \t");
uri = strtok(NULL, " \t");
if (strcmp("GET", method) != 0) {
snprintf(header, sizeof(header),
"HTTP/1.0 405 Method Not Allowed\nContent-type: text/html\n\n");
write(sock, header, sizeof(header));
shutdown(sock, SHUT_RDWR);
close(sock);
log_req(sock, req_orig, 405);
return;
}
if (strcmp("/", uri) == 0) {
snprintf(header, sizeof(header),
"HTTP/1.0 301 Moved Permanently\nLocation: /index.html\n\n");
write(sock, header, sizeof(header));
shutdown(sock, SHUT_RDWR);
close(sock);
log_req(sock, req_orig, 301);
return;
}
snprintf(fname, sizeof(fname), "%s%s", docdir, uri);
i = stat(fname, &sb);
if (i != 0) {
if (access(uri, F_OK) != -1) {
err403_error_req(sock, uri);
log_req(sock, req_orig, 403);
} else {
err404_error_req(sock, uri);
log_req(sock, req_orig, 404);
}
shutdown(sock, SHUT_RDWR);
close(sock);
return;
}
uritmp = strdup(uri);
snprintf(header, sizeof(header),
"HTTP/1.0 200 OK\nContent-type: %s\n\n", gettype(uritmp));
free(uritmp);
write(sock, header, sizeof(header));
fd = open(fname, O_RDONLY, 0666);
n = read(fd, contents, sizeof(contents));
write(sock, contents, n);
shutdown(sock, SHUT_RDWR);
close(fd);
close(sock);
log_req(sock, req_orig, 200);
return;
}
#define MAX_NUM_THREADS 512
pthread_t threads[MAX_NUM_THREADS];
void * thread_start_listen(void *arg);
void handle_req(int sock, char *req);
char *docdir;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment