HiveBrain v1.2.0
Get Started
← Back to all entries
patternpythonMinor

Subclass of socket with optional TLS

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
withtlssubclassoptionalsocket

Problem

import ssl
from socket import socket

class testsock(socket):
    def __init__(self, tls=True):
        super(testsock, self).__init__()
        self.connect(('192.168.0.1', 443))
        if tls:
            self = ssl.wrap_socket(self) # Ugly "handover"

testsock(tls=True)


This code works, it's the second ugliest bodge I've done as of today.

But it works with the behavior I desire, that .send() and .close() etc are "intact".

However, if I ever try to add any other values to my custom class testsock those will effectively be gone, since I replace self with whatever wrap_socket spits out.

I could use something that looks like this:

class testsock():
    def __init__(self, tls=True):
        self.s = socket()
        ...
        self.s = ssl.wrap_socket(self.s)

    def send(self, data):
        self.s.send(data)


But that goes against the elegance of inheriting the socket object and not having to create all the functions you'd expect out of a ordinary socket. (I know, lazy programming, but it looks cleaner without all the definitions).

I could probably iterate over all the .__dict__ or inspect.getmembers() functions and replace them with the matching available items from wrap_socket, but that would also cause for concerns later on, but it would look something like:

for name, obj in inspect.getmembers(self):
    print(name, obj)


It would yield something like:

close >


I'm not sure how I'd get socket from this, obj.__class__.__name__ doesn't yield this. If I could some how get socket from the object I would know that it's a socket() bound method, and replace the corresponding method from wrap_socket() return.

I've heard of metaclass programming, and I'm having a go at it.

But my question is, out of all the options - Which would be the best according to the community?

Would it be possible to inline-replace the inherited socket object with whatever ssl.wrap_socket returns?

Solution

You don't need a class here, a simple function will do:

import ssl
from socket import socket

def testsock(tls=True):
    sock = socket()
    sock.connect(('192.168.0.1', 443))
    if tls:
        sock = ssl.wrap_socket(sock)
    return sock

testsock(tls=True)


Returned objects are either plain sockets or ssl sockets but they share some common behaviour (like send or recv). If you need to add attributes, you can do that before the return statement.

In case you wish to be able to extend it better, you can still use the socket as an attribute on a custom class and expose the socket API through __getattr__:

class TestSock(object):
    def __init__(self, tls=True):
        sock = socket()
        sock.connect(('192.168.0.1', 443))
        if tls:
            sock = ssl.wrap_socket(sock)
        self.socket = sock
        # other attributes

    def __getattr__(self, attribute_name):
        """Defer unknown behaviour to the socket"""
        return getattr(self.socket, attribute_name)

    # other methods


But you don't need it for now.

Code Snippets

import ssl
from socket import socket

def testsock(tls=True):
    sock = socket()
    sock.connect(('192.168.0.1', 443))
    if tls:
        sock = ssl.wrap_socket(sock)
    return sock

testsock(tls=True)
class TestSock(object):
    def __init__(self, tls=True):
        sock = socket()
        sock.connect(('192.168.0.1', 443))
        if tls:
            sock = ssl.wrap_socket(sock)
        self.socket = sock
        # other attributes

    def __getattr__(self, attribute_name):
        """Defer unknown behaviour to the socket"""
        return getattr(self.socket, attribute_name)

    # other methods

Context

StackExchange Code Review Q#153035, answer score: 4

Revisions (0)

No revisions yet.