コード例 #1
0
    def do_GET(self, headers_only=False):
        '''
        GET request handler
        '''
        logger = logging.getLogger('do_GET')
        self.reqtime = time.time()
        self.connected = True
        # Set HTTP protocol version
        if self.request_version == 'HTTP/1.1':
            self.protocol_version = 'HTTP/1.1'
        # Don't wait videodestroydelay if error happened
        self.errorhappened = True
        # Connected client IP address
        self.clientip = self.headers['X-Forwarded-For'] \
            if self.headers.has_key('X-Forwarded-For') else self.request.getpeername()[0]

        if AceConfig.firewall:
            # If firewall enabled
            self.clientinrange = any(map(lambda i: ipaddr.IPAddress(self.clientip) \
                                in ipaddr.IPNetwork(i), AceConfig.firewallnetranges))

            if (AceConfig.firewallblacklistmode and self.clientinrange) or \
                (not AceConfig.firewallblacklistmode and not self.clientinrange):
                logger.info('Dropping connection from ' + self.clientip + ' due to ' + \
                            'firewall rules')
                self.dieWithError(403)  # 403 Forbidden
                return

        logger.info("Accepted connection from " + self.clientip + " path " +
                    requests.utils.unquote(self.path).decode('UTF-8'))
        logger.debug("Headers:\n" + str(self.headers))
        try:
            self.splittedpath = self.path.split('/')
            self.reqtype = self.splittedpath[1].lower()
            # If first parameter is 'pid','torrent', 'infohash'.... etc or it should be handled
            # by plugin
            if not (self.reqtype
                    in ('pid', 'torrent', 'infohash', 'url', 'raw', 'efile')
                    or self.reqtype in AceStuff.pluginshandlers):
                self.dieWithError(400)  # 400 Bad Request
                return
        except IndexError:
            self.dieWithError(400)  # 400 Bad Request
            return

        # Handle request with plugin handler
        if self.reqtype in AceStuff.pluginshandlers:
            try:
                AceStuff.pluginshandlers.get(self.reqtype).handle(
                    self, headers_only)
            except Exception as e:
                logger.error('Plugin exception: ' + repr(e))
                logger.error(traceback.format_exc())
                self.dieWithError()
            finally:
                self.closeConnection()
                return

        self.handleRequest(headers_only)
コード例 #2
0
    def do_GET(self, headers_only=False):
        '''
        GET request handler
        '''
        logger = logging.getLogger('do_GET')
        self.reqtime = time.time()
        self.connected = True
        # Don't wait videodestroydelay if error happened
        self.errorhappened = True
        # Headers sent flag for fake headers UAs
        self.headerssent = False
        # Current greenlet
        self.requestgreenlet = gevent.getcurrent()
        # Connected client IP address
        self.clientip = self.request.getpeername()[0]

        if AceConfig.firewall:
            # If firewall enabled
            self.clientinrange = any(map(lambda i: ipaddr.IPAddress(self.clientip) \
                                in ipaddr.IPNetwork(i), AceConfig.firewallnetranges))

            if (AceConfig.firewallblacklistmode and self.clientinrange) or \
                (not AceConfig.firewallblacklistmode and not self.clientinrange):
                logger.info('Dropping connection from ' + self.clientip + ' due to ' + \
                            'firewall rules')
                self.dieWithError(403)  # 403 Forbidden
                return

        logger.info("Accepted connection from " + self.clientip + " path " +
                    self.path)

        try:
            self.splittedpath = self.path.split('/')
            self.reqtype = self.splittedpath[1].lower()
            # If first parameter is 'pid' or 'torrent' or it should be handled
            # by plugin
            if not (self.reqtype in ('pid', 'torrent')
                    or self.reqtype in AceStuff.pluginshandlers):
                self.dieWithError(400)  # 400 Bad Request
                return
        except IndexError:
            self.dieWithError(400)  # 400 Bad Request
            return

        # Handle request with plugin handler
        if self.reqtype in AceStuff.pluginshandlers:
            try:
                AceStuff.pluginshandlers.get(self.reqtype).handle(
                    self, headers_only)
            except Exception as e:
                logger.error('Plugin exception: ' + repr(e))
                logger.error(traceback.format_exc())
                self.dieWithError()
            finally:
                self.closeConnection()
                return
        self.handleRequest(headers_only)
コード例 #3
0
    def handle(self, connection, headers_only=False):
        current_time = time.time()

        if connection.reqtype == 'favicon.ico':
            connection.send_response(404)
            return

        connection.send_response(200)
        connection.send_header('Content-type', 'text/html; charset=utf-8')
        connection.end_headers()

        if headers_only:
            return

        connection.wfile.write('<meta http-equiv="Refresh" content="60" />'
                               '<html><body><h4>Connected clients: ' +
                               str(self.stuff.clientcounter.total) + '</h4>')
        connection.wfile.write(
            '<h5>Concurrent connections limit: ' + str(self.config.maxconns) +
            '</h5><table  border="1" cellspacing="0" cellpadding="3">')
        for i in self.stuff.clientcounter.clients:
            for c in self.stuff.clientcounter.clients[i]:
                connection.wfile.write('<tr><td>')
                if c.channelIcon:
                    connection.wfile.write('<img src="' + c.channelIcon +
                                           '" width="40" height="16" />&nbsp;')
                if c.channelName:
                    connection.wfile.write(c.channelName.encode('UTF8'))
                else:
                    connection.wfile.write(i)

                connection.wfile.write('</td><td>' + c.handler.clientip +
                                       '</td>')
                clientinrange = any(
                    map(
                        lambda i: ipaddr.IPAddress(c.handler.clientip) in
                        ipaddr.IPNetwork(i), localnetranges))

                if clientinrange:
                    connection.wfile.write('<td>' + 'Local IP adress' +
                                           '</td>')
                else:
                    geo_ip_info = self.geo_ip_lookup(c.handler.clientip)
                    connection.wfile.write(
                        '<td>' + geo_ip_info.get('country').encode('utf-8') +
                        ', ' + geo_ip_info.get('city').encode('utf-8') +
                        '</td>')

                connection.wfile.write(
                    '<td>' +
                    time.strftime('%c', time.localtime(c.connectionTime)) +
                    '</td>')
                connection.wfile.write('<td>' + time.strftime(
                    "%H:%M:%S", time.gmtime(current_time - c.connectionTime)) +
                                       '</td></tr>')
        connection.wfile.write('</table></body></html>')
コード例 #4
0
ファイル: pvrserver.py プロジェクト: mozguletz/kodipvrserver
    def perform_validation(self):
        logger = logging.getLogger('http_HTTPHandler.perform_validation')
        # If firewall enabled
        if PVRConfig.firewall:
            self.clientinrange = any(map(lambda i: ipaddr.IPAddress(self.clientip) \
                                in ipaddr.IPNetwork(i), PVRConfig.firewallnetranges))

            if (PVRConfig.firewallblacklistmode and self.clientinrange) or \
                (not PVRConfig.firewallblacklistmode and not self.clientinrange):
                logger.info('Dropping connection from ' + self.clientip +
                            ' due to firewall rules')
                self.dieWithError(403)  # 403 Forbidden
                raise Exception('Not a valid IP')

        logger.info("Accepted connection from " + self.clientip)
        logger.info("RequestURL " + self.path)
        logger.info("User Agent " + self.headers.get('User-Agent'))

        try:
            # If first parameter is 'pid' or 'torrent' or it should be handled
            # by plugin
            if not (self.reqtype in (clients.SopcastProcess.ENGINE_TYPE,
                                     clients.HLSClient.ENGINE_TYPE)
                    or self.reqtype in clients.AceClient.ENGINE_TYPE
                    or self.reqtype in PVRStuff.pluginshandlers):
                self.dieWithError(400)  # 400 Bad Request
                raise Exception('Not valid handler')
        except IndexError:
            self.dieWithError(400)  # 400 Bad Request
            raise Exception('Not a valid handler')

        # Check if third parameter exists
        # …/pid/blablablablabla/video.mpg
        #                      |_________|
        # And if it ends with regular video extension
        try:
            if len(self.splittedpath) > 2 and not self.path.endswith(
                ('.3gp', '.avi', '.flv', '.mkv', '.mov', '.mp4', '.mpeg',
                 '.mpg', '.ogv', '.ts', '.asf')):
                logger.error(
                    "Request seems like valid but no valid video extension was provided"
                )
                self.dieWithError(400)
                raise Exception('Not a valid handler video stream')
        except IndexError:
            self.dieWithError(400)  # 400 Bad Request
            raise Exception('Not a valid handler video stream')
