Moved mpd session to mopidy.frontends.mpd
This commit is contained in:
parent
9df16e0716
commit
74aa96b300
@ -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()
|
||||
|
||||
@ -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()
|
||||
@ -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()
|
||||
|
||||
@ -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):
|
||||
|
||||
@ -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):
|
||||
|
||||
@ -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)
|
||||
Loading…
Reference in New Issue
Block a user