• [500lines]500行代码写web server


    项目地址:https://github.com/aosabook/500lines/tree/master/web-server.作者是来自Mozilla的Greg Wilson.项目是用py2写成.下面文章中贴出的是已经转换后的能在python3.4下运行的代码,所以可能会与原先的有少许不同.

    简单地讲,你在浏览器里输入一个网址,浏览器作为客户端会通过DNS解析域名找到对应的IP,并将这串字符串的一部分作为请求发给这个IP的服务器,服务器解析字符串,解析出你的请求内容做出相应处理,然后把一串字符串回复给浏览器,浏览器以一定格式展现这些字符串,就是你看到的网页.

    由于协议是固定的HTTP协议,所以解析这一步基本上可以认为是一样的,同样的,将字符串回复给浏览器也可以认为是一样的,所以对不同的server来讲,不同的是解析出请求后的处理逻辑.正是基于这一点,python的标准库里已经给我们提供了很多好用的模块.

    先看一个最简单的helloworld版的webserver

     1 import http.server
     2 
     3 class RequestHandler(http.server.BaseHTTPRequestHandler):
     4     '''Handle HTTP requests by returning a fixed 'page'.'''
     5 
     6     # Page to send back.
     7     Page = '''
     8 <html>
     9 <body>
    10 <p>Hello, web!</p>
    11 </body>
    12 </html>
    13 '''
    14 
    15     # Handle a GET request.                       
    16 
    17     def do_GET(self):
    18         self.send_response(200)
    19         self.send_header("Content-type", "text/html")
    20         self.send_header("Content-Length", str(len(self.Page)))
    21         self.end_headers()
    22         self.wfile.write(bytearray(page,"gbk"))
    23 
    24 #----------------------------------------------------------------------
    25 
    26 if __name__ == '__main__':
    27     serverAddress = ('', 8080)
    28     server = http.server.HTTPServer(serverAddress, RequestHandler)
    29     server.serve_forever()

    代码非常易懂,main函数里在8080端口启动http监听.RequestHandler继承自http.server.BaseHTTPRequestHandler,这个BaseHTTPRequestHandler已经帮我们做好了解析浏览器发来的HTTP请求并调用相应的do_GET()方法的工作.所以我们要做的仅仅是在do_GET()内实现我们的逻辑就好了.

     ___________________________________________________________________________________________________________________________

    现在来看看第二个例子:

     1 import http.server
     2 
     3 class RequestHandler(http.server.BaseHTTPRequestHandler):
     4     '''Respond to HTTP requests with info about the request.'''
     5 
     6     # Template for page to send back.
     7     Page = '''
     8 <html>
     9 <body>
    10 <table>
    11 <tr>  <td>Header</td>         <td>Value</td>           </tr>
    12 <tr>  <td>Date and time</td>  <td>%(date_time)s</td>   </tr>
    13 <tr>  <td>Client host</td>    <td>%(client_host)s</td> </tr>
    14 <tr>  <td>Client port</td>    <td>%(client_port)s</td> </tr>
    15 <tr>  <td>Command</td>        <td>%(command)s</td>     </tr>
    16 <tr>  <td>Path</td>           <td>%(path)s</td>        </tr>
    17 </table>
    18 </body>
    19 </html>
    20 '''
    21 
    22     # Handle a request by constructing an HTML page that echoes the
    23     # request back to the caller.
    24     def do_GET(self):
    25         page = self.create_page()
    26         self.send_page(page)
    27 
    28     # Create an information page to send.
    29     def create_page(self):
    30         values = {
    31             'date_time'   : self.date_time_string(),
    32             'client_host' : self.client_address[0],
    33             'client_port' : self.client_address[1],
    34             'command'     : self.command,
    35             'path'        : self.path
    36         }
    37         page = self.Page % values
    38         return page
    39 
    40     # Send the created page.
    41     def send_page(self, page):
    42         self.send_response(200)
    43         self.send_header("Content-type", "text/html")
    44         self.send_header("Content-Length", str(len(page)))
    45         self.end_headers()
    46         self.wfile.write(bytearray(page,"gbk"))
    47 
    48 #----------------------------------------------------------------------
    49 
    50 if __name__ == '__main__':
    51     serverAddress = ('', 8080)
    52     server = http.server.HTTPServer(serverAddress, RequestHandler)
    53     server.serve_forever()

     这个例子也很好懂,例子一里是返回一个静态的页面,这一次我们写一个页面的模板,然后将解析出来的datatime,clientaddress啥的填充到模板,然后返回这个页面给浏览器.这样我们就算是能看到一个每次请求内容都会变化的动态的页面了.

    ___________________________________________________________________________________________________________________________

    现在来看看第三个例子,与例子二相比,这个例子稍微复杂了一些,它添加了一些错误处理的代码,并且不再是简单地提供一个写好了模板的页面:

     1 import sys, os, http.server
     2 
     3 class ServerException(Exception):
     4     '''For internal error reporting.'''
     5     pass
     6 
     7 class RequestHandler(http.server.BaseHTTPRequestHandler):
     8     '''
     9     If the requested path maps to a file, that file is served.
    10     If anything goes wrong, an error page is constructed.
    11     '''
    12 
    13     # How to display an error.
    14     Error_Page = """
    15         <html>
    16         <body>
    17         <h1>Error accessing %(path)s</h1>
    18         <p>%(msg)s</p>
    19         </body>
    20         </html>
    21         """
    22 
    23     # Classify and handle request.
    24     def do_GET(self):
    25         try:
    26 
    27             # Figure out what exactly is being requested.
    28             full_path = os.getcwd() + self.path
    29             print(self.path," ",full_path)
    30             # It doesn't exist...
    31             if not os.path.exists(full_path):
    32                 raise ServerException("'%s' not found" % self.path)
    33 
    34             # ...it's a file...
    35             elif os.path.isfile(full_path):
    36                 self.handle_file(full_path)
    37 
    38             # ...it's something we don't handle.
    39             else:
    40                 raise ServerException("Unknown object '%s'" % self.path)
    41 
    42         # Handle errors.
    43         except Exception as msg:
    44             self.handle_error(msg)
    45 
    46     def handle_file(self, full_path):
    47         try:
    48             with open(full_path, 'r') as input:
    49                 content = input.read()
    50             self.send_content(content)
    51         except IOError as msg:
    52             msg = "'%s' cannot be read: %s" % (self.path, msg)
    53             self.handle_error(msg)
    54 
    55     # Handle unknown objects.
    56     def handle_error(self, msg):
    57         content = self.Error_Page % {'path' : self.path,
    58                                      'msg'  : msg}
    59         self.send_content(content)
    60 
    61     # Send actual content.
    62     def send_content(self, content):
    63         self.send_response(200)
    64         self.send_header("Content-type", "text/html")
    65         self.send_header("Content-Length", str(len(content)))
    66         self.end_headers()
    67         self.wfile.write(bytearray(content,"gbk"))
    68 
    69 #----------------------------------------------------------------------
    70 
    71 if __name__ == '__main__':
    72     serverAddress = ('', 8080)
    73     server = http.server.HTTPServer(serverAddress, RequestHandler)
    74     server.serve_forever()

    这个例子读取程序所在的当前目录下的某个文件,将文件内容返回给浏览器.我们看看do_GET(self)这个函数内干了什么,首先是获取一个全路径,然后判断这个全路径是否存在,存在的话指向的是否为一个文件,是文件则读取这个文件并返回给浏览器,否则则返回一个error_page。

    ___________________________________________________________________________________________________________________________

    现在来看看第四个例子:

     1 import sys, os, http.server
     2 
     3 class ServerException(Exception):
     4     '''For internal error reporting.'''
     5     pass
     6 
     7 class RequestHandler(http.server.BaseHTTPRequestHandler):
     8     '''
     9     If the requested path maps to a file, that file is served.
    10     If anything goes wrong, an error page is constructed.
    11     '''
    12 
    13     # How to display a directory listing.
    14     Listing = '''
    15 <html>
    16 <body>
    17 <ul>
    18 %s
    19 </ul>
    20 </body>
    21 </html>
    22 '''
    23 
    24     # How to display an error.
    25     Error_Page = """
    26         <html>
    27         <body>
    28         <h1>Error accessing %(path)s</h1>
    29         <p>%(msg)s</p>
    30         </body>
    31         </html>
    32         """
    33 
    34     # Classify and handle request.
    35     def do_GET(self):
    36         try:
    37 
    38             # Figure out what exactly is being requested.
    39             full_path = os.getcwd() + self.path
    40 
    41             # It doesn't exist...
    42             if not os.path.exists(full_path):
    43                 raise ServerException("'%s' not found" % self.path)
    44 
    45             # ...it's a file...
    46             elif os.path.isfile(full_path):
    47                 self.handle_file(full_path)
    48 
    49             # ...it's a directory...
    50             elif os.path.isdir(full_path):
    51                 self.list_dir(full_path)
    52 
    53             # ...it's something we don't handle.
    54             else:
    55                 raise ServerException("Unknown object '%s'" % self.path)
    56 
    57         # Handle errors.
    58         except Exception as msg:
    59             self.handle_error(msg)
    60 
    61     def handle_file(self, full_path):
    62         try:
    63             with open(full_path, 'r') as input:
    64                 content = input.read()
    65             self.send_content(content)
    66         except IOError as msg:
    67             msg = "'%s' cannot be read: %s" % (self.path, msg)
    68             self.handle_error(msg)
    69 
    70     def list_dir(self, full_path):
    71         try:
    72             entries = os.listdir(full_path)
    73             bullets = ['<li>%s</li>' % e for e in entries if not e.startswith('.')]
    74             page = self.Listing % '
    '.join(bullets)
    75             self.send_content(page)
    76         except OSError as msg:
    77             msg = "'%s' cannot be listed: %s" % (self.path, msg)
    78             self.handle_error(msg)
    79 
    80     # Handle unknown objects.
    81     def handle_error(self, msg):
    82         content = self.Error_Page % {'path' : self.path,
    83                                      'msg'  : msg}
    84         self.send_content(content)
    85 
    86     # Send actual content.
    87     def send_content(self, content):
    88         self.send_response(200)
    89         self.send_header("Content-type", "text/html")
    90         self.send_header("Content-Length", str(len(content)))
    91         self.end_headers()
    92         self.wfile.write(bytearray(content,"gbk"))
    93 
    94 #----------------------------------------------------------------------
    95 
    96 if __name__ == '__main__':
    97     serverAddress = ('', 8080)
    98     server = http.server.HTTPServer(serverAddress, RequestHandler)
    99     server.serve_forever()

    这个例子和上个例子是类似的,不过是多了个对全路径指向的是一个目录而不是一个文件这种状况的处理.

    ——————————————————————————————————————————————————————————————————————————————————

    第五个例子比上述的看起来又要复杂一些,不过其实只是多了一个命令行的处理以及一些错误处理函数而已.getopt模块用法看这里:https://docs.python.org/3/library/getopt.html?highlight=getopt#module-getopt,

    在例子4和例子3中,我们拼接全路径是通过full_path = os.getcwd() + self.path来拼接.也就是说比如你的server.py位于D盘,那么你的full_path就只能是D:xxxxx这种形式.

    那么在例子5里,我们通过在启动server.py时通过命令行的方式(比如:python server.py -v C:)传参一个根目录,那么这时候就能处理C:XXXX路径的文件了.

      1 import sys, os, http.server
      2 
      3 class RequestHandler(http.server.BaseHTTPRequestHandler):
      4 
      5     # Root of files being served.
      6     Root_Directory = None
      7 
      8     # Is debugging output on?
      9     Debug = False
     10 
     11     # HTTP error codes.
     12     ERR_NO_PERM   = 403
     13     ERR_NOT_FOUND = 404
     14     ERR_INTERNAL  = 500
     15 
     16     # How to display a single item in a directory listing.
     17     Listing_Item = "<li>%s</li>"
     18 
     19     # How to display a whole page of listings.
     20     Listing_Page = """
     21         <html>
     22         <body>
     23         <h1>Listing for %(path)s</h1>
     24         <ul>
     25         %(filler)s
     26         </ul>
     27         </body>
     28         </html>
     29         """
     30 
     31     # Classify and handle request.
     32     def do_GET(self):
     33 
     34         self.log("path is '%s'" % self.path)
     35 
     36         # Handle any errors that arise.
     37         try:
     38 
     39             # Class not yet initialized.
     40             if self.Root_Directory is None:
     41                 self.err_internal("Root directory not set")
     42                 return
     43 
     44             # Figure out what exactly is being requested.
     45             abs_path = self.create_abs_path()
     46             self.log("abs_path is '%s'" % abs_path)
     47 
     48             # It isn't below the root path.
     49             if not self.is_parent_dir(self.Root_Directory, abs_path):
     50                 self.log("abs_path not below root directory")
     51                 msg = "Path '%s' not below root directory '%s'" % 
     52                       (abs_path, self.Root_Directory)
     53                 self.err_no_perm(msg)
     54 
     55             # It doesn't exist.
     56             elif not os.path.exists(abs_path):
     57                 self.log("abs_path doesn't exist")
     58                 self.err_not_found(abs_path)
     59 
     60             # It's a file.
     61             elif os.path.isfile(abs_path):
     62                 self.log("abs_path is a file")
     63                 self.handle_file(abs_path)
     64 
     65             # It's a directory.
     66             elif os.path.isdir(abs_path):
     67                 self.log("abs_path is a directory")
     68                 self.handle_dir(abs_path)
     69 
     70             # ...we can't tell.
     71             else:
     72                 self.log("can't tell what abs_path is")
     73                 self.err_not_found(abs_path)
     74 
     75         # Handle general errors.
     76         except Exception as msg:
     77             self.err_internal("Unexpected exception: %s" % msg)
     78 
     79     def create_abs_path(self):
     80         head = os.path.abspath(self.Root_Directory)
     81         result = os.path.normpath(head + self.path)
     82         return result
     83 
     84     def is_parent_dir(self, left, right):
     85         return os.path.commonprefix([left, right]) == left
     86 
     87     def handle_file(self, abs_path):
     88         try:
     89             input = open(abs_path, "r")
     90             content = input.read()
     91             input.close()
     92             self.send_content(content)
     93         except IOError as msg:
     94             msg = "'%s' cannot be read: %s" % (self.path, msg)
     95             self.err_no_perm(msg)
     96 
     97     def handle_dir(self, abs_path):
     98         try:
     99             listing = os.listdir(abs_path)
    100             filler = '
    '.join([(self.Listing_Item % item) for item in listing])
    101             content = self.Listing_Page % {'path'   : self.path,
    102                                            'filler' : filler}
    103             self.send_content(content)
    104         except IOError as msg:
    105             msg = "'%s' cannot be listed: %s" % (self.path, msg)
    106             self.send_error(ERR_NO_PERM, msg)
    107 
    108     # Send actual content.
    109     def send_content(self, content):
    110         self.send_response(200)
    111         self.send_header("Content-type", "text/html")
    112         self.send_header("Content-Length", str(len(content)))
    113         self.end_headers()
    114         self.wfile.write(bytearray(content,"gbk"))
    115 
    116     # Report internal errors.
    117     def err_internal(self, msg):
    118         self.send_error(self.ERR_INTERNAL, msg)
    119 
    120     # Handle missing object errors.
    121     def err_not_found(self, abs_path):
    122         self.send_error(self.ERR_NOT_FOUND, "'%s' not found" % self.path)
    123 
    124     # Handle no permission errors.
    125     def err_no_perm(self, msg):
    126         self.send_error(self.ERR_NO_PERM, msg)
    127 
    128     # Write a log message if in debugging mode
    129     def log(self, msg):
    130         if self.Debug:
    131             print(msg)
    132 
    133 #----------------------------------------------------------------------
    134 
    135 if __name__ == '__main__':
    136 
    137     # Main libraries
    138     import getopt
    139 
    140     # How to use
    141     Usage = "server.py [-v] root_directory"
    142 
    143     # Handle command-line arguments
    144     options, rest = getopt.getopt(sys.argv[1:], "v")
    145     #print(sys.argv[1:])
    146     #print(options,rest)
    147     for (flag, arg) in options:
    148         if flag == "-v":
    149             RequestHandler.Debug = True
    150         else:
    151             print(Usage, file=sys.stderr)
    152             sys.exit(1)
    153 
    154     if not rest:
    155         print(Usage, file=sys.stderr)
    156         sys.exit(1)
    157     root = os.path.abspath(rest[0])
    158     if not os.path.isdir(root):
    159         print("No such directory '%s'" % root, file=sys.stderr)
    160         sys.exit(1)
    161     RequestHandler.Root_Directory = root
    162 
    163     # Create and run server.
    164     server_address = ('', 8080)
    165     server = http.server.HTTPServer(server_address, RequestHandler)
    166     server.serve_forever()

    --------------------------------------------------------------------------------------------------------------------------------------------------------------------在前面的例子中我们注意到在http_header里类型我们总是填写的“text/html”.如果浏览器请求的是一个图片呢?你可以运行例子5,你会发现浏览器是无法正常显示这个图片的.

    self.send_header("Content-type", "text/html")

    例子6与例子5的区别只是加了一个文件类型(例如是个文本啊还是个图片啊)的判断,从而在回发content的时候告知浏览器content-type以使其知道应该如何处理这种文件.有关mime type的知识可以看这里

    http://www.cnblogs.com/jsean/articles/1610265.html

      1 import sys, os, http.server, mimetypes
      2 class RequestHandler(http.server.BaseHTTPRequestHandler):
      3 
      4     # Root of files being served.
      5     Root_Directory = None
      6 
      7     # Is debugging output on?
      8     Debug = False
      9 
     10     # HTTP error codes.
     11     ERR_NO_PERM   = 403
     12     ERR_NOT_FOUND = 404
     13     ERR_INTERNAL  = 500
     14 
     15     # How to display a single item in a directory listing.
     16     Listing_Item = "<li>%s</li>"
     17 
     18     # How to display a whole page of listings.
     19     Listing_Page = """
     20         <html>
     21         <body>
     22         <h1>Listing for %(path)s</h1>
     23         <ul>
     24         %(filler)s
     25         </ul>
     26         </body>
     27         </html>
     28         """
     29 
     30     # MIME types of files.
     31     File_Types = mimetypes.types_map
     32 
     33     # Classify and handle request.
     34     def do_GET(self):
     35 
     36         self.log("path is '%s'" % self.path)
     37 
     38         # Handle any errors that arise.
     39         try:
     40 
     41             # Class not yet initialized.
     42             if self.Root_Directory is None:
     43                 self.err_internal("Root directory not set")
     44                 return
     45 
     46             # Figure out what exactly is being requested.
     47             abs_path = self.create_abs_path()
     48             self.log("abs_path is '%s'" % abs_path)
     49 
     50             # It isn't below the root path.
     51             if not self.is_parent_dir(self.Root_Directory, abs_path):
     52                 self.log("abs_path not below root directory")
     53                 msg = "Path '%s' not below root directory '%s'" % 
     54                       (abs_path, self.Root_Directory)
     55                 self.err_no_perm(msg)
     56 
     57             # It doesn't exist.
     58             elif not os.path.exists(abs_path):
     59                 self.log("abs_path doesn't exist")
     60                 self.err_not_found(abs_path)
     61 
     62             # It's a file.
     63             elif os.path.isfile(abs_path):
     64                 self.log("abs_path is a file")
     65                 self.handle_file(abs_path)
     66 
     67             # It's a directory.
     68             elif os.path.isdir(abs_path):
     69                 self.log("abs_path is a directory")
     70                 self.handle_dir(abs_path)
     71 
     72             # ...we can't tell.
     73             else:
     74                 self.log("can't tell what abs_path is")
     75                 self.err_not_found(abs_path)
     76 
     77         # Handle general errors.
     78         except Exception as msg:
     79             self.err_internal("Unexpected exception: %s" % msg)
     80 
     81     def create_abs_path(self):
     82         head = os.path.abspath(self.Root_Directory)
     83         result = os.path.normpath(head + self.path)
     84         return result
     85 
     86     def is_parent_dir(self, left, right):
     87         return os.path.commonprefix([left, right]) == left
     88 
     89     # Guess the MIME type of a file from its name.
     90     def guess_file_type(self, path):
     91         base, ext = os.path.splitext(path)
     92         if ext in self.File_Types:
     93             return self.File_Types[ext]
     94         ext = ext.lower()
     95         if ext in self.File_Types:
     96             return self.File_Types[ext]
     97         return self.File_Types['']
     98 
     99     # Handle files.  Must read in binary mode!
    100     def handle_file(self, abs_path):
    101         try:
    102             input = open(abs_path, "rb")
    103             content = input.read()
    104             input.close()
    105             fileType = self.guess_file_type(abs_path)
    106             print(fileType)
    107             self.send_content(content, fileType)
    108         except IOError as msg:
    109             msg = "'%s' cannot be read: %s" % (self.path, msg)
    110             self.err_no_perm(msg)
    111 
    112     # Handle directories.
    113     def handle_dir(self, abs_path):
    114         try:
    115             listing = os.listdir(abs_path)
    116             filler = '
    '.join([(self.Listing_Item % item) for item in listing])
    117             content = self.Listing_Page % {'path'   : self.path,
    118                                            'filler' : filler}
    119             print(type(content))                               
    120             self.send_content(content.encode())
    121         except IOError as msg:
    122             msg = "'%s' cannot be listed: %s" % (self.path, msg)
    123             self.err_no_perm(msg)
    124 
    125     # Send actual content.
    126     def send_content(self, content, fileType="text/html"):
    127         length = str(len(content))
    128         self.log("sending content, fileType '%s', length %s" % (fileType, length))
    129         self.send_response(200)
    130         self.send_header("Content-type", fileType)
    131         self.send_header("Content-Length", length)
    132         self.end_headers()
    133         print(type(self.wfile),type(content))
    134         self.wfile.write(content)
    135 
    136     # Report internal errors.
    137     def err_internal(self, msg):
    138         self.send_error(self.ERR_INTERNAL, msg)
    139 
    140     # Handle missing object errors.
    141     def err_not_found(self, abs_path):
    142         self.send_error(self.ERR_NOT_FOUND, "'%s' not found" % self.path)
    143 
    144     # Handle no permission errors.
    145     def err_no_perm(self, msg):
    146         self.send_error(self.ERR_NO_PERM, msg)
    147 
    148     # Write a log message if in debugging mode
    149     def log(self, msg):
    150         if self.Debug:
    151             print(msg)
    152 
    153 #----------------------------------------------------------------------
    154 
    155 if __name__ == '__main__':
    156 
    157     # Main libraries
    158     import getopt
    159 
    160     # How to use
    161     Usage = "server.py [-v] root_directory"
    162 
    163     # Handle command-line arguments
    164     options, rest = getopt.getopt(sys.argv[1:], "v")
    165 
    166     for (flag, arg) in options:
    167         if flag == "-v":
    168             RequestHandler.Debug = True
    169         else:
    170             print(Usage, file=sys.stderr)
    171             sys.exit(1)
    172 
    173     if not rest:
    174         print(Usage, file=sys.stderr)
    175         sys.exit(1)
    176     root = os.path.abspath(rest[0])
    177     if not os.path.isdir(root):
    178         print("No such directory '%s'" % root, file=sys.stderr)
    179         sys.exit(1)
    180     RequestHandler.Root_Directory = root
    181 
    182     # Create and run server.
    183     server_address = ('', 8080)
    184     server = http.server.HTTPServer(server_address, RequestHandler)
    185     server.serve_forever()

    -----------------------------------------------------------------------------------------------------------------------------------------------------------------

    以上的例子中,我们返回给客户端的内容,不管是txt也好,html也好,png也好,都可以看做是静态的文件,是不能被执行的.以下这个例子通过subprocess模块的Popen()可以执行一个.py文件,然后将执行的py文件产生的stdout内容返回给浏览器.有关subprocess的内容可以看这里:https://docs.python.org/3.4/library/subprocess.html

      1 import sys, os, http.server, mimetypes, gettext,subprocess
      2 
      3 class RequestHandler(http.server.BaseHTTPRequestHandler):
      4 
      5     # Root of files being served.
      6     Root_Directory = None
      7 
      8     # Is debugging output on?
      9     Debug = False
     10 
     11     # HTTP error codes.
     12     ERR_NO_PERM   = 403
     13     ERR_NOT_FOUND = 404
     14     ERR_INTERNAL  = 500
     15 
     16     # MIME types of files.
     17     File_Types = mimetypes.types_map
     18 
     19     # Filename extensions that identify executables.
     20     Exec_Extensions = {
     21         ".py" : None
     22     }
     23 
     24     # Classify and handle request.
     25     def do_GET(self):
     26 
     27         # Handle any errors that arise.
     28         try:
     29 
     30             # Class not yet initialized.
     31             if self.Root_Directory is None:
     32                 self.err_internal("Root directory not set")
     33                 return
     34 
     35             # Figure out what exactly is being requested.
     36             abs_path, query_params = self.parse_path()
     37             self.log("abs_path is '%s'" % abs_path)
     38             self.log("query_params is '%s'" % query_params)
     39 
     40             # It isn't below the root path.
     41             if not self.is_parent_dir(self.Root_Directory, abs_path):
     42                 self.log("abs_path not below root directory")
     43                 self.err_no_perm("Path '%s' not below root directory '%s'" % 
     44                                  (abs_path, self.Root_Directory))
     45 
     46             # It doesn't exist.
     47             elif not os.path.exists(abs_path):
     48                 self.log("abs_path doesn't exist")
     49                 self.err_not_found(abs_path)
     50 
     51             # It's a file. (Ignore query parameters if the file is
     52             # not being executed.)
     53             elif os.path.isfile(abs_path):
     54                 if self.is_executable(abs_path):
     55                     self.log("abs_path is an executable")
     56                     self.handle_executable(abs_path, query_params)
     57                 else:
     58                     self.log("abs_path is a file")
     59                     self.handle_static_file(abs_path)
     60 
     61             # It's a directory --- ignore query parameters.
     62             elif os.path.isdir(abs_path):
     63                 self.log("abs_path is a directory")
     64                 self.handle_dir(abs_path)
     65 
     66             # ...we can't tell.
     67             else:
     68                 self.log("can't tell what abs_path is")
     69                 self.err_not_found(abs_path)
     70 
     71         # Handle general errors.
     72         except Exception as msg:
     73             self.err_internal("Unexpected exception in main despatch: %s" % msg)
     74 
     75     def parse_path(self):
     76         '''Create the absolute path for a request, and extract the query
     77         parameter string (if any).'''
     78         parts = self.path.split("?")
     79         if len(parts) == 1:
     80             request_path, queryString = self.path, ""
     81         elif len(parts) == 2:
     82             request_path, queryString = parts
     83         else:
     84             pass
     85         head = os.path.abspath(self.Root_Directory)
     86         result = os.path.normpath(head + request_path)
     87         return result, queryString
     88 
     89     def is_parent_dir(self, left, right):
     90         return os.path.commonprefix([left, right]) == left
     91 
     92     def guess_file_type(self, path):
     93         base, ext = os.path.splitext(path)
     94         if ext in self.File_Types:
     95             return self.File_Types[ext]
     96         ext = ext.lower()
     97         if ext in self.File_Types:
     98             return self.File_Types[ext]
     99         return self.File_Types['']
    100 
    101     def is_executable(self, abs_path):
    102         '''Does this path map to an executable file?'''
    103         root, ext = os.path.splitext(abs_path)
    104         return ext in self.Exec_Extensions
    105 
    106     def handle_static_file(self, abs_path):
    107         '''Handle static files.  Must read in binary mode!'''
    108         try:
    109             input = file(abs_path, "rb")
    110             content = input.read()
    111             input.close()
    112             file_type = self.guess_file_type(abs_path)
    113             self.send_content(content, file_type)
    114         except IOError as msg:
    115             self.err_no_perm("'%s' cannot be read: %s" % (self.path, msg))
    116 
    117     # Handle directories.
    118     def handle_dir(self, abs_path):
    119 
    120         # How to display a single item in a directory listing.
    121         listing_item = "<li>%s</li>"
    122 
    123         # How to display a whole page of listings.
    124         listing_page = 
    125             "<html>" + 
    126             "<body>" + 
    127             "<h1>Listing for " + "%(path)s" + "</h1>" + 
    128             "<ul>" + 
    129             "%(filler)s" + 
    130             "</ul>" + 
    131             "</body>" + 
    132             "</html>"
    133 
    134         try:
    135             listing = os.listdir(abs_path)
    136             filler = '
    '.join([(listing_item % item) for item in listing])
    137             content = listing_page % {'path'   : self.path,
    138                                       'filler' : filler}
    139             self.send_content(content)
    140         except IOError as msg:
    141             self.err_no_perm("'%s' cannot be listed: %s" % msg)
    142 
    143     # Handle executable file.
    144     def handle_executable(self, abs_path, query_params):
    145         # Passing query parameters?
    146         if query_params:
    147             os.environ["REQUEST_METHOD"] = "GET"
    148             os.environ["QUERY_STRING"] = query_params
    149         cmd = "python " + abs_path
    150         #p = subprocess.Popen(cmd, shell=True, bufsize=bufsize,stdin=PIPE, stdout=PIPE, close_fds=True)
    151         p = subprocess.Popen(cmd,stdin=subprocess.PIPE, stdout=subprocess.PIPE)
    152         print(type(p),p)
    153         (childInput, childOutput) = (p.stdin,p.stdout)
    154         print(cmd,childInput,childOutput)
    155         #childInput, childOutput = subprocess.popen2(cmd)
    156         childInput.close()
    157         response = childOutput.read()
    158         childOutput.close()
    159         self.log("handle_executable: response length is %d" % len(response))
    160         self.send_response(200)
    161         self.wfile.write(response)
    162 
    163     # Send actual content.
    164     def send_content(self, content, fileType="text/html"):
    165         length = str(len(content))
    166         self.log("sending content, fileType '%s', length %s" % (fileType, length))
    167         self.send_response(200)
    168         self.send_header("Content-type", fileType)
    169         self.send_header("Content-Length", length)
    170         self.end_headers()
    171         self.wfile.write(content)
    172 
    173     # Report internal errors.
    174     def err_internal(self, msg):
    175         self.send_error(self.ERR_INTERNAL, msg)
    176 
    177     # Handle missing object errors.
    178     def err_not_found(self, abs_path):
    179         self.send_error(self.ERR_NOT_FOUND, "'%s' not found" % self.path)
    180 
    181     # Handle no permission errors.
    182     def err_no_perm(self, msg):
    183         self.send_error(self.ERR_NO_PERM, msg)
    184 
    185     # Handle execution errors.
    186     def errExec(self, msg):
    187         self.send_error(self.ERR_NO_PERM, msg)
    188 
    189     # Write a log message if in debugging mode
    190     def log(self, msg):
    191         if self.Debug:
    192             print("nitinat:", msg)
    193 
    194 #----------------------------------------------------------------------
    195 
    196 if __name__ == '__main__':
    197 
    198     # Main libraries
    199     import getopt
    200 
    201     # How to handle fatal startup errors
    202     def fatal(msg):
    203         print("nitinat:", msg, file=sys.stderr)
    204         sys.exit(1)
    205 
    206     # Defaults
    207     host = ''
    208     port = 8080
    209     root = None
    210 
    211     # How to use
    212     Usage = "server.py [-h host] [-p port] [-v] -r|Root_Directory"
    213 
    214     # Handle command-line arguments
    215     options, rest = getopt.getopt(sys.argv[1:], "h:p:rv")
    216 
    217     for (flag, arg) in options:
    218         if flag == "-h":
    219             host = arg
    220             if not arg:
    221                 msg = "No host given with -h"
    222                 fatal(msg)
    223         elif flag == "-p":
    224             try:
    225                 port = int(arg)
    226             except ValueError as msg:
    227                 fatal("Unable to convert '%s' to integer: %s" % (arg, msg))
    228         elif flag == "-r":
    229             root = os.getcwd()
    230         elif flag == "-v":
    231             RequestHandler.Debug = True
    232         else:
    233             fatal(Usage)
    234 
    235     # Make sure root directory is set, and is a directory.
    236     if (root and rest) or (not root and not rest):
    237         fatal(Usage)
    238     if not root:
    239         root = os.path.abspath(rest[0])
    240     if not os.path.isdir(root):
    241         fatal("No such directory '%s'" % root)
    242     RequestHandler.Root_Directory = root
    243 
    244     # Create and run server.
    245     server_address = (host, port)
    246     server = http.server.HTTPServer(server_address, RequestHandler)
    247     server.serve_forever()

    以上就是这个500行的简易小项目的全部内容了,可以看出来,虽然每一个程序都越来越复杂,其实程序的骨架并没有变,我们不断丰富的只是解析出请求后具体的处理逻辑代码而已,由于python的标准库已经帮我们做了许多诸如解析请求啊,回发内容啊等等这些内容,我们用python写起web来还是蛮容易的.虽然只是很简单的一些小程序,但是已经描述出了基本的web的写法,还是值得一看的.从git上下载该项目以后,里面的chapter.md可以好好看一看,里面简要介绍了web和HTTP协议的一些知识.你可以用markdownpad或者直接在线https://www.zybuluo.com/mdeditor来阅读这个.md文件.

  • 相关阅读:
    jsp中一个标签两种方式绑定两个click事件导致未执行的问题
    Chrome浏览器自动填充<input>标签的密码
    js中字符串的replace方法区分单双引号
    注册页面-省市联动
    mac OS X下制定ll指令
    ssh通过密钥免密登录linux服务器
    mac OS X下PhpStorm+MAMP PRO+Xdebug+FireFox集成开发和断点调试环境配置
    ubuntn配置桌面快捷方式
    centos安装Redis
    Spring文件下载与上传 FormData
  • 原文地址:https://www.cnblogs.com/sdu20112013/p/4142047.html
Copyright © 2020-2023  润新知