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


    初级代码游戏的专栏介绍与文章目录-CSDN博客

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

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

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

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

    目录

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

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

            代理服务器认证

            隧道请求CONNECT

            代码示例


            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地址认证,在进入这个代码之前就已经处理过了):

    1. //servicethreadhttp.cpp
    2. #include "stdafx.h"
    3. #include "mystd.h"
    4. #include "Proxy.h"
    5. #include "httpresp.h"
    6. extern CProxyApp theApp;
    7. //HTTP协议处理线程
    8. DWORD ServiceThreadHttp(LPDWORD lpdwParam)
    9. {
    10. //--线程参数处理------------------------
    11. int cdindex;//连接数据索引
    12. struct ServiceData * servicedata;
    13. cdindex=((struct ThreadUserData *)lpdwParam)->index;
    14. servicedata=((struct ThreadUserData *)lpdwParam)->servicedata;
    15. //--------------------------------------
    16. struct ConnectionData * cd;
    17. struct LogStruct * logs;
    18. cd=&servicedata->connectiondataarray.pconnectiondata[cdindex];
    19. if(-1!=cd->log)
    20. {
    21. logs=servicedata->memlogfile.logstruct+cd->log;
    22. }
    23. else
    24. {
    25. logs=NULL;
    26. }
    27. //----------------------------------------
    28. struct ConfigInfo * pci;
    29. pci=&servicedata->serviceconfigfiledata.configarray[cd->serviceindex];
    30. int headlen;
    31. int port;
    32. char host[256];
    33. char uri[256];
    34. unsigned long addr;
    35. SOCKADDR_IN sa;
    36. BOOL isTunnel=FALSE;//是否是隧道请求
    37. char tunnelresponse[]="HTTP/1.0 200 Connection established\x0d\x0a"
    38. "Proxy-agent: FreeProxy 1.0\x0d\x0a\x0d\x0a";
    39. //退出?
    40. if(CONNECTIONDATA_CMD_QUIT==cd->cmd)
    41. {
    42. closesocket(cd->sdc.s);
    43. if(-1!=cd->log)
    44. {
    45. logs->state=LOGSTRUCT_STATE_NOUSE;
    46. }
    47. cd->state=CONNECTION_NOUSE;
    48. return (DWORD)-1;
    49. }
    50. //接收请求
    51. cd->sdc.bufcount=RecvHttpRequest(cd->sdc.s,cd->sdc.buf,BUFFERSIZE,
    52. &cd->cmd,&headlen,pci->islimitpost,1000*pci->maxpost);
    53. if(0>cd->sdc.bufcount)
    54. {
    55. //DebugMessage("RecvHttpRequest失败");
    56. closesocket(cd->sdc.s);
    57. if(-1!=cd->log)
    58. {
    59. logs->state=LOGSTRUCT_STATE_NOUSE;
    60. }
    61. cd->state=CONNECTION_NOUSE;
    62. return (DWORD)-1;
    63. }
    64. //分析请求
    65. /* char tracertfile[256];
    66. if(-1!=mymemindex(cd->sdc.buf,cd->sdc.bufcount,"says=%2Fnick",strlen("says=%2Fnick")))
    67. {
    68. strcpy(tracertfile,"tracert_");
    69. itoa(cdindex,tracertfile+strlen(tracertfile),10);
    70. if(-1!=cd->log)WriteTracertFile(tracertfile,logs->username,strlen(logs->username));
    71. WriteTracertFile(tracertfile,cd->sdc.buf,cd->sdc.bufcount);
    72. }*/
    73. if(0>GetHttpURL(cd->sdc.buf,&cd->sdc.bufcount,headlen+4,host,256,&port,uri,256))
    74. {
    75. if(pci->isenableconnect && 0<=GetTunnelURL(cd->sdc.buf,&cd->sdc.bufcount,headlen+4,host,256,&port,uri,256))
    76. {//是隧道请求
    77. isTunnel=TRUE;
    78. if(-1!=cd->log)
    79. {
    80. strcpy(logs->domainname,host);
    81. }
    82. }
    83. else
    84. {
    85. send(cd->sdc.s,httpresp400,strlen(httpresp400),0);
    86. closesocket(cd->sdc.s);
    87. if(-1!=cd->log)
    88. {
    89. logs->state=LOGSTRUCT_STATE_NOUSE;
    90. }
    91. cd->state=CONNECTION_NOUSE;
    92. return (DWORD)-2;
    93. }
    94. }
    95. else
    96. {
    97. if(-1!=cd->log)
    98. {
    99. strcpy(logs->domainname,host);
    100. }
    101. }
    102. ClearProxyInfo(cd->sdc.buf,&cd->sdc.bufcount);
    103. //检查目标许可
    104. if(IsForbidden(&theApp.bandata,host,uri))
    105. {
    106. send(cd->sdc.s,httpresp403,strlen(httpresp403),0);
    107. closesocket(cd->sdc.s);
    108. if(-1!=cd->log)
    109. {
    110. logs->state=LOGSTRUCT_STATE_NOUSE;
    111. }
    112. cd->state=CONNECTION_NOUSE;
    113. return (DWORD)-1;
    114. }
    115. //退出?
    116. if(CONNECTIONDATA_CMD_QUIT==cd->cmd)
    117. {
    118. closesocket(cd->sdc.s);
    119. if(-1!=cd->log)
    120. {
    121. logs->state=LOGSTRUCT_STATE_NOUSE;
    122. }
    123. cd->state=CONNECTION_NOUSE;
    124. return (DWORD)-1;
    125. }
    126. //记录日志,计时开始
    127. if(-1!=cd->log)
    128. {
    129. time(&logs->timestart);
    130. }
    131. //域名解析
    132. if(1!=GetAddrByHost(addr,host))
    133. {
    134. send(cd->sdc.s,httpresp600,strlen(httpresp600),0);
    135. closesocket(cd->sdc.s);
    136. if(-1!=cd->log)
    137. {
    138. logs->state=LOGSTRUCT_STATE_NOUSE;
    139. }
    140. cd->state=CONNECTION_NOUSE;
    141. return (DWORD)-3;
    142. }
    143. memcpy(&(sa.sin_addr.S_un.S_addr),&addr,4);
    144. sa.sin_family=AF_INET;
    145. sa.sin_port=htons((unsigned short)port);
    146. //建立SOCKET
    147. if(INVALID_SOCKET==(cd->sdr.s=socket(AF_INET,SOCK_STREAM,0)))
    148. {
    149. send(cd->sdc.s,httpresp601,strlen(httpresp601),0);
    150. closesocket(cd->sdc.s);
    151. if(-1!=cd->log)
    152. {
    153. logs->state=LOGSTRUCT_STATE_NOUSE;
    154. }
    155. cd->state=CONNECTION_NOUSE;
    156. return (DWORD)-4;
    157. }
    158. //退出?
    159. if(CONNECTIONDATA_CMD_QUIT==cd->cmd)
    160. {
    161. closesocket(cd->sdc.s);
    162. closesocket(cd->sdr.s);
    163. if(-1!=cd->log)
    164. {
    165. logs->state=LOGSTRUCT_STATE_NOUSE;
    166. }
    167. cd->state=CONNECTION_NOUSE;
    168. return (DWORD)-1;
    169. }
    170. //连接
    171. if(SOCKET_ERROR==connect(cd->sdr.s,(struct sockaddr *)&sa,sizeof(sa)))
    172. {
    173. send(cd->sdc.s,httpresp602,strlen(httpresp602),0);
    174. closesocket(cd->sdc.s);
    175. closesocket(cd->sdr.s);
    176. if(-1!=cd->log)
    177. {
    178. logs->state=LOGSTRUCT_STATE_NOUSE;
    179. }
    180. cd->state=CONNECTION_NOUSE;
    181. return (DWORD)-5;
    182. }
    183. else
    184. {
    185. if(-1!=cd->log)
    186. {
    187. strcpy(logs->domainname,uri);
    188. }
    189. }
    190. //退出?
    191. if(CONNECTIONDATA_CMD_QUIT==cd->cmd)
    192. {
    193. closesocket(cd->sdc.s);
    194. closesocket(cd->sdr.s);
    195. if(-1!=cd->log)
    196. {
    197. logs->state=LOGSTRUCT_STATE_NOUSE;
    198. }
    199. cd->state=CONNECTION_NOUSE;
    200. return (DWORD)-1;
    201. }
    202. //发送请求
    203. if(isTunnel)
    204. {
    205. if(SOCKET_ERROR==send(cd->sdc.s,tunnelresponse,strlen(tunnelresponse),0))
    206. {
    207. send(cd->sdc.s,httpresp603,strlen(httpresp603),0);
    208. closesocket(cd->sdc.s);
    209. closesocket(cd->sdr.s);
    210. if(-1!=cd->log)
    211. {
    212. logs->state=LOGSTRUCT_STATE_NOUSE;
    213. }
    214. cd->state=CONNECTION_NOUSE;
    215. return (DWORD)-6;
    216. }
    217. }
    218. if(SOCKET_ERROR==send(cd->sdr.s,cd->sdc.buf,cd->sdc.bufcount,0))
    219. {
    220. send(cd->sdc.s,httpresp603,strlen(httpresp603),0);
    221. closesocket(cd->sdc.s);
    222. closesocket(cd->sdr.s);
    223. if(-1!=cd->log)
    224. {
    225. logs->state=LOGSTRUCT_STATE_NOUSE;
    226. }
    227. cd->state=CONNECTION_NOUSE;
    228. return (DWORD)-6;
    229. }
    230. //记录字节数
    231. if(-1!=cd->log)
    232. {
    233. logs->bytecount+=cd->sdc.bufcount;
    234. }
    235. ///
    236. TraceData(servicedata->isDataTrace,&servicedata->memlogfile.logdatatrace[cd->log].dc,cd->sdc.buf,cd->sdc.bufcount);
    237. //退出?
    238. if(CONNECTIONDATA_CMD_QUIT==cd->cmd)
    239. {
    240. closesocket(cd->sdc.s);
    241. closesocket(cd->sdr.s);
    242. if(-1!=cd->log)
    243. {
    244. logs->state=LOGSTRUCT_STATE_NOUSE;
    245. }
    246. cd->state=CONNECTION_NOUSE;
    247. return (DWORD)-1;
    248. }
    249. //接收数据并发给客户
    250. TransData(cd->sdr.s,cd->sdc.s,cd->sdr.buf,BUFFERSIZE,&cd->cmd,&cd->sdr.bufcount,servicedata,cd);
    251. //记录字节数
    252. if(-1!=cd->log)
    253. {
    254. logs->bytecount+=cd->sdr.bufcount;
    255. }
    256. closesocket(cd->sdc.s);
    257. closesocket(cd->sdr.s);
    258. if(-1!=cd->log)
    259. {
    260. time(&logs->timeend);
    261. logs->state=LOGSTRUCT_STATE_USED;
    262. }
    263. cd->state=CONNECTION_NOUSE;
    264. return 1;
    265. }
    266. //接收HTTP请求(如果出错,不执行closesocket())
    267. int RecvHttpRequest(SOCKET s,char * buf,int buflen,int * cmd,int* headlen,BOOL islimitpost,int maxpost)
    268. {
    269. maxpost+=1;
    270. const char CRLF[]="\x0d\x0a";
    271. const char CRLFCRLF[]="\x0d\x0a\x0d\x0a";
    272. const char CONTENTLENGTH[]="Content-Length:";
    273. int recvcount=0;
    274. int temp;
    275. int recvall=0;
    276. BOOL tempbool;
    277. struct timeval timeout;
    278. timeout.tv_sec=0;
    279. timeout.tv_usec=100000;
    280. for(;1;)
    281. {
    282. //退出?
    283. if(CONNECTIONDATA_CMD_QUIT==*cmd)
    284. {
    285. return -1;
    286. }
    287. if(1!=IsSocketReadReady(s,timeout,tempbool))
    288. {
    289. return -2;
    290. }
    291. if(tempbool)
    292. {
    293. recvcount=recv(s,buf+recvall,buflen-recvall,0);
    294. }
    295. else
    296. {
    297. continue;
    298. }
    299. if(SOCKET_ERROR==recvcount)
    300. {
    301. return -3;
    302. }
    303. else if(0==recvcount)
    304. {
    305. return -4;
    306. }
    307. recvall+=recvcount;
    308. //在使用后面代码段时使用CRLFCRLF,4,否则使用CRLF,2
    309. temp=mymemindex(buf,recvall,(char*)CRLFCRLF,4);
    310. if(-1!=temp)
    311. {
    312. *headlen=temp;
    313. break;
    314. }
    315. }
    316. if(islimitpost && -1!=(temp=mymemindex(buf,*headlen,(char*)CONTENTLENGTH,15)))
    317. {
    318. long i;
    319. char len[10];
    320. if(-1==(i=mymemindex(buf+temp,buflen-temp,(char*)CRLF,2)))
    321. {
    322. return -5;
    323. }
    324. i-=strlen(CONTENTLENGTH);
    325. if(i>9)
    326. {
    327. return -6;
    328. }
    329. memcpy(len,buf+temp+strlen(CONTENTLENGTH),i);
    330. len[i]='\0';
    331. i=atoi(len);
    332. if(i>maxpost)
    333. {
    334. return -7;
    335. }
    336. }
    337. return recvall;
    338. }
    339. //取得URL
    340. int GetHttpURL(char* buf,int * buflenall,int buflen,char * host,int hostbuflen,int * port,char * uri,int uribuflen)
    341. {
    342. const char CRLF[]="\x0d\x0a";
    343. int urlstart,urlend;
    344. int hoststart,hostend,hostlen;
    345. int portstart,portend,portlen;
    346. int pos;
    347. char str[10];
    348. urlend=mymemindex(buf,buflen,(char*)CRLF,2);
    349. if(-1==(urlstart=mymemindex(buf,urlend,"http://",7)))
    350. {
    351. return -2;
    352. }
    353. if(urlend-urlstart>=uribuflen)
    354. {
    355. memcpy(uri,buf+urlstart,uribuflen-1);
    356. uri[uribuflen-1]='\0';
    357. }
    358. else
    359. {
    360. memcpy(uri,buf+urlstart,urlend-urlstart);
    361. uri[urlend-urlstart]='\0';
    362. }
    363. //得到主机名起始位置
    364. hoststart=urlstart+7;
    365. if(-1==(pos=mymemindex(buf+hoststart,urlend-hoststart,"/",1)))
    366. {
    367. return -3;
    368. }
    369. portend=pos+hoststart;
    370. pos=mymemindex(buf+hoststart,portend-hoststart,":",1);
    371. if(-1!=pos)//有端口
    372. {
    373. portstart=pos+hoststart+1;//得到端口起始位置
    374. hostend=pos+hoststart;
    375. portlen=portend-portstart;
    376. memcpy(str,buf+portstart,portlen);
    377. str[portlen]='\0';
    378. if(0==portlen) *port=80;//若端口长度为零,实际上无端口
    379. {
    380. if(0==(*port=atoi(str)))
    381. return -4;
    382. }
    383. }
    384. else//无端口
    385. {
    386. *port=80;
    387. hostend=portend;
    388. }
    389. hostlen=hostend-hoststart;
    390. if(hostlen>=hostbuflen)
    391. return -5;
    392. memcpy(host,buf+hoststart,hostlen);
    393. host[hostlen]='\0';
    394. //HTTP请求处理
    395. long i;
    396. //降版本1.1为1.0
    397. if('1'==buf[urlend-1])
    398. {
    399. buf[urlend-1]='0';
    400. }
    401. //擦去URL
    402. i=portend-urlstart;
    403. memmove(buf+urlstart,buf+portend,*buflenall-portend);
    404. *buflenall-=i;
    405. return hostlen;
    406. }
    407. //取得隧道请求
    408. int GetTunnelURL(char* buf,int * buflenall,int buflen,char * host,int hostbuflen,int * port,char * uri,int uribuflen)
    409. {
    410. const char CRLF[]="\x0d\x0a";
    411. int urlstart,urlend;
    412. int hoststart,hostend,hostlen;
    413. int portstart,portend,portlen;
    414. int pos;
    415. char str[10];
    416. urlend=mymemindex(buf,buflen,(char*)CRLF,2);
    417. if(buflen<8 || 0!=memcmp(buf,"CONNECT",7))
    418. return -2;
    419. if(' '!=buf[7])
    420. return -2;
    421. for(urlstart=8;urlstart
    422. {
    423. if(' '!=buf[urlstart])
    424. break;
    425. }
    426. if(urlend>=uribuflen)
    427. {
    428. memcpy(uri,buf,uribuflen-1);
    429. uri[uribuflen-1]='\0';
    430. }
    431. else
    432. {
    433. memcpy(uri,buf,urlend);
    434. uri[urlend]='\0';
    435. }
    436. //得到主机名起始位置
    437. hoststart=urlstart;
    438. if(-1==(pos=mymemindex(buf+hoststart,urlend-hoststart,"/",1)))
    439. {
    440. return -3;
    441. }
    442. portend=pos+hoststart;
    443. pos=mymemindex(buf+hoststart,portend-hoststart,":",1);
    444. if(-1!=pos)//有端口
    445. {
    446. portstart=pos+hoststart+1;//得到端口起始位置
    447. hostend=pos+hoststart;
    448. portlen=portend-portstart;
    449. memcpy(str,buf+portstart,portlen);
    450. str[portlen]='\0';
    451. if(0==portlen) *port=80;//若端口长度为零,实际上无端口
    452. {
    453. if(0==(*port=atoi(str)))
    454. return -4;
    455. }
    456. }
    457. else//无端口
    458. {
    459. *port=80;
    460. hostend=portend;
    461. }
    462. hostlen=hostend-hoststart;
    463. if(hostlen>=hostbuflen)
    464. return -5;
    465. memcpy(host,buf+hoststart,hostlen);
    466. host[hostlen]='\0';
    467. //HTTP请求处理
    468. *buflenall=0;
    469. return hostlen;
    470. }
    471. //清除代理信息
    472. int ClearProxyInfo(char * buf,int * buflenall)
    473. {
    474. const char PROXYCONNECTION[]="Proxy-Connection";
    475. const char CRLF[]="\x0d\x0a";
    476. int i,j;
    477. if(2>(i=mymemindex(buf,*buflenall,PROXYCONNECTION,strlen(PROXYCONNECTION))))return 1;//前面至少应有一个CRLF
    478. if(0!=memcmp(buf+i-2,CRLF,2))return 1;
    479. if(-1==(j=mymemindex(buf+i+strlen(PROXYCONNECTION),(*buflenall)-i-strlen(PROXYCONNECTION),CRLF,2)))
    480. {
    481. j=(*buflenall)-i-strlen(PROXYCONNECTION);
    482. }
    483. //擦去代理信息
    484. memmove(buf+i-2,buf+i+strlen(PROXYCONNECTION)+j,(*buflenall)-(i+strlen(PROXYCONNECTION)+j));
    485. *buflenall-=2+strlen(PROXYCONNECTION)+j;
    486. return 1;
    487. }

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

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

    (这里是结束)

  • 相关阅读:
    数据结构和算法:分治
    【硬件通讯协议】SIP总线协议以及模拟(软件)SPI
    原神私服搭建服务器配置该如何选择
    美国疾控中心:持续减肥,降低32%患癌风险,降低48%癌死亡风险
    这些傻白甜的Linux命令,不会有人教你!
    计算机四级网络-网络技术-第六章 网络管理与网络安全
    gin框架初识
    SHAP 和 LIME 解释模型
    总结springboot启动jar,指定配置文件
    python排序算法(六)、希尔排序
  • 原文地址:https://blog.csdn.net/2301_77171572/article/details/133803245