Stream-Server in drei Zeilen mit rack 0

Posted by fwoeck
on Friday, January 23

Einen minimalistischen Webservice mit rack zu bauen ist fast trivial. Man benötigt ein Objekt, dass auf die Methode call(env) mit einem Array aus drei Elementen reagiert, wie z.B. diesem hier:

[200, {"Content-Type"=>"text/plain"}, "Moooh!"]

Dieses in der richtigen Weise mit einem Webserver an rack gebunden, gibt einen funktionsfähigen Dienst:

1
2
3
4
5
6
7
8
require 'thin'
require 'rack'

class App
  def call(env)
    [200, { 'Content-Type' => 'text/plain' }, 'Moooh!']
  end
end
>> app = App.new
>> app.call(1)
=> [200, {"Content-Type"=>"text/plain"}, "Moooh!"]
>> Rack::Handler::Thin.run(app, :Port => 1234)
>> Thin web server (v1.0.0 codename That's What She Said)
>> Maximum connections set to 1024
>> Listening on 0.0.0.0:1234, CTRL+C to stop

Und voila: die Assetfunktion von mephisto funktioniert nach dem letzten Update nicht mehr, sonst gäb’s hier ein Bild. So muss man sich vorstellen, man gäbe im Firefox als URL

http://127.0.0.1:1234

ein und bekäme ein freundliches ‘Moooh!’

Da Proc-Schnispel ein eingebautes call haben ginge es auch so:

1
2
3
app = Proc.new do |env|
  [200, { 'Content-Type' => 'text/plain' }, 'Moooh!']
end

File-Streaming

Als letzter Parameter des Arrays (hier “Moooh!”) kann jedes Objekt dienen, dass each implementiert. Auf diesem Wege kann man recht einfach z.B. einen File-Streamer schreiben:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
require 'thin'
require 'rack'

class StreamingFile
  def initialize(file)
    @file = file
  end

  def length
    File.size(@file)
  end

  def last_modified
    File.mtime(@file).rfc822
  end

  def each
    File.open(@file, "r") do |file|
      while part = file.read(8192)
        yield part
      end
    end
  end
end

file = StreamingFile.new('/var/www/html/test.mp3')

app = Proc.new do |env|
  [200, { 'Content-Type' => 'audio/mp3', 'Content-Length' => file.length.to_s }, file]
end

Rack::Handler::Thin.run(app, :Port => 1234)

Env

Im env-Parameter stehen übrigens die Headerdaten des Clients (HTTP_USER_AGENT etc.) in einem Hash zusammengetragen und zur Auswertung bereit.

Unix Pipes

So etwas funktioniert auch mit Pipes. Via STDIN

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
require 'thin'
require 'rack'

class StreamingFile
  def initialize(file)
    @file = file
  end

  def each
    while part = @file.read(8192)
      yield part
    end
  end
end

file = StreamingFile.new(STDIN)

app = Proc.new do |env|
  [200, { 'Content-Type' => 'application/octet-stream' }, file]
end

Rack::Handler::Thin.run(app, :Port => 1234)

kann man z.B. ein lokales tar-Archiv-streamen:

tar cz /home/me | ruby stream.rb

Cool?

Cool! Mehr dazu hier: danwebb.net

Comments

Leave a response

Comment