You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

252 lines
8.1 KiB

// #param warning(disable:4996)
#include "csapp.h"
#include<time.h>
int read_ini(char* filename,const char* key,void* value);
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);
char add_log[1024];
int main(int argc, char **argv)
{
int listenfd, connfd, port, clientlen;
struct sockaddr_in clientaddr;
// read_ini("webserver.ini","port",
// if (argc != 2) {
// fprintf(stderr, "usage: %s <port>\n", argv[0]);
// exit(1);
// }
// port = atoi(argv[1]);
char ini[1024];
if (read_ini("webserver.ini","port",&ini)==-1){
printf("read_ini error\n");
return 0;
}
port=atoi((const char*)ini);
printf("\n\n\n%d\n\n\n",port);
listenfd = Open_listenfd(port);// 打开一个监听套接字
while (1) {// 执行典型的无限服务器循环,不断接受连接请求
clientlen = sizeof(clientaddr);
connfd = Accept(listenfd, (SA *)&clientaddr, &clientlen);
char ipv4_address[1024];
inet_ntop(AF_INET,&clientaddr.sin_addr,ipv4_address,INET_ADDRSTRLEN);
time_t nowt=time(0);
struct tm* nowlt=localtime(&nowt);
sprintf(add_log,"%d/%d/%d %d:%d:%d IP:%s ",\
nowlt->tm_year+1900,nowlt->tm_mon,nowlt->tm_mday,nowlt->tm_hour,\
nowlt->tm_min,nowlt->tm_sec,ipv4_address);
doit(connfd); // 执行事务
Close(connfd);
}
}
// 简单的读取配置
int read_ini(char* filename,const char* key,void* value){
FILE* fl;
if ((fl=fopen(filename,"r"))==NULL){
perror("fopen");
return -1;
}
char sLine[1024];
while ((fgets(sLine,1024,fl))!=NULL){
if (strncmp(sLine,"//",2)==0) continue;
if ('#'==sLine[0]) continue;
char* eq=strchr(sLine,'=');
if (NULL!=eq && strncmp(key,sLine,strlen(key))==0){
sLine[strlen(sLine)-1]='\0';
fclose(fl);
printf("\n\n\n%siswhat\n\n\n",(char*)eq);
while (*(eq+1)==' ' && *(eq+1)!='.') eq++;
strcpy(value,eq+1);
return 0;
}
}
fclose(fl);
return -1;
}
// doit 处理请求
void doit(int fd)
{
int is_static;
struct stat sbuf;
char buf[MAXLINE], method[MAXLINE], uri[MAXLINE], version[MAXLINE];
char filename[MAXLINE], cgiargs[MAXLINE];
rio_t rio;
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;
}
sprintf(add_log,"%s%s %s\n",add_log,method,uri);
FILE* fl;
if ((fl=fopen("webserver.log","a+"))!=NULL){
fputs((const char*)add_log,fl);
add_log[0]='\0';
fclose(fl);
}
read_requesthdrs(&rio); // 读并忽略请求报头
// Parse URI from GET request
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;
}
if (is_static) { // Serve static content
if (!(S_ISREG(sbuf.st_mode)) || !(S_IRUSR & sbuf.st_mode)) {
// S_ISREG 此文件是否是普通文件 S_IRUSR & sbuf.st_mode 用户是否具有读权限
clienterror(fd, filename, "403", "Forbidden",
"Tiny couldn't read the file");
return;
}
serve_static(fd, filename, sbuf.st_size); //servestatic
}
else { // Serve dynamic content
if (!(S_ISREG(sbuf.st_mode)) || !(S_IXUSR & sbuf.st_mode)) { //executable
clienterror(fd, filename, "403", "Forbidden",
"Tiny couldn't run the CGI program");
return;
}
serve_dynamic(fd, filename, cgiargs); //servedynamic
}
}
// read_requesthdrs - read and parse HTTP request headers
void read_requesthdrs(rio_t *rp)
{
char buf[MAXLINE];
Rio_readlineb(rp, buf, MAXLINE);
while(strcmp(buf, "\r\n")) { //line:netp:readhdrs:checkterm
Rio_readlineb(rp, buf, MAXLINE);
printf("%s", buf);
}
return;
}
// parse_uri - parse URI into filename and CGI args
// return 0 if dynamic content, 1 if static
int parse_uri(char *uri, char *filename, char *cgiargs)
{
char *ptr;
if (!strstr(uri, "cgi-bin")) { // Static content
strcpy(cgiargs, "");
if (read_ini("webserver.ini","root",filename)==-1){
printf("read_ini error\n");
return -1;
}
if (filename[0]!='/') strcpy(filename, ".");
printf("\n\n\n%sis root\n\n\n",filename);
// strcpy(filename, ".");
strcat(filename, uri);
if (uri[strlen(uri)-1] == '/')
strcat(filename, "index.html");
return 1;
}
else { // Dynamic content
ptr = index(uri, '?');
if (ptr) {
strcpy(cgiargs, ptr+1);
*ptr = '\0';
}
else
strcpy(cgiargs, "");
strcpy(filename, ".");
strcat(filename, uri);
return 0;
}
}
// serve_static - copy a file back to the client
void serve_static(int fd, char *filename, int filesize)
{
int srcfd;
char *srcp, filetype[MAXLINE], buf[MAXBUF];
// Send response headers to client
get_filetype(filename, filetype);
sprintf(buf, "HTTP/1.0 200 OK\r\n");
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));
// Send response body to client
srcfd = Open(filename, O_RDONLY, 0);
srcp = Mmap(0, filesize, PROT_READ, MAP_PRIVATE, srcfd, 0);
Close(srcfd);
Rio_writen(fd, srcp, filesize);
Munmap(srcp, filesize);
}
// get_filetype - derive file type from file name
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");
}
// serve_dynamic - run a CGI program on behalf of the client
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) {
// Real server would set all CGI vars here
setenv("QUERY_STRING", cgiargs, 1);
Dup2(fd, STDOUT_FILENO); // Redirect stdout to client
Execve(filename, emptylist, environ); // Run CGI program
}
Wait(NULL); // Parent waits for and reaps child
}
// clienterror - returns an error message to the client
void clienterror(int fd, char *cause, char *errnum,
char *shortmsg, char *longmsg)
{
char buf[MAXLINE], body[MAXBUF];
// Build the HTTP response body
sprintf(body, "<html><title>Tiny Error</title>");
sprintf(body, "%s<body bgcolor=""ffffff"">\r\n", body);
sprintf(body, "%s%s: %s\r\n", body, errnum, shortmsg);
sprintf(body, "%s<p>%s: %s\r\n", body, longmsg, cause);
sprintf(body, "%s<hr><em>The Tiny Web server</em>\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));
}