Fix Batch issues. Fixes issue #41.
diff --git a/apiclient/discovery.py b/apiclient/discovery.py
index 4f76ca0..9344392 100644
--- a/apiclient/discovery.py
+++ b/apiclient/discovery.py
@@ -343,7 +343,7 @@
           'location': 'query'
           }
 
-    if httpMethod in ['PUT', 'POST', 'PATCH']:
+    if httpMethod in ['PUT', 'POST', 'PATCH'] and 'request' in methodDesc:
       methodDesc['parameters']['body'] = {
           'description': 'The request body.',
           'type': 'object',
@@ -353,12 +353,13 @@
         methodDesc['parameters']['body'].update(methodDesc['request'])
       else:
         methodDesc['parameters']['body']['type'] = 'object'
-      if 'mediaUpload' in methodDesc:
-        methodDesc['parameters']['media_body'] = {
-            'description': 'The filename of the media request body.',
-            'type': 'string',
-            'required': False,
-            }
+    if 'mediaUpload' in methodDesc:
+      methodDesc['parameters']['media_body'] = {
+          'description': 'The filename of the media request body.',
+          'type': 'string',
+          'required': False,
+          }
+      if 'body' in methodDesc['parameters']:
         methodDesc['parameters']['body']['required'] = False
 
     argmap = {} # Map from method parameter name to query parameter name
diff --git a/apiclient/errors.py b/apiclient/errors.py
index 0d420df..fca3960 100644
--- a/apiclient/errors.py
+++ b/apiclient/errors.py
@@ -91,9 +91,18 @@
   pass
 
 
-class BatchError(Error):
+class BatchError(HttpError):
   """Error occured during batch operations."""
-  pass
+
+  def __init__(self, reason, resp=None, content=None):
+    self.resp = resp
+    self.content = content
+    self.reason = reason
+
+  def __repr__(self):
+      return '<BatchError %s "%s">' % (self.resp.status, self.reason)
+
+  __str__ = __repr__
 
 
 class UnexpectedMethodError(Error):
diff --git a/apiclient/http.py b/apiclient/http.py
index 8ea5c73..6de443e 100644
--- a/apiclient/http.py
+++ b/apiclient/http.py
@@ -491,7 +491,7 @@
         (None, None, parsed.path, parsed.params, parsed.query, None)
         )
     status_line = request.method + ' ' + request_line + ' HTTP/1.1\n'
-    major, minor = request.headers.get('content-type', 'text/plain').split('/')
+    major, minor = request.headers.get('content-type', 'application/json').split('/')
     msg = MIMENonMultipart(major, minor)
     headers = request.headers.copy()
 
@@ -506,6 +506,7 @@
 
     if request.body is not None:
       msg.set_payload(request.body)
+      msg['content-length'] = str(len(request.body))
 
     body = msg.as_string(False)
     # Strip off the \n\n that the MIME lib tacks onto the end of the payload.
@@ -525,7 +526,7 @@
     """
     # Strip off the status line
     status_line, payload = payload.split('\n', 1)
-    protocol, status, reason = status_line.split(' ')
+    protocol, status, reason = status_line.split(' ', 2)
 
     # Parse the rest of the response
     parser = FeedParser()
@@ -604,6 +605,7 @@
     Raises:
       apiclient.errors.HttpError if the response was not a 2xx.
       httplib2.Error if a transport error has occured.
+      apiclient.errors.BatchError if the response is the wrong format.
     """
     if http is None:
       for request_id in self._order:
@@ -649,14 +651,15 @@
 
     # Prepend with a content-type header so FeedParser can handle it.
     header = 'Content-Type: %s\r\n\r\n' % resp['content-type']
-    content = header + content
+    for_parser = header + content
 
     parser = FeedParser()
-    parser.feed(content)
+    parser.feed(for_parser)
     respRoot = parser.close()
 
     if not respRoot.is_multipart():
-      raise BatchError("Response not in multipart/mixed format.")
+      raise BatchError("Response not in multipart/mixed format.", resp,
+          content)
 
     parts = respRoot.get_payload()
     for part in parts:
diff --git a/tests/test_http.py b/tests/test_http.py
index b7054dc..cbbac61 100644
--- a/tests/test_http.py
+++ b/tests/test_http.py
@@ -114,7 +114,15 @@
 EXPECTED = """POST /someapi/v1/collection/?foo=bar HTTP/1.1
 Content-Type: application/json
 MIME-Version: 1.0
-Host: www.googleapis.com\r\n\r\n{}"""
+Host: www.googleapis.com
+content-length: 2\r\n\r\n{}"""
+
+
+NO_BODY_EXPECTED = """POST /someapi/v1/collection/?foo=bar HTTP/1.1
+Content-Type: application/json
+MIME-Version: 1.0
+Host: www.googleapis.com
+content-length: 0\r\n\r\n"""
 
 
 RESPONSE = """HTTP/1.1 200 OK
@@ -144,6 +152,7 @@
 ETag: "etag/sheep"\r\n\r\n{"baz": "qux"}
 --batch_foobarbaz--"""
 
+
 class TestBatch(unittest.TestCase):
 
   def setUp(self):
@@ -160,8 +169,8 @@
         None,
         model.response,
         'https://www.googleapis.com/someapi/v1/collection/?foo=bar',
-        method='POST',
-        body='{}',
+        method='GET',
+        body='',
         headers={'content-type': 'application/json'})
 
 
@@ -189,6 +198,20 @@
     s = batch._serialize_request(request).splitlines()
     self.assertEquals(s, EXPECTED.splitlines())
 
+  def test_serialize_request_no_body(self):
+    batch = BatchHttpRequest()
+    request = HttpRequest(
+        None,
+        None,
+        'https://www.googleapis.com/someapi/v1/collection/?foo=bar',
+        method='POST',
+        body='',
+        headers={'content-type': 'application/json'},
+        methodId=None,
+        resumable=None)
+    s = batch._serialize_request(request).splitlines()
+    self.assertEquals(s, NO_BODY_EXPECTED.splitlines())
+
   def test_deserialize_response(self):
     batch = BatchHttpRequest()
     resp, content = batch._deserialize_response(RESPONSE)
@@ -247,6 +270,29 @@
     self.assertEqual(callbacks.responses['1'], {'foo': 42})
     self.assertEqual(callbacks.responses['2'], {'baz': 'qux'})
 
+  def test_execute_request_body(self):
+    batch = BatchHttpRequest()
+
+    batch.add(self.request1)
+    batch.add(self.request2)
+    http = HttpMockSequence([
+      ({'status': '200',
+        'content-type': 'multipart/mixed; boundary="batch_foobarbaz"'},
+        'echo_request_body'),
+      ])
+    try:
+      batch.execute(http)
+      self.fail('Should raise exception')
+    except BatchError, e:
+      boundary, _ = e.content.split(None, 1)
+      self.assertEqual('--', boundary[:2])
+      parts = e.content.split(boundary)
+      self.assertEqual(4, len(parts))
+      self.assertEqual('', parts[0])
+      self.assertEqual('--', parts[3])
+      header = parts[1].splitlines()[1]
+      self.assertEqual('Content-Type: application/http', header)
+
   def test_execute_global_callback(self):
     class Callbacks(object):
       def __init__(self):