Index: /Dev/trunk/Gruntfile.js
===================================================================
--- /Dev/trunk/Gruntfile.js	(revision 465)
+++ /Dev/trunk/Gruntfile.js	(revision 466)
@@ -29,4 +29,14 @@
                     dest: developmentDir,
                     ext: '.js'
+                }]
+            }
+        },
+        coffeelint: {
+            compile: {
+                options: require('./'+srcDir+'.coffeelint.json'),
+                files: [{
+                    expand: true,
+                    cwd: srcDir,
+                    src: ['client/qed-client/**/*.coffee', 'server/**.coffee']
                 }]
             }
@@ -116,4 +126,5 @@
     });
 
+    grunt.loadNpmTasks('grunt-coffeelint');
     grunt.loadNpmTasks('grunt-contrib-clean');
     grunt.loadNpmTasks('grunt-contrib-coffee');
@@ -126,5 +137,6 @@
     // development tasks
     grunt.registerTask('install-deps', ['copy:server-deps', 'copy:client-deps']);
-    grunt.registerTask('compile', ['less:compile', 'jshint:compile', 'htmlhint:compile', 'coffee:compile', 'copy:compile']);
+    grunt.registerTask('lint', ['jshint:compile', 'htmlhint:compile', 'coffeelint:compile']);
+    grunt.registerTask('compile', ['lint', 'less:compile', 'coffee:compile', 'copy:compile']);
     grunt.registerTask('build', ['clean:development', 'install-deps', 'compile']);
 
@@ -135,3 +147,5 @@
     grunt.registerTask('default', ['compile']);
 
+    // util functions
+
 };
Index: /Dev/trunk/package.json
===================================================================
--- /Dev/trunk/package.json	(revision 465)
+++ /Dev/trunk/package.json	(revision 466)
@@ -13,5 +13,6 @@
     "grunt-contrib-clean": "~0.4.1",
     "grunt-contrib-coffee": "~0.7.0",
-    "underscore": "~1.4.4"
+    "underscore": "~1.4.4",
+    "grunt-coffeelint": "0.0.6"
   }
 }
Index: /Dev/trunk/src/.coffeelint.json
===================================================================
--- /Dev/trunk/src/.coffeelint.json	(revision 466)
+++ /Dev/trunk/src/.coffeelint.json	(revision 466)
@@ -0,0 +1,74 @@
+{
+    "no_tabs": {
+        "level": "error"
+    },
+    "no_trailing_whitespace": {
+        "level": "error",
+        "allowed_in_comments": false
+    },
+    "max_line_length": {
+        "value": 80,
+        "level": "error"
+    },
+    "camel_case_classes": {
+        "level": "error"
+    },
+    "indentation": {
+        "value": 4,
+        "level": "error"
+    },
+    "no_implicit_braces": {
+        "level": "ignore"
+    },
+    "no_trailing_semicolons": {
+        "level": "error"
+    },
+    "no_plusplus": {
+        "level": "ignore"
+    },
+    "no_throwing_strings": {
+        "level": "error"
+    },
+    "cyclomatic_complexity": {
+        "value": 10,
+        "level": "ignore"
+    },
+    "no_backticks": {
+        "level": "error"
+    },
+    "line_endings": {
+        "level": "ignore",
+        "value": "unix"
+    },
+    "no_implicit_parens": {
+        "level": "ignore"
+    },
+    "empty_constructor_needs_parens": {
+        "level": "ignore"
+    },
+    "non_empty_constructor_needs_parens": {
+        "level": "ignore"
+    },
+    "no_empty_param_list": {
+        "level": "ignore"
+    },
+    "space_operators": {
+        "level": "ignore"
+    },
+    "duplicate_key": {
+        "level": "error"
+    },
+    "newlines_after_classes": {
+        "value": 3,
+        "level": "ignore"
+    },
+    "no_stand_alone_at": {
+        "level": "ignore"
+    },
+    "arrow_spacing": {
+        "level": "ignore"
+    },
+    "coffeescript_error": {
+        "level": "error"
+    }
+}
Index: /Dev/trunk/src/client/index.html
===================================================================
--- /Dev/trunk/src/client/index.html	(revision 465)
+++ /Dev/trunk/src/client/index.html	(revision 466)
@@ -30,4 +30,5 @@
         </div>
         <div id="toaster" data-dojo-type="qed-client/app/Notifications"></div>
