diff --git a/src/allmydata/immutable/downloader/status.py b/src/allmydata/immutable/downloader/status.py
index 5d60db0..3970ca1 100644
--- a/src/allmydata/immutable/downloader/status.py
+++ b/src/allmydata/immutable/downloader/status.py
@@ -157,9 +157,49 @@ class DownloadStatus:
     def get_size(self):
         return self.size
     def get_status(self):
-        return "not impl yet" # TODO
+        # mention all outstanding segment requests
+        outstanding = set()
+        errorful = set()
+        for s_ev in self.segment_events:
+            (etype, segnum, when, segstart, seglen, decodetime) = s_ev
+            if etype == "request":
+                outstanding.add(segnum)
+            elif etype == "delivery":
+                outstanding.remove(segnum)
+            else: # "error"
+                outstanding.remove(segnum)
+                errorful.add(segnum)
+        def join(segnums):
+            if len(segnums) == 1:
+                return "segment %s" % list(segnums)[0]
+            else:
+                return "segments %s" % (",".join([str(i)
+                                                  for i in sorted(segnums)]))
+        error_s = ""
+        if errorful:
+            error_s = "; errors on %s" % join(errorful)
+        if outstanding:
+            s = "fetching %s" % join(outstanding)
+        else:
+            s = "idle"
+        return s + error_s
+
     def get_progress(self):
-        return 0.1 # TODO
+        # measure all read events that aren't completely done, return the
+        # total percentage complete for them
+        if not self.read_events:
+            return 0.0
+        total_outstanding, total_received = 0, 0
+        for r_ev in self.read_events:
+            (start, length, ign1, finishtime, bytes, ign2, ign3) = r_ev
+            if finishtime is None:
+                total_outstanding += length
+                total_received += bytes
+            # else ignore completed requests
+        if not total_outstanding:
+            return 1.0
+        return 1.0 * total_received / total_outstanding
+
     def using_helper(self):
         return False
     def get_active(self):
diff --git a/src/allmydata/test/test_download.py b/src/allmydata/test/test_download.py
index add4d24..71a556b 100644
--- a/src/allmydata/test/test_download.py
+++ b/src/allmydata/test/test_download.py
@@ -16,6 +16,7 @@ from allmydata.test.common import ShouldFailMixin
 from allmydata.interfaces import NotEnoughSharesError, NoSharesError
 from allmydata.immutable.downloader.common import BadSegmentNumberError, \
      BadCiphertextHashError, DownloadStopped
+from allmydata.immutable.downloader.status import DownloadStatus
 from allmydata.codec import CRSDecoder
 from foolscap.eventual import fireEventually, flushEventualQueue
 
@@ -1214,3 +1215,45 @@ class DownloadV2(_Base, unittest.TestCase):
             return d
         d.addCallback(_uploaded)
         return d
+
+class Status(unittest.TestCase):
+    def test_status(self):
+        now = 12345.1
+        ds = DownloadStatus("si-1", 123)
+        self.failUnlessEqual(ds.get_status(), "idle")
+        ds.add_segment_request(0, now)
+        self.failUnlessEqual(ds.get_status(), "fetching segment 0")
+        ds.add_segment_delivery(0, now+1, 0, 1000, 2.0)
+        self.failUnlessEqual(ds.get_status(), "idle")
+        ds.add_segment_request(2, now+2)
+        ds.add_segment_request(1, now+2)
+        self.failUnlessEqual(ds.get_status(), "fetching segments 1,2")
+        ds.add_segment_error(1, now+3)
+        self.failUnlessEqual(ds.get_status(),
+                             "fetching segment 2; errors on segment 1")
+
+    def test_progress(self):
+        now = 12345.1
+        ds = DownloadStatus("si-1", 123)
+        self.failUnlessEqual(ds.get_progress(), 0.0)
+        e = ds.add_read_event(0, 1000, now)
+        self.failUnlessEqual(ds.get_progress(), 0.0)
+        e.update(500, 2.0, 2.0)
+        self.failUnlessEqual(ds.get_progress(), 0.5)
+        e.finished(now+2)
+        self.failUnlessEqual(ds.get_progress(), 1.0)
+
+        e1 = ds.add_read_event(1000, 2000, now+3)
+        e2 = ds.add_read_event(4000, 2000, now+3)
+        self.failUnlessEqual(ds.get_progress(), 0.0)
+        e1.update(1000, 2.0, 2.0)
+        self.failUnlessEqual(ds.get_progress(), 0.25)
+        e2.update(1000, 2.0, 2.0)
+        self.failUnlessEqual(ds.get_progress(), 0.5)
+        e1.update(1000, 2.0, 2.0)
+        e1.finished(now+4)
+        # now there is only one outstanding read, and it is 50% done
+        self.failUnlessEqual(ds.get_progress(), 0.5)
+        e2.update(1000, 2.0, 2.0)
+        e2.finished(now+5)
+        self.failUnlessEqual(ds.get_progress(), 1.0)
diff --git a/src/allmydata/test/test_web.py b/src/allmydata/test/test_web.py
index dfaaabd..78b9902 100644
--- a/src/allmydata/test/test_web.py
+++ b/src/allmydata/test/test_web.py
@@ -83,6 +83,9 @@ def build_one_ds():
     ds.add_segment_delivery(0, now+1, 0, 100, 0.5)
     ds.add_segment_request(1, now+2)
     ds.add_segment_error(1, now+3)
+    # two outstanding requests
+    ds.add_segment_request(2, now+4)
+    ds.add_segment_request(3, now+5)
 
     e = ds.add_dyhb_sent("serverid_a", now)
     e.finished([1,2], now+1)
