gmyth-stream/server/0.3/lib/request_handler.py
author melunko
Tue Aug 21 16:02:24 2007 +0100 (2007-08-21)
branchtrunk
changeset 814 8a6b143057d7
parent 802 d0e8c542c38e
child 815 7f290a3a34b1
permissions -rw-r--r--
[svn r820] Added gmyth_scheduler_add_exception() function to remove only one schedule when all occurrences options has been used.
morphbr@718
     1
#!/usr/bin/env python
morphbr@718
     2
morphbr@718
     3
__author__ = "Gustavo Sverzut Barbieri / Artur Duque de Souza"
morphbr@718
     4
__author_email__ = "barbieri@gmail.com / artur.souza@indt.org.br"
morphbr@718
     5
__license__ = "GPL"
morphbr@723
     6
__version__ = "0.3"
morphbr@718
     7
morphbr@718
     8
import os
morphbr@723
     9
import cgi
morphbr@723
    10
import socket
morphbr@723
    11
import logging
morphbr@723
    12
import urlparse
morphbr@718
    13
import threading
morphbr@718
    14
import SocketServer
morphbr@718
    15
import BaseHTTPServer
renatofilho@800
    16
import mimetypes
morphbr@723
    17
morphbr@718
    18
import lib.utils as utils
morphbr@723
    19
import lib.file_handler as files
morphbr@723
    20
import lib.transcoder as transcoder
morphbr@718
    21
morphbr@718
    22
from log import Log
morphbr@718
    23
morphbr@718
    24
__all__ = ("RequestHandler")
morphbr@718
    25
morphbr@718
    26
class RequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
morphbr@718
    27
    """Class that implements an HTTP request handler for our server."""
morphbr@718
    28
    log = logging.getLogger("gms.request")
morphbr@718
    29
    def_transcoder = None
morphbr@718
    30
    transcoders = utils.PluginSet(transcoder.Transcoder)
morphbr@718
    31
    transcoders_log = Log()
morphbr@718
    32
    tid_queue = []
morphbr@718
    33
morphbr@718
    34
    menu = {
morphbr@718
    35
        "Log": "/get_log.do",
morphbr@718
    36
        "Stop": "/stop-transcoder.do",
morphbr@718
    37
        "Status": "/status.do",
morphbr@718
    38
        "All Log": "/get_all_log.do",
morphbr@718
    39
        "Version": "/version.do",
morphbr@718
    40
        "Shutdown": "/shutdown.do"
morphbr@718
    41
        }
morphbr@718
    42
morphbr@718
    43
    @classmethod
morphbr@718
    44
    def load_plugins_transcoders(cls, directory):
morphbr@718
    45
        cls.transcoders.load_from_directory(directory)
morphbr@718
    46
morphbr@718
    47
        if cls.def_transcoder is None and cls.transcoders:
morphbr@718
    48
            cls.def_transcoder = cls.transcoders[0].name
morphbr@718
    49
    # load_plugins_transcoders()
morphbr@718
    50
morphbr@718
    51
morphbr@718
    52
    def do_dispatch(self, body):
morphbr@718
    53
        self.url = self.path
morphbr@718
    54
        pieces = urlparse.urlparse(self.path)
morphbr@718
    55
        self.path = pieces[2]
morphbr@718
    56
        self.query = cgi.parse_qs(pieces[4])
morphbr@718
    57
morphbr@723
    58
        url = {
morphbr@723
    59
            "/": self.serve_main,
morphbr@723
    60
            "/shutdown.do": self.serve_shutdown,
morphbr@723
    61
            "/stop-transcoder.do": self.serve_stop_transcoder,
morphbr@723
    62
            "/status.do": self.serve_status,
morphbr@723
    63
            "/version.do": self.serve_version,
morphbr@723
    64
            "/new_id.do": self.serve_new_id,
morphbr@723
    65
            "/get_log.do": self.serve_get_log,
morphbr@723
    66
            "/get_all_log.do": self.serve_get_all_log,
morphbr@723
    67
            "/stream.do": self.serve_stream,
renatofilho@800
    68
            "/transcode.do": self.serve_transcode,
morphbr@723
    69
            "/list.do": self.serve_list,
morphbr@773
    70
            "/get_file_info.do": self.serve_file_info,
morphbr@723
    71
            }
morphbr@723
    72
