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 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