コード例 #5
0
    def do_GET(self, headers_only=False):
        '''
        GET request handler
        '''
        logger = logging.getLogger('http_HTTPHandler')
        self.clientconnected = True
        # Don't wait videodestroydelay if error happened
        self.errorhappened = True
        # Headers sent flag for fake headers UAs
        self.headerssent = False
        # Current greenlet
        self.requestgreenlet = gevent.getcurrent()
        # Connected client IP address
        self.clientip = self.request.getpeername()[0]

        if AceConfig.firewall:
            # If firewall enabled
            self.clientinrange = any(map(lambda i: ipaddr.IPAddress(self.clientip) \
                                in ipaddr.IPNetwork(i), AceConfig.firewallnetranges))

            if (AceConfig.firewallblacklistmode and self.clientinrange) or \
                (not AceConfig.firewallblacklistmode and not self.clientinrange):
                logger.info('Dropping connection from ' + self.clientip + ' due to ' + \
                            'firewall rules')
                self.dieWithError(403)  # 403 Forbidden
                return

        logger.info("Accepted connection from " + self.clientip + " path " +
                    self.path)

        try:
            self.splittedpath = self.path.split('/')
            self.reqtype = self.splittedpath[1].lower()
            # If first parameter is 'pid' or 'torrent' or it should be handled
            # by plugin
            if not (self.reqtype in ('pid', 'torrent')
                    or self.reqtype in AceStuff.pluginshandlers):
                self.dieWithError(400)  # 400 Bad Request
                return
        except IndexError:
            self.dieWithError(400)  # 400 Bad Request
            return

        # Handle request with plugin handler
        if self.reqtype in AceStuff.pluginshandlers:
            try:
                AceStuff.pluginshandlers.get(self.reqtype).handle(self)
            except Exception as e:
                logger.error('Plugin exception: ', exc_info=True)
                self.dieWithError()
            finally:
                self.closeConnection()
                return

        # Check if third parameter exists
        # …/pid/blablablablabla/video.mpg
        #                      |_________|
        # And if it ends with regular video extension
        try:
            if not self.path.endswith(
                ('.3gp', '.avi', '.flv', '.mkv', '.mov', '.mp4', '.mpeg',
                 '.mpg', '.ogv', '.ts')):
                logger.error(
                    "Request seems like valid but no valid video extension was provided"
                )
                self.dieWithError(400)
                return
        except IndexError:
            self.dieWithError(400)  # 400 Bad Request
            return

        # Limit concurrent connections
        if AceConfig.maxconns > 0 and AceStuff.clientcounter.total >= AceConfig.maxconns:
            logger.debug("Maximum connections reached, can't serve this")
            self.dieWithError(503)  # 503 Service Unavailable
            return

        # Pretend to work fine with Fake UAs or HEAD request.
        useragent = self.headers.get('User-Agent')
        fakeua = useragent and useragent in AceConfig.fakeuas
        if headers_only or fakeua:
            if fakeua:
                logger.debug("Got fake UA: " + self.headers.get('User-Agent'))
            # Return 200 and exit
            self.send_response(200)
            self.send_header("Content-Type", "video/mpeg")
            self.end_headers()
            self.closeConnection()
            return

        self.path_unquoted = urllib2.unquote(self.splittedpath[2])
        # Make list with parameters
        self.params = list()
        for i in xrange(3, 8):
            try:
                self.params.append(int(self.splittedpath[i]))
            except (IndexError, ValueError):
                self.params.append('0')

        # Adding client to clientcounter
        clients = AceStuff.clientcounter.add(self.path_unquoted, self.clientip)
        # If we are the one client, but sucessfully got ace from clientcounter,
        # then somebody is waiting in the videodestroydelay state
        self.ace = AceStuff.clientcounter.getAce(self.path_unquoted)
        if not self.ace:
            shouldcreateace = True
        else:
            shouldcreateace = False

        # Use PID as VLC ID if PID requested
        # Or torrent url MD5 hash if torrent requested
        if self.reqtype == 'pid':
            self.vlcid = self.path_unquoted
        else:
            self.vlcid = hashlib.md5(self.path_unquoted).hexdigest()

        # If we don't use VLC and we're not the first client
        if clients != 1 and not AceConfig.vlcuse:
            AceStuff.clientcounter.delete(self.path_unquoted, self.clientip)
            logger.error(
                "Not the first client, cannot continue in non-VLC mode")
            self.dieWithError(503)  # 503 Service Unavailable
            return

        if shouldcreateace:
            # If we are the only client, create AceClient
            try:
                self.ace = aceclient.AceClient(
                    AceConfig.acehost,
                    AceConfig.aceport,
                    connect_timeout=AceConfig.aceconntimeout,
                    result_timeout=AceConfig.aceresulttimeout)
                # Adding AceClient instance to pool
                AceStuff.clientcounter.addAce(self.path_unquoted, self.ace)
                logger.debug("AceClient created")
            except aceclient.AceException as e:
                logger.error("AceClient create exception: " + repr(e))
                AceStuff.clientcounter.delete(self.path_unquoted,
                                              self.clientip)
                self.dieWithError(502)  # 502 Bad Gateway
                return

        # Send fake headers if this User-Agent is in fakeheaderuas tuple
        if fakeua:
            logger.debug("Sending fake headers for " + useragent)
            self.send_response(200)
            self.send_header("Content-Type", "video/mpeg")
            self.end_headers()
            # Do not send real headers at all
            self.headerssent = True

        try:
            self.hanggreenlet = gevent.spawn(self.hangDetector)
            logger.debug("hangDetector spawned")
            gevent.sleep()

            # Initializing AceClient
            if shouldcreateace:
                self.ace.aceInit(gender=AceConfig.acesex,
                                 age=AceConfig.aceage,
                                 product_key=AceConfig.acekey,
                                 pause_delay=AceConfig.videopausedelay)
                logger.debug("AceClient inited")
                if self.reqtype == 'pid':
                    contentinfo = self.ace.START(
                        self.reqtype, {
                            'content_id': self.path_unquoted,
                            'file_indexes': self.params[0]
                        })
                elif self.reqtype == 'torrent':
                    self.paramsdict = dict(
                        zip(aceclient.acemessages.AceConst.START_TORRENT,
                            self.params))
                    self.paramsdict['url'] = self.path_unquoted
                    contentinfo = self.ace.START(self.reqtype, self.paramsdict)
                logger.debug("START done")

            # Getting URL
            self.url = self.ace.getUrl(AceConfig.videotimeout)
            # Rewriting host for remote Ace Stream Engine
            self.url = self.url.replace('127.0.0.1', AceConfig.acehost)
            self.errorhappened = False

            if shouldcreateace:
                logger.debug("Got url " + self.url)
                # If using VLC, add this url to VLC
                if AceConfig.vlcuse:
                    # Force ffmpeg demuxing if set in config
                    if AceConfig.vlcforceffmpeg:
                        self.vlcprefix = 'http/ffmpeg://'
                    else:
                        self.vlcprefix = ''

                    # Sleeping videodelay
                    gevent.sleep(AceConfig.videodelay)

                    AceStuff.vlcclient.startBroadcast(
                        self.vlcid, self.vlcprefix + self.url,
                        AceConfig.vlcmux, AceConfig.vlcpreaccess)
                    # Sleep a bit, because sometimes VLC doesn't open port in
                    # time
                    gevent.sleep(0.5)

            # Building new VLC url
            if AceConfig.vlcuse:
                self.url = 'http://' + AceConfig.vlchost + \
                    ':' + str(AceConfig.vlcoutport) + '/' + self.vlcid
                logger.debug("VLC url " + self.url)

            # Sending client headers to videostream
            self.video = urllib2.Request(self.url)
            for key in self.headers.dict:
                self.video.add_header(key, self.headers.dict[key])

            self.video = urllib2.urlopen(self.video)

            # Sending videostream headers to client
            if not self.headerssent:
                self.send_response(self.video.getcode())
                if self.video.info().dict.has_key('connection'):
                    del self.video.info().dict['connection']
                if self.video.info().dict.has_key('server'):
                    del self.video.info().dict['server']
                if self.video.info().dict.has_key('transfer-encoding'):
                    del self.video.info().dict['transfer-encoding']
                if self.video.info().dict.has_key('keep-alive'):
                    del self.video.info().dict['keep-alive']

                for key in self.video.info().dict:
                    self.send_header(key, self.video.info().dict[key])
                # End headers. Next goes video data
                self.end_headers()
                logger.debug("Headers sent")

            if not AceConfig.vlcuse:
                # Sleeping videodelay
                gevent.sleep(AceConfig.videodelay)

            # Run proxyReadWrite
            self.proxyReadWrite()

            # Waiting until hangDetector is joined
            self.hanggreenlet.join()
            logger.debug("Request handler finished")

        except (aceclient.AceException, vlcclient.VlcException,
                urllib2.URLError) as e:
            logger.error("Exception: " + repr(e))
            self.errorhappened = True
            self.dieWithError()
        except gevent.GreenletExit:
            # hangDetector told us about client disconnection
            pass
        except Exception as e:
            # Unknown exception
            logger.error("Unknown exception: " + repr(e))
            self.errorhappened = True
            self.dieWithError()
        finally:
            logger.debug("END REQUEST")
            AceStuff.clientcounter.delete(self.path_unquoted, self.clientip)
            if not self.errorhappened and not AceStuff.clientcounter.get(
                    self.path_unquoted):
                # If no error happened and we are the only client
                logger.debug("Sleeping for " +
                             str(AceConfig.videodestroydelay) + " seconds")
                gevent.sleep(AceConfig.videodestroydelay)
            if not AceStuff.clientcounter.get(self.path_unquoted):
                logger.debug("That was the last client, destroying AceClient")
                if AceConfig.vlcuse:
                    try:
                        AceStuff.vlcclient.stopBroadcast(self.vlcid)
                    except:
                        pass
                self.ace.destroy()
                AceStuff.clientcounter.deleteAce(self.path_unquoted)
