Skip to content

Commit

Permalink
Merge c81882e into eb00fa4
Browse files Browse the repository at this point in the history
  • Loading branch information
joamag committed Aug 27, 2021
2 parents eb00fa4 + c81882e commit 898318d
Show file tree
Hide file tree
Showing 30 changed files with 1,257 additions and 125 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ jobs:
name: Build
strategy:
matrix:
python-version: [2.7, 3.5, 3.6, 3.7, rc]
python-version: [2.7, 3.4, 3.5, 3.6, 3.7, 3.8, 3.9, rc]
runs-on: ubuntu-latest
container: python:${{ matrix.python-version }}
steps:
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ session.shelve*
net.ca
fs.data

/.vscode/settings.json

/dist
/build
/src/netius.egg-info
Expand Down
84 changes: 84 additions & 0 deletions examples/echo/echos_tcp.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-

# Hive Netius System
# Copyright (c) 2008-2018 Hive Solutions Lda.
#
# This file is part of Hive Netius System.
#
# Hive Netius System is free software: you can redistribute it and/or modify
# it under the terms of the Apache License as published by the Apache
# Foundation, either version 2.0 of the License, or (at your option) any
# later version.
#
# Hive Netius System is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# Apache License for more details.
#
# You should have received a copy of the Apache License along with
# Hive Netius System. If not, see <http://www.apache.org/licenses/>.

__author__ = "João Magalhães <joamag@hive.pt>"
""" The author(s) of the module """

__version__ = "1.0.0"
""" The version of the module """

__revision__ = "$LastChangedRevision$"
""" The revision number of the module """

__date__ = "$LastChangedDate$"
""" The last change date of the module """

__copyright__ = "Copyright (c) 2008-2018 Hive Solutions Lda."
""" The copyright for the module """

__license__ = "Apache License, Version 2.0"
""" The license for the module """

import netius

import asyncio

class EchoServerClientProtocol(asyncio.Protocol):
"""
Simple protocol implementation for an echo protocol
that writes back the received message through the
response pipeline. This implementation is inspired by
the Python asyncio documentation example.
:see: https://docs.python.org/3.6/library/asyncio-protocol.html#protocol-examples
"""

def connection_made(self, transport):
peername = transport.get_extra_info("peername")
print("Connection from %s" % str(peername))
self.transport = transport

def data_received(self, data):
message = data.decode()
print("Data received: %s" % message)

print("Sending: %s" % message)
self.transport.write(data)

print("Closing the client socket")
self.transport.close()

loop = netius.get_loop(_compat = True)

coro = loop.create_server(
lambda: EchoServerClientProtocol(),
"127.0.0.1", 8888
)
server = loop.run_until_complete(coro)

print("Serving on %s" % (server.sockets[0].getsockname(),))

try: loop.run_forever()
except KeyboardInterrupt: pass

server.close()
loop.run_until_complete(server.wait_closed())
loop.close()
2 changes: 1 addition & 1 deletion examples/echo/echos_udp.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ def datagram_received(self, data, addr):

loop = netius.get_loop(_compat = True)
listen = loop.create_datagram_endpoint(
EchoServerProtocol,
lambda: EchoServerProtocol(),
local_addr = ("127.0.0.1", 9999)
)
transport, protocol = loop.run_until_complete(listen)
Expand Down
78 changes: 78 additions & 0 deletions examples/echo/hello_http.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-

# Hive Netius System
# Copyright (c) 2008-2018 Hive Solutions Lda.
#
# This file is part of Hive Netius System.
#
# Hive Netius System is free software: you can redistribute it and/or modify
# it under the terms of the Apache License as published by the Apache
# Foundation, either version 2.0 of the License, or (at your option) any
# later version.
#
# Hive Netius System is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# Apache License for more details.
#
# You should have received a copy of the Apache License along with
# Hive Netius System. If not, see <http://www.apache.org/licenses/>.

__author__ = "João Magalhães <joamag@hive.pt>"
""" The author(s) of the module """

__version__ = "1.0.0"
""" The version of the module """

__revision__ = "$LastChangedRevision$"
""" The revision number of the module """

__date__ = "$LastChangedDate$"
""" The last change date of the module """

__copyright__ = "Copyright (c) 2008-2018 Hive Solutions Lda."
""" The copyright for the module """

__license__ = "Apache License, Version 2.0"
""" The license for the module """

import netius

import asyncio

class HelloHTTPServerProtocol(asyncio.Protocol):

def __init__(self, keep_alive = True):
super().__init__()
self.keep_alive = keep_alive

