# Orca # # Copyright 2006-2008 Sun Microsystems Inc. # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the # Free Software Foundation, Inc., Franklin Street, Fifth Floor, # Boston MA 02110-1301 USA. """Provides an HTTP server for Orca. This currently serves mainly as something that self-voicing applications can use as their speech service.""" __id__ = "$Id$" __version__ = "$Revision$" __date__ = "$Date$" __copyright__ = "Copyright (c) 2006-2008 Sun Microsystems Inc." __license__ = "LGPL" import threading import BaseHTTPServer import debug import orca_platform import settings import speech _httpRequestThread = None # Handlers for logging speech and braille output. # loggingFileHandlers = {} loggingStreamHandlers = {} class _HTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): """Provides support for communicating with Orca via HTTP. This is mainly to support self-voicing applications that want to use Orca as a speech service. The protocol is simple: POST content is 'stop', 'speak:', or 'isSpeaking'. To test this, run: wget --post-data='speak:hello world' localhost:20433 """ def log_request(self, code=None, size=None): """Override to avoid getting a log message on stdout for each GET, POST, etc. request""" pass def do_GET(self): self.send_response(200) self.send_header("Content-type", "text/html") self.end_headers() self.wfile.write("

Orca %s

" \ % orca_platform.version) def do_POST(self): contentLength = self.headers.getheader('content-length') if contentLength: contentLength = int(contentLength) inputBody = self.rfile.read(contentLength) debug.println(debug.LEVEL_FINEST, "httpserver._HTTPRequestHandler received %s" \ % inputBody) if inputBody.startswith("speak:"): speech.speak(inputBody[6:]) self.send_response(200, 'OK') elif inputBody == "stop": speech.stop() self.send_response(200, 'OK') elif inputBody == "isSpeaking": self.send_response(200, 'OK') self.send_header("Content-type", "text/html") self.end_headers() self.wfile.write("%s" % speech.isSpeaking()) else: debug.println(debug.LEVEL_FINEST, "httpserver._HTTPRequestHandler received no data") class _HTTPRequestThread(threading.Thread): """Runs a _HTTPRequestHandler in a separate thread.""" def run(self): """Try to start an HTTP server on settings.httpServerPort. If this fails, retry settings.maxHttpServerRetries times, each time incrementing the server port number by 1. If we are still unable to start a server, just fail gracefully. """ portNo = settings.httpServerPort connected = False while not connected and \ (portNo < settings.httpServerPort + settings.maxHttpServerRetries): try: httpd = BaseHTTPServer.HTTPServer(('', portNo), _HTTPRequestHandler) connected = True except: if portNo == settings.httpServerPort: debug.printException(debug.LEVEL_WARNING) debug.println(debug.LEVEL_WARNING, "httpserver._HTTPRequestThread unable to start server " \ "on port %d" % portNo) portNo += 1 if not connected: debug.println(debug.LEVEL_WARNING, "httpserver._HTTPRequestThread server startup failed.") else: httpd.serve_forever() def init(): """Creates an HTTP server that listens for speak commands from a separate port defined by settings.httpServerPort. We run this as a daemon so it will die automatically when orca dies.""" global _httpRequestThread if settings.httpServerPort and (not _httpRequestThread): try: _httpRequestThread = _HTTPRequestThread() _httpRequestThread.setDaemon(True) _httpRequestThread.start() except: debug.printException(debug.LEVEL_WARNING) def shutdown(): """Stops the HTTP server. [[[WDW - not implemented yet.]]]""" pass