1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 import socket
19 import resource
20 import time
21
22 try:
23 from twisted.web import http
24 except ImportError:
25 from twisted.protocols import http
26
27 from twisted.web import server, resource as web_resource
28 from twisted.internet import defer
29
30 from flumotion.configure import configure
31 from flumotion.common import log
32
33
34 from flumotion.common import keycards
35
36
37 __all__ = ['HTTPStreamingResource']
38 __version__ = "$Rev$"
39
40
41 ERROR_TEMPLATE = """<!doctype html public "-//IETF//DTD HTML 2.0//EN">
42 <html>
43 <head>
44 <title>%(code)d %(error)s</title>
45 </head>
46 <body>
47 <h2>%(code)d %(error)s</h2>
48 </body>
49 </html>
50 """
51
52
53
54
55 HTTP_VERSION = configure.version
56
57
59 HTTP_NAME = 'FlumotionHTTPServer'
60 HTTP_SERVER = '%s/%s' % (HTTP_NAME, HTTP_VERSION)
61
62 __reserve_fds__ = 50
63
64 logCategory = 'httpstreamer'
65
66
67
68
69 isLeaf = True
70
72 """
73 @param streamer: L{Streamer}
74 """
75 self.streamer = streamer
76 self.httpauth = httpauth
77
78 self._requests = {}
79 self._removing = {}
80
81 self.maxclients = self.getMaxAllowedClients(-1)
82 self.maxbandwidth = -1
83
84
85 self._redirectOnFull = None
86
87 socket = 'flumotion.component.plugs.request.RequestLoggerPlug'
88 self.loggers = streamer.plugs.get(socket, [])
89
90 socket = \
91 'flumotion.component.plugs.requestmodifier.RequestModifierPlug'
92 self.modifiers = streamer.plugs.get(socket, [])
93
94 self.logfilter = None
95
96 web_resource.Resource.__init__(self)
97
106
108 self.putChild(path, self)
109
111 self.logfilter = logfilter
112
114 """
115 Close the logfile, then reopen using the previous logfilename
116 """
117 for logger in self.loggers:
118 self.debug('rotating logger %r' % logger)
119 logger.rotate()
120
121 - def logWrite(self, request, bytes_sent, time_connected):
122 headers = request.getAllHeaders()
123
124 args = {'ip': request.getClientIP(),
125 'time': time.gmtime(),
126 'method': request.method,
127 'uri': request.uri,
128 'username': '-',
129 'get-parameters': request.args,
130 'clientproto': request.clientproto,
131 'response': request.code,
132 'bytes-sent': bytes_sent,
133 'referer': headers.get('referer', None),
134 'user-agent': headers.get('user-agent', None),
135 'time-connected': time_connected}
136
137 args.update(self._getExtraLogArgs(request))
138
139 l = []
140 for logger in self.loggers:
141 l.append(defer.maybeDeferred(
142 logger.event, 'http_session_completed', args))
143
144 return defer.DeferredList(l)
145
147 self.info('setting maxclients to %d' % limit)
148 self.maxclients = self.getMaxAllowedClients(limit)
149
150 self.info('set maxclients to %d' % self.maxclients)
151
153 self.maxbandwidth = limit
154 self.info("set maxbandwidth to %d", self.maxbandwidth)
155
157 self._redirectOnFull = url
158
160 raise NotImplementedError("isReady must be implemented by "
161 "subclasses")
162
164 """
165 maximum number of allowed clients based on soft limit for number of
166 open file descriptors and fd reservation. Increases soft limit to
167 hard limit if possible.
168 """
169 (softmax, hardmax) = resource.getrlimit(resource.RLIMIT_NOFILE)
170 import sys
171 version = sys.version_info
172
173 if maxclients != -1:
174 neededfds = maxclients + self.__reserve_fds__
175
176
177
178
179 if version[:3] == (2, 4, 3) and \
180 not hasattr(socket, "has_2_4_3_patch"):
181 self.warning(
182 'Setting hardmax to 1024 due to python 2.4.3 bug')
183 hardmax = 1024
184
185 if neededfds > softmax:
186 lim = min(neededfds, hardmax)
187 resource.setrlimit(resource.RLIMIT_NOFILE, (lim, hardmax))
188 return lim - self.__reserve_fds__
189 else:
190 return maxclients
191 else:
192 return softmax - self.__reserve_fds__
193
195 """
196 Check whether or not the server reached the limit of concurrent client
197 """
198 if self.maxclients >= 0 and len(self._requests) >= self.maxclients:
199 return True
200 elif self.maxbandwidth >= 0:
201
202 if ((len(self._requests) + 1) *
203 self.streamer.getCurrentBitrate() >= self.maxbandwidth):
204 return True
205 return False
206
208 """
209 Extra arguments for logging. Should be overriden by subclasses
210 that provide extra arguments for logging
211
212 @rtype: dict
213 @returns: A dictionary with the extra arguments
214 """
215 return {}
216
225
227 """
228 Add a request, so it can be used for statistics.
229
230 @param id: the of the client (fd or session id)
231 @type request: int
232 """
233 self._requests[id] = request and request or id
234
236 """
237 Delete a request from the list
238
239 @param request: the id of the client
240 @type request: int
241 """
242 try:
243 del self._requests[id]
244 except Exception:
245 self.warning("Error removing request: %s", id)
246
248 """
249 Returns whether we want to log a request from this IP; allows us to
250 filter requests from automated monitoring systems.
251 """
252 if self.logfilter:
253 return not self.logfilter.isInRange(ip)
254 else:
255 return True
256
257
258
260 self.debug('Not sending data, it\'s not ready')
261 return server.NOT_DONE_YET
262
281