Moved mpd session to mopidy.frontends.mpd

This commit is contained in:
Thomas Adamcik 2011-06-17 01:44:22 +02:00
parent 9df16e0716
commit 74aa96b300
6 changed files with 66 additions and 121 deletions

View File

@ -1,14 +1,19 @@
import asyncore
import gobject
import logging
import sys
from pykka.actor import ThreadingActor
from mopidy.frontends.base import BaseFrontend
from mopidy.frontends.mpd.server import MpdServer
from mopidy.utils.process import BaseThread
from mopidy import settings
from mopidy.utils import network
from mopidy.frontends.mpd.dispatcher import MpdDispatcher
from mopidy.frontends.mpd.protocol import ENCODING, VERSION, LINE_TERMINATOR
from mopidy.utils.log import indent
logger = logging.getLogger('mopidy.frontends.mpd')
# FIXME no real need for frontend to be threading actor
class MpdFrontend(ThreadingActor, BaseFrontend):
"""
The MPD frontend.
@ -25,22 +30,61 @@ class MpdFrontend(ThreadingActor, BaseFrontend):
"""
def __init__(self):
self._thread = None
hostname = network.format_hostname(settings.MPD_SERVER_HOSTNAME)
port = settings.MPD_SERVER_PORT
try:
network.Listener(hostname, port, session=MpdSession)
except IOError, e:
logger.error(u'MPD server startup failed: %s', e)
sys.exit(1)
logger.info(u'MPD server running at [%s]:%s', hostname, port)
class MpdSession(ThreadingActor):
"""
The MPD client session. Keeps track of a single client session. Any
requests from the client is passed on to the MPD request dispatcher.
"""
def __init__(self, sock, addr):
self.sock = sock # Prevent premature GC of socket closing it
self.addr = addr
self.channel = gobject.IOChannel(sock.fileno())
self.dispatcher = MpdDispatcher()
def on_start(self):
self._thread = MpdThread()
self._thread.start()
try:
self.send_response([u'OK MPD %s' % VERSION])
self.request_loop()
except gobject.GError:
self.stop()
def on_receive(self, message):
pass # Ignore any messages
def close(self):
self.channel.close()
def request_loop(self):
while True:
data = self.channel.readline()
if not data:
return self.close()
request = data.rstrip(LINE_TERMINATOR).decode(ENCODING)
logger.debug(u'Request from [%s]:%s: %s', self.addr[0],
self.addr[1], indent(request))
response = self.dispatcher.handle_request(request)
self.send_response(response)
class MpdThread(BaseThread):
def __init__(self):
super(MpdThread, self).__init__()
self.name = u'MpdThread'
def run_inside_try(self):
logger.debug(u'Starting MPD server thread')
server = MpdServer()
server.start()
def send_response(self, response):
"""
Format a response from the MPD command handlers and send it to the
client.
"""
if response:
response = LINE_TERMINATOR.join(response)
logger.debug(u'Response to [%s]:%s: %s', self.addr[0],
self.addr[1], indent(response))
response = u'%s%s' % (response, LINE_TERMINATOR)
data = response.encode(ENCODING)
self.channel.write(data)
self.channel.flush()

View File

