Index: lib/mouseHole/proxyhandler.rb
===================================================================
--- lib/mouseHole/proxyhandler.rb	(revision 107)
+++ lib/mouseHole/proxyhandler.rb	(working copy)
@@ -5,6 +5,7 @@
   class MountHandler < Mongrel::HttpHandler
     include HandlerMixin
     include LoggerMixin
+    include Views
 
     def initialize(block)
       @block = block
@@ -30,52 +31,66 @@
     end
 
     def process(request, response)
-      start = Time.now
-      uri, reqh, env = page_prep(request)
-        
-      if uri.path =~ %r!/([\w\-]{32})/!
-        token, trail = $1, $'
-        app = @central.find_app :token => token
-        if app
-          hdlr = app.find_handler :is => :mount, :on => :all, :name => trail
-          return hdlr.process(request, response)
+      begin
+        start = Time.now
+        uri, reqh, env = page_prep(request)
+  
+        if uri.path =~ %r!/([\w\-]{32})/!
+          token, trail = $1, $'
+          app = @central.find_app :token => token
+          if app
+            hdlr = app.find_handler :is => :mount, :on => :all, :name => trail
+            return hdlr.process(request, response)
+          end
         end
-      end
 
-      header = []
-      choose_header(reqh, header)
-      set_via(header)
-
-      http = Net::HTTP.new(env['server-name'], env['server-port'], @central.proxy_host, @central.proxy_port)
-      http.open_timeout = 10
-      http.read_timeout = 20
-      reqm = Net::HTTP.const_get(env['request-method'].capitalize)
-      debug "-> connecting to #{uri}", :since => start
-      resin = http.request(reqm.new(uri.request_uri, header), reqm::REQUEST_HAS_BODY ? request.body : nil) do |resin|
         header = []
-        debug " > opened #{uri}", :since => start
-        choose_header(resin.to_hash, header)
+        choose_header(reqh, header)
         set_via(header)
 
-        page = Page.new(uri, resin.code, header)
-        if page.converter and !DOMAINS.include?(env['server-name']) and @central.rewrite(page, resin)
-          info "*> rewriting #{page.location}", :since => start
-          output(page, response)
-        else
-          debug " > streaming #{page.location}", :since => start
-          response.status = resin.code.to_i
-          header.each { |k, v| response.header[k] = v }
-          response.send_plain_status
-          response.send_header
-          resin.read_body do |chunk|
-            response.write(chunk)
+        http = Net::HTTP.new(env['server-name'], env['server-port'], @central.proxy_host, @central.proxy_port)
+        http.open_timeout = 10
+        http.read_timeout = 20
+        reqm = Net::HTTP.const_get(env['request-method'].capitalize)
+        debug "-> connecting to #{uri}", :since => start
+        resin = http.request(reqm.new(uri.request_uri, header), reqm::REQUEST_HAS_BODY ? request.body : nil) do |resin|
+          header = []
+          debug " > opened #{uri}", :since => start
+          choose_header(resin.to_hash, header)
+          set_via(header)
+
+          page = Page.new(uri, resin.code, header)
+          if page.converter and !DOMAINS.include?(env['server-name']) and @central.rewrite(page, resin)
+            info "*> rewriting #{page.location}", :since => start
+            output(page, response)
+          else
+            debug " > streaming #{page.location}", :since => start
+            response.status = resin.code.to_i
+            header.each { |k, v| response.header[k] = v }
+            response.send_plain_status
+            response.send_header
+            resin.read_body do |chunk|
+              response.write(chunk)
+            end
           end
         end
+        debug "-> finished #{uri}", :since => start
+      rescue
+        handle_error($!, response)
       end
-      debug "-> finished #{uri}", :since => start
       resin
     end
 
-  end
+    def handle_error(error, response)
+      response.status = 500
+      response.send_plain_status
+      response.header['Content-Type'] = 'text/html; charset=utf-8'
+      response.send_header
+      error 'ERROR: ' + $!.class.name + ' ' + $!.message
+      @error = error
+      render response, :error
+    end
 
+	end
+
 end
Index: lib/mouseHole/views.rb
===================================================================
--- lib/mouseHole/views.rb	(revision 107)
+++ lib/mouseHole/views.rb	(working copy)
@@ -13,7 +13,7 @@
         script :type => "text/javascript", :src => R(Static, 'js', 'jquery.js')
         script :type => "text/javascript", :src => R(Static, 'js', 'interface.js')
         script :type => "text/javascript", :src => R(Static, 'js', 'mouseHole.js')
-        style "@import '#{R(Static, 'css', 'doorway.css')}';", :type => 'text/css'
+        style "@import '#{self/R(Static, 'css', 'doorway.css')}';", :type => 'text/css'
       end
       body do
         div.mousehole! do
@@ -220,11 +220,47 @@
 
   def data
     div.main do
-      h1 { 'Data ' + span('Viewer') } 
+      h1 { text('Data '); span('Viewer') } 
       p %{Welcome to MouseHole.}
     end
   end
 
+  def error
+    div.main do
+      div.exception do
+        case @error
+        when Errno::ECONNREFUSED
+          h2 "Connection Refused"
+          p "The remote server refused the connection. Check that you got the
+  URL right, and make sure the remote service is running."
+        when Errno::EINVAL
+          h2 "Invalid"
+          p "Connecting to the remote host failed, probably due to an
+invalid IP address"
+        when SocketError
+          h2 "#{@error.message.gsub(/\n/, '<br />')}"
+          if @error.message =~ /Hostname not known/
+            p "The remote host could not be found. You might want to try
+with or without a 'www.', check its spelling, or recite the incantation
+backward." 
+          end
+        else
+          h2 "#{@error.class}"
+          h3(@error.message.gsub(/\n/, '<br />'))
+          p "The exception was:"
+          ul.backtrace do
+            @error.backtrace.each do |bt|
+              li "from #{bt}"
+            end
+          end
+        end
+        p.actionbuttons do 
+          button(:class => 'action', :onclick => "window.location.reload()") { "Try again" }
+        end
+      end
+    end
+  end
+
   def red str
     RedCloth.new(str.gsub(/^ +/, '')).to_html
   end
Index: lib/mouseHole/mixins/handler.rb
===================================================================
--- lib/mouseHole/mixins/handler.rb	(revision 107)
+++ lib/mouseHole/mixins/handler.rb	(working copy)
@@ -77,6 +77,20 @@
       return URI(uri), reqh, env
     end
 
+    def render(response, *args, &block)
+      begin
+        debug '-> render'
+        @root = 'http://mh'
+        m = Mab.new({}, self)
+        s=m.capture{send(:doorway,*args,&block)}
+        s=m.capture{send(:layout){s}} if /^_/!~args[0].to_s and m.respond_to?:layout
+        response.write(s)
+      rescue Exception
+        error $!
+        error $!.backtrace.join("\n")
+      end
+    end
+
   end
 
 end
Index: static/css/doorway.css
===================================================================
--- static/css/doorway.css	(revision 107)
+++ static/css/doorway.css	(working copy)
@@ -367,3 +367,7 @@
   list-style: none;
   margin: 0;
 }
+
+#mousehole p.actionbuttons { 
+  text-align: center;
+}
