题注

最近我在学习nginx的lua插件,发现结合nginx的异步io和lua的流程控制能力,还是有很丰富的想象空间的:几乎所有常见的http请求的处理逻辑都能搞定,诸如查查数据库,访问一下memcache,读写一下本地文件等,都不在话下。恰好我正在研究一个第三方http服务的调用api,就产生了将其移植到lua上的想法。

其中涉及到了构造https请求的需求。我先采用ngx.socket.tcp()自己构造了一个http格式的请求,测试结果为可用,然后考虑能不能找到封装好了的httpclient。放狗搜索了一番后,只找到了这个库:lua-resty-http (https://github.com/liseen/lua-resty-http)。无奈下载下来后发现其不支持https,遂fork并修改了一番(https://github.com/kyriosli/lua-resty-http),加上了https支持。

到此貌似整个移植过程就完了?NO,nginx本身就是玩http的,为啥我们还要自己构造http请求,解析请求结果,管理连接复用呢?想到这一层,我又把眼光转到了nginx自带的api上

原理

ngx_lua本身不提供httpclient功能,但提供了ngx.location.capture接口。这个接口可以发送一个子请求(subrequest),并获取子请求的响应结果。子请求可以调用lua,或者返回文件,甚至通过proxy_pass访问另一个地址。

关键就在这里!如果我们使用proxy_pass机制结合subrequest,不就能实现http接口请求了吗?

且慢!

一般来说,httpclient要求能够指定method,scheme,域名,端口,请求头,以及数据。但默认的subrequest将继承所有当前请求的信息。好在ngx.location.capture接口支持指定method和data。那么只要搞定scheme,域名,端口,请求头就可以了。

实现

使用过nginx的proxy_pass的同学都知道,proxy_pass支持从变量中获取值(如常见的proxy_pass $scheme://xxx$request_uri;)。而rewrite能够利用捕获组设置变量,所以,我们可以在nginx的配置文件中加入这么一条location规则:

location /proxy/ {
internal;
rewrite ^/proxy/(https?)/([^/]+)/(\d+)/(.*) /$ break;
proxy_pass $://$2:$3;
}
  • 第一行,指定规则为internal规则,防止外部请求命中此规则
  • 第二行,使用rewrite将地址中的scheme,host,port匹配出来;使用break终止匹配,不再检查其它location
  • 第三行,使用proxy_pass将scheme,host,port拼装成服务器地址

这样,我们要请求如https://www.example.com:443/foo/bar时,只需要在lua代码中构造一个子请求,地址为/proxy/https/www.example.com/443/foo/bar即可。

接下来就是header问题,通过ngx.req.set_header可以在lua中添加需要的请求头。

由于header的继承性,我们需要考虑来自客户端的哪些header不能被传递到远端接口中:

  • Host: 因为proxy_pass默认会重写host,所以无需处理(在常规的proxy_pass中,可能需要使用proxy_set_header命令修正Host头)
  • Content-Type: 如果来自客户端的请求类型为POST,需要将该头去掉
  • Content-Length: 同上
  • Accept-Encoding: 绝大多数时候,浏览器接受gzip, deflate格式的压缩,如果远端接口启用了压缩,此头将导致返回的数据格式被压缩过,从而lua无法正确处理。因此需要去掉此头
  • Cookie: 涉及到隐私问题,所以这个头也需要去掉

 通过ngx.req.clear_header可以将需要去掉的头去掉。注意为了不影响后续处理,所有被去掉/改写的头应该被恢复,所有被添加的头应该被去掉。

下面是一个简单的示例代码:

-- POST https://www.example.com:443/foo/bar?hello=world

ngx.req.set_header("Content-Type", "application/json;charset=utf8");
ngx.req.set_header("Accept", "application/json"); local res = ngx.location.capture('/proxy/https/www.example.com/443/foo/bar', {
method = ngx.HTTP_POST,
body = body,
args = {hello = 'world'}
});

ngx_lua学习笔记 -- capture + proxy 实现httpclient的更多相关文章

  1. Android(java)学习笔记211:采用httpclient提交数据(qq登录案例)

    1.Apache -Httpclient HttpClient 是 Apache Jakarta Common 下的子项目,可以用来提供高效的.最新的.功能丰富的支持 HTTP 协议的客户端编程工具包 ...

  2. Android(java)学习笔记154:采用HttpClient提交数据(qq登录案例)

    1.Apache -Httpclient HttpClient 是 Apache Jakarta Common 下的子项目,可以用来提供高效的.最新的.功能丰富的支持 HTTP 协议的客户端编程工具包 ...

  3. vue.js 源代码学习笔记 ----- instance proxy

    /* not type checking this file because flow doesn't play well with Proxy */ import config from 'core ...

  4. ES6学习笔记(11)----Proxy

    参考书<ECMAScript 6入门>http://es6.ruanyifeng.com/ Proxy1.概述    Proxy可以用来修改对象的默认操作    let obj = {na ...

  5. Python学习笔记 capture 1

    最近开始学习Python3.x,真的感觉Python的语法与C++,Java有很大的不同,Python从某些方面来说语法更简单.Python作为一种解释性语言和编译型语言如C++来说,还是各有千秋的. ...

  6. Ionic3学习笔记(十三)HttpClient 实现 HTTP 请求以及踩过的一些坑

    本文为原创文章,转载请标明出处 目录 猫眼API HttpClient 实现 HTTP 请求 安装 HttpClientModule 模块 创建 provider 创建 page 一些坑 坑1: 未在 ...

  7. ES6-11学习笔记--代理Proxy

    Proxy代理 常用拦截方法 ES5拦截: let obj = {} let newVal = '' Object.defineProperty(obj, 'name', { get() { cons ...

  8. NodeJS学习笔记 (7)网络服务-http-client(ok)

    原文:https://github.com/chyingp/nodejs-learning-guide 自己敲代码: ClientRequest概览 当你调用 http.request(options ...

  9. Dynamic CRM 2013学习笔记(三十)Linq使用报错 A proxy type with the name account has been defined by another assembly

    在CRM中使用linq时,有时会报这个错误: A proxy type with the name account has been defined by another assembly. Curr ...

随机推荐

  1. spring中ApplicationContextAware接口使用理解

    一.这个接口有什么用?当一个类实现了这个接口(ApplicationContextAware)之后,这个类就可以方便获得ApplicationContext中的所有bean.换句话说,就是这个类可以直 ...

  2. Objective-C:NSDectionary字典的常见操作

    NSDectionary字典:它是一个存储键值的容器,每一个键key都对应着一个值value,可以通过键key一次性找到目标值value,这是一个比较好的存储器,相比于数组而言,它明显提高了查询效率. ...

  3. 我所遭遇过的游戏中间件--Scaleform

    我所遭遇过的游戏中间件---Scaleform Scaleform帮助开发人员利用现代系统的三维硬件加速性能创建电影品质的菜单.游戏内HUD,动画纹理.迷你游戏以及移动游戏与应用.Scaleform作 ...

  4. 在Android中使用Android Ksoap2调用WebService

    一.WebService介绍 WebService是基于SOAP协议可实现web服务器与web服务器之间的通信,因采用SOAP协议传送XML数据具有平台无关性,也是成为解决异构平台之间通信的重要解决方 ...

  5. 比较windows phone 的回退事件与android的回退事件

    public void onBackPressed() { finish(); } 如果要做一个页面导航的功能的话,就我而言,认为,windows phone开发比android更加人性化,更加傻瓜化 ...

  6. 微博推荐算法学习(Weibo Recommend Algolrithm)

    原文:http://hijiangtao.github.io/2014/10/06/WeiboRecommendAlgorithm/ 基础及关联算法 作用:为微博推荐挖掘必要的基础资源.解决推荐时的通 ...

  7. C#.NET常见问题(FAQ)-如何在系统变量中加入新的环境变量

    比如我要将C:\Windows\Microsoft.NET\Framework\v3.5这个目录加入环境变量 则在系统的环境变量中点击Path,编辑,然后加入一个分号";",然后粘 ...

  8. TotalCommander使用方法,如何对图片批量重命名

    1 文件或文件夹重命名 F2 2 计算所有文件夹的大小 A/t+Shift+Enter.(这样对于文件的更新操作就更加快捷有效了,比如我的文档里面只有若干个子文件夹有更新,则别的都不用动,只要修改那些 ...

  9. 【AngularJS】Controller

    理解控制器 在Angular中,一个容器就是一个JavaScript构造函数,用来增强Angular Scope. 当一个控制器通过ng-controller指令绑定到DOM,Angular就会实例化 ...

  10. es6-块级作用域let 和 var的区别

    块级绑定 js的处理机制和我们大家想象的完全不同,并不完全是所谓函数调用以及上下执行那样简单,它是存有”域”的本质区别的. var具有全局污染特性,所以es6才会出现let .const. 下面通过一 ...