Compare commits

..

2 Commits

View File

@ -1,306 +1,382 @@
//====================================================== file = weblite.c ===== //====================================================== file = weblite.c =====
//= A super light weight HTTP server = //= A super light weight HTTP server =
//============================================================================= //=============================================================================
//= Notes: = //= Notes: =
//= 1) Compiles for Winsock only since uses Windows threads. Generates = //= 1) Compiles for Winsock only since uses Windows threads. Generates =
//= one warning about unreachable code in main. Ignore this warning. = //= one warning about unreachable code in main. Ignore this warning. =
//= 2) Serves HTML and GIF only. = //= 2) Serves HTML and GIF only. =
//= 3) Sometimes the browser drops a connection when doing a refresh. = //= 3) Sometimes the browser drops a connection when doing a refresh. =
//= This is handled by checking the recv() return code in the function = //= This is handled by checking the recv() return code in the function =
//= that handles GETs. = //= that handles GETs. =
//= 4) The 404 HTML message does not always display in Explorer. = //= 4) The 404 HTML message does not always display in Explorer. =
//=---------------------------------------------------------------------------= //=---------------------------------------------------------------------------=
//= Execution notes: = //= Execution notes: =
//= 1) Execute this program in the directory which will be the root for = //= 1) Execute this program in the directory which will be the root for =
//= all file references (i.e., the directory that is considered at = //= all file references (i.e., the directory that is considered at =
//= "public.html"). = //= "public.html"). =
//= 2) Open a Web browser and surf to http://xxx.xxx.xxx.xxx/yyy where = //= 2) Open a Web browser and surf to http://xxx.xxx.xxx.xxx/yyy where =
//= xxx.xxx.xxx.xxx is the IP address or hostname of the machine that = //= xxx.xxx.xxx.xxx is the IP address or hostname of the machine that =
//= weblite is executing on and yyy is the requested object. = //= weblite is executing on and yyy is the requested object. =
//= 3) The only output (to stdout) from weblite is a message with the = //= 3) The only output (to stdout) from weblite is a message with the =
//= of the file currently being sent = //= of the file currently being sent =
//=---------------------------------------------------------------------------= //=---------------------------------------------------------------------------=
//= Build: bcc32 weblite.c, cl weblite.c wsock32.lib (or ws2_32.lib) = //= Build: bcc32 weblite.c, cl weblite.c wsock32.lib (or ws2_32.lib) =
//= gcc weblite.c -lsocket -lnsl for BSD = //= gcc weblite.c -lsocket -lnsl for BSD =
//=---------------------------------------------------------------------------= //=---------------------------------------------------------------------------=
//= Execute: weblite = //= Execute: weblite =
//=---------------------------------------------------------------------------= //=---------------------------------------------------------------------------=
//= History: KJC (12/29/00) - Genesis (from server.c) = //= History: KJC (12/29/00) - Genesis (from server.c) =
//= HF (01/25/01) - Ported to multi-platform environment = //= HF (01/25/01) - Ported to multi-platform environment =
//============================================================================= //=============================================================================
#define UNIX // WIN for Windows environment, UNIX for BSD or LINUX env. #define WIN // WIN for Windows environment, UNIX for BSD or LINUX env.
//----- Include files --------------------------------------------------------- //----- Include files ---------------------------------------------------------
#include <stdio.h> // Needed for printf() #include <stdio.h> // Needed for printf()
#include <stdlib.h> // Needed for exit() #include <stdlib.h> // Needed for exit()
#include <string.h> // Needed for strcpy() and strlen() #include <string.h> // Needed for strcpy() and strlen()
#include <fcntl.h> // Needed for file i/o constants #include <fcntl.h> // Needed for file i/o constants
#include <sys/stat.h> // Needed for file i/o constants #include <sys/stat.h> // Needed for file i/o constants
#include <future>
/* FOR BSD UNIX/LINUX ---------------------------------------------------- */
#ifdef UNIX /* FOR BSD UNIX/LINUX ---------------------------------------------------- */
#include <sys/types.h> // #ifdef UNIX
#include <netinet/in.h> // #include <sys/types.h> //
#include <sys/socket.h> // #include <netinet/in.h> //
#include <arpa/inet.h> // #include <sys/socket.h> //
#include <unistd.h> #include <arpa/inet.h> //
#endif #include <unistd.h>
/* ------------------------------------------------------------------------ */ #endif
/* ------------------------------------------------------------------------ */
/* FOR WIN ---------------------------------------------------------------- */
#ifdef WIN /* FOR WIN ---------------------------------------------------------------- */
#include <stddef.h> // Needed for _threadid #ifdef WIN
#include <process.h> // Needed for _beginthread() and _endthread() #include <stddef.h> // Needed for _threadid
#include <io.h> // Needed for open(), close(), and eof() #include <process.h> // Needed for _beginthread() and _endthread()
#include <windows.h> // Needed for all Winsock stuff #include <io.h> // Needed for open(), close(), and eof()
#endif #include <winsock2.h>
/* ------------------------------------------------------------------------ */ #include <windows.h> // Needed for all Winsock stuff
// Lazy struct fixes
//----- HTTP response messages ---------------------------------------------- typedef int32_t socklen_t;
#define OK_IMAGE "HTTP/1.0 200 OK\nContent-Type:image/gif\n\n" #endif
#define OK_TEXT "HTTP/1.0 200 OK\nContent-Type:text/html\n\n" /* ------------------------------------------------------------------------ */
#define NOTOK_404 "HTTP/1.0 404 Not Found\nContent-Type:text/html\n\n"
#define MESS_404 "<html><body><h1>FILE NOT FOUND</h1></body></html>"
//----- HTTP response messages ----------------------------------------------
//----- Defines ------------------------------------------------------------- #define OK_IMAGE "HTTP/1.0 200 OK\nContent-Type:image/gif\n\n"
#define BUF_SIZE 4096 // Buffer size (big enough for a GET) #define OK_TEXT "HTTP/1.0 200 OK\nContent-Type:text/html\n\n"
#define PORT_NUM 7080 // Port number for a Web server #define NOTOK_404 "HTTP/1.0 404 Not Found\nContent-Type:text/html\n\n"
#define MESS_404 "<html><body><h1>FILE NOT FOUND</h1></body></html>"
//----- Function prototypes -------------------------------------------------
/* FOR WIN --------------------------------------------------------------- */ //----- Defines -------------------------------------------------------------
#ifdef WIN #define BUF_SIZE 4096 // Buffer size (big enough for a GET)
void handle_get(void *in_arg); // Thread function to handle GET #define PORT_NUM 7080 // Port number for a Web server
#endif
/* ----------------------------------------------------------------------- */ //----- Function prototypes -------------------------------------------------
void ClientRequest(int server_s, int client_s);
/* FOR UNIX/LINUX -------------------------------------------------------- */ /* FOR WIN --------------------------------------------------------------- */
#ifdef UNIX #ifdef WIN
void child_proc(int server_s, int client_s); // Fork function for GET void handle_get(void *in_arg); // Thread function to handle GET
#endif #endif
/* ----------------------------------------------------------------------- */ /* ----------------------------------------------------------------------- */
//===== modeule main ======================================================== /* FOR UNIX/LINUX -------------------------------------------------------- */
int main(void) #ifdef UNIX
{ void child_proc(int server_s, int client_s); // Fork function for GET
/* FOR WIN ------------------------------------------------------------- */ #endif
#ifdef WIN /* ----------------------------------------------------------------------- */
WORD wVersionRequested = MAKEWORD(1, 1); // Stuff for WSA functions
WSADATA wsaData; // Stuff for WSA functions //===== modeule main ========================================================
#endif int main(void)
/* --------------------------------------------------------------------- */ {
/* FOR WIN ------------------------------------------------------------- */
unsigned int server_s; // Server socket descriptor #ifdef WIN
struct sockaddr_in server_addr; // Server Internet address WORD wVersionRequested = MAKEWORD(1, 1); // Stuff for WSA functions
unsigned int client_s; // Client socket descriptor WSADATA wsaData; // Stuff for WSA functions
struct sockaddr_in client_addr; // Client Internet address #endif
struct in_addr client_ip_addr; // Client IP address /* --------------------------------------------------------------------- */
socklen_t addr_len; // Internet address length
unsigned int server_s; // Server socket descriptor
/* FOR UNIX/LINUX ------------------------------------------------------ */ struct sockaddr_in server_addr; // Server Internet address
#ifdef UNIX unsigned int client_s; // Client socket descriptor
int nChild_proc_id; // New child process ID. struct sockaddr_in client_addr; // Client Internet address
#endif struct in_addr client_ip_addr; // Client IP address
/* --------------------------------------------------------------------- */ socklen_t addr_len; // Internet address length
/* FOR WIN ------------------------------------------------------------- */ /* FOR UNIX/LINUX ------------------------------------------------------ */
#ifdef WIN #ifdef UNIX
// Initialize winsock int nChild_proc_id; // New child process ID.
WSAStartup(wVersionRequested, &wsaData); #endif
#endif /* --------------------------------------------------------------------- */
/* --------------------------------------------------------------------- */
/* FOR WIN ------------------------------------------------------------- */
// Create a socket, fill-in address information, and then bind it #ifdef WIN
server_s = socket(AF_INET, SOCK_STREAM, 0); // Initialize winsock
server_addr.sin_family = AF_INET; WSAStartup(wVersionRequested, &wsaData);
server_addr.sin_port = htons(PORT_NUM); #endif
server_addr.sin_addr.s_addr = htonl(INADDR_ANY); /* --------------------------------------------------------------------- */
bind(server_s, (struct sockaddr *)&server_addr, sizeof(server_addr));
// Create a socket, fill-in address information, and then bind it
// Main loop to listen, accept, and then spin-off a thread to handle the GET server_s = socket(AF_INET, SOCK_STREAM, 0);
while (1) if (server_s == -1)
{ {
// Listen for connections and then accept printf("ERROR - Unable to create socket on server\n");
listen(server_s, 100); exit(1);
addr_len = sizeof(client_addr); }
server_addr.sin_family = AF_INET;
client_s = accept(server_s, (struct sockaddr *)&client_addr, &addr_len); server_addr.sin_port = htons(PORT_NUM);
if (client_s == 0) server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
{ int bindRet = bind(server_s, (struct sockaddr *)&server_addr, sizeof(server_addr));
printf("ERROR - Unable to create socket \n"); if (bindRet == -1)
exit(1); {
} printf("ERROR - Unable to bind socket\n");
exit(1);
/* FOR UNIX/LINUX ---------------------------------------------------- */ }
#ifdef UNIX // Listen for connections and then accept
// Spin-off a child process by fork listen(server_s, 100);
nChild_proc_id = fork();
// Main loop to listen, accept, and then spin-off a thread to handle the GET
// Separate the parent and child process here ... while (1)
if (nChild_proc_id == -1) // if I am a new child, go to the child module. {
{ addr_len = sizeof(client_addr);
child_proc(server_s, client_s);
} client_s = accept(server_s, (struct sockaddr *)&client_addr, &addr_len);
#endif if (client_s == -1)
/* ------------------------------------------------------------------- */ {
perror("ERROR - Unable to create socket to client\n");
/* FOR WIN ----------------------------------------------------------- */ continue;
#ifdef WIN }
// Spin-off a thread to handle this request (pass only client_s)
if (_beginthread(handle_get, 4096, (void *)client_s) < 0) std::async(std::launch::async, ClientRequest, server_s, client_s);
{
printf("ERROR - Unable to create thread \n"); /* FOR UNIX/LINUX ---------------------------------------------------- */
exit(1); #ifdef UNIX
} /*
#endif // Spin-off a child process by fork
/* ------------------------------------------------------------------- */ nChild_proc_id = fork();
}
// Separate the parent and child process here ...
/* FOR UNIX/LINUX ------------------------------------------------------ */ if (nChild_proc_id == -1) // if I am a new child, go to the child module.
#ifdef UNIX {
// Close the server socket child_proc(server_s, client_s);
close(server_s); }
#endif */
/* --------------------------------------------------------------------- */ #endif
/* ------------------------------------------------------------------- */
/* FOR WIN ------------------------------------------------------------- */
#ifdef WIN /* FOR WIN ----------------------------------------------------------- */
// Close the server socket and clean-up winsock #ifdef WIN
printf("this web server is shutting down .....\a\n"); /*
// Spin-off a thread to handle this request (pass only client_s)
closesocket(server_s); if (_beginthread(handle_get, 4096, (void *)client_s) < 0)
WSACleanup(); {
#endif printf("ERROR - Unable to create thread \n");
/* --------------------------------------------------------------------- */ exit(1);
}
// To make sure this "main" returns an integer. */
return (1); #endif
} /* ------------------------------------------------------------------- */
}
/* FOR WIN --------------------------------------------------------------- */ close(server_s);
#ifdef WIN /* FOR UNIX/LINUX ------------------------------------------------------ */
//=========================================================================== #ifdef UNIX
//= This is is the thread function to handle the GET = // Close the server socket
//= - It is assumed that the request is a GET = //close(server_s);
//=========================================================================== #endif
void handle_get(void *in_arg) /* --------------------------------------------------------------------- */
{
unsigned int client_s; // Client socket descriptor /* FOR WIN ------------------------------------------------------------- */
char in_buf[BUF_SIZE]; // Input buffer for GET resquest #ifdef WIN
char out_buf[BUF_SIZE]; // Output buffer for HTML response // Close the server socket and clean-up winsock
char *file_name; // File name printf("this web server is shutting down .....\a\n");
unsigned int fh; // File handle
unsigned int buf_len; // Buffer length for file reads closesocket(server_s);
unsigned int retcode; // Return code WSACleanup();
#endif
// Set client_s to in_arg /* --------------------------------------------------------------------- */
client_s = (unsigned int)in_arg;
// To make sure this "main" returns an integer.
// Receive the GET request from the Web browser return (1);
retcode = recv(client_s, in_buf, BUF_SIZE, 0); }
// Handle the GET if there is one (see note #3 in the header) void ClientRequest(int server_s, int client_s) {
if (retcode != -1) char in_buf[BUF_SIZE]; // Input buffer for GET resquest
{ char out_buf[BUF_SIZE]; // Output buffer for HTML response
// Parse out the filename from the GET request char *file_name; // File name
strtok(in_buf, " "); unsigned int fh; // File handle
file_name = strtok(NULL, " "); unsigned int buf_len; // Buffer length for file reads
unsigned int retcode; // Return code
// Open the requested file
// - Start at 2nd char to get rid of leading "\" // Receive the GET request from the Web browser
fh = open(&file_name[1], O_RDONLY | O_BINARY, S_IREAD | S_IWRITE); retcode = recv(client_s, in_buf, BUF_SIZE, 0);
// Generate and send the response (404 if could not open the file) // Handle the GET if there is one (see note #3 in the header)
if (fh == -1) if (retcode != -1)
{ {
printf("File %s not found - sending an HTTP 404 \n", &file_name[1]); // Parse out the filename from the GET request
strcpy(out_buf, NOTOK_404); strtok(in_buf, " ");
send(client_s, out_buf, strlen(out_buf), 0); file_name = strtok(NULL, " ");
strcpy(out_buf, MESS_404);
send(client_s, out_buf, strlen(out_buf), 0); // Open the requested file
} // - Start at 2nd char to get rid of leading "\"
else #ifdef WIN
{ fh = open(&file_name[1], O_RDONLY | O_BINARY, S_IREAD | S_IWRITE);
printf("File %s is being sent \n", &file_name[1]); #endif
if (strstr(file_name, ".gif") != NULL) #ifdef UNIX
strcpy(out_buf, OK_IMAGE); fh = open(&file_name[1], O_RDONLY, S_IREAD | S_IWRITE);
else #endif
strcpy(out_buf, OK_TEXT);
send(client_s, out_buf, strlen(out_buf), 0); // Generate and send the response (404 if could not open the file)
while (!eof(fh)) if (fh == -1)
{ {
buf_len = read(fh, out_buf, BUF_SIZE); printf("File %s not found - sending an HTTP 404 \n", &file_name[1]);
send(client_s, out_buf, buf_len, 0); strcpy(out_buf, NOTOK_404);
} send(client_s, out_buf, strlen(out_buf), 0);
close(fh); strcpy(out_buf, MESS_404);
} send(client_s, out_buf, strlen(out_buf), 0);
} }
else
// Close the client socket and end the thread {
closesocket(client_s); printf("File %s is being sent \n", &file_name[1]);
_endthread(); if (strstr(file_name, ".gif") != NULL)
} strcpy(out_buf, OK_IMAGE);
#endif else
/* ----------------------------------------------------------------------- */ strcpy(out_buf, OK_TEXT);
send(client_s, out_buf, strlen(out_buf), 0);
/* FOR UNIX/LINUX -------------------------------------------------------- */ do {
#ifdef UNIX buf_len = read(fh, out_buf, BUF_SIZE);
void child_proc(int server_s, int client_s) send(client_s, out_buf, buf_len, 0);
{ } while (buf_len != 0);
char in_buf[BUF_SIZE]; // Input buffer for GET resquest close(fh);
char out_buf[BUF_SIZE]; // Output buffer for HTML response }
char *file_name; // File name }
unsigned int fh; // File handle // Close the client socket and end the thread
unsigned int buf_len; // Buffer length for file reads close(client_s);
ssize_t retcode; // Return code }
// Shut down the parent pipe /* FOR WIN --------------------------------------------------------------- */
close(server_s); #ifdef WIN
//===========================================================================
// Receive the GET request from the Web browser //= This is is the thread function to handle the GET =
retcode = recv(client_s, in_buf, BUF_SIZE, 0); //= - It is assumed that the request is a GET =
//===========================================================================
// Handle the GET if there is one (see note #3 in the header) void handle_get(void *in_arg)
if (retcode != -1) {
{ unsigned int client_s; // Client socket descriptor
// Parse out the filename from the GET request char in_buf[BUF_SIZE]; // Input buffer for GET resquest
strtok(in_buf, " "); char out_buf[BUF_SIZE]; // Output buffer for HTML response
file_name = strtok(NULL, " "); char *file_name; // File name
unsigned int fh; // File handle
// Open the requested file unsigned int buf_len; // Buffer length for file reads
// - Start at 2nd char to get rid of leading "\" unsigned int retcode; // Return code
// fh = open(&file_name[1], O_RDONLY | O_BINARY, S_IREAD | S_IWRITE);
fh = open(&file_name[1], O_RDONLY, S_IREAD | S_IWRITE); // Set client_s to in_arg
client_s = (unsigned int&)in_arg;
// Generate and send the response (404 if could not open the file)
if (fh == -1) // Receive the GET request from the Web browser
{ retcode = recv(client_s, in_buf, BUF_SIZE, 0);
printf("File %s not found - sending an HTTP 404 \n", &file_name[1]);
strcpy(out_buf, NOTOK_404); // Handle the GET if there is one (see note #3 in the header)
send(client_s, out_buf, strlen(out_buf), 0); if (retcode != -1)
strcpy(out_buf, MESS_404); {
send(client_s, out_buf, strlen(out_buf), 0); // Parse out the filename from the GET request
} strtok(in_buf, " ");
else file_name = strtok(NULL, " ");
{
printf("File %s is being sent \n", &file_name[1]); // Open the requested file
if (strstr(file_name, ".gif") != NULL) // - Start at 2nd char to get rid of leading "\"
strcpy(out_buf, OK_IMAGE); fh = open(&file_name[1], O_RDONLY | O_BINARY, S_IREAD | S_IWRITE);
else
strcpy(out_buf, OK_TEXT); // Generate and send the response (404 if could not open the file)
send(client_s, out_buf, strlen(out_buf), 0); if (fh == -1)
{
while (fh != EOF) printf("File %s not found - sending an HTTP 404 \n", &file_name[1]);
{ strcpy(out_buf, NOTOK_404);
buf_len = read(fh, out_buf, BUF_SIZE); send(client_s, out_buf, strlen(out_buf), 0);
send(client_s, out_buf, buf_len, 0); strcpy(out_buf, MESS_404);
} send(client_s, out_buf, strlen(out_buf), 0);
close(fh); }
} else
} {
printf("File %s is being sent \n", &file_name[1]);
// Shut down my (the child) pipe if (strstr(file_name, ".gif") != NULL)
close(client_s); strcpy(out_buf, OK_IMAGE);
exit(1); else
} strcpy(out_buf, OK_TEXT);
#endif send(client_s, out_buf, strlen(out_buf), 0);
/* ----------------------------------------------------------------------- */ while (!eof(fh))
{
buf_len = read(fh, out_buf, BUF_SIZE);
send(client_s, out_buf, buf_len, 0);
}
close(fh);
}
}
// Close the client socket and end the thread
closesocket(client_s);
_endthread();
}
#endif
/* ----------------------------------------------------------------------- */
/* FOR UNIX/LINUX -------------------------------------------------------- */
#ifdef UNIX
void child_proc(int server_s, int client_s)
{
char in_buf[BUF_SIZE]; // Input buffer for GET resquest
char out_buf[BUF_SIZE]; // Output buffer for HTML response
char *file_name; // File name
unsigned int fh; // File handle
unsigned int buf_len; // Buffer length for file reads
ssize_t retcode; // Return code
// Shut down the parent pipe
close(server_s);
// Receive the GET request from the Web browser
retcode = recv(client_s, in_buf, BUF_SIZE, 0);
// Handle the GET if there is one (see note #3 in the header)
if (retcode != -1)
{
// Parse out the filename from the GET request
strtok(in_buf, " ");
file_name = strtok(NULL, " ");
// Open the requested file
// - Start at 2nd char to get rid of leading "\"
// fh = open(&file_name[1], O_RDONLY | O_BINARY, S_IREAD | S_IWRITE);
fh = open(&file_name[1], O_RDONLY, S_IREAD | S_IWRITE);
// Generate and send the response (404 if could not open the file)
if (fh == -1)
{
printf("File %s not found - sending an HTTP 404 \n", &file_name[1]);
strcpy(out_buf, NOTOK_404);
send(client_s, out_buf, strlen(out_buf), 0);
strcpy(out_buf, MESS_404);
send(client_s, out_buf, strlen(out_buf), 0);
}
else
{
printf("File %s is being sent \n", &file_name[1]);
if (strstr(file_name, ".gif") != NULL)
strcpy(out_buf, OK_IMAGE);
else
strcpy(out_buf, OK_TEXT);
send(client_s, out_buf, strlen(out_buf), 0);
do {
buf_len = read(fh, out_buf, BUF_SIZE);
send(client_s, out_buf, buf_len, 0);
} while (buf_len != 0);
close(fh);
}
}
// Shut down my (the child) pipe
close(client_s);
exit(1);
}
#endif
/* ----------------------------------------------------------------------- */