整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

工具-获取HTTP接口服务的各个时间

工具-获取HTTP接口服务的各个时间

当我们访问一个网站或者网站接口服务,想知道"时间都去哪里了",这时候我们借助工具来分析。

可以将时间大致分为两部分:一部分是从我们请求到网站服务端所经历的耗时,另一部分是服务端自身处理该服务完毕后响应回来的时间,这些都是可以作为后面结果分析的判断依据。


下面将介绍两个方案,仅供参考。

开始计时


第一种方案:


使用系统curl命令模拟网站服务请求,得到各个时间段的时间。

需要注意的是:请确保curl 是最新版本,否则一些参数选项无法使用。


例子:

$ curl -w "Result: \n dnslookup: %{time_namelookup} \n connect: %{time_connect} \n appconnect: %{time_appconnect} \n pretransfer: %{time_pretransfer} \n starttransfer: %{time_starttransfer} \n total: %{time_total} \n ----\n time_redirect: %{time_redirect} \n ----\n size: %{size_download}\n" "https://www.baidu.com"
<!DOCTYPE html>
<!--STATUS OK--><html> <head><meta http-equiv=content-type content=text/html;charset=utf-8><meta http-equiv=X-UA-Compatible content=IE=Edge><meta content=always name=referrer><link rel=stylesheet type=text/css href=https://ss1.bdstatic.com/5eN1bjq8AAUYm2zgoY3K/r/www/cache/bdorz/baidu.min.css><title>百度一下,你就知道</title></head> <body link=#0000cc> <div id=wrapper> <div id=head> <div class=head_wrapper> <div class=s_form> <div class=s_form_wrapper> <div id=lg> <img hidefocus=true src=//www.baidu.com/img/bd_logo1.png width=270 height=129> </div> <form id=form name=f action=//www.baidu.com/s class=fm> <input type=hidden name=bdorz_come value=1> <input type=hidden name=ie value=utf-8> <input type=hidden name=f value=8> <input type=hidden name=rsv_bp value=1> <input type=hidden name=rsv_idx value=1> <input type=hidden name=tn value=baidu><span class="bg s_ipt_wr"><input id=kw name=wd class=s_ipt value maxlength=255 autocomplete=off autofocus=autofocus></span><span class="bg s_btn_wr"><input type=submit id=su value=百度一下 class="bg s_btn" autofocus></span> </form> </div> </div> <div id=u1> <a href=http://news.baidu.com name=tj_trnews class=mnav>新闻</a> <a href=https://www.hao123.com name=tj_trhao123 class=mnav>hao123</a> <a href=http://map.baidu.com name=tj_trmap class=mnav>地图</a> <a href=http://v.baidu.com name=tj_trvideo class=mnav>视频</a> <a href=http://tieba.baidu.com name=tj_trtieba class=mnav>贴吧</a> <noscript> <a href=http://www.hmttv.cn/uploadfile/2024/1011/20241011104128743.gif?login&tpl=mn&u=http%3A%2F%2Fwww.baidu.com%2f%3fbdorz_come%3d1 name=tj_login class=lb>登录</a> </noscript> <script>document.write('<a href="http://www.hmttv.cn/uploadfile/2024/1011/20241011104128743.gif?login&tpl=mn&u='+ encodeURIComponent(window.location.href+ (window.location.search==="" ? "?" : "&")+ "bdorz_come=1")+ '" name="tj_login" class="lb">登录</a>');
                </script> <a href=//www.baidu.com/more/ name=tj_briicon class=bri style="display: block;">更多产品</a> </div> </div> </div> <div id=ftCon> <div id=ftConw> <p id=lh> <a href=http://home.baidu.com>关于百度</a> <a href=http://ir.baidu.com>About Baidu</a> </p> <p id=cp>?2017 Baidu <a href=http://www.baidu.com/duty/>使用百度前必读</a>  <a href=http://jianyi.baidu.com/ class=cp-feedback>意见反馈</a> 京ICP证030173号  <img src=//www.baidu.com/img/gs.gif> </p> </div> </div> </div> </body> </html>
Result: 
 dnslookup: 0.005 
 connect: 0.009 
 appconnect: 0.128 
 pretransfer: 0.128 
 starttransfer: 0.134 
 total: 0.134 
 ----
 time_redirect: 0.000 
 ----
 size: 2443


参数:

  • time_namelookup:DNS解析时间,从请求开始到DNS解析完毕所用时间
  • time_connect:建立到服务器的 TCP 连接所用的时间
  • time_appconnect:从起始到应用侧(SSL)连接/握手完成的耗时。(在7.19.0 版加入)
  • time_pretransfer:从开始至准备开始传输数据的时间
  • time_starttransfer:在发出请求之后,Web 服务器返回数据的第一个字节所用的时间
  • time_total:全部操作耗费的时间,单位为秒。精确到毫秒。
  • size_download:下载的总字节数。
  • speed_download:下载速度,单位-字节每秒。

从上面的例子可以看到

  1. DNS解析耗时:0.005秒
  2. TCP建立连接的耗时:(0.009-0.005)=0.004秒
  3. SSL握手完成耗时:(0.128-0.009)=0.119秒
  4. server处理数据的时间:(0.134-0.128)=0.006秒
  5. 总体的耗时:0.134秒
  6. 整个过程没有redirect,所以redirect的耗时为0


上面还有个小技巧:


$ cat ~/.curlrc 
-w "Result: \n dnslookup: %{time_namelookup} \n connect: %{time_connect} \n appconnect: %{time_appconnect} \n pretransfer: %{time_pretransfer} \n starttransfer: %{time_starttransfer} \n total: %{time_total} \n ----\n time_redirect: %{time_redirect} \n ----\n size: %{size_download}\n"


$ curl "https://www.baidu.com"


