jls-lumberjack 0.0.1

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.
Files changed (2) hide show
  1. data/ruby/lib/lumberjack/server.rb +150 -0
  2. metadata +47 -0
@@ -0,0 +1,150 @@
1
+ require "socket"
2
+ require "thread"
3
+ require "openssl"
4
+ require "zlib"
5
+
6
+ module Lumberjack
7
+ class Server
8
+ attr_reader :port
9
+
10
+ # Create a new Lumberjack server.
11
+ #
12
+ # - options is a hash. Valid options are:
13
+ #
14
+ # * :port - the port to listen on
15
+ # * :address - the host/address to bind to
16
+ # * :ssl_certificate - the path to the ssl cert to use
17
+ # * :ssl_key - the path to the ssl key to use
18
+ # * :ssl_key_passphrase - the key passphrase (optional)
19
+ def initialize(options={})
20
+ @options = {
21
+ :port => 0,
22
+ :address => "0.0.0.0",
23
+ :ssl_certificate => nil,
24
+ :ssl_key => nil,
25
+ :ssl_key_passphrase => nil,
26
+ }.merge(options)
27
+
28
+ [:ssl_certificate, :ssl_key].each do |k|
29
+ if @options[k].nil?
30
+ raise "You must specify #{k} in Lumberjack::Server.new(...)"
31
+ end
32
+ end
33
+
34
+ @tcp_server = TCPServer.new(@options[:port])
35
+ # Query the port in case the port number is '0'
36
+ # TCPServer#addr == [ address_family, port, address, address ]
37
+ @port = @tcp_server.addr[1]
38
+ @ssl = OpenSSL::SSL::SSLContext.new
39
+ @ssl.cert = OpenSSL::X509::Certificate.new(File.read(@options[:ssl_certificate]))
40
+ @ssl.key = OpenSSL::PKey::RSA.new(File.read(@options[:ssl_key]),
41
+ @options[:ssl_key_passphrase])
42
+ @ssl_server = OpenSSL::SSL::SSLServer.new(@tcp_server, @ssl)
43
+ end # def initialize
44
+
45
+ def run(&block)
46
+ while true
47
+ begin
48
+ Thread.new(@ssl_server.accept) do |fd|
49
+ Connection.new(fd).run(&block)
50
+ end
51
+ rescue => e
52
+ p :accept_error => e
53
+ end
54
+ end
55
+ end # def run
56
+ end # class Server
57
+
58
+ class Connection
59
+ def initialize(fd)
60
+ @fd = fd
61
+ end # def initialize
62
+
63
+ def run(&block)
64
+ each_event(&block)
65
+ end # def run
66
+
67
+ def each_event(&block)
68
+ last_ack = 0
69
+ window_size = 0
70
+ io = IOWrap.new(@fd)
71
+ while true
72
+ version = io.read(1)
73
+ frame = io.read(1)
74
+
75
+ if frame == "W" # window size
76
+ window_size = io.read(4).unpack("N").first / 2
77
+ #puts "Window size: #{window_size}"
78
+ next
79
+ elsif frame == "C" # compressed data
80
+ length = io.read(4).unpack("N").first
81
+ #puts "Compressed frame length #{length}"
82
+ compressed = io.read(length)
83
+ original = Zlib::Inflate.inflate(compressed)
84
+ #original = LZ4::uncompress(compressed, length)
85
+ io.pushback(original)
86
+ next
87
+ elsif frame != "D"
88
+ #puts "Unexpected frame type: #{version.inspect} / #{frame.inspect}"
89
+ io.close
90
+ return
91
+ end
92
+ #
93
+ # data frame
94
+ sequence = io.read(4).unpack("N").first
95
+ count = io.read(4).unpack("N").first
96
+
97
+ map = {}
98
+ count.times do
99
+ key_len = io.read(4).unpack("N").first
100
+ key = io.read(key_len);
101
+ value_len = io.read(4).unpack("N").first
102
+ value = io.read(value_len);
103
+ map[key] = value
104
+ end
105
+
106
+ block.call(map)
107
+
108
+ if sequence - last_ack >= window_size
109
+ # ack this.
110
+ io.syswrite(["1", "A", sequence].pack("AAN"))
111
+ last_ack = sequence
112
+ end
113
+ end
114
+ end # def each_event
115
+ end # class Connection
116
+
117
+ # Wrap an io-like object but support pushback.
118
+ class IOWrap
119
+ def initialize(io)
120
+ @io = io
121
+ @buffer = ""
122
+ end
123
+
124
+ def read(bytes)
125
+ if @buffer.empty?
126
+ #puts "reading direct from @io"
127
+ return @io.read(bytes)
128
+ elsif @buffer.length > bytes
129
+ #puts "reading buffered"
130
+ data = @buffer[0...bytes]
131
+ @buffer[0...bytes] = ""
132
+ return data
133
+ else
134
+ data = @buffer.clone
135
+ @buffer.clear
136
+ return data + @io.read(bytes - data.length)
137
+ end
138
+ end
139
+
140
+ def pushback(data)
141
+ #puts "Pushback: #{data[0..30].inspect}..."
142
+ @buffer += data
143
+ end
144
+
145
+ def method_missing(method, *args)
146
+ @io.send(method, *args)
147
+ end
148
+ end # class IOWrap
149
+ end # module Lumberjack
150
+
metadata ADDED
@@ -0,0 +1,47 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: jls-lumberjack
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Jordan Sissel
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-09-18 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: lumberjack log transport library
15
+ email:
16
+ - jls@semicomplete.com
17
+ executables: []
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - ruby/lib/lumberjack/server.rb
22
+ homepage: https://github.com/jordansissel/lumberjack
23
+ licenses: []
24
+ post_install_message:
25
+ rdoc_options: []
26
+ require_paths:
27
+ - ruby/lib
28
+ required_ruby_version: !ruby/object:Gem::Requirement
29
+ none: false
30
+ requirements:
31
+ - - ! '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ required_rubygems_version: !ruby/object:Gem::Requirement
35
+ none: false
36
+ requirements:
37
+ - - ! '>='
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
40
+ requirements: []
41
+ rubyforge_project:
42
+ rubygems_version: 1.8.24
43
+ signing_key:
44
+ specification_version: 3
45
+ summary: lumberjack log transport library
46
+ test_files: []
47
+ has_rdoc: