linux c http cgi,实现简单HTTP服务器-图片与CGI

#include "csapp.h"

void doit(int fd);

void read_requesthdrs(rio_t *rp);

int parse_uri(char *uri, char *filename, char *cgiargs);

void serve_static(int fd, char *filename, int filesize);

void get_filetype(char *filename, char *filetype);

void serve_dynamic(int fd, char *filename, char *cgiargs);

void clienterror(int fd, char *cause, char *errnum,

char *shortmsg, char *longmsg);

int main(int argc, char **argv)

{

int listenfd, connfd, port, clientlen;

struct sockaddr_in clientaddr;

/* 检查命令行输入 */

if (argc != 2) {

fprintf(stderr, "usage: %s \n", argv[0]);

exit(1);

}

port = atoi(argv[1]); //获取服务器端口号

//循环监听客户端请求

listenfd = Open_listenfd(port);

while (1) {

clientlen = sizeof(clientaddr);

connfd = Accept(listenfd, (SA *)&clientaddr, &clientlen); //接收客户端的请求

doit(connfd); //执行服务

Close(connfd); //关闭与客户端的连接

}

}

/* $end tinymain */

/* * doit - 处理一个Http请求 */

/* $begin doit */

void doit(int fd)

{

int is_static; //是否为静态请求

struct stat sbuf;

char buf[MAXLINE],

method[MAXLINE], //请求方法

uri[MAXLINE], //uri路径

version[MAXLINE]; //MAXLINE:8192

char filename[MAXLINE], //文件名

cgiargs[MAXLINE]; //cgi程序的参数

rio_t rio;

//读取客户的请求 以及HTTP头文件

//Rio_readinitb是封装过的IO读取函数

Rio_readinitb(&rio, fd);

Rio_readlineb(&rio, buf, MAXLINE); //读取请求

sscanf(buf, "%s %s %s", method, uri, version); //解析请求

if (strcasecmp(method, "GET")) { //只能提供GET

clienterror(fd, method, "501", "Not Implemented",

"Tiny does not implement this method");

return;

}

read_requesthdrs(&rio); //读取请求头

//从浏览器的输入栏中解析GET请求

is_static = parse_uri(uri, filename, cgiargs); //静态图片 / 动态加法请求检查

if (stat(filename, &sbuf) < 0) { //没有用户所请求的文件

clienterror(fd, filename, "404", "Not found",

"Tiny couldn't find this file");

return;

} //line:netp:doit:endnotfound

if (is_static) { /* 静态请求,返回图片*/

if (!(S_ISREG(sbuf.st_mode)) || !(S_IRUSR & sbuf.st_mode)) { //line:netp:doit:readable

clienterror(fd, filename, "403", "Forbidden",

"Tiny couldn't read the file");

return;

}

serve_static(fd, filename, sbuf.st_size); //将图片发送给客户端

}

else { /* 调用CGI程序执行加法运算*/

if (!(S_ISREG(sbuf.st_mode)) || !(S_IXUSR & sbuf.st_mode)) {

clienterror(fd, filename, "403", "Forbidden",

"Tiny couldn't run the CGI program");

return;

}

serve_dynamic(fd, filename, cgiargs); //动态服务,传入参数给cgi程序

}

}

/* $end doit */

/* * read_requesthdrs - read and parse HTTP request headers */

/* $begin read_requesthdrs */

void read_requesthdrs(rio_t *rp)

{

char buf[MAXLINE];

Rio_readlineb(rp, buf, MAXLINE);

while(strcmp(buf, "\r\n")) { //HTTP Header 规定以\r\n结尾

Rio_readlineb(rp, buf, MAXLINE);

printf("%s", buf);

}

return;

}

/* $end read_requesthdrs */

/* * * 解析浏览器发来的URI中的文件名 或者 是 CGI参数(加法的两个数) * 返回值:0为动态CGI,1为静态图片 */

/* $begin parse_uri */

int parse_uri(char *uri, char *filename, char *cgiargs)