+        <div id="loginDialog" data-dojo-type="qed-client/ui/LoginDialog"></div>
     </body>
 </html>
Index: /Dev/trunk/src/client/qed-client/index.js
===================================================================
--- /Dev/trunk/src/client/qed-client/index.js	(revision 465)
+++ /Dev/trunk/src/client/qed-client/index.js	(revision 466)
@@ -4,4 +4,5 @@
     "./app/Router",
     "./routes",
+    "./session",
     "./store",
     "./store/request",
@@ -9,24 +10,18 @@
     "dojo/_base/array",
     "dojo/parser",
-    "dojo/request/registry",
     "./stddeps",
     "dojo/domReady!"
-], function(Content, Page, Router, routes, store, request, MainMenu, array, parser, registry) {
+], function(Content, Page, Router, routes, session, store, request, MainMenu, array, parser) {
 
-    registry.register(/.*/,request);
+    parser.parse();
+
+    new MainMenu({},"menu").startup();
+
+    array.forEach(routes,function(route){
+        Router.register(route);
+    });
+    Router.startup();
+
+    session.restore();
     
-    parser.parse();
-    store.info()
-    .then(function(){
-        new MainMenu({},"menu").startup();
-        array.forEach(routes,function(route){
-            Router.register(route);
-        });
-        Router.startup();
-    },function(){
-        Content.startup();
-        Content.set(new Page({
-            templateString: "<div>Application database is not properly configured. Contact your system administrator.</div>"
-        }));
-    });
 });
Index: /Dev/trunk/src/client/qed-client/pages/surveyRun.js
===================================================================
--- /Dev/trunk/src/client/qed-client/pages/surveyRun.js	(revision 465)
+++ /Dev/trunk/src/client/qed-client/pages/surveyRun.js	(revision 466)
@@ -86,5 +86,5 @@
         },
         _link: function(url,label) {
-            return '<a target="_black" href="'+url+'">'+(label || url)+'</a>';
+            return '<a target="_blank" href="'+url+'">'+(label || url)+'</a>';
         },
         _onSave: function(evt) {
@@ -111,8 +111,4 @@
             if ( evt ) { event.stop(evt); }
             return false;
-        },
-        _onExport: function(evt) {
-            if ( evt ) { event.stop(evt); }
-            return false;
         }
     });
Index: /Dev/trunk/src/client/qed-client/pages/templates/question.html
===================================================================
--- /Dev/trunk/src/client/qed-client/pages/templates/question.html	(revision 465)
+++ /Dev/trunk/src/client/qed-client/pages/templates/question.html	(revision 466)
@@ -1,3 +1,3 @@
-<div class="orange">
+<form class="orange">
     <div data-dojo-type="dijit/layout/ContentPane" data-dojo-props="region:'top'">
         <h2>
@@ -15,3 +15,3 @@
         </div>
     </div>
-</div>
+</form>
Index: /Dev/trunk/src/client/qed-client/pages/templates/surveyRun.html
===================================================================
--- /Dev/trunk/src/client/qed-client/pages/templates/surveyRun.html	(revision 465)
+++ /Dev/trunk/src/client/qed-client/pages/templates/surveyRun.html	(revision 466)
@@ -45,8 +45,5 @@
             <div>
                 <div class="qedLabel">Export results</div>
-                <button data-dojo-type="dijit/form/Button"
-                        class="qedField blue"
-                        data-dojo-props="baseClass: 'rftBlockButton', iconClass: 'rftIcon rftIconSave'"
-                        data-dojo-attach-event="onClick:_onExport">To CSV</button>
+                <a target="_blank" href="/surveyRun/${surveyRunId}/csv" class="qedField">To CSV</a>
             </div>
         </fieldset>
