Virtual hosting with WEBrick?

Hi,

I’m checking out WEBrick and it looks great.

I wonder if there is some easy way to do “virtual
hosting” so that I can host several domains on the
same machine.

So what I want is to have separate MountTables
for different domain names.

My idea is to simply write a subclass of HTTPServer
that adds a virtual_mount method that mounts servlets
on given paths and UriRegexp’s. For the servlet to be invoked the uri must
match the UriRegexp and then
search_servlet is used like now.

Does this sound like a good idea or is there a better/simpler/existing
solution?

Regards,

···


Robert Feldt

I think you could run them on different ports then use apache for the
virtual domains and mod_proxy to get each virtual domain to the correct
port. Just guessing but I think this should work…

culley

Robert Feldt wrote:

···

Hi,

I’m checking out WEBrick and it looks great.

I wonder if there is some easy way to do “virtual
hosting” so that I can host several domains on the
same machine.

So what I want is to have separate MountTables
for different domain names.

My idea is to simply write a subclass of HTTPServer
that adds a virtual_mount method that mounts servlets
on given paths and UriRegexp’s. For the servlet to be invoked the uri
must match the UriRegexp and then
search_servlet is used like now.

Does this sound like a good idea or is there a better/simpler/existing
solution?

Regards,

Hi,

In message opru5op2dyoglyup@mail1.telia.com,

My idea is to simply write a subclass of HTTPServer
that adds a virtual_mount method that mounts servlets
on given paths and UriRegexp’s. For the servlet to be invoked the uri must
match the UriRegexp and then
search_servlet is used like now.

I found that HTTPServer is a container of enough parameters
for virtual hosting. (ie. port, bind address, hostname and
url namespace)
In my approach, the entire server is added to another server.

In current WEBrick, access logging couldn’t be separated for
each server. Please let me think more.

require ‘webrick’

module WEBrick
class VirtualHostServer < HTTPServer
def initialize(config)
super(config)
@virtual_hosts =
end

def virtual_host(server)
  @virtual_hosts << server
end

def service(req, res)
  if server = lookup_server(req)
    server.service(req, res)
  else
    super
  end
end

def lookup_server(req) 
  # this condition is a quick hacked.
  # it may be a security issue. should considered more...
  @virtual_hosts.find{|server|
    (server[:Port].nil?        || req.port == server[:Port])           &&
    (server[:BindAddress].nil? || req.addr[3] == server[:BindAddress]) &&
    (server[:ServerName].nil?  || req.host == server[:ServerName])
  }
end

end
end

build default server

svr = WEBrick::VirtualHostServer.new(
:Port => 10080,
:BindAddress => “0.0.0.0”
)
svr.mount_proc(“/”){|req, res|
res.body = “This is default server!”
res[‘content-type’] = “text/plain”
}

build a virtual server for “localhost”

localhost = WEBrick::HTTPServer.new(
:DoNotListen => true, # don’t forget!
:Port => nil,
:BindAddress => nil,
:ServerName => “localhost”
)
localhost.mount_proc(“/”){|req, res|
res.body = “This is localhost!”
res[‘content-type’] = “text/plain”
}

svr.virtual_host(localhost)
svr.start

···

`Robert Feldt feldt@ce.chalmers.se’ wrote:


gotoyuzo

culley harrelson culley@fastmail.fm skrev den Mon, 8 Sep 2003 10:50:07 +0900:

I think you could run them on different ports then use apache for the virtual domains and mod_proxy to get each virtual domain to the correct port. Just guessing but I think this should work…

Yeah, but if you wanna use pure-Ruby/webrick you can use my approach:

require ‘webrick’
module WEBrick

A VHostServer is a HTTPServer that holds several mount tables.

Each mount table corresponds to a regexp. The mount table to use

for a request is the first one whose regexp matches the uri in the

request. This allows for easy “virtual hosting”.

class VHostServer < HTTPServer
def initialize(*args)
super
@mount_tables = # holds [UriRE, MountTable] pairs
@default_mount_table = @mount_tab
end

def vhost_mount(uriRegexp, dir, servlet, *options)
  @logger.debug(sprintf("%s is mounted on %s for %s.", servlet.inspect, dir, uriRegexp.inspect))
  re, mt = @mount_tables.detect {|re, mt| re == uriRegexp}
  unless mt
    mt = MountTable.new
    @mount_tables << [ uriRegexp, mt ]
  end
  mt[dir] = [ servlet, options ]
end

def service(req, res)
  if req.unparsed_uri == "*"
    if req.request_method == "OPTIONS"
      do_OPTIONS(req, res)
      raise HTTPStatus::OK
    end
    raise HTTPStatus::NotFound, "`#{req.unparsed_uri}' not found."
  end

  # Only change from HTTPServer#service:
  url = req.request_uri.to_s
  servlet, options, script_name, path_info = search_servlet(req.path, url)
  # ------------------------------------
  raise HTTPStatus::NotFound, "`#{req.path}' not found." unless servlet
  req.script_name = script_name
  req.path_info = path_info
  si = servlet.get_instance(self, *options)
  @logger.debug(format("%s is invoked.", si.class.name))
  si.service(req, res)
end

def search_servlet(path, url)
  mount_table = search_mount_table(url)
  script_name, path_info = mount_table.scan(path)
  servlet, options = mount_table[script_name]
  if servlet
    [ servlet, options, script_name, path_info ]
  end
end

def search_mount_table(url)
  re, mt = @mount_tables.detect {|re, mt| re.match(url) }
  mt || @default_mount_table
end

end
end

I just thought it was already in there…

Regards,

Robert