morphbr@723
    73
        try:
morphbr@723
    74
            url[self.path](body)
morphbr@748
    75
        except KeyError:
morphbr@744
    76
            try:
morphbr@744
    77
                action = self.query.get("action", None)
morphbr@744
    78
                if action and "stream.do" in action:
morphbr@744
    79
                    self.serve_stream(body)
morphbr@744
    80
                elif os.path.exists("html/%s" % self.path):
morphbr@744
    81
                    data = open("html/%s" % self.path)
morphbr@744
    82
                    self.wfile.write(data.read())
morphbr@744
    83
                else:
morphbr@744
    84
                    self.send_error(404, "File not found")
morphbr@744
    85
            except Exception, e:
morphbr@744
    86
                self.log.error(e)
morphbr@723
    87
morphbr@718
    88
    # do_dispatch()
morphbr@718
    89
morphbr@718
    90
morphbr@718
    91
    def do_GET(self):
morphbr@718
    92
        self.do_dispatch(True)
morphbr@718
    93
    # do_GET()
morphbr@718
    94
morphbr@718
    95
morphbr@718
    96
    def do_HEAD(self):
morphbr@718
    97
        self.do_dispatch(False)
morphbr@718
    98
    # do_HEAD()
morphbr@718
    99
morphbr@718
   100
morphbr@718
   101
    def _nav_items(self):
morphbr@718
   102
        ret = ""
morphbr@718
   103
        for name, url in self.menu.items():
morphbr@718
   104
            ret += utils.getHTML("menu", {"name": name, "url": url})
morphbr@718
   105
        return ret
morphbr@718
   106
    # _nav_items()
morphbr@718
   107
morphbr@723
   108
morphbr@718
   109
    def serve_main(self, body):
morphbr@718
   110
        self.send_response(200)
morphbr@718
   111
        self.send_header("Content-Type", "text/html")
morphbr@718
   112
        self.send_header('Connection', 'close')
morphbr@718
   113
        self.end_headers()
morphbr@718
   114
        if body:
morphbr@718
   115
            self.wfile.write(utils.getHTML("index", {"menu": self._nav_items()}))
morphbr@718
   116
    # serve_main()
morphbr@718
   117
morphbr@723
   118
morphbr@718
   119
    def serve_version(self, body):
morphbr@718
   120
        self.send_response(200)
morphbr@718
   121
        self.send_header("Content-Type", "text/html")
morphbr@718
   122
        self.send_header('Connection', 'close')
morphbr@718
   123
        self.end_headers()
morphbr@718
   124
        if body:
morphbr@718
   125
            self.wfile.write("Version: %s" %  __version__)
morphbr@723
   126
    # serve_version
morphbr@718
   127
morphbr@718
   128
morphbr@718
   129
    def serve_shutdown(self, body):
morphbr@718
   130
        self.send_response(200)
morphbr@718
   131
        self.send_header("Content-Type", "text/html")
morphbr@718
   132
        self.send_header('Connection', 'close')
morphbr@718
   133
        self.end_headers()
morphbr@718
   134
        if body:
morphbr@718
   135
            self.wfile.write(utils.getHTML("shutdown"))
morphbr@718
   136
        self.server.server_close()
morphbr@718
   137
    # serve_shutdown()
morphbr@718
   138
morphbr@718
   139
morphbr@723
   140
    def serve_list(self, body):
morphbr@723
   141
        self.send_response(200)
morphbr@723
   142
        self.send_header("Content-Type", "text/html")
morphbr@723
   143
        self.send_header('Connection', 'close')
morphbr@723
   144
        self.end_headers()
morphbr@724
   145
morphbr@724
   146
        if body:
morphbr@723
   147
            file_list = []
morphbr@724
   148
            files.list_media_files(".transcoded", file_list)
morphbr@723
   149
            output = files.FileList(map(lambda x, y: x+y, file_list,
morphbr@723
   150
                                        ["<br>"]*len(file_list)))
morphbr@724
   151
            self.wfile.write(output)
morphbr@723
   152
morphbr@723
   153
    # serve_list()
morphbr@723
   154
morphbr@723
   155
morphbr@718
   156
    def serve_stop_all_transcoders(self, body):
morphbr@718
   157
        self.send_response(200)
morphbr@718
   158
        self.send_header("Content-Type", "text/html")
