编写HTTP协议代理的一些知识(源码)

        早期上网经常需要使用代理服务器,现在用的比较少了,大家更耳熟能详的反而是“反向代理”如Nginx。

        代理服务器一般用作局域网上网,而反向代理则是把来自互联网的连接转发到局域网上,作用刚好相反。

        HTTP协议自身就带有对代理服务器的支持。HTTP协议目前主要有多个版本,0.9太简单,基本不见了,1.0只支持一个连接一个请求,1.1则支持长连接,2.0极大复杂化了传输过程,支持多路复用。协议版本这么多,但是代理服务器作为中间商,可以选择一个较低的版本,用户的客户端和服务器一般都有能力适应多个版本。

        代理服务器可以选择比较简单的HTTP1.0版本,一个连接就是一个请求,只需要在连接建立之后做处理,处理完请求就是简单的数据转发了。

        HTTP1.0协议对代理服务器的支持基本就是两点:

  1. 请求行对使用绝对URL
  2. 专用于代理服务器的Proxy-XXXX头标

        代理服务器要做的事情是:

  1. 取出请求行的服务器域名和端口并擦除(擦除后与直接请求的请求行相同)
  2. 将协议版本降低为自己支持的版本
  3. 根据proxy-XXXX头标处理并擦除
  4. 像直接请求一样访问服务器
  5. 转发数据给用户

        前面说的“擦除”是把后面的数据前移而不是设置为空格,设置为空格并不符合HTTP协议,服务器一般不能理解。

        原则上代理服务器可以支持客户端和服务器是不同的协议版本,比如客户端是1.0而服务器是1.1,这将极大地影响程序复杂度。

        虽然HTTP的BODY与代理服务器处理无关,只需要接受完头部就可以处理,但是最好整个请求完整发送,因为有些服务器不能处理请求头和BODY分开的情形。

        代理服务器认证

        代理服务器通过Proxy-XXXX头标进行认证,这个认证是代理服务器的认证而不是用户要访问的服务器的认证。代理服务器认证完后就应该删除这些头标,因为这些头标对目标服务器毫无意义。

        隧道请求CONNECT

        CONNECT是个不常用的头标,专门用于代理。代理服务器取得目标服务器后直接连上去就可以了,然后就是双向转发数据。

        代码示例

        下面的代码就是一个HTTP1.0代理的协议处理部分的代码,没有认证(因为用的是IP地址认证,在进入这个代码之前就已经处理过了):