我们把-w参数的值写到curl配置文件~/.curlrc,这样我们按照之前的curl命令访问具体网址就可以得到各个时间。


第二种方案:


当你拥有Python的环境时,可以借助一个库,可以方便清晰的了解耗时,这个库就是httpstat。


注意:该工具还是依赖底层curl命令,所以确保curl是最新版本。


这里我以Python3环境为例,介绍下如何使用。


  • 安装库
$ pip3 install httpstat


  • 检查httpstat命令是否存在
$ which httpstat
~/3rd/Python-3.7.4/bin/httpstat


  • 访问网站
$ httpstat "https://www.baidu.com"
Connected to 163.177.151.110:443 from 10.10.10.10:41350

HTTP/1.1 200 OK
Accept-Ranges: bytes
Cache-Control: private, no-cache, no-store, proxy-revalidate, no-transform
Connection: keep-alive
Content-Length: 2443
Content-Type: text/html
Date: Mon, 25 May 2020 03:14:53 GMT
Etag: "58860402-98b"
Last-Modified: Mon, 23 Jan 2017 13:24:18 GMT
Pragma: no-cache
Server: bfe/1.0.8.18
Set-Cookie: BDORZ=27315; max-age=86400; domain=.baidu.com; path=/

Body stored in: /tmp/tmp35tdqpkp

  DNS Lookup   TCP Connection   TLS Handshake   Server Processing   Content Transfer
[    41ms    |       5ms      |     45ms      |        6ms        |        1ms       ]
             |                |               |                   |                  |
    namelookup:41ms           |               |                   |                  |
                        connect:46ms          |                   |                  |
                                    pretransfer:91ms              |                  |
                                                      starttransfer:97ms             |
                                                                                 total:98ms   


是不是很直观? so easy。


总结:


  • 如果你不想用python,那么第一种方案最直接高效。
  • 如果你想用python,那么第二种方案显示效果最佳。


今天你get 到了吗?喜欢的话,关注收藏下。

GINX主要设计作为反向代理服务器,但随着NGINX的发展,它同样能作为正向代理的选项之一。正向代理本身并不复杂,而如何代理加密的HTTPS流量是正向代理需要解决的主要问题。本文将介绍利用NGINX来正向代理HTTPS流量两种方案,及其使用场景和主要问题。

HTTP/HTTPS正向代理的分类

简单介绍下正向代理的分类作为理解下文的背景知识:

按客户端有无感知的分类

  • 普通代理:在客户端需要在浏览器中或者系统环境变量手动设置代理的地址和端口。如squid,在客户端指定squid服务器IP和端口3128。
  • 透明代理:客户端不需要做任何代理设置,“代理”这个角色对于客户端是透明的。如企业网络链路中的Web Gateway设备。

按代理是否解密HTTPS的分类

  • 隧道代理 :也就是透传代理。代理服务器只是在TCP协议上透传HTTPS流量,对于其代理的流量的具体内容不解密不感知。客户端和其访问的目的服务器做直接TLS/SSL交互。本文中讨论的NGINX代理方式属于这种模式。
  • 中间人(MITM, Man-in-the-Middle)代理:代理服务器解密HTTPS流量,对客户端利用自签名证书完成TLS/SSL握手,对目的服务器端完成正常TLS交互。在客户端-代理-服务器的链路中建立两段TLS/SSL会话。如Charles,简单原理描述可以参考文章。

https://www.jianshu.com/p/405f9d76f8c4

  • 注:这种情况客户端在TLS握手阶段实际上是拿到的代理服务器自己的自签名证书,证书链的验证默认不成功,需要在客户端信任代理自签证书的Root CA证书。所以过程中是客户端有感的。如果要做成无感的透明代理,需要向客户端推送自建的Root CA证书,在企业内部环境下是可实现的。

为什么正向代理处理HTTPS流量需要特殊处理?

作为反向代理时,代理服务器通常终结 (terminate) HTTPS加密流量,再转发给后端实例。HTTPS流量的加解密和认证过程发生在客户端和反向代理服务器之间。

而作为正向代理在处理客户端发过来的流量时,HTTP加密封装在了TLS/SSL中,代理服务器无法看到客户端请求URL中想要访问的域名,如下图。所以代理HTTPS流量,相比于HTTP,需要做一些特殊处理。

NGINX的解决方案

根据前文中的分类方式,NGINX解决HTTPS代理的方式都属于透传(隧道)模式,即不解密不感知上层流量。具体的方式有如下7层和4层的两类解决方案。

HTTP CONNECT隧道 (7层解决方案)

历史背景

早在1998年,也就是TLS还没有正式诞生的SSL时代,主导SSL协议的Netscape公司就提出了关于利用web代理来tunneling SSL流量的INTERNET-DRAFT。其核心思想就是利用HTTP CONNECT请求在客户端和代理之间建立一个HTTP CONNECT Tunnel,在CONNECT请求中需要指定客户端需要访问的目的主机和端口。Draft中的原图如下:

整个过程可以参考HTTP权威指南中的图:

  1. 客户端给代理服务器发送HTTP CONNECT请求。
  2. 代理服务器利用HTTP CONNECT请求中的主机和端口与目的服务器建立TCP连接。
  3. 代理服务器给客户端返回HTTP 200响应。
  4. 客户端和代理服务器建立起HTTP CONNECT隧道,HTTPS流量到达代理服务器后,直接通过TCP透传给远端目的服务器。代理服务器的角色是透传HTTPS流量,并不需要解密HTTPS。

NGINX ngx_http_proxy_connect_module模块

NGINX作为反向代理服务器,官方一直没有支持HTTP CONNECT方法。但是基于NGINX的模块化、可扩展性好的特性,阿里的@chobits提供了ngx_http_proxy_connect_module模块,来支持HTTP CONNECT方法,从而让NGINX可以扩展为正向代理。

环境搭建

以CentOS 7的环境为例。

1) 安装