morphbr@718
   159
        self.send_header('Connection', 'close')
morphbr@718
   160
        self.end_headers()
morphbr@718
   161
        if body:
morphbr@718
   162
            self.server.stop_transcoders()
morphbr@723
   163
            self.wfile.write(utils.getHTML("stop_all",
morphbr@723
   164
                                           {"menu": self._nav_items()}))
morphbr@718
   165
    # serve_stop_all_transcoders()
morphbr@718
   166
morphbr@718
   167
morphbr@718
   168
    def serve_stop_selected_transcoders(self, body, tids=[]):
morphbr@718
   169
        self.send_response(200)
morphbr@718
   170
        self.send_header("Content-Type", "text/html")
morphbr@718
   171
        self.send_header('Connection', 'close')
morphbr@718
   172
        self.end_headers()
morphbr@718
   173
        opts = ""
morphbr@718
   174
        if body:
morphbr@718
   175
            transcoders = self.server.get_transcoders()
morphbr@718
   176
morphbr@718
   177
            for tid in tids:
morphbr@718
   178
                for t, r in transcoders:
morphbr@718
   179
                    if t.tid == int(tid):
morphbr@718
   180
                        try:
morphbr@718
   181
                            t.stop()
morphbr@718
   182
                        except Exception, e:
morphbr@718
   183
                            self.log.info("Plugin already stopped")
morphbr@718
   184
morphbr@718
   185
                        opts += utils._create_html_item("%s" % t)
morphbr@718
   186
morphbr@718
   187
                        break
morphbr@718
   188
morphbr@718
   189
                self.wfile.write(utils.getHTML("stop_selected",
morphbr@718
   190
                                               {"menu": self._nav_items(),
morphbr@718
   191
                                                "opts": opts}))
morphbr@718
   192
    # serve_stop_selected_transcoders()
morphbr@718
   193
morphbr@718
   194
morphbr@718
   195
    def serve_stop_transcoder(self, body):
morphbr@718
   196
        req = self.query.get("request", None)
morphbr@718
   197
        tid = self.query.get("tid", None)
morphbr@718
   198
        if req and "all" in req:
morphbr@718
   199
            self.serve_stop_all_transcoders(body)
morphbr@718
   200
        elif tid:
morphbr@718
   201
            self.serve_stop_selected_transcoders(body, tid[0].split(";"))
morphbr@718
   202
        else:
morphbr@718
   203
            self.serve_status(body)
morphbr@718
   204
    # serve_stop_transcoder()
morphbr@718
   205
morphbr@718
   206
morphbr@718
   207
    def serve_status(self, body):
morphbr@718
   208
        self.send_response(200)
morphbr@718
   209
        self.send_header("Content-Type", "text/html")
morphbr@718
   210
        self.send_header('Connection', 'close')
morphbr@718
   211
        self.end_headers()
morphbr@718
   212
        stopone = ""
morphbr@732
   213
        running = ""
morphbr@732
   214
        stopall = ""
morphbr@718
   215
morphbr@718
   216
        if body:
morphbr@718
   217
            tl = self.server.get_transcoders()
morphbr@741
   218
            if not tl and not self.query.get("tid", None) and \
morphbr@741
   219
                   not self.query.get("running", None):
morphbr@718
   220
                running = "<p>No running transcoder.</p>\n"
morphbr@718
   221
morphbr@741
   222
            elif not tl and self.query.get("tid", None):
morphbr@724
   223
                tids = self.query.get("tid")
morphbr@724
   224
                for tid in tids:
morphbr@724
   225
                    stat = self.transcoders_log.get_status(int(tid))
morphbr@729
   226
                    self.wfile.write("%s<br>" % stat)
morphbr@743
   227
                return True
morphbr@723
   228
morphbr@741
   229
            elif self.query.get("running", None):
morphbr@741
   230
                for transcoder, request in tl:
morphbr@741
   231
                    outf = transcoder.params_first("outfile")
morphbr@741
   232
                    tid = transcoder.tid
morphbr@741
   233
                    self.wfile.write("%s:%s<br>" % (tid, outf))
morphbr@741
   234
                return True
morphbr@741
   235
morphbr@718
   236
            elif self.query.get("tid", None):
morphbr@724
   237
                req_tid = self.query.get("tid")
