1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 import string
22 import os
23
24 from flumotion.component import component
25 from flumotion.common import log, messages, errors, netutils
26 from flumotion.component.component import moods
27 from flumotion.component.misc.porter import porterclient
28 from flumotion.component.base import http as httpbase
29 from twisted.web import resource, server, http
30 from twisted.web import error as weberror
31 from twisted.internet import defer, reactor, error, abstract
32 from twisted.python import filepath
33 from flumotion.twisted import fdserver
34 from twisted.cred import credentials
35
36 from twisted.web.static import loadMimeTypes, getTypeAndEncoding
37
38 -class File(resource.Resource, filepath.FilePath, log.Loggable):
89
91 """
92 Now that we're authenticated (or authentication wasn't requested),
93 write the file (or appropriate other response) to the client.
94 We override static.File to implement Range requests, and to get access
95 to the transfer object to abort it later; the bulk of this is a direct
96 copy, though.
97 """
98 self.restat()
99
100 ext = os.path.splitext(self.basename())[1].lower()
101 type = self.contentTypes.get(ext, self.defaultType)
102
103 if not self.exists():
104 self.debug("Couldn't find resource %s", self.basename())
105 return self.childNotFound.render(request)
106
107 if self.isdir():
108 return self.childNotFound.render(request)
109
110
111
112
113
114
115 request.setHeader('Connection', 'close')
116
117 request.setHeader('Accept-Ranges', 'bytes')
118
119 if type:
120 request.setHeader('content-type', type)
121
122 try:
123 f = self.openForReading()
124 except IOError, e:
125 import errno
126 if e[0] == errno.EACCES:
127 return weberror.ForbiddenResource().render(request)
128 else:
129 raise
130
131 if request.setLastModified(self.getmtime()) is http.CACHED:
132 return ''
133
134 tsize = fsize = size = self.getFileSize()
135 range = request.getHeader('range')
136 start = 0
137 if range is not None:
138
139
140
141
142
143
144 bytesrange = string.split(range, '=')
145 if len(bytesrange) != 2:
146 request.setResponseCode(http.REQUESTED_RANGE_NOT_SATISFIABLE)
147 return ''
148
149 start, end = string.split(bytesrange[1], '-', 1)
150 if start:
151 start = int(start)
152 f.seek(start)
153 if end:
154 end = int(end)
155 else:
156 end = size - 1
157 fsize = end - start + 1
158 elif end:
159 lastbytes = int(end)
160 if size < lastbytes:
161 lastbytes = size
162 start = size - lastbytes
163 f.seek(start)
164 fsize = lastbytes
165 end = size - 1
166 else:
167 request.setResponseCode(http.REQUESTED_RANGE_NOT_SATISFIABLE)
168 return ''
169 size = end + 1
170
171 request.setResponseCode(http.PARTIAL_CONTENT)
172 request.setHeader('Content-Range', "bytes %d-%d/%d" %
173 (start, end, tsize))
174
175 request.setHeader("Content-Length", str(fsize))
176
177 if request.method == 'HEAD':
178 return ''
179
180 request._transfer = FileTransfer(f, size, request)
181
182 return server.NOT_DONE_YET
183
188
190 """
191 A class to represent the transfer of a file over the network.
192 """
193 request = None
194
195 - def __init__(self, file, size, request):
196 self.file = file
197 self.size = size
198 self.request = request
199 self.written = self.file.tell()
200 self.bytesWritten = 0
201 request.registerProducer(self, 0)
202
218
221
225