📜  在Python创建代理 Web 服务器 | 2套

📅  最后修改于: 2021-10-19 06:38:06             🧑  作者: Mango

先决条件:在Python创建代理 Web 服务器 – Set1

在本教程中,添加了一些有趣的功能以使其更有用。

  • 添加域黑名单。对于前。 google.com、facebook.com。在我们的配置字典中创建一个 BLACKLIST_DOMAINS 列表。现在,只需忽略/删除收到的黑名单域的请求。 (理想情况下,我们必须以禁止响应进行响应。)
    # Check if the host:port is blacklisted
    for i in range(0, len(config['BLACKLIST_DOMAINS'])):
        if config['BLACKLIST_DOMAINS'][i] in url:
            conn.close()
    return
    
  • 添加主机阻止:比如说,您可能需要允许来自特定子网的连接或特定人员的连接。要添加此内容,请创建所有允许主机的列表。由于主机也可以是子网,因此添加正则表达式以匹配 IP 地址,特别是 IPV4 地址。 “ IPv4 地址以点十进制表示法规范表示,它由四个十进制数字组成,每个数字的范围从 0 到 255,用点分隔,例如 172.16.254.1。每个部分代表地址的一组 8 位(八位字节)。”
  • 使用正则表达式匹配正确的 IP 地址:
    • 在 Server 类中创建一个新方法 _ishostAllowed ,并使用 fnmatch 模块匹配正则表达式。遍历所有正则表达式并允许请求,如果它与其中任何一个匹配。如果未发现客户端地址是任何正则表达式的一部分,则发送 FORBIDDEN 响应。同样,现在跳过这个响应创建部分。

注意:我们将在接下来的教程中创建一个完整的自定义网络服务器,将创建一个 createResponse函数来处理通用响应的创建。

def _ishostAllowed(self, host):

    """ Check if host is allowed to access
        the content """
    for wildcard in config['HOST_ALLOWED']:
        if fnmatch.fnmatch(host, wildcard):
            return True
    return False

默认主机匹配正则表达式为“*”以匹配所有主机。但是,也可以使用“192.168.*”形式的正则表达式。服务器当前处理请求但不显示任何消息,因此我们不知道服务器的状态。它的消息应该登录到控制台。为此,请使用日志记录模块,因为它是线程安全的。 (如果你记得的话,服务器是多线程的。)

导入模块并设置其初始配置。

logging.basicConfig(level = logging.DEBUG,
format = '[%(CurrentTime)-10s] (%(ThreadName)-10s) %(message)s',)
  • 创建一个单独的方法来记录每条消息:将其作为参数传递,并带有附加数据(例如线程名称和当前时间)以跟踪日志。还创建一个函数,为日志着色,以便在 STDOUT 上看起来很漂亮。
    为了实现这一点,在配置中添加一个布尔值 COLORED_LOGGING 并创建一个新函数,该函数根据 LOG_LEVEL 为传递给它的每个 msg 着色。
def log(self, log_level, client, msg):

    """ Log the messages to appropriate place """
    LoggerDict = {
       'CurrentTime' : strftime("%a, %d %b %Y %X", localtime()),
       'ThreadName' : threading.currentThread().getName()
    }
    if client == -1: # Main Thread
        formatedMSG = msg
    else: # Child threads or Request Threads
        formatedMSG = '{0}:{1} {2}'.format(client[0], client[1], msg)
    logging.debug('%s', utils.colorizeLog(config['COLORED_LOGGING'],
    log_level, formatedMSG), extra=LoggerDict)
  • 创建一个新模块 ColorizePython.py:它包含一个 pycolors 类,该类维护一个颜色代码列表。将其分离到另一个模块中,以使代码模块化并遵循 PEP8 标准。
# ColorizePython.py
class pycolors:
HEADER = '\033[95m'
OKBLUE = '\033[94m'
OKGREEN = '\033[92m'
WARNING = '\033[93m'
FAIL = '\033[91m'
ENDC = '\033[0m' # End color
BOLD = '\033[1m'
UNDERLINE = '\033[4m'

模块:

import ColorizePython

方法:

def colorizeLog(shouldColorize, log_level, msg):
    ## Higher is the log_level in the log()
    ## argument, the lower is its priority.
    colorize_log = {
    "NORMAL": ColorizePython.pycolors.ENDC,
    "WARNING": ColorizePython.pycolors.WARNING,
    "SUCCESS": ColorizePython.pycolors.OKGREEN,
    "FAIL": ColorizePython.pycolors.FAIL,
    "RESET": ColorizePython.pycolors.ENDC
    }

    if shouldColorize.lower() == "true":
        if log_level in colorize_log:
            return colorize_log[str(log_level)] + msg + colorize_log['RESET']
        return colorize_log["NORMAL"] + msg + colorize_log["RESET"]
    return msg
  • 由于 colorizeLog 不是服务器类的函数,它被创建为一个名为 utils.py 的单独模块,该模块存储所有使代码更易于理解的实用程序并将此方法放在那里。在需要的地方添加适当的日志消息,尤其是在服务器状态发生变化时。
  • 修改 server 中的 shutdown 方法,在退出应用程序之前退出所有正在运行的线程。 threading.enumerate()遍历所有正在运行的线程,因此我们不需要维护它们的列表。当我们尝试结束 main_thread 时,线程模块的行为是意外的。官方文档还说明了这一点:

“如果尝试加入当前线程,join() 会引发 RuntimeError,因为这会导致死锁。在线程启动之前 join() 也是一个错误,尝试这样做会引发相同的异常。”

所以,适当地跳过它。这是相同的代码。

def shutdown(self, signum, frame):
    """ Handle the exiting server. Clean all traces """
    self.log("WARNING", -1, 'Shutting down gracefully...')
    main_thread = threading.currentThread() # Wait for all clients to exit
    for t in threading.enumerate():
        if t is main_thread:
            continue
            self.log("FAIL", -1, 'joining ' + t.getName())
        t.join()
        self.serverSocket.close()
    sys.exit(0)

如果您有任何意见/建议/疑问,请随时提出。 🙂

关于作者:

Pinkesh Badjatiya来自 IIIT Hyderabad。他是一个内心极客,拥有大量值得寻找的项目。他的项目工作可以在这里看到。

如果您还想在这里展示您的博客,请参阅 GBlog,了解 GeeksforGeeks 上的客座博客写作。