1. Web基礎html
MIME類型 | 描述 |
text/html | HTML頁面 |
text/plain | 無格式文本 |
image/gif | GIF格式編碼的二進制圖像 |
image/jpeg | JPEG格式編碼的二進制圖像 |
http://www.google.com:80/index.html
表示因特網主機 www.google.com 上一個稱爲 index.html 的HTML文件,它是由一個監聽端口80的Web服務器所管理的。 HTTP默認端口號爲80web
http://www.ics.cs.cmu.edu:8000/cgi-bin/adder?123&456
表示一個 /cgi-bin/adder 的可執行文件,帶兩個參數字符串爲 123 和 456編程
狀態代碼 | 狀態消息 | 描述 |
200 | 成功 | 處理請求無誤 |
301 | 永久移動 | 內容移動到位置頭中指明的主機上 |
400 | 錯誤請求 | 服務器不能理解請求 |
403 | 禁止 | 服務器無權訪問所請求的文件 |
404 | 未發現 | 服務器不能找到所請求的文件 |
501 | 未實現 | 服務器不支持請求的方法 |
505 | HTTP版本不支持 | 服務器不支持請求的版本 |
GET /cgi-bin/adder?123&456 HTTP/1.1
它調用 fork 來建立一個子進程,並調用 execve 在子進程的上下文中執行 /cgi-bin/adder 程序瀏覽器
環境變量 | 描述 |
QUERY_STRING | 程序參數 |
SERVER_PORT | 父進程偵聽的端口 |
REQUEST_METHOD | GET 或 POST |
REMOTE_HOST | 客戶端的域名 |
REMOTE_ADDR | 客戶端的點分十進制IP地址 |
CONTENT_TYPE | 只對POST而言,請求體的MIME類型 |
CONTENT_LENGTH | 只對POST而言,請求體的字節大小 |
1 int main(int argc, char **argv) 2 { 3 int listenfd, connfd, port, clientlen; 4 struct sockaddr_in clientaddr; 5 6 /* Check command line args */ 7 if (argc != 2) { 8 fprintf(stderr, "usage: %s <port>\n", argv[0]); 9 exit(1); 10 } 11 port = atoi(argv[1]); 12 13 listenfd = Open_listenfd(port); 14 while (1) { 15 clientlen = sizeof(clientaddr); 16 connfd = Accept(listenfd, (SA *)&clientaddr, &clientlen); //line:netp:tiny:accept 17 doit(connfd); //line:netp:tiny:doit 18 Close(connfd); //line:netp:tiny:close 19 } 20 }
1 void doit(int fd) 2 { 3 int is_static; 4 struct stat sbuf; 5 char buf[MAXLINE], method[MAXLINE], uri[MAXLINE], version[MAXLINE]; 6 char filename[MAXLINE], cgiargs[MAXLINE]; 7 rio_t rio; 8 9 /* Read request line and headers */ 10 Rio_readinitb(&rio, fd); 11 Rio_readlineb(&rio, buf, MAXLINE); //line:netp:doit:readrequest 12 sscanf(buf, "%s %s %s", method, uri, version); //line:netp:doit:parserequest 13 if (strcasecmp(method, "GET")) { //line:netp:doit:beginrequesterr 14 clienterror(fd, method, "501", "Not Implemented", 15 "Tiny does not implement this method"); 16 return; 17 } //line:netp:doit:endrequesterr 18 read_requesthdrs(&rio); //line:netp:doit:readrequesthdrs 19 20 /* Parse URI from GET request */ 21 is_static = parse_uri(uri, filename, cgiargs); //line:netp:doit:staticcheck 22 if (stat(filename, &sbuf) < 0) { //line:netp:doit:beginnotfound 23 clienterror(fd, filename, "404", "Not found", 24 "Tiny couldn't find this file"); 25 return; 26 } //line:netp:doit:endnotfound 27 28 if (is_static) { /* Serve static content */ 29 if (!(S_ISREG(sbuf.st_mode)) || !(S_IRUSR & sbuf.st_mode)) { //line:netp:doit:readable 30 clienterror(fd, filename, "403", "Forbidden", 31 "Tiny couldn't read the file"); 32 return; 33 } 34 serve_static(fd, filename, sbuf.st_size); //line:netp:doit:servestatic 35 } 36 else { /* Serve dynamic content */ 37 if (!(S_ISREG(sbuf.st_mode)) || !(S_IXUSR & sbuf.st_mode)) { //line:netp:doit:executable 38 clienterror(fd, filename, "403", "Forbidden", 39 "Tiny couldn't run the CGI program"); 40 return; 41 } 42 serve_dynamic(fd, filename, cgiargs); //line:netp:doit:servedynamic 43 } 44 }
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)); }
1 void read_requesthdrs(rio_t *rp) 2 { 3 char buf[MAXLINE]; 4 5 Rio_readlineb(rp, buf, MAXLINE); 6 while(strcmp(buf, "\r\n")) { //line:netp:readhdrs:checkterm 7 Rio_readlineb(rp, buf, MAXLINE); 8 printf("%s", buf); 9 } 10 return; 11 }
1 int parse_uri(char *uri, char *filename, char *cgiargs) 2 { 3 char *ptr; 4 5 if (!strstr(uri, "cgi-bin")) { /* Static content */ //line:netp:parseuri:isstatic 6 strcpy(cgiargs, ""); //line:netp:parseuri:clearcgi 7 strcpy(filename, "."); //line:netp:parseuri:beginconvert1 8 strcat(filename, uri); //line:netp:parseuri:endconvert1 9 if (uri[strlen(uri)-1] == '/') //line:netp:parseuri:slashcheck 10 strcat(filename, "home.html"); //line:netp:parseuri:appenddefault 11 return 1; 12 } 13 else { /* Dynamic content */ //line:netp:parseuri:isdynamic 14 ptr = index(uri, '?'); //line:netp:parseuri:beginextract 15 if (ptr) { 16 strcpy(cgiargs, ptr+1); 17 *ptr = '\0'; 18 } 19 else 20 strcpy(cgiargs, ""); //line:netp:parseuri:endextract 21 strcpy(filename, "."); //line:netp:parseuri:beginconvert2 22 strcat(filename, uri); //line:netp:parseuri:endconvert2 23 return 0; 24 } 25 }
1 void serve_static(int fd, char *filename, int filesize) 2 { 3 int srcfd; 4 char *srcp, filetype[MAXLINE], buf[MAXBUF]; 5 6 /* Send response headers to client */ 7 get_filetype(filename, filetype); //line:netp:servestatic:getfiletype 8 sprintf(buf, "HTTP/1.0 200 OK\r\n"); //line:netp:servestatic:beginserve 9 sprintf(buf, "%sServer: Tiny Web Server\r\n", buf); 10 sprintf(buf, "%sContent-length: %d\r\n", buf, filesize); 11 sprintf(buf, "%sContent-type: %s\r\n\r\n", buf, filetype); 12 Rio_writen(fd, buf, strlen(buf)); //line:netp:servestatic:endserve 13 14 /* Send response body to client */ 15 srcfd = Open(filename, O_RDONLY, 0); //line:netp:servestatic:open 16 srcp = Mmap(0, filesize, PROT_READ, MAP_PRIVATE, srcfd, 0);//line:netp:servestatic:mmap 17 Close(srcfd); //line:netp:servestatic:close 18 Rio_writen(fd, srcp, filesize); //line:netp:servestatic:write 19 Munmap(srcp, filesize); //line:netp:servestatic:munmap 20 } 21 22 /* 23 * get_filetype - derive file type from file name 24 */ 25 void get_filetype(char *filename, char *filetype) 26 { 27 if (strstr(filename, ".html")) 28 strcpy(filetype, "text/html"); 29 else if (strstr(filename, ".gif")) 30 strcpy(filetype, "image/gif"); 31 else if (strstr(filename, ".jpg")) 32 strcpy(filetype, "image/jpeg"); 33 else 34 strcpy(filetype, "text/plain"); 35 }
1 void serve_dynamic(int fd, char *filename, char *cgiargs) 2 { 3 char buf[MAXLINE], *emptylist[] = { NULL }; 4 5 /* Return first part of HTTP response */ 6 sprintf(buf, "HTTP/1.0 200 OK\r\n"); 7 Rio_writen(fd, buf, strlen(buf)); 8 sprintf(buf, "Server: Tiny Web Server\r\n"); 9 Rio_writen(fd, buf, strlen(buf)); 10 11 if (Fork() == 0) { /* child */ //line:netp:servedynamic:fork 12 /* Real server would set all CGI vars here */ 13 setenv("QUERY_STRING", cgiargs, 1); //line:netp:servedynamic:setenv 14 Dup2(fd, STDOUT_FILENO); /* Redirect stdout to client */ //line:netp:servedynamic:dup2 15 Execve(filename, emptylist, environ); /* Run CGI program */ //line:netp:servedynamic:execve 16 } 17 Wait(NULL); /* Parent waits for and reaps child */ //line:netp:servedynamic:wait 18 }