コード例 #6
0
ファイル: stat_plugin.py プロジェクト: subin7/aceproxy
    def handle(self, connection, headers_only=False):
        current_time = time.time()

        if connection.reqtype == 'favicon.ico':
            connection.send_response(404)
            return
        connection.wfile.write('<html><head>')
        connection.wfile.write(
            '<meta charset="UTF-8" http-equiv="Refresh" content="60"/>')
        connection.wfile.write('<title>AceProxy stat info</title>')
        connection.wfile.write(
            '<link rel="stylesheet" type="text/css" href="http://cloud.github.com/downloads/lafeber/world-flags-sprite/flags16.css"/>'
        )
        connection.wfile.write(
            '<link rel="shortcut icon" href="http://i.piccy.info/i9/5777461ca749986f6fb4c4b06a70bfbe/1504856430/10417/1177931/SHesterenka_150x150.png" type="image/png">'
        )
        connection.wfile.write('</head>')
        connection.wfile.write(
            '<body><div class="f16"><h4>Connected clients: ' +
            str(self.stuff.clientcounter.total) + '</h4>')
        connection.wfile.write(
            '<h5>Concurrent connections limit: ' + str(self.config.maxconns) +
            '</h5><table  border="2" cellspacing="0" cellpadding="3">')
        connection.wfile.write(
            '<tr align=CENTER valign=BASELINE BGCOLOR="#eeeee5"><td>Channel name</td><td>Client IP</td><td>Client/Location</td><td>Start time</td><td>Duration</td></tr>'
        )

        for i in self.stuff.clientcounter.clients:
            for c in self.stuff.clientcounter.clients[i]:
                connection.wfile.write('<tr><td>')
                if c.channelIcon:
                    connection.wfile.write('<img src="' + c.channelIcon +
                                           '" width="40" height="16"/>&nbsp;')
                if c.channelName:
                    connection.wfile.write(c.channelName.encode('UTF8'))
                else:
                    connection.wfile.write(i)

                connection.wfile.write('</td><td>' + c.handler.clientip +
                                       '</td>')
                clientinrange = any(
                    map(
                        lambda i: ipaddr.IPAddress(c.handler.clientip) in
                        ipaddr.IPNetwork(i), localnetranges))

                if clientinrange:
                    connection.wfile.write('<td>' + self.mac_lookup(
                        c.handler.clientip).encode('UTF8').strip() + '</td>')
                else:
                    geo_ip_info = self.geo_ip_lookup(c.handler.clientip)
                    connection.wfile.write(
                        '<td>' + geo_ip_info.get('country').encode('UTF8') +
                        ', ' + geo_ip_info.get('city').encode('UTF8') +
                        '&nbsp;<i class="flag ' + geo_ip_info.get(
                            'country_code').encode('UTF8').lower() +
                        '"></i>&nbsp;</td>')
                connection.wfile.write(
                    '<td>' +
                    time.strftime('%c', time.localtime(c.connectionTime)) +
                    '</td>')
                connection.wfile.write('<td align="center">' + time.strftime(
                    "%H:%M:%S", time.gmtime(current_time - c.connectionTime)) +
                                       '</td></tr>')
        connection.wfile.write('</table></div></body></html>')
