Python 实现一个简单的http服务器

Python运行环境:Ubuntu Ubuntu 18.04.1 LTS
本文仅用于记录学习和分析Python的过程,Python还有很多搭建web服务器的框架本文都不涉及。

最简单的方式

执行命令:

 $ python -m SimpleHTTPServer  
//如果不指定端口,默认使用8000

然后在浏览器上输入:

http://localhost:8000/ 
//把localhost改成Linux主机ip

然后Duang~~~
Python 实现一个简单的http服务器_第1张图片

网页上显示出这个,那个A_File是我在Linux下运行Python文件里面的一个文件。

什么,就这么简单?好吧,作为一个Linux C程序员,不得不说,我得研究一下源码。

SimpleHTTPServer

网页哪来的?

最基本的理解是,有网页就得有html文件,而且很显而易见的是,这个网页看上去是调整过格式的。

开始的想法

一开始想到就是在我的Linux环境里是有一个这样的html文件存在的,在使用SimpleHTTPServer模块时会找到它,并且推给前端。然后就在Python安装路径和系统里找这个文件,最后都把以前玩过的Apache和Tomcat里面的html都翻出来了也没对上号。。。

换个思路

先考虑两点:
1. 打开网页时HTTP Server会有这样的信息:“GET / HTTP/1.1” 200 -。说明打开网页的时候执行的是GET请求(这个好像是理所当然的)。
2. 从浏览器上查看网页源码:

<html>
<title>Directory listing for /title>
<body>
<h2>Directory listing for /h2>
<hr>
<ul>
<li><a href="A_File">A_Filea>
ul>
<hr>
body>
html>

接下来从源码的角度分析一下:
在我的Linux系统中找到SimpleHTTPServer模块源码(感兴趣的源码自行查找),查看do_GET() 方法:

def do_GET(self):
"""Serve a GET request."""
f = self.send_head()
if f:
    try:
        self.copyfile(f, self.wfile)
    finally:
        f.close()

这么一看应该是要看一下send_head() 方法做了什么,里面有这样一段代码:

path = self.translate_path(self.path)
f = None
if os.path.isdir(path):
    parts = urlparse.urlsplit(self.path)
    if not parts.path.endswith('/'):
        # redirect browser - doing basically what apache does
        self.send_response(301)
        new_parts = (parts[0], parts[1], parts[2] + '/',
                     parts[3], parts[4])
        new_url = urlparse.urlunsplit(new_parts)
        self.send_header("Location", new_url)
        self.end_headers()
        return None
    for index in "index.html", "index.htm":
        index = os.path.join(path, index)
        if os.path.exists(index):
            path = index
            break
    else:
        return self.list_directory(path)

主要看一下for循环的作用,实际上是在当前路径下查找index.html, index.htm 如果没找到就return到 list_directory() 方法,紧接着就在list_directory() 方法里看到这样的代码:

 f.write('')
 f.write("\nDirectory listing for %s\n" % displaypath)
 f.write("\n

Directory listing for %s

\n"
% displaypath) f.write("
\n
    \n") for name in list: fullname = os.path.join(path, name) displayname = linkname = name # Append / for directories or @ for symbolic links if os.path.isdir(fullname): displayname = name + "/" linkname = name + "/" if os.path.islink(fullname): displayname = name + "@" # Note: a link to a directory displays with @ and links with / f.write('
  • %s\n' % (urllib.quote(linkname), cgi.escape(displayname))) f.write("
\n
\n\n\n"
)

很显然,这个段代码刚好对应到了上面贴的网页源码。现在真相大白了,在打开网页时do_GET() 方法会调用 list_directory() 创建一个html出来给前端显示。

验证一下

虽然从代码的逻辑上分析完了,但是总觉得怪怪的,还是要自己跑一下代码才放心啊。

使用Python的好处就是可以方便的进行验证,这一点就比C方便的多。
首先把SimpleHTTPServer.py从源码路径里copy出来放到与A_File同一路径下,然后执行命令:

$ python SimpleHTTPServer.py

然后用服务器ip打开网页会看到:
Python 实现一个简单的http服务器_第2张图片
网页可以正常打开,显示的也正是预期的结果,说明刚刚的操作是正确的。
这也正是用Python的优势所在,这个源码文件其实是需要BaseHTTPServer 等模块支持的,但是我并没有把这个模块copy过来。当然也是因为在Linux环境下安装Python的时候就已经配置好环境变量了。当然也可以类比Shell。

根据上面的分析,如果再当前路径下有html文件会怎么样呢?
创建一个index.html文件,里面加上一句"Hello World!",然后启动脚本在打开网页会显示如下:
Python 实现一个简单的http服务器_第3张图片
(万年的Hello World???)
这说明之前对那个for 循环部分的代码分析是没错的。同样的可以把list_directory() 方法里面的StringIO 打印出来也会对上面的介绍进行验证。

至于在GET请求时 “GET / HTTP/1.1” 200 - 这个信息打印是怎么打印出来的呢,可以把BaseHTTPServer 模块源码copy过来做一下验证,里面有个log_message() 方法,具体代码逻辑就不在这里分析了。

也就是说,所有的现象只要仔细分析一下源码都能找到根源。

为什么要用Python搭建,因为如果单纯的想要从服务器上下载文件,用Python 的SimpleHTTPServer 模块搭建一个服务器要比搭建ftp或者是tftp简单的多。
但是这个单纯的执行一下 命令python -m SimpleHTTPServer,只能实现文件的下载功能,如果想要实现文件上传功能可以参考:https://github.com/tualatrix/tools/blob/master/SimpleHTTPServerWithUpload.py 这里面实现了POST请求功能。

你可能感兴趣的:(Python)