Tinyhttpd源码浅读
来源:互联网 发布:马云怎么通过淘宝赚钱 编辑:程序博客网 时间:2024/04/30 10:17
简介
为了加深对APU(UNIX环境高级编程)学习后的理解,粗略地读了下tinyhttpd源码,真得是短小精悍。
代码本身实现了一个接受处理http请求的服务器。http协议是个无状态的传输层协议,有很多种请求类型,tinyhttpd实现了其中最常见的get和post请求类型的处理逻辑。由于http协议的无状态性,代码的可分离性很好。
对于应用unix系统的一些接口,该源码是一个很好的应用例子。作者J. David Blackstone是个有意思的学生,在README里面的内容说,这个就是他学生时自己的一个课程的大作业。
调用关系图
代码一共13个函数,包括main函数在内。其调用关系如下:
代码主流程其实并不复杂,大量的代码用来处理主流程之外的异常情况。
a) 服务器启动监听客户端请求
–> b) 客户端请求到达, 服务端创建线程处理该请求
–> c) 读取method字段, 根据get和post来分别处理
–> d) 把处理结果发给客户端
其中, 过程 c) 中需要根据情况进行一些细节来分支处理:
1) url以/结尾, 则指定index.html
2) 获取文件信息,找不到文件,返回404状态
3) GET请求且无参数, 则把文件以http方式返回给客户端
4) GET请求且带参数, 或者POST请求, 文件有执行权限, 则执行cgi代码
除此之外,主进程把 子进程执行cgi代码后 的结果发给客户端。cgi代码做的工作其实只有:从环境变量和标准输入中读取数据、处理数据、向标准输出输出数据。
模块介绍
工作的起点,从这个入口开始。
int main(void);
分为两个部分:建立连接并监听,然后,处理从连接来的请求。
int startup(u_short *port);void accept_request(int client);
这里可以说是处理http请求的主模块。代码只对http最基础的进行了实现和处理。
主要有三种方式处理:
void serve_file(int, const char *);void execute_cgi(int, const char *, const char *, const char *);void unimplemented(int);
这三个模块分别处理:无参get请求,带参get请求和post请求,其他类型的请求。
接下来,是这些处理模块的各种细节的代码,具体可以参考http协议的细节规定:
int get_line(int, char *, int);void headers(int, const char *);void cat(int, FILE *);
分别是:读取一行,构造http头,输出文件.
剩下的是处理异常的代码:
void error_die(const char *);void not_found(int client);void bad_request(int);void cannot_execute(int);
分别作用是: 退出,请求文件不存在, 收到的请求出现错误, 服务端文件不能执行。
代码介绍
1) 主流程模块
int main(void){ int server_sock = -1; u_short port = 0; int client_sock = -1; struct sockaddr_in client_name; int client_name_len = sizeof(client_name); pthread_t newthread; server_sock = startup(&port); printf("httpd running on port %d\n", port); while (1) { client_sock = accept(server_sock,(struct sockaddr *)&client_name, &client_name_len); if (client_sock == -1) error_die("accept"); /* accept_request(client_sock); */ if (pthread_create(&newthread , NULL, accept_request, client_sock) != 0) perror("pthread_create"); } close(server_sock); return(0);}
int startup(u_short *port){ int httpd = 0; struct sockaddr_in name; httpd = socket(PF_INET, SOCK_STREAM, 0); if (httpd == -1) error_die("socket"); memset(&name, 0, sizeof(name)); name.sin_family = AF_INET; name.sin_port = htons(*port); name.sin_addr.s_addr = htonl(INADDR_ANY); if (bind(httpd, (struct sockaddr *)&name, sizeof(name)) < 0) error_die("bind"); if (*port == 0) /* if dynamically allocating a port */ { int namelen = sizeof(name); if (getsockname(httpd, (struct sockaddr *)&name, &namelen) == -1) error_die("getsockname"); *port = ntohs(name.sin_port); } if (listen(httpd, 5) < 0) error_die("listen"); return(httpd);}
void accept_request(int client){ char buf[1024]; int numchars; char method[255]; char url[255]; char path[512]; size_t i, j; struct stat st; int cgi = 0; /* becomes true if server decides this is a CGI * program */ char *query_string = NULL; numchars = get_line(client, buf, sizeof(buf)); i = 0; j = 0; while (!ISspace(buf[j]) && (i < sizeof(method) - 1)) { method[i] = buf[j]; i++; j++; } method[i] = '\0'; if (strcasecmp(method, "GET") && strcasecmp(method, "POST")) { unimplemented(client); return; } if (strcasecmp(method, "POST") == 0) cgi = 1; i = 0; while (ISspace(buf[j]) && (j < sizeof(buf))) j++; while (!ISspace(buf[j]) && (i < sizeof(url) - 1) && (j < sizeof(buf))) { url[i] = buf[j]; i++; j++; } url[i] = '\0'; if (strcasecmp(method, "GET") == 0) { query_string = url; while ((*query_string != '?') && (*query_string != '\0')) query_string++; if (*query_string == '?') { cgi = 1; *query_string = '\0'; query_string++; } }
2) 处理http请求的主模块
void serve_file(int client, const char *filename){ FILE *resource = NULL; int numchars = 1; char buf[1024]; buf[0] = 'A'; buf[1] = '\0'; while ((numchars > 0) && strcmp("\n", buf)) /* read & discard headers */ numchars = get_line(client, buf, sizeof(buf)); resource = fopen(filename, "r"); if (resource == NULL) not_found(client); else { headers(client, filename); cat(client, resource); } fclose(resource);}
void execute_cgi(int client, const char *path, const char *method, const char *query_string){ char buf[1024]; int cgi_output[2]; int cgi_input[2]; pid_t pid; int status; int i; char c; int numchars = 1; int content_length = -1; buf[0] = 'A'; buf[1] = '\0'; if (strcasecmp(method, "GET") == 0) while ((numchars > 0) && strcmp("\n", buf)) /* read & discard headers */ numchars = get_line(client, buf, sizeof(buf)); else /* POST */ { numchars = get_line(client, buf, sizeof(buf)); while ((numchars > 0) && strcmp("\n", buf)) { buf[15] = '\0'; if (strcasecmp(buf, "Content-Length:") == 0) content_length = atoi(&(buf[16])); numchars = get_line(client, buf, sizeof(buf)); } if (content_length == -1) { bad_request(client); return; } }
void unimplemented(int client){ char buf[1024]; sprintf(buf, "HTTP/1.0 501 Method Not Implemented\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, SERVER_STRING); send(client, buf, strlen(buf), 0); sprintf(buf, "Content-Type: text/html\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "<HTML><HEAD><TITLE>Method Not Implemented\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "</TITLE></HEAD>\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "<BODY><P>HTTP request method not supported.\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "</BODY></HTML>\r\n"); send(client, buf, strlen(buf), 0);}
3) 处理模块的各种细节方法
int get_line(int sock, char *buf, int size){ int i = 0; char c = '\0'; int n; while ((i < size - 1) && (c != '\n')) { n = recv(sock, &c, 1, 0); /* DEBUG printf("%02X\n", c); */ if (n > 0) { if (c == '\r') { n = recv(sock, &c, 1, MSG_PEEK); /* DEBUG printf("%02X\n", c); */ if ((n > 0) && (c == '\n')) recv(sock, &c, 1, 0); else c = '\n'; } buf[i] = c; i++; } else c = '\n'; } buf[i] = '\0'; return(i);}
void headers(int client, const char *filename){ char buf[1024]; (void)filename; /* could use filename to determine file type */ strcpy(buf, "HTTP/1.0 200 OK\r\n"); send(client, buf, strlen(buf), 0); strcpy(buf, SERVER_STRING); send(client, buf, strlen(buf), 0); sprintf(buf, "Content-Type: text/html\r\n"); send(client, buf, strlen(buf), 0); strcpy(buf, "\r\n"); send(client, buf, strlen(buf), 0);}
void cat(int client, FILE *resource){ char buf[1024]; fgets(buf, sizeof(buf), resource); while (!feof(resource)) { send(client, buf, strlen(buf), 0); fgets(buf, sizeof(buf), resource); }}
4) 异常处理模块
void error_die(const char *sc){ perror(sc); exit(1);}
void not_found(int client){ char buf[1024]; sprintf(buf, "HTTP/1.0 404 NOT FOUND\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, SERVER_STRING); send(client, buf, strlen(buf), 0); sprintf(buf, "Content-Type: text/html\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "<HTML><TITLE>Not Found</TITLE>\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "<BODY><P>The server could not fulfill\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "your request because the resource specified\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "is unavailable or nonexistent.\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "</BODY></HTML>\r\n"); send(client, buf, strlen(buf), 0);}
void bad_request(int client){ char buf[1024]; sprintf(buf, "HTTP/1.0 400 BAD REQUEST\r\n"); send(client, buf, sizeof(buf), 0); sprintf(buf, "Content-type: text/html\r\n"); send(client, buf, sizeof(buf), 0); sprintf(buf, "\r\n"); send(client, buf, sizeof(buf), 0); sprintf(buf, "<P>Your browser sent a bad request, "); send(client, buf, sizeof(buf), 0); sprintf(buf, "such as a POST without a Content-Length.\r\n"); send(client, buf, sizeof(buf), 0);}
void cannot_execute(int client){ char buf[1024]; sprintf(buf, "HTTP/1.0 500 Internal Server Error\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "Content-type: text/html\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "<P>Error prohibited CGI execution.\r\n"); send(client, buf, strlen(buf), 0);}
5) 其他
#define ISspace(x) isspace((int)(x))#define SERVER_STRING "Server: jdbhttpd/0.1.0\r\n"
引用
[1] HTTP协议详解 http://www.cnblogs.com/EricaMIN1987_IT/p/3837436.html
[2] 源码分析之tinyhttpd-0.1 http://www.cnblogs.com/wanpengcoder/p/5304521.html
[3] tinyhttpd源码详解 http://blog.csdn.net/baiwfg2/article/details/45582723
- Tinyhttpd源码浅读
- tinyhttpd源码学习1
- tinyhttpd源码学习2
- 源码阅读tinyhttpd
- tinyhttpd源码详解
- TinyHTTPd源码剖析
- Tinyhttpd源码剖析(一)
- Tinyhttpd源码剖析(二)
- Tinyhttpd源码解析
- Tinyhttpd-源码阅读笔记
- Tinyhttpd源码分析
- tinyhttpd源码详解
- tinyhttpd源码分析
- tinyhttpd源码详解
- 阅读源码:tinyhttpd
- tinyhttpd源码分析
- 【剖析tinyhttpd源码】
- TinyHttpd源码感悟
- VPS的centOS6安装远程桌面
- 深度学习的2016: NIPS 2016速览
- Java环境准备:JDK和Maven的安装和配置
- 2016,再见。
- 小米手机刷机&ROOT原理
- Tinyhttpd源码浅读
- 爬虫框架scrapy,爬取豆瓣电影top250
- 判断一个矩阵是否可对角化
- android studio导入slidingmenu库出错的一种原因
- 【C++解题报告】求阶乘之和(定义函数)
- Android6.0动态权限分析
- 一图读懂JVM架构解析
- Lattice系列FPGA入门相关2(FPGA和CPLD的区别)
- border