Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Connection refused when setting port to zero and inside a docker container #276

Open
KGHVerilator opened this issue Jun 22, 2021 · 9 comments · May be fixed by #381
Open

Connection refused when setting port to zero and inside a docker container #276

KGHVerilator opened this issue Jun 22, 2021 · 9 comments · May be fixed by #381
Assignees
Milestone

Comments

@KGHVerilator
Copy link

KGHVerilator commented Jun 22, 2021

I am trying to develop a test email server that should run in a docker container. The docker container is run with --network=host -P; though for unit tests I would like to use --network=none.

I am deliberately specifying that port 0 should be used (let the OS pick an available port). I then have a function to get the port that was actually used. The server code is

#! /usr/bin/env python3

import asyncio #pylint: disable=unused-import
from aiosmtpd.controller import Controller

class DemoEmailHandler:
    async def handle_DATA(self, server, session, envelope):
        message = {
            'peer' : session.peer,
            'mailfrom' : envelope.mail_from,
            'to': envelope.rcpt_tos,
            'data' : envelope.content}
        print(message)
        return '250 Message accepted for delivery'


class DemoEmailServer:
    def __init__(self):
        self._handler = DemoEmailHandler()
        self._server = Controller(
            handler=self._handler,
            hostname="0.0.0.0",
            port=0
        )
        self._server.start()
        self._port = self._server.server.sockets[0].getsockname()[1]

    def stop(self):
        """Tell the server to stop"""
        self._server.stop()

    def getPort(self) -> int:
        """Return the port the SMTP (email) server is actually on"""
        return self._port

SERVER = DemoEmailServer()
print(SERVER.getPort())
SERVER.stop()

I keep getting connection refused

Traceback (most recent call last):
  File "./demo_email_server.py", line 36, in <module>
    SERVER = DemoEmailServer()
  File "./demo_email_server.py", line 25, in __init__
    self._server.start()
  File "/usr/local/lib/python3.8/dist-packages/aiosmtpd-1.4.2-py3.8.egg/aiosmtpd/controller.py", line 223, in start
    self._trigger_server()
  File "/usr/local/lib/python3.8/dist-packages/aiosmtpd-1.4.2-py3.8.egg/aiosmtpd/controller.py", line 313, in _trigger_server
    s = stk.enter_context(create_connection((hostname, self.port), 1.0))
  File "/usr/lib/python3.8/socket.py", line 808, in create_connection
    raise err
  File "/usr/lib/python3.8/socket.py", line 796, in create_connection
    sock.connect(sa)
ConnectionRefusedError: [Errno 111] Connection refused

when running inside a docker container. If I run on the host then the server starts up fine.

(My host and container are both Ubuntu 20.04, python 3.8.5, the container has aiosmtpd 1.4.2 installed from source).

@pepoluan
Copy link
Collaborator

pepoluan commented Oct 19, 2021

Ah, that's a new use case for me (setting port to 0)

Currently the code for Controller tries to connect to the specified port to trigger the server to activate once, in order to ensure that SMTP initializes correctly. It is a simple mechanism and currently does not consider the special port=0 case.

I'll see if I can add more intelligence to _trigger_server()

@pepoluan pepoluan self-assigned this Oct 19, 2021
@pepoluan pepoluan added this to the 1.5 milestone Oct 19, 2021
@KGHVerilator
Copy link
Author

KGHVerilator commented Oct 19, 2021 via email

@waynew
Copy link
Collaborator

waynew commented Oct 20, 2021

FWIW you can check out how I create an ssl server https://github.com/waynew/orouboros/blob/89905978a423e130e9ea2c57690b2e4a6cc48f3d/orouboros.py#L313

@diazona
Copy link

diazona commented Apr 6, 2022

I've also run into this problem while trying to prepare pytest-dev/pytest-localserver for the removal of smtpd. I wanted to bump this issue to bring it some attention.

I'll probably wind up regretting saying this 😛 but it seems like a simple fix, just replace create_connection((hostname, self.port), 1.0) with create_connection(self.server_coro.sockets[0].getsockname(), 1.0), or something like that. Would you be open to accepting a pull request for that change @pepoluan? I'd be happy to prepare one, or at least put in a chunk of time to try to figure out a solution.

@diazona
Copy link

diazona commented Apr 6, 2022

...and almost immediately I figured out why it's not that simple, with the mixing of synchronous and asynchronous code 😆 I knew I shouldn't have said anything. But I would still be quite willing to put some time into this, if it'd help.

@waynew
Copy link
Collaborator

waynew commented Jun 9, 2022

Okay, so.... I know it's been a very long time since I've touched this, but - yeah, the fix is pretty straightforward, I think.

First off, yeah I was able to reproduce this.

Second off, I think the correct thing for us to do here is in the _create_server function where we do self.loop.create_server we could change it to:

server = self.loop.create_server(...)
if not self.port:
    self.port = server.sockets[0].getsockname()[1]
return server

By making that change locally, it all worked 👍 We'll want some test cases for that obviously, but it shouldn't be too hard to do!

@waynew
Copy link
Collaborator

waynew commented Jun 9, 2022

Well, heck! The fix was pretty easy, though I did have to move the change to _trigger_server.

But I'm struggling with the test cases -- I think we actually have some tests that are failing on some more modern Pythons anyway 🤔

@KGHVerilator
Copy link
Author

Hopefully this will work out; thank you for look into it.

@alext alext linked a pull request Jul 10, 2023 that will close this issue
6 tasks
@alext
Copy link

alext commented Jul 10, 2023

@waynew I was also hitting this, and I found the fix you describe worked for me as well, so I've raised it as #381.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants