diff options
author | Kazuki Yamaguchi <k@rhe.jp> | 2017-06-08 11:36:52 +0900 |
---|---|---|
committer | Kazuki Yamaguchi <k@rhe.jp> | 2017-06-08 12:47:51 +0900 |
commit | 353fd27a96c576f248fe54e74ce75aeaab711322 (patch) | |
tree | 97640fc86206c24c53a1f99dac3a365f8f1c297b | |
parent | 4d86351b6799db4338561ec84c5531ba14ce64b2 (diff) | |
download | puke-353fd27a96c576f248fe54e74ce75aeaab711322.tar.gz |
Use IO-like object as the response body
Introduce Puke::ConcatenatedIO that behaves like IO and reads
sequentially from multiple sources. As of Ruby 2.4, #readpartial (with
two argument) and #close are required by WEBrick.
This allows us to avoid loading the entire content of the mboxes on
memory.
-rw-r--r-- | lib/puke/concatenated_io.rb | 29 | ||||
-rw-r--r-- | lib/puke/core.rb | 4 | ||||
-rw-r--r-- | lib/puke/server.rb | 14 |
3 files changed, 40 insertions, 7 deletions
diff --git a/lib/puke/concatenated_io.rb b/lib/puke/concatenated_io.rb new file mode 100644 index 0000000..dfa2940 --- /dev/null +++ b/lib/puke/concatenated_io.rb @@ -0,0 +1,29 @@ +require_relative "core" + +class Puke::ConcatenatedIO + def initialize(ios) + @ios = ios + end + + def readpartial(n, buf) + rotate_if_needed + f = @ios.first or raise EOFError + r = f.readpartial(n, buf) + r + end + + def close + @ios.each(&:close) + # Ensure #readpartial won't be called anymore + @ios = nil + end + + private + + def rotate_if_needed + if f = @ios.first and f.eof? + f.close + @ios.shift + end + end +end diff --git a/lib/puke/core.rb b/lib/puke/core.rb index c37b17a..77652aa 100644 --- a/lib/puke/core.rb +++ b/lib/puke/core.rb @@ -52,8 +52,8 @@ module Puke { mid: mid, tid: tid.to_i, subject: subject, date: Time.rfc2822(date) } end - def read(mid) - File.read(File.join(datadir, mangle_mid(mid) + ".mbox")) + def open(mid, &blk) + File.open(File.join(datadir, mangle_mid(mid) + ".mbox"), &blk) end CreateMutex = Thread::Mutex.new diff --git a/lib/puke/server.rb b/lib/puke/server.rb index 29d4479..432d84a 100644 --- a/lib/puke/server.rb +++ b/lib/puke/server.rb @@ -1,4 +1,5 @@ require_relative "core" +require_relative "concatenated_io" require "webrick" require "erb" @@ -52,14 +53,17 @@ EOF def mid(req, res) /\A\/(?<mid>.+@.+?)(?:\/(?<unya>\w+)\.mbox)?\z/ =~ req.path_info and tid = Puke.mid?(mid) or (res.status = 404 and return) + # NB: Puke::ConcatenatedIO is not really an IO object and + # 'res.body.is_a?(IO)' returns false, leading the non-existent 'bytesize' + # method to be called by WEBrick, unless chunked response is enabled. + res.chunked = true + case unya when "thread" - # TODO: Concatenated-IO object?(?) - res.body = Puke.thread(tid).inject("") do |s, imid| - s << Puke.read(imid) - end + files = Puke.thread(tid).map { |mid| Puke.open(mid) } + res.body = Puke::ConcatenatedIO.new(files) when nil, "raw" - res.body = Puke.read(mid) + res.body = Puke.open(mid) else res.status = 400 return |