/* Author: Hui Chen <usa.chen[at]gmail[dot]com>
*
* FleaHttpd: A fast Httpd as small as a flea
* Published under GNU Public License version 3
*
*/
#include <arpa/inet.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#define ERROR 1
#define MAX_MSG 1000
#define MSG_LEN_LIMIT 4000
#define PAGE_LEN_LIMIT 10000000
#define DEFAULT_PAGE_LEN_LIMIT 1000000
inline int readline (int, char *, size_t, int);
inline void print_usage ();
int
main (int argc, char *argv[])
{
int i, j, k, n;
int p[2];
FILE *ptr;
int pid;
int page_len = 0, default_page_len, page_404_len;
int content_length;
int use_default, use_404;
int sd, newSd, server_port = 80;
char server_port_c[20];
socklen_t cliLen;
struct sockaddr_in cliAddr, servAddr;
char *pi, *pos;
char filename[2560], script_name[2560], script_query[2560],
line[MAX_MSG + 1];
int script_query_len;
char *message = malloc (MSG_LEN_LIMIT);;
char *root_dir = NULL;
int support_cgi = 0;
int http_method; //0 GET, 1 POST, 2 PUT, 3 others
char *myenviron[100];
extern char **environ;
environ = myenviron;
if (argc == 1)
print_usage ();
else
{
if ((argc - 1) % 2)
print_usage ();
i = 1;
while (i < argc)
{
if (argv[i][0] != '-')
print_usage ();
switch (argv[i][1])
{
case 'p':
if ((i + 1) >= argc)
print_usage ();
if (!(k = atoi (argv[i + 1])))
print_usage ();
server_port = k;
break;
case 'r':
if ((i + 1) >= argc)
print_usage ();
root_dir = argv[i + 1];
break;
default:
print_usage ();
}
i += 2;
}
if (chdir (root_dir))
printf ("Can't change to directory %s!\n", root_dir);
}
setenv ("SERVER_SOFTWARE", "FleaHttpd/0.0.1", 1);
setenv ("GATEWAY_INTERFACE", "CGI/1.1", 1);
setenv ("SERVER_PROTOCOL", "HTTP/1.1", 1);
setenv ("SERVER_NAME", "localhost", 1);
sprintf (server_port_c, "%d", server_port);
setenv ("SERVER_PORT", server_port_c, 1);
int content_type = 0; //0 plain, 1 html, 2 jpeg, 3 png, 4 gif, 5 css, 6 bmp, 7 cgi
char header_0[] =
"HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nConnection: close\r\n\r\n";
char header_1[] =
"HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nConnection: close\r\n\r\n";
char header_2[] =
"HTTP/1.1 200 OK\r\nContent-Type: image/jpeg\r\nConnection: close\r\n\r\n";
char header_3[] =
"HTTP/1.1 200 OK\r\nContent-Type: image/png\r\nConnection: close\r\n\r\n";
char header_4[] =
"HTTP/1.1 200 OK\r\nContent-Type: image/gif\r\nConnection: close\r\n\r\n";
char header_5[] =
"HTTP/1.1 200 OK\r\nContent-Type: text/css\r\nConnection: close\r\n\r\n";
char header_6[] =
"HTTP/1.1 200 OK\r\nContent-Type: image/bmp\r\nConnection: close\r\n\r\n";
char header_7[] = "HTTP/1.1 200 OK\r\n";
char *header[] =
{ header_0, header_1, header_2, header_3, header_4, header_5, header_6,
header_7
};
char header_notfound[] =
"HTTP/1.1 404 Not Found\r\nContent-Type: text/html\r\n\r\n";
int header_len_404 = strlen (header_notfound);
int header_len[8];
for (i = 0; i < 8; i++)
header_len[i] = strlen (header[i]);
char *default_page = malloc (DEFAULT_PAGE_LEN_LIMIT);
char buildin_default_page[] = "<h2>FleaHttpd is online!</h2>";
char *page_404 = malloc (DEFAULT_PAGE_LEN_LIMIT);
char buildin_page_404[] = "<h2>404</h2>\nThe page cannot be found.";
char *page = malloc (PAGE_LEN_LIMIT);
FILE *fp, *fp1;
if (!(fp = fopen ("index.html", "r")))
{
free (default_page);
default_page = buildin_default_page;
default_page_len = strlen (buildin_default_page);
}
else
{
memcpy (default_page, header[1], header_len[1]);
default_page_len =
header_len[1] + fread (default_page + header_len[1], 1,
DEFAULT_PAGE_LEN_LIMIT, fp);
if (default_page_len == 0 || default_page_len >= DEFAULT_PAGE_LEN_LIMIT)
{
printf ("Size of index.html is illegal! sizeof(file) = %d\n",
default_page_len);
return ERROR;
}
fclose (fp);
}
if (!(fp = fopen ("404.html", "r")))
{
free (page_404);
page_404 = buildin_page_404;
page_404_len = strlen (buildin_page_404);
}
else
{
memcpy (page_404, header_notfound, header_len_404);
page_404_len =
header_len_404 + fread (page_404 + header_len_404, 1,
DEFAULT_PAGE_LEN_LIMIT, fp);
if (page_404_len == 0 || page_404_len >= DEFAULT_PAGE_LEN_LIMIT)
{
printf ("Size of 404.html is illegal! sizeof(file) = %d\n",
page_404_len);
return ERROR;
}
fclose (fp);
}
sd = socket (AF_INET, SOCK_STREAM, 0);
if (sd < 0)
{
printf ("Can't open socket!\n");
return ERROR;
}
servAddr.sin_family = AF_INET;
servAddr.sin_addr.s_addr = htonl (INADDR_ANY);
servAddr.sin_port = htons (server_port);
if (bind (sd, (struct sockaddr *) &servAddr, sizeof (servAddr)) < 0)
{
printf ("Can't bind port %d!\n", server_port);
return ERROR;
}
listen (sd, 1000);
while (1)
{
cliLen = sizeof (cliAddr);
newSd = accept (sd, (struct sockaddr *) &cliAddr, &cliLen);
if (newSd < 0)
{
continue;
}
pos = message;
content_length = 0;
*filename = 0;
use_default = 0;
use_404 = 1;
content_type = 0;
http_method = 3;
script_query[0] = 0;
script_query_len = 0;
while ((n = readline (newSd, line, MAX_MSG, 0)) > 0)
{
if (line[n - 2] == '\r')
{
n = n - 2;
line[n] = 0;
}
if (n == 0)
break;
switch (*line)
{
case 'G':
if (line[1] == 'E')
http_method = 0;
case 'P':
if (line[1] == 'O')
http_method = 1;
else if (line[1] == 'U')
http_method = 2;
for (pi = line + (http_method == 1 ? 5 : 4);
(*pi) == '.' && (*pi) == '/'; pi++);
pi++;
script_name[0] = '/';
for (i = 0; pi[i] != ' ' && pi[i] != '?'; i++)
{
filename[i] = pi[i];
script_name[i + 1] = pi[i];
}
if (!i)
{
use_default = 1;
break;
}
filename[i] = 0;
script_name[i + 1] = 0;
if (pi[i] == '?')
{
for (i += 1; pi[i] != ' '; i += 1)
{
script_query[script_query_len] = pi[i];
script_query_len++;
}
script_query[script_query_len] = 0;
}
for (j = i - 1;
j >= 0 && filename[j] != '/' && filename[j] != '.'; j--);
if (filename[j] == '.')
{
j++;
if (filename[j] == 'h' && filename[j + 1] == 't'
&& filename[j + 2] == 'm' && filename[j + 3] == 'l')
content_type = 1;
else if (filename[j] == 'H' && filename[j + 1] == 'T'
&& filename[j + 2] == 'M'
&& filename[j + 3] == 'L')
content_type = 1;
else if (filename[j] == 'j' && filename[j + 1] == 'p'
&& filename[j + 2] == 'g')
content_type = 2;
else if (filename[j] == 'J' && filename[j + 1] == 'P'
&& filename[j + 2] == 'G')
content_type = 2;
else if (filename[j] == 'j' && filename[j + 1] == 'p'
&& filename[j + 2] == 'e'
&& filename[j + 3] == 'g')
content_type = 2;
else if (filename[j] == 'J' && filename[j + 1] == 'P'
&& filename[j + 2] == 'E'
&& filename[j + 3] == 'G')
content_type = 2;
else if (filename[j] == 'p' && filename[j + 1] == 'n'
&& filename[j + 2] == 'g')
content_type = 3;
else if (filename[j] == 'P' && filename[j + 1] == 'N'
&& filename[j + 2] == 'G')
content_type = 3;
else if (filename[j] == 'g' && filename[j + 1] == 'i'
&& filename[j + 2] == 'f')
content_type = 4;
else if (filename[j] == 'G' && filename[j + 1] == 'I'
&& filename[j + 2] == 'F')
content_type = 4;
else if (filename[j] == 'c' && filename[j + 1] == 's'
&& filename[j + 2] == 's')
content_type = 5;
else if (filename[j] == 'C' && filename[j + 1] == 'S'
&& filename[j + 2] == 'S')
content_type = 5;
else if (filename[j] == 'b' && filename[j + 1] == 'm'
&& filename[j + 2] == 'p')
content_type = 6;
else if (filename[j] == 'B' && filename[j + 1] == 'M'
&& filename[j + 2] == 'P')
content_type = 6;
else if (filename[j] == 'c' && filename[j + 1] == 'g'
&& filename[j + 2] == 'i')
content_type = 7;
else if (filename[j] == 'C' && filename[j + 1] == 'G'
&& filename[j + 2] == 'I')
content_type = 7;
}
use_default = 0;
use_404 = 0;
break;
default:
break;
}
if (http_method == 1 && *line == 'C' && line[7] == '-'
&& line[8] == 'L')
content_length = atoi (line + 16);
}
if (http_method == 1 && content_length)
{
n = readline (newSd, line, MAX_MSG, 1);
if (n)
{
n--;
if (n != content_length)
{
close (newSd);
continue;
}
line[n] = 0;
if (script_query_len)
{
script_query[script_query_len] = '&';
script_query_len++;
}
for (i = 0; i < n; i += 1)
{
script_query[script_query_len] = line[i];
script_query_len++;
}
script_query[script_query_len] = 0;
}
}
if (support_cgi && script_query_len)
setenv ("QUERY_STRING", script_query, 1);
else
unsetenv ("QUERY_STRING");
if (!use_default && http_method < 3)
{
if (support_cgi && content_type == 7) //CGI
{
if (pipe (p) < 0 || !(fp1 = fopen (filename, "r")))
{
use_default = 0;
use_404 = 1;
}
else
{
fclose (fp1);
if ((pid = fork ()), pid == 0)
{
setenv ("REQUEST_METHOD", "GET", 1);
setenv ("SCRIPT_NAME", script_name, 1);
setenv ("REMOTE_ADDR", inet_ntoa (cliAddr.sin_addr), 1);
close (1);
dup (p[1]);
close (p[0]);
close (p[1]);
if (execl (filename, filename, NULL))
exit (0);
}
close (p[1]);
if (!(ptr = fdopen (p[0], "r")))
{
use_default = 0;
use_404 = 1;
}
else
{
memcpy (page, header[content_type],
header_len[content_type]);
page_len =
header_len[content_type] + fread (page +
header_len
[content_type], 1,
PAGE_LEN_LIMIT,
ptr);
fclose (ptr);
use_default = 0;
use_404 = 0;
}
}
}
else
{
if (!(fp = fopen (filename, "r")))
{
use_default = 0;
use_404 = 1;
}
else
{
memcpy (page, header[content_type],
header_len[content_type]);
page_len =
header_len[content_type] + fread (page +
header_len
[content_type], 1,
PAGE_LEN_LIMIT, fp);
if (page_len == 0 || page_len == PAGE_LEN_LIMIT)
{
use_default = 0;
use_404 = 1;
}
else
{
use_default = 0;
use_404 = 0;
fclose (fp);
}
}
}
}
if (use_default)
send (newSd, default_page, default_page_len, 0);
else if (use_404)
send (newSd, page_404, page_404_len, 0);
else
send (newSd, page, page_len, 0);
close (newSd);
}
free (message);
free (default_page);
free (page);
free (page_404);
close (sd);
return 0;
}
inline int
readline (int fd, char *bufptr, size_t len, int flush)
{
char *bufx = bufptr;
static char *bp;
static int cnt = 0;
static char b[MSG_LEN_LIMIT];
char c;
int r_l;
if (flush && cnt > 0)
{
r_l = cnt > len ? len : cnt;
memcpy (bufptr, bp, r_l);
cnt -= r_l;
return r_l;
}
while (--len > 0)
{
if (--cnt <= 0)
{
cnt = recv (fd, b, sizeof (b), 0);
if (cnt < 0)
return -1;
if (cnt == 0)
return 0;
bp = b;
}
c = *bp++;
*bufptr++ = c;
if (c == '\n')
{
*bufptr = '\0';
return bufptr - bufx;
}
}
return -1;
}
inline void
print_usage ()
{
printf
("Usage: \n\tfleahttpd -p port -r wwwroot\nExample: \n\tfleahttpd -p 80 -r /var/www/fleahttpd\n");
exit (0);
}
linux c socket之开源http服务器fleahttpd
最新推荐文章于 2021-05-15 16:52:24 发布