Created
November 19, 2012 21:05
-
-
Save xeoncross/4113893 to your computer and use it in GitHub Desktop.
A web server in C language using only the standard libraries and thought it would be useful for you guys if I share the code. The server runs on Linux and includes features like handling HTTP GET request, handling content types(txt, html, jpg, zip. rar,
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
/* | |
* WebServer.c | |
* | |
* Created on: Nov 3, 2012 | |
* Author: pavithra | |
* | |
* A web server in C language using only the standard libraries. | |
* The port number is passed as an argument. | |
* | |
* http://css.dzone.com/articles/web-server-c | |
*/ | |
#include <stdio.h> | |
#include <unistd.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <sys/types.h> | |
#include <sys/socket.h> | |
#include <netinet/in.h> | |
#include <fcntl.h> | |
#include <errno.h> | |
#define EOL "\r\n" | |
#define EOL_SIZE 2 | |
typedef struct { | |
char *ext; | |
char *mediatype; | |
} extn; | |
//Possible media types | |
extn extensions[] ={ | |
{"gif", "image/gif" }, | |
{"txt", "text/plain" }, | |
{"jpg", "image/jpg" }, | |
{"jpeg","image/jpeg"}, | |
{"png", "image/png" }, | |
{"ico", "image/ico" }, | |
{"zip", "image/zip" }, | |
{"gz", "image/gz" }, | |
{"tar", "image/tar" }, | |
{"htm", "text/html" }, | |
{"html","text/html" }, | |
{"php", "text/html" }, | |
{"pdf","application/pdf"}, | |
{"zip","application/octet-stream"}, | |
{"rar","application/octet-stream"}, | |
{0,0} }; | |
/* | |
A helper function | |
*/ | |
void error(const char *msg) { | |
perror(msg); | |
exit(1); | |
} | |
/* | |
A helper function | |
*/ | |
int get_file_size(int fd) { | |
struct stat stat_struct; | |
if (fstat(fd, &stat_struct) == -1) | |
return (1); | |
return (int) stat_struct.st_size; | |
} | |
/* | |
A helper function | |
*/ | |
void send_new(int fd, char *msg) { | |
int len = strlen(msg); | |
if (send(fd, msg, len, 0) == -1) { | |
printf("Error in send\n"); | |
} | |
} | |
/* | |
This function recieves the buffer | |
until an "End of line(EOL)" byte is recieved | |
*/ | |
int recv_new(int fd, char *buffer) { | |
char *p = buffer; // Use of a pointer to the buffer rather than dealing with the buffer directly | |
int eol_matched = 0; // Use to check whether the recieved byte is matched with the buffer byte or not | |
while (recv(fd, p, 1, 0) != 0) // Start receiving 1 byte at a time | |
{ | |
if (*p == EOL[eol_matched]) // if the byte matches with the first eol byte that is '\r' | |
{ | |
++eol_matched; | |
if (eol_matched == EOL_SIZE) // if both the bytes matches with the EOL | |
{ | |
*(p + 1 - EOL_SIZE) = '\0'; // End the string | |
return (strlen(buffer)); // Return the bytes recieved | |
} | |
} else { | |
eol_matched = 0; | |
} | |
p++; // Increment the pointer to receive next byte | |
} | |
return (0); | |
} | |
/* | |
A helper function: Returns the | |
web root location. | |
*/ | |
char* webroot() { | |
// open the file "conf" for reading | |
FILE *in = fopen("conf", "rt"); | |
// read the first line from the file | |
char buff[1000]; | |
fgets(buff, 1000, in); | |
// close the stream | |
fclose(in); | |
char* nl_ptr = strrchr(buff, '\n'); | |
if (nl_ptr != NULL) | |
*nl_ptr = '\0'; | |
return strdup(buff); | |
} | |
/* | |
Handles php requests | |
*/ | |
void php_cgi(char* script_path, int fd) { | |
send_new(fd, "HTTP/1.1 200 OK\n Server: Web Server in C\n Connection: close\n"); | |
dup2(fd, STDOUT_FILENO); | |
char script[500]; | |
strcpy(script, "SCRIPT_FILENAME="); | |
strcat(script, script_path); | |
putenv("GATEWAY_INTERFACE=CGI/1.1"); | |
putenv(script); | |
putenv("QUERY_STRING="); | |
putenv("REQUEST_METHOD=GET"); | |
putenv("REDIRECT_STATUS=true"); | |
putenv("SERVER_PROTOCOL=HTTP/1.1"); | |
putenv("REMOTE_HOST=127.0.0.1"); | |
execl("/usr/bin/php-cgi", "php-cgi", NULL); | |
} | |
/* | |
This function parses the HTTP requests, | |
arrange resource locations, | |
check for supported media types, | |
serves files in a web root, | |
sends the HTTP error codes. | |
*/ | |
int connection(int fd) { | |
char request[500], resource[500], *ptr; | |
int fd1, length; | |
if (recv_new(fd, request) == 0) { | |
printf("Recieve Failed\n"); | |
} | |
printf("%s\n", request); | |
// Check for a valid browser request | |
ptr = strstr(request, " HTTP/"); | |
if (ptr == NULL) { | |
printf("NOT HTTP !\n"); | |
} else { | |
*ptr = 0; | |
ptr = NULL; | |
if (strncmp(request, "GET ", 4) == 0) { | |
ptr = request + 4; | |
} | |
if (ptr == NULL) { | |
printf("Unknown Request ! \n"); | |
} else { | |
if (ptr[strlen(ptr) - 1] == '/') { | |
strcat(ptr, "index.html"); | |
} | |
strcpy(resource, webroot()); | |
strcat(resource, ptr); | |
char* s = strchr(ptr, '.'); | |
int i; | |
for (i = 0; extensions[i].ext != NULL; i++) { | |
if (strcmp(s + 1, extensions[i].ext) == 0) { | |
fd1 = open(resource, O_RDONLY, 0); | |
printf("Opening \"%s\"\n", resource); | |
if (fd1 == -1) { | |
printf("404 File not found Error\n"); | |
send_new(fd, "HTTP/1.1 404 Not Found\r\n"); | |
send_new(fd, "Server : Web Server in C\r\n\r\n"); | |
send_new(fd, "<html><head><title>404 Not Found</head></title>"); | |
send_new(fd, "<body><p>404 Not Found: The requested resource could not be found!</p></body></html>"); | |
//Handling php requests | |
} else if (strcmp(extensions[i].ext, "php") == 0) { | |
php_cgi(resource, fd); | |
sleep(1); | |
close(fd); | |
exit(1); | |
} else { | |
printf("200 OK, Content-Type: %s\n\n", | |
extensions[i].mediatype); | |
send_new(fd, "HTTP/1.1 200 OK\r\n"); | |
send_new(fd, "Server : Web Server in C\r\n\r\n"); | |
if (ptr == request + 4) // if it is a GET request | |
{ | |
if ((length = get_file_size(fd1)) == -1) | |
printf("Error in getting size !\n"); | |
size_t total_bytes_sent = 0; | |
ssize_t bytes_sent; | |
while (total_bytes_sent < length) { | |
//Zero copy optimization | |
if ((bytes_sent = sendfile(fd, fd1, 0, | |
length - total_bytes_sent)) <= 0) { | |
if (errno == EINTR || errno == EAGAIN) { | |
continue; | |
} | |
perror("sendfile"); | |
return -1; | |
} | |
total_bytes_sent += bytes_sent; | |
} | |
} | |
} | |
break; | |
} | |
int size = sizeof(extensions) / sizeof(extensions[0]); | |
if (i == size - 2) { | |
printf("415 Unsupported Media Type\n"); | |
send_new(fd, "HTTP/1.1 415 Unsupported Media Type\r\n"); | |
send_new(fd, "Server : Web Server in C\r\n\r\n"); | |
send_new(fd, "<html><head><title>415 Unsupported Media Type</head></title>"); | |
send_new(fd, "<body><p>415 Unsupported Media Type!</p></body></html>"); | |
} | |
} | |
close(fd); | |
} | |
} | |
shutdown(fd, SHUT_RDWR); | |
} | |
int main(int argc, char *argv[]) { | |
int sockfd, newsockfd, portno, pid; | |
socklen_t clilen; | |
struct sockaddr_in serv_addr, cli_addr; | |
if (argc < 2) { | |
fprintf(stderr, "ERROR, no port provided\n"); | |
exit(1); | |
} | |
sockfd = socket(AF_INET, SOCK_STREAM, 0); | |
if (sockfd < 0) | |
error("ERROR opening socket"); | |
bzero((char *) &serv_addr, sizeof(serv_addr)); | |
portno = atoi(argv[1]); | |
serv_addr.sin_family = AF_INET; | |
serv_addr.sin_addr.s_addr = INADDR_ANY; | |
serv_addr.sin_port = htons(portno); | |
if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) | |
error("ERROR on binding"); | |
listen(sockfd, 5); | |
clilen = sizeof(cli_addr); | |
/* | |
Server runs forever, forking off a separate | |
process for each connection. | |
*/ | |
while (1) { | |
newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen); | |
if (newsockfd < 0) | |
error("ERROR on accept"); | |
pid = fork(); | |
if (pid < 0) | |
error("ERROR on fork"); | |
if (pid == 0) { | |
close(sockfd); | |
connection(newsockfd); | |
exit(0); | |
} else | |
close(newsockfd); | |
} /* end of while */ | |
close(sockfd); | |
return 0; /* we never get here */ | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Can you please explain your code? I really want to use C as my language for websites. Can you please teach me sir? I will oblige with any of your demands.. Thanks