Created
February 20, 2018 04:30
-
-
Save kmicinski/afa3c1dec7ef612292fdeb27be8f625a to your computer and use it in GitHub Desktop.
Hint code for 311
This file contains hidden or 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
// Note that I am using: | |
// https://github.com/francoiscolas/multipart-parser | |
// Assuming it is compiled in subdirectory multipart-parser, I compile with: | |
// gcc server_example.c multipart-parser/multipartparser.o | |
// Standard C libraries | |
#include <stdio.h> | |
#include <stdlib.h> | |
// Various POSIX libraries | |
#include <unistd.h> | |
// Various string utilities | |
#include <string.h> | |
// Operations on files | |
#include <fcntl.h> | |
// Gives us access to the C99 "bool" type | |
#include <stdbool.h> | |
// Includes for socket programming | |
#include <arpa/inet.h> | |
#include <netinet/in.h> | |
#include <sys/types.h> | |
#include <sys/socket.h> | |
#include <sys/stat.h> | |
// Memory management stuff | |
#include <sys/mman.h> | |
// Multipart parser | |
#include "multipart-parser/multipartparser.h" | |
#define perror(err) fprintf(stderr, "%s\n", err); | |
#define BUFLEN 1024 | |
// | |
// Global variables | |
// | |
int server_fd = -1; | |
char *root; // Root directory from which the server serves files | |
char buffer[1024]; | |
char secret_key[100]; | |
int LOG_ENABLED = 1; | |
void logmsg(char *message) { | |
printf("%s\n", message); | |
fflush(stdout); | |
} | |
/* | |
* Returns true if string `pre` is a prefix of `str` | |
*/ | |
bool prefix(const char *pre, const char *str) | |
{ | |
return strncmp(pre, str, strlen(pre)) == 0; | |
} | |
void hello_world() { | |
printf("Hello, world!\n"); | |
fflush(stdout); | |
} | |
/* | |
* cerror - returns an error message to the client | |
*/ | |
void cerror(FILE *stream, char *cause, char *errno, | |
char *shortmsg, char *longmsg) { | |
fprintf(stream, "HTTP/1.1 %s %s\n", errno, shortmsg); | |
fprintf(stream, "Content-type: text/html\n"); | |
fprintf(stream, "\n"); | |
fprintf(stream, "<html><title>Tiny Error</title>"); | |
fprintf(stream, "<body bgcolor=""ffffff"">\n"); | |
fprintf(stream, "%s: %s\n", errno, shortmsg); | |
fprintf(stream, "<p>%s: %s\n", longmsg, cause); | |
fprintf(stream, "<hr><em>The Tiny Web server</em>\n"); | |
} | |
struct fatFilePointer { | |
int length; | |
char *data; | |
}; | |
#define CHUNK_SIZE 100000 | |
struct fatFilePointer read_file(char *name) | |
{ | |
FILE *file; | |
char *buffer; | |
unsigned long fileLen; | |
struct fatFilePointer ret; | |
ret.length = 0; | |
ret.data = NULL; | |
//Open file | |
file = fopen(name, "rb"); | |
if (!file) | |
{ | |
fprintf(stderr, "Unable to open file %s", name); | |
return ret; | |
} | |
fileLen = 0; | |
buffer = malloc(CHUNK_SIZE); | |
char temp[CHUNK_SIZE]; | |
unsigned long bytesRead; | |
do { | |
bytesRead = fread(temp,1,CHUNK_SIZE,file); | |
char *newbuffer = malloc(fileLen + CHUNK_SIZE); | |
for (unsigned long i = 0; i < fileLen; i++) { | |
newbuffer[i] = buffer[i]; | |
} | |
for (unsigned long i = 0; i < bytesRead; i++) { | |
newbuffer[fileLen + i] = temp[i]; | |
} | |
fileLen += bytesRead; | |
char *oldbuf = buffer; | |
buffer = newbuffer; | |
free(oldbuf); | |
} while (bytesRead == CHUNK_SIZE); | |
ret.length = fileLen; | |
ret.data = buffer; | |
return ret; | |
} | |
int starts_with(const char *a, const char *b) | |
{ | |
if (strncmp(a, b, strlen(b)) == 0) return 1; | |
return 0; | |
} | |
int total = 0; | |
int file = 0; | |
int on_data(multipartparser *parser,const char *at, size_t length) { | |
printf("here!\n"); | |
printf("Length: %lu\n",length); | |
write(file,at,length); | |
total += length; | |
printf("Total %d\n", total); | |
return 0; | |
} | |
/* | |
* Responsd to an HTTP request | |
*/ | |
void serve_http(int socket) { | |
char method[100]; | |
char filename[100]; | |
char filetype[30]; | |
char version[100]; | |
char cgiargs[100]; | |
char uri[200]; | |
char *p; | |
FILE *stream = fdopen(socket, "r+"); | |
char *partial_body = malloc(BUFLEN); | |
int used = 0; | |
struct stat sbuf; | |
int fd = -1; | |
fgets(buffer, BUFLEN, stream); | |
printf("%s", buffer); | |
sscanf(buffer, "%s %s %s", method, uri, version); | |
strcpy(partial_body,buffer); | |
used += strlen(buffer); | |
int length = 0; | |
char boundary[50]; | |
// Parse each header in sequence | |
while(strcmp(buffer, "\r\n")) { | |
fgets(buffer, BUFLEN, stream); | |
printf("%s", buffer); | |
strcat(partial_body,buffer); | |
used += strlen(buffer); | |
if (starts_with(buffer,"Content-Length:")) { | |
sscanf(buffer,"Content-Length: %d",&length); | |
printf("Length is %d\n",length); | |
} | |
if (starts_with(buffer,"Content-Type: multipart/form-data;")) { | |
sscanf(buffer,"Content-Type: multipart/form-data; boundary=%s",boundary); | |
printf("Boundary is %s\n",boundary); | |
} | |
} | |
strcat(partial_body,buffer); | |
used += strlen(buffer); | |
char *body = malloc(length); | |
int read = fread(body,1,length,stream); | |
printf("Read %d out of %d bytes\n", read, length); | |
//printf("Body: %s",body); | |
char *total = malloc(strlen(partial_body)+length); | |
memcpy(total,partial_body,strlen(partial_body)); | |
memcpy(total+strlen(partial_body),body,length); | |
//write(1,body,length); | |
multipartparser_callbacks callbacks; | |
multipartparser parser; | |
multipartparser_callbacks_init(&callbacks); // It only sets all callbacks to NULL. | |
file = open("file",O_WRONLY | O_CREAT,0644); | |
callbacks.on_data = &on_data; | |
multipartparser_init(&parser, boundary); | |
int nparsed = multipartparser_execute(&parser, &callbacks, total, strlen(partial_body)+length); | |
printf("%d\n", file); | |
/* tiny only supports the GET method */ | |
if (strcasecmp(method, "GET") != 0) { | |
cerror(stream, method, "501", "Not Implemented", | |
"Tiny does not implement this method"); | |
fclose(stream); | |
close(socket); | |
return; | |
} | |
/* read (and ignore) the HTTP headers */ | |
printf("HTTP request: %s\n",buffer); | |
/* parse the uri [crufty] */ | |
strcpy(cgiargs, ""); | |
strcpy(filename, root); | |
strcat(filename, uri); | |
if (uri[strlen(uri)-1] == '/') { | |
strcat(filename, "index.html"); | |
} | |
printf("%s", filename); | |
/* make sure the file exists */ | |
if (stat(filename, &sbuf) < 0) { | |
cerror(stream, filename, "404", "Not found", | |
"Tiny couldn't find this file"); | |
fclose(stream); | |
close(socket); | |
return; | |
} | |
/* serve static content */ | |
if (strstr(filename, ".html")) { | |
strcpy(filetype, "text/html"); | |
} else if (strstr(filename, ".gif")) { | |
strcpy(filetype, "image/gif"); | |
} else if (strstr(filename, ".jpg")) { | |
strcpy(filetype, "image/jpg"); | |
} else { | |
strcpy(filetype, "text/plain"); | |
} | |
struct fatFilePointer contents = read_file(filename); | |
/* print response header */ | |
fprintf(stream, "HTTP/1.1 200 OK\n"); | |
fprintf(stream, "Server: Tiny Web Server\n"); | |
printf("Writing file with length: %d\n", (int)sbuf.st_size); | |
fprintf(stream, "Content-length: %d\n", contents.length); | |
fprintf(stream, "Content-type: %s\n", filetype); | |
fprintf(stream, "\r\n"); | |
// Use mmap to return arbitrary-sized response body | |
fwrite(contents.data, 1, contents.length, stream); | |
free(contents.data); | |
fflush(stream); | |
} | |
int handle_connection(int socket) | |
{ | |
serve_http(socket); | |
return 0; | |
} | |
// Run this at cleanup, closes server file descriptor | |
void cleanup() { | |
if (server_fd != -1) { | |
close(server_fd); | |
} | |
} | |
// Main entry point for program | |
int main(int argc, char** argv) | |
{ | |
int socket_id; | |
int client; | |
socklen_t addrlen = sizeof(struct sockaddr_in); | |
struct sockaddr_in this_addr; | |
struct sockaddr_in peer_addr; | |
unsigned short port = 8080; /* Port to listen on */ | |
if(argc != 4) { | |
printf("Usage: %s <port-number> <root-directory> <secret-key>\n", argv[0]); | |
exit(1); | |
} | |
port = atoi(argv[1]); | |
root = argv[2]; | |
strcpy(secret_key,argv[3]); | |
// We've stack allocated this_addr and peer_addr, so zero them | |
// (since we wouldn't know what was there otherwise). | |
memset(&this_addr, 0, addrlen ); | |
memset(&peer_addr, 0, addrlen ); | |
// Set input port | |
this_addr.sin_port = htons(port); | |
// Say that we want internet traffic | |
this_addr.sin_family = AF_INET; | |
// Accept connections to all IP addresses assigned to this machine | |
this_addr.sin_addr.s_addr = htonl(INADDR_ANY); | |
// Actually get us a socket that will listen for internet | |
// connections | |
socket_id = socket( AF_INET, SOCK_STREAM, IPPROTO_IP); | |
if (setsockopt(socket_id, SOL_SOCKET, SO_REUSEADDR, &(int){ 1 }, sizeof(int)) < 0) { | |
logmsg("setsockopt(SO_REUSEADDR) failed"); | |
exit(1); | |
} | |
// Set that socket up using the configuration we specified | |
if (bind(socket_id, (const struct sockaddr *) &this_addr, addrlen) != 0) { | |
logmsg("bind failed!"); | |
exit(1); | |
} | |
// Listen for connections on this socket | |
if (listen(socket_id, 5) != 0) { | |
logmsg("listen failed!"); | |
exit(1); | |
} | |
printf("There's a server running on port %d.\n", port); | |
// Loop forever while there is a connection | |
while((client = accept(socket_id, (struct sockaddr *) &peer_addr, | |
&addrlen)) != -1) { | |
printf("Got a connection on port %d, handling now.", port); | |
handle_connection(client); | |
close(client); | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment