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