from errno import ECONNRESET, ENOTCONN, ESHUTDOWN, ECONNABORTED, EPIPE, EBADF, \
     EWOULDBLOCK 
from urllib.parse import urlparse

import select
import socket

_DISCONNECTED = frozenset({ECONNRESET, ENOTCONN, ESHUTDOWN, ECONNABORTED, EPIPE,
                           EBADF})

try:
    channels
except NameError:
    channels = {}

CRLF = "\r\n\r\n"

class SocketChannel(object):

    def __init__(self, host, port):
        self.__sock = None

        self.__host = host
        self.__port = port

        self.__channels = channels

    def create(self):
        self.__sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    def open(self):
        try:
            self.__sock.connect((self.__host, self.__port))
            self.__channels[self.__sock.fileno()] = self
        except Exception as err:
            self.handle_error(err)

    def close(self):
        del self.__channels[self.__sock.fileno()]
        self.__sock.close()

    def read(self, nbytes):
        try:
            data = self.__sock.recv(nbytes)
            if data == b'':
                self.handle_close()
            return data
        except OSError as err:
            if err.errno in _DISCONNECTED:
                self.handle_close()
                return b''
        except Exception as exc:
            self.handle_error(exc)

    def write(self, data):
        try:
            result = self.__sock.send(data)
            return result
        except OSError as err:
            if err.errno == EWOULDBLOCK:
                return 0
            elif err.errno in _DISCONNECTED:
                self.handle_close()
                return 0
        except Exception as exc:
            self.handle_error(exc)

    def readable(self):
        return True

    def writable(self):
        return True

    def handle_read(self):
        pass

    def handle_write(self):
        pass

    def handle_close(self):
        self.close()

    def handle_error(self, err):
        pass


class HttpClient(SocketChannel):

    def __init__(self, host, port):
        SocketChannel.__init__(self, host, port)       
        self.create()
        self.__buffer = b''

    def send_request(self, path, method="GET", headers={}, body=None):
        request_data = self.__prepare_request(method, path, headers, body)
        self.__buffer = bytes(request_data, "utf8")
        self.open()

    def readable(self):
        return True

    def writable(self):
        return len(self.__buffer) > 0

    def handle_read(self):
        print("Read data: {0}".format(self.read(2048)))

    def handle_write(self):
        nbytes = self.write(self.__buffer)
        self.__buffer = self.__buffer[nbytes:]

    def handle_close(self):
        print("Connection closed")
        self.close()

    def handle_error(self, err):
        print("Handling err: {0}".format(err))

    def __get_host_and_port(self, url_parts):
        host, port = url_parts.netloc.split(":")
        if port == "":
            return (host, 80)
        else:
            return (host, int(port))

    def __prepare_request(self, method, path, headers, body):
        message = "{0} {1} HTTP/1.0 {2}".format(method, path, CRLF)
        for key, value in headers.items():
            if type(value) is list:
                value = ",".join(value)
            message += "{}: {} {}".format(key, value, CRLF)
        if body is not None:
            message += body + CRLF
        return message


def loop(wait=0.1):
    
    while True:
        readers = [fd for fd in channels.keys() if channels[fd].readable()]
        writers = [fd for fd in channels.keys() if channels[fd].writable()]

        r, w, _ = select.select(readers, writers, channels.keys(), wait)

        for fd in r:
            channels[fd].handle_read()
        for fd in w:
            channels[fd].handle_write()


headers = {
    "Connection": "close",
    "Host": "127.0.0.1:5000",
    "User-Agent": "TUES User Agent",
    "Accept": "text/html"
}

client = HttpClient("127.0.0.1", 5000)
client.send_request("/download", headers=headers)

loop()

from errno import ECONNRESET, ENOTCONN, ESHUTDOWN, ECONNABORTED, EPIPE, EBADF, \
     EWOULDBLOCK 
from urllib.parse import urlparse

import select
import socket

_DISCONNECTED = frozenset({ECONNRESET, ENOTCONN, ESHUTDOWN, ECONNABORTED, EPIPE,
                           EBADF})

try:
    channels
except NameError:
    channels = {}

CRLF = "\r\n\r\n"

class SocketChannel(object):

    def __init__(self, host, port):
        self.__sock = None

        self.__host = host
        self.__port = port

        self.__channels = channels

    def create(self):
        self.__sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    def open(self):
        try:
            self.__sock.connect((self.__host, self.__port))
            self.__channels[self.__sock.fileno()] = self
        except Exception as err:
            self.handle_error(err)

    def close(self):
        del self.__channels[self.__sock.fileno()]
        self.__sock.close()

    def read(self, nbytes):
        try:
            data = self.__sock.recv(nbytes)
            if data == b'':
                self.handle_close()
            return data
        except OSError as err:
            if err.errno in _DISCONNECTED:
                self.handle_close()
                return b''
        except Exception as exc:
            self.handle_error(exc)

    def write(self, data):
        try:
            result = self.__sock.send(data)
            return result
        except OSError as err:
            if err.errno == EWOULDBLOCK:
                return 0
            elif err.errno in _DISCONNECTED:
                self.handle_close()
                return 0
        except Exception as exc:
            self.handle_error(exc)

    def readable(self):
        return True

    def writable(self):
        return True

    def handle_read(self):
        pass

    def handle_write(self):
        pass

    def handle_close(self):
        self.close()

    def handle_error(self, err):
        pass


class HttpClient(SocketChannel):

    def __init__(self, host, port):
        SocketChannel.__init__(self, host, port)       
        self.create()
        self.__buffer = b''

    def send_request(self, path, method="GET", headers={}, body=None):
        request_data = self.__prepare_request(method, path, headers, body)
        self.__buffer = bytes(request_data, "utf8")
        self.open()

    def readable(self):
        return True

    def writable(self):
        return len(self.__buffer) > 0

    def handle_read(self):
        print("Read data: {0}".format(self.read(2048)))

    def handle_write(self):
        nbytes = self.write(self.__buffer)
        self.__buffer = self.__buffer[nbytes:]

    def handle_close(self):
        print("Connection closed")
        self.close()

    def handle_error(self, err):
        print("Handling err: {0}".format(err))

    def __get_host_and_port(self, url_parts):
        host, port = url_parts.netloc.split(":")
        if port == "":
            return (host, 80)
        else:
            return (host, int(port))

    def __prepare_request(self, method, path, headers, body):
        message = "{0} {1} HTTP/1.0 {2}".format(method, path, CRLF)
        for key, value in headers.items():
            if type(value) is list:
                value = ",".join(value)
            message += "{}: {} {}".format(key, value, CRLF)
        if body is not None:
            message += body + CRLF
        return message


def loop(wait=0.1):
    
    while True:
        readers = [fd for fd in channels.keys() if channels[fd].readable()]
        writers = [fd for fd in channels.keys() if channels[fd].writable()]

        r, w, _ = select.select(readers, writers, channels.keys(), wait)

        for fd in r:
            channels[fd].handle_read()
        for fd in w:
            channels[fd].handle_write()


headers = {
    "Connection": "close",
    "Host": "127.0.0.1:5000",
    "User-Agent": "TUES User Agent",
    "Accept": "text/html"
}

client = HttpClient("127.0.0.1", 5000)
client.send_request("/download", headers=headers)

loop()