morphbr@718
   238
                for transcoder, request in tl:
morphbr@724
   239
                    if str(transcoder.tid) in req_tid:
morphbr@734
   240
                        self.wfile.write("Status:%s:%s %%" % (\
morphbr@724
   241
                            transcoder.tid, transcoder.status))
melunko@795
   242
                        return True
melunko@795
   243
                stat = self.transcoders_log.get_status(int(req_tid[0]))
melunko@795
   244
                self.wfile.write("%s<br>" % stat)
morphbr@724
   245
                return True
morphbr@718
   246
morphbr@718
   247
            else:
morphbr@718
   248
                running = "<p>Running transcoders:</p>\n"
morphbr@718
   249
                stopall = utils._create_html_item("<a href='%s?request=all'>"
morphbr@718
   250
                                                 "[STOP ALL]</a>" %
morphbr@718
   251
                                                 self.menu["Stop"])
morphbr@718
   252
morphbr@718
   253
                for transcoder, request in tl:
morphbr@718
   254
                    stopone += utils._create_html_item("%s;"
morphbr@718
   255
                                                       "<a href='%s?tid=%s'>"
morphbr@718
   256
                                                       " [STOP] </a>") % (
morphbr@718
   257
                        transcoder, self.menu["Stop"], transcoder.tid)
morphbr@718
   258
morphbr@718
   259
            self.wfile.write(utils.getHTML("status",
morphbr@718
   260
                                           {"menu": self._nav_items(),
morphbr@718
   261
                                            "running": running,
morphbr@718
   262
                                            "stopall": stopall,
morphbr@718
   263
                                            "stopone": stopone}))
morphbr@718
   264
    # serve_status()
morphbr@718
   265
morphbr@718
   266
morphbr@718
   267
    def _get_transcoder(self):
morphbr@718
   268
        # get transcoder option: mencoder is the default
morphbr@718
   269
        request_transcoders = self.query.get("transcoder", ["mencoder"])
morphbr@718
   270
morphbr@718
   271
        for t in request_transcoders:
morphbr@718
   272
            transcoder = self.transcoders.get(t)
morphbr@718
   273
            if transcoder:
morphbr@718
   274
                return transcoder
morphbr@718
   275
morphbr@718
   276
        if not transcoder:
morphbr@718
   277
            return self.transcoders[self.def_transcoder]
morphbr@718
   278
    # _get_transcoder()
morphbr@718
   279
morphbr@718
   280
morphbr@718
   281
    def _get_new_id(self, tid):
morphbr@718
   282
        self.server.last_tid = utils.create_tid(tid)
morphbr@718
   283
        self.tid_queue.append(self.server.last_tid)
morphbr@718
   284
        return self.server.last_tid
morphbr@718
   285
    # _get_new_id()
morphbr@718
   286
morphbr@718
   287
morphbr@718
   288
    def serve_new_id(self, body):
morphbr@718
   289
        self.send_response(200)
morphbr@718
   290
        self.send_header("Content-Type", "text/html")
morphbr@718
   291
        self.send_header('Connection', 'close')
morphbr@718
   292
        self.end_headers()
morphbr@718
   293
morphbr@718
   294
        if body:
morphbr@718
   295
            self.wfile.write("%s" % self._get_new_id(self.server.last_tid))
morphbr@718
   296
    # serve_new_id()
morphbr@718
   297
morphbr@718
   298
    def serve_get_log(self, body):
morphbr@718
   299
        self.send_response(200)
morphbr@718
   300
        self.send_header("Content-Type", "text/html")
morphbr@718
   301
        self.send_header('Connection', 'close')
morphbr@718
   302
        self.end_headers()
morphbr@718
   303
morphbr@718
   304
        if body:
morphbr@718
   305
            if self.query.get("tid", None):
morphbr@718
   306
                tid = int(self.query.get("tid")[0])
morphbr@718
   307
                stat = self.transcoders_log.get_status(tid)
morphbr@723
   308
                self.wfile.write("Status: %s" % stat)
morphbr@718
   309
            else:
morphbr@718
   310
                stat = self.transcoders_log.get_status()
morphbr@718
   311
                for rtid, status in stat.iteritems():
morphbr@718
   312
                    self.wfile.write("<b>%s</b>: %s<br><br>" % (rtid, status))
morphbr@718
   313
    # serve_get_log()
