PageRenderTime 26ms CodeModel.GetById 14ms app.highlight 9ms RepoModel.GetById 1ms app.codeStats 0ms

/Lib/SimpleHTTPServer.py

http://unladen-swallow.googlecode.com/
Python | 218 lines | 122 code | 17 blank | 79 comment | 19 complexity | a921195b9bfe76a4e71328cb2d1a0c3d MD5 | raw file
  1"""Simple HTTP Server.
  2
  3This module builds on BaseHTTPServer by implementing the standard GET
  4and HEAD requests in a fairly straightforward manner.
  5
  6"""
  7
  8
  9__version__ = "0.6"
 10
 11__all__ = ["SimpleHTTPRequestHandler"]
 12
 13import os
 14import posixpath
 15import BaseHTTPServer
 16import urllib
 17import cgi
 18import shutil
 19import mimetypes
 20try:
 21    from cStringIO import StringIO
 22except ImportError:
 23    from StringIO import StringIO
 24
 25
 26class SimpleHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
 27
 28    """Simple HTTP request handler with GET and HEAD commands.
 29
 30    This serves files from the current directory and any of its
 31    subdirectories.  The MIME type for files is determined by
 32    calling the .guess_type() method.
 33
 34    The GET and HEAD requests are identical except that the HEAD
 35    request omits the actual contents of the file.
 36
 37    """
 38
 39    server_version = "SimpleHTTP/" + __version__
 40
 41    def do_GET(self):
 42        """Serve a GET request."""
 43        f = self.send_head()
 44        if f:
 45            self.copyfile(f, self.wfile)
 46            f.close()
 47
 48    def do_HEAD(self):
 49        """Serve a HEAD request."""
 50        f = self.send_head()
 51        if f:
 52            f.close()
 53
 54    def send_head(self):
 55        """Common code for GET and HEAD commands.
 56
 57        This sends the response code and MIME headers.
 58
 59        Return value is either a file object (which has to be copied
 60        to the outputfile by the caller unless the command was HEAD,
 61        and must be closed by the caller under all circumstances), or
 62        None, in which case the caller has nothing further to do.
 63
 64        """
 65        path = self.translate_path(self.path)
 66        f = None
 67        if os.path.isdir(path):
 68            if not self.path.endswith('/'):
 69                # redirect browser - doing basically what apache does
 70                self.send_response(301)
 71                self.send_header("Location", self.path + "/")
 72                self.end_headers()
 73                return None
 74            for index in "index.html", "index.htm":
 75                index = os.path.join(path, index)
 76                if os.path.exists(index):
 77                    path = index
 78                    break
 79            else:
 80                return self.list_directory(path)
 81        ctype = self.guess_type(path)
 82        try:
 83            # Always read in binary mode. Opening files in text mode may cause
 84            # newline translations, making the actual size of the content
 85            # transmitted *less* than the content-length!
 86            f = open(path, 'rb')
 87        except IOError:
 88            self.send_error(404, "File not found")
 89            return None
 90        self.send_response(200)
 91        self.send_header("Content-type", ctype)
 92        fs = os.fstat(f.fileno())
 93        self.send_header("Content-Length", str(fs[6]))
 94        self.send_header("Last-Modified", self.date_time_string(fs.st_mtime))
 95        self.end_headers()
 96        return f
 97
 98    def list_directory(self, path):
 99        """Helper to produce a directory listing (absent index.html).
100
101        Return value is either a file object, or None (indicating an
102        error).  In either case, the headers are sent, making the
103        interface the same as for send_head().
104
105        """
106        try:
107            list = os.listdir(path)
108        except os.error:
109            self.send_error(404, "No permission to list directory")
110            return None
111        list.sort(key=lambda a: a.lower())
112        f = StringIO()
113        displaypath = cgi.escape(urllib.unquote(self.path))
114        f.write('<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">')
115        f.write("<html>\n<title>Directory listing for %s</title>\n" % displaypath)
116        f.write("<body>\n<h2>Directory listing for %s</h2>\n" % displaypath)
117        f.write("<hr>\n<ul>\n")
118        for name in list:
119            fullname = os.path.join(path, name)
120            displayname = linkname = name
121            # Append / for directories or @ for symbolic links
122            if os.path.isdir(fullname):
123                displayname = name + "/"
124                linkname = name + "/"
125            if os.path.islink(fullname):
126                displayname = name + "@"
127                # Note: a link to a directory displays with @ and links with /
128            f.write('<li><a href="%s">%s</a>\n'
129                    % (urllib.quote(linkname), cgi.escape(displayname)))
130        f.write("</ul>\n<hr>\n</body>\n</html>\n")
131        length = f.tell()
132        f.seek(0)
133        self.send_response(200)
134        self.send_header("Content-type", "text/html")
135        self.send_header("Content-Length", str(length))
136        self.end_headers()
137        return f
138
139    def translate_path(self, path):
140        """Translate a /-separated PATH to the local filename syntax.
141
142        Components that mean special things to the local file system
143        (e.g. drive or directory names) are ignored.  (XXX They should
144        probably be diagnosed.)
145
146        """
147        # abandon query parameters
148        path = path.split('?',1)[0]
149        path = path.split('#',1)[0]
150        path = posixpath.normpath(urllib.unquote(path))
151        words = path.split('/')
152        words = filter(None, words)
153        path = os.getcwd()
154        for word in words:
155            drive, word = os.path.splitdrive(word)
156            head, word = os.path.split(word)
157            if word in (os.curdir, os.pardir): continue
158            path = os.path.join(path, word)
159        return path
160
161    def copyfile(self, source, outputfile):
162        """Copy all data between two file objects.
163
164        The SOURCE argument is a file object open for reading
165        (or anything with a read() method) and the DESTINATION
166        argument is a file object open for writing (or
167        anything with a write() method).
168
169        The only reason for overriding this would be to change
170        the block size or perhaps to replace newlines by CRLF
171        -- note however that this the default server uses this
172        to copy binary data as well.
173
174        """
175        shutil.copyfileobj(source, outputfile)
176
177    def guess_type(self, path):
178        """Guess the type of a file.
179
180        Argument is a PATH (a filename).
181
182        Return value is a string of the form type/subtype,
183        usable for a MIME Content-type header.
184
185        The default implementation looks the file's extension
186        up in the table self.extensions_map, using application/octet-stream
187        as a default; however it would be permissible (if
188        slow) to look inside the data to make a better guess.
189
190        """
191
192        base, ext = posixpath.splitext(path)
193        if ext in self.extensions_map:
194            return self.extensions_map[ext]
195        ext = ext.lower()
196        if ext in self.extensions_map:
197            return self.extensions_map[ext]
198        else:
199            return self.extensions_map['']
200
201    if not mimetypes.inited:
202        mimetypes.init() # try to read system mime.types
203    extensions_map = mimetypes.types_map.copy()
204    extensions_map.update({
205        '': 'application/octet-stream', # Default
206        '.py': 'text/plain',
207        '.c': 'text/plain',
208        '.h': 'text/plain',
209        })
210
211
212def test(HandlerClass = SimpleHTTPRequestHandler,
213         ServerClass = BaseHTTPServer.HTTPServer):
214    BaseHTTPServer.test(HandlerClass, ServerClass)
215
216
217if __name__ == '__main__':
218    test()