Example #1
0
    def __call__(self, environ, start_response):
        """The WSGI application callable"""
        verbose = self._verbose
        requestDict = dict(environ=environ)

        requestID = self._requestID
        self._requestID = requestID + 1
        startTime = time()
        requestTime = startTime
        requestDict['requestID'] = requestID
        requestDict['time'] = requestTime
        requestDict['format'] = 'CGI'
        if verbose:
            uri = requestURI(environ)
            if not self._silentURIs or not self._silentURIs.search(uri):
                requestDict['verbose'] = True
                requestTime = localtime(requestTime)[:6]
                fmt = '{:5d}  {:4d}-{:02d}-{:02d} {:02d}:{:02d}:{:02d}  {}'
                print(fmt.format(requestID, *requestTime, uri))

        requestDict['input'] = environ['wsgi.input']

        streamOut = WSGIStreamOut(start_response,
                                  bufferSize=self._responseBufferSize,
                                  useWrite=self._wsgiWrite,
                                  encoding=self._outputEncoding)
        transaction = self.dispatchRawRequest(requestDict, streamOut)
        try:
            streamOut.close()
            aborted = False
        except ConnectionAbortedError:
            aborted = True

        if verbose and requestDict.get('verbose'):
            endTime = time()
            duration = round((endTime - startTime) * 1000)
            if aborted:
                error = '* connection aborted *'
            else:
                error = requestURI(environ)
            fmt = '{:5d}  {:16.0f} ms  {}\n'
            print(fmt.format(requestID, duration, error))

        transaction._application = None
        transaction.die()
        del transaction

        return streamOut.iterable()
Example #2
0
    def endRequest(self, error=None):
        """Track end of a raw request.

        Subclasses can use and override this method.
        """
        if self._verbose:
            requestDict = self._requestDict
            if requestDict.get('verbose'):
                requestID = requestDict['requestID']
                duration = round((time() - requestDict['time']) * 1000)
                if not error:
                    env = requestDict.get('environ')
                    error = requestURI(env) if env else '-'
                print '%5d  %14.0f msec  %s\n' % (requestID, duration, error)
Example #3
0
    def endRequest(self, error=None):
        """Track end of a raw request.

        Subclasses can use and override this method.
        """
        if self._verbose:
            requestDict = self._requestDict
            if requestDict.get('verbose'):
                requestID = requestDict['requestID']
                duration = round((time() - requestDict['time'])*1000)
                if not error:
                    env = requestDict.get('environ')
                    error = requestURI(env) if env else '-'
                print '%5d  %14.0f msec  %s\n' % (
                    requestID, duration, error)
Example #4
0
	def handleRequest(self):
		"""Handle a request.

		Actually performs the request, creating the environment and calling
		self.doTransaction(env, input) to perform the response.

		"""
		self.server_version = 'Webware/' + self._server.version()
		env = {}
		if self.headers.has_key('Content-Type'):
			env['CONTENT_TYPE'] = self.headers['Content-Type']
			del self.headers['Content-Type']
		if self.headers.has_key('Content-Length'):
			env['CONTENT_LENGTH'] = self.headers['Content-Length']
			del self.headers['Content-Length']
		key = 'If-Modified-Since'
		if self.headers.has_key(key):
			env[key] = self.headers[key]
			del self.headers[key]
		self.headersToEnviron(self.headers, env)
		env['REMOTE_ADDR'], env['REMOTE_PORT'] = map(str, self.client_address)
		env['REQUEST_METHOD'] = self.command
		path = self.path
		if path.find('?') != -1:
			env['REQUEST_URI'], env['QUERY_STRING'] = path.split('?', 1)
		else:
			env['REQUEST_URI'] = path
			env['QUERY_STRING'] = ''
			env['SCRIPT_NAME'] = ''
		env['PATH'] = os.getenv('PATH', '')
		env['PATH_INFO'] = env['REQUEST_URI']
		env['SERVER_ADDR'], env['SERVER_PORT'] = map(str, self._serverAddress)
		env['SERVER_SOFTWARE'] = self.server_version
		env['SERVER_PROTOCOL'] = self.protocol_version
		env['GATEWAY_INTERFACE'] = 'CGI/1.1'
		if self._server._verbose:
			uri = requestURI(env)
			startTime = time.time()
			sys.stdout.write('%5i  %s  %s\n'
				% (self._requestID, timestamp()['pretty'], uri))

		self.doTransaction(env, self.rfile)

		if self._server._verbose:
			duration = ('%0.2f secs' % (time.time() - startTime)).ljust(19)
			sys.stdout.write('%5i  %s  %s\n\n'
				% (self._requestID, duration, uri))