@ -1,76 +0,0 @@
import logging
import sys
import gobject
from pykka.actor import ThreadingActor
from mopidy import settings
from mopidy.utils import network
from mopidy.frontends.mpd.dispatcher import MpdDispatcher
from mopidy.frontends.mpd.protocol import ENCODING, LINE_TERMINATOR, VERSION
from mopidy.utils.log import indent
logger = logging.getLogger('mopidy.frontends.mpd.server')
class MpdServer(object):
"""
The MPD server. Creates a :class:`mopidy.frontends.mpd.session.MpdSession`
for each client connection.
"""
def start(self):
"""Start MPD server."""
try:
hostname = network.format_hostname(settings.MPD_SERVER_HOSTNAME)
port = settings.MPD_SERVER_PORT
logger.debug(u'MPD server is binding to [%s]:%s', hostname, port)
network.Listener((hostname, port), session=MpdSession)
logger.info(u'MPD server running at [%s]:%s', hostname, port)
except IOError, e:
logger.error(u'MPD server startup failed: %s' %
str(e).decode('utf-8'))
sys.exit(1)
class MpdSession(ThreadingActor):
"""
The MPD client session. Keeps track of a single client session. Any
requests from the client is passed on to the MPD request dispatcher.
"""
def __init__(self, sock, addr):
self.sock = sock # Prevent premature GC
self.addr = addr
self.channel = gobject.IOChannel(sock.fileno())
self.dispatcher = MpdDispatcher(session=self)
def on_start(self):
try:
self.send_response([u'OK MPD %s' % VERSION])
self.request_loop()
except gobject.GError, e:
self.stop()
def request_loop(self):
while True:
logger.debug('Trying to readline')
request = self.channel.readline()[:-1].decode(ENCODING)
logger.debug(u'Request from [%s]:%s: %s', self.addr[0],
self.addr[1], indent(request))
response = self.dispatcher.handle_request(request)
self.send_response(response)
def send_response(self, response):
"""
Format a response from the MPD command handlers and send it to the
client.
"""
if response:
response = LINE_TERMINATOR.join(response)
logger.debug(u'Response to [%s]:%s: %s', self.addr[0],
self.addr[1], indent(response))
response = u'%s%s' % (response, LINE_TERMINATOR)
data = response.encode(ENCODING)
self.channel.write(data)
self.channel.flush()

View File

@ -39,15 +39,15 @@ def format_hostname(hostname):
class Listener(object):
"""Setup listener and register it with gobject loop."""
def __init__(self, addr, session):
def __init__(self, host, port, session):
self.session = session
self.sock = create_socket()
self.sock.setblocking(0)
self.sock.bind(addr)
self.sock.bind((host, port))
self.sock.listen(5)
gobject.io_add_watch(self.sock.fileno(), gobject.IO_IN, self.handle)
logger.debug('Listening on %s using %s', addr, self.session)
logger.debug('Listening on [%s]:%s using %s', host, port, self.session)
def handle(self, fd, flags):
sock, addr = self.sock.accept()

View File

@ -3,7 +3,7 @@ import unittest
from mopidy import settings
from mopidy.frontends.mpd.dispatcher import MpdDispatcher
from mopidy.frontends.mpd.session import MpdSession
from mopidy.frontends.mpd import MpdSession
class AuthenticationTest(unittest.TestCase):
def setUp(self):

View File

@ -4,7 +4,7 @@ import unittest
from mopidy import settings
from mopidy.backends.dummy import DummyBackend
from mopidy.frontends.mpd.dispatcher import MpdDispatcher
from mopidy.frontends.mpd.session import MpdSession
from mopidy.frontends.mpd import MpdSession
from mopidy.mixers.dummy import DummyMixer
class ConnectionHandlerTest(unittest.TestCase):

View File

@ -1,23 +0,0 @@
import unittest
from mopidy import settings
from mopidy.backends.dummy import DummyBackend
from mopidy.frontends.mpd import server
from mopidy.mixers.dummy import DummyMixer
class MpdSessionTest(unittest.TestCase):
def setUp(self):
self.backend = DummyBackend.start().proxy()
self.mixer = DummyMixer.start().proxy()
self.session = server.MpdSession(None, None, (None, None))
def tearDown(self):
self.backend.stop().get()
self.mixer.stop().get()
settings.runtime.clear()
def test_found_terminator_catches_decode_error(self):
# Pressing Ctrl+C in a telnet session sends a 0xff byte to the server.
self.session.input_buffer = ['\xff']
self.session.found_terminator()
self.assertEqual(len(self.session.input_buffer), 0)