armstrong 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/armstrong.gemspec +1 -1
- data/demo/armstrong_test.rb +1 -1
- data/lib/armstrong/connection.rb +32 -15
- data/lib/armstrong/main_actors.rb +12 -4
- data/lib/armstrong.rb +12 -3
- metadata +4 -4
data/armstrong.gemspec
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
Gem::Specification.new 'armstrong', '0.
|
1
|
+
Gem::Specification.new 'armstrong', '0.4.0' do |s|
|
2
2
|
s.description = "Armstrong is an Mongrel2 fronted, actor-based web development framework similar in style to sinatra. With natively-threaded interpreters (Rubinius2), Armstrong provides true concurrency and high stability, by design."
|
3
3
|
s.summary = "Highly concurrent, sinatra-like framework"
|
4
4
|
s.author = "Artem Titoulenko"
|
data/demo/armstrong_test.rb
CHANGED
data/lib/armstrong/connection.rb
CHANGED
@@ -1,7 +1,9 @@
|
|
1
|
+
require 'ffi'
|
1
2
|
require 'ffi-rzmq'
|
3
|
+
require 'json'
|
2
4
|
|
3
5
|
class Connection
|
4
|
-
attr_reader :app_id, :sub_addr, :pub_addr, :request_sock, :response_sock
|
6
|
+
attr_reader :app_id, :sub_addr, :pub_addr, :request_sock, :response_sock, :context
|
5
7
|
|
6
8
|
def initialize(app_id, zmq_sub_pub_addr=["tcp://127.0.0.1", 9999, "tcp://127.0.0.1", 9998])
|
7
9
|
@app_id = app_id
|
@@ -12,27 +14,29 @@ class Connection
|
|
12
14
|
end
|
13
15
|
|
14
16
|
def connect
|
15
|
-
context = ZMQ::Context.new 1
|
16
|
-
@request_sock = context.socket ZMQ::PULL
|
17
|
+
@context = ZMQ::Context.new 1
|
18
|
+
@request_sock = @context.socket ZMQ::PULL
|
17
19
|
@request_sock.connect @sub_addr
|
18
20
|
|
19
|
-
@response_sock = context.socket ZMQ::PUB
|
21
|
+
@response_sock = @context.socket ZMQ::PUB
|
20
22
|
@response_sock.setsockopt ZMQ::IDENTITY, @app_id
|
21
23
|
@response_sock.connect @pub_addr
|
22
24
|
end
|
23
25
|
|
24
|
-
#raw recv
|
26
|
+
#raw recv, unparsed message
|
25
27
|
def recv
|
26
28
|
msg = ""
|
27
|
-
@request_sock.recv_string
|
29
|
+
rc = @request_sock.recv_string(msg)
|
30
|
+
puts "errno [#{ZMQ::Util.errno}] with description [#{ZMQ::Util.error_string}]" unless ZMQ::Util.resultcode_ok?(rc)
|
28
31
|
msg
|
29
32
|
end
|
30
33
|
|
31
34
|
#parse the request, this is the best way to get stuff back, as a Hash
|
32
35
|
def receive
|
33
|
-
parse(
|
36
|
+
parse(recv)
|
34
37
|
end
|
35
38
|
|
39
|
+
# sends the message off, formatted for Mongrel2 to understand
|
36
40
|
def send(uuid, conn_id, msg)
|
37
41
|
header = "%s %d:%s" % [uuid, conn_id.join(' ').length, conn_id.join(' ')]
|
38
42
|
string = header + ', ' + msg
|
@@ -40,12 +44,14 @@ class Connection
|
|
40
44
|
@response_sock.send_string string, ZMQ::NOBLOCK
|
41
45
|
end
|
42
46
|
|
43
|
-
|
44
|
-
|
47
|
+
# reply to an env with `message` string
|
48
|
+
def reply(env, message)
|
49
|
+
self.send(env[:sender], [env[:conn_id]], message)
|
45
50
|
end
|
46
51
|
|
47
|
-
|
48
|
-
|
52
|
+
# reply to a req with a valid http header
|
53
|
+
def reply_http(env, body, code=200, headers={"Content-type" => "text/html"})
|
54
|
+
self.reply(env, http_response(body, code, headers))
|
49
55
|
end
|
50
56
|
|
51
57
|
private
|
@@ -56,14 +62,25 @@ class Connection
|
|
56
62
|
"HTTP/1.1 #{code} #{StatusMessage[code.to_i]}\r\n#{headers_s}\r\n\r\n#{body}"
|
57
63
|
end
|
58
64
|
|
65
|
+
def parse_netstring(ns)
|
66
|
+
len, rest = ns.split(':', 2)
|
67
|
+
len = len.to_i
|
68
|
+
raise "Netstring did not end in ','" unless rest[len].chr == ','
|
69
|
+
[ rest[0...len], rest[(len+1)..-1] ]
|
70
|
+
end
|
71
|
+
|
59
72
|
def parse(msg)
|
60
|
-
if
|
73
|
+
if msg.nil? || msg.empty?
|
61
74
|
return nil
|
62
75
|
end
|
63
76
|
|
64
|
-
|
65
|
-
|
66
|
-
|
77
|
+
env = {}
|
78
|
+
env[:sender], env[:conn_id], env[:path], rest = msg.split(' ', 4)
|
79
|
+
env[:headers], head_rest = parse_netstring(rest)
|
80
|
+
env[:body], _ = parse_netstring(head_rest)
|
81
|
+
|
82
|
+
env[:headers] = JSON.parse(env[:headers])
|
83
|
+
return env
|
67
84
|
end
|
68
85
|
|
69
86
|
# From WEBrick: thanks dawg.
|
@@ -7,6 +7,16 @@ module Aleph
|
|
7
7
|
end
|
8
8
|
end
|
9
9
|
|
10
|
+
# take the route and pattern and keys and this function will match the keyworded params in
|
11
|
+
# the url with the pattern. Example:
|
12
|
+
#
|
13
|
+
# url: /user/2/view/345
|
14
|
+
# pattern: /user/:id/view/:comment
|
15
|
+
#
|
16
|
+
# returns:
|
17
|
+
#
|
18
|
+
# params = {id: 2, comment: 345}
|
19
|
+
#
|
10
20
|
def process_route(route, pattern, keys, values = [])
|
11
21
|
return unless match = pattern.match(route)
|
12
22
|
values += match.captures.map { |v| URI.decode(v) if v }
|
@@ -32,7 +42,7 @@ Aleph::Base.replier_proc = Proc.new do
|
|
32
42
|
end
|
33
43
|
msg.when(Reply) do |m|
|
34
44
|
begin
|
35
|
-
conn.reply_http(m.env, m.
|
45
|
+
conn.reply_http(m.env, m.body, m.code, m.headers)
|
36
46
|
rescue Exception => e
|
37
47
|
puts "Actor[:replier]: I messed up with exception: #{e.message}"
|
38
48
|
end
|
@@ -44,7 +54,6 @@ end
|
|
44
54
|
# this nifty mess helps us just to use the output of the Procs that handle
|
45
55
|
# the request instead of making the user manually catch messages and send
|
46
56
|
# them out to the replier.
|
47
|
-
|
48
57
|
Aleph::Base.container_proc = Proc.new do
|
49
58
|
data = Actor.receive
|
50
59
|
env, proccess = data.env, data.proccess
|
@@ -53,7 +62,7 @@ Aleph::Base.container_proc = Proc.new do
|
|
53
62
|
#just like Rack: env, code, headers, body. HINT HINT
|
54
63
|
Aleph::Base.replier << Reply.new(env, response[0], response[1], response[2])
|
55
64
|
else
|
56
|
-
Aleph::Base.replier << Reply.new(env, 200, {"Content-Type"
|
65
|
+
Aleph::Base.replier << Reply.new(env, 200, {"Content-Type", "text/html;charset=utf-8", "Connection", "keep-alive", "Server", "Armstrong", "X-Frame-Options", "sameorigin", "X-XSS_Protection", "1; mode=block"}, response)
|
57
66
|
end
|
58
67
|
end
|
59
68
|
|
@@ -73,7 +82,6 @@ Aleph::Base.request_handler_proc = Proc.new do
|
|
73
82
|
|
74
83
|
f.when(Request) do |r|
|
75
84
|
failure = true
|
76
|
-
r.env[:headers] = JSON.parse(r.env[:headers])
|
77
85
|
verb = r.env[:headers]["METHOD"]
|
78
86
|
routes[verb].each do |route|
|
79
87
|
if route.route[0].match(r.env[:path])
|
data/lib/armstrong.rb
CHANGED
@@ -1,8 +1,6 @@
|
|
1
1
|
require 'actor'
|
2
2
|
require 'rubygems'
|
3
3
|
require 'lazy'
|
4
|
-
require 'open-uri'
|
5
|
-
require 'json'
|
6
4
|
|
7
5
|
libdir = File.dirname(__FILE__)
|
8
6
|
$LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir)
|
@@ -16,6 +14,7 @@ module Aleph
|
|
16
14
|
class << self
|
17
15
|
attr_accessor :conn, :routes
|
18
16
|
|
17
|
+
# uuid generator. There's a pretty low chance of collision.
|
19
18
|
def new_uuid
|
20
19
|
values = [
|
21
20
|
rand(0x0010000),
|
@@ -76,7 +75,16 @@ module Aleph
|
|
76
75
|
end
|
77
76
|
end
|
78
77
|
|
79
|
-
class Armstrong < Base
|
78
|
+
class Armstrong < Base
|
79
|
+
|
80
|
+
# the kicker. It all gets launched from here.
|
81
|
+
# this function makes a new connection object to handle the communication,
|
82
|
+
# promises to start the replier, request handler, and their supervisor,
|
83
|
+
# gives the replier the connection information, tells the request_handler
|
84
|
+
# what routes it should be able to match, then checks that all of the services
|
85
|
+
# are running correctly, gives us a launch time, then jumps into our main loop
|
86
|
+
# that waits for an incoming message, parses it, and sends it off to be
|
87
|
+
# operated on by the request handler. Boom.
|
80
88
|
def self.run!
|
81
89
|
uuid = new_uuid
|
82
90
|
puts "starting Armstrong as mongrel2 service #{uuid}"
|
@@ -138,6 +146,7 @@ module Aleph
|
|
138
146
|
self.target = Armstrong
|
139
147
|
end
|
140
148
|
|
149
|
+
# Sinatras secret sauce.
|
141
150
|
at_exit { Armstrong.run! }
|
142
151
|
end
|
143
152
|
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: armstrong
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 62288469761365696
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
8
|
+
- 4
|
9
9
|
- 0
|
10
|
-
version: 0.
|
10
|
+
version: 0.4.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Artem Titoulenko
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2011-11-
|
18
|
+
date: 2011-11-20 00:00:00 Z
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
21
|
name: ffi
|