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 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
- The most basic API looks
23
+ To enable streaming, run
24
24
 
25
- env['thick.async'].async!(*params, &block)
25
+ env['thick.response'].chunked # For chunked encoded stream
26
+ env['thick.response'].streamed # For raw stream
26
27
 
27
- the application has to respond in a standard manner
28
+ and define "thick.async" callback in environment, so it reacts to "call" and takes one argument
28
29
 
29
- [status, headers, body]
30
+ env['thick.async'] = proc do |response|
31
+ # do something here
32
+ end
30
33
 
31
- the response will be processed and send to the client, however the connection is not closed, instead the block is
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
- env['thick.async'].call(chunk)
36
+ To write content to the stream use
35
37
 
36
- after the response is completed the application has to close the connection
38
+ response.writeContent("some content")
37
39
 
38
- env['thick.async'].close
40
+ and do not forget close the stream once done
39
41
 
40
- In more complex scenarios the params and block may be omitted. It's up to the application to wait till the response
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
- env['thick.async'].ready?
44
+ Example using Sinatra
45
45
 
46
- if the method's response is positive it's safe to stream the data.
46
+ require 'sinatra/base'
47
47
 
48
- In the most complex cases it might be necessary to completely bypass default HTTP response generated by the server. In
49
- those cases the application calls
48
+ class App < Sinatra::Base
50
49
 
51
- env['thick.async'].custom!(*params, &block)
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
- the method informs the server to not send any response on the connection and do not close the connection.
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
- env['thick.async'].ready?
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
@@ -44,4 +44,4 @@ OptionParser.new do |opts|
44
44
 
45
45
  end.parse!
46
46
 
47
- Thick.start(options)
47
+ Thick.create(options)
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 'java.io.RandomAccessFile'
5
- java_import 'java.net.InetSocketAddress'
6
- java_import 'java.util.concurrent.Executors'
7
- java_import 'org.jboss.netty.bootstrap.ServerBootstrap'
8
- java_import 'org.jboss.netty.buffer.ChannelBuffer'
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
- @application.call(env)
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.start(options)
4
- @options = {
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: #{@options.inspect}"
12
+ puts "* Starting Thick: #{options.inspect}"
15
13
 
16
- @options[:application] = Loader.new(@options)
17
- @server = Server.new(@options).start
14
+ env = Thick::Java::ServerEnvironment.new
15
+ env.address = options[:address]
16
+ env.port = options[:port]
18
17
 
19
- if @options[:controller]
20
- @controller = Controller.new(@options, @server)
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
@@ -1,3 +1,3 @@
1
1
  module Thick
2
- VERSION = '0.0.5' unless const_defined?(:VERSION)
2
+ VERSION = '0.0.6' unless const_defined?(:VERSION)
3
3
  end
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-3.5.11.Final.jar', __FILE__)
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/buffer'
11
- require 'thick/async_response'
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.5
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-11-30 00:00:00.000000000 Z
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
- bGliL3RoaWNrL3NlcnZlcl9oYW5kbGVyLnJi
70
- - !binary |-
71
- bGliL3RoaWNrL2FzeW5jX3Jlc3BvbnNlLnJi
72
- - !binary |-
73
- bGliL3RoaWNrL2J1ZmZlci5yYg==
74
- - !binary |-
75
- bGliL3RoaWNrL2NvbnRyb2xsZXIucmI=
76
- - !binary |-
77
- bGliL3RoaWNrL3NlcnZlci5yYg==
67
+ bGliL2phcnMvbmV0dHktNC4wLjAuQWxwaGE3Lmphcg==
78
68
  - !binary |-
79
- bGliL2phcnMvbmV0dHktMy41LjExLkZpbmFsLmphcg==
69
+ bGliL2phcnMvdGhpY2stMC4wLjEuamFy
80
70
  - LICENSE
81
71
  - README.md
82
72
  - ROADMAP.md
Binary file
@@ -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
@@ -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
@@ -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