Skip to content
This repository was archived by the owner on Jul 17, 2018. It is now read-only.

Commit 50210eb

Browse files
committed
Initial release
0 parents  commit 50210eb

11 files changed

+800
-0
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
spec/resty/

Gemfile

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
source :rubygems
2+
3+
gem "multi_json"
4+
gem "oj"
5+
gem "sinatra"
6+
7+
group :test do
8+
gem "rspec"
9+
gem "thin"
10+
gem "resty_test"
11+
end

Gemfile.lock

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
GEM
2+
remote: http://rubygems.org/
3+
specs:
4+
daemons (1.1.9)
5+
diff-lcs (1.1.3)
6+
eventmachine (1.0.0)
7+
excon (0.16.10)
8+
multi_json (1.5.0)
9+
oj (2.0.2)
10+
rack (1.4.4)
11+
rack-protection (1.3.2)
12+
rack
13+
resty_test (0.1.0)
14+
excon
15+
rspec (2.12.0)
16+
rspec-core (~> 2.12.0)
17+
rspec-expectations (~> 2.12.0)
18+
rspec-mocks (~> 2.12.0)
19+
rspec-core (2.12.2)
20+
rspec-expectations (2.12.1)
21+
diff-lcs (~> 1.1.3)
22+
rspec-mocks (2.12.1)
23+
sinatra (1.3.3)
24+
rack (~> 1.3, >= 1.3.6)
25+
rack-protection (~> 1.2)
26+
tilt (~> 1.3, >= 1.3.3)
27+
thin (1.5.0)
28+
daemons (>= 1.0.9)
29+
eventmachine (>= 0.12.6)
30+
rack (>= 1.0.0)
31+
tilt (1.3.3)
32+
33+
PLATFORMS
34+
ruby
35+
36+
DEPENDENCIES
37+
multi_json
38+
oj
39+
resty_test
40+
rspec
41+
sinatra
42+
thin

README.markdown