def connection_made(self, transport):
self.transport = transport
self.peername = transport.get_extra_info("peername")
print("Connection from %s" % str(self.peername))

def connection_lost(self, exc):
print("Connection from %s lost (%s)" % (str(self.peername), str(exc)))

def data_received(self, data):
keep_alive_s = b"keep-alive" if self.keep_alive else b"close"
self.transport.write(b"HTTP/1.1 200 OK\r\nConnection: %s\r\nContent-Type: text/plain\r\nContent-Length: 13\r\n\r\nHello, world!" % keep_alive_s)
if not self.keep_alive: self.transport.close()

loop = netius.get_loop(_compat = True)

coro = loop.create_server(
lambda: HelloHTTPServerProtocol(),
"127.0.0.1", 8888
)
server = loop.run_until_complete(coro)

print("Serving on %s" % (server.sockets[0].getsockname(),))

try: loop.run_forever()
except KeyboardInterrupt: pass

server.close()
loop.run_until_complete(server.wait_closed())
loop.close()
7 changes: 4 additions & 3 deletions examples/http/http_asyncio.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,12 @@ def print_http_headers(url, encoding = "utf-8"):
url = urllib.parse.urlsplit(url)

if url.scheme == "https":
connect = asyncio.open_connection(url.hostname, 443, ssl = True)
connect = asyncio.open_connection(url.hostname, url.port or 443, ssl = True)
else:
connect = asyncio.open_connection(url.hostname, 80)
connect = asyncio.open_connection(url.hostname, url.port or 80)

reader, writer = yield from connect
query = "HEAD {path} HTTP/1.0\r\n" + "Host: {hostname}\r\n" + "\r\n"
query = "HEAD {path} HTTP/1.1\r\nConnection: keep-alive\r\nHost: {hostname}\r\n\r\n"
query = query.format(path = url.path or "/", hostname = url.hostname)
writer.write(query.encode(encoding))

Expand All @@ -62,6 +62,7 @@ def print_http_headers(url, encoding = "utf-8"):
if not line: break
line = line.decode(encoding).rstrip()
if line: print(line)
else: break

writer.close()

Expand Down
2 changes: 2 additions & 0 deletions pylintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[messages control]
disable=C0103,C0111,C0121,C0123,C0301,C0302,C0321,C0325,C0326,C0330,C0412,C0413,E1128,W0102,W0106,W0108,W0150,W0201,W0212,W0221,W0401,W0603,W0613,W0621,W0622,W0702,W1113
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ def read_file(path):
"netius.mock",
"netius.pool",
"netius.servers",
"netius.servers.runners",
"netius.sh",
"netius.test"
],
Expand Down
8 changes: 5 additions & 3 deletions src/netius/base/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
from . import protocol
from . import request
from . import server
from . import service
from . import stream
from . import tls
from . import transport
Expand All @@ -66,8 +67,8 @@
SSL_DH_PATH, Base, BaseThread, new_loop_main, new_loop_asyncio, new_loop,\
ensure_main, ensure_asyncio, ensure_loop, get_main, get_loop, get_event_loop,\
stop_loop, compat_loop, get_poll, build_future, ensure, ensure_pool
from .compat import BaseLoop, CompatLoop, is_compat, is_asyncio, build_datagram,\
connect_stream
from .compat import BaseLoop, CompatLoop, is_compat, is_asyncio, run, build_datagram,\
connect_stream, serve_stream
from .config import conf, conf_prefix, conf_suffix, conf_s, conf_r, conf_d, conf_ctx
from .conn import OPEN, CLOSED, PENDING, CHUNK_SIZE, Connection
from .container import Container, ContainerServer
Expand All @@ -79,8 +80,9 @@
from .protocol import Protocol, DatagramProtocol, StreamProtocol
from .request import Request, Response
from .server import Server, DatagramServer, StreamServer
from .service import Service
from .stream import Stream
from .tls import fingerprint, match_fingerprint, match_hostname, dnsname_match,\
dump_certificate
from .transport import Transport, TransportDatagram, TransportStream
from .transport import Transport, TransportDatagram, TransportStream, ServerTransport
from .util import camel_to_underscore, verify
24 changes: 21 additions & 3 deletions src/netius/base/async_neo.py
Original file line number Diff line number Diff line change
Expand Up @@ -258,22 +258,40 @@ def wait(*args, **kwargs):
def coroutine_return(coroutine):
"""
Allows for the abstraction of the return value of a coroutine
object to be the result of the future yield as the first element
object to be the result of the future yielded as the last element
of the generator.
This allows the possibility to provide compatibility with the legacy
not return allowed generators.
In case no value is yielded then an invalid value is returned as the
result of the async coroutine.
:type coroutine: CoroutineObject
:param coroutine: The coroutine object that is going to be yield back
:param coroutine: The coroutine object that is going to yield back
and have its last future result returned from the generator.
"""