コード例 #7
0
ファイル: stat_plugin.py プロジェクト: subin7/aceproxy-1
    def handle(self, connection, headers_only=False):
        current_time = time.time()

        if connection.reqtype == 'favicon.ico':
            connection.send_response(404)
            return

        connection.send_response(200)
        connection.send_header('Content-type', 'text/html; charset=utf-8')
        connection.send_header('Connection', 'close')
        connection.end_headers()

        if headers_only:
            return
        # Sys Info
        cpu_nums = psutil.cpu_count()
        cpu_percent = psutil.cpu_percent()
        max_mem = psutil.virtual_memory()
        disk = psutil.disk_usage('/')

        connection.wfile.write('<html><head>')
        connection.wfile.write(
            '<meta charset="UTF-8" http-equiv="Refresh" content="60"/>')
        connection.wfile.write('<title>AceProxy stat info</title>')
        connection.wfile.write(
            '<link rel="stylesheet" type="text/css" href="http://cloud.github.com/downloads/lafeber/world-flags-sprite/flags16.css"/>'
        )
        connection.wfile.write(
            '<link rel="shortcut icon" href="http://i.piccy.info/i9/5777461ca749986f6fb4c4b06a70bfbe/1504856430/10417/1177931/SHesterenka_150x150.png" type="image/png">'
        )
        connection.wfile.write('<style>h5 {margin-bottom: -15px;}</style>')
        connection.wfile.write('</head>')
        connection.wfile.write('<body><div class="f16">')

        connection.wfile.write('<p>Connections limit: ' +
                               str(self.config.maxconns) +
                               '&nbsp;&nbsp;&nbsp;Connected clients: ' +
                               str(self.stuff.clientcounter.total) + '</p>')

        connection.wfile.write(
            '<table  border="2" cellspacing="0" cellpadding="3">')
        connection.wfile.write(
            '<tr align=CENTER valign=BASELINE BGCOLOR="#eeeee5"><td>Channel name/CID</td><td>Client IP</td><td>Client/Location</td><td>Start time</td><td>Duration</td></tr>'
        )

        for i in self.stuff.clientcounter.clients:
            for c in self.stuff.clientcounter.clients[i]:
                connection.wfile.write('<tr><td>')
                if c.channelIcon:
                    connection.wfile.write('<img src="' + c.channelIcon +
                                           '" width="40" height="16"/>&nbsp;')
                if c.channelName:
                    connection.wfile.write(c.channelName.encode('UTF8'))
                else:
                    connection.wfile.write(i)

                connection.wfile.write('</td><td>' + c.handler.clientip +
                                       '</td>')
                clientinrange = any(
                    map(
                        lambda i: ipaddr.IPAddress(c.handler.clientip) in
                        ipaddr.IPNetwork(i), localnetranges))

                if clientinrange:
                    connection.wfile.write('<td>' + self.mac_lookup(
                        c.handler.clientip).encode('UTF8').strip() + '</td>')
                else:
                    geo_ip_info = self.geo_ip_lookup(c.handler.clientip)
                    connection.wfile.write(
                        '<td>' + geo_ip_info.get('country').encode('UTF8') +
                        ', ' + geo_ip_info.get('city').encode('UTF8') +
                        '&nbsp;<i class="flag ' + geo_ip_info.get(
                            'country_code').encode('UTF8').lower() +
                        '"></i>&nbsp;</td>')
                connection.wfile.write(
                    '<td>' +
                    time.strftime('%c', time.localtime(c.connectionTime)) +
                    '</td>')
                connection.wfile.write('<td align="center">' + time.strftime(
                    "%H:%M:%S", time.gmtime(current_time - c.connectionTime)) +
                                       '</td></tr>')
        connection.wfile.write('</table></div>')

        connection.wfile.write('<h5>SYSTEM INFO :</h5>')
        connection.wfile.write('<p><font size="-3">CPU cores: %s' % cpu_nums +
                               ' used : %s' % cpu_percent + '%</br>')
        connection.wfile.write('RAM MiB &nbsp;')
        connection.wfile.write(
            'total: %s ' % str(round(max_mem.total / 2**20, 2)) +
            '&nbsp;used: %s' % str(round(max_mem.used / 2**20, 2)) +
            '&nbsp;free: %s </br>' % str(round(max_mem.available / 2**20, 2)))
        connection.wfile.write('DISK GiB &nbsp;')
        connection.wfile.write(
            'total: %s ' % str(round(disk.total / 2**30, 2)) +
            '&nbsp;used: %s' % str(round(disk.used / 2**30, 2)) +
            '&nbsp;free: %s </font></p>' % str(round(disk.free / 2**30, 2)))
        connection.wfile.write('</body></html>')