morphbr@718
   314
morphbr@718
   315
    def serve_get_all_log(self, body):
morphbr@718
   316
        self.send_response(200)
morphbr@718
   317
        self.send_header("Content-Type", "text/html")
morphbr@718
   318
        self.send_header('Connection', 'close')
morphbr@718
   319
        self.end_headers()
morphbr@718
   320
morphbr@718
   321
        if body:
morphbr@718
   322
            if self.query.get("tid", None):
morphbr@718
   323
                tid = int(self.query.get("tid")[0])
morphbr@718
   324
                stat = self.transcoders_log.get_status(tid, True)
morphbr@718
   325
                for status in stat:
morphbr@718
   326
                    self.wfile.write("%s<br><br>" % status)
morphbr@718
   327
            else:
morphbr@718
   328
                stat = self.transcoders_log.get_status(None, True)
morphbr@718
   329
                for rtid, history in stat.iteritems():
morphbr@718
   330
                    for status in history:
morphbr@718
   331
                        self.wfile.write("<b>%s</b>: %s<br>" % (rtid, status))
morphbr@718
   332
                    self.wfile.write("<br><br>")
morphbr@718
   333
    # serve_get_all_log()
morphbr@718
   334
morphbr@773
   335
morphbr@773
   336
    def serve_file_info(self, body):
morphbr@773
   337
        if body:
morphbr@773
   338
morphbr@773
   339
            file_dat = self.query.get("file", None)
morphbr@773
   340
morphbr@773
   341
            if file_dat:
morphbr@773
   342
                self.send_response(200)
morphbr@773
   343
                self.send_header("Content-Type", "text/html")
morphbr@773
   344
                self.send_header('Connection', 'close')
morphbr@773
   345
                self.end_headers()
morphbr@773
   346
morphbr@773
   347
                try:
morphbr@775
   348
                    opts = files.TranscodedFile(file_dat[0], self.query).opts
morphbr@773
   349
                    for key in opts.keys():
morphbr@775
   350
                        self.wfile.write("%s=%s<br>" % (key, opts.get(key, "None")[0]))
morphbr@775
   351
morphbr@774
   352
                except Exception, e:
morphbr@773
   353
                    self.send_error(500, str(e))
morphbr@773
   354
                    return
morphbr@773
   355
    # serve_file_info()
morphbr@773
   356
morphbr@718
   357
    def serve_stream(self, body):
renatofilho@802
   358
        filename = self.query.get("file", None)[0]
renatofilho@800
   359
renatofilho@802
   360
        if not filename:
renatofilho@802
   361
            self.send_error(404, "File not found")
renatofilho@802
   362
            return
renatofilho@802
   363
renatofilho@802
   364
        #Only stream files on .transcode dir
renatofilho@803
   365
        filename = ".transcoded/" + os.path.basename(filename)
renatofilho@803
   366
        self.log.error("Stream file: %s" % filename)
renatofilho@800
   367
        if not os.path.exists (filename):
renatofilho@800
   368
            self.send_error(404, "File not found")
renatofilho@800
   369
            return
renatofilho@800
   370
renatofilho@800
   371
        size = int(os.path.getsize(filename))
renatofilho@800
   372
        self.send_response(200)
renatofilho@800
   373
        self.send_header("Content-Type", mimetypes.guess_type(filename)[0])
renatofilho@800
   374
        self.send_header("Cache-Control","no-cache")
renatofilho@800
   375
        self.send_header("Content-Length", size)
renatofilho@800
   376
        #self.send_header("Connection", "close")
renatofilho@800
   377
        self.end_headers()
renatofilho@800
   378
renatofilho@800
   379
        media = open(filename)
renatofilho@800
   380
        data_in = " "
renatofilho@800
   381
        total_read = 0
renatofilho@800
   382
renatofilho@800
   383
        test_tid = int(self.query.get("tid", "0")[0])
renatofilho@800
   384
        if test_tid == 0 or test_tid not in self.tid_queue:
renatofilho@800
   385
            test_tid = self._get_new_id(self.server.last_tid)
renatofilho@800
   386
renatofilho@800
   387
        self.transcoders_log.insert(test_tid, "gms.Stream: %s" % filename)
renatofilho@800
   388
renatofilho@800
   389
        try:
renatofilho@803
   390
            file_data = ""