+159
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
Name
2+
====
3+
4+
lua-resty-http - Lua HTTP client driver for ngx_lua
5+
6+
Example
7+
=======
8+
9+
server {
10+
location /test {
11+
content_by_lua '
12+
local http = require "resty.http"
13+
local client = http:new()
14+
client:set_timeout(1000) -- 1 sec
15+
16+
local ok, err = client:connect("checkip.amazonaws.com", 80)
17+
if not ok then
18+
ngx.say("failed to connect: ", err)
19+
return
20+
end
21+
22+
local res, err = client:request({ path = "/" })
23+
if not res then
24+
ngx.say("failed to retrieve: ", err)
25+
return
26+
end
27+
28+
-- close connection, or put it into the connection pool
29+
if res.headers["connection"] == "close" then
30+
local ok, err = client:close()
31+
if not ok then
32+
ngx.say("failed to close: ", err)
33+
return
34+
end
35+
else
36+
client:set_keepalive(0, 100)
37+
end
38+
39+
if res.status >= 200 and res.status < 300 then
40+
ngx.say("My IP is: " .. res.body)
41+
else
42+
ngx.say("Query returned a non-200 response: " .. res.status)
43+
end
44+
';
45+
}
46+
}
47+
48+
API
49+
===
50+
51+
new
52+
---
53+
`syntax: client, err = http:new()`
54+
55+
Creates a HTTP object.
56+
57+
connect
58+
-------
59+
`syntax: ok, err = client:connect(host, port?, conf?)`
60+
61+
Attempts to connect to the remote `host` and `port`. Port 80 is assumed, when no port is given.
62+
63+
Before actually resolving the host name and connecting to the remote backend, this method will always look up the connection pool for matched idle connections created by previous calls of this method.
64+
65+
An optional Lua `conf` table can be specified to declare various options:
66+
67+
* `scheme`
68+
: Specifies a scheme, defaults to "http" or "https", depending on the port.
69+
* `path`
70+
: Specifies a default endpoint.
71+
72+
request
73+
-------
74+
`syntax: res, err = client:request(opts?)`
75+
76+
Attempts to retrieve data from a remote location.
77+
78+
An optional Lua `opts` table can be specified to declare various options:
79+
80+
* `method`
81+
: Specifies the request method, defaults to `'get'`.
82+
* `path`
83+
: Specifies the path, defaults to `'/'`.
84+
* `query`
85+
: Specifies query parameters. Accepts either a string or a Lua table.
86+
* `headers`
87+
: Specifies request headers. Accepts a Lua table.
88+
89+
Returns a `res` object containing three attributes:
90+
91+
* `res.status` (number)
92+
: The resonse status, e.g. 200
93+
* `res.headers` (table)
94+
: A Lua table with response headers
95+
* `res.body` (string)
96+
: The plain response body
97+
98+
set_timeout
99+
----------
100+
`syntax: client:set_timeout(time)`
101+
102+
Sets the timeout (in ms) protection for subsequent operations, including the `connect` method.
103+
104+
set_keepalive
105+
------------
106+
`syntax: ok, err = client:set_keepalive(max_idle_timeout, pool_size)`
107+
108+
Puts the current connection immediately into the ngx_lua cosocket connection pool.
109+
110+
You can specify the max idle timeout (in ms) when the connection is in the pool and the maximal size of the pool every nginx worker process.
111+
112+
In case of success, returns `1`. In case of errors, returns `nil` with a string describing the error.
113+
114+
Only call this method in the place you would have called the `close` method instead. Calling this method will immediately turn the current connection into the `closed` state. Any subsequent operations other than `connect()` on the current object will return the `closed` error.
115+
116+
get_reused_times
117+
----------------
118+
`syntax: times, err = client:get_reused_times()`
119+
120+
This method returns the (successfully) reused times for the current connection. In case of error, it returns `nil` and a string describing the error.
121+
122+
close
123+
-----
124+
`syntax: ok, err = client:close()`
125+
126+
Closes the current connection and returns the status.
127+
128+
In case of success, returns `1`. In case of errors, returns `nil` with a string describing the error.
129+
130+
Licence
131+
=======
132+
133+
Some code and documentation parts were (shamelessly) taken from [lua-resty-redis](https://github.com/agentzh/lua-resty-redis), MIT, Copyright by Yichun Zhang.
134+
135+
Greatly inspired by [excon](https://github.com/geemus/excon), MIT, Copyright by Wesley Beary.
136+
137+
This code is covered by MIT License.
138+
(The MIT License)
139+
140+
Copyright (c) 2013 Black Square Media Ltd
141+
142+
Permission is hereby granted, free of charge, to any person obtaining
143+
a copy of this software and associated documentation files (the
144+
'Software'), to deal in the Software without restriction, including
145+
without limitation the rights to use, copy, modify, merge, publish,
146+
distribute, sublicense, and/or sell copies of the Software, and to
147+
permit persons to whom the Software is furnished to do so, subject to
148+
the following conditions:
149+
150+
The above copyright notice and this permission notice shall be
151+
included in all copies or substantial portions of the Software.
152+
153+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
154+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
155+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
156+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
157+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
158+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
159+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Rakefile

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
require 'rake'
2+
include Rake::DSL
3+
4+
require 'rspec/mocks/version'
5+
require 'rspec/core/rake_task'
6+
RSpec::Core::RakeTask.new(:spec)
7+
8+
desc 'Default: run specs.'
9+
task :default => :spec

benchmark/nginx.conf

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
worker_processes 1;
2+
events {
3+
worker_connections 1024;
4+
}
5+
6+
http {
7+
8+
lua_package_path 'lib/?.lua;;';
9+
10+
init_by_lua '
11+
cjson = require("cjson")
12+
http = require("resty.http")
13+
';
14+
15+
server {
16+
listen 1984;
17+
18+
location /http {
19+
content_by_lua '
20+
local params = ngx.req.get_uri_args()
21+
local clnt,_ = http:new()
22+
local total = 0
23+
local times = tonumber(params.times) or 100
24+
25+
clnt:set_timeout(1000)
26+
27+
for _=1,times do
28+
clnt:connect("127.0.0.1", 1986)
29+
local res, _ = clnt:request({ query = params })
30+
total = total + #res.body
31+
clnt:set_keepalive()
32+
end
33+
ngx.print(total)
34+
';
35+
}
36+
37+
location /capture {
38+
content_by_lua '
39+
local params = ngx.req.get_uri_args()
40+
local total = 0
41+
local times = tonumber(params.times) or 100
42+
43+
for _=1,times do
44+
local res, _ = ngx.location.capture("/proxy/delegate", { args = params })
45+
total = total + #res.body
46+
end
47+
ngx.print(total)
48+
';
49+
}
50+
51+
location /proxy/delegate {
52+
internal;
53+
proxy_pass http://127.0.0.1:1986;
54+
}
55+
}
56+
}

benchmark/vs_proxy.rb

+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
=begin
2+
Very naive resty.http vs capture/proxy benchmark:
3+
4+
$ ruby benchmark/vs_proxy.rb
5+
== Sinatra/1.3.3 has taken the stage on 1986 for test with backup from Thin
6+
>> Thin web server (v1.5.0 codename Knife)
7+
>> Maximum connections set to 1024
8+
>> Listening on 0.0.0.0:1986, CTRL+C to stop
9+
Rehearsal --------------------------------------------------------
10+
S via resty.http 0.110000 0.030000 0.140000 ( 0.147134)
11+
S via capture/proxy 0.080000 0.010000 0.090000 ( 0.104620)
12+
L via resty.http 0.020000 0.020000 0.040000 ( 0.056494)
13+
L via capture/proxy 0.030000 0.010000 0.040000 ( 0.166540)
14+
----------------------------------------------- total: 0.310000sec
15+
16+
user system total real
17+
S via resty.http 0.040000 0.020000 0.060000 ( 0.073327)
18+
S via capture/proxy 0.040000 0.030000 0.070000 ( 0.084767)
19+
L via resty.http 0.010000 0.020000 0.030000 ( 0.048865)
20+
L via capture/proxy 0.020000 0.020000 0.040000 ( 0.172873)
21+
22+
=end
23+
ENV['RACK_ENV'] ||= 'test'
24+
25+
require 'bundler/setup'
26+
require 'resty_test'
27+
require 'benchmark'
28+
29+
RestyTest.configure do |c|
30+
c.root = File.expand_path("../../spec/resty", __FILE__)
31+
c.config_file = File.expand_path("../nginx.conf", __FILE__)
32+
end
33+
34+
BACKEND = Thread.new do
35+
require 'sinatra/base'
36+
37+
class Server < Sinatra::Base
38+
39+
get '/*' do
40+
len = params[:len].to_i if params[:len]
41+
"A" * (len || 1024)
42+
end
43+
44+
run! port: 1986
45+
end
46+
end
47+
48+
RestyTest.start!
49+
sleep(1)
50+
client = Excon.new("http://127.0.0.1:1984")
51+
lquery = { len: (1024 * 1024 * 2), times: 5 }
52+
53+
54+
Benchmark.bmbm(20) do |x|
55+
x.report "S via resty.http" do
56+
raise "Invalid response size" unless client.post(path: "/http").body == "102400"
57+
end
58+
x.report "S via capture/proxy" do
59+
raise "Invalid response size" unless client.post(path: "/capture").body == "102400"
60+
end
61+
62+
x.report "L via resty.http" do
63+
raise "Invalid response size" unless client.post(path: "/http", query: lquery).body == "10485760"
64+
end
65+
x.report "L via capture/proxy" do
66+
raise "Invalid response size" unless client.post(path: "/capture", query: lquery).body == "10485760"
67+
end
68+
end
69+
70+
BACKEND.exit
71+
sleep(0.1) while BACKEND.alive?
72+
RestyTest.stop!

0 commit comments

Comments
 (0)