Tiny Http源码解析

相关概念

源码
HTTP
CGI
getsockname()

函数说明

     accept_request:  处理从套接字上监听到的一个 HTTP 请求,在这里可以很大一部分地体现服务器处理请求流程。bad_request: 返回给客户端这是个错误请求,HTTP 状态吗 400 BAD REQUEST.cat: 读取服务器上某个文件写到 socket 套接字。cannot_execute: 主要处理发生在执行 cgi 程序时出现的错误。error_die: 把错误信息写到 perror 并退出。execute_cgi: 运行 cgi 程序的处理,也是个主要函数。get_line: 读取套接字的一行,把回车换行等情况都统一为换行符结束。headers: 把 HTTP 响应的头部写到套接字。not_found: 主要处理找不到请求的文件时的情况。sever_file: 调用 cat 把服务器文件返回给浏览器。startup: 初始化 httpd 服务,包括建立套接字,绑定端口,进行监听等。unimplemented: 返回给浏览器表明收到的 HTTP 请求所用的 method 不被支持。

解析

main


int main(void)
{int server_sock = -1;u_short port = 0;int client_sock = -1;struct sockaddr_in client_name;//这边要为socklen_t类型socklen_t client_name_len = sizeof(client_name);pthread_t newthread;//建立TCP连接,监听连接请求server_sock = startup(&port);printf("httpd running on port %d\n", port);while (1){//接受请求,函数原型//#include <sys/types.h>//#include <sys/socket.h>  //int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);client_sock = accept(server_sock,(struct sockaddr *)&client_name,&client_name_len);if (client_sock == -1)error_die("accept");/* accept_request(client_sock); *///每次收到请求,创建一个线程来处理接受到的请求//把client_sock转成地址作为参数传入pthread_createif (pthread_create(&newthread, NULL, (void *)accept_request, (void *)(intptr_t)client_sock) != 0)perror("pthread_create");}close(server_sock);return(0);
}

accept_request

void accept_request(void *arg)
{//socketint client = (intptr_t)arg;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;//根据上面的Get请求,可以看到这边就是取第一行//这边都是在处理第一条http信息//"GET / HTTP/1.1\n"numchars = get_line(client, buf, sizeof(buf));i = 0; j = 0;//第一行字符串提取Getwhile (!ISspace(buf[j]) && (i < sizeof(method) - 1)){method[i] = buf[j];i++; j++;}//结束method[i] = '\0';//判断是Get还是Postif (strcasecmp(method, "GET") && strcasecmp(method, "POST")){unimplemented(client);return;}//如果是POST,cgi置为1if (strcasecmp(method, "POST") == 0)cgi = 1;i = 0;//跳过空格while (ISspace(buf[j]) && (j < sizeof(buf)))j++;//得到 "/"   注意:如果你的http的网址为http://192.168.0.23:47310/index.html//               那么你得到的第一条http信息为GET /index.html HTTP/1.1,那么//               解析得到的就是/index.htmlwhile (!ISspace(buf[j]) && (i < sizeof(url) - 1) && (j < sizeof(buf))){url[i] = buf[j];i++; j++;}url[i] = '\0';//判断Get请求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++;}}//路径sprintf(path, "htdocs%s", url);//默认地址,解析到的路径如果为/,则自动加上index.htmlif (path[strlen(path) - 1] == '/')strcat(path, "index.html");//获得文件信息if (stat(path, &st) == -1) {//把所有http信息读出然后丢弃while ((numchars > 0) && strcmp("\n", buf))  /* read & discard headers */numchars = get_line(client, buf, sizeof(buf));//没有找到not_found(client);}else{if ((st.st_mode & S_IFMT) == S_IFDIR)strcat(path, "/index.html");//如果你的文件默认是有执行权限的,自动解析成cgi程序,如果有执行权限但是不能执行,会接受到报错信号if ((st.st_mode & S_IXUSR) ||(st.st_mode & S_IXGRP) ||(st.st_mode & S_IXOTH)    )cgi = 1;if (!cgi)//接读取文件返回给请求的http客户端serve_file(client, path);else//执行cgi文件execute_cgi(client, path, method, query_string);}//执行完毕关闭socketclose(client);
}