renatofilho@800
   391
            while data_in != "":
renatofilho@800
   392
                data_in = media.read(4096)
renatofilho@803
   393
                file_data += data_in
renatofilho@803
   394
renatofilho@803
   395
                #total_read += 4096
renatofilho@803
   396
            self.wfile.write(file_data)
renatofilho@803
   397
                #status = utils.progress_bar(total_read, size, 50)
renatofilho@803
   398
                #msg_status = "Status:%s:%s%%" % (test_tid, status)
renatofilho@803
   399
                #self.transcoders_log._update_status(test_tid, msg_status)
renatofilho@802
   400
renatofilho@802
   401
            self.transcoders_log._update_status(test_tid, "OK: Done")
renatofilho@800
   402
renatofilho@800
   403
        except Exception, e:
renatofilho@800
   404
            self.log.error("Stream error: %s" %e)
renatofilho@800
   405
            self.transcoders_log._update_status(test_tid, "Error: %s" % e)
renatofilho@800
   406
    # serve_stream()
renatofilho@800
   407
renatofilho@800
   408
    def serve_transcode(self, body):
renatofilho@802
   409
        type = self.query.get("type", None)[0]
renatofilho@802
   410
        if type.upper() == "FILE":
renatofilho@802
   411
            self.send_error(404, "Transcode local files not allowed")
renatofilho@802
   412
            return
renatofilho@802
   413
morphbr@718
   414
        transcoder = self._get_transcoder()
morphbr@718
   415
        try:
morphbr@718
   416
            obj = transcoder(self.query)
morphbr@718
   417
        except Exception, e:
morphbr@718
   418
            self.send_error(500, str(e))
morphbr@718
   419
            return
morphbr@718
   420
morphbr@718
   421
        self.send_response(200)
morphbr@718
   422
        self.send_header("Content-Type", obj.get_mimetype())
renatofilho@800
   423
        self.send_header("Cache-Control","no-cache")
morphbr@765
   424
morphbr@765
   425
        if (obj.name == "gmencoder"):
morphbr@765
   426
            self.send_header("Transfer-Encoding", "chunked")
morphbr@765
   427
renatofilho@802
   428
        #self.send_header("Connection", "close")
morphbr@718
   429
        self.end_headers()
morphbr@718
   430
morphbr@718
   431
        if body:
morphbr@744
   432
            test_tid = int(self.query.get("tid", "0")[0])
morphbr@744
   433
            if test_tid == 0 or test_tid not in self.tid_queue:
morphbr@744
   434
                test_tid = self._get_new_id(self.server.last_tid)
morphbr@744
   435
morphbr@724
   436
            if self.query.get("transcoder", None):
morphbr@724
   437
                self.transcoders_log.insert(test_tid, "gms.%s" % obj.name)
morphbr@724
   438
                obj.tid = test_tid
morphbr@724
   439
                obj.log = self.transcoders_log
morphbr@718
   440
morphbr@724
   441
                self.server.add_transcoders(self, obj)
renatofilho@803
   442
                if obj.start(self.wfile):
renatofilho@803
   443
                    self.transcoders_log.info (test_tid, "OK")
renatofilho@803
   444
                else:
renatofilho@803
   445
                    self.transcoders_log.info (test_tid, "Fail")
renatofilho@803
   446
morphbr@724
   447
                self.server.del_transcoders(self, obj)
morphbr@726
   448
                files.TranscodedFile("", self.query)
morphbr@724
   449
morphbr@718
   450
    # serve_stream()
morphbr@718
   451
morphbr@718
   452
morphbr@718
   453
    def log_request(self, code='-', size='-'):
morphbr@718
   454
        self.log.info('"%s" %s %s', self.requestline, str(code), str(size))
morphbr@718
   455
    # log_request()
morphbr@718
   456
morphbr@718
   457
morphbr@718
   458
    def log_error(self, format, *args):
morphbr@718
   459
        self.log.error("%s: %s" % (self.address_string(), format % args))
morphbr@718
   460
    # log_error()
morphbr@718
   461
morphbr@718
   462
morphbr@718
   463
    def log_message(self, format, *args):
morphbr@718
   464
        self.log.info("%s: %s" % (self.address_string(), format % args))
morphbr@718
   465
    # log_message()
morphbr@724
   466
morphbr@718
   467
# RequestHandler