对于新安装的环境,参考正常的安装步骤和安装这个模块的步骤(https://github.com/chobits/ngx_http_proxy_connect_module),把对应版本的patch打上之后,在configure的时候加上参数--add-module=/path/to/ngx_http_proxy_connect_module,示例如下:

./configure \
--user=www \
--group=www \
--prefix=/usr/local/nginx \
--with-http_ssl_module \
--with-http_stub_status_module \
--with-http_realip_module \
--with-threads \
--add-module=/root/src/ngx_http_proxy_connect_module

对于已经安装编译安装完的环境,需要加入以上模块,步骤如下:

# 停止NGINX服务
# systemctl stop nginx
# 备份原执行文件
# cp /usr/local/nginx/sbin/nginx /usr/local/nginx/sbin/nginx.bak
# 在源代码路径重新编译
# cd /usr/local/src/nginx-1.16.0
./configure \
--user=www \
--group=www \
--prefix=/usr/local/nginx \
--with-http_ssl_module \
--with-http_stub_status_module \
--with-http_realip_module \
--with-threads \
--add-module=/root/src/ngx_http_proxy_connect_module
# make
# 不要make install
# 将新生成的可执行文件拷贝覆盖原来的nginx执行文件
# cp objs/nginx /usr/local/nginx/sbin/nginx
# /usr/bin/nginx -V
nginx version: nginx/1.16.0
built by gcc 4.8.5 20150623 (Red Hat 4.8.5-36) (GCC)
built with OpenSSL 1.0.2k-fips 26 Jan 2017
TLS SNI support enabled
configure arguments: --user=www --group=www --prefix=/usr/local/nginx --with-http_ssl_module --with-http_stub_status_module --with-http_realip_module --with-threads --add-module=/root/src/ngx_http_proxy_connect_module

2) nginx.conf文件配置

server {
 listen 443;
 
 # dns resolver used by forward proxying
 resolver 114.114.114.114;
 # forward proxy for CONNECT request
 proxy_connect;
 proxy_connect_allow 443;
 proxy_connect_connect_timeout 10s;
 proxy_connect_read_timeout 10s;
 proxy_connect_send_timeout 10s;
 # forward proxy for non-CONNECT request
 location / {
 proxy_pass http://$host;
 proxy_set_header Host $host;
 }
 }

使用场景

7层需要通过HTTP CONNECT来建立隧道,属于客户端有感知的普通代理方式,需要在客户端手动配置HTTP(S)代理服务器IP和端口。在客户端用curl 加-x参数访问如下:

# curl https://www.baidu.com -svo /dev/null -x 39.105.196.164:443
* About to connect() to proxy 39.105.196.164 port 443 (#0)
* Trying 39.105.196.164...
* Connected to 39.105.196.164 (39.105.196.164) port 443 (#0)
* Establish HTTP proxy tunnel to www.baidu.com:443
> CONNECT www.baidu.com:443 HTTP/1.1
> Host: www.baidu.com:443
> User-Agent: curl/7.29.0
> Proxy-Connection: Keep-Alive
>
< HTTP/1.1 200 Connection Established
< Proxy-agent: nginx
<
* Proxy replied OK to CONNECT request
* Initializing NSS with certpath: sql:/etc/pki/nssdb
* CAfile: /etc/pki/tls/certs/ca-bundle.crt
 CApath: none
* SSL connection using TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
* Server certificate:
* subject: CN=baidu.com,O="Beijing Baidu Netcom Science Technology Co., Ltd",OU=service operation department,L=beijing,ST=beijing,C=CN
...
> GET / HTTP/1.1
> User-Agent: curl/7.29.0
> Host: www.baidu.com
> Accept: */*
>
< HTTP/1.1 200 OK
...
{ [data not shown]

从上面-v参数打印出的细节,可以看到客户端先往代理服务器39.105.196.164建立了HTTP CONNECT隧道,代理回复HTTP/1.1 200 Connection Established后就开始交互TLS/SSL握手和流量了。

NGINX stream (4层解决方案)

既然是使用透传上层流量的方法,那可不可做成“4层代理”,对TCP/UDP以上的协议实现彻底的透传呢?答案是可以的。NGINX官方从1.9.0版本开始支持ngx_stream_core_module模块,模块默认不build,需要configure时加上--with-stream选项来开启。

问题

用NGINX stream在TCP层面上代理HTTPS流量肯定会遇到本文一开始提到的那个问题:代理服务器无法获取客户端想要访问的目的域名。因为在TCP的层面获取的信息仅限于IP和端口层面,没有任何机会拿到域名信息。要拿到目的域名,必须要有拆上层报文获取域名信息的能力,所以NGINX stream的方式不是完全严格意义上的4层代理,还是要略微借助些上层能力。

ngx_stream_ssl_preread_module模块

要在不解密的情况下拿到HTTPS流量访问的域名,只有利用TLS/SSL握手的第一个Client Hello报文中的扩展地址SNI (Server Name Indication)来获取。NGINX官方从1.11.5版本开始支持利用ngx_stream_ssl_preread_module模块来获得这个能力,模块主要用于获取Client Hello报文中的SNI和ALPN信息。对于4层正向代理来说,从Client Hello报文中提取SNI的能力是至关重要的,否则NGINX stream的解决方案无法成立。同时这也带来了一个限制,要求所有客户端都需要在TLS/SSL握手中带上SNI字段,否则NGINX stream代理完全没办法知道客户端需要访问的目的域名。

环境搭建

1) 安装

对于新安装的环境,参考正常的安装步骤,直接在configure的时候加上--with-stream,--with-stream_ssl_preread_module和--with-stream_ssl_module选项即可。示例如下:

./configure \
--user=www \
--group=www \
--prefix=/usr/local/nginx \
--with-http_ssl_module \
--with-http_stub_status_module \
--with-http_realip_module \
--with-threads \
--with-stream \
--with-stream_ssl_preread_module \
--with-stream_ssl_module

对于已经安装编译安装完的环境,需要加入以上3个与stream相关的模块,步骤如下:

# 停止NGINX服务
# systemctl stop nginx
# 备份原执行文件
# cp /usr/local/nginx/sbin/nginx /usr/local/nginx/sbin/nginx.bak
# 在源代码路径重新编译
# cd /usr/local/src/nginx-1.16.0
# ./configure \
--user=www \
--group=www \
--prefix=/usr/local/nginx \
--with-http_ssl_module \
--with-http_stub_status_module \
--with-http_realip_module \
--with-threads \
--with-stream \
--with-stream_ssl_preread_module \
--with-stream_ssl_module
# make
# 不要make install
# 将新生成的可执行文件拷贝覆盖原来的nginx执行文件
# cp objs/nginx /usr/local/nginx/sbin/nginx
# nginx -V
nginx version: nginx/1.16.0
built by gcc 4.8.5 20150623 (Red Hat 4.8.5-36) (GCC)
built with OpenSSL 1.0.2k-fips 26 Jan 2017
TLS SNI support enabled
configure arguments: --user=www --group=www --prefix=/usr/local/nginx --with-http_ssl_module --with-http_stub_status_module --with-http_realip_module --with-threads --with-stream --with-stream_ssl_preread_module --with-stream_ssl_module

2) nginx.conf文件配置

NGINX stream与HTTP不同,需要在stream块中进行配置,但是指令参数与HTTP块都是类似的,主要配置部分如下:

stream {
 resolver 114.114.114.114;
 server {
 listen 443;
 ssl_preread on;
 proxy_connect_timeout 5s;
 proxy_pass $ssl_preread_server_name:$server_port;
 }
}

使用场景

对于4层正向代理,NGINX对上层流量基本上是透传,也不需要HTTP CONNECT来建立隧道。适合于透明代理的模式,比如将访问的域名利用DNS解定向到代理服务器。我们可以通过在客户端绑定/etc/hosts来模拟。

在客户端:

cat /etc/hosts
...
# 把域名www.baidu.com绑定到正向代理服务器39.105.196.164
39.105.196.164 www.baidu.com
# 正常利用curl来访问www.baidu.com即可。
# curl https://www.baidu.com -svo /dev/null
* About to connect() to www.baidu.com port 443 (#0)
* Trying 39.105.196.164...
* Connected to www.baidu.com (39.105.196.164) port 443 (#0)
* Initializing NSS with certpath: sql:/etc/pki/nssdb
* CAfile: /etc/pki/tls/certs/ca-bundle.crt
 CApath: none
* SSL connection using TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
* Server certificate:
* subject: CN=baidu.com,O="Beijing Baidu Netcom Science Technology Co., Ltd",OU=service operation department,L=beijing,ST=beijing,C=CN
* start date: 5月 09 01:22:02 2019 GMT
* expire date: 6月 25 05:31:02 2020 GMT
* common name: baidu.com
* issuer: CN=GlobalSign Organization Validation CA - SHA256 - G2,O=GlobalSign nv-sa,C=BE
> GET / HTTP/1.1
> User-Agent: curl/7.29.0
> Host: www.baidu.com
> Accept: */*
>
< HTTP/1.1 200 OK
< Accept-Ranges: bytes
< Cache-Control: private, no-cache, no-store, proxy-revalidate, no-transform
< Connection: Keep-Alive
< Content-Length: 2443
< Content-Type: text/html
< Date: Fri, 21 Jun 2019 05:46:07 GMT
< Etag: "5886041d-98b"
< Last-Modified: Mon, 23 Jan 2017 13:24:45 GMT
< Pragma: no-cache
< Server: bfe/1.0.8.18
< Set-Cookie: BDORZ=27315; max-age=86400; domain=.baidu.com; path=/
<
{ [data not shown]
* Connection #0 to host www.baidu.com left intact

常见问题

1) 客户端手动设置代理导致访问不成功

4层正向代理是透传上层HTTPS流量,不需要HTTP CONNECT来建立隧道,也就是说不需要客户端设置HTTP(S)代理。如果我们在客户端手动设置HTTP(s)代理是否能访问成功呢? 我们可以用curl -x来设置代理为这个正向服务器访问测试,看看结果:

# curl https://www.baidu.com -svo /dev/null -x 39.105.196.164:443
* About to connect() to proxy 39.105.196.164 port 443 (#0)
* Trying 39.105.196.164...
* Connected to 39.105.196.164 (39.105.196.164) port 443 (#0)
* Establish HTTP proxy tunnel to www.baidu.com:443
> CONNECT www.baidu.com:443 HTTP/1.1
> Host: www.baidu.com:443
> User-Agent: curl/7.29.0
> Proxy-Connection: Keep-Alive
>
* Proxy CONNECT aborted
* Connection #0 to host 39.105.196.164 left intact

可以看到客户端试图于正向NGINX前建立HTTP CONNECT tunnel,但是由于NGINX是透传,所以把CONNECT请求直接转发给了目的服务器。目的服务器不接受CONNECT方法,所以最终出现"Proxy CONNECT aborted",导致访问不成功。

2) 客户端没有带SNI导致访问不成功

上文提到用NGINX stream做正向代理的关键因素之一是利用ngx_stream_ssl_preread_module提取出Client Hello中的SNI字段。如果客户端客户端不携带SNI字段,会造成代理服务器无法获知目的域名的情况,导致访问不成功。

在透明代理模式下(用手动绑定hosts的方式模拟),我们可以在客户端用openssl来模拟:

# openssl s_client -connect www.baidu.com:443 -msg
CONNECTED(00000003)
>>> TLS 1.2 [length 0005]
 16 03 01 01 1c
>>> TLS 1.2 Handshake [length 011c], ClientHello
 01 00 01 18 03 03 6b 2e 75 86 52 6c d5 a5 80 d7
 a4 61 65 6d 72 53 33 fb 33 f0 43 a3 aa c2 4a e3
 47 84 9f 69 8b d6 00 00 ac c0 30 c0 2c c0 28 c0
 24 c0 14 c0 0a 00 a5 00 a3 00 a1 00 9f 00 6b 00
 6a 00 69 00 68 00 39 00 38 00 37 00 36 00 88 00
 87 00 86 00 85 c0 32 c0 2e c0 2a c0 26 c0 0f c0
 05 00 9d 00 3d 00 35 00 84 c0 2f c0 2b c0 27 c0
 23 c0 13 c0 09 00 a4 00 a2 00 a0 00 9e 00 67 00
 40 00 3f 00 3e 00 33 00 32 00 31 00 30 00 9a 00
 99 00 98 00 97 00 45 00 44 00 43 00 42 c0 31 c0
 2d c0 29 c0 25 c0 0e c0 04 00 9c 00 3c 00 2f 00
 96 00 41 c0 12 c0 08 00 16 00 13 00 10 00 0d c0
 0d c0 03 00 0a 00 07 c0 11 c0 07 c0 0c c0 02 00
 05 00 04 00 ff 01 00 00 43 00 0b 00 04 03 00 01
 02 00 0a 00 0a 00 08 00 17 00 19 00 18 00 16 00
 23 00 00 00 0d 00 20 00 1e 06 01 06 02 06 03 05
 01 05 02 05 03 04 01 04 02 04 03 03 01 03 02 03
 03 02 01 02 02 02 03 00 0f 00 01 01
140285606590352:error:140790E5:SSL routines:ssl23_write:ssl handshake failure:s23_lib.c:177:
---
no peer certificate available
---
No client certificate CA names sent
---
SSL handshake has read 0 bytes and written 289 bytes
...

openssl s_client默认不带SNI,可以看到上面的请求在TLS/SSL握手阶段,发出Client Hello后就结束了。因为代理服务器不知道要把Client Hello往哪个目的域名转发。

如果用openssl带servername参数来指定SNI,则可以正常访问成功,命令如下:

# openssl s_client -connect www.baidu.com:443 -servername www.baidu.com

总结

本文总结了NGINX利用HTTP CONNECT隧道和NGINX stream两种方式做HTTPS正向代理的原理,环境搭建,使用场景和主要问题,希望给大家在做各种场景的正向代理时提供参考。

作者:怀知

者 | LightZhang666

责编 | 屠敏

出品 | CSDN 博客

本篇文章包含了curl的常用案例使用。

常见网页访问示例

基本用法

访问一个网页:

curl https://www.baidu.com

执行后,相关的网页信息会打印出来。

进度条展示

有时候我们不需要进度表展示,而需要进度条展示。比如:下载文件时。

可以通过 -#, --progress-bar 选项实现。

[root@iZ28xbsfvc4Z 20190713]# curl https://www.baidu.com | head -n1 # 进度表显示
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 2443 100 2443 0 0 11662 0 --:--:-- --:--:-- --:--:-- 11688
<!DOCTYPE html>
[root@iZ28xbsfvc4Z 20190713]# curl -# https://www.baidu.com | head -n1 # 进度条显示
######################################################################## 100.0%
<!DOCTYPE html>

静默模式与错误信息打印

当我们做一些操作时,可能会出现进度表。这时我们可以使用 -s, --silent 静默模式去掉这些不必要的信息。

如果使用 -s, --silent 时,还需要打印错误信息,那么还需要使用 -S, --show-error 选项。

静默模式示例

[root@iZ28xbsfvc4Z ~]# curl https://www.baidu.com | head -n1
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 2443 100 2443 0 0 11874 0 --:--:-- --:--:-- --:--:-- 11859
<!DOCTYPE html>
[root@iZ28xbsfvc4Z ~]# curl -s https://www.baidu.com | head -n1
<!DOCTYPE html>

静默模式结合错误信息打印

[root@iZ28xbsfvc4Z 20190713]# curl -s https://140.205.16.113/ 
[root@iZ28xbsfvc4Z 20190713]#
[root@iZ28xbsfvc4Z 20190713]# curl -sS https://140.205.16.113/
curl: (51) Unable to communicate securely with peer: requested domain name does not match the server's certificate.

显示详细操作信息

使用 -v, --verbose 选项实现。

以 > 开头的行表示curl发送的"header data";< 表示curl接收到的通常情况下隐藏的"header data";而以 * 开头的行表示curl提供的附加信息。

[root@iZ28xbsfvc4Z 20190712]# curl -v https://www.baidu.com
* About to connect to www.baidu.com port 443 (#0)
* Trying 180.101.49.12...
* Connected to www.baidu.com (180.101.49.12) port 443 (#0)
* Initializing NSS with certpath: sql:/etc/pki/nssdb
* CAfile: /etc/pki/tls/certs/ca-bundle.crt
CApath: none
* SSL connection using TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
* Server certificate:
* subject: CN=baidu.com,O="Beijing Baidu Netcom Science Technology Co., Ltd",OU=service operation department,L=beijing,ST=beijing,C=CN
* start date: May 09 01:22:02 2019 GMT
* expire date: Jun 25 05:31:02 2020 GMT
* common name: baidu.com
* issuer: CN=GlobalSign Organization Validation CA - SHA256 - G2,O=GlobalSign nv-sa,C=BE
> GET / HTTP/1.1
> User-Agent: curl/7.29.0
> Host: www.baidu.com
> Accept: */*
>
< HTTP/1.1 200 OK
< Accept-Ranges: bytes
< Cache-Control: private, no-cache, no-store, proxy-revalidate, no-transform
< Connection: Keep-Alive
< Content-Length: 2443
< Content-Type: text/html
< Date: Fri, 12 Jul 2019 08:26:23 GMT
< Etag: "588603eb-98b"
< Last-Modified: Mon, 23 Jan 2017 13:23:55 GMT
< Pragma: no-cache
< Server: bfe/1.0.8.18
< Set-Cookie: BDORZ=27315; max-age=86400; domain=.baidu.com; path=/
<
<!DOCTYPE html>
……………… # curl 网页的具体信息

指定访问的请求方法

当然curl默认使用GET方式访问。使用了 -d, --data <data> 选项,那么会默认为 POST方法访问。如果此时还想实现 GET 访问,那么可以使用 -G, --get 选项强制curl 使用GET方法访问。

同时 -X, --request <command> 选项也可以指定访问方法。

POST请求和数据传输

为了抓包查看信息所以使用了 --local-port <num>[-num] 选项,在实际应用中不需要该选项。

[root@iZ28xbsfvc4Z ~]# curl -sv --local-port 9000 -X POST -d 'user=zhang&pwd=123456' http://www.zhangblog.com/2019/06/24/domainexpire/ | head -n1 
## 或者
[root@iZ28xbsfvc4Z ~]# curl -sv --local-port 9000 -d 'user=zhang&pwd=123456' http://www.zhangblog.com/2019/06/24/domainexpire/ | head -n1
* About to connect to www.zhangblog.com port 80 (#0)
* Trying 120.27.48.179...
* Connected to www.zhangblog.com (120.27.48.179) port 80 (#0)
> POST /2019/06/24/domainexpire/ HTTP/1.1 # POST 请求方法
> User-Agent: curl/7.29.0
> Host: www.zhangblog.com
> Accept: */*
> Content-Length: 21
> Content-Type: application/x-www-form-urlencoded
>
} [data not shown]
* upload completely sent off: 21 out of 21 bytes
< HTTP/1.1 405 Not Allowed
< Server: nginx/1.14.2
< Date: Thu, 18 Jul 2019 07:56:23 GMT
< Content-Type: text/html
< Content-Length: 173
< Connection: keep-alive
<
{ [data not shown]
* Connection #0 to host www.zhangblog.com left intact
<html>

抓包信息

[root@iZ28xbsfvc4Z tcpdump]# tcpdump -i any port 9000 -A -s 0

指定请求方法

curl -vs -X POST https://www.baidu.com | head -n1
curl -vs -X PUT https://www.baidu.com | head -n1

保存访问网页

使用linux的重定向功能保存

curl www.baidu.com >> baidu.html

使用curl的大O选项

通过 -O, --remote-name 选项实现。

[root@iZ28xbsfvc4Z 20190712]# curl -O https://www.baidu.com # 使用了 -O 选项,必须指定到具体的文件 错误使用
curl: Remote file name has no length!
curl: try 'curl --help' or 'curl --manual' for more information
[root@iZ28xbsfvc4Z 20190712]# curl -O https://www.baidu.com/index.html # 使用了 -O 选项,必须指定到具体的文件 正确使用
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 2443 100 2443 0 0 13289 0 --:--:-- --:--:-- --:--:-- 13349

使用curl的小o选项

通过 -o, --output <file> 选项实现。

[root@iZ28xbsfvc4Z 20190713]# curl -o sina.txt https://www.sina.com.cn/ # 单个操作
[root@iZ28xbsfvc4Z 20190713]# ll
-rw-r--r-- 1 root root 154 Jul 13 21:06 sina.txt
[root@iZ28xbsfvc4Z 20190703]# curl "http://www.{baidu,douban}.com" -o "site_#1.txt" # 批量操作,注意curl 的地址需要用引号括起来
[1/2]: http://www.baidu.com --> site_baidu.txt
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 2381 100 2381 0 0 46045 0 --:--:-- --:--:-- --:--:-- 46686

[2/2]: http://www.douban.com --> site_douban.txt
100 162 100 162 0 0 3173 0 --:--:-- --:--:-- --:--:-- 3173
[root@iZ28xbsfvc4Z 20190703]#
[root@iZ28xbsfvc4Z 20190703]# ll
total 220
-rw-r--r-- 1 root root 2381 Jul 4 16:53 site_baidu.txt
-rw-r--r-- 1 root root 162 Jul 4 16:53 site_douban.txt

允许不安全访问

当我们使用curl进行https访问访问时,如果SSL证书是我们自签发的证书,那么这个时候需要使用 -k, --insecure 选项,允许不安全的访问。

[root@iZ28xbsfvc4Z ~]# curl https://140.205.16.113/ # 被拒绝
curl: (51) Unable to communicate securely with peer: requested domain name does not match the server's certificate.
[root@iZ28xbsfvc4Z ~]#
[root@iZ28xbsfvc4Z ~]# curl -k https://140.205.16.113/ # 允许执行不安全的证书连接
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html>
<head><title>403 Forbidden</title></head>
<body bgcolor="white">
<h1>403 Forbidden</h1>
<p>You don't have permission to access the URL on this server.<hr/>Powered by Tengine</body>
</html>

获取HTTP响应状态码

在脚本中,这是很常见的测试网站是否正常的用法。

通过 -w, --write-out <format> 选项实现。

[root@iZ28xbsfvc4Z 20190713]# curl -o /dev/ -s -w %{http_code} https://baidu.com
302[root@iZ28xbsfvc4Z 20190713]#
[root@iZ28xbsfvc4Z 20190713]#
[root@iZ28xbsfvc4Z 20190713]# curl -o /dev/ -s -w %{http_code} https://www.baidu.com
200[root@iZ28xbsfvc4Z 20190713]#

指定proxy服务器以及其端口

很多时候上网需要用到代理服务器(比如是使用代理服务器上网或者因为使用curl别人网站而被别人屏蔽IP地址的时候),幸运的是curl通过使用 -x, --proxy <[protocol://][user:password@]proxyhost[:port]> 选项来支持设置代理。

curl -x 192.168.100.100:1080 https://www.baidu.com

模仿浏览器访问

有些网站需要使用特定的浏览器去访问他们,有些还需要使用某些特定的浏览器版本。我们可以通过 -A, --user-agent <agent string> 或者 -H, --header <header> 选项实现模拟浏览器访问。

curl -A "Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/75.0.3770.999" http://www.zhangblog.com/2019/06/24/domainexpire/ 
或者
curl -H 'User-Agent: Mozilla/5.0' http://www.zhangblog.com/2019/06/24/domainexpire/

伪造referer(盗链)

有些网站的网页对http访问的链接来源做了访问限制,这些限制几乎都是通过referer来实现的。

比如:要求是先访问首页,然后再访问首页中的邮箱页面,这时访问邮箱的referer地址就是访问首页成功后的页面地址。如果服务器发现对邮箱页面访问的referer地址不是首页的地址,就断定那是个盗连了。

可以通过 -e, --referer 或则 -H, --header <header> 实现伪造 referer 。

curl -e 'https://www.baidu.com' http://www.zhangblog.com/2019/06/24/domainexpire/
或者
curl -H 'Referer: https://www.baidu.com' http://www.zhangblog.com/2019/06/24/domainexpire/

构造HTTP请求头

可以通过 -H, --header <header> 实现构造http请求头。

curl -H 'Connection: keep-alive' -H 'Referer: https://sina.com.cn' -H 'User-Agent: Mozilla/1.0' http://www.zhangblog.com/2019/06/24/domainexpire/

保存响应头信息

可以通过 -D, --dump-header <file> 选项实现。

[root@iZ28xbsfvc4Z 20190703]# curl -D baidu_header.info www.baidu.com 
………………
[root@iZ28xbsfvc4Z 20190703]# ll
total 4
-rw-r--r-- 1 root root 400 Jul 3 10:11 baidu_header.info # 生成的头文件

限时访问

--connect-timeout <seconds> 连接服务端的超时时间。这只限制了连接阶段,一旦curl连接了此选项就不再使用了。

# 当前 https://www.zhangXX.com 是国外服务器,访问受限
[root@iZ28xbsfvc4Z ~]# curl --connect-timeout 10 https://www.zhangXX.com | head
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- 0:00:10 --:--:-- 0
curl: (28) Connection timed out after 10001 milliseconds

-m, --max-time <seconds> 允许整个操作花费的最大时间(以秒为单位)。这对于防止由于网络或链接变慢而导致批处理作业挂起数小时非常有用。

[root@iZ28xbsfvc4Z ~]# curl -m 10 --limit-rate 5 http://www.baidu.com/ | head # 超过10秒后,断开连接
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
2 2381 2 50 0 0 4 0 0:09:55 0:00:10 0:09:45 4
curl: (28) Operation timed out after 10103 milliseconds with 50 out of 2381 bytes received
<!DOCTYPE html>
<!--STATUS OK--><html> <head><met
### 或
[root@iZ28xbsfvc4Z ~]# curl -m 10 https://www.zhangXX.com | head # 超过10秒后,断开连接
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- 0:00:10 --:--:-- 0
curl: (28) Connection timed out after 10001 milliseconds

显示抓取错误

当我们请求访问失败时或者没有该网页时,网站一般都会给出一个错误的提示页面。

如果我们不需要这个错误页面,只想得到简洁的错误信息。那么可以通过 -f, --fail 选项实现。

[root@iZ28xbsfvc4Z 20190713]# curl http://www.zhangblog.com/201912312
<html>
<head><title>404 Not Found</title></head>
<body bgcolor="white">
<center><h1>404 Not Found</h1></center>
<hr><center>nginx/1.14.2</center>
</body>
</html>
[root@iZ28xbsfvc4Z 20190713]# curl -f http://www.zhangblog.com/201912312 # 得到更简洁的错误信息
curl: (22) The requested URL returned error: 404 Not Found

表单登录与cookie使用

参见「Linux curl 表单登录或提交与cookie使用」:http://www.zhangblog.com/2019/07/20/curl03/

文件上传与下载

涉及 FTP 服务,简单快速搭建可参考:《CentOS7下安装FTP服务》「https://www.cnblogs.com/zhi-leaf/p/5983550.html」

文件下载网页文件下载

# 以进度条展示,而不是进度表展示
[root@iZ28xbsfvc4Z 20190715]# curl -# -o tmp.data2 http://www.zhangblog.com/uploads/tmp/tmp.data
######################################################################## 100.0%

FTP文件下载

说明1:其中 ftp1 用户是ftp服务端的账号,具体家目录是:/mnt/ftp1

说明2:当我们使用 curl 通过 FTP 进行下载时,后面跟的路径都是:当前使用的 ftp 账号家目录为基础的相对路径,然后找到的目标文件。

示例1

# 其中 tmp.data 的绝对路径是:/mnt/ftp1/tmpdata/tmp.data ;ftp1 账号的家目录是:/mnt/ftp1
# 说明:/tmpdata/tmp.data 这个路径是针对 ftp1 账号的家目录而言的
[yun@nginx_proxy01 20190715]$ curl -O ftp://ftp1:123456@172.16.1.195:21/tmpdata/tmp.data
# 或者
[yun@nginx_proxy01 20190715]$ curl -O -u ftp1:123456 ftp://172.16.1.195:21/tmpdata/tmp.data
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 2048M 100 2048M 0 0 39.5M 0 0:00:51 0:00:51 --:--:-- 143M

示例2

# 其中 nginx-1.14.2.tar.gz 的绝对路径是:/tmp/nginx-1.14.2.tar.gz ;ftp1 账号的家目录是:/mnt/ftp1
# 说明:/../../tmp/nginx-1.14.2.tar.gz 这个路径是针对 ftp1 账号的家目录而言的
[yun@nginx_proxy01 20190715]$ curl -O ftp://ftp1:123456@172.16.1.195:21/../../tmp/nginx-1.14.2.tar.gz
# 或者
[yun@nginx_proxy01 20190715]$ curl -O -u ftp1:123456 ftp://172.16.1.195:21/../../tmp/nginx-1.14.2.tar.gz
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 991k 100 991k 0 0 5910k 0 --:--:-- --:--:-- --:--:-- 5937k

文件上传

FTP文件上传

可以通过 -T, --upload-file <file> 选项实现。

说明1:其中 ftp1 用户是ftp服务端的账号,具体家目录是:/mnt/ftp1

# 其中 tmp_client.data 是客户端本地文件; 
# /tmpdata/ 这个路径是针对 ftp1 账号的家目录而言的,且上传时该目录必须是存在的,否则上传失败。
# 因此上传后文件在ftp服务端的绝对路径是:/mnt/ftp1/tmpdata/tmp_client.data
[yun@nginx_proxy01 20190715]$ curl -T tmp_client.data ftp://ftp1:123456@172.16.1.195:21/tmpdata/
# 或者
[yun@nginx_proxy01 20190715]$ curl -T tmp_client.data -u ftp1:123456 ftp://172.16.1.195:21/tmpdata/
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 2048M 0 0 100 2048M 0 95.4M 0:00:21 0:00:21 --:--:-- 49.3M

断点续传

使用 -C, --continue-at <offset> 选项实现。其中使用 “-C -”「注意有空格和无空格的情况」,告诉curl自动找出在哪里/如何恢复传输。

网页端断点续传下载

curl -C - -o tmp.data http://www.zhangblog.com/uploads/tmp/tmp.data # 下载一个 2G 的文件

FTP断点续传下载

细节就不多说了,可参见上面的「FTP文件下载

curl -C - -o tmp.data1 ftp://ftp1:123456@172.16.1.195:21/tmpdata/tmp.data # 下载一个 2G 的文件
# 或则
curl -C - -o tmp.data1 -u ftp1:123456 ftp://172.16.1.195:21/tmpdata/tmp.data # 下载一个 2G 的文件

分段下载

有时文件比较大,或者难以迅速传输,而利用分段传输,可以实现稳定、高效并且有保障的传输,更具有实用性,同时容易对差错文件进行更正。

可使用 -r, --range <range> 选项实现。

如下示例使用了同一张图片,大小为 18196 字节。

网页端分段下载分段下载

[root@iZ28xbsfvc4Z 20190715]# curl -I http://www.zhangblog.com/uploads/hexo/00.jpg # 查看文件大小
HTTP/1.1 200 OK
Server: nginx/1.14.2
Date: Mon, 15 Jul 2019 03:23:44 GMT
Content-Type: image/jpeg
Content-Length: 18196 # 文件大小
Last-Modified: Fri, 05 Jul 2019 08:04:58 GMT
Connection: keep-alive
ETag: "5d1f04aa-4714"
Accept-Ranges: bytes
### 分段下载一个文件
[root@iZ28xbsfvc4Z 20190715]# curl -r 0-499 -o 00-jpg.part1 http://www.zhangblog.com/uploads/hexo/00.jpg
[root@iZ28xbsfvc4Z 20190715]# curl -r 500-999 -o 00-jpg.part2 http://www.zhangblog.com/uploads/hexo/00.jpg
[root@iZ28xbsfvc4Z 20190715]# curl -r 1000- -o 00-jpg.part3 http://www.zhangblog.com/uploads/hexo/00.jpg

查看下载文件

[root@iZ28xbsfvc4Z 20190715]# ll
total 36
-rw-r--r-- 1 root root 500 Jul 15 11:25 00-jpg.part1
-rw-r--r-- 1 root root 500 Jul 15 11:25 00-jpg.part2
-rw-r--r-- 1 root root 17196 Jul 15 11:26 00-jpg.part3

文件合并

[root@iZ28xbsfvc4Z 20190715]# cat 00-jpg.part1 00-jpg.part2 00-jpg.part3 > 00.jpg
[root@iZ28xbsfvc4Z 20190715]# ll 00.jpg
total 56
-rw-r--r-- 1 root root 18196 Jul 15 11:29 00.jpg

FTP分段下载分段下载

[yun@nginx_proxy01 20190715]$ curl -r 0-499 -o 00-jpg.part1 ftp://ftp1:123456@172.16.1.195:21/tmpdata/00.jpg
[yun@nginx_proxy01 20190715]$ curl -r 500-999 -o 00-jpg.part2 ftp://ftp1:123456@172.16.1.195:21/tmpdata/00.jpg
[yun@nginx_proxy01 20190715]$ curl -r 1000- -o 00-jpg.part3 ftp://ftp1:123456@172.16.1.195:21/tmpdata/00.jpg

查看下载文件

[yun@nginx_proxy01 20190715]$ ll 00-jpg.part*
-rw-rw-r-- 1 yun yun 500 Jul 15 17:59 00-jpg.part1
-rw-rw-r-- 1 yun yun 500 Jul 15 18:00 00-jpg.part2
-rw-rw-r-- 1 yun yun 17196 Jul 15 18:00 00-jpg.part3

文件合并

[yun@nginx_proxy01 20190715]$ cat 00-jpg.part1 00-jpg.part2 00-jpg.part3 > 00.jpg
[yun@nginx_proxy01 20190715]$ ll 00.jpg
-rw-rw-r-- 1 yun yun 18196 Jul 15 18:02 00.jpg

声明:本文为CSDN博主「LightZhang666」的原创文章,版权归作者所有,如需转载请联系作者。

原文:https://blog.csdn.net/woshizhangliang999/article/details/98946071

【End】