{

char *ptr;

if (!strstr(uri, "cgi-bin")) { /* Static content */ //line:netp:parseuri:isstatic

strcpy(cgiargs, ""); //line:netp:parseuri:clearcgi

strcpy(filename, "."); //line:netp:parseuri:beginconvert1

strcat(filename, uri); //line:netp:parseuri:endconvert1

if (uri[strlen(uri)-1] == '/') //line:netp:parseuri:slashcheck

strcat(filename, "home.html"); //line:netp:parseuri:appenddefault

return 1;

}

else { /* Dynamic content */ //line:netp:parseuri:isdynamic

ptr = index(uri, '?'); //line:netp:parseuri:beginextract

if (ptr) {

strcpy(cgiargs, ptr+1);

*ptr = '\0';

}

else

strcpy(cgiargs, ""); //line:netp:parseuri:endextract

strcpy(filename, "."); //line:netp:parseuri:beginconvert2

strcat(filename, uri); //line:netp:parseuri:endconvert2

return 0;

}

}

/* $end parse_uri */

/* * * 静态服务 : 拷贝图片返回给客户 */

/* $begin serve_static */

void serve_static(int fd, char *filename, int filesize)

{

int srcfd;

char *srcp, filetype[MAXLINE], buf[MAXBUF];

/* 发送服务器响应Header给客户端 */

get_filetype(filename, filetype); //line:netp:servestatic:getfiletype

sprintf(buf, "HTTP/1.0 200 OK\r\n"); //line:netp:servestatic:beginserve

sprintf(buf, "%sServer: Tiny Web Server\r\n", buf);

sprintf(buf, "%sContent-length: %d\r\n", buf, filesize);

sprintf(buf, "%sContent-type: %s\r\n\r\n", buf, filetype);

Rio_writen(fd, buf, strlen(buf)); //line:netp:servestatic:endserve

/* 通过内存映射,发送文件 */

srcfd = Open(filename, O_RDONLY, 0); //line:netp:servestatic:open

srcp = Mmap(0, filesize, PROT_READ, MAP_PRIVATE, srcfd, 0);//line:netp:servestatic:mmap

Close(srcfd); //line:netp:servestatic:close

Rio_writen(fd, srcp, filesize); //line:netp:servestatic:write

Munmap(srcp, filesize); //line:netp:servestatic:munmap

}

/* * * 获取文件类型 */

void get_filetype(char *filename, char *filetype)

{

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/jpeg");

else

strcpy(filetype, "text/plain");

}

/* $end serve_static */

/* * * 动态服务:执行CGI程序,将结果返回给客户端(浏览器显示加法结果) */

/* $begin serve_dynamic */

void serve_dynamic(int fd, char *filename, char *cgiargs)

{

char buf[MAXLINE], *emptylist[] = { NULL };

/* Return first part of HTTP response */

sprintf(buf, "HTTP/1.0 200 OK\r\n");

Rio_writen(fd, buf, strlen(buf));

sprintf(buf, "Server: Tiny Web Server\r\n");

Rio_writen(fd, buf, strlen(buf));

//创建子进程

if (Fork() == 0) { /* child */ //line:netp:servedynamic:fork

/* Real server would set all CGI vars here */

//发送环境变量

setenv("QUERY_STRING", cgiargs, 1);

//重定向stdout

Dup2(fd, STDOUT_FILENO);

//执行服务器上的CGI程序

Execve(filename, emptylist, environ);

}

Wait(NULL); // 父进程等待子进程结束并回收

}

/* $end serve_dynamic */

/* * * 返回请求错误消息给客户端 */

/* $begin clienterror */

void clienterror(int fd, char *cause, char *errnum,

char *shortmsg, char *longmsg)

{

char buf[MAXLINE], body[MAXBUF];

/* Build the HTTP response body */

sprintf(body, "

Tiny Error");

sprintf(body, "%s

\r\n", body);

sprintf(body, "%s%s: %s\r\n", body, errnum, shortmsg);

sprintf(body, "%s

%s: %s\r\n", body, longmsg, cause);

sprintf(body, "%s


The Tiny Web server\r\n", body);

/* Print the HTTP response */

sprintf(buf, "HTTP/1.0 %s %s\r\n", errnum, shortmsg);

Rio_writen(fd, buf, strlen(buf));

sprintf(buf, "Content-type: text/html\r\n");

Rio_writen(fd, buf, strlen(buf));

sprintf(buf, "Content-length: %d\r\n\r\n", (int)strlen(body));

Rio_writen(fd, buf, strlen(buf));

Rio_writen(fd, body, strlen(body));

}

/* $end clienterror */

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值