rflow-components-http 0.0.2

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/.gitignore ADDED
@@ -0,0 +1,6 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ \#*
6
+ .\#*
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec
data/Rakefile ADDED
@@ -0,0 +1,15 @@
1
+ require 'bundler'
2
+ require 'rspec/core/rake_task'
3
+ require 'rdoc/task'
4
+ Bundler::GemHelper.install_tasks
5
+
6
+ RSpec::Core::RakeTask.new(:spec) do |t|
7
+ t.verbose = true
8
+ t.rspec_opts = '--tty --color'
9
+ end
10
+
11
+ Rake::RDocTask.new do |rd|
12
+ rd.main = "README"
13
+ rd.rdoc_files.include("README", "lib/**/*.rb")
14
+ rd.rdoc_dir = File.join('doc', 'html')
15
+ end
File without changes
@@ -0,0 +1,68 @@
1
+ class RFlow
2
+ module Components
3
+ module HTTP
4
+
5
+ # The set of extensions to add capability to HTTP data types
6
+ module Extensions
7
+
8
+ module IPConnectionExtension
9
+ # Default accessors
10
+ # TODO: validate the stuffs
11
+ ['client_ip', 'client_port', 'server_ip', 'server_port'].each do |name|
12
+ define_method name do |*args|
13
+ data_object[name]
14
+ end
15
+ define_method :"#{name}=" do |*args|
16
+ data_object[name] = args.first
17
+ end
18
+ end
19
+ end
20
+
21
+ # Need to be careful when extending to not clobber data already in data_object
22
+ module HTTPRequestExtension
23
+ def self.extended(base_data)
24
+ base_data.data_object ||= {'uri' => '/', 'method' => 'GET', 'protocol' => 'HTTP/1.0', 'headers'=>{}, 'content' => ''}
25
+ end
26
+
27
+ # Default accessors
28
+ ['method', 'uri', 'query_string', 'protocol', 'headers', 'content'].each do |name|
29
+ define_method name do |*args|
30
+ data_object[name]
31
+ end
32
+ define_method :"#{name}=" do |*args|
33
+ data_object[name] = args.first
34
+ end
35
+ end
36
+ end
37
+
38
+ # Need to be careful when extending to not clobber data already in data_object
39
+ module HTTPResponseExtension
40
+ def self.extended(base_data)
41
+ base_data.data_object ||= {'protocol' => 'HTTP/1.0', 'status_code' => 200, 'status_reason_phrase' => 'OK', 'headers' => {}, 'content' => ''}
42
+ end
43
+
44
+ # Default accessors
45
+ ['protocol', 'status_reason_phrase', 'headers', 'content'].each do |name|
46
+ define_method name do |*args|
47
+ data_object[name]
48
+ end
49
+ define_method :"#{name}=" do |*args|
50
+ data_object[name] = args.first
51
+ end
52
+ end
53
+
54
+ ['status_code'].each do |name|
55
+ define_method name do |*args|
56
+ data_object[name]
57
+ end
58
+ define_method :"#{name}=" do |*args|
59
+ data_object[name] = args.first.to_i
60
+ end
61
+ end
62
+
63
+ end
64
+
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,123 @@
1
+ require 'eventmachine'
2
+ require 'evma_httpserver'
3
+
4
+ require 'rflow'
5
+
6
+ class RFlow
7
+ module Components
8
+ module HTTP
9
+
10
+ class Server < RFlow::Component
11
+ input_port :response_port
12
+ output_port :request_port
13
+
14
+ attr_accessor :port, :listen, :server_signature, :connections
15
+
16
+ def configure!(config)
17
+ @listen = config['listen'] ? config['listen'] : '127.0.0.1'
18
+ @port = config['port'] ? config['port'].to_i : 8000
19
+ @connections = Hash.new
20
+ end
21
+
22
+ def run!
23
+ @server_signature = EM.start_server(@listen, @port, Connection) do |conn|
24
+ conn.server = self
25
+ self.connections[conn.signature.to_s] = conn
26
+ end
27
+ end
28
+
29
+ # Getting all messages to response_port, which we need to filter for
30
+ # those that pertain to this component and have active connections.
31
+ # This is done by inspecting the provenance, specifically the
32
+ # context attribute that we stored originally
33
+ def process_message(input_port, input_port_key, connection, message)
34
+ RFlow.logger.debug "Received a message"
35
+ return unless message.data_type_name == 'RFlow::Message::Data::HTTP::Response'
36
+
37
+
38
+ RFlow.logger.debug "Received a HTTP::Response message, determining if its mine"
39
+ my_events = message.provenance.find_all {|processing_event| processing_event.component_instance_uuid == instance_uuid}
40
+ RFlow.logger.debug "Found #{my_events.size} processing events from me"
41
+ # Attempt to send the data to each context match
42
+ my_events.each do |processing_event|
43
+ RFlow.logger.debug "Inspecting #{processing_event.context}"
44
+ connection_signature = processing_event.context
45
+ if connections[connection_signature]
46
+ RFlow.logger.debug "Found connection for #{processing_event.context}"
47
+ connections[connection_signature].send_http_response message
48
+ end
49
+ end
50
+ end
51
+
52
+ class Connection < EventMachine::Connection
53
+ include EventMachine::HttpServer
54
+
55
+ attr_accessor :server
56
+ attr_reader :client_ip, :client_port, :server_ip, :server_port
57
+
58
+ def post_init
59
+ @client_port, @client_ip = Socket.unpack_sockaddr_in(get_peername) rescue ["?", "?.?.?.?"]
60
+ @server_port, @server_ip = Socket.unpack_sockaddr_in(get_sockname) rescue ["?", "?.?.?.?"]
61
+ RFlow.logger.debug "Connection from #{@client_ip}:#{@client_port} to #{@server_ip}:#{@server_port}"
62
+ super
63
+ no_environment_strings
64
+ end
65
+
66
+
67
+ def receive_data(data)
68
+ RFlow.logger.debug "Received #{data.bytesize} bytes of data from #{client_ip}:#{client_port} to #{@server_ip}:#{@server_port}"
69
+ super
70
+ end
71
+
72
+
73
+ def process_http_request
74
+ RFlow.logger.debug "Received a HTTP request from #{client_ip}:#{client_port} to #{@server_ip}:#{@server_port}"
75
+
76
+ processing_event = RFlow::Message::ProcessingEvent.new(server.instance_uuid, Time.now.utc)
77
+
78
+ request_message = RFlow::Message.new('RFlow::Message::Data::HTTP::Request')
79
+ request_message.data.uri = @http_request_uri
80
+
81
+ processing_event.context = signature
82
+ processing_event.completed_at = Time.now.utc
83
+ request_message.provenance << processing_event
84
+
85
+ server.request_port.send_message request_message
86
+ end
87
+
88
+
89
+ def send_http_response(response_message=nil)
90
+ RFlow.logger.debug "Sending an HTTP response to #{client_ip}:#{client_port}"
91
+ resp = EventMachine::DelegatedHttpResponse.new(self)
92
+
93
+ # Default values
94
+ resp.status = 200
95
+ resp.content = ""
96
+ resp.headers["Content-Type"] = "text/html"
97
+ resp.headers["Server"] = "Apache"
98
+
99
+ if response_message
100
+ resp.status = response_message.data.status_code
101
+ resp.content = response_message.data.content
102
+ response_message.data.headers.each do |header, value|
103
+ resp[headers] = value
104
+ end
105
+ end
106
+
107
+ resp.send_response
108
+ close_connection_after_writing
109
+ end
110
+
111
+
112
+ # Called when a connection is torn down for whatever reason.
113
+ # Remove this connection from the server's list
114
+ def unbind(reason=nil)
115
+ RFlow.logger.debug "Disconnected from HTTP client #{client_ip}:#{client_port} due to '#{reason}'"
116
+ server.connections.delete(self.signature)
117
+ end
118
+ end
119
+ end
120
+
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,7 @@
1
+ class RFlow
2
+ module Components
3
+ module HTTP
4
+ VERSION = "0.0.2"
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,34 @@
1
+ require 'rflow/components/http/extensions'
2
+ require 'rflow/components/http/server'
3
+
4
+ class RFlow
5
+ module Components
6
+ module HTTP
7
+ # Load the schemas
8
+ SCHEMA_DIRECTORY = ::File.expand_path(::File.join(::File.dirname(__FILE__), '..', '..', '..', 'schema'))
9
+
10
+ SCHEMA_FILES = {
11
+ 'http_response.avsc' => 'RFlow::Message::Data::HTTP::Response',
12
+ 'http_request.avsc' => 'RFlow::Message::Data::HTTP::Request',
13
+ }
14
+
15
+ SCHEMA_FILES.each do |file_name, data_type_name|
16
+ schema_string = ::File.read(::File.join(SCHEMA_DIRECTORY, file_name))
17
+ RFlow::Configuration.add_available_data_type data_type_name, 'avro', schema_string
18
+ end
19
+
20
+ # Load the data extensions
21
+ RFlow::Configuration.add_available_data_extension('RFlow::Message::Data::HTTP::Request',
22
+ RFlow::Components::HTTP::Extensions::IPConnectionExtension)
23
+ RFlow::Configuration.add_available_data_extension('RFlow::Message::Data::HTTP::Request',
24
+ RFlow::Components::HTTP::Extensions::HTTPRequestExtension)
25
+
26
+
27
+ RFlow::Configuration.add_available_data_extension('RFlow::Message::Data::HTTP::Response',
28
+ RFlow::Components::HTTP::Extensions::IPConnectionExtension)
29
+ RFlow::Configuration.add_available_data_extension('RFlow::Message::Data::HTTP::Response',
30
+ RFlow::Components::HTTP::Extensions::HTTPResponseExtension)
31
+
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,2 @@
1
+ require 'rflow'
2
+ require 'rflow/components/http'
@@ -0,0 +1,28 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "rflow/components/http/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "rflow-components-http"
7
+ s.version = RFlow::Components::HTTP::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.required_ruby_version = '~> 1.9'
10
+ s.authors = ["Michael L. Artz"]
11
+ s.email = ["michael.artz@redjack.com"]
12
+ s.homepage = ""
13
+ s.summary = %q{HTTP client and server components for the RFlow FBP framework}
14
+ s.description = %q{HTTP client and server components for the RFlow FBP framework. Also includes the necessary HTTP::Request and HTTP::Response message types}
15
+
16
+ s.rubyforge_project = "rflow-components-http"
17
+
18
+ s.files = `git ls-files`.split("\n")
19
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
20
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
21
+ s.require_paths = ["lib"]
22
+
23
+ s.add_dependency 'rflow', '~> 0.0'
24
+ s.add_dependency 'eventmachine_httpserver', '~> 0.2'
25
+
26
+ s.add_development_dependency 'rspec', '~> 2.6'
27
+ s.add_development_dependency 'rake', '>= 0.8.7'
28
+ end
@@ -0,0 +1,19 @@
1
+ {
2
+ "type": "record",
3
+ "name": "Request",
4
+ "namespace": "org.rflow.message.data.http",
5
+ "aliases": [],
6
+ "fields": [
7
+ {"name": "client_ip", "type": ["string", "null"]},
8
+ {"name": "client_port", "type": ["int", "null"]},
9
+ {"name": "server_ip", "type": ["string", "null"]},
10
+ {"name": "server_port", "type": ["int", "null"]},
11
+
12
+ {"name": "method", "type": "string"},
13
+ {"name": "uri", "type": "string"},
14
+ {"name": "query_string", "type": ["string", "null"]},
15
+ {"name": "protocol", "type": "string"},
16
+ {"name": "headers", "type": {"type": "map", "values": ["string", "null"]}},
17
+ {"name": "content", "type": ["bytes", "null"]}
18
+ ]
19
+ }
@@ -0,0 +1,18 @@
1
+ {
2
+ "type": "record",
3
+ "name": "Response",
4
+ "namespace": "org.rflow.message.data.http",
5
+ "aliases": [],
6
+ "fields": [
7
+ {"name": "client_ip", "type": ["string", "null"]},
8
+ {"name": "client_port", "type": ["int", "null"]},
9
+ {"name": "server_ip", "type": ["string", "null"]},
10
+ {"name": "server_port", "type": ["int", "null"]},
11
+
12
+ {"name": "protocol", "type": "string"},
13
+ {"name": "status_code", "type": "int"},
14
+ {"name": "status_reason_phrase", "type": "string"},
15
+ {"name": "headers", "type": {"type": "map", "values": "string"}},
16
+ {"name": "content", "type": "bytes"}
17
+ ]
18
+ }
@@ -0,0 +1,43 @@
1
+ require 'spec_helper.rb'
2
+
3
+ describe RFlow::Components::HTTP::Extensions::HTTPRequestExtension do
4
+ before(:each) do
5
+ @schema_string = RFlow::Configuration.available_data_types['RFlow::Message::Data::HTTP::Request']['avro']
6
+ end
7
+
8
+ it "should work" do
9
+ RFlow::Configuration.available_data_extensions['RFlow::Message::Data::HTTP::Request'].should include(described_class)
10
+
11
+ request = RFlow::Message.new('RFlow::Message::Data::HTTP::Request')
12
+ request.data.uri.should == '/'
13
+ request.data.method.should == 'GET'
14
+ request.data.query_string.should == nil
15
+ request.data.protocol.should == 'HTTP/1.0'
16
+ request.data.headers.should == {}
17
+
18
+ request.data.uri = 'POST'
19
+ request.data.uri.should == 'POST'
20
+ end
21
+
22
+ end
23
+
24
+ describe RFlow::Components::HTTP::Extensions::HTTPResponseExtension do
25
+ before(:each) do
26
+ @schema_string = RFlow::Configuration.available_data_types['RFlow::Message::Data::HTTP::Response']['avro']
27
+ end
28
+
29
+ it "should work" do
30
+ RFlow::Configuration.available_data_extensions['RFlow::Message::Data::HTTP::Response'].should include(described_class)
31
+
32
+ response = RFlow::Message.new('RFlow::Message::Data::HTTP::Response')
33
+ response.data.protocol.should == 'HTTP/1.0'
34
+ response.data.status_code.should == 200
35
+ response.data.status_reason_phrase.should == 'OK'
36
+ response.data.headers.should == {}
37
+ response.data.content.should == ''
38
+
39
+ response.data.status_code = 404
40
+ response.data.status_code.should == 404
41
+ end
42
+
43
+ end
@@ -0,0 +1,67 @@
1
+ require 'spec_helper.rb'
2
+
3
+ describe 'RFlow::Message::Data::HTTP::Request Avro Schema' do
4
+ before(:each) do
5
+ @schema_string = RFlow::Configuration.available_data_types['RFlow::Message::Data::HTTP::Request']['avro']
6
+ end
7
+
8
+ it "should encode and decode an object" do
9
+ request = {
10
+ 'method' => 'METHOD',
11
+ 'uri' => 'URI',
12
+ 'query_string' => 'QUERYSTRING',
13
+ 'protocol' => 'PROTOCOL',
14
+ 'headers' => {
15
+ 'header1' => 'HEADER1',
16
+ 'header2' => 'HEADER2',
17
+ },
18
+ 'content' => '',
19
+ }
20
+
21
+ expect {encode_avro(@schema_string, request)}.to_not raise_error
22
+ avro_encoded_request = encode_avro(@schema_string, request)
23
+
24
+ expect {decode_avro(@schema_string, avro_encoded_request)}.to_not raise_error
25
+ decoded_request = decode_avro(@schema_string, avro_encoded_request)
26
+
27
+ decoded_request['method'].should == request['method']
28
+ decoded_request['uri'].should == request['uri']
29
+ decoded_request['query_string'].should == request['query_string']
30
+ decoded_request['protocol'].should == request['protocol']
31
+ decoded_request['headers'].should == request['headers']
32
+ decoded_request['content'].should == request['content']
33
+ end
34
+ end
35
+
36
+
37
+ describe 'RFlow::Message::Data::HTTP::Response Avro Schema' do
38
+ before(:each) do
39
+ @schema_string = RFlow::Configuration.available_data_types['RFlow::Message::Data::HTTP::Response']['avro']
40
+ end
41
+
42
+ it "should encode and decode an object" do
43
+ response = {
44
+ 'protocol' => 'METHOD',
45
+ 'status_code' => 200,
46
+ 'status_reason_phrase' => 'STATUSREASONPHRASE',
47
+ 'headers' => {
48
+ 'header1' => 'HEADER1',
49
+ 'header2' => 'HEADER2',
50
+ },
51
+ 'content' => 'CONTENT',
52
+ }
53
+
54
+ expect {encode_avro(@schema_string, response)}.to_not raise_error
55
+ avro_encoded_response = encode_avro(@schema_string, response)
56
+
57
+ expect {decode_avro(@schema_string, avro_encoded_response)}.to_not raise_error
58
+ decoded_response = decode_avro(@schema_string, avro_encoded_response)
59
+
60
+ decoded_response['protocol'].should == response['protocol']
61
+ decoded_response['status_code'].should == response['status_code']
62
+ decoded_response['status_reason_phrase'].should == response['status_reason_phrase']
63
+ decoded_response['headers'].should == response['headers']
64
+ decoded_response['content'].should == response['content']
65
+ end
66
+ end
67
+
@@ -0,0 +1,18 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib', 'rflow-components-http'))
2
+
3
+ require 'logger'
4
+
5
+ RFlow.logger = Logger.new STDOUT
6
+
7
+ def decode_avro(schema_string, serialized_object)
8
+ schema = Avro::Schema.parse(schema_string)
9
+ sio = StringIO.new(serialized_object)
10
+ Avro::IO::DatumReader.new(schema, schema).read Avro::IO::BinaryDecoder.new(sio)
11
+ end
12
+
13
+ def encode_avro(schema_string, object)
14
+ schema = Avro::Schema.parse(schema_string)
15
+ sio = StringIO.new
16
+ Avro::IO::DatumWriter.new(schema).write object, Avro::IO::BinaryEncoder.new(sio)
17
+ sio.string
18
+ end
metadata ADDED
@@ -0,0 +1,128 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rflow-components-http
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Michael L. Artz
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2014-03-17 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rflow
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '0.0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: '0.0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: eventmachine_httpserver
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: '0.2'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: '0.2'
46
+ - !ruby/object:Gem::Dependency
47
+ name: rspec
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: '2.6'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '2.6'
62
+ - !ruby/object:Gem::Dependency
63
+ name: rake
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: 0.8.7
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: 0.8.7
78
+ description: HTTP client and server components for the RFlow FBP framework. Also
79
+ includes the necessary HTTP::Request and HTTP::Response message types
80
+ email:
81
+ - michael.artz@redjack.com
82
+ executables: []
83
+ extensions: []
84
+ extra_rdoc_files: []
85
+ files:
86
+ - .gitignore
87
+ - Gemfile
88
+ - Rakefile
89
+ - lib/rflow-components-http.rb
90
+ - lib/rflow/components/http.rb
91
+ - lib/rflow/components/http/client.rb
92
+ - lib/rflow/components/http/extensions.rb
93
+ - lib/rflow/components/http/server.rb
94
+ - lib/rflow/components/http/version.rb
95
+ - rflow-components-http.gemspec
96
+ - schema/http_request.avsc
97
+ - schema/http_response.avsc
98
+ - spec/extensions_spec.rb
99
+ - spec/schema_spec.rb
100
+ - spec/spec_helper.rb
101
+ homepage: ''
102
+ licenses: []
103
+ post_install_message:
104
+ rdoc_options: []
105
+ require_paths:
106
+ - lib
107
+ required_ruby_version: !ruby/object:Gem::Requirement
108
+ none: false
109
+ requirements:
110
+ - - ~>
111
+ - !ruby/object:Gem::Version
112
+ version: '1.9'
113
+ required_rubygems_version: !ruby/object:Gem::Requirement
114
+ none: false
115
+ requirements:
116
+ - - ! '>='
117
+ - !ruby/object:Gem::Version
118
+ version: '0'
119
+ requirements: []
120
+ rubyforge_project: rflow-components-http
121
+ rubygems_version: 1.8.24
122
+ signing_key:
123
+ specification_version: 3
124
+ summary: HTTP client and server components for the RFlow FBP framework
125
+ test_files:
126
+ - spec/extensions_spec.rb
127
+ - spec/schema_spec.rb
128
+ - spec/spec_helper.rb