Index: /Dev/trunk/src/client/qed-client/session.coffee
===================================================================
--- /Dev/trunk/src/client/qed-client/session.coffee	(revision 466)
+++ /Dev/trunk/src/client/qed-client/session.coffee	(revision 466)
@@ -0,0 +1,44 @@
+define [
+    "dojo/_base/declare",
+    "dojo/_base/json",
+    "dojo/Evented",
+    "dojo/request"
+], (declare, json, Evented, request) ->
+    Session = declare [Evented],
+        info: null
+        get: () ->
+            @info
+        restore: () ->
+            request '/api/login',
+                method: "GET"
+                handleAs: "json"
+            .then (res) =>
+                @_set res
+            , () =>
+                @_set null
+        login: (username, password) ->
+            request '/api/login',
+                method: "POST"
+                handleAs: "json"
+                data:
+                    username: username
+                    password: password
+            .then (res) =>
+                @_set res
+            , () =>
+                @_set null
+        logout: () ->
+            request '/api/logout',
+                method: "POST"
+                handleAs: "json"
+            .then ((res) =>
+                console.log "Logged out"
+                @_set null), () =>
+                console.log "Error logging out"
+        
+        _set: (newInfo) ->
+            @info = newInfo
+            @emit 'change', @info
+            @info
+
+    new Session()
Index: /Dev/trunk/src/client/qed-client/stddeps.js
===================================================================
--- /Dev/trunk/src/client/qed-client/stddeps.js	(revision 465)
+++ /Dev/trunk/src/client/qed-client/stddeps.js	(revision 466)
@@ -33,7 +33,9 @@
     './model/widgets/SurveyWidget',
 
+    './ui/LoginDialog',
     './ui/MainMenu',
     './ui/MenuBarLink',
     './ui/MenuLink',
+    './ui/SessionMenu',
 
     './widgets/LineWithActionsWidget',