execute_cgi

void execute_cgi(int client, const char *path,const char *method, const char *query_string)
{
//缓冲区char buf[1024];//2根管道int cgi_output[2];int cgi_input[2];//进程pid和状态pid_t pid;int status;int i;char c;//读取的字符数int numchars = 1;//http的content_lengthint content_length = -1;//默认字符buf[0] = 'A'; buf[1] = '\0';//忽略大小写比较字符串if (strcasecmp(method, "GET") == 0)//读取数据,把整个header都读掉,以为Get写死了直接读取index.html,没有必要分析余下的http信息了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)){//如果是POST请求,就需要得到Content-Length,Content-Length:这个字符串一共长为15位,所以//取出头部一句后,将第16位设置结束符,进行比较//第16位置为结束buf[15] = '\0';if (strcasecmp(buf, "Content-Length:") == 0)//内存从第17位开始就是长度,将17位开始的所有字符串转成整数就是content_lengthcontent_length = atoi(&(buf[16]));numchars = get_line(client, buf, sizeof(buf));}if (content_length == -1) {bad_request(client);return;}}sprintf(buf, "HTTP/1.0 200 OK\r\n");send(client, buf, strlen(buf), 0);//建立output管道if (pipe(cgi_output) < 0) {cannot_execute(client);return;}//建立input管道if (pipe(cgi_input) < 0) {cannot_execute(client);return;}//       fork后管道都复制了一份,都是一样的//       子进程关闭2个无用的端口,避免浪费             //       ×<------------------------->1    output//       0<-------------------------->×   input //       父进程关闭2个无用的端口,避免浪费             //       0<-------------------------->×   output//       ×<------------------------->1    input//       此时父子进程已经可以通信//fork进程,子进程用于执行CGI//父进程用于收数据以及发送子进程处理的回复数据if ( (pid = fork()) < 0 ) {cannot_execute(client);return;}if (pid == 0)  /* child: CGI script */{char meth_env[255];char query_env[255];char length_env[255];//子进程输出重定向到output管道的1端dup2(cgi_output[1], 1);//子进程输入重定向到input管道的0端dup2(cgi_input[0], 0);//关闭无用管道口close(cgi_output[0]);close(cgi_input[1]);//CGI环境变量sprintf(meth_env, "REQUEST_METHOD=%s", method);putenv(meth_env);if (strcasecmp(method, "GET") == 0) {sprintf(query_env, "QUERY_STRING=%s", query_string);putenv(query_env);}else {   /* POST */sprintf(length_env, "CONTENT_LENGTH=%d", content_length);putenv(length_env);}//替换执行pathexecl(path, path, NULL);//int m = execl(path, path, NULL);//如果path有问题,例如将html网页改成可执行的,但是执行后m为-1//退出子进程,管道被破坏,但是父进程还在往里面写东西,触发Program received signal SIGPIPE, Broken pipe.exit(0);} else {    /* parent *///关闭无用管道口close(cgi_output[1]);close(cgi_input[0]);if (strcasecmp(method, "POST") == 0)for (i = 0; i < content_length; i++) {//得到post请求数据,写到input管道中,供子进程使用recv(client, &c, 1, 0);write(cgi_input[1], &c, 1);}//从output管道读到子进程处理后的信息,然后send出去while (read(cgi_output[0], &c, 1) > 0)send(client, &c, 1, 0);//完成操作后关闭管道close(cgi_output[0]);close(cgi_input[1]);//等待子进程返回waitpid(pid, &status, 0);}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/677968.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

《CSS 简易速速上手小册》第5章:CSS 动画与过渡(2024 最新版)

文章目录 5.1 CSS 过渡基础&#xff1a;网页的微妙舞步5.1.1 基础知识5.1.2 重点案例&#xff1a;按钮悬停效果5.1.3 拓展案例 1&#xff1a;渐变显示导航菜单5.1.4 拓展案例 2&#xff1a;动态调整元素大小 5.2 关键帧动画&#xff1a;编排你的网页芭蕾5.2.1 基础知识5.2.2 重…

ctfshow-web21~28-WP

爆破(21-28) web21 题目给了一个zip文件,打开后解压是爆破的字典,我们抓包一下网址看看 发现账号和密码都被base64了,我们发送到intruder模块,给爆破的位置加上$符圈住 去base64解码一下看看格式

golang常用库之-操作数据库ORM:GORM 包介绍 | 一些 GORM 提示和注意事项

文章目录 golang操作数据库ORM&#xff1a;GORM 包介绍及实战一、什么是GORM 包二、GORM基本使用官方快速开始demo 一些 GORM 提示和注意事项 参考 golang操作数据库ORM&#xff1a;GORM 包介绍及实战 一、什么是GORM 包 官网&#xff1a;https://gorm.io/ github&#xff1a…

【PyQt】09-控件提示信息、Lable标签

文章目录 前言一、控件提示信息1.1 代码1.2 解释 < b >在HTML标签中的作用1.3 添加按键后的代码运行结果 二、QLabel控件介绍2.1 内容2.2 常用的事件2.3 代码结果展示 总结 前言 1、控件提示信息 2、QLabel控件介绍 一、控件提示信息 关键点在于 效果如图所示&#x…

VR和AR傻傻分不清,一句话给你讲明白。

不说废话&#xff0c;直接说结论&#xff0c;虚拟现实&#xff08;Virtual Reality&#xff0c;VR&#xff09;和增强现实&#xff08;Augmented Reality&#xff0c;AR&#xff09;。如果现实是A&#xff0c;虚拟是B&#xff0c;那么VRB&#xff0c;ARAB&#xff0c;就这简单&…

大模型实战营第二期——3. 基于 InternLM 和 LangChain 搭建你的知识库

github地址&#xff1a;InternLM/tutorial-书生浦语大模型实战营文档地址&#xff1a;基于 InternLM 和 LangChain 搭建你的知识库视频地址&#xff1a;基于 InternLM 和 LangChain 搭建你的知识库Intern Studio: https://studio.intern-ai.org.cn/console/instance动手学大模型…

2-8 单链表+双链表+模拟栈+模拟队列

今天给大家用数组来实现链表栈和队列 单链表&#xff1a; 首先要明白是如何用数组实现&#xff0c; 在这里需要用到几个数组&#xff0c;head表示头节点的下标&#xff0c;e[i]表示表示下标为i的值&#xff0c;ne[i]表示当前节点下一个节点的下标。idx表示当前已经用到那个点…

HTTP 协议在互联网中的作用是什么?

首先&#xff0c;HTTP 协议是互联网上应用最为广泛的一种网络协议。它被设计为无状态的&#xff0c;意味着服务器不会为每个请求保持状态。HTTP 协议的请求方法有 GET、POST、PUT、DELETE 等&#xff0c;分别对应着不同的操作。例如&#xff0c;GET 方法用于请求数据&#xff0…

Git的基础操作指令

目录 1 前言 2 指令 2.1 git init 2.2 touch xxx 2.3 git status 2.4 git add xxx 2.5 git commit -m xxxx 2.5 git log及git log --prettyoneline --all --graph --abbrev-commit 2.6 rm xxx 2.7 git reset --hard xxx(含小技巧) 2.8 git reflog 2.9 mv xxx yyy 1…

【BIAI】Lecture 14 - Sleep and Dreaming

Sleep and Dreaming 专业词汇 pons 延髓 parietal cortex 顶叶皮层 limbic system 边缘系统 temporal cortex 颞叶皮层 dorsolateral prefrontal cortex 背外侧前额叶皮层 pineal gland 松果体 Suprachiasmatic Nucleus 视交叉上核 课程大纲 Sleep stages awake无眼动睡眠&am…

【Python如何求出所有3位数的回文数】

回文数就是正向读和逆向读都相同的数&#xff0c;如66&#xff0c;626&#xff0c;72127 1、求出所有3位数的回文数python代码如下&#xff1a; # 输出所有3位数的回文数 for i in range(100, 1000): # 从100循环到999&#xff0c;不包含1000if str(i) str(i)[::-1]: # 如…

动态水印怎么加 怎么去除动态水印 视频剪辑软件 会声会影安激活序列号 会声会影怎么剪辑视频

为了防止白嫖或者增加美观效果&#xff0c;视频制作者可能会采用动态水印的方式&#xff0c;让其他人难以盗取视频使用。动态水印的添加&#xff0c;需要应用到运动路径功能。接下来&#xff0c;本文会教大家动态水印怎么加&#xff0c;怎么去除动态水印的相关内容。感兴趣的小…

【知识整理】接手新技术团队、管理团队

引言 针对目前公司三大技术中心的不断升级&#xff0c;技术管理岗位要求越来越高&#xff0c;且团队人员特别是管理岗位的选择任命更是重中之重&#xff0c;下面针对接手新的技术团队做简要整理&#xff1b; 一、实践操作 1、前期准备 1、熟悉情况&#xff1a; 熟悉人员&am…

XSS-Lab

1.关于20关的payload合集。 <script>alert(1)</script> "><script>alert(1)</script> onclickalert(1) " onclick"alert(1) "><a href"javascript:alert(1)"> "><a HrEf"javascript:alert…

【机器学习笔记】基于实例的学习

基于实例的学习 文章目录 基于实例的学习1 基本概念与最近邻方法2 K-近邻&#xff08;KNN&#xff09;3 距离加权 KNN4 基于实例/记忆的学习器5 局部加权回归5 多种回归方式对比6 懒惰学习与贪婪学习 ​ 动机&#xff1a;人们通过 记忆和行动来推理学习。 1 基本概念与最近邻方…

四轴飞行器pid简介

四轴飞行器通过螺旋桨的转动产生升力&#xff0c;克服重力飞行&#xff0c; 四电机如果同一方向&#xff0c;除升力外&#xff0c;还会有个旋转分量&#xff0c;所以一般两个相临电机反方向旋转&#xff0c;抵消掉旋转分量&#xff0c;实现飞行器的平稳飞行。现实情况电机螺旋浆…

C#入门及进阶|数组和集合(六):集合概述

1.集合概述 数组是一组具有相同名称和类型的变量集合&#xff0c;但是数组初始化后就不便于再改变其大小&#xff0c;不能实现在程序中动态添加和删除数组元素&#xff0c;使数组的使用具有很多局限性。集合能解决数组存在的这个问题&#xff0c;下面我们来学习介绍集合…

Web课程学习笔记--jsonp的原理与简单实现

jsonp的原理与简单实现 原理 由于同源策略的限制&#xff0c;XmlHttpRequest只允许请求当前源&#xff08;域名、协议、端口&#xff09;的资源&#xff0c;为了实现跨域请求&#xff0c;可以通过script标签实现跨域请求&#xff0c;然后在服务端输出JSON数据并执行回调函数&…

AES加密中的CBC和ECB

目录 1.说明 2.ECB模式&#xff08;base64&#xff09; 3.CBC模式 4.总结 1.说明 AES是常见的对称加密算法&#xff0c;加密和解密使用相同的密钥&#xff0c;流程如下&#xff1a; 主要概念如下&#xff1a; ①明文 ②密钥 用来加密明文的密码&#xff0c;在对称加密算…

算法学习——LeetCode力扣二叉树篇2

算法学习——LeetCode力扣二叉树篇2 107. 二叉树的层序遍历 II 107. 二叉树的层序遍历 II - 力扣&#xff08;LeetCode&#xff09; 描述 给你二叉树的根节点 root &#xff0c;返回其节点值 自底向上的层序遍历 。 &#xff08;即按从叶子节点所在层到根节点所在的层&#…