Example #5
0
	def handleRequest(self):
		"""Handle request.

		Creates the request dictionary, and creates a `TASStreamOut` object
		for the response, then calls `Application.dispatchRawRequest`, which
		does the rest of the work (here we just clean up after).

		"""
		verbose = self._server._verbose
		self._startTime = time.time()

		requestDict = self.receiveDict()
		if not requestDict:
			return

		if verbose:
			uri = requestDict.has_key('environ') \
				and requestURI(requestDict['environ']) or '-'
			sys.stdout.write('%5i  %s  %s\n'
				% (self._requestID, timestamp()['pretty'], uri))

		requestDict['input'] = self.makeInput()
		requestDict['requestID'] = self._requestID

		streamOut = TASStreamOut(self._sock, bufferSize=self._server._responseBufferSize)
		transaction = self._server._app.dispatchRawRequest(requestDict, streamOut)
		try:
			streamOut.close()
			aborted = False
		except ConnectionAbortedError:
			aborted = True

		try:
			self._sock.shutdown(1)
			self._sock.close()
		except Exception:
			pass

		if verbose:
			duration = ('%0.2f secs' % (time.time() - self._startTime)).ljust(19)
			sys.stdout.write('%5i  %s  %s\n\n' % (self._requestID, duration,
				aborted and '*connection aborted*' or uri))

		transaction._application = None
		transaction.die()
		del transaction
Example #6
0
    def startRequest(self, requestDict=None):
        """Track start of a raw request.

        Subclasses can use and override this method.
        """
        requestDict = requestDict or {}
        requestID = self._requestID
        requestTime = requestDict.get('time') or time()
        requestDict['requestID'] = requestID
        requestDict['time'] = requestTime
        # The request object is stored for tracking/debugging purposes.
        self._requestDict = requestDict
        if self._verbose:
            env = requestDict.get('environ')
            uri = requestURI(env) if env else '-'
            if not self._silentURIs or not self._silentURIs.search(uri):
                requestDict['verbose'] = True
                requestTime = localtime(requestTime)[:6]
                print '%5d  %4d-%02d-%02d %02d:%02d:%02d  %s' % (
                    (requestID,) + requestTime + (uri,))
