linux c socket之开源http服务器fleahttpd

161 篇文章 9 订阅
/* 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);
}

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要在 Linux 上使用 C 语言编写 HTTP 服务器,您可以使用 Sockets API 和 HTTP 解析器库。以下是一个简单的示例程序,它使用 Sockets API 创建一个 TCP 服务器并侦听来自客户端的请求,然后使用 HTTP 解析器库解析请求并发送响应。 ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <arpa/inet.h> #include <netinet/in.h> #include <http_parser.h> static int on_message_complete(http_parser *parser) { int fd = *((int *) parser->data); const char *response = "HTTP/1.1 200 OK\r\nContent-Length: 13\r\n\r\nHello, World!"; write(fd, response, strlen(response)); close(fd); return 0; } int main() { int server_fd, client_fd; struct sockaddr_in server_addr, client_addr; socklen_t client_len = sizeof(client_addr); char buffer[1024]; http_parser_settings parser_settings; http_parser parser; http_parser_init(&parser, HTTP_REQUEST); parser_settings.on_message_complete = on_message_complete; parser.data = &client_fd; server_fd = socket(AF_INET, SOCK_STREAM, 0); if (server_fd < 0) { perror("socket"); exit(1); } memset(&server_addr, 0, sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_addr.s_addr = htonl(INADDR_ANY); server_addr.sin_port = htons(8080); if (bind(server_fd, (struct sockaddr *) &server_addr, sizeof(server_addr)) < 0) { perror("bind"); exit(1); } if (listen(server_fd, 10) < 0) { perror("listen"); exit(1); } while (1) { client_fd = accept(server_fd, (struct sockaddr *) &client_addr, &client_len); if (client_fd < 0) { perror("accept"); continue; } http_parser_execute(&parser, &parser_settings, buffer, read(client_fd, buffer, 1024)); } return 0; } ``` 这个程序利用 Sockets API 创建了一个 TCP 服务器,并绑定到 8080 端口。然后它进入一个无限循环,等待客户端连接。每当有一个客户端连接时,它使用 HTTP 解析器库解析请求,并使用 `on_message_complete` 函数发送响应。在本例中,响应是一个简单的 "Hello, World!" 字符串。 请注意,这只是一个简单的示例程序。在实际应用中,您需要添加错误处理、并发支持和更完整的 HTTP 功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值