generator = _coroutine_return(coroutine)
return AwaitWrapper(generator)

def _coroutine_return(coroutine):
# unsets the initial future reference value, this
# way it's possible to avoid future based return
# statement in case the coroutine is "empty"
future = None

# iterates over the coroutine generator yield
# the values (should be future instances) and
# ignoring possible invalid values, the last
# value yielded is considered to be future
# that is going to be set as the return value
for value in coroutine:
if value == None: continue
yield value
future = value
return future.result()

# in case at least one valid future was yielded
# then set the result of such future as the result
# of the current coroutine (return statement)
return future.result() if future else None
25 changes: 22 additions & 3 deletions src/netius/base/async_old.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ class Future(object):
:see: https://en.wikipedia.org/wiki/Futures_and_promises
"""

_asyncio_future_blocking = True

def __init__(self, loop = None):
self.status = 0
self._loop = loop
Expand Down Expand Up @@ -117,6 +119,18 @@ def add_ready_callback(self, function):
def add_closed_callback(self, function):
self.closed_callbacks.append(function)

def remove_done_callback(self, function):
self.done_callbacks.remove(function)

def remove_partial_callback(self, function):
self.partial_callbacks.remove(function)

def remove_ready_callback(self, function):
self.ready_callbacks.remove(function)

def remove_closed_callback(self, function):
self.closed_callbacks.remove(function)

def approve(self, cleanup = True):
self.set_result(None, cleanup = cleanup)

Expand Down Expand Up @@ -336,15 +350,20 @@ def notify(event, data = None):
def coroutine_return(coroutine):
"""
Allows for the abstraction of the return value of a coroutine
object to be the result of the future yield as the first element
object to be the result of the future yielded as the last element
of the generator.
This allows the possibility to provide compatibility with the legacy
not return allowed generators.
In case no value is yielded then an invalid value is returned as the
result of the async coroutine.
:type coroutine: CoroutineObject
:param coroutine: The coroutine object that is going to be yield back
:param coroutine: The coroutine object that is going to yield back
and have its last future result returned from the generator.
"""

for value in coroutine: yield value
for value in coroutine:
if value == None: continue
yield value
4 changes: 2 additions & 2 deletions src/netius/base/asynchronous.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,10 @@
# imports the base (old) version of the async implementation
# that should be compatible with all the available python
# interpreters, base collection of async library
from .async_old import * #@UnusedWildImport
from .async_old import * #@UnusedWildImport pylint: disable=W0614

# verifies if the current python interpreter version supports
# the new version of the async implementation and if that's the
# case runs the additional import of symbols, this should override
# most of the symbols that have just been created
if is_neo(): from .async_neo import * #@UnusedWildImport
if is_neo(): from .async_neo import * #@UnusedWildImport pylint: disable=W0614
8 changes: 4 additions & 4 deletions src/netius/base/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -317,9 +317,9 @@ def ensure_socket(self):
# as nothing else remain to be done in the current method
if self.socket: return

# prints a small debug message about the udp socket that is going
# prints a small debug message about the UDP socket that is going
# to be created for the client's connection
self.debug("Creating clients's udp socket ...")
self.debug("Creating clients's UDP socket ...")

# creates the socket that it's going to be used for the listening
# of new connections (client socket) and sets it as non blocking
Expand Down Expand Up @@ -689,13 +689,13 @@ def connect(
# ensures that the proper socket family is defined in case the
# requested host value is unix socket oriented, this step greatly
# simplifies the process of created unix socket based clients
family = socket.AF_UNIX if host == "unix" else family
family = socket.AF_UNIX if host == "unix" else family #@UndefinedVariable pylint: disable=E1101

# verifies the kind of socket that is going to be used for the
# connect operation that is going to be performed, note that the
# unix type should be used with case as it does not exist in every
# operative system and may raised an undefined exceptions
is_unix = hasattr(socket, "AF_UNIX") and family == socket.AF_UNIX
is_unix = hasattr(socket, "AF_UNIX") and family == socket.AF_UNIX #@UndefinedVariable pylint: disable=E1101
is_inet = family in (socket.AF_INET, socket.AF_INET6)

# runs a series of default operation for the SSL related attributes
Expand Down

0 comments on commit 898318d

Please sign in to comment.