Example #7
0
    def writeContent(self):
        app = self.application()
        server = app.server()
        request = self.request()
        field = request.field
        myRequestID = request.requestID()
        wr = self.writeln

        try:
            threadHandler = server._threadHandler
            abortRequest = server._canAbortRequest and server.abortRequest
        except AttributeError:
            threadHandler = {}
            abortRequest = None

        maxRequestTime = server.setting('MaxRequestTime', 0) or 0

        try:
            max_duration = int(field('duration'))
        except (KeyError, ValueError):
            max_duration = int(maxRequestTime/2) or 60

        wr('<h2>Current thread status</h2>',
            '<p>Automatic cancelation of long-running requests is controlled by'
            ' the <tt>AppServer.config</tt> setting <tt>MaxRequestTime</tt>.</p>')

        if maxRequestTime:
            if abortRequest:
                wr('<p>Currently, this is set to <b>%d</b> seconds.</p>'
                    % maxRequestTime)
            else:
                wr('<p>Currently, this is set to %d seconds, but inactive.</p>'
                    % maxRequestTime)
        else:
            wr('<p>Currently, this setting is disabled.</p>')

        wr('<form action="ThreadControl" method="post">', '<p>')
        if abortRequest:
            wr('<input name="cancel_all" type="submit"'
                ' value="Cancel all requests below">')
            wr('<input name="cancel_selected" type="submit"'
                ' value="Cancel all selected requests">')
        else:
            wr('<p>You need Python 2.3 with <code>ctypes</code> or a'
                ' newer Python version to enable cancelation of threads.</p>')
        wr('<input name="refresh_view" type="submit"'
            ' value="Refresh view">', '</p>')
        if abortRequest:
            wr('<p><input name="cancel_long" type="submit"'
                ' value="Cancel all long-running requests">'
                ' (longer than <input type="text" name="duration" value="%d"'
                ' size="6" maxlength="12" style="text-align: right">'
                ' seconds)</p>' % max_duration)
        wr('<p>You can <a href="Sleep" target="_blank">create a long-running'
            ' request</a> in a separate browser window for testing.</p>'
            '<p>(Your web browser may get stuck if it is waiting for more'
            ' than one of these.)</p>')

        if field('cancel_selected', None):
            killIDs = field('selectedIDs', None) or []
        elif field('cancel_all', None):
            killIDs = field('allIDs', '').split(',')
        elif field('cancel_long', None):
            killIDs = field('longIDs', None) or []
        else:
            killIDs = []
        if not isinstance(killIDs, list):
            killIDs = [killIDs]
        try:
            killIDs = map(int, killIDs)
        except ValueError:
            killIDs = []
        killedIDs = []
        errorIDs = []
        activeIDs = []
        for h in threadHandler.values():
            try:
                activeIDs.append(h._requestID)
            except AttributeError:
                continue
        for requestID in killIDs:
            if (not requestID or requestID == myRequestID
                    or requestID not in activeIDs):
                continue
            if abortRequest:
                try:
                    killed = abortRequest(requestID) == 1
                except Exception:
                    killed = 0
            else:
                killed = 0
            if killed:
                killedIDs.append(requestID)
            else:
                errorIDs.append(requestID)
        if killedIDs:
            msg = (len(killedIDs) > 1 and
                'The following requests have been canceled: %s'
                    % ', '.join(map(str, killedIDs)) or
                'Request %d has been canceled.' % killedIDs[0])
            wr('<p style="color:green">%s</p>' % msg)
            tries = 100
            while tries:
                pendingIDs = []
                for h in threadHandler.values():
                    try:
                        requestID = h._requestID
                        if requestID in killedIDs:
                            pendingIDs.append(requestID)
                    except AttributeError:
                        continue
                if pendingIDs:
                    sleep(0.125)
                    tries -= 1
                else:
                    pendingIDs = []
                    tries = 0
            if pendingIDs:
                msg = (len(pendingIDs) > 1 and
                    'The following of these are still pending: %s'
                        % ', '.join(map(str, pendingIDs)) or
                    'The request is still pending.')
                wr('<p>%s</p><p>You can'
                    ' <a href="ThreadControl">refresh the view<a>'
                    ' to verify cancelation.</p>' % msg)
        if errorIDs:
            msg = (len(errorIDs) > 1 and
                'The following requests could not be canceled: %s'
                    % ', '.join(map(str, errorIDs)) or
                'Request %d could not be canceled.' % errorIDs[0])
            wr('<p style="color:red">%s</p>' % msg)

        curTime = time()
        activeThreads = []
        try:
            threadCount = server._threadCount
        except AttributeError:
            threadCount = 0
        for t, h in threadHandler.items():
            try:
                name = t.getName()
                requestID = h._requestID
                requestDict = h._requestDict
                if requestID != requestDict['requestID']:
                    raise AttributeError
                startTime = requestDict.get('time')
                env = requestDict.get('environ')
                client = env and (env.get('REMOTE_NAME')
                    or env.get('REMOTE_ADDR')) or None
                uri = env and requestURI(env) or None
                activeThreads.append((name, requestID,
                    startTime, curTime - startTime, client, uri))
            except AttributeError:
                continue

        if activeThreads:
            headings = ('Thread name', 'Request ID', 'Start time',
                'Duration', 'Client', 'Request URI')
            sort = field('sort', None)
            wr('<table class="NiceTable"><tr>')
            column = 0
            sort_column = 1
            sort = field('sort', None)
            for heading in headings:
                sort_key = heading.lower().replace(' ', '_')
                if sort_key == sort:
                    sort_column = column
                wr('<th><a href="ThreadControl?sort=%s">%s</a></th>'
                    % (sort_key, heading))
                column += 1
            if abortRequest:
                wr('<th>Cancel</th>')
            wr('</tr>')
            if sort_column:
                def key(t):
                    return t[sort_column], t[1]
            else:
                def key(t):
                    try:
                        n = int(t[0].rsplit('-', 1)[-1])
                    except ValueError:
                        n = 0
                    return n, t[0]
            activeThreads.sort(key=key)
        else:
            wr('<p>Could not determine the active threads.</p>')
        longIDs = []
        for (name, requestID, startTime, duration,
                client, uri) in activeThreads:
            if startTime:
                startTime = strtime(startTime)
                duration = int(duration + 0.5)
                if duration > 0:
                    if duration > max_duration:
                        duration = '<b>%s</b>' % duration
                        if requestID:
                            longIDs.append(requestID)
                    duration = '%s&nbsp;s' % duration
                else:
                    duration = int(1000*(duration) + 0.5)
                    duration = '%s&nbsp;ms' % duration
            else:
                duration = startTime = '-'
            if abortRequest and requestID and requestID != myRequestID:
                checkbox = ('<input type="hidden" name="allIDs" value="%d">'
                    '<input type="checkbox" name="selectedIDs" value="%d">'
                    % (requestID, requestID))
            else:
                checkbox = '&nbsp;'
            if not requestID:
                requestID = '-'
            elif requestID == myRequestID:
                requestID = '<b>%s</b>' % requestID
            if not client:
                client = '-'
            if uri:
                uri = uri.replace('/', '/' + '<wbr>')
            else:
                uri = '-'
            wr('<tr><td align="right">', name,
                '</td><td align="right">', requestID,
                '</td><td>', startTime, '</td><td align="right">', duration,
                '</td><td>', client, '</td><td>', uri, '</td>')
            if abortRequest:
                wr('<td align="center">', checkbox, '</td>')
            wr('</tr>')
        if activeThreads:
            wr('</table>')
        if abortRequest:
            longIDs = ','.join(map(str, longIDs))
            wr('<input type="hidden" name="longIDs" value="%s">' % longIDs)
        wr('</form>')

        if threadCount > len(activeThreads):
            wr('<p>Idle threads waiting for requests: <b>%d</b></p>' %
                (threadCount - len(activeThreads)))
        wr('<p>Current time: %s</p>' % strtime(curTime))
