thick 0.0.5-java → 0.0.6-java
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/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
|