Skip to content

@igrigorik /webapp.rb
Created

Embed URL

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Inspired by @JEG2's talk at Rubyconf... Any ruby object, as a webapp! 'Cause we can. :-)
require 'rubygems'
require 'rack'
class Object
def webapp
class << self
define_method :call do |env|
func, *attrs = env['PATH_INFO'].split('/').reject(&:empty?)
[200, {}, send(func, *attrs)]
end
end
self
end
end
Rack::Handler::Mongrel.run [].webapp, :Port => 9292
# ^^^^^^^^^^^
# | (x)
# ROFLSCALE DB ---/
#
# http://localhost:9292/push/1 -> 1
# http://localhost:9292/push/2 -> 12
# http://localhost:9292/push/3 -> 123
# http://localhost:9292/to_a -> 123
# http://localhost:9292/pop -> 3
# http://localhost:9292/shift -> 1
# Implementations in other languages (thanks guys!):
# Node.js: https://gist.github.com/700995
# Groovy: https://gist.github.com/702337
# Python: https://gist.github.com/702001
# Perl w/ plack: https://gist.github.com/703620
# Perl w/ continuity: https://gist.github.com/703651
# Io: https://gist.github.com/703431
# Great explanation of how this works in Ruby on Stackoverflow:
# http://stackoverflow.com/questions/4198883/exposing-any-ruby-object-over-the-web
@lukaszkorecki

Amazing.

@haihappen

Omg! Nice :)

@rkh

Should be [200, {}, [send(func, *attrs).to_s]].

@yonkeltron

I quite like this. However, you could do really horrible stuff with eval and friends.

@Oshuma

I changed REQUEST_PATH to PATH_INFO since REQUEST_PATH always returns "/" on Webrick. Also added the #to_s() call as suggested by @rkh.

Updated gist: https://gist.github.com/701304

@tobi

http://localhost:9292/instance_eval/exec("rm -rf /")

@technoweenie

The roflcopter in the comments is blowing my mind.

@igrigorik
Owner

@Oshuma: good stuff, will update the gist in a sec.

@tobi: with great power comes.. nah, actually, your example wouldn't work because the "/" in rm would get swallowed by split! :-)

@technoweenie: woot! that's exactly what I was going for.

@drnic

I love the roflcopter

@wilson

Running one at http://rofl.vcloudlabs.com/ have fun

@verborghs

@JamieFlournoy J2EE had these kind of security feature a long time ago if i remember correctly the spring bean introspector feature(http://www.springsource.com/security/cve-2010-1622) and it was more lines of code, so better

@tingletech

RickRoll spoiler: Don't click on the trollish "J2EE version" link from @JamieFlournoy

@jamesgolick

http://localhost/`/rm/-Rf/*

@WalterYu

Seriously, the roflcopter is amazing.

@ataylor284

Here's a groovy version: https://gist.github.com/702337

@rafmagana

what about this hehehe:

class Object

  DANGEROUS_METHODS = methods.grep(/eval|instance|module|method|send|taint|extend|include|freeze/)

  def webapp
    class << self
      define_method :call do |env|
        func, *attrs = env['PATH_INFO'].split('/').reject(&:empty?)
        result = DANGEROUS_METHODS.include?(func) ? 'METHOD NOT ALLOWED' : send(func, *attrs).to_s
        [200, {}, [result]]
      end
    end
    self
  end
end
@markiz

@rafmagana

`
alias (unsure)

It should be a whitelist instead of black list, I believe.

@rafmagana

mmm, well, yes, it might be, the only thing is that we'd need an ALLOWED_METHODS per class or something like that, I mean, I wouldn't do the following in the Object class:

ALLOWED_METHODS = w%[a lot of methods of different classes]

well, I don't know, maybe, hehe

@mbleigh

Might take some inspiration from this to make it actually possible to expose objects as Rack endpoints in Grape...still thinking of how to make it work exactly.

@jaymcgavren

$SAFE = 1 would disable a lot of the nastier methods. You'd still have to undef them in JRuby, though.

@rafmagana

woow ROaaS = Ruby Objects as a Service hehehe

@mtodd

ROFLSCALE! \m/

@igrigorik
Owner

@Oshuma: Nice! :-)

@draegtun

Very nice. Here are some implementations I did in Perl & Io:

https://gist.github.com/703620 - Perl using plack
https://gist.github.com/703651 - Perl using Continuity
https://gist.github.com/703431 - Io

@igrigorik
Owner

@draegtun: awesome, thanks! Added your gist links to the one above.

@lucj

Hello, this is really great but there are some things I am not really sure. For instance, I cannot get the object class with http://localhost:9292/class.

After:
http://localhost:9292/push/1 -> 1
http://localhost:9292/push/2 -> 12
http://localhost:9292/push/3 -> 123
http://localhost:9292/to_a -> 123

I would expect
http://localhost:9292/class -> Array

Sorry if I'm wrong, just trying to understand :)

Regards,
Luc

@igrigorik
Owner

Luc, you have to be careful with your version of Ruby. Rack expects an object on which you can call "each". Under Ruby 1.9, String does not have an .each method. Chances are, Rack is erroring out because of that. A simple workaround would be wrap every returned object into a "StringIO.new(send(...).to_s))".

@lucj

Hello,
Hmm, sounds strange. In fact I'm using Ruby 1.8.7

luc@venus:~/Projects/rubyobject ruby --version
ruby 1.8.7 (2009-06-12 patchlevel 174) [universal-darwin10.0]

Should it work with this version ?
Thanks a lot for your help.
Regards,
Luc

@igrigorik
Owner

Yep, 1.8.7 should work. What does your console output when you run the server and make that request?

@lucj

hmmm, you'r right, it's talking about the each method... but I'm running ruby 1.8.7

config.ru:1:in new'
config.ru:1
Wed Nov 24 16:03:46 +0100 2010: Read error: #<NoMethodError: undefined method
each' for Array:Class>
/Library/Ruby/Gems/1.8/gems/rack-1.2.1/lib/rack/chunked.rb:37:in each'
/Library/Ruby/Gems/1.8/gems/rack-1.2.1/lib/rack/handler/mongrel.rb:80:in
process'
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/gems/1.8/gems/mongrel-1.1.5/lib/mongrel.rb:159:in process_client'
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/gems/1.8/gems/mongrel-1.1.5/lib/mongrel.rb:158:in
each'
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/gems/1.8/gems/mongrel-1.1.5/lib/mongrel.rb:158:in `process_cli

@igrigorik
Owner

Right, you're seeing the problem I described below. You need to convert everything to a StringIO and then you're good to go.

@danny

So I updated it a bit in my fork; it removes url encoding and returns json serialization of results

https://gist.github.com/737959

@igrigorik
Owner

@danny: nice!

@Burgestrand

How come you didn’t settle with def self.call(env) instead?

require 'rack'

class Object
  def to_webapp
    def self.call(env)
      func, *attrs = env['PATH_INFO'].split('/').reject(&:empty?)
      [200, {}, send(func || :inspect, *attrs)]
    end
    self
  end
end

Rack::Handler::WEBrick.run [].to_webapp, :Port => 9292
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.