Example #8
0
    def __init__(self, dict={}):
        ##		import pprint
        ##		pprint.pprint(dict)
        Request.__init__(self)
        self._parents = []
        if dict:
            # Dictionaries come in from web server adapters like the CGIAdapter
            assert dict['format'] == 'CGI'
            self._time = dict['time']
            self._environ = dict['environ']
            self._input = dict['input']
            self._fields = FieldStorage.FieldStorage(self._input,
                                                     environ=self._environ,
                                                     keep_blank_values=1,
                                                     strict_parsing=0)
            self._fields.parse_qs()
            self._cookies = Cookie()
            if self._environ.has_key('HTTP_COOKIE'):
                # Protect the loading of cookies with an exception handler, because some cookies
                # from IE are known to break the cookie module.
                try:
                    self._cookies.load(self._environ['HTTP_COOKIE'])
                except:
                    traceback.print_exc(file=sys.stderr)
        else:
            # If there's no dictionary, we pretend we're a CGI script and see what happens...
            import time
            self._time = time.time()
            self._environ = os.environ.copy()
            self._input = None
            self._fields = cgi.FieldStorage(keep_blank_values=1)
            self._cookies = Cookie()

        # Debugging
        if 0:
            f = open('env.text', 'a')
            save = sys.stdout
            sys.stdout = f
            print '>> env for request:'
            keys = self._environ.keys()
            keys.sort()
            for key in keys:
                print '%s: %s' % (repr(key), repr(self._environ[key]))
            print
            sys.stdout = save
            f.close()

        # Fix up environ if it doesn't look right.

        # Fix #1: No PATH_INFO
        # This can happen when there is no extra path info past the adapter.
        # e.g., http://localhost/WebKit.cgi
        if not self._environ.has_key('PATH_INFO'):
            self._environ['PATH_INFO'] = ''

        # Fix #2: No REQUEST_URI
        # REQUEST_URI isn't actually part of the CGI standard and some
        # web servers like IIS don't set it (as of 8/22/2000).
        if not self._environ.has_key('REQUEST_URI'):
            self._environ['REQUEST_URI'] = requestURI(self._environ)

        self._adapterName = self._environ.get('SCRIPT_NAME', '')

        # We use the cgi module to get the fields, but then change them into an ordinary dictionary of values
        try:
            keys = self._fields.keys()
        except TypeError:
            # This can happen if, for example, the request is an XML-RPC request, not
            # a regular POST from an HTML form.  In that case we just create an empty
            # set of fields.
            keys = []
        dict = {}

        for key in keys:
            value = self._fields[key]
            if type(value) is not ListType:
                if value.filename:
                    if debug: print "Uploaded File Found"
                else:
                    value = value.value  # i.e., if we don't have a list, we have one of those cgi.MiniFieldStorage objects. Get it's value.
            else:
                value = map(lambda miniFieldStorage: miniFieldStorage.value,
                            value)  # extract those .value's

            dict[key] = value
        self._fieldStorage = self._fields
        self._fields = dict
        self._pathInfo = None

        # We use Tim O'Malley's Cookie class to get the cookies, but then change them into an ordinary dictionary of values
        dict = {}
        for key in self._cookies.keys():
            dict[key] = self._cookies[key].value
        self._cookies = dict

        self._transaction = None
        self._serverRootPath = ""
        self._extraURLPath = ""

        # try to get automatic path session
        # if UseAutomaticPathSessions is enabled in Application.config
        # Application.py redirects the browser to a url with SID in path
        # http://gandalf/a/_SID_=2001080221301877755/Examples/
        # _SID_ is extracted and removed from path
        self._pathSession = None
        if self._environ['PATH_INFO'][1:6] == '_SID_':
            self._pathSession = self._environ['PATH_INFO'][7:].split('/', 1)[0]
            self._cookies['_SID_'] = self._pathSession
            sidstring = '_SID_=' + self._pathSession + '/'
            self._environ['REQUEST_URI'] = self._environ[
                'REQUEST_URI'].replace(sidstring, '')
            self._environ['PATH_INFO'] = self._environ['PATH_INFO'].replace(
                sidstring, '')
            if self._environ.has_key('PATH_TRANSLATED'):
                self._environ['PATH_TRANSLATED'] = self._environ[
                    'PATH_TRANSLATED'].replace(sidstring, '')
            assert (not self._environ.has_key('WK_URI'))  # obsolete?

        self._sessionExpired = 0

        # Save the original urlPath.
        self._originalURLPath = self.urlPath()

        if debug:
            print "Done setting up request, found keys %s" % repr(
                self._fields.keys())
Example #9
0
    def writeContent(self):
        app = self.application()
        server = app.server()
        request = self.request()
        field = request.field
        myRequestID = request.requestID()
        wr = self.writeln

        threadHandler = server._threadHandler
        abortRequest = server.abortRequest
        maxRequestTime = server.setting('MaxRequestTime', 0) or 0

        try:
            max_duration = int(field('duration'))
        except (KeyError, ValueError):
            max_duration = int(maxRequestTime/2) or 60

        wr('<h2>Current thread status</h2>',
            '<p>Automatic cancelation of long-running requests is controlled by'
            ' the <code>AppServer.config</code> setting <code>MaxRequestTime</code>.</p>')

        if maxRequestTime:
            wr('<p>Currently, this is set to <b>%d</b> seconds.</p>'
                % maxRequestTime)
        else:
            wr('<p>Currently, this setting is disabled.</p>')

        wr('<form action="ThreadControl" method="post">', '<p>')
        wr('<input name="cancel_all" type="submit"'
            ' value="Cancel all requests below">')
        wr('<input name="cancel_selected" type="submit"'
            ' value="Cancel all selected requests">')
        wr('<input name="refresh_view" type="submit"'
            ' value="Refresh view">', '</p>')
        wr('<p><input name="cancel_long" type="submit"'
            ' value="Cancel all long-running requests">'
            ' (longer than <input type="text" name="duration" value="%d"'
            ' size="6" maxlength="12" style="text-align: right">'
            ' seconds)</p>' % max_duration)
        wr('<p>You can <a href="Sleep" target="_blank">create a long-running'
            ' request</a> in a separate browser window for testing.</p>'
            '<p>(Your web browser may get stuck if it is waiting for more'
            ' than one of these.)</p>')

        if field('cancel_selected', None):
            killIDs = field('selectedIDs', None) or []
        elif field('cancel_all', None):
            killIDs = field('allIDs', '').split(',')
        elif field('cancel_long', None):
            killIDs = field('longIDs', None) or []
        else:
            killIDs = []
        if not isinstance(killIDs, list):
            killIDs = [killIDs]
        try:
            killIDs = map(int, killIDs)
        except ValueError:
            killIDs = []
        killedIDs = []
        errorIDs = []
        activeIDs = []
        for h in threadHandler.values():
            try:
                activeIDs.append(h._requestID)
            except AttributeError:
                continue
        for requestID in killIDs:
            if (not requestID or requestID == myRequestID
                    or requestID not in activeIDs):
                continue
            try:
                killed = abortRequest(requestID) == 1
            except Exception:
                killed = 0
            if killed:
                killedIDs.append(requestID)
            else:
                errorIDs.append(requestID)
        if killedIDs:
            msg = (len(killedIDs) > 1 and
                'The following requests have been canceled: %s'
                    % ', '.join(map(str, killedIDs)) or
                'Request %d has been canceled.' % killedIDs[0])
            wr('<p style="color:green">%s</p>' % msg)
            tries = 100
            while tries:
                pendingIDs = []
                for h in threadHandler.values():
                    try:
                        requestID = h._requestID
                        if requestID in killedIDs:
                            pendingIDs.append(requestID)
                    except AttributeError:
                        continue
                if pendingIDs:
                    sleep(0.125)
                    tries -= 1
                else:
                    pendingIDs = []
                    tries = 0
            if pendingIDs:
                msg = (len(pendingIDs) > 1 and
                    'The following of these are still pending: %s'
                        % ', '.join(map(str, pendingIDs)) or
                    'The request is still pending.')
                wr('<p>%s</p><p>You can'
                    ' <a href="ThreadControl">refresh the view<a>'
                    ' to verify cancelation.</p>' % msg)
        if errorIDs:
            msg = (len(errorIDs) > 1 and
                'The following requests could not be canceled: %s'
                    % ', '.join(map(str, errorIDs)) or
                'Request %d could not be canceled.' % errorIDs[0])
            wr('<p style="color:red">%s</p>' % msg)

        curTime = time()
        activeThreads = []
        try:
            threadCount = server._threadCount
        except AttributeError:
            threadCount = 0
        for t, h in threadHandler.items():
            try:
                name = t.getName()
                requestID = h._requestID
                requestDict = h._requestDict
                if requestID != requestDict['requestID']:
                    raise AttributeError
                startTime = requestDict.get('time')
                env = requestDict.get('environ')
                client = (env.get('REMOTE_NAME')
                    or env.get('REMOTE_ADDR')) if env else None
                uri = requestURI(env) if env else None
                activeThreads.append((name, requestID,
                    startTime, curTime - startTime, client, uri))
            except AttributeError:
                continue

        if activeThreads:
            headings = ('Thread name', 'Request ID', 'Start time',
                'Duration', 'Client', 'Request URI')
            sort = field('sort', None)
            wr('<table class="NiceTable"><tr>')
            column = 0
            sort_column = 1
            sort = field('sort', None)
            for heading in headings:
                sort_key = heading.lower().replace(' ', '_')
                if sort_key == sort:
                    sort_column = column
                wr('<th><a href="ThreadControl?sort=%s">%s</a></th>'
                    % (sort_key, heading))
                column += 1
            wr('<th>Cancel</th>')
            wr('</tr>')
            if sort_column:
                def key(t):
                    return t[sort_column], t[1]
            else:
                def key(t):
                    try:
                        n = int(t[0].rsplit('-', 1)[-1])
                    except ValueError:
                        n = 0
                    return n, t[0]
            activeThreads.sort(key=key)
        else:
            wr('<p>Could not determine the active threads.</p>')
        longIDs = []
        for (name, requestID, startTime, duration,
                client, uri) in activeThreads:
            if startTime:
                startTime = strtime(startTime)
                duration = int(duration + 0.5)
                if duration > 0:
                    if duration > max_duration:
                        duration = '<b>%s</b>' % duration
                        if requestID:
                            longIDs.append(requestID)
                    duration = '%s&nbsp;s' % duration
                else:
                    duration = int(1000*(duration) + 0.5)
                    duration = '%s&nbsp;ms' % duration
            else:
                duration = startTime = '-'
            if requestID and requestID != myRequestID:
                checkbox = ('<input type="hidden" name="allIDs" value="%d">'
                    '<input type="checkbox" name="selectedIDs" value="%d">'
                    % (requestID, requestID))
            else:
                checkbox = '&nbsp;'
            if not requestID:
                requestID = '-'
            elif requestID == myRequestID:
                requestID = '<b>%s</b>' % requestID
            if not client:
                client = '-'
            if uri:
                uri = uri.replace('/', '/' + '<wbr>')
            else:
                uri = '-'
            wr('<tr><td style="text-align:right">', name,
                '</td><td style="text-align:right">', requestID,
                '</td><td>', startTime,
                '</td><td style="text-align:right">', duration,
                '</td><td>', client, '</td><td>', uri, '</td>')
            wr('<td style="text-align:center">', checkbox, '</td>')
            wr('</tr>')
        if activeThreads:
            wr('</table>')
        longIDs = ','.join(map(str, longIDs))
        wr('<input type="hidden" name="longIDs" value="%s">' % longIDs)
        wr('</form>')

        if threadCount > len(activeThreads):
            wr('<p>Idle threads waiting for requests: <b>%d</b></p>' %
                (threadCount - len(activeThreads)))
        wr('<p>Current time: %s</p>' % strtime(curTime))