Files
Iris/mopidy_iris/core.py

938 lines
30 KiB
Python
Raw Normal View History

2017-02-18 12:45:48 +13:00
from __future__ import unicode_literals
import random, string, logging, json, pykka, pylast, urllib, urllib2, os, sys, mopidy_iris, subprocess
import tornado.web
import tornado.websocket
import tornado.ioloop
import tornado.httpclient
import requests
import time
2017-02-18 12:45:48 +13:00
from mopidy import config, ext
from mopidy.core import CoreListener
from pkg_resources import parse_version
from tornado.escape import json_encode, json_decode
import socket
2017-09-25 14:10:46 -04:00
if sys.platform == 'win32':
import ctypes
2017-02-18 12:45:48 +13:00
# import logger
logger = logging.getLogger(__name__)
class IrisCore(object):
version = 0
2017-09-25 14:10:46 -04:00
if sys.platform == 'win32':
is_root = ctypes.windll.shell32.IsUserAnAdmin() != 0
else:
is_root = os.geteuid() == 0
2017-02-18 12:45:48 +13:00
spotify_token = False
queue_metadata = {}
connections = {}
2017-04-18 08:52:28 +12:00
initial_consume = False
2017-02-18 12:45:48 +13:00
radio = {
"enabled": 0,
"seed_artists": [],
"seed_genres": [],
"seed_tracks": [],
"results": []
2017-02-18 12:45:48 +13:00
}
snapcast_listener = False
##
# Create a new snapcast TCP connection
#
# @return socket
##
def new_snapcast_socket(self):
2018-03-19 16:09:27 +13:00
if not self.config['iris'].get('snapcast_enabled'):
logger.error("Iris Snapcast not enabled")
raise Exception("Snapcast not enabled")
try:
snapcast = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
2018-03-22 16:59:04 +13:00
snapcast.settimeout(10)
snapcast.connect((self.config['iris']['snapcast_host'], self.config['iris']['snapcast_port']))
except socket.gaierror, e:
logger.error("Iris could not connect to Snapcast: %s" % e)
2018-03-09 16:38:20 +13:00
raise Exception(e);
except socket.error, e:
logger.error("Iris could not connect to Snapcast: %s" % e)
2018-03-09 16:38:20 +13:00
raise Exception(e);
return snapcast
2018-03-19 16:09:27 +13:00
##
# Create our ongoing notification listener
#
# TODO: Make non-blocking
##
def create_snapcast_listener(self):
try:
listener = self.new_snapcast_socket()
self.snapcast_listener = listener
logger.info("Iris connected to Snapcast")
except Exception, e:
logger.error("Iris could not connect to Snapcast: %s" % e)
2018-03-09 16:38:20 +13:00
##
# Disconnect our Snapcast listener
##
2018-03-19 16:09:27 +13:00
def snapcast_disconnect_listener(self):
if self.snapcast_listener:
self.snapcast_listener.close()
2018-03-19 16:09:27 +13:00
self.snapcast_listener = None
##
# Handle a message from the Snapcast Telnet API
2018-03-09 16:38:20 +13:00
# We create a connection for this request, and then drop it once completed. This is because
# we have a separate socket for monitoring notifications
#
# @param data = string
##
def snapcast_handle_message(self, data):
2018-03-19 16:09:27 +13:00
logger.debug("Iris received Snapcast message: "+data)
try:
data = json.loads(data)
except:
logger.error("Iris could not digest Snapcast message: "+data)
print "broadcasting..."
print data
self.broadcast(data)
##
# Send a request to Snapcast
2018-03-09 16:38:20 +13:00
#
# We create a connection for this request, and then drop it once completed. This is because
# we have a separate socket for monitoring notifications
##
def snapcast_instruct(self, *args, **kwargs):
callback = kwargs.get('callback', None)
request_id = kwargs.get('request_id', None)
data = kwargs.get('data', {})
2018-03-21 21:40:33 +13:00
request = {
'id': self.generateGuid(),
'jsonrpc': '2.0',
'method': data['method'],
'params': data['params'] if 'params' in data else {}
}
# Convert to string. For some really nuts reason we need an extra trailing curly brace...
2018-03-21 21:40:33 +13:00
request = json.dumps(request)+'}'
# Create our connection
try:
snapcast_socket = self.new_snapcast_socket()
2018-03-19 16:09:27 +13:00
except Exception, e:
callback(response=None, error={
'message': "Could not connect to Snapcast",
2018-03-19 16:09:27 +13:00
'data': str(e)
})
return
# Attempt to send the request
try:
2018-03-21 21:40:33 +13:00
snapcast_socket.send(request.encode('ascii')+b"\n")
except socket.error, e:
logger.error("Iris could not send request to Snapcast: %s" % e)
2018-03-19 16:09:27 +13:00
callback(response=None, error={
'message': "Failed to send request to Snapcast",
'data': str(e)
})
return
# Wait for response
while True:
try:
response = snapcast_socket.recv(8192)
except socket.error, e:
logger.error("Iris failed to receive Snapcast response: %s" % e)
2018-03-19 16:09:27 +13:00
callback(response=None, error={
'message': "Failed to receive Snapcast response",
'data': str(e)
})
snapcast_socket.close()
if not len(response):
break
try:
response = json.loads(response)
2018-03-21 21:40:33 +13:00
if 'result' in response:
callback(response=response['result'])
else:
callback(error=response['error'])
except:
logger.error("Iris received malformed Snapcast response: "+response)
2018-03-19 16:09:27 +13:00
callback(response=None, error={
'message': "Malformed Snapcast response",
'data': response
})
return
return
2017-02-18 12:45:48 +13:00
##
# Generate a random string
#
# Used for connection_ids where none is provided by client
# @return string
##
2018-03-21 21:40:33 +13:00
def generateGuid(self):
length = 12
return ''.join(random.choice(string.lowercase) for i in range(length))
2017-02-18 12:45:48 +13:00
##
# Digest a protocol header into it's id/name parts
#
# @return dict
##
def digest_protocol(self, protocol):
# if we're a string, split into list
# this handles the different ways we get this passed (select_subprotocols gives string, headers.get gives list)
if isinstance(protocol, basestring):
# make sure we strip any spaces (IE gives "element,element", proper browsers give "element, element")
protocol = [i.strip() for i in protocol.split(',')]
# if we've been given a valid array
try:
client_id = protocol[0]
2017-02-18 12:45:48 +13:00
connection_id = protocol[1]
username = protocol[2]
generated = False
# invalid, so just create a default connection, and auto-generate an ID
except:
client_id = self.generateGuid(12)
2017-02-18 12:45:48 +13:00
connection_id = self.generateGuid(12)
username = 'Anonymous'
generated = True
# construct our protocol object, and return
return {
"client_id": client_id,
"connection_id": connection_id,
"username": username,
"generated": generated
}
2017-02-18 12:45:48 +13:00
2017-02-18 22:04:05 +13:00
def send_message(self, *args, **kwargs):
callback = kwargs.get('callback', None)
data = kwargs.get('data', None)
logger.debug(data)
# Catch invalid recipient
if data['recipient'] not in self.connections:
error = 'Connection "'+data['recipient']+'" not found'
logger.error(error)
error = {
'message': error
}
if (callback):
callback(False, error)
else:
return error
# Sending of an error
if 'error' in data:
message = {
'jsonrpc': '2.0',
'error': data['error']
}
# Sending of a regular message
else:
message = {
'jsonrpc': '2.0',
'method': data['method'] if 'method' in data else None
}
if 'id' in data:
message['id'] = data['id']
if 'params' in data:
message['params'] = data['params']
if 'result' in data:
message['result'] = data['result']
# Dispatch the message
try:
self.connections[data['recipient']]['connection'].write_message(json_encode(message))
response = {
'message': 'Sent message to '+data['recipient']
}
if (callback):
callback(response)
else:
return response
except:
error = 'Failed to send message to '+ data['recipient']
logger.error(error)
error = {
'message': error
}
if (callback):
callback(False, error)
else:
return error
2017-02-18 12:45:48 +13:00
def broadcast(self, *args, **kwargs):
callback = kwargs.get('callback', None)
2018-03-19 16:09:27 +13:00
data = kwargs.get('data', None)
logger.debug(data)
if 'error' in data:
message = {
'jsonrpc': '2.0',
'error': data['error']
}
else:
message = {
'jsonrpc': '2.0',
'method': data['method'] if 'method' in data else None,
'params': data['params'] if 'params' in data else None
}
2017-02-18 12:45:48 +13:00
for connection in self.connections.itervalues():
send_to_this_connection = True
# Don't send the broadcast to the origin, naturally
2018-03-19 16:09:27 +13:00
if 'connection_id' in data:
if connection['connection_id'] == data["connection_id"]:
send_to_this_connection = False
if send_to_this_connection:
connection['connection'].write_message(json_encode(message))
response = {
2018-03-19 16:09:27 +13:00
'message': 'Broadcast to '+str(len(self.connections))+' connections'
}
if (callback):
callback(response)
else:
2018-03-19 16:09:27 +13:00
return response
2017-02-18 12:45:48 +13:00
##
2017-02-18 22:04:05 +13:00
# Connections
#
# Contains all our connections and client details. This requires updates
# when new clients connect, and old ones disconnect. These events are broadcast
# to all current connections
2017-02-18 12:45:48 +13:00
##
2017-02-18 22:04:05 +13:00
def get_connections(self, *args, **kwargs):
callback = kwargs.get('callback', None)
2017-02-18 22:04:05 +13:00
connections = []
for connection in self.connections.itervalues():
connections.append(connection['client'])
response = {
2017-02-18 22:04:05 +13:00
'connections': connections
}
if (callback):
callback(response)
else:
return response
2017-02-18 22:04:05 +13:00
def add_connection(self, *args, **kwargs):
connection_id = kwargs.get('connection_id', None)
connection = kwargs.get('connection', None)
client = kwargs.get('client', None)
2017-02-18 12:45:48 +13:00
new_connection = {
'client': client,
'connection_id': connection_id,
2017-02-18 12:45:48 +13:00
'connection': connection
}
self.connections[connection_id] = new_connection
self.send_message(data={
'recipient': connection_id,
'method': 'connection_added',
'params': {
'connection': {
'connection_id': connection_id,
'client_id': client['client_id'],
'username': client['username'],
'ip': client['ip']
}
2017-02-19 21:27:43 +13:00
}
})
2017-02-19 21:27:43 +13:00
2018-03-19 16:09:27 +13:00
self.broadcast(data={
'method': 'connection_added',
'params': {
'connection': client
}
})
2017-02-18 12:45:48 +13:00
def remove_connection(self, connection_id):
if connection_id in self.connections:
try:
2017-02-18 22:04:05 +13:00
client = self.connections[connection_id]['client']
2017-02-18 12:45:48 +13:00
del self.connections[connection_id]
2018-03-19 16:09:27 +13:00
self.broadcast(data={
'method': "connection_removed",
'params': {
'connection': client
}
})
2017-02-18 12:45:48 +13:00
except:
logger.error('Failed to close connection to '+ connection_id)
2017-02-18 22:04:05 +13:00
def set_username(self, *args, **kwargs):
callback = kwargs.get('callback', None)
data = kwargs.get('data', {})
2017-02-18 22:04:05 +13:00
connection_id = data['connection_id']
2017-02-18 22:04:05 +13:00
if connection_id in self.connections:
self.connections[connection_id]['client']['username'] = data['username']
2018-03-19 16:09:27 +13:00
self.broadcast(data={
'method': "connection_changed",
'params': {
'connection': self.connections[connection_id]['client']
}
})
response = {
'connection_id': connection_id,
2017-02-19 21:27:43 +13:00
'username': data['username']
}
if (callback):
callback(response)
else:
return response
2017-02-18 22:04:05 +13:00
else:
error = 'Connection "'+data['connection_id']+'" not found'
logger.error(error)
2017-10-20 21:49:41 +13:00
error = {
'message': error
}
if (callback):
2017-10-20 21:49:41 +13:00
callback(False, error)
else:
return error
2017-02-18 22:04:05 +13:00
2017-02-18 12:45:48 +13:00
2017-02-18 22:04:05 +13:00
##
# System controls
#
# Faciitates upgrades and configuration fetching
##
2017-02-18 12:45:48 +13:00
def get_config(self, *args, **kwargs):
callback = kwargs.get('callback', False)
2017-03-31 08:46:44 +13:00
# handle config setups where there is no username/password
# Iris won't work properly anyway, but at least we won't get server errors
if 'spotify' in self.config and 'username' in self.config['spotify']:
spotify_username = self.config['spotify']['username']
else:
spotify_username = False
response = {
'config': {
"spotify_username": spotify_username,
"country": self.config['iris']['country'],
"locale": self.config['iris']['locale'],
2017-11-02 21:20:56 +13:00
"spotify_authorization_url": self.config['iris']['spotify_authorization_url'],
"lastfm_authorization_url": self.config['iris']['lastfm_authorization_url'],
"snapcast_enabled": self.config['iris']['snapcast_enabled']
}
2017-02-18 12:45:48 +13:00
}
if (callback):
callback(response)
else:
return response
def get_version(self, *args, **kwargs):
callback = kwargs.get('callback', False)
2017-02-18 12:45:48 +13:00
url = 'https://pypi.python.org/pypi/Mopidy-Iris/json'
req = urllib2.Request(url)
try:
response = urllib2.urlopen(req, timeout=30).read()
response = json.loads(response)
latest_version = response['info']['version']
# compare our versions, and convert result to boolean
upgrade_available = cmp( parse_version( latest_version ), parse_version( self.version ) )
upgrade_available = ( upgrade_available == 1 )
except urllib2.HTTPError as e:
latest_version = '0.0.0'
upgrade_available = False
response = {
2017-02-18 12:45:48 +13:00
'version': {
'current': self.version,
'latest': latest_version,
'is_root': self.is_root,
'upgrade_available': upgrade_available
}
}
if (callback):
callback(response)
else:
return response
def perform_upgrade(self, *args, **kwargs):
callback = kwargs.get('callback', False)
2017-02-18 12:45:48 +13:00
2017-02-18 22:04:05 +13:00
try:
subprocess.check_call(["pip", "install", "--upgrade", "Mopidy-Iris"])
response = {
'result': "Upgrade started"
}
if (callback):
callback(response)
else:
return response
2017-09-22 16:15:50 +12:00
except subprocess.CalledProcessError as e:
2017-10-20 21:49:41 +13:00
error = {
'result': "Could not start upgrade"
}
if (callback):
2017-10-20 21:49:41 +13:00
callback(False, error)
else:
2017-10-20 21:49:41 +13:00
return error
2017-02-18 12:45:48 +13:00
def restart(self, *args, **kwargs):
2017-02-18 22:04:05 +13:00
os.execl(sys.executable, *([sys.executable]+sys.argv))
##
# Spotify Radio
#
# Accepts seed URIs and creates radio-like experience. When our tracklist is nearly
# empty, we fetch more recommendations. This can result in duplicates. We keep the
# recommendations limit low to avoid timeouts and slow UI
##
2017-02-18 12:45:48 +13:00
def get_radio(self, *args, **kwargs):
callback = kwargs.get('callback', False)
response = {
2017-02-18 12:45:48 +13:00
'radio': self.radio
}
if (callback):
callback(response)
else:
return response
2017-02-18 12:45:48 +13:00
def change_radio(self, *args, **kwargs):
callback = kwargs.get('callback', False)
data = kwargs.get('data', {})
2017-04-18 08:52:28 +12:00
# figure out if we're starting or updating radio mode
if data['update'] and self.radio['enabled']:
2017-04-18 08:52:28 +12:00
starting = False
self.initial_consume = self.core.tracklist.get_consume()
else:
2017-04-18 08:52:28 +12:00
starting = True
# fetch more tracks from Mopidy-Spotify
2017-04-18 04:44:39 +12:00
self.radio = data
self.radio['enabled'] = 1;
self.radio['results'] = [];
2017-04-18 04:44:39 +12:00
uris = self.load_more_tracks()
2017-04-18 08:52:28 +12:00
# make sure we got recommendations
if uris:
if starting:
self.core.tracklist.clear()
self.core.tracklist.set_consume(True)
# We only want to play the first batch
added = self.core.tracklist.add(uris = uris[0:3])
if (not added.get()):
logger.error("No recommendations added to queue")
self.radio['enabled'] = 0;
error = {
'message': 'No recommendations added to queue',
'radio': self.radio
}
if (callback):
callback(False, error)
else:
return error
# Save results (minus first batch) for later use
self.radio['results'] = uris[3:]
2017-04-18 08:52:28 +12:00
if starting:
self.core.playback.play()
2018-03-19 16:09:27 +13:00
self.broadcast(data={
'method': "radio_started",
'params': {
'radio': self.radio
}
})
else:
2018-03-19 16:09:27 +13:00
self.broadcast(data={
'method': "radio_changed",
'params': {
'radio': self.radio
}
})
self.get_radio(callback=callback)
return
# Failed fetching/adding tracks, so no-go
else:
logger.error("No recommendations returned by Spotify")
self.radio['enabled'] = 0;
error = {
'code': 32500,
'message': 'Could not start radio',
'data': {
'radio': self.radio
}
}
if (callback):
callback(False, error)
else:
return error
2017-02-18 12:45:48 +13:00
def stop_radio(self, *args, **kwargs):
callback = kwargs.get('callback', False)
2017-04-18 08:52:28 +12:00
2017-02-18 12:45:48 +13:00
self.radio = {
"enabled": 0,
"seed_artists": [],
"seed_genres": [],
"seed_tracks": [],
"results": []
2017-02-18 12:45:48 +13:00
}
2017-04-18 08:52:28 +12:00
# restore initial consume state
self.core.tracklist.set_consume(self.initial_consume)
self.core.playback.stop()
2017-02-18 12:45:48 +13:00
2018-03-19 16:09:27 +13:00
self.broadcast(data={
'method': "radio_stopped",
'params': {
'radio': self.radio
}
})
2017-02-18 12:45:48 +13:00
response = {
'message': 'Stopped radio'
}
if (callback):
callback(response)
else:
return response
2017-02-18 12:45:48 +13:00
2017-02-18 22:04:05 +13:00
def load_more_tracks(self, *args, **kwargs):
2017-02-18 22:04:05 +13:00
try:
self.get_spotify_token()
spotify_token = self.spotify_token
access_token = spotify_token['access_token']
2017-02-18 22:04:05 +13:00
except:
2017-09-22 16:15:50 +12:00
error = 'IrisFrontend: access_token missing or invalid'
logger.error(error)
return False
2017-02-18 22:04:05 +13:00
try:
url = 'https://api.spotify.com/v1/recommendations/'
url = url+'?seed_artists='+(",".join(self.radio['seed_artists'])).replace('spotify:artist:','')
url = url+'&seed_genres='+(",".join(self.radio['seed_genres'])).replace('spotify:genre:','')
url = url+'&seed_tracks='+(",".join(self.radio['seed_tracks'])).replace('spotify:track:','')
url = url+'&limit=50'
req = urllib2.Request(url)
req.add_header('Authorization', 'Bearer '+access_token)
response = urllib2.urlopen(req, timeout=30).read()
response_dict = json.loads(response)
2017-02-18 22:04:05 +13:00
uris = []
for track in response_dict['tracks']:
2017-02-18 22:04:05 +13:00
uris.append( track['uri'] )
return uris
2017-02-18 22:04:05 +13:00
except:
logger.error('IrisFrontend: Failed to fetch Spotify recommendations')
return False
def check_for_radio_update( self ):
tracklistLength = self.core.tracklist.length.get()
if (tracklistLength < 3 and self.radio['enabled'] == 1):
# Grab our loaded tracks
uris = self.radio['results']
# We've run out of pre-fetched tracks, so we need to get more recommendations
if (len(uris) < 3):
uris = self.load_more_tracks()
# Remove the next batch, and update our results
self.radio['results'] = uris[3:]
# Only add the next set of uris
uris = uris[0:3]
self.core.tracklist.add(uris = uris)
2017-02-18 22:04:05 +13:00
##
# Additional queue metadata
#
# This maps tltracks with extra info for display in Iris, including
# added_by and from_uri.
##
def get_queue_metadata(self, *args, **kwargs):
callback = kwargs.get('callback', False)
response = {
2017-02-18 12:45:48 +13:00
'queue_metadata': self.queue_metadata
}
if (callback):
callback(response)
else:
return response
2017-02-18 12:45:48 +13:00
def add_queue_metadata(self, *args, **kwargs):
callback = kwargs.get('callback', False)
data = kwargs.get('data', {})
2017-02-18 22:04:05 +13:00
for tlid in data['tlids']:
item = {
'tlid': tlid,
2018-03-19 16:09:27 +13:00
'added_from': data['added_from'] if 'added_from' in data else None,
'added_by': data['added_by'] if 'added_by' in data else None
2017-02-18 22:04:05 +13:00
}
self.queue_metadata['tlid_'+str(tlid)] = item
2018-03-19 16:09:27 +13:00
self.broadcast(data={
'method': 'queue_metadata_changed',
'params': {
'queue_metadata': self.queue_metadata
}
})
response = {
'message': 'Added queue metadata'
}
if (callback):
callback(response)
else:
return response
2017-02-18 22:04:05 +13:00
def clean_queue_metadata(self, *args, **kwargs):
callback = kwargs.get('callback', False)
2017-02-18 22:04:05 +13:00
cleaned_queue_metadata = {}
for tltrack in self.core.tracklist.get_tl_tracks().get():
# if we have metadata for this track, push it through to cleaned dictionary
if 'tlid_'+str(tltrack.tlid) in self.queue_metadata:
cleaned_queue_metadata['tlid_'+str(tltrack.tlid)] = self.queue_metadata['tlid_'+str(tltrack.tlid)]
self.queue_metadata = cleaned_queue_metadata
##
# Spotify authentication
#
# Uses the Client Credentials Flow, so is invisible to the user. We need this token for
# any backend spotify requests (we don't tap in to Mopidy-Spotify, yet). Also used for
# passing token to frontend for javascript requests without use of the Authorization Code Flow.
##
def get_spotify_token(self, *args, **kwargs):
callback = kwargs.get('callback', False)
# Expired, so go get a new one
if (not self.spotify_token or self.spotify_token['expires_at'] <= time.time()):
self.refresh_spotify_token()
response = {
2017-02-18 22:04:05 +13:00
'spotify_token': self.spotify_token
}
if (callback):
callback(response)
else:
return response
def refresh_spotify_token(self, *args, **kwargs):
callback = kwargs.get('callback', None)
# Use client_id and client_secret from config
# This was introduced in Mopidy-Spotify 3.1.0
url = 'https://auth.mopidy.com/spotify/token'
data = {
'client_id': self.config['spotify']['client_id'],
'client_secret': self.config['spotify']['client_secret'],
'grant_type': 'client_credentials'
}
2017-02-18 22:04:05 +13:00
try:
http_client = tornado.httpclient.HTTPClient()
request = tornado.httpclient.HTTPRequest(url, method='POST', body=urllib.urlencode(data))
response = http_client.fetch(request)
2017-02-18 22:04:05 +13:00
token = json.loads(response.body)
token['expires_at'] = time.time() + token['expires_in']
self.spotify_token = token
2018-03-19 16:09:27 +13:00
self.broadcast(data={
'method': 'spotify_token_changed',
'params': {
'spotify_token': self.spotify_token
}
})
2017-02-18 22:04:05 +13:00
2017-10-20 21:49:41 +13:00
response = {
'spotify_token': token
}
if (callback):
callback(response)
else:
2017-10-20 21:49:41 +13:00
return response
2017-02-18 22:04:05 +13:00
except urllib2.HTTPError as e:
error = json.loads(e.read())
2017-10-20 21:49:41 +13:00
error = {'message': 'Could not refresh token: '+error['error_description']}
if (callback):
2017-10-20 21:49:41 +13:00
callback(False, error)
else:
2017-10-20 21:49:41 +13:00
return error
##
# Proxy a request to an external provider
#
# This is required when requesting to non-CORS providers. We simply make the request
# server-side and pass that back. All we change is the response's Access-Control-Allow-Origin
# to prevent CORS-blocking by the browser.
##
def proxy_request(self, *args, **kwargs):
callback = kwargs.get('callback', False)
origin_request = kwargs.get('request', None)
try:
data = kwargs.get('data', {})
except:
2017-10-20 21:49:41 +13:00
callback(False, {
'message': 'Malformed data',
'source': 'proxy_request'
})
2017-10-20 21:49:41 +13:00
return
# Our request includes data, so make sure we POST the data
if 'url' not in data:
2017-10-20 21:49:41 +13:00
callback(False, {
'message': 'Malformed data (missing URL)',
'source': 'proxy_request',
'original_request': data
})
2017-10-20 21:49:41 +13:00
return
# Construct request headers
# If we have an original request, pass through it's headers
if origin_request:
headers = origin_request.headers
else:
headers = {}
# Adjust headers
headers["Accept-Language"] = "*"
headers["Accept-Encoding"] = "deflate"
if "Content-Type" in headers:
del headers["Content-Type"]
if "Host" in headers:
del headers["Host"]
if "X-Requested-With" in headers:
del headers["X-Requested-With"]
if "X-Forwarded-Server" in headers:
del headers["X-Forwarded-Server"]
if "X-Forwarded-Host" in headers:
del headers["X-Forwarded-Host"]
if "X-Forwarded-For" in headers:
del headers["X-Forwarded-For"]
if "Referrer" in headers:
del headers["Referrer"]
# Our request includes data, so make sure we POST the data
if ('data' in data and data['data']):
http_client = tornado.httpclient.AsyncHTTPClient()
request = tornado.httpclient.HTTPRequest(data['url'], method='POST', body=json.dumps(data['data']), headers=headers, validate_cert=False)
http_client.fetch(request, callback=callback)
# No data, so just a simple GET request
else:
# Strip out our origin content-length otherwise this confuses
# the target server as content-length doesn't apply to GET requests
if "Content-Length" in headers:
del headers["Content-Length"]
http_client = tornado.httpclient.AsyncHTTPClient()
request = tornado.httpclient.HTTPRequest(data['url'], headers=headers, validate_cert=False)
http_client.fetch(request, callback=callback)
##
# Simple test method
##
def test(self, *args, **kwargs):
callback = kwargs.get('callback', None)
2017-10-20 21:49:41 +13:00
data = kwargs.get('data', {})
if data and 'force_error' in data:
callback(False, {'message': "Could not sleep, forced error"})
return
else:
time.sleep(1)
callback({'message': "Slept for one second"}, False)
return