thick 0.0.5-java → 0.0.6-java
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +28 -25
- data/bin/thick +1 -1
- data/lib/jars/netty-4.0.0.Alpha7.jar +0 -0
- data/lib/jars/thick-0.0.1.jar +0 -0
- data/lib/thick/java.rb +5 -48
- data/lib/thick/loader.rb +22 -1
- data/lib/thick/thick.rb +8 -14
- data/lib/thick/version.rb +1 -1
- data/lib/thick.rb +4 -10
- metadata +4 -14
- data/lib/jars/netty-3.5.11.Final.jar +0 -0
- data/lib/thick/async_response.rb +0 -88
- data/lib/thick/buffer.rb +0 -42
- data/lib/thick/controller.rb +0 -88
- data/lib/thick/pipeline_factory.rb +0 -29
- data/lib/thick/server.rb +0 -25
- data/lib/thick/server_handler.rb +0 -147
data/README.md
CHANGED
@@ -20,45 +20,48 @@ Thick provides interfaces to control it's behaviour.
|
|
20
20
|
|
21
21
|
Asynchronous responses provide functionality to stream response to the client by chunks.
|
22
22
|
|
23
|
-
|
23
|
+
To enable streaming, run
|
24
24
|
|
25
|
-
env['thick.
|
25
|
+
env['thick.response'].chunked # For chunked encoded stream
|
26
|
+
env['thick.response'].streamed # For raw stream
|
26
27
|
|
27
|
-
|
28
|
+
and define "thick.async" callback in environment, so it reacts to "call" and takes one argument
|
28
29
|
|
29
|
-
[
|
30
|
+
env['thick.async'] = proc do |response|
|
31
|
+
# do something here
|
32
|
+
end
|
30
33
|
|
31
|
-
|
32
|
-
executed with parameters passed as params. From the block the application can stream data to the client
|
34
|
+
now simply respond as usual. Once the headers and the basic content is sent, the callback will be invoked.
|
33
35
|
|
34
|
-
|
36
|
+
To write content to the stream use
|
35
37
|
|
36
|
-
|
38
|
+
response.writeContent("some content")
|
37
39
|
|
38
|
-
|
40
|
+
and do not forget close the stream once done
|
39
41
|
|
40
|
-
|
41
|
-
header is sent to the client and stream it's data. To simplify the signalization the application may check if it's safe
|
42
|
-
to send it's data by.
|
42
|
+
response.close
|
43
43
|
|
44
|
-
|
44
|
+
Example using Sinatra
|
45
45
|
|
46
|
-
|
46
|
+
require 'sinatra/base'
|
47
47
|
|
48
|
-
|
49
|
-
those cases the application calls
|
48
|
+
class App < Sinatra::Base
|
50
49
|
|
51
|
-
|
50
|
+
get '/' do
|
51
|
+
env['thick.async'] = proc do |response|
|
52
|
+
(1..10).each do |i|
|
53
|
+
response.writeContent(i.to_s)
|
54
|
+
sleep(1)
|
55
|
+
end
|
56
|
+
response.close
|
57
|
+
end
|
58
|
+
env['thick.response'].chunked
|
59
|
+
"0"
|
60
|
+
end
|
52
61
|
|
53
|
-
|
54
|
-
The contract is the same as with async! mechanism. The block with parameters params is triggered by the server when
|
55
|
-
it's safe to stream data. If the block is not given it's completely up to the application to stream the data.
|
56
|
-
The application may check whether the server considers safe to stream data by the same way as with async! response
|
62
|
+
end
|
57
63
|
|
58
|
-
|
59
|
-
|
60
|
-
In the case of custom responses it should be safe to stream right away without checking the server's opinion. Also with
|
61
|
-
custom responses it's not necessary to return standard rack response.
|
64
|
+
run App
|
62
65
|
|
63
66
|
## Starting
|
64
67
|
|
data/bin/thick
CHANGED
Binary file
|
Binary file
|
data/lib/thick/java.rb
CHANGED
@@ -1,53 +1,10 @@
|
|
1
1
|
module Thick
|
2
2
|
module Java
|
3
3
|
|
4
|
-
java_import '
|
5
|
-
java_import '
|
6
|
-
java_import '
|
7
|
-
java_import '
|
8
|
-
java_import '
|
9
|
-
java_import 'org.jboss.netty.buffer.ChannelBufferInputStream'
|
10
|
-
java_import 'org.jboss.netty.buffer.ChannelBuffers'
|
11
|
-
java_import 'org.jboss.netty.handler.codec.http.DefaultHttpChunk'
|
12
|
-
java_import 'org.jboss.netty.handler.codec.http.DefaultHttpResponse'
|
13
|
-
java_import 'org.jboss.netty.handler.codec.http.HttpContentDecompressor'
|
14
|
-
java_import 'org.jboss.netty.handler.codec.http.HttpHeaders'
|
15
|
-
java_import 'org.jboss.netty.handler.codec.http.HttpChunk'
|
16
|
-
java_import 'org.jboss.netty.handler.codec.http.HttpChunkAggregator'
|
17
|
-
java_import 'org.jboss.netty.handler.codec.http.HttpRequest'
|
18
|
-
java_import 'org.jboss.netty.handler.codec.http.HttpRequestDecoder'
|
19
|
-
java_import 'org.jboss.netty.handler.codec.http.HttpResponseEncoder'
|
20
|
-
java_import 'org.jboss.netty.handler.codec.http.HttpResponseStatus'
|
21
|
-
java_import 'org.jboss.netty.handler.codec.http.HttpVersion'
|
22
|
-
java_import 'org.jboss.netty.handler.codec.http.QueryStringDecoder'
|
23
|
-
java_import 'org.jboss.netty.handler.codec.http.websocketx.CloseWebSocketFrame'
|
24
|
-
java_import 'org.jboss.netty.handler.codec.http.websocketx.PingWebSocketFrame'
|
25
|
-
java_import 'org.jboss.netty.handler.codec.http.websocketx.PongWebSocketFrame'
|
26
|
-
java_import 'org.jboss.netty.handler.codec.http.websocketx.TextWebSocketFrame'
|
27
|
-
java_import 'org.jboss.netty.handler.codec.http.websocketx.WebSocketFrame'
|
28
|
-
java_import 'org.jboss.netty.handler.codec.http.websocketx.WebSocketServerHandshakerFactory'
|
29
|
-
java_import 'org.jboss.netty.handler.codec.spdy.SpdyFrameDecoder'
|
30
|
-
java_import 'org.jboss.netty.handler.codec.spdy.SpdyFrameEncoder'
|
31
|
-
java_import 'org.jboss.netty.handler.codec.spdy.SpdySessionHandler'
|
32
|
-
java_import 'org.jboss.netty.handler.execution.ExecutionHandler'
|
33
|
-
java_import 'org.jboss.netty.handler.execution.OrderedMemoryAwareThreadPoolExecutor'
|
34
|
-
java_import 'org.jboss.netty.handler.logging.LoggingHandler'
|
35
|
-
java_import 'org.jboss.netty.handler.stream.ChunkedStream'
|
36
|
-
java_import 'org.jboss.netty.handler.stream.ChunkedWriteHandler'
|
37
|
-
java_import 'org.jboss.netty.channel.DefaultFileRegion'
|
38
|
-
java_import 'org.jboss.netty.channel.ChannelFutureListener'
|
39
|
-
java_import 'org.jboss.netty.channel.ChannelPipelineFactory'
|
40
|
-
java_import 'org.jboss.netty.channel.Channels'
|
41
|
-
java_import 'org.jboss.netty.channel.SimpleChannelUpstreamHandler'
|
42
|
-
java_import 'org.jboss.netty.channel.ChannelUpstreamHandler'
|
43
|
-
java_import 'org.jboss.netty.channel.ChannelDownstreamHandler'
|
44
|
-
java_import 'org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory'
|
45
|
-
java_import 'org.jboss.netty.logging.InternalLogLevel'
|
46
|
-
java_import 'org.jboss.netty.util.CharsetUtil'
|
47
|
-
java_import 'org.jruby.CompatVersion'
|
48
|
-
java_import 'org.jruby.embed.AttributeName'
|
49
|
-
java_import 'org.jruby.embed.LocalContextScope'
|
50
|
-
java_import 'org.jruby.embed.ScriptingContainer'
|
51
|
-
|
4
|
+
java_import 'cz.marekjelen.thick.Server'
|
5
|
+
java_import 'cz.marekjelen.thick.ServerEnvironment'
|
6
|
+
java_import 'cz.marekjelen.thick.ServerRubyInterface'
|
7
|
+
java_import 'io.netty.buffer.Unpooled'
|
8
|
+
java_import 'io.netty.buffer.ByteBufInputStream'
|
52
9
|
end
|
53
10
|
end
|
data/lib/thick/loader.rb
CHANGED
@@ -2,6 +2,8 @@ module Thick
|
|
2
2
|
|
3
3
|
class Loader
|
4
4
|
|
5
|
+
include Thick::Java::ServerRubyInterface
|
6
|
+
|
5
7
|
def initialize(options)
|
6
8
|
@options = options
|
7
9
|
ENV['RACK_ENV'] ||= ENV['RAILS_ENV'] ||= @options[:environment]
|
@@ -9,7 +11,26 @@ module Thick
|
|
9
11
|
end
|
10
12
|
|
11
13
|
def call(env)
|
12
|
-
|
14
|
+
env['rack.input'] = env['rack.input'].to_io
|
15
|
+
env['rack.errors'] = env['rack.errors'].to_io
|
16
|
+
|
17
|
+
status, headers, body = @application.call(env)
|
18
|
+
|
19
|
+
response = env['thick.response']
|
20
|
+
|
21
|
+
response.setStatus(status)
|
22
|
+
|
23
|
+
headers.each_pair do |name, value|
|
24
|
+
response.setHeader(name, value)
|
25
|
+
end
|
26
|
+
|
27
|
+
if body.respond_to?(:to_path)
|
28
|
+
response.send_file(body.to_path)
|
29
|
+
else
|
30
|
+
body.each { |chunk| response.writeContent(chunk.to_s) }
|
31
|
+
response.send
|
32
|
+
env['thick.async'].call(response) if response.chunked?
|
33
|
+
end
|
13
34
|
rescue => e
|
14
35
|
puts e.message
|
15
36
|
puts e.backtrace
|
data/lib/thick/thick.rb
CHANGED
@@ -1,29 +1,23 @@
|
|
1
1
|
module Thick
|
2
2
|
|
3
|
-
def self.
|
4
|
-
|
3
|
+
def self.create(options)
|
4
|
+
options = {
|
5
5
|
:address => '0.0.0.0',
|
6
6
|
:port => 9292,
|
7
7
|
:environment => 'development',
|
8
|
-
:reloader => false,
|
9
|
-
:controller => false,
|
10
8
|
:directory => Dir.getwd,
|
11
9
|
:file => 'config.ru'
|
12
10
|
}.merge(options)
|
13
11
|
|
14
|
-
puts "* Starting Thick: #{
|
12
|
+
puts "* Starting Thick: #{options.inspect}"
|
15
13
|
|
16
|
-
|
17
|
-
|
14
|
+
env = Thick::Java::ServerEnvironment.new
|
15
|
+
env.address = options[:address]
|
16
|
+
env.port = options[:port]
|
18
17
|
|
19
|
-
|
20
|
-
|
21
|
-
@controller.start
|
22
|
-
end
|
18
|
+
env.application = Loader.new(options)
|
19
|
+
Thick::Java::Server.new(env).start
|
23
20
|
|
24
|
-
loop do
|
25
|
-
sleep(100) # ToDo: Is this hack needed?
|
26
|
-
end
|
27
21
|
end
|
28
22
|
|
29
23
|
end
|
data/lib/thick/version.rb
CHANGED
data/lib/thick.rb
CHANGED
@@ -2,16 +2,10 @@ require 'rack'
|
|
2
2
|
|
3
3
|
require 'java'
|
4
4
|
|
5
|
-
require File.expand_path('../jars/netty-
|
5
|
+
require File.expand_path('../jars/netty-4.0.0.Alpha7.jar', __FILE__)
|
6
|
+
require File.expand_path('../jars/thick-0.0.1.jar', __FILE__)
|
6
7
|
|
7
8
|
require 'thick/java'
|
8
|
-
require 'thick/thick'
|
9
9
|
require 'thick/version'
|
10
|
-
require 'thick/
|
11
|
-
require 'thick/
|
12
|
-
require 'thick/loader'
|
13
|
-
require 'thick/server_handler'
|
14
|
-
require 'thick/pipeline_factory'
|
15
|
-
require 'thick/controller'
|
16
|
-
require 'thick/server'
|
17
|
-
|
10
|
+
require 'thick/thick'
|
11
|
+
require 'thick/loader'
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: thick
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.6
|
5
5
|
prerelease:
|
6
6
|
platform: java
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-12-01 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rack
|
@@ -55,8 +55,6 @@ files:
|
|
55
55
|
YmluL3RoaWNr
|
56
56
|
- !binary |-
|
57
57
|
bGliL3RoaWNrLnJi
|
58
|
-
- !binary |-
|
59
|
-
bGliL3RoaWNrL3BpcGVsaW5lX2ZhY3RvcnkucmI=
|
60
58
|
- !binary |-
|
61
59
|
bGliL3RoaWNrL2phdmEucmI=
|
62
60
|
- !binary |-
|
@@ -66,17 +64,9 @@ files:
|
|
66
64
|
- !binary |-
|
67
65
|
bGliL3RoaWNrL3ZlcnNpb24ucmI=
|
68
66
|
- !binary |-
|
69
|
-
|
70
|
-
- !binary |-
|
71
|
-
bGliL3RoaWNrL2FzeW5jX3Jlc3BvbnNlLnJi
|
72
|
-
- !binary |-
|
73
|
-
bGliL3RoaWNrL2J1ZmZlci5yYg==
|
74
|
-
- !binary |-
|
75
|
-
bGliL3RoaWNrL2NvbnRyb2xsZXIucmI=
|
76
|
-
- !binary |-
|
77
|
-
bGliL3RoaWNrL3NlcnZlci5yYg==
|
67
|
+
bGliL2phcnMvbmV0dHktNC4wLjAuQWxwaGE3Lmphcg==
|
78
68
|
- !binary |-
|
79
|
-
|
69
|
+
bGliL2phcnMvdGhpY2stMC4wLjEuamFy
|
80
70
|
- LICENSE
|
81
71
|
- README.md
|
82
72
|
- ROADMAP.md
|
Binary file
|
data/lib/thick/async_response.rb
DELETED
@@ -1,88 +0,0 @@
|
|
1
|
-
module Thick
|
2
|
-
|
3
|
-
class AsyncResponse
|
4
|
-
|
5
|
-
def initialize(context)
|
6
|
-
@context = context
|
7
|
-
@ready = false
|
8
|
-
@custom = false
|
9
|
-
@async = false
|
10
|
-
@block = nil
|
11
|
-
@chunked = false
|
12
|
-
@content_length = nil
|
13
|
-
@closed = false
|
14
|
-
@params = []
|
15
|
-
end
|
16
|
-
|
17
|
-
def call(chunk)
|
18
|
-
chunk = Thick::Java::ChannelBuffers.copiedBuffer(chunk.to_s, Thick::Java::CharsetUtil::UTF_8)
|
19
|
-
chunk = Thick::Java::DefaultHttpChunk.new(chunk) if @chunked
|
20
|
-
@context.channel.write(chunk) unless closed?
|
21
|
-
end
|
22
|
-
|
23
|
-
def close
|
24
|
-
if @chunked
|
25
|
-
chunk = Thick::Java::ChannelBuffers.copiedBuffer("0\r\n\r\n", Thick::Java::CharsetUtil::UTF_8)
|
26
|
-
else
|
27
|
-
chunk = Thick::Java::ChannelBuffers::EMPTY_BUFFER
|
28
|
-
end
|
29
|
-
@context.channel.write(chunk).addListener(Thick::Java::ChannelFutureListener::CLOSE) unless closed?
|
30
|
-
closed!
|
31
|
-
end
|
32
|
-
|
33
|
-
def ready!
|
34
|
-
@ready = true
|
35
|
-
@block.call(*@params) if @block
|
36
|
-
end
|
37
|
-
|
38
|
-
def ready?
|
39
|
-
@ready
|
40
|
-
end
|
41
|
-
|
42
|
-
def custom!(*params, &block)
|
43
|
-
@custom = true
|
44
|
-
@block = block
|
45
|
-
@params = params
|
46
|
-
end
|
47
|
-
|
48
|
-
def custom?
|
49
|
-
@custom
|
50
|
-
end
|
51
|
-
|
52
|
-
def async!(*params, &block)
|
53
|
-
@async = true
|
54
|
-
@block = block
|
55
|
-
@params = params
|
56
|
-
end
|
57
|
-
|
58
|
-
def async?
|
59
|
-
@async
|
60
|
-
end
|
61
|
-
|
62
|
-
def chunked!
|
63
|
-
@chunked = true
|
64
|
-
end
|
65
|
-
|
66
|
-
def chunked?
|
67
|
-
@chunked
|
68
|
-
end
|
69
|
-
|
70
|
-
def content_length!(length)
|
71
|
-
@content_length = length
|
72
|
-
end
|
73
|
-
|
74
|
-
def content_length
|
75
|
-
@content_length
|
76
|
-
end
|
77
|
-
|
78
|
-
def closed!
|
79
|
-
@closed = true
|
80
|
-
end
|
81
|
-
|
82
|
-
def closed?
|
83
|
-
@closed
|
84
|
-
end
|
85
|
-
|
86
|
-
end
|
87
|
-
|
88
|
-
end
|
data/lib/thick/buffer.rb
DELETED
@@ -1,42 +0,0 @@
|
|
1
|
-
module Thick
|
2
|
-
|
3
|
-
class Buffer
|
4
|
-
|
5
|
-
def initialize
|
6
|
-
@buffers = []
|
7
|
-
@buffer = nil
|
8
|
-
@io = nil
|
9
|
-
end
|
10
|
-
|
11
|
-
def io
|
12
|
-
@buffer ||= Thick::Java::ChannelBuffers.wrapped_buffer(*@buffers)
|
13
|
-
@io ||= Thick::Java::ChannelBufferInputStream.new(@buffer).to_io.binmode
|
14
|
-
end
|
15
|
-
|
16
|
-
def write(data)
|
17
|
-
raise ArgumentError unless data.kind_of?(Thick::Java::ChannelBuffer)
|
18
|
-
@buffers << data
|
19
|
-
@buffer = nil
|
20
|
-
@io = nil
|
21
|
-
end
|
22
|
-
|
23
|
-
def gets
|
24
|
-
self.io.gets
|
25
|
-
end
|
26
|
-
|
27
|
-
def read(length = nil, buffer = nil)
|
28
|
-
buffer ? self.io.read(length, buffer) : self.io.read(length)
|
29
|
-
end
|
30
|
-
|
31
|
-
def each(&block)
|
32
|
-
self.io.each(&block)
|
33
|
-
end
|
34
|
-
|
35
|
-
def rewind
|
36
|
-
@buffer.reader_index(0) if @buffer
|
37
|
-
@io = nil
|
38
|
-
end
|
39
|
-
|
40
|
-
end
|
41
|
-
|
42
|
-
end
|
data/lib/thick/controller.rb
DELETED
@@ -1,88 +0,0 @@
|
|
1
|
-
module Thick
|
2
|
-
class Controller
|
3
|
-
|
4
|
-
def initialize(options, server)
|
5
|
-
@options = options
|
6
|
-
@server = server
|
7
|
-
@factory = Thick::Java::NioServerSocketChannelFactory.new(Thick::Java::Executors.new_cached_thread_pool, Thick::Java::Executors.new_cached_thread_pool)
|
8
|
-
@bootstrap = Thick::Java::ServerBootstrap.new(@factory)
|
9
|
-
@bootstrap.pipeline_factory = Controller::PipelineFactory.new(@options)
|
10
|
-
end
|
11
|
-
|
12
|
-
def start
|
13
|
-
@channel = @bootstrap.bind(Thick::Java::InetSocketAddress.new('127.0.0.1', 9393))
|
14
|
-
puts "* Controller started on 127.0.0.1:9393"
|
15
|
-
self
|
16
|
-
end
|
17
|
-
|
18
|
-
def stop
|
19
|
-
@channel.close
|
20
|
-
self
|
21
|
-
end
|
22
|
-
|
23
|
-
class PipelineFactory
|
24
|
-
|
25
|
-
include Thick::Java::ChannelPipelineFactory
|
26
|
-
|
27
|
-
def initialize(options)
|
28
|
-
@options = options
|
29
|
-
end
|
30
|
-
|
31
|
-
def getPipeline
|
32
|
-
pipeline = Thick::Java::Channels.pipeline
|
33
|
-
pipeline.add_last('decoder', Thick::Java::HttpRequestDecoder.new)
|
34
|
-
pipeline.add_last('aggregator', Thick::Java::HttpChunkAggregator.new(65536))
|
35
|
-
pipeline.add_last('encoder', Thick::Java::HttpResponseEncoder.new)
|
36
|
-
pipeline.add_last('handler', Controller::Handler.new(@options))
|
37
|
-
pipeline
|
38
|
-
end
|
39
|
-
|
40
|
-
end
|
41
|
-
|
42
|
-
class Handler < Thick::Java::SimpleChannelUpstreamHandler
|
43
|
-
|
44
|
-
def messageReceived(context, event)
|
45
|
-
message = event.message
|
46
|
-
case message
|
47
|
-
when Thick::Java::HttpRequest
|
48
|
-
handle_request(context, message)
|
49
|
-
when Thick::Java::WebSocketFrame
|
50
|
-
handle_websocket_frame(context, message)
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
def handle_request(context, request)
|
55
|
-
url = "ws://" + request.get_header(Thick::Java::HttpHeaders::Names::HOST) + '/controller'
|
56
|
-
factory = Thick::Java::WebSocketServerHandshakerFactory.new(url, nil, false)
|
57
|
-
@handshaker = factory.new_handshaker(request)
|
58
|
-
if @handshaker
|
59
|
-
@handshaker.handshake(context, request)
|
60
|
-
else
|
61
|
-
factory.send_unsupported_webSocket_version_response(context)
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
def handle_websocket_frame(context, frame)
|
66
|
-
case frame
|
67
|
-
when Thick::Java::CloseWebSocketFrame
|
68
|
-
@handshaker.close(context, frame)
|
69
|
-
when Thick::Java::PingWebSocketFrame
|
70
|
-
context.channel.write(Thick::Java::PongWebSocketFrame.new(frame.binary_data))
|
71
|
-
when Thick::Java::TextWebSocketFrame
|
72
|
-
handle_websocket_message(context, frame.text)
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
def websocket_send(context, message)
|
77
|
-
context.channel.write(Thick::Java::TextWebSocketFrame.new(message.to_s))
|
78
|
-
end
|
79
|
-
|
80
|
-
def handle_websocket_message(context, message)
|
81
|
-
|
82
|
-
end
|
83
|
-
|
84
|
-
end
|
85
|
-
|
86
|
-
end
|
87
|
-
|
88
|
-
end
|
@@ -1,29 +0,0 @@
|
|
1
|
-
module Thick
|
2
|
-
|
3
|
-
class PipelineFactory
|
4
|
-
|
5
|
-
include Thick::Java::ChannelPipelineFactory
|
6
|
-
|
7
|
-
def initialize(options)
|
8
|
-
@options = options
|
9
|
-
@executor = Thick::Java::ExecutionHandler.new(Thick::Java::OrderedMemoryAwareThreadPoolExecutor.new(20, 0, 0))
|
10
|
-
end
|
11
|
-
|
12
|
-
def getPipeline
|
13
|
-
pipeline = Thick::Java::Channels.pipeline
|
14
|
-
|
15
|
-
pipeline.add_last('http_decoder', Thick::Java::HttpRequestDecoder.new)
|
16
|
-
pipeline.add_last('http_encoder', Thick::Java::HttpResponseEncoder.new)
|
17
|
-
|
18
|
-
pipeline.add_last('decompressor', Thick::Java::HttpContentDecompressor.new)
|
19
|
-
|
20
|
-
pipeline.add_last('executor', @executor)
|
21
|
-
|
22
|
-
pipeline.add_last('handler', ServerHandler.new(@options))
|
23
|
-
|
24
|
-
pipeline
|
25
|
-
end
|
26
|
-
|
27
|
-
end
|
28
|
-
|
29
|
-
end
|
data/lib/thick/server.rb
DELETED
@@ -1,25 +0,0 @@
|
|
1
|
-
module Thick
|
2
|
-
|
3
|
-
class Server
|
4
|
-
|
5
|
-
def initialize(options)
|
6
|
-
@options = options
|
7
|
-
@factory = Thick::Java::NioServerSocketChannelFactory.new(Thick::Java::Executors.new_cached_thread_pool, Thick::Java::Executors.new_cached_thread_pool)
|
8
|
-
@bootstrap = Thick::Java::ServerBootstrap.new(@factory)
|
9
|
-
@bootstrap.pipeline_factory = PipelineFactory.new(@options)
|
10
|
-
end
|
11
|
-
|
12
|
-
def start
|
13
|
-
@channel = @bootstrap.bind(Thick::Java::InetSocketAddress.new(@options[:address], @options[:port]))
|
14
|
-
puts "* Server started on #{@options[:address]}:#{@options[:port]}"
|
15
|
-
self
|
16
|
-
end
|
17
|
-
|
18
|
-
def stop
|
19
|
-
@channel.close
|
20
|
-
self
|
21
|
-
end
|
22
|
-
|
23
|
-
end
|
24
|
-
|
25
|
-
end
|
data/lib/thick/server_handler.rb
DELETED
@@ -1,147 +0,0 @@
|
|
1
|
-
module Thick
|
2
|
-
|
3
|
-
class ServerHandler < Thick::Java::SimpleChannelUpstreamHandler
|
4
|
-
|
5
|
-
def initialize(options)
|
6
|
-
super()
|
7
|
-
@options = options
|
8
|
-
end
|
9
|
-
|
10
|
-
def channelConnected(context, event)
|
11
|
-
end
|
12
|
-
|
13
|
-
def messageReceived(context, event)
|
14
|
-
request = event.message
|
15
|
-
case request
|
16
|
-
when Thick::Java::HttpRequest
|
17
|
-
on_request(context, request)
|
18
|
-
when Thick::Java::HttpChunk
|
19
|
-
on_chunk(context, request)
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
def serve_file(context, file, from = 0, to = nil)
|
24
|
-
raf = Thick::Java::RandomAccessFile.new(file, "r")
|
25
|
-
to ||= File.size(file)
|
26
|
-
region = Thick::Java::DefaultFileRegion.new(raf.channel, from, to)
|
27
|
-
context.channel.write(region)
|
28
|
-
end
|
29
|
-
|
30
|
-
def on_request(context, request)
|
31
|
-
|
32
|
-
# Serve file if exists in public directory
|
33
|
-
file = File.join(@options[:directory], 'public', request.uri)
|
34
|
-
return serve_file(context, file).addListener(Thick::Java::ChannelFutureListener::CLOSE) if !request.uri.index('..') && File.exists?(file) && !File.directory?(file)
|
35
|
-
|
36
|
-
query_string = request.uri.split('?', 2)
|
37
|
-
@env = {
|
38
|
-
# Rack specific
|
39
|
-
'rack.version' => [1,1],
|
40
|
-
'rack.url_scheme' => 'http', # ToDo: support https?
|
41
|
-
'rack.input' => Buffer.new,
|
42
|
-
'rack.errors' => $stdout, # ToDo: Sure about that?
|
43
|
-
'rack.multithread' => true,
|
44
|
-
'rack.multiprocess' => false,
|
45
|
-
'rack.run_once' => false,
|
46
|
-
# HTTP specific
|
47
|
-
'REQUEST_METHOD' => request.get_method.name,
|
48
|
-
'SCRIPT_NAME' => '',
|
49
|
-
'PATH_INFO' => query_string[0],
|
50
|
-
'QUERY_STRING' => if query_string[1] && query_string[1] != '' then "?#{query_string[1]}" else '' end,
|
51
|
-
'SERVER_NAME' => 'localhost', # ToDo: Be more precise!
|
52
|
-
'SERVER_PORT' => '8080', # ToDo: Be more precise!
|
53
|
-
# Thick specific
|
54
|
-
'thick.async' => AsyncResponse.new(context),
|
55
|
-
'thick.options' => @options
|
56
|
-
}
|
57
|
-
|
58
|
-
# Get content length if available
|
59
|
-
length = Thick::Java::HttpHeaders.get_content_length(request)
|
60
|
-
@env['CONTENT_LENGTH'] = length if length > 0
|
61
|
-
|
62
|
-
# Extract headers and normalize it's names
|
63
|
-
request.header_names.each do |name|
|
64
|
-
rack_name = name.gsub('-', '_').upcase
|
65
|
-
rack_name = "HTTP_#{rack_name}" unless %w(CONTENT_TYPE CONTENT_LENGTH).include?(rack_name)
|
66
|
-
@env[rack_name] = request.get_header(name)
|
67
|
-
end
|
68
|
-
|
69
|
-
# Write request content into prepared IO
|
70
|
-
@env['rack.input'].write(request.content)
|
71
|
-
|
72
|
-
# No chunks expected, handle request
|
73
|
-
handle_request(context) unless request.chunked?
|
74
|
-
end
|
75
|
-
|
76
|
-
def on_chunk(context, chunk)
|
77
|
-
# Write request content into prepared IO
|
78
|
-
@env['rack.input'].write(chunk.content)
|
79
|
-
|
80
|
-
# No more chunks expected, handle request
|
81
|
-
handle_request(context) if chunk.last?
|
82
|
-
end
|
83
|
-
|
84
|
-
def handle_request(context)
|
85
|
-
response = @options[:application].call(@env)
|
86
|
-
|
87
|
-
# Let the application make the response as it wants
|
88
|
-
if @env['thick.async'].custom?
|
89
|
-
@env['thick.async'].ready!
|
90
|
-
return
|
91
|
-
end
|
92
|
-
|
93
|
-
# Unpack response
|
94
|
-
status, headers, content = response
|
95
|
-
|
96
|
-
# Create response and set status as requested by application
|
97
|
-
@response = Thick::Java::DefaultHttpResponse.new(Thick::Java::HttpVersion::HTTP_1_1, Thick::Java::HttpResponseStatus.value_of(status.to_i))
|
98
|
-
|
99
|
-
# Set headers as requested by application
|
100
|
-
headers.each do |name, value|
|
101
|
-
Thick::Java::HttpHeaders.set_header(@response, name, value)
|
102
|
-
end
|
103
|
-
|
104
|
-
if @env['thick.async'].async?
|
105
|
-
if length = @env['thick.async'].content_length
|
106
|
-
Thick::Java::HttpHeaders.set_content_length(@response, length)
|
107
|
-
else
|
108
|
-
@response.remove_header('Content-Length')
|
109
|
-
end
|
110
|
-
@response.chunked = true if @env['thick.async'].chunked?
|
111
|
-
end
|
112
|
-
|
113
|
-
context.channel.write(@response)
|
114
|
-
|
115
|
-
# Set content as requested by application
|
116
|
-
case
|
117
|
-
when content.respond_to?(:to_path)
|
118
|
-
serve_file(context, content.to_path)
|
119
|
-
else
|
120
|
-
content.each do |chunk|
|
121
|
-
context.channel.write(Thick::Java::ChannelBuffers.copiedBuffer(chunk, Thick::Java::CharsetUtil::UTF_8))
|
122
|
-
end
|
123
|
-
end
|
124
|
-
|
125
|
-
# When content is closable, close it
|
126
|
-
content.close if content.respond_to?(:close)
|
127
|
-
|
128
|
-
# Trigger async handler and marker if async response requested, close connection otherwise
|
129
|
-
if @env['thick.async'].async?
|
130
|
-
@env['thick.async'].ready!
|
131
|
-
else
|
132
|
-
@env['thick.async'].close
|
133
|
-
end
|
134
|
-
end
|
135
|
-
|
136
|
-
def channelClosed(context, event)
|
137
|
-
@env['thick.async'].closed!
|
138
|
-
end
|
139
|
-
|
140
|
-
def exceptionCaught(context, e)
|
141
|
-
puts e.message if e.respond_to?(:message)
|
142
|
-
puts e.backtrace if e.respond_to?(:backtrace)
|
143
|
-
end
|
144
|
-
|
145
|
-
end
|
146
|
-
|
147
|
-
end
|