coveralls: 
[![Coverage Status](https://coveralls.io/builds/14246503/badge)](https://coveralls.io/builds/14246503)

Coverage decreased (-0.8%) to 89.64% when pulling **b79589e9daec23c08537795327876a336be505df on errors** into **d47d0ec83f7d76912d5b6b00fd130d79f892939c on master**.

coveralls: 
[![Coverage Status](https://coveralls.io/builds/14246507/badge)](https://coveralls.io/builds/14246507)

Coverage increased (+0.1%) to 90.583% when pulling **b79589e9daec23c08537795327876a336be505df on errors** into **d47d0ec83f7d76912d5b6b00fd130d79f892939c on master**.

coveralls: 
[![Coverage Status](https://coveralls.io/builds/14246523/badge)](https://coveralls.io/builds/14246523)

Coverage increased (+5.6%) to 96.035% when pulling **ed28971b3184dcc7a9a4275edd8fc66ac7d1bd61 on errors** into **d47d0ec83f7d76912d5b6b00fd130d79f892939c on master**.

diff --git a/grole.py b/grole.py
index 72eba4a..bd9ed83 100755
--- a/grole.py
+++ b/grole.py
@@ -376,6 +376,9 @@ class Grole:
                 self._logger.info('{}: {} -> {}'.format(peer, req.path,  res.code))
         except EOFError:
             self._logger.debug('Connection closed from {}'.format(peer))
+        except Exception as e:
+            self._logger.error('Connection error ({}) from {}'.format(e, peer))
+            writer.close()
 
     def run(self, host='localhost', port=1234):
         """
@@ -389,7 +392,11 @@ class Grole:
         # Setup loop
         loop = asyncio.get_event_loop()
         coro = asyncio.start_server(self._handle, host, port, loop=loop)
-        server = loop.run_until_complete(coro)
+        try:
+            server = loop.run_until_complete(coro)
+        except Exception as e:
+            self._logger.error('Could not launch server: {}'.format(e))
+            return
 
         # Run the server
         self._logger.info('Serving on {}'.format(server.sockets[0].getsockname()))

HTTPS connections result in exceptions
To reproduce simply run grole.py and connect to https://127.0.0.1:1234/

ERROR:asyncio:Task exception was never retrieved
future: <Task finished coro=<Grole._handle() done, defined at ./grole.py:336> exception=UnicodeDecodeError('utf-8', b'[Long hex string that I redacted]\n', 4, 5, 'invalid start byte')>
Traceback (most recent call last):
  File "/usr/lib/python3.5/asyncio/tasks.py", line 239, in _step
    result = coro.send(None)
  File "./grole.py", line 349, in _handle
    await req._read(reader)
  File "./grole.py", line 45, in _read
    self.method, self.location, self.version = start_line.decode().split()
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xae in position 4: invalid start byte
diff --git a/test/helpers.py b/test/helpers.py
index 51c1394..85f307b 100644
--- a/test/helpers.py
+++ b/test/helpers.py
@@ -32,3 +32,19 @@ class FakeWriter():
 
     def get_extra_info(self, arg):
         return 'fake'
+
+class ErrorWriter():
+    def __init__(self):
+        self.closed = False
+
+    async def drain(self):
+        return
+
+    def write(self, data):
+        raise Exception('Broken')
+
+    def get_extra_info(self, arg):
+        return 'fake'
+
+    def close(self):
+        self.closed = True
diff --git a/test/test_grole.py b/test/test_grole.py
index 26eb5e7..02a7a6d 100644
--- a/test/test_grole.py
+++ b/test/test_grole.py
@@ -1,6 +1,6 @@
 import unittest
 import pathlib
-from helpers import FakeReader, FakeWriter, a_wait
+from helpers import *
 
 import grole
 
@@ -43,6 +43,18 @@ class TestGrole(unittest.TestCase):
         data = wr.data.split(b'\r\n')[0]
         self.assertEqual(b'HTTP/1.1 500 Internal Server Error', data)
 
+    def test_close_big_error(self):
+        @self.app.route('/')
+        def error(env, req):
+            a = []
+            return a[1]
+
+        rd = FakeReader(data=b'GET / HTTP/1.1\r\n\r\n')
+        wr = ErrorWriter()
+        a_wait(self.app._handle(rd, wr))
+        self.assertTrue(wr.closed)
+
+
     def test_404(self):
         rd = FakeReader(data=b'GET / HTTP/1.1\r\n\r\n')
         wr = FakeWriter()
diff --git a/test/test_main.py b/test/test_main.py
new file mode 100644
index 0000000..4df7d18
--- /dev/null
+++ b/test/test_main.py
@@ -0,0 +1,17 @@
+import unittest
+
+import grole
+
+class TestMain(unittest.TestCase):
+
+    def test_launch(self):
+        # Success is that it doesn't do anything
+        grole.main(['-p', '80'])
+
+    def test_launch2(self):
+        # Success is that it doesn't do anything
+        grole.main(['-p', '80', '-q'])
+
+    def test_launch3(self):
+        # Success is that it doesn't do anything
+        grole.main(['-p', '80', '-v'])
diff --git a/test/test_serve.py b/test/test_serve.py
index 1e6955c..45a725e 100644
--- a/test/test_serve.py
+++ b/test/test_serve.py
@@ -36,4 +36,11 @@ class TestServe(unittest.TestCase):
                self.assertEqual(html, b'foo\n')
         p.terminate()
 
+    def test_https(self):
+        p = multiprocessing.Process(target=simple_server)
+        p.start()
+        time.sleep(0.1)
+        self.assertRaises(urllib.error.URLError)
+        p.terminate()
+
 

pip install -e .
parse_log_pytest
pytest
coverage coveralls pytest
3.6
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
attrs @ file:///opt/conda/conda-bld/attrs_1642510447205/work
certifi==2021.5.30
charset-normalizer==2.0.12
coverage==6.2
coveralls==3.3.1
docopt==0.6.2
-e git+https://github.com/witchard/grole.git@d47d0ec83f7d76912d5b6b00fd130d79f892939c#egg=grole
idna==3.10
importlib-metadata @ file:///tmp/build/80754af9/importlib-metadata_1631916693255/work
iniconfig @ file:///home/linux1/recipes/ci/iniconfig_1610983019677/work
more-itertools @ file:///tmp/build/80754af9/more-itertools_1637733554872/work
packaging @ file:///tmp/build/80754af9/packaging_1637314298585/work
pluggy @ file:///tmp/build/80754af9/pluggy_1615976315926/work
py @ file:///opt/conda/conda-bld/py_1644396412707/work
pyparsing @ file:///tmp/build/80754af9/pyparsing_1635766073266/work
pytest==6.2.4
requests==2.27.1
toml @ file:///tmp/build/80754af9/toml_1616166611790/work
typing_extensions @ file:///opt/conda/conda-bld/typing_extensions_1647553014482/work
urllib3==1.26.20
zipp @ file:///tmp/build/80754af9/zipp_1633618647012/work

name: grole
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - attrs=21.4.0=pyhd3eb1b0_0
  - ca-certificates=2025.2.25=h06a4308_0
  - certifi=2021.5.30=py36h06a4308_0
  - importlib-metadata=4.8.1=py36h06a4308_0
  - importlib_metadata=4.8.1=hd3eb1b0_0
  - iniconfig=1.1.1=pyhd3eb1b0_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.3=he6710b0_2
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - more-itertools=8.12.0=pyhd3eb1b0_0
  - ncurses=6.4=h6a678d5_0
  - openssl=1.1.1w=h7f8727e_0
  - packaging=21.3=pyhd3eb1b0_0
  - pip=21.2.2=py36h06a4308_0
  - pluggy=0.13.1=py36h06a4308_0
  - py=1.11.0=pyhd3eb1b0_0
  - pyparsing=3.0.4=pyhd3eb1b0_0
  - pytest=6.2.4=py36h06a4308_2
  - python=3.6.13=h12debd9_1
  - readline=8.2=h5eee18b_0
  - setuptools=58.0.4=py36h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - toml=0.10.2=pyhd3eb1b0_0
  - typing_extensions=4.1.1=pyh06a4308_0
  - wheel=0.37.1=pyhd3eb1b0_0
  - xz=5.6.4=h5eee18b_1
  - zipp=3.6.0=pyhd3eb1b0_0
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - charset-normalizer==2.0.12
      - coverage==6.2
      - coveralls==3.3.1
      - docopt==0.6.2
      - idna==3.10
      - requests==2.27.1
      - urllib3==1.26.20
prefix: /opt/conda/envs/grole

test/test_grole.py::TestGrole::test_close_big_error test/test_main.py::TestMain::test_launch test/test_main.py::TestMain::test_launch2 test/test_main.py::TestMain::test_launch3
test/test_grole.py::TestGrole::test_404 test/test_grole.py::TestGrole::test_async test/test_grole.py::TestGrole::test_error test/test_grole.py::TestGrole::test_hello test/test_grole.py::TestStatic::test_file test/test_grole.py::TestStatic::test_index test/test_grole.py::TestStatic::test_index2 test/test_grole.py::TestStatic::test_notfound test/test_grole.py::TestDoc::test_doc test/test_serve.py::TestServe::test_fileserver test/test_serve.py::TestServe::test_https test/test_serve.py::TestServe::test_simple
grole.py
grole.py