source: python/vendor/Python-2.7.6/Lib/SimpleHTTPServer.py

Last change on this file was 388, checked in by dmik, 11 years ago

python: Update vendor to 2.7.6.

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