Skip to content

extmod/asyncio/stream.py: Add ipv6 support to start_server(). #17311

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

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

andrewleech
Copy link
Contributor

Summary

I'm starting to use ipv6 more with micropython devices as their discoverability can actually be much better on a local network, without worrying about needing to manually configure (ipv4) ip addresses.

To that end, this PR adds support to start_server() to automatically use the parsed protocol, ie setting socket.AF_INET or socket.AF_INET6 on the socket as parsed by getaddrinfo()

When adding unit tests for this change, I found the multi-test runner was missing support for ipv6 ip address detection, so that's been added as well (in a separate commit)

The ip address detection in multi-test runner has also been updated to use the newer / preferred network.ipconfig("addr4") style interface by default, falling back to ipconfig if needed.

Testing

Unit tests are included which check ipv6 support in both net_hosted style test and mult_net tests.

Without the change to extmod/asyncio/stream.py the tests fails with OSError: 97

Trade-offs and Alternatives

Copy link

codecov bot commented May 16, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 98.38%. Comparing base (2dada06) to head (6684282).
⚠️ Report is 311 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master   #17311      +/-   ##
==========================================
- Coverage   98.54%   98.38%   -0.16%     
==========================================
  Files         169      171       +2     
  Lines       21898    22257     +359     
==========================================
+ Hits        21579    21898     +319     
- Misses        319      359      +40     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@andrewleech andrewleech force-pushed the asyncio_server_ipv6 branch from 3443947 to 56e8e68 Compare May 16, 2025 01:26
Copy link

github-actions bot commented May 16, 2025

Code size report:

   bare-arm:    +0 +0.000% 
minimal x86:    +0 +0.000% 
   unix x64:   +56 +0.007% standard
      stm32:   +24 +0.006% PYBV10
     mimxrt:   +24 +0.006% TEENSY40
        rp2:   +16 +0.002% RPI_PICO_W
       samd:   +24 +0.009% ADAFRUIT_ITSYBITSY_M4_EXPRESS
  qemu rv32:    +0 +0.000% VIRT_RV32

@dpgeorge dpgeorge added the extmod Relates to extmod/ directory in source label May 20, 2025
@dpgeorge dpgeorge added this to the release-1.26.0 milestone May 20, 2025
@andrewleech andrewleech force-pushed the asyncio_server_ipv6 branch 2 times, most recently from 6227c48 to 3b17ecc Compare May 27, 2025 05:57
@@ -180,11 +180,11 @@ async def start_server(cb, host, port, backlog=5, ssl=None):
import socket

# Create and bind server socket.
host = socket.getaddrinfo(host, port)[0] # TODO this is blocking!
s = socket.socket()
addr_info = socket.getaddrinfo(host, port)[0] # TODO this is blocking!
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be possible to add the family argument to start_server() and pass it through to getaddrinfo() here. Then you could force it to use IPv6 (or IPv4). But maybe that's a separate PR?


try:
# Check if IPv6 is supported
socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This detection won't work. All ports based on bare-metal lwIP will let this pass even if they don't have IPv6 enabled.

I don't know if it's possible to detect IPv6??

print("SKIP")
raise SystemExit

PORT = 8001 # Different from other tests to avoid conflicts
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this needs to use a different port, 8000 should be fine.


async def handle_connection(reader, writer):
# Test that peername exists
peer = writer.get_extra_info("peername")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The peer variable is unused. I don't think you need to call this function at all.


data = await reader.read(100)
print("read:", data)
assert data == message, "Data mismatch"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't need this assert, the .exp will test the data is correct.


try:
# Check if IPv6 is supported
socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As above, this detection won't work.

try:
# Connect with IPv6 client
print("connect to ipv6 server")
reader, writer = await asyncio.open_connection("::", PORT)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For this to work requires loopback mode in the TCP/IP driver. That's fine on unix, but it'll fail on most bare-metal ports.

# Read response
data = await reader.read(100)
print("read:", data)
assert data == test_msg, "Data mismatch"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assert not needed.

@@ -79,19 +79,59 @@ def globals(**gs):
print("SET {{}} = {{!r}}".format(g, gs[g]))
multitest.flush()
@staticmethod
def get_network_ip():
def _get_ip_from_ifconfig(_nic, ipv6=False):
# Helper to get IP address from an interface object using appropriate format
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment doesn't add much, and I'm confused what "appropriate format" means?

pass

# Find active network interface
try:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code below is very hard to understand. Why so many try/except's?

Ensures that the underlying socket is opened with the correct protocol
as parsed by getaddrinfo().

Signed-off-by: Andrew Leech <andrew.leech@planetinnovation.com.au>
@andrewleech andrewleech force-pushed the asyncio_server_ipv6 branch from 3b17ecc to 6684282 Compare July 30, 2025 23:41
@andrewleech
Copy link
Contributor Author

The change to the multi test runner and new (duplicated) ipv6 tests have been removed from this PR to simplify it.

Updates to support testing ipv6 have been added in a different way in #17793

Copy link
Member

@dpgeorge dpgeorge left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for updating, looks good now.

@dpgeorge
Copy link
Member

Actually, on second thoughts, I'm wondering if the family argument to start_server() is needed at all? It looks like that's not used in the tests in #17793, so maybe it's a useless (or rarely used) argument?

Adding that argument does add a fair amount to the code size. Whereas just passing through the family from getaddrinfo to the socket costs almost nothing.

So, if you want to get this in sooner rather than later, I suggest reverting the family argument addition.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
extmod Relates to extmod/ directory in source
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy