We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Python 3: Add Cover Image hangs
<!-- What problem are we solving? What does the experience look like today? What are the symptoms? -->

### Evidence / Screenshot (if possible)
http://localhost:8080/works/OL6037022W/Remix?debug=true on Docker Python 3
* Or http://staging.openlibrary.org/works/OL6037022W/Remix?debug=true

Command to launch Open Library on Docker on Python 3:
```
docker-compose down ; \
    PYENV_VERSION=3.9.0 docker-compose -f docker-compose.yml -f docker-compose.infogami-local.yml up -d ; \
    docker-compose logs -f --tail=10 web
```
### Relevant url?
<!-- `https://openlibrary.org/...` -->
1. http://localhost:8080/works/OL6037022W/Remix?debug=true
2. On the image, click Add Cover Image
3. Browse and select an appropriate local image file and click Submit
4. Internal Server Error

### Steps to Reproduce
<!-- What steps caused you to find the bug? -->
1. Go to ...
2. Do ...

<!-- What actually happened after these steps? What did you expect to happen? -->
* Actual: 
* Expected: 

### Details

- **Logged in (Y/N)?** 
- **Browser type/version?** 
- **Operating system?** 
- **Environment (prod/dev/local)?** prod
<!-- If not sure, put prod -->

### Proposal & Constraints
<!-- What is the proposed solution / implementation? Is there a precedent of this approach succeeding elsewhere? -->

### Related files
<!-- Files related to this issue; this is super useful for new contributors who might want to help! If you're not sure, leave this blank; a maintainer will add them. -->

### Stakeholders
<!-- @ tag stakeholders of this bug -->

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `openlibrary/plugins/upstream/covers.py`
Content:
```
1 """Handle book cover/author photo upload.
2 """
3 import web
4 import simplejson
5 
6 from infogami.utils import delegate
7 from infogami.utils.view import safeint
8 from openlibrary import accounts
9 from openlibrary.plugins.upstream.models import Image
10 from openlibrary.plugins.upstream.utils import get_coverstore_url, render_template
11 
12 from six.moves import urllib
13 
14 
15 def setup():
16     pass
17 
18 class add_cover(delegate.page):
19     path = "(/books/OL\d+M)/add-cover"
20     cover_category = "b"
21 
22     def GET(self, key):
23         book = web.ctx.site.get(key)
24         return render_template('covers/add', book)
25 
26     def POST(self, key):
27         book = web.ctx.site.get(key)
28         if not book:
29             raise web.notfound("")
30 
31         i = web.input(file={}, url="")
32 
33         # remove references to field storage objects
34         web.ctx.pop("_fieldstorage", None)
35 
36         data = self.upload(key, i)
37         coverid = data.get('id')
38 
39         if coverid:
40             self.save(book, coverid, url=i.url)
41             cover = Image(web.ctx.site, "b", coverid)
42             return render_template("covers/saved", cover)
43         else:
44             return render_template("covers/add", book, {'url': i.url}, data)
45 
46     def upload(self, key, i):
47         """Uploads a cover to coverstore and returns the response."""
48         olid = key.split("/")[-1]
49 
50         if i.file is not None and hasattr(i.file, 'value'):
51             data = i.file.value
52         else:
53             data = None
54 
55         if i.url and i.url.strip() == "http://":
56             i.url = ""
57 
58         user = accounts.get_current_user()
59         params = {
60             "author": user and user.key,
61             "data": data,
62             "source_url": i.url,
63             "olid": olid,
64             "ip": web.ctx.ip
65         }
66 
67         upload_url = '%s/%s/upload2' % (
68             get_coverstore_url(), self.cover_category)
69 
70         if upload_url.startswith("//"):
71             upload_url = "http:" + upload_url
72 
73         try:
74             response = urllib.request.urlopen(upload_url, urllib.parse.urlencode(params))
75             out = response.read()
76         except urllib.error.HTTPError as e:
77             out = {'error': e.read()}
78 
79         return web.storage(simplejson.loads(out))
80 
81     def save(self, book, coverid, url=None):
82         book.covers = [coverid] + [cover.id for cover in book.get_covers()]
83         book._save("Added new cover", action="add-cover", data={"url": url})
84 
85 class add_work_cover(add_cover):
86     path = "(/works/OL\d+W)/add-cover"
87     cover_category = "w"
88 
89     def upload(self, key, i):
90         if "coverid" in i and safeint(i.coverid):
91             return web.storage(id=int(i.coverid))
92         else:
93             return add_cover.upload(self, key, i)
94 
95 class add_photo(add_cover):
96     path = "(/authors/OL\d+A)/add-photo"
97     cover_category = "a"
98 
99     def save(self, author, photoid, url=None):
100         author.photos = [photoid] + [photo.id for photo in author.get_photos()]
101         author._save("Added new photo", action="add-photo", data={"url": url})
102 
103 class manage_covers(delegate.page):
104     path = "(/books/OL\d+M)/manage-covers"
105     def GET(self, key):
106         book = web.ctx.site.get(key)
107         if not book:
108             raise web.notfound()
109         return render_template("covers/manage", key, self.get_images(book))
110 
111     def get_images(self, book):
112         return book.get_covers()
113 
114     def get_image(self, book):
115         return book.get_cover()
116 
117     def save_images(self, book, covers):
118         book.covers = covers
119         book._save('Update covers')
120 
121     def POST(self, key):
122         book = web.ctx.site.get(key)
123         if not book:
124             raise web.notfound()
125 
126         images = web.input(image=[]).image
127         if '-' in images:
128             images = [int(id) for id in images[:images.index('-')]]
129             self.save_images(book, images)
130             return render_template("covers/saved", self.get_image(book), showinfo=False)
131         else:
132             # ERROR
133             pass
134 
135 class manage_work_covers(manage_covers):
136     path = "(/works/OL\d+W)/manage-covers"
137 
138 
139 class manage_photos(manage_covers):
140     path = "(/authors/OL\d+A)/manage-photos"
141 
142     def get_images(self, author):
143         return author.get_photos()
144 
145     def get_image(self, author):
146         return author.get_photo()
147 
148     def save_images(self, author, photos):
149         author.photos = photos
150         author._save('Update photos')
151
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/openlibrary/plugins/upstream/covers.py b/openlibrary/plugins/upstream/covers.py
--- a/openlibrary/plugins/upstream/covers.py
+++ b/openlibrary/plugins/upstream/covers.py
@@ -1,7 +1,7 @@
 """Handle book cover/author photo upload.
 """
+import requests
 import web
-import simplejson
 
 from infogami.utils import delegate
 from infogami.utils.view import safeint
@@ -9,8 +9,6 @@
 from openlibrary.plugins.upstream.models import Image
 from openlibrary.plugins.upstream.utils import get_coverstore_url, render_template
 
-from six.moves import urllib
-
 
 def setup():
     pass
@@ -71,12 +69,10 @@
             upload_url = "http:" + upload_url
 
         try:
-            response = urllib.request.urlopen(upload_url, urllib.parse.urlencode(params))
-            out = response.read()
-        except urllib.error.HTTPError as e:
-            out = {'error': e.read()}
-
-        return web.storage(simplejson.loads(out))
+            payload = requests.compat.urlencode(params).encode('utf-8')
+            return web.storage(requests.post(upload_url, data=payload).json())
+        except requests.HTTPError as e:
+            return web.storage({'error': e.read()})
 
     def save(self, book, coverid, url=None):
         book.covers = [coverid] + [cover.id for cover in book.get_covers()]

{"golden_diff": "diff --git a/openlibrary/plugins/upstream/covers.py b/openlibrary/plugins/upstream/covers.py\n--- a/openlibrary/plugins/upstream/covers.py\n+++ b/openlibrary/plugins/upstream/covers.py\n@@ -1,7 +1,7 @@\n \"\"\"Handle book cover/author photo upload.\n \"\"\"\n+import requests\n import web\n-import simplejson\n \n from infogami.utils import delegate\n from infogami.utils.view import safeint\n@@ -9,8 +9,6 @@\n from openlibrary.plugins.upstream.models import Image\n from openlibrary.plugins.upstream.utils import get_coverstore_url, render_template\n \n-from six.moves import urllib\n-\n \n def setup():\n     pass\n@@ -71,12 +69,10 @@\n             upload_url = \"http:\" + upload_url\n \n         try:\n-            response = urllib.request.urlopen(upload_url, urllib.parse.urlencode(params))\n-            out = response.read()\n-        except urllib.error.HTTPError as e:\n-            out = {'error': e.read()}\n-\n-        return web.storage(simplejson.loads(out))\n+            payload = requests.compat.urlencode(params).encode('utf-8')\n+            return web.storage(requests.post(upload_url, data=payload).json())\n+        except requests.HTTPError as e:\n+            return web.storage({'error': e.read()})\n \n     def save(self, book, coverid, url=None):\n         book.covers = [coverid] + [cover.id for cover in book.get_covers()]\n", "issue": "Python 3: Add Cover Image hangs\n<!-- What problem are we solving? What does the experience look like today? What are the symptoms? -->\r\n\r\n### Evidence / Screenshot (if possible)\r\nhttp://localhost:8080/works/OL6037022W/Remix?debug=true on Docker Python 3\r\n* Or http://staging.openlibrary.org/works/OL6037022W/Remix?debug=true\r\n\r\nCommand to launch Open Library on Docker on Python 3:\r\n```\r\ndocker-compose down ; \\\r\n    PYENV_VERSION=3.9.0 docker-compose -f docker-compose.yml -f docker-compose.infogami-local.yml up -d ; \\\r\n    docker-compose logs -f --tail=10 web\r\n```\r\n### Relevant url?\r\n<!-- `https://openlibrary.org/...` -->\r\n1. http://localhost:8080/works/OL6037022W/Remix?debug=true\r\n2. On the image, click Add Cover Image\r\n3. Browse and select an appropriate local image file and click Submit\r\n4. Internal Server Error\r\n\r\n### Steps to Reproduce\r\n<!-- What steps caused you to find the bug? -->\r\n1. Go to ...\r\n2. Do ...\r\n\r\n<!-- What actually happened after these steps? What did you expect to happen? -->\r\n* Actual: \r\n* Expected: \r\n\r\n### Details\r\n\r\n- **Logged in (Y/N)?** \r\n- **Browser type/version?** \r\n- **Operating system?** \r\n- **Environment (prod/dev/local)?** prod\r\n<!-- If not sure, put prod -->\r\n\r\n### Proposal & Constraints\r\n<!-- What is the proposed solution / implementation? Is there a precedent of this approach succeeding elsewhere? -->\r\n\r\n### Related files\r\n<!-- Files related to this issue; this is super useful for new contributors who might want to help! If you're not sure, leave this blank; a maintainer will add them. -->\r\n\r\n### Stakeholders\r\n<!-- @ tag stakeholders of this bug -->\n", "before_files": [{"content": "\"\"\"Handle book cover/author photo upload.\n\"\"\"\nimport web\nimport simplejson\n\nfrom infogami.utils import delegate\nfrom infogami.utils.view import safeint\nfrom openlibrary import accounts\nfrom openlibrary.plugins.upstream.models import Image\nfrom openlibrary.plugins.upstream.utils import get_coverstore_url, render_template\n\nfrom six.moves import urllib\n\n\ndef setup():\n    pass\n\nclass add_cover(delegate.page):\n    path = \"(/books/OL\\d+M)/add-cover\"\n    cover_category = \"b\"\n\n    def GET(self, key):\n        book = web.ctx.site.get(key)\n        return render_template('covers/add', book)\n\n    def POST(self, key):\n        book = web.ctx.site.get(key)\n        if not book:\n            raise web.notfound(\"\")\n\n        i = web.input(file={}, url=\"\")\n\n        # remove references to field storage objects\n        web.ctx.pop(\"_fieldstorage\", None)\n\n        data = self.upload(key, i)\n        coverid = data.get('id')\n\n        if coverid:\n            self.save(book, coverid, url=i.url)\n            cover = Image(web.ctx.site, \"b\", coverid)\n            return render_template(\"covers/saved\", cover)\n        else:\n            return render_template(\"covers/add\", book, {'url': i.url}, data)\n\n    def upload(self, key, i):\n        \"\"\"Uploads a cover to coverstore and returns the response.\"\"\"\n        olid = key.split(\"/\")[-1]\n\n        if i.file is not None and hasattr(i.file, 'value'):\n            data = i.file.value\n        else:\n            data = None\n\n        if i.url and i.url.strip() == \"http://\":\n            i.url = \"\"\n\n        user = accounts.get_current_user()\n        params = {\n            \"author\": user and user.key,\n            \"data\": data,\n            \"source_url\": i.url,\n            \"olid\": olid,\n            \"ip\": web.ctx.ip\n        }\n\n        upload_url = '%s/%s/upload2' % (\n            get_coverstore_url(), self.cover_category)\n\n        if upload_url.startswith(\"//\"):\n            upload_url = \"http:\" + upload_url\n\n        try:\n            response = urllib.request.urlopen(upload_url, urllib.parse.urlencode(params))\n            out = response.read()\n        except urllib.error.HTTPError as e:\n            out = {'error': e.read()}\n\n        return web.storage(simplejson.loads(out))\n\n    def save(self, book, coverid, url=None):\n        book.covers = [coverid] + [cover.id for cover in book.get_covers()]\n        book._save(\"Added new cover\", action=\"add-cover\", data={\"url\": url})\n\nclass add_work_cover(add_cover):\n    path = \"(/works/OL\\d+W)/add-cover\"\n    cover_category = \"w\"\n\n    def upload(self, key, i):\n        if \"coverid\" in i and safeint(i.coverid):\n            return web.storage(id=int(i.coverid))\n        else:\n            return add_cover.upload(self, key, i)\n\nclass add_photo(add_cover):\n    path = \"(/authors/OL\\d+A)/add-photo\"\n    cover_category = \"a\"\n\n    def save(self, author, photoid, url=None):\n        author.photos = [photoid] + [photo.id for photo in author.get_photos()]\n        author._save(\"Added new photo\", action=\"add-photo\", data={\"url\": url})\n\nclass manage_covers(delegate.page):\n    path = \"(/books/OL\\d+M)/manage-covers\"\n    def GET(self, key):\n        book = web.ctx.site.get(key)\n        if not book:\n            raise web.notfound()\n        return render_template(\"covers/manage\", key, self.get_images(book))\n\n    def get_images(self, book):\n        return book.get_covers()\n\n    def get_image(self, book):\n        return book.get_cover()\n\n    def save_images(self, book, covers):\n        book.covers = covers\n        book._save('Update covers')\n\n    def POST(self, key):\n        book = web.ctx.site.get(key)\n        if not book:\n            raise web.notfound()\n\n        images = web.input(image=[]).image\n        if '-' in images:\n            images = [int(id) for id in images[:images.index('-')]]\n            self.save_images(book, images)\n            return render_template(\"covers/saved\", self.get_image(book), showinfo=False)\n        else:\n            # ERROR\n            pass\n\nclass manage_work_covers(manage_covers):\n    path = \"(/works/OL\\d+W)/manage-covers\"\n\n\nclass manage_photos(manage_covers):\n    path = \"(/authors/OL\\d+A)/manage-photos\"\n\n    def get_images(self, author):\n        return author.get_photos()\n\n    def get_image(self, author):\n        return author.get_photo()\n\n    def save_images(self, author, photos):\n        author.photos = photos\n        author._save('Update photos')\n", "path": "openlibrary/plugins/upstream/covers.py"}], "after_files": [{"content": "\"\"\"Handle book cover/author photo upload.\n\"\"\"\nimport requests\nimport web\n\nfrom infogami.utils import delegate\nfrom infogami.utils.view import safeint\nfrom openlibrary import accounts\nfrom openlibrary.plugins.upstream.models import Image\nfrom openlibrary.plugins.upstream.utils import get_coverstore_url, render_template\n\n\ndef setup():\n    pass\n\nclass add_cover(delegate.page):\n    path = \"(/books/OL\\d+M)/add-cover\"\n    cover_category = \"b\"\n\n    def GET(self, key):\n        book = web.ctx.site.get(key)\n        return render_template('covers/add', book)\n\n    def POST(self, key):\n        book = web.ctx.site.get(key)\n        if not book:\n            raise web.notfound(\"\")\n\n        i = web.input(file={}, url=\"\")\n\n        # remove references to field storage objects\n        web.ctx.pop(\"_fieldstorage\", None)\n\n        data = self.upload(key, i)\n        coverid = data.get('id')\n\n        if coverid:\n            self.save(book, coverid, url=i.url)\n            cover = Image(web.ctx.site, \"b\", coverid)\n            return render_template(\"covers/saved\", cover)\n        else:\n            return render_template(\"covers/add\", book, {'url': i.url}, data)\n\n    def upload(self, key, i):\n        \"\"\"Uploads a cover to coverstore and returns the response.\"\"\"\n        olid = key.split(\"/\")[-1]\n\n        if i.file is not None and hasattr(i.file, 'value'):\n            data = i.file.value\n        else:\n            data = None\n\n        if i.url and i.url.strip() == \"http://\":\n            i.url = \"\"\n\n        user = accounts.get_current_user()\n        params = {\n            \"author\": user and user.key,\n            \"data\": data,\n            \"source_url\": i.url,\n            \"olid\": olid,\n            \"ip\": web.ctx.ip\n        }\n\n        upload_url = '%s/%s/upload2' % (\n            get_coverstore_url(), self.cover_category)\n\n        if upload_url.startswith(\"//\"):\n            upload_url = \"http:\" + upload_url\n\n        try:\n            payload = requests.compat.urlencode(params).encode('utf-8')\n            return web.storage(requests.post(upload_url, data=payload).json())\n        except requests.HTTPError as e:\n            return web.storage({'error': e.read()})\n\n    def save(self, book, coverid, url=None):\n        book.covers = [coverid] + [cover.id for cover in book.get_covers()]\n        book._save(\"Added new cover\", action=\"add-cover\", data={\"url\": url})\n\nclass add_work_cover(add_cover):\n    path = \"(/works/OL\\d+W)/add-cover\"\n    cover_category = \"w\"\n\n    def upload(self, key, i):\n        if \"coverid\" in i and safeint(i.coverid):\n            return web.storage(id=int(i.coverid))\n        else:\n            return add_cover.upload(self, key, i)\n\nclass add_photo(add_cover):\n    path = \"(/authors/OL\\d+A)/add-photo\"\n    cover_category = \"a\"\n\n    def save(self, author, photoid, url=None):\n        author.photos = [photoid] + [photo.id for photo in author.get_photos()]\n        author._save(\"Added new photo\", action=\"add-photo\", data={\"url\": url})\n\nclass manage_covers(delegate.page):\n    path = \"(/books/OL\\d+M)/manage-covers\"\n    def GET(self, key):\n        book = web.ctx.site.get(key)\n        if not book:\n            raise web.notfound()\n        return render_template(\"covers/manage\", key, self.get_images(book))\n\n    def get_images(self, book):\n        return book.get_covers()\n\n    def get_image(self, book):\n        return book.get_cover()\n\n    def save_images(self, book, covers):\n        book.covers = covers\n        book._save('Update covers')\n\n    def POST(self, key):\n        book = web.ctx.site.get(key)\n        if not book:\n            raise web.notfound()\n\n        images = web.input(image=[]).image\n        if '-' in images:\n            images = [int(id) for id in images[:images.index('-')]]\n            self.save_images(book, images)\n            return render_template(\"covers/saved\", self.get_image(book), showinfo=False)\n        else:\n            # ERROR\n            pass\n\nclass manage_work_covers(manage_covers):\n    path = \"(/works/OL\\d+W)/manage-covers\"\n\n\nclass manage_photos(manage_covers):\n    path = \"(/authors/OL\\d+A)/manage-photos\"\n\n    def get_images(self, author):\n        return author.get_photos()\n\n    def get_image(self, author):\n        return author.get_photo()\n\n    def save_images(self, author, photos):\n        author.photos = photos\n        author._save('Update photos')\n", "path": "openlibrary/plugins/upstream/covers.py"}]}