Handle unknown media length (#406)

Some media download APIs does not support partial downloads and do not
return a 'Content-length' or 'Content-range' header and is therefore
never considered done.

This change is to consider responses where the media length is
unknown to be done.

This commit could potentially break media downloads where the API
supports partial download and only returns a 'Content-length' header
when the entire file has been served/read.

This commit fixes issue #15
diff --git a/docs/epy/googleapiclient.http-pysrc.html b/docs/epy/googleapiclient.http-pysrc.html
index efaa799..3506be8 100644
--- a/docs/epy/googleapiclient.http-pysrc.html
+++ b/docs/epy/googleapiclient.http-pysrc.html
@@ -844,7 +844,7 @@
 <a name="L655"></a><tt class="py-lineno"> 655</tt>  <tt class="py-line"><tt class="py-docstring">    Returns:</tt> </tt>
 <a name="L656"></a><tt class="py-lineno"> 656</tt>  <tt class="py-line"><tt class="py-docstring">      (status, done): (MediaDownloadStatus, boolean)</tt> </tt>
 <a name="L657"></a><tt class="py-lineno"> 657</tt>  <tt class="py-line"><tt class="py-docstring">         The value of 'done' will be True when the media has been fully</tt> </tt>
-<a name="L658"></a><tt class="py-lineno"> 658</tt>  <tt class="py-line"><tt class="py-docstring">         downloaded.</tt> </tt>
+<a name="L658"></a><tt class="py-lineno"> 658</tt>  <tt class="py-line"><tt class="py-docstring">         downloaded or the total size of the media is unknown.</tt> </tt>
 <a name="L659"></a><tt class="py-lineno"> 659</tt>  <tt class="py-line"><tt class="py-docstring"></tt> </tt>
 <a name="L660"></a><tt class="py-lineno"> 660</tt>  <tt class="py-line"><tt class="py-docstring">    Raises:</tt> </tt>
 <a name="L661"></a><tt class="py-lineno"> 661</tt>  <tt class="py-line"><tt class="py-docstring">      googleapiclient.errors.HttpError if the response was not a 2xx.</tt> </tt>
@@ -873,7 +873,7 @@
 <a name="L684"></a><tt class="py-lineno"> 684</tt>  <tt class="py-line">      <tt class="py-keyword">elif</tt> <tt class="py-string">'content-length'</tt> <tt class="py-keyword">in</tt> <tt class="py-name">resp</tt><tt class="py-op">:</tt> </tt>
 <a name="L685"></a><tt class="py-lineno"> 685</tt>  <tt class="py-line">        <tt class="py-name">self</tt><tt class="py-op">.</tt><tt class="py-name">_total_size</tt> <tt class="py-op">=</tt> <tt class="py-name">int</tt><tt class="py-op">(</tt><tt class="py-name">resp</tt><tt class="py-op">[</tt><tt class="py-string">'content-length'</tt><tt class="py-op">]</tt><tt class="py-op">)</tt> </tt>
 <a name="L686"></a><tt class="py-lineno"> 686</tt>  <tt class="py-line"> </tt>
-<a name="L687"></a><tt class="py-lineno"> 687</tt>  <tt class="py-line">      <tt class="py-keyword">if</tt> <tt class="py-name">self</tt><tt class="py-op">.</tt><tt class="py-name">_progress</tt> <tt class="py-op">==</tt> <tt class="py-name">self</tt><tt class="py-op">.</tt><tt class="py-name">_total_size</tt><tt class="py-op">:</tt> </tt>
+<a name="L687"></a><tt class="py-lineno"> 687</tt>  <tt class="py-line">      <tt class="py-keyword">if</tt> <tt class="py-name">self</tt><tt class="py-op">.</tt><tt class="py-name">_total_size</tt> <tt class="py-keyword">is</tt> <tt class="py-name">None</tt> <tt class="py-keyword">or</tt> <tt class="py-name">self</tt><tt class="py-op">.</tt><tt class="py-name">_progress</tt> <tt class="py-op">==</tt> <tt class="py-name">self</tt><tt class="py-op">.</tt><tt class="py-name">_total_size</tt><tt class="py-op">:</tt> </tt>
 <a name="L688"></a><tt class="py-lineno"> 688</tt>  <tt class="py-line">        <tt class="py-name">self</tt><tt class="py-op">.</tt><tt class="py-name">_done</tt> <tt class="py-op">=</tt> <tt class="py-name">True</tt> </tt>
 <a name="L689"></a><tt class="py-lineno"> 689</tt>  <tt class="py-line">      <tt class="py-keyword">return</tt> <tt id="link-86" class="py-name" targets="Class googleapiclient.http.MediaDownloadProgress=googleapiclient.http.MediaDownloadProgress-class.html"><a title="googleapiclient.http.MediaDownloadProgress" class="py-name" href="#" onclick="return doclink('link-86', 'MediaDownloadProgress', 'link-86');">MediaDownloadProgress</a></tt><tt class="py-op">(</tt><tt class="py-name">self</tt><tt class="py-op">.</tt><tt class="py-name">_progress</tt><tt class="py-op">,</tt> <tt class="py-name">self</tt><tt class="py-op">.</tt><tt class="py-name">_total_size</tt><tt class="py-op">)</tt><tt class="py-op">,</tt> <tt class="py-name">self</tt><tt class="py-op">.</tt><tt class="py-name">_done</tt> </tt>
 <a name="L690"></a><tt class="py-lineno"> 690</tt>  <tt class="py-line">    <tt class="py-keyword">else</tt><tt class="py-op">:</tt> </tt>
@@ -2189,7 +2189,7 @@
 <table border="0" cellpadding="0" cellspacing="0" width="100%%">
   <tr>
     <td align="left" class="footer">
-    Generated by Epydoc 3.0.1 on Mon Jun  5 13:38:40 2017
+    Generated by Epydoc 3.0.1 on Wed Jun 14 20:18:21 2017
     </td>
     <td align="right" class="footer">
       <a target="mainFrame" href="http://epydoc.sourceforge.net"
diff --git a/docs/epy/googleapiclient.http.MediaIoBaseDownload-class.html b/docs/epy/googleapiclient.http.MediaIoBaseDownload-class.html
index 06f3781..3bf1a57 100644
--- a/docs/epy/googleapiclient.http.MediaIoBaseDownload-class.html
+++ b/docs/epy/googleapiclient.http.MediaIoBaseDownload-class.html
@@ -266,7 +266,7 @@
 Returns:
   (status, done): (MediaDownloadStatus, boolean)
      The value of 'done' will be True when the media has been fully
-     downloaded.
+     downloaded or the total size of the media is unknown.
 
 Raises:
   googleapiclient.errors.HttpError if the response was not a 2xx.
@@ -308,7 +308,7 @@
 <table border="0" cellpadding="0" cellspacing="0" width="100%%">
   <tr>
     <td align="left" class="footer">
-    Generated by Epydoc 3.0.1 on Mon Jun  5 13:38:40 2017
+    Generated by Epydoc 3.0.1 on Wed Jun 14 20:18:20 2017
     </td>
     <td align="right" class="footer">
       <a target="mainFrame" href="http://epydoc.sourceforge.net"
diff --git a/googleapiclient/http.py b/googleapiclient/http.py
index eb46449..ee8d6e3 100644
--- a/googleapiclient/http.py
+++ b/googleapiclient/http.py
@@ -658,7 +658,7 @@
     Returns:
       (status, done): (MediaDownloadProgress, boolean)
          The value of 'done' will be True when the media has been fully
-         downloaded.
+         downloaded or the total size of the media is unknown.
 
     Raises:
       googleapiclient.errors.HttpError if the response was not a 2xx.
@@ -687,7 +687,7 @@
       elif 'content-length' in resp:
         self._total_size = int(resp['content-length'])
 
-      if self._progress == self._total_size:
+      if self._total_size is None or self._progress == self._total_size:
         self._done = True
       return MediaDownloadProgress(self._progress, self._total_size), self._done
     else:
diff --git a/tests/test_http.py b/tests/test_http.py
index 04e1142..9d48266 100644
--- a/tests/test_http.py
+++ b/tests/test_http.py
@@ -576,6 +576,29 @@
     self.assertEqual(0, download._total_size)
     self.assertEqual(0, status.progress())
 
+  def test_media_io_base_download_unknown_media_size(self):
+    self.request.http = HttpMockSequence([
+      ({'status': '200'}, b'123')
+    ])
+
+    download = MediaIoBaseDownload(
+      fd=self.fd, request=self.request, chunksize=3)
+
+    self.assertEqual(self.fd, download._fd)
+    self.assertEqual(0, download._progress)
+    self.assertEqual(None, download._total_size)
+    self.assertEqual(False, download._done)
+    self.assertEqual(self.request.uri, download._uri)
+
+    status, done = download.next_chunk()
+
+    self.assertEqual(self.fd.getvalue(), b'123')
+    self.assertEqual(True, done)
+    self.assertEqual(3, download._progress)
+    self.assertEqual(None, download._total_size)
+    self.assertEqual(0, status.progress())
+
+
 EXPECTED = """POST /someapi/v1/collection/?foo=bar HTTP/1.1
 Content-Type: application/json
 MIME-Version: 1.0