Index: /Dev/trunk/src/client/qed-client/store.js
===================================================================
--- /Dev/trunk/src/client/qed-client/store.js	(revision 465)
+++ /Dev/trunk/src/client/qed-client/store.js	(revision 466)
@@ -9,5 +9,5 @@
     
     var couchStore = new CouchStore({
-        target: 'data/couch/' /*,
+        target: 'api/data/' /*,
         validate: function(object) {
             var result = jsonSchema.validate(object,schema);
Index: /Dev/trunk/src/client/qed-client/store/CouchStore.js
===================================================================
--- /Dev/trunk/src/client/qed-client/store/CouchStore.js	(revision 465)
+++ /Dev/trunk/src/client/qed-client/store/CouchStore.js	(revision 466)
@@ -5,5 +5,5 @@
     "dojo/_base/json",
     "dojo/_base/lang",
-    "dojo/request/registry",
+    "dojo/request",
     "dojo/store/util/QueryResults"
 ], function(Deferred, array, declare, json, lang, request, QueryResults) {
Index: /Dev/trunk/src/client/qed-client/ui/LoginDialog.coffee
===================================================================
--- /Dev/trunk/src/client/qed-client/ui/LoginDialog.coffee	(revision 466)
+++ /Dev/trunk/src/client/qed-client/ui/LoginDialog.coffee	(revision 466)
@@ -0,0 +1,32 @@
+define [
+    "../session",
+    "dijit/_WidgetsInTemplateMixin",
+    "dijit/Dialog",
+    "dijit/form/ValidationTextBox",
+    "dojo/_base/declare",
+    "dojo/_base/event",
+    "dojo/dom-construct",
+    "dojo/text!./templates/LoginDialog.html"
+], (session, _WidgetsInTemplateMixin, Dialog,
+    ValidationTextBox, declare, event,
+    domConstruct, template) ->
+    declare [Dialog,_WidgetsInTemplateMixin],
+        templateString: template
+        title: "Login to QED"
+        show: () ->
+            @loginForm.reset()
+            @inherited arguments
+        onLogin: (evt) ->
+            if @loginForm.validate
+                value = @loginForm.get 'value'
+                session.login value.username,
+                              value.password
+                .then (() => @hide()),
+                      () => alert "Login failed!"
+            @hide()
+            event.stop evt if evt
+            false
+        onCancel: (evt) ->
+            @hide()
+            event.stop evt if evt
+            false
Index: /Dev/trunk/src/client/qed-client/ui/MenuLink.js
===================================================================
--- /Dev/trunk/src/client/qed-client/ui/MenuLink.js	(revision 465)
+++ /Dev/trunk/src/client/qed-client/ui/MenuLink.js	(revision 466)
@@ -1,10 +1,16 @@
-define(['dojo/_base/declare','dijit/MenuItem','../app/Router'
-], function(declare,MenuItem,Router) {
+define([
+    "../app/Router",
+    "dijit/MenuItem",
+    "dojo/_base/declare",
+    "dojo/_base/event"
+], function(Router, MenuItem, declare, event) {
     return declare([MenuItem],{
         options:{
             path: null
         },
-        onClick: function() {
+        onClick: function(evt) {
             if ( this.path ) { Router.go(this.path); }
+            if ( evt ) { event.stop(evt); }
+            return false;
         }
     });
Index: /Dev/trunk/src/client/qed-client/ui/SessionMenu.coffee
===================================================================
--- /Dev/trunk/src/client/qed-client/ui/SessionMenu.coffee	(revision 466)
+++ /Dev/trunk/src/client/qed-client/ui/SessionMenu.coffee	(revision 466)
@@ -0,0 +1,33 @@
+define [
+    "../session",
+    "dijit/DropDownMenu",
+    "dijit/registry",
+    "dijit/MenuBarItem",
+    "dojo/_base/declare",
+    "dojo/_base/event",
+    "dojo/on"
+], (session, DropDownMenu, registry, MenuBarItem, declare, event, _on) ->
+    declare [MenuBarItem],
+        info: null
+        postCreate: () ->
+            @loginDialog = registry.byId 'loginDialog'
+            console.error "Cannot find loginDialog" if not @loginDialog
+            @set 'label', "Login"
+        startup: () ->
+            if @_started then return
+            @inherited arguments
+            @_update session.get()
+            _on session, 'change', (info) => @_update info
+        _update: (info) ->
+            @info = info
+            if info
+                @set 'label', "Logout, "+info.username
+            else
+                @set 'label', "Login"
+        onClick: (evt) ->
+            if @info
+                session.logout()
+            else
+                @loginDialog.show()
+            event.stop evt if evt
+            false
Index: /Dev/trunk/src/client/qed-client/ui/templates/LoginDialog.html
===================================================================
--- /Dev/trunk/src/client/qed-client/ui/templates/LoginDialog.html	(revision 466)
+++ /Dev/trunk/src/client/qed-client/ui/templates/LoginDialog.html	(revision 466)
@@ -0,0 +1,51 @@
+<div class="dijitDialog"
+     role="dialog"
+     aria-labelledby="${id}_title">
+    <div data-dojo-attach-point="titleBar"
+         class="dijitDialogTitleBar">
+        <span data-dojo-attach-point="titleNode"
+              class="dijitDialogTitle"
+              id="${id}_title" role="heading" level="1"></span>
+        <span data-dojo-attach-point="closeButtonNode"
+              class="dijitDialogCloseIcon"
+              data-dojo-attach-event="ondijitclick: onCancel"
+              title="${buttonCancel}" role="button" tabindex="0">
+            <span data-dojo-attach-point="closeText"
+                  class="closeText" title="${buttonCancel}">x</span>
+        </span>
+    </div>
+    <div class="dijitDialogPaneContent">
+        <form data-dojo-type="dijit/form/Form"
+              data-dojo-attach-point="loginForm"
+              data-dojo-attach-event="onSubmit:onLogin">
+            <div>
+              <label for="username"
+                     class="qedLabel">Username</label>
+              <div name="username"
+                   class="qedField"
+                   data-dojo-type="dijit/form/ValidationTextBox"
+                   data-dojo-props="required: true"></div>
+            </div>
+            <div>
+              <label for="password" class="qedLabel">Password</label>
+              <div name="password"
+                   class="qedField"
+                   data-dojo-type="dijit/form/ValidationTextBox"
+                   data-dojo-props="required: true"></div>
+            </div>
+            <div data-dojo-type="dijit/form/Button"
+                 type="submit"
+                 data-dojo-attach-event="onLogin">Login</div>
+            <div data-dojo-type="dijit/form/Button"
+                 data-dojo-attach-event="onCancel">Cancel</div>
+        </form>
+        <!-- Container node is required, otherwise
+             root is taken and emptied
+         -->
+        <div data-dojo-attach-point="containerNode"></div>
+    </div>
+</div>
+
+
+
+
Index: /Dev/trunk/src/client/qed-client/ui/templates/MainMenu.html
===================================================================
--- /Dev/trunk/src/client/qed-client/ui/templates/MainMenu.html	(revision 465)
+++ /Dev/trunk/src/client/qed-client/ui/templates/MainMenu.html	(revision 466)
@@ -12,5 +12,5 @@
         </div>
         <div class="rftMainMenuButton" data-dojo-type="./MenuBarLink" data-dojo-props="path:'/results'">Results</div>
-        <div class="rftMainMenuButton" data-dojo-type="./MenuBarLink">Logout</div>
+        <div class="rftMainMenuButton" data-dojo-type="./SessionMenu"></div>
     </div>
 </div>
Index: v/trunk/src/client/qed-client/ui/templates/SessionBar.html
===================================================================
--- /Dev/trunk/src/client/qed-client/ui/templates/SessionBar.html	(revision 465)
+++ 	(revision )
@@ -1,5 +1,0 @@
-<div class="${baseClass}">
-    <span class="rftIcon rftIconSession" data-dojo-attach-point="iconNode"></span>
-    <div data-dojo-type="../widgets/LineWithActionsWidget" data-dojo-attach-point="upperNode" data-dojo-props="actions: {'inspect':{callback: inspect, properties: {blockButton: true}}}"></div>
-    <div data-dojo-type="../widgets/LineWithActionsWidget" data-dojo-attach-point="lowerNode"></div>
-</div>
Index: /Dev/trunk/src/client/qed-client/ui/templates/SessionMenu.html
===================================================================
--- /Dev/trunk/src/client/qed-client/ui/templates/SessionMenu.html	(revision 466)
+++ /Dev/trunk/src/client/qed-client/ui/templates/SessionMenu.html	(revision 466)
@@ -0,0 +1,6 @@
+<div>
+  <div class="rftMainMenuButton" data-dojo-type="dijit/MenuBarItem" data-dojo-props="label: 'Login'"></div>
+  <div class="rftMainMenuButton" data-dojo-type="dijit/PopupMenuBarItem" data-dojo-props="label: 'Logged in as user!'">
+    <div data-dojo-type="dijit/DropDownMenu"></div>
+  </div>
+</div>
Index: /Dev/trunk/src/client/qed-client/widgets/_ComplexValueMixin.coffee
===================================================================
--- /Dev/trunk/src/client/qed-client/widgets/_ComplexValueMixin.coffee	(revision 465)
+++ /Dev/trunk/src/client/qed-client/widgets/_ComplexValueMixin.coffee	(revision 466)
@@ -15,9 +15,13 @@
             @inherited arguments
             if @domNode.tagName.toLowerCase() isnt "form"
-                console.warn "Not scoping a _ComplexValueMixin in a form element can cause name clashes. E.g. radio buttons might stop working correctly. It is recommended to use <form> as the root element in your template for", @declaredClass
+                console.warn "Not scoping a _ComplexValueMixin in a
+                        form element can cause name clashes. E.g.
+                        radio buttons might stop working correctly.
+                        It is recommended to use <form> as the root
+                        element in your template for", @declaredClass
 
         _setDisabledAttr: (value) ->
             @_set "disabled", value
-            array.forEach @_getDescendantFormWidgets(), (child) => 
+            array.forEach @_getDescendantFormWidgets(), (child) =>
                 child.set "disabled", value
 
Index: /Dev/trunk/src/server/app.js
===================================================================
--- /Dev/trunk/src/server/app.js	(revision 465)
+++ /Dev/trunk/src/server/app.js	(revision 466)
@@ -4,5 +4,5 @@
 var fs = require("fs");
 var path = require("path");
-var proxy = require("./simple-http-proxy");
+var proxy = require("./util/simple-http-proxy");
 var _ = require("underscore");
 
@@ -31,4 +31,10 @@
         }
     }));
+    passport.serializeUser(function(user, done) {
+        done(null, user.username);
+    });
+    passport.deserializeUser(function(id, done) {
+        done(null, {username: id});
+    });
 
     var app = express();
@@ -56,10 +62,48 @@
     });
 
+    
     // url to login (might work on others as well?)
     // you should then have a session to work with
-    app.post('/api/login');
+    // should return a user info object
+    function returnUser(req,res) {
+        res.send(200, req.user);
+    }
+    app.post(
+        '/api/login',
+        passport.authenticate('local'),
+        returnUser);
 
+    // ensure we're authenticated on API calls
+    app.use('/api', function(req,res,next){
+        if (!req.user) {
+            return res.send(401,{error:"Login before accessing API."});
+        } else {
+            return next();
+        }
+    });
+    
+    app.get(
+        '/api/login',
+        returnUser);
+
+    app.post(
+        '/api/logout', function(req,res){
+            req.logout();
+            res.send(200,{});
+        });
+
+    app.get('/api/surveyRun/:id/response/csv',
+            function(req, res) {
+                var id = req.params.id;
+                // query CouchDB and build the CSV file
+                res.set({
+                    'Content-Type': 'text/csv',
+                    'Content-Disposition': 'attachment; filename=responses-'+id+'.csv'
+                });
+                res.send(200, "Response for surveyRun "+id);
+            });
+    
     // forward to couch
-    app.use('/data/couch', proxy(settings.couchDbURL));
+    app.use('/api/data', proxy(settings.couchDbURL));
 
     return app;
Index: /Dev/trunk/src/server/config/config-couchdb.js
===================================================================
--- /Dev/trunk/src/server/config/config-couchdb.js	(revision 465)
+++ /Dev/trunk/src/server/config/config-couchdb.js	(revision 466)
@@ -1,5 +1,4 @@
 var q = require('q');
-var HTTP = require('q-io/http');
-var URL = require('url');
+var request = require('../util/request');
 var _ = require('underscore');
 var util = require('util');
@@ -27,9 +26,7 @@
     }
 
-    function request(method,path,content) {
+    function dbRequest(method,path,content) {
         var url = couchDbURL+path;
-        var parsedURL = URL.parse(url);
         var options = {
-            url: url,
             method: method,
             headers: {
@@ -37,32 +34,12 @@
                 'accept': 'application/json'
             },
-            body: {
-                forEach: function(callback) {
-                    callback(JSON.stringify(content || {}));
-                }
-            }
+            body: content
         };
-        // because q-io doesn't support auth properly, we have to
-        // build the f*ing wheel again.
-        if ( parsedURL.auth ) {
-            var auth = new Buffer(parsedURL.auth).toString("base64");
-            options.headers.authorization = 'Basic '+auth;
-        }
-        return HTTP.request(options)
-        .then(function(res){
-            return res.body.read().then(function(content){
-                return JSON.parse(content.toString() || "{}");
-            });
-        },function(res){
-            return res.body.read().then(function(error){
-                console.warn(error); // q.all doesn't do errors, so let's show them here
-                return JSON.parse(error.toString() || "{}");
-            });
-        });
+        return request(url,options);
     }
 
     console.log("Configuring CouchDB for QED");
     console.log("Checking CouchDB version");
-    return request('GET','')
+    return dbRequest('GET','')
     .then(function(res){
         if (res.version !== "1.2.0" ) {
@@ -73,10 +50,10 @@
     }).then(function(res){
         console.log("Checking database 'qed'");
-        return request('GET','qed')
+        return dbRequest('GET','qed')
         .then(function(res){
             console.log("Database 'qed' found.");
         },function(err){
             console.log("Creating database 'qed'");
-            return request('PUT','qed');
+            return dbRequest('PUT','qed');
         });
     }).then(function(){
@@ -92,10 +69,10 @@
                 case "update":
                     console.log(docUrl+" updating.");
-                    return request('GET',docUrl)
+                    return dbRequest('GET',docUrl)
                     .then(function(oldDoc){
                         _.extend(oldDoc,doc);
-                        return request('PUT',docUrl,oldDoc);
+                        return dbRequest('PUT',docUrl,oldDoc);
                     },function(){
-                        return request('PUT',docUrl,doc);
+                        return dbRequest('PUT',docUrl,doc);
                     });
                 break;
@@ -103,10 +80,10 @@
                 default:
                     console.log(docUrl+" replacing.");
-                    return request('GET',docUrl)
+                    return dbRequest('GET',docUrl)
                     .then(function(oldDoc){
                         _.extend(doc,_.pick(oldDoc,'_id','_rev'));
-                        return request('PUT',docUrl,doc);
+                        return dbRequest('PUT',docUrl,doc);
                     },function(){
-                        return request('PUT',docUrl,doc);
+                        return dbRequest('PUT',docUrl,doc);
                     });
                 break;
Index: v/trunk/src/server/simple-http-proxy.js
===================================================================
--- /Dev/trunk/src/server/simple-http-proxy.js	(revision 465)
+++ 	(revision )
@@ -1,106 +1,0 @@
-
-/**
- * Module dependencies
- */
-var url = require('url')
-    , join = require('path').join
-    , protocols = {
-        http: require('http'),
-        https: require('https')
-    };
-
-module.exports = function(endpoint, opts) {
-    opts = opts || {};
-
-    var parsedUrl = url.parse(endpoint);
-
-    return function simpleHttpProxy(req, res, next) {
-        // Get our forwarding info
-        var hostInfo = req.headers.host.split(":");
-
-        // Remove the host header
-        delete req.headers.host;
-
-        // Optionally delete cookie
-        if (opts.cookies === false) {
-            delete req.headers.cookie;
-        }
-
-        // Should we keep the trailing slash?
-        var trailingSlash = req.originalUrl[req.originalUrl.length-1] === "/";
-
-        // Setup the options
-        var options = {
-            hostname: parsedUrl.hostname,
-            auth: parsedUrl.auth,
-            port: parsedUrl.port,
-            headers: req.headers,
-            path: join(parsedUrl.pathname, trailingSlash ? req.url : req.url.substring(1)),
-            method: req.method
-        };
-
-        // Enable forwarding headers
-        if(opts.xforward) {
-            // Get the path at which the middleware is mounted
-            var resPath = req.originalUrl.replace(req.url, "");
-
-            // We'll need to add a / if it's not on there
-            if (resPath.indexOf("/") !== 0) {
-                resPath = join("/", resPath);
-            }
-
-            // Pass along our headers
-            options.headers[opts.xforward.proto || "x-forwarded-proto"] = req.connection.encrypted ? "https" : "http";
-            options.headers[opts.xforward.host || "x-forwarded-host"] = hostInfo[0];
-            options.headers[opts.xforward.path || "x-forwarded-path"] = resPath;
-
-            if (hostInfo[1]) {
-                options.headers[opts.xforward.port || "x-forwarded-port"] = hostInfo[1];
-            }
-        }
-
-        /**
-         * Hack for nginx backends where node, by default, sends no 'content-length'
-         * header if no data is sent with the request and sets the 'transfer-encoding'
-         * to 'chunked'. Nginx will then send a 411 because the body contains a '0'
-         * which is the length of the chunk
-         *
-         *     GET /proxy
-         *     transfer-encoding: chunked
-         *     
-         *     0
-         *
-         */
-        if( ["POST", "DELETE"].indexOf(req.method) !== -1 && options.headers['transfer-encoding'] !== 'chunked') {
-            options.headers['content-length'] = options.headers['content-length'] || '0';
-        }
-
-        // Make the request with the correct protocol
-        var request = protocols[(parsedUrl.protocol || 'http').replace(":", "")].request(options, function(response) {
-
-            // Send down the statusCode and headers
-            res.writeHead(response.statusCode, response.headers);
-
-            // Pipe the response
-            response.pipe(res);
-        });
-
-        // Handle any timeouts that occur
-        request.setTimeout(opts.timeout || 10000, function() {
-            // Clean up the socket
-            // TODO is there a better way to do this? There's a 'socket hang up' error being emitted...
-            request.setSocketKeepAlive(false);
-            request.socket.destroy();
-
-            // Pass down the error
-            var err = new Error("Proxy to '"+endpoint+"' timed out");
-            request.emit("error", err);
-        });
-
-        // Pipe the client request upstream
-        req.pipe(request);
-
-        // Pass on our errors
-        request.on('error', next);
-    };
-};
Index: /Dev/trunk/src/server/util/request.js
===================================================================
--- /Dev/trunk/src/server/util/request.js	(revision 466)
+++ /Dev/trunk/src/server/util/request.js	(revision 466)
@@ -0,0 +1,39 @@
+var http = require('q-io/http'),
+    url = require('url'),
+    _ = require('underscore');
+
+module.exports = function(urlOrObject, options) {
+
+    options = options
+        ? _.clone(options)
+        : {};
+    options.url = _.isString(urlOrObject)
+        ? url.parse(urlOrObject)
+        : _.clone(urlOrObject);
+
+    // wrap content into q-io thingy
+    if ( options.body ) {
+        options.body = {
+            forEach: function(callback) {
+                callback(JSON.stringify(options.body));
+            }
+        };
+    };
+    
+    // add auth header since q-io doesn't support this
+    if ( options.url.auth ) {
+        options.headers.authorization = 'Basic '+(new Buffer(options.url.auth).toString("base64"));
+    }
+    
+    // make request and collect results
+    return http.request(options)
+    .then(function(res){
+        return res.body.read().then(function(content){
+            return content.length > 0 ? JSON.parse(content) : null;
+        });
+    },function(res){
+        return res.body.read().then(function(error){
+            return error.length > 0 ? JSON.parse(error) : null;
+        });
+    });
+};
Index: /Dev/trunk/src/server/util/simple-http-proxy.js
===================================================================
--- /Dev/trunk/src/server/util/simple-http-proxy.js	(revision 466)
+++ /Dev/trunk/src/server/util/simple-http-proxy.js	(revision 466)
@@ -0,0 +1,106 @@
+
+/**
+ * Module dependencies
+ */
+var url = require('url')
+    , join = require('path').join
+    , protocols = {
+        http: require('http'),
+        https: require('https')
+    };
+
+module.exports = function(endpoint, opts) {
+    opts = opts || {};
+
+    var parsedUrl = url.parse(endpoint);
+
+    return function simpleHttpProxy(req, res, next) {
+        // Get our forwarding info
+        var hostInfo = req.headers.host.split(":");
+
+        // Remove the host header
+        delete req.headers.host;
+
+        // Optionally delete cookie
+        if (opts.cookies === false) {
+            delete req.headers.cookie;
+        }
+
+        // Should we keep the trailing slash?
+        var trailingSlash = req.originalUrl[req.originalUrl.length-1] === "/";
+
+        // Setup the options
+        var options = {
+            hostname: parsedUrl.hostname,
+            auth: parsedUrl.auth,
+            port: parsedUrl.port,
+            headers: req.headers,
+            path: join(parsedUrl.pathname, trailingSlash ? req.url : req.url.substring(1)),
+            method: req.method
+        };
+
+        // Enable forwarding headers
+        if(opts.xforward) {
+            // Get the path at which the middleware is mounted
+            var resPath = req.originalUrl.replace(req.url, "");
+
+            // We'll need to add a / if it's not on there
+            if (resPath.indexOf("/") !== 0) {
+                resPath = join("/", resPath);
+            }
+
+            // Pass along our headers
+            options.headers[opts.xforward.proto || "x-forwarded-proto"] = req.connection.encrypted ? "https" : "http";
+            options.headers[opts.xforward.host || "x-forwarded-host"] = hostInfo[0];
+            options.headers[opts.xforward.path || "x-forwarded-path"] = resPath;
+
+            if (hostInfo[1]) {
+                options.headers[opts.xforward.port || "x-forwarded-port"] = hostInfo[1];
+            }
+        }
+
+        /**
+         * Hack for nginx backends where node, by default, sends no 'content-length'
+         * header if no data is sent with the request and sets the 'transfer-encoding'
+         * to 'chunked'. Nginx will then send a 411 because the body contains a '0'
+         * which is the length of the chunk
+         *
+         *     GET /proxy
+         *     transfer-encoding: chunked
+         *     
+         *     0
+         *
+         */
+        if( ["POST", "DELETE"].indexOf(req.method) !== -1 && options.headers['transfer-encoding'] !== 'chunked') {
+            options.headers['content-length'] = options.headers['content-length'] || '0';
+        }
+
+        // Make the request with the correct protocol
+        var request = protocols[(parsedUrl.protocol || 'http').replace(":", "")].request(options, function(response) {
+
+            // Send down the statusCode and headers
+            res.writeHead(response.statusCode, response.headers);
+
+            // Pipe the response
+            response.pipe(res);
+        });
+
+        // Handle any timeouts that occur
+        request.setTimeout(opts.timeout || 10000, function() {
+            // Clean up the socket
+            // TODO is there a better way to do this? There's a 'socket hang up' error being emitted...
+            request.setSocketKeepAlive(false);
+            request.socket.destroy();
+
+            // Pass down the error
+            var err = new Error("Proxy to '"+endpoint+"' timed out");
+            request.emit("error", err);
+        });
+
+        // Pipe the client request upstream
+        req.pipe(request);
+
+        // Pass on our errors
+        request.on('error', next);
+    };
+};