//servicethreadhttp.cpp#include "stdafx.h"
#include "mystd.h"
#include "Proxy.h"
#include "httpresp.h"extern CProxyApp theApp;//HTTP协议处理线程
DWORD ServiceThreadHttp(LPDWORD lpdwParam)
{
//--线程参数处理------------------------int cdindex;//连接数据索引struct ServiceData * servicedata;cdindex=((struct ThreadUserData *)lpdwParam)->index;servicedata=((struct ThreadUserData *)lpdwParam)->servicedata;
//--------------------------------------struct ConnectionData * cd;struct LogStruct * logs;cd=&servicedata->connectiondataarray.pconnectiondata[cdindex];if(-1!=cd->log){logs=servicedata->memlogfile.logstruct+cd->log;}else{logs=NULL;}
//----------------------------------------struct ConfigInfo * pci;pci=&servicedata->serviceconfigfiledata.configarray[cd->serviceindex];int headlen;int port;char host[256];char uri[256];unsigned long addr;SOCKADDR_IN sa;BOOL isTunnel=FALSE;//是否是隧道请求char tunnelresponse[]="HTTP/1.0 200 Connection established\x0d\x0a""Proxy-agent: FreeProxy 1.0\x0d\x0a\x0d\x0a";//退出?if(CONNECTIONDATA_CMD_QUIT==cd->cmd){closesocket(cd->sdc.s);if(-1!=cd->log){logs->state=LOGSTRUCT_STATE_NOUSE;}cd->state=CONNECTION_NOUSE;return (DWORD)-1;}//接收请求cd->sdc.bufcount=RecvHttpRequest(cd->sdc.s,cd->sdc.buf,BUFFERSIZE,&cd->cmd,&headlen,pci->islimitpost,1000*pci->maxpost);if(0>cd->sdc.bufcount){//DebugMessage("RecvHttpRequest失败");closesocket(cd->sdc.s);if(-1!=cd->log){logs->state=LOGSTRUCT_STATE_NOUSE;}cd->state=CONNECTION_NOUSE;return (DWORD)-1;}//分析请求
/*	char tracertfile[256];if(-1!=mymemindex(cd->sdc.buf,cd->sdc.bufcount,"says=%2Fnick",strlen("says=%2Fnick"))){strcpy(tracertfile,"tracert_");itoa(cdindex,tracertfile+strlen(tracertfile),10);if(-1!=cd->log)WriteTracertFile(tracertfile,logs->username,strlen(logs->username));WriteTracertFile(tracertfile,cd->sdc.buf,cd->sdc.bufcount);}*/if(0>GetHttpURL(cd->sdc.buf,&cd->sdc.bufcount,headlen+4,host,256,&port,uri,256)){if(pci->isenableconnect && 0<=GetTunnelURL(cd->sdc.buf,&cd->sdc.bufcount,headlen+4,host,256,&port,uri,256)){//是隧道请求isTunnel=TRUE;if(-1!=cd->log){strcpy(logs->domainname,host);}}else{send(cd->sdc.s,httpresp400,strlen(httpresp400),0);closesocket(cd->sdc.s);if(-1!=cd->log){logs->state=LOGSTRUCT_STATE_NOUSE;}cd->state=CONNECTION_NOUSE;return (DWORD)-2;}}else{if(-1!=cd->log){strcpy(logs->domainname,host);}}ClearProxyInfo(cd->sdc.buf,&cd->sdc.bufcount);//检查目标许可if(IsForbidden(&theApp.bandata,host,uri)){send(cd->sdc.s,httpresp403,strlen(httpresp403),0);closesocket(cd->sdc.s);if(-1!=cd->log){logs->state=LOGSTRUCT_STATE_NOUSE;}cd->state=CONNECTION_NOUSE;return (DWORD)-1;}//退出?if(CONNECTIONDATA_CMD_QUIT==cd->cmd){closesocket(cd->sdc.s);if(-1!=cd->log){logs->state=LOGSTRUCT_STATE_NOUSE;}cd->state=CONNECTION_NOUSE;return (DWORD)-1;}//记录日志,计时开始if(-1!=cd->log){time(&logs->timestart);}//域名解析if(1!=GetAddrByHost(addr,host)){send(cd->sdc.s,httpresp600,strlen(httpresp600),0);closesocket(cd->sdc.s);if(-1!=cd->log){logs->state=LOGSTRUCT_STATE_NOUSE;}cd->state=CONNECTION_NOUSE;return (DWORD)-3;}memcpy(&(sa.sin_addr.S_un.S_addr),&addr,4);sa.sin_family=AF_INET;sa.sin_port=htons((unsigned short)port);//建立SOCKETif(INVALID_SOCKET==(cd->sdr.s=socket(AF_INET,SOCK_STREAM,0))){send(cd->sdc.s,httpresp601,strlen(httpresp601),0);closesocket(cd->sdc.s);if(-1!=cd->log){logs->state=LOGSTRUCT_STATE_NOUSE;}cd->state=CONNECTION_NOUSE;return (DWORD)-4;}//退出?if(CONNECTIONDATA_CMD_QUIT==cd->cmd){closesocket(cd->sdc.s);closesocket(cd->sdr.s);if(-1!=cd->log){logs->state=LOGSTRUCT_STATE_NOUSE;}cd->state=CONNECTION_NOUSE;return (DWORD)-1;}//连接if(SOCKET_ERROR==connect(cd->sdr.s,(struct sockaddr *)&sa,sizeof(sa))){send(cd->sdc.s,httpresp602,strlen(httpresp602),0);closesocket(cd->sdc.s);closesocket(cd->sdr.s);if(-1!=cd->log){logs->state=LOGSTRUCT_STATE_NOUSE;}cd->state=CONNECTION_NOUSE;return (DWORD)-5;}else{if(-1!=cd->log){strcpy(logs->domainname,uri);}}//退出?if(CONNECTIONDATA_CMD_QUIT==cd->cmd){closesocket(cd->sdc.s);closesocket(cd->sdr.s);if(-1!=cd->log){logs->state=LOGSTRUCT_STATE_NOUSE;}cd->state=CONNECTION_NOUSE;return (DWORD)-1;}//发送请求if(isTunnel){if(SOCKET_ERROR==send(cd->sdc.s,tunnelresponse,strlen(tunnelresponse),0)){send(cd->sdc.s,httpresp603,strlen(httpresp603),0);closesocket(cd->sdc.s);closesocket(cd->sdr.s);if(-1!=cd->log){logs->state=LOGSTRUCT_STATE_NOUSE;}cd->state=CONNECTION_NOUSE;return (DWORD)-6;}}if(SOCKET_ERROR==send(cd->sdr.s,cd->sdc.buf,cd->sdc.bufcount,0)){send(cd->sdc.s,httpresp603,strlen(httpresp603),0);closesocket(cd->sdc.s);closesocket(cd->sdr.s);if(-1!=cd->log){logs->state=LOGSTRUCT_STATE_NOUSE;}cd->state=CONNECTION_NOUSE;return (DWORD)-6;}//记录字节数if(-1!=cd->log){logs->bytecount+=cd->sdc.bufcount;}///TraceData(servicedata->isDataTrace,&servicedata->memlogfile.logdatatrace[cd->log].dc,cd->sdc.buf,cd->sdc.bufcount);//退出?if(CONNECTIONDATA_CMD_QUIT==cd->cmd){closesocket(cd->sdc.s);closesocket(cd->sdr.s);if(-1!=cd->log){logs->state=LOGSTRUCT_STATE_NOUSE;}cd->state=CONNECTION_NOUSE;return (DWORD)-1;}//接收数据并发给客户TransData(cd->sdr.s,cd->sdc.s,cd->sdr.buf,BUFFERSIZE,&cd->cmd,&cd->sdr.bufcount,servicedata,cd);//记录字节数if(-1!=cd->log){logs->bytecount+=cd->sdr.bufcount;}closesocket(cd->sdc.s);closesocket(cd->sdr.s);if(-1!=cd->log){time(&logs->timeend);logs->state=LOGSTRUCT_STATE_USED;}cd->state=CONNECTION_NOUSE;return 1;
}//接收HTTP请求(如果出错,不执行closesocket())
int RecvHttpRequest(SOCKET s,char * buf,int buflen,int * cmd,int* headlen,BOOL islimitpost,int maxpost)
{maxpost+=1;const char CRLF[]="\x0d\x0a";const char CRLFCRLF[]="\x0d\x0a\x0d\x0a";const char CONTENTLENGTH[]="Content-Length:";int recvcount=0;int temp;int recvall=0;BOOL tempbool;struct timeval timeout;timeout.tv_sec=0;timeout.tv_usec=100000;for(;1;){//退出?if(CONNECTIONDATA_CMD_QUIT==*cmd){return -1;}if(1!=IsSocketReadReady(s,timeout,tempbool)){return -2;}if(tempbool){recvcount=recv(s,buf+recvall,buflen-recvall,0);}else{continue;}if(SOCKET_ERROR==recvcount){return -3;}else if(0==recvcount){return -4;}recvall+=recvcount;//在使用后面代码段时使用CRLFCRLF,4,否则使用CRLF,2temp=mymemindex(buf,recvall,(char*)CRLFCRLF,4);if(-1!=temp){*headlen=temp;break;}}if(islimitpost && -1!=(temp=mymemindex(buf,*headlen,(char*)CONTENTLENGTH,15))){long i;char len[10];if(-1==(i=mymemindex(buf+temp,buflen-temp,(char*)CRLF,2))){return -5;}i-=strlen(CONTENTLENGTH);if(i>9){return -6;}memcpy(len,buf+temp+strlen(CONTENTLENGTH),i);len[i]='\0';i=atoi(len);if(i>maxpost){return -7;}}return recvall;
}//取得URL
int GetHttpURL(char* buf,int * buflenall,int buflen,char * host,int hostbuflen,int * port,char * uri,int uribuflen)
{const char CRLF[]="\x0d\x0a";int urlstart,urlend;int hoststart,hostend,hostlen;int portstart,portend,portlen;int pos;char str[10];urlend=mymemindex(buf,buflen,(char*)CRLF,2);if(-1==(urlstart=mymemindex(buf,urlend,"http://",7))){return -2;	}if(urlend-urlstart>=uribuflen){memcpy(uri,buf+urlstart,uribuflen-1);uri[uribuflen-1]='\0';}else{memcpy(uri,buf+urlstart,urlend-urlstart);uri[urlend-urlstart]='\0';}//得到主机名起始位置hoststart=urlstart+7;if(-1==(pos=mymemindex(buf+hoststart,urlend-hoststart,"/",1))){return -3;}portend=pos+hoststart;pos=mymemindex(buf+hoststart,portend-hoststart,":",1);if(-1!=pos)//有端口{portstart=pos+hoststart+1;//得到端口起始位置hostend=pos+hoststart;portlen=portend-portstart;memcpy(str,buf+portstart,portlen);str[portlen]='\0';if(0==portlen) *port=80;//若端口长度为零,实际上无端口{if(0==(*port=atoi(str)))return -4;}}else//无端口{*port=80;hostend=portend;}hostlen=hostend-hoststart;if(hostlen>=hostbuflen)return -5;memcpy(host,buf+hoststart,hostlen);host[hostlen]='\0';//HTTP请求处理long i;//降版本1.1为1.0if('1'==buf[urlend-1]){buf[urlend-1]='0';}//擦去URLi=portend-urlstart;memmove(buf+urlstart,buf+portend,*buflenall-portend);*buflenall-=i;return hostlen;
}//取得隧道请求
int GetTunnelURL(char* buf,int * buflenall,int buflen,char * host,int hostbuflen,int * port,char * uri,int uribuflen)
{const char CRLF[]="\x0d\x0a";int urlstart,urlend;int hoststart,hostend,hostlen;int portstart,portend,portlen;int pos;char str[10];urlend=mymemindex(buf,buflen,(char*)CRLF,2);if(buflen<8 || 0!=memcmp(buf,"CONNECT",7))return -2;if(' '!=buf[7])return -2;for(urlstart=8;urlstart<buflen;urlstart++){if(' '!=buf[urlstart])break;}if(urlend>=uribuflen){memcpy(uri,buf,uribuflen-1);uri[uribuflen-1]='\0';}else{memcpy(uri,buf,urlend);uri[urlend]='\0';}//得到主机名起始位置hoststart=urlstart;if(-1==(pos=mymemindex(buf+hoststart,urlend-hoststart,"/",1))){return -3;}portend=pos+hoststart;pos=mymemindex(buf+hoststart,portend-hoststart,":",1);if(-1!=pos)//有端口{portstart=pos+hoststart+1;//得到端口起始位置hostend=pos+hoststart;portlen=portend-portstart;memcpy(str,buf+portstart,portlen);str[portlen]='\0';if(0==portlen) *port=80;//若端口长度为零,实际上无端口{if(0==(*port=atoi(str)))return -4;}}else//无端口{*port=80;hostend=portend;}hostlen=hostend-hoststart;if(hostlen>=hostbuflen)return -5;memcpy(host,buf+hoststart,hostlen);host[hostlen]='\0';//HTTP请求处理*buflenall=0;return hostlen;
}//清除代理信息
int ClearProxyInfo(char * buf,int * buflenall)
{const char PROXYCONNECTION[]="Proxy-Connection";const char CRLF[]="\x0d\x0a";int i,j;if(2>(i=mymemindex(buf,*buflenall,PROXYCONNECTION,strlen(PROXYCONNECTION))))return 1;//前面至少应有一个CRLFif(0!=memcmp(buf+i-2,CRLF,2))return 1;if(-1==(j=mymemindex(buf+i+strlen(PROXYCONNECTION),(*buflenall)-i-strlen(PROXYCONNECTION),CRLF,2))){j=(*buflenall)-i-strlen(PROXYCONNECTION);}//擦去代理信息memmove(buf+i-2,buf+i+strlen(PROXYCONNECTION)+j,(*buflenall)-(i+strlen(PROXYCONNECTION)+j));*buflenall-=2+strlen(PROXYCONNECTION)+j;return 1;
}

        主要就是这么几件事:取出目标地址和端口,擦除目标信息,降低版本为1.0,擦除Proxy-XXXX头标,连接目标,双向转发数据。

        这个代码是从实际项目中截取出来的。

(这里是结束)

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

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

相关文章

在 Windows 平台上启动 MATLAB

目录 在 Windows 平台上启动 MATLAB 选择 MATLAB 图标 从 Windows 系统命令行调用 matlab 从 MATLAB 命令提示符调用 matlab 打开与 MATLAB 相关联的文件 从 Windows 资源管理器工具中选择 MATLAB 可执行文件 在 Windows 平台上启动 MATLAB 选择以下一种方式启动 MATLAB…

105AspectRatio调整宽高比组件_flutter

AspectRatio组件 AspectRatio 的作用是根据设置调整子元素 child 的宽高比。 AspectRatio 首先会在布局限制条件允许的范围内尽可能的扩展&#xff0c;widget 的高度是由宽 度和比率决定的&#xff0c;类似于 BoxFit 中的 contain&#xff0c;按照固定比率去尽量占满区域。 …

Java 继承与实现

一、继承&#xff08;extends&#xff09; 1.1 继承概念 继承是面向对象的基本特征&#xff0c;它允许子类继承父类的特征和行为&#xff0c;以提高代码的复用率和维护性等。下面一张图生动地展示了继承和类之间的关系&#xff1a; 继承图 上图中&#xff0c;“动物”、“食草…

数据结构与算法-栈

栈和队列是两种常用的线性结构&#xff0c;属于特殊的线性表&#xff0c;是线性表相关运算的一个子集。一般来说&#xff0c;线性表上的插入和删除操作不受任何限制&#xff0c;但栈只能在表的一端进行插入和删除操作&#xff0c;而队列则只能在一端进行插入操作&#xff0c;在…

物联网市场规模迅速增加,在交通、医疗、农业等方面发展势头迅猛

物联网&#xff08;Internet of things&#xff09;是一系列用于解决物的信息识别、交换、控制等技术的集合应用形成的网络。当连接从互联网时代的人与人走向万物互联&#xff0c;万物的数字化、智能化依赖物联网技术。因此&#xff0c;物联网是指利用各类信息识别设备&#xf…

Ceph介绍与部署

Ceph介绍与部署 一、存储基础1.1、单机存储设备1.1.1、单机存储的问题 1.2、商业存储解决方案1.3、分布式存储&#xff08;软件定义的存储 SDS&#xff09;1.3.1、分布式存储的类型 二、Ceph 简介三、Ceph 优势四、Ceph 架构五、Ceph 核心组件5.1、Pool中数据保存方式支持两种类…

Vue2实现图片预览功能 -- v-viewer:图片查看器

一. 先看效果图 二. 具体步骤 简介&#xff1a;一款基于 viewer.js 封装的Vue版插件&#xff0c;可用于图像查看&#xff0c;以及图片的旋转、缩放等功能预览 官网&#xff1a;v-viewer 文档说明&#xff1a;Vue图片浏览组件v-viewer&#xff0c;支持旋转、缩放、翻转等操作 - …

Ultra-Fast-Lane-Detection-v2 裁剪数据增强

目录 标注拆分为独立文件加载并数据增强 Ultra-Fast-Lane-Detection-v2 裁剪数据增强 main方法是调用示例

Android Studio的笔记--HttpURLConnection使用GET下载zip文件

HttpURLConnection使用GET下载zip文件 http get下载zip文件MainActivity.javaAndroidMainfest.xmlactivity_main.xmllog http get下载zip文件 MainActivity.java 用HttpURLConnection GET方法进行需注意&#xff1a; 1、Android 9及以上版本需要设置这个&#xff0c;否则会有…

网络爬虫实践小结

背景 近期工作中要解决两个问题&#xff0c;一个是数据组需要网爬一些图片数据&#xff0c;另外一个是要批量爬取公司用于文档协同的一个网站上的附件。于是乎&#xff0c;就写了两个脚本去完成任务。 爬虫思路 第一步&#xff1a;向确定的url发送请求&#xff0c;接收服务器…

[小林coding]4.2TCP重传,滑动窗口,流量控制,拥塞控制_1013

1.重传 1.1 超时重传 两个情况&#xff1a; a 数据包丢失 b ack应答丢失 RTT&#xff1a;网络包往返的时间&#xff08;不是一个定值&#xff09; RTO&#xff1a;超时重传的时间间隔&#xff08;也是一个动态的&#xff09; RTO设置的时间长&#xff1a;浪费时间资源&…

grafana api创建dashboard 记录

文章目录 json model导入申请api key创建dashboard删除dashboard json model导入 直接在ui通过json model 导入&#xff0c;开发自己用还好&#xff0c;但对非开发人员不太友好&#xff0c;故考虑通过api后台自动创建 api doc : https://grafana.com/docs/grafana/v9.3/devel…

AR动态贴纸SDK,让创作更加生动有趣

在当今的社交媒体时代&#xff0c;视频已经成为了人们表达自我、分享生活的重要方式。然而&#xff0c;如何让你的视频在众多的信息中脱颖而出&#xff0c;吸引更多的关注和点赞呢&#xff1f;答案可能就在你的手中——美摄AR动态贴纸SDK。 美摄AR动态贴纸SDK是一款专为视频编辑…

R语言——赋值(= ,<- ,<<-)

R语言 R语言——赋值&#xff08; &#xff0c;<- &#xff0c;<<-&#xff09; 文章目录 R语言一、 与 <- 的区别二、 <<- ,向上一环境层写入变量 R语言中" <- " 与 " " 都可以用来赋值&#xff0c;但R中建议使用" <- “…

ESP32网络开发实例-UDP数据发送与接收

UDP数据发送与接收 文章目录 UDP数据发送与接收1、UDP简单介绍2、软件准备3、硬件准备4、代码实现本文将详细介绍在Arduino开发环境中,如何实现ESP32通过UDP协议进行数据发送与接收。 1、UDP简单介绍 用户数据报协议 (UDP) 是一种跨互联网使用的通信协议,用于对时间敏感的传…

Linux开发工具:vim的介绍和用法及其简单配置

前言 Vim 简介. 编辑. Vim是从 vi 发展出来的一个文本编辑器。. 代码补全、编译及错误跳转等方便编程的功能特别丰富&#xff0c;在程序员中被广泛使用&#xff0c;和Emacs并列成为类Unix系统用户最喜欢的文本编辑器。. [1] vim的设计理念是命令的组合。. 用户学习了各种各样的…

【统计学概念】初学者指南:了解置信区间

一、说明 什么是置信区间&#xff1f;如何将概率转化成信心度&#xff1f;信心度如何去工作&#xff1f;这些初步的统计概念需要明晰&#xff0c;然后才能应用统计模型&#xff0c;然后是贝叶斯推理&#xff0c;我们将逐步深入这些概念。 二、总体与样本个体统计 总体是研究人…

【Unity】【VR】详解Oculus Integration输入

【背景】 以下内容适用于Oculus Integration开发VR场景,也就是OVR打头的Scripts,不适用于OpenXR开发场景,也就是XR打头Scripts。 【详解】 OVR的Input相对比较容易获取。重点在于区分不同动作机制的细节效果。 OVR Input的按键存在Button和RawButton两个系列 RawButton…

比较和同步数据库架构和数据:MssqlMerge Pro Crack

比较和同步数据库架构和数据 适用于Oracle、MySQL 和 MariaDB、SQL Server、PostgreSQL、SQLite、MS Access和跨 DBMS 场景 业界领先的文本比较工具中常用的两面板 UI 快速过滤器显示所有/新/更改/新更改 合并两个方向的更改 轻量级&#xff1a;跨 DBMS 工具小于 20 MB&#xf…

数据结构之手撕顺序表(增删查改等)

0.引言 在本章之后&#xff0c;就要求大家对于指针、结构体、动态开辟等相关的知识要熟练的掌握&#xff0c;如果有小伙伴对上面相关的知识还不是很清晰&#xff0c;要先弄明白再过来接着学习哦&#xff01; 那进入正题&#xff0c;在讲解顺序表之前&#xff0c;我们先来介绍…