当我们访问一个网站或者网站接口服务,想知道"时间都去哪里了",这时候我们借助工具来分析。
可以将时间大致分为两部分:一部分是从我们请求到网站服务端所经历的耗时,另一部分是服务端自身处理该服务完毕后响应回来的时间,这些都是可以作为后面结果分析的判断依据。
下面将介绍两个方案,仅供参考。
开始计时
使用系统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
参数:
从上面的例子可以看到
上面还有个小技巧:
$ 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
$ 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。
今天你get 到了吗?喜欢的话,关注收藏下。
GINX主要设计作为反向代理服务器,但随着NGINX的发展,它同样能作为正向代理的选项之一。正向代理本身并不复杂,而如何代理加密的HTTPS流量是正向代理需要解决的主要问题。本文将介绍利用NGINX来正向代理HTTPS流量两种方案,及其使用场景和主要问题。
简单介绍下正向代理的分类作为理解下文的背景知识:
按客户端有无感知的分类
按代理是否解密HTTPS的分类
https://www.jianshu.com/p/405f9d76f8c4
作为反向代理时,代理服务器通常终结 (terminate) HTTPS加密流量,再转发给后端实例。HTTPS流量的加解密和认证过程发生在客户端和反向代理服务器之间。
而作为正向代理在处理客户端发过来的流量时,HTTP加密封装在了TLS/SSL中,代理服务器无法看到客户端请求URL中想要访问的域名,如下图。所以代理HTTPS流量,相比于HTTP,需要做一些特殊处理。
根据前文中的分类方式,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权威指南中的图:
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】
*请认真填写需求信息,我们会在24小时内与您取得联系。