Got test coverage to 97%. Added missing file.
diff --git a/.hgignore b/.hgignore
index 6b95477..1c08454 100644
--- a/.hgignore
+++ b/.hgignore
@@ -5,3 +5,4 @@
 */.git/*
 .gitignore
 samples/cmdline/*.dat
+htmlcov/*
diff --git a/apiclient/discovery.py b/apiclient/discovery.py
index cbba1dc..9eec549 100644
--- a/apiclient/discovery.py
+++ b/apiclient/discovery.py
@@ -102,7 +102,7 @@
       return simplejson.loads(content)['data']
     else:
       logging.debug('Content from bad request was: %s' % content)
-      if resp['content-type'] != 'application/json':
+      if resp.get('content-type', '') != 'application/json':
         raise HttpError('%d %s' % (resp.status, resp.reason))
       else:
         raise HttpError(simplejson.loads(content)['error'])
@@ -263,7 +263,7 @@
         for key in methodDesc['location']:
           p = p[key]
         url = p
-      except KeyError:
+      except (KeyError, TypeError):
         return None
 
       headers = {}
diff --git a/apiclient/http.py b/apiclient/http.py
new file mode 100644
index 0000000..16591fb
--- /dev/null
+++ b/apiclient/http.py
@@ -0,0 +1,34 @@
+# Copyright 2010 Google Inc. All Rights Reserved.
+
+"""One-line documentation for http module.
+
+A detailed description of http.
+"""
+
+__author__ = 'jcgregorio@google.com (Joe Gregorio)'
+
+
+class HttpRequest(object):
+  """Encapsulate an HTTP request.
+  """
+
+  def __init__(self, http, uri, method="GET", body=None, headers=None, postproc=None):
+    self.uri = uri
+    self.method = method
+    self.body = body
+    self.headers = headers or {}
+    self.http = http
+    self.postproc = postproc
+
+  def execute(self, http=None):
+    """Execute the request.
+
+    If an http object is passed in it is used instead of the
+    httplib2.Http object that the request was constructed with.
+    """
+    if http is None:
+      http = self.http
+    resp, content = http.request(self.uri, self.method,
+                                      body=self.body,
+                                      headers=self.headers)
+    return self.postproc(resp, content)
diff --git a/functional_tests/test_services.py b/functional_tests/test_services.py
index 5a9fa23..e82ec24 100644
--- a/functional_tests/test_services.py
+++ b/functional_tests/test_services.py
@@ -24,8 +24,12 @@
   def test_can_get_buzz_activities_with_many_params(self):
     buzz = build('buzz', 'v1')
     max_results = 2
-    activities = buzz.activities().list(userId='googlebuzz', scope='@self',
-                                        max_comments=max_results*2 ,max_liked=max_results*3,
-                                        max_results=max_results).execute()['items']
+    actcol = buzz.activities()
+    activities = actcol.list(userId='googlebuzz', scope='@self',
+                             max_comments=max_results*2 ,max_liked=max_results*3,
+                             max_results=max_results).execute()['items']
     activity_count = len(activities)
     self.assertEquals(max_results, activity_count)
+
+    activities = actcol.list_next(activities)
+    self.assertEquals(activities, None) # Public streams don't have next links
diff --git a/samples/cmdline/buzz.py b/samples/cmdline/buzz.py
index 60c7d50..b692be5 100644
--- a/samples/cmdline/buzz.py
+++ b/samples/cmdline/buzz.py
@@ -28,8 +28,11 @@
 
   p = build("buzz", "v1", http=http)
   activities = p.activities()
-  activitylist = activities.list(scope='@self', userId='@me').execute()
+  activitylist = activities.list(max_results='2', scope='@self', userId='@me').execute()
   print activitylist['items'][0]['title']
+  activitylist = activities.list_next(activitylist).execute()
+  print activitylist['items'][0]['title']
+
   activities.insert(userId='@me', body={
     'title': 'Testing insert',
     'object': {
diff --git a/tests/test_discovery.py b/tests/test_discovery.py
index 539796f..c44f1e4 100644
--- a/tests/test_discovery.py
+++ b/tests/test_discovery.py
@@ -22,7 +22,7 @@
 
 __author__ = 'jcgregorio@google.com (Joe Gregorio)'
 
-from apiclient.discovery import build
+from apiclient.discovery import build, key2param
 import httplib2
 import os
 import unittest
@@ -41,6 +41,12 @@
     return httplib2.Response(self.headers), self.data
 
 
+class Utilities(unittest.TestCase):
+  def test_key2param(self):
+    self.assertEqual('max_results', key2param('max-results'))
+    self.assertEqual('x007_bond', key2param('007-bond'))
+
+
 class Discovery(unittest.TestCase):
   def test_method_error_checking(self):
     self.http = HttpMock('buzz.json', {'status': '200'})
@@ -86,6 +92,23 @@
     self.assertTrue(getattr(buzz, 'comments'))
     self.assertTrue(getattr(buzz, 'related'))
 
+  def test_auth(self):
+    self.http = HttpMock('buzz.json', {'status': '200'})
+    buzz = build('buzz', 'v1', self.http)
+    auth = buzz.auth_discovery()
+    self.assertTrue('request' in auth)
+
+
+class Next(unittest.TestCase):
+  def test_next(self):
+    self.http = HttpMock('buzz.json', {'status': '200'})
+    buzz = build('buzz', 'v1', self.http)
+    activities = {'links':
+                  {'next':
+                   [{'href': 'http://www.googleapis.com/next-link'}]}}
+    request = buzz.activities().list_next(activities)
+    self.assertEqual(request.uri, 'http://www.googleapis.com/next-link')
+
 
 if __name__ == '__main__':
   unittest.main()
diff --git a/tests/test_json_model.py b/tests/test_json_model.py
index 8049568..79b63ab 100644
--- a/tests/test_json_model.py
+++ b/tests/test_json_model.py
@@ -21,9 +21,10 @@
 
 __author__ = 'jcgregorio@google.com (Joe Gregorio)'
 
-from apiclient.discovery import JsonModel
+from apiclient.discovery import JsonModel, HttpError
 import os
 import unittest
+import httplib2
 
 # Python 2.5 requires different modules
 try:
@@ -82,6 +83,47 @@
     self.assertEqual(query_dict['bar'], [u'\N{COMET}'.encode('utf-8')])
     self.assertEqual(body, '{"data": {}}')
 
+  def test_user_agent(self):
+    model = JsonModel()
+
+    headers = {'user-agent': 'my-test-app/1.23.4'}
+    path_params = {}
+    query_params = {}
+    body = {}
+
+    headers, params, query, body = model.request(headers, path_params, query_params, body)
+
+    self.assertEqual(headers['user-agent'], 'my-test-app/1.23.4 google-api-python-client/1.0')
+
+  def test_bad_response(self):
+    model = JsonModel()
+    resp = httplib2.Response({'status': '401'})
+    resp.reason = 'Unauthorized'
+    content = '{"error": "not authorized"}'
+
+    try:
+      content = model.response(resp, content)
+      self.fail('Should have thrown an exception')
+    except HttpError, e:
+      self.assertTrue('Unauthorized' in str(e))
+
+    resp['content-type'] = 'application/json'
+
+    try:
+      content = model.response(resp, content)
+      self.fail('Should have thrown an exception')
+    except HttpError, e:
+      self.assertTrue('not authorized' in str(e))
+
+
+  def test_good_response(self):
+    model = JsonModel()
+    resp = httplib2.Response({'status': '200'})
+    resp.reason = 'OK'
+    content = '{"data": "is good"}'
+
+    content = model.response(resp, content)
+    self.assertEqual(content, 'is good')
 
 if __name__ == '__main__':
   unittest.main()