コード例 #1
0
	def _line(self, l):	# {{{
		if DEBUG > 4:
			log('Debug: Received line: %s' % l)
		if self.address is not None:
			if not l.strip():
				self._handle_headers()
				return
			try:
				key, value = l.split(':', 1)
			except ValueError:
				log('Invalid header line: %s' % l)
				return
			self.headers[key.lower()] = value.strip()
			return
		else:
			try:
				self.method, url, self.standard = l.split()
				for prefix in self.proxy:
					if url.startswith('/' + prefix + '/') or url == '/' + prefix:
						self.prefix = '/' + prefix
						break
				else:
					self.prefix = ''
				address = urlparse(url)
				path = address.path[len(self.prefix):] or '/'
				self.url = path + url[len(address.path):]
				self.address = urlparse(self.url)
				self.query = parse_qs(self.address.query)
			except:
				traceback.print_exc()
				self.server.reply(self, 400)
				self.socket.close()
			return
コード例 #2
0
	def send(self, data, opcode = 1):	# Send a WebSocket frame.  {{{
		'''Send a Websocket frame to the remote end of the connection.
		@param data: Data to send.
		@param opcode: Opcade to send.  0 = fragment, 1 = text packet, 2 = binary packet, 8 = close request, 9 = ping, 10 = pong.
		'''
		if DEBUG > 3:
			log('websend:' + repr(data))
		assert opcode in(0, 1, 2, 8, 9, 10)
		if self._is_closed:
			return None
		if opcode == 1:
			data = data.encode('utf-8')
		if self.mask[1]:
			maskchar = 0x80
			# Masks are stupid, but the standard requires them.  Don't waste time on encoding (or decoding, if also using this module).
			mask = b'\0\0\0\0'
		else:
			maskchar = 0
			mask = b''
		if len(data) < 126:
			l = bytes((maskchar | len(data),))
		elif len(data) < 1 << 16:
			l = bytes((maskchar | 126,)) + struct.pack('!H', len(data))
		else:
			l = bytes((maskchar | 127,)) + struct.pack('!Q', len(data))
		try:
			self.socket.send(bytes((0x80 | opcode,)) + l + mask + data)
		except:
			# Something went wrong; close the socket(in case it wasn't yet).
			if DEBUG > 0:
				traceback.print_exc()
			log('closing socket due to problem while sending.')
			self.socket.close()
		if opcode == 8:
			self.socket.close()
コード例 #3
0
	def _line(self, l):	# {{{
		if DEBUG > 4:
			log('Debug: Received line: %s' % l)
		if self.address is not None:
			if not l.strip():
				self._handle_headers()
				return
			try:
				key, value = l.split(':', 1)
			except ValueError:
				log('Invalid header line: %s' % l)
				return
			self.headers[key.lower()] = value.strip()
			return
		else:
			try:
				self.method, url, self.standard = l.split()
				for prefix in self.proxy:
					if url.startswith('/' + prefix + '/') or url == '/' + prefix:
						self.prefix = '/' + prefix
						break
				else:
					self.prefix = ''
				address = urlparse(url)
				path = address.path[len(self.prefix):] or '/'
				self.url = path + url[len(address.path):]
				self.address = urlparse(self.url)
				self.query = parse_qs(self.address.query)
			except:
				traceback.print_exc()
				self.server.reply(self, 400)
				self.socket.close()
			return
コード例 #4
0
	def send(self, data, opcode = 1):	# Send a WebSocket frame.  {{{
		'''Send a Websocket frame to the remote end of the connection.
		@param data: Data to send.
		@param opcode: Opcade to send.  0 = fragment, 1 = text packet, 2 = binary packet, 8 = close request, 9 = ping, 10 = pong.
		'''
		if DEBUG > 3:
			log('websend:' + repr(data))
		assert opcode in(0, 1, 2, 8, 9, 10)
		if self._is_closed:
			return None
		if opcode == 1:
			data = data.encode('utf-8')
		if self.mask[1]:
			maskchar = 0x80
			# Masks are stupid, but the standard requires them.  Don't waste time on encoding (or decoding, if also using this module).
			mask = b'\0\0\0\0'
		else:
			maskchar = 0
			mask = b''
		if len(data) < 126:
			l = bytes((maskchar | len(data),))
		elif len(data) < 1 << 16:
			l = bytes((maskchar | 126,)) + struct.pack('!H', len(data))
		else:
			l = bytes((maskchar | 127,)) + struct.pack('!Q', len(data))
		try:
			self.socket.send(bytes((0x80 | opcode,)) + l + mask + data)
		except:
			# Something went wrong; close the socket(in case it wasn't yet).
			if DEBUG > 0:
				traceback.print_exc()
			log('closing socket due to problem while sending.')
			self.socket.close()
		if opcode == 8:
			self.socket.close()
コード例 #5
0
def run_user_code():
    error = False
    try:
        global network
        #user_code.bl = network
        exec(open('./user_code.py').read(), globals())
        print('2')

        #user_code.network = network
        print('3')

    except Exception as err:
        print('User code crashed. Error: %s' % err)
        network.log('Code crashed. Error: %s' % err)
        error = True
    if error:
        print('Revert to default code')
        network.log('Revert to default code')
        gc.collect()
        print(gc.mem_free())
        try:
            while True:
                network.process()
                time.sleep_ms(100)
        except:
            pass  #machine.reset()
コード例 #6
0
	def __init__(self, port, target, *a, **ka): # {{{
		'''Start a new RPC HTTP server.
		Extra arguments are passed to the Httpd constructor,
		which passes its extra arguments to network.Server.
		@param port: Port to listen on.  Same format as in
			python-network.
		@param target: Communication object class.  A new
			object is created for every connection.  Its
			constructor is called with the newly created
			RPC as an argument.
		@param log: If set, debugging is enabled and logging is
			sent to this file.  If it is a directory, a log
			file with the current date and time as filename
			will be used.
		'''
		## Function to send an event to some or all connected
		# clients.
		# To send to some clients, add an identifier to all
		# clients in a group, and use that identifier in the
		# item operator, like so:
		# @code{.py}
		# connection0.groups.clear()
		# connection1.groups.add('foo')
		# connection2.groups.add('foo')
		# server.broadcast.bar(42)	# This is sent to all clients.
		# server.broadcast['foo'].bar(42)	# This is only sent to clients in group 'foo'.
		# @endcode
		self.broadcast = RPChttpd._Broadcast(self)
		if 'log' in ka:
			name = ka.pop('log')
			if name:
				global DEBUG
				if DEBUG < 2:
					DEBUG = 2
				if os.path.isdir(name):
					n = os.path.join(name, time.strftime('%F %T%z'))
					old = n
					i = 0
					while os.path.exists(n):
						i += 1
						n = '%s.%d' % (old, i)
				else:
					n = name
				try:
					f = open(n, 'a')
					if n != name:
						sys.stderr.write('Logging to %s\n' % n)
				except IOError:
					fd, n = tempfile.mkstemp(prefix = os.path.basename(n) + '-' + time.strftime('%F %T%z') + '-', text = True)
					sys.stderr.write('Opening file %s failed, using tempfile instead: %s\n' % (name, n))
					f = os.fdopen(fd, 'a')
				stderr_fd = sys.stderr.fileno()
				os.close(stderr_fd)
				os.dup2(f.fileno(), stderr_fd)
				log('Start logging to %s, commandline = %s' % (n, repr(sys.argv)))
		Httpd.__init__(self, port, target, websocket = RPC, *a, **ka)
コード例 #7
0
	def __init__(self, port, target, *a, **ka): # {{{
		'''Start a new RPC HTTP server.
		Extra arguments are passed to the Httpd constructor,
		which passes its extra arguments to network.Server.
		@param port: Port to listen on.  Same format as in
			python-network.
		@param target: Communication object class.  A new
			object is created for every connection.  Its
			constructor is called with the newly created
			RPC as an argument.
		@param log: If set, debugging is enabled and logging is
			sent to this file.  If it is a directory, a log
			file with the current date and time as filename
			will be used.
		'''
		## Function to send an event to some or all connected
		# clients.
		# To send to some clients, add an identifier to all
		# clients in a group, and use that identifier in the
		# item operator, like so:
		# @code{.py}
		# connection0.groups.clear()
		# connection1.groups.add('foo')
		# connection2.groups.add('foo')
		# server.broadcast.bar(42)	# This is sent to all clients.
		# server.broadcast['foo'].bar(42)	# This is only sent to clients in group 'foo'.
		# @endcode
		self.broadcast = RPChttpd._Broadcast(self)
		if 'log' in ka:
			name = ka.pop('log')
			if name:
				global DEBUG
				if DEBUG < 2:
					DEBUG = 2
				if os.path.isdir(name):
					n = os.path.join(name, time.strftime('%F %T%z'))
					old = n
					i = 0
					while os.path.exists(n):
						i += 1
						n = '%s.%d' % (old, i)
				else:
					n = name
				try:
					f = open(n, 'a')
					if n != name:
						sys.stderr.write('Logging to %s\n' % n)
				except IOError:
					fd, n = tempfile.mkstemp(prefix = os.path.basename(n) + '-' + time.strftime('%F %T%z') + '-', text = True)
					sys.stderr.write('Opening file %s failed, using tempfile instead: %s\n' % (name, n))
					f = os.fdopen(fd, 'a')
				stderr_fd = sys.stderr.fileno()
				os.close(stderr_fd)
				os.dup2(f.fileno(), stderr_fd)
				log('Start logging to %s, commandline = %s' % (n, repr(sys.argv)))
		Httpd.__init__(self, port, target, websocket = RPC, *a, **ka)
コード例 #8
0
	def _send(self, type, object): # {{{
		'''Send an RPC packet.
		@param type: The packet type.
			One of "return", "error", "call".
		@param object: The data to send.
			Return value, error message, or function arguments.
		'''
		if DEBUG > 1:
			log('sending:' + repr(type) + repr(object))
		Websocket.send(self, json.dumps((type, object)))
コード例 #9
0
	def _send(self, type, object): # {{{
		'''Send an RPC packet.
		@param type: The packet type.
			One of "return", "error", "call".
		@param object: The data to send.
			Return value, error message, or function arguments.
		'''
		if DEBUG > 1:
			log('sending:' + repr(type) + repr(object))
		Websocket.send(self, json.dumps((type, object)))
コード例 #10
0
	def post(self, connection):	# A non-WebSocket page was requested with POST.  Same as page() above, plus connection.post, which is a dict of name:(headers, sent_filename, local_filename).  When done, the local files are unlinked; remove the items from the dict to prevent this.  The default is to return an error (so POST cannot be used to retrieve static pages!) {{{
		'''Handle POST request.
		This function responds with an error by default.  It
		must be overridden to handle POST requests.

		@param connection: Same as for page(), plus connection.post, which is a 2-tuple.
			The first element is a dict of name:['value', ...] for fields without a file.
			The second element is a dict of name:[(local_filename, remote_filename), ...] for fields with a file.
			When done, the local files are unlinked; remove the items from the dict to prevent this.
		@return True to keep connection open after this request, False to close it.
		'''
		log('Warning: ignoring POST request.')
		self.reply(connection, 501)
		return False
コード例 #11
0
	def post(self, connection):	# A non-WebSocket page was requested with POST.  Same as page() above, plus connection.post, which is a dict of name:(headers, sent_filename, local_filename).  When done, the local files are unlinked; remove the items from the dict to prevent this.  The default is to return an error (so POST cannot be used to retrieve static pages!) {{{
		'''Handle POST request.
		This function responds with an error by default.  It
		must be overridden to handle POST requests.

		@param connection: Same as for page(), plus
			connection.post, which is a dict of
			name:(headers, sent_filename, local_filename).
			When done, the local files are unlinked; remove
			the items from the dict to prevent this.
		@return True to keep connection open after this
			request, False to close it.
		'''
		log('Warning: ignoring POST request.')
		self.reply(connection, 501)
		return False
コード例 #12
0
	def _parse_args(self, header): # {{{
		if ';' not in header:
			return (header.strip(), {})
		pos = header.index(';') + 1
		main = header[:pos].strip()
		ret = {}
		while pos < len(header):
			if '=' not in header[pos:]:
				if header[pos:].strip() != '':
					log('header argument %s does not have a value' % header[pos:].strip())
				return main, ret
			p = header.index('=', pos)
			key = header[pos:p].strip().lower()
			pos = p + 1
			value = ''
			quoted = False
			while True:
				first = (len(header), None)
				if not quoted and ';' in header[pos:]:
					s = header.index(';', pos)
					if s < first[0]:
						first = (s, ';')
				if '"' in header[pos:]:
					q = header.index('"', pos)
					if q < first[0]:
						first = (q, '"')
				if '\\' in header[pos:]:
					b = header.index('\\', pos)
					if b < first[0]:
						first = (b, '\\')
				value += header[pos:first[0]]
				pos = first[0] + 1
				if first[1] == ';' or first[1] is None:
					break
				if first[1] == '\\':
					value += header[pos]
					pos += 1
					continue
				quoted = not quoted
			ret[key] = value
		return main, ret
コード例 #13
0
	def _parse_args(self, header): # {{{
		if ';' not in header:
			return (header.strip(), {})
		pos = header.index(';') + 1
		main = header[:pos].strip()
		ret = {}
		while pos < len(header):
			if '=' not in header[pos:]:
				if header[pos:].strip() != '':
					log('header argument %s does not have a value' % header[pos:].strip())
				return main, ret
			p = header.index('=', pos)
			key = header[pos:p].strip().lower()
			pos = p + 1
			value = ''
			quoted = False
			while True:
				first = (len(header), None)
				if not quoted and ';' in header[pos:]:
					s = header.index(';', pos)
					if s < first[0]:
						first = (s, ';')
				if '"' in header[pos:]:
					q = header.index('"', pos)
					if q < first[0]:
						first = (q, '"')
				if '\\' in header[pos:]:
					b = header.index('\\', pos)
					if b < first[0]:
						first = (b, '\\')
				value += header[pos:first[0]]
				pos = first[0] + 1
				if first[1] == ';' or first[1] is None:
					break
				if first[1] == '\\':
					value += header[pos]
					pos += 1
					continue
				quoted = not quoted
			ret[key] = value
		return main, ret
コード例 #14
0
	def _base64_decoder(self, data, final):	# {{{
		ret = b''
		pos = 0
		table = b'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='
		current = []
		while len(data) >= pos + 4 - len(current):
			c = data[pos]
			pos += 1
			if c not in table:
				if c not in b'\r\n':
					log('ignoring invalid character %s in base64 string' % c)
				continue
			current.append(table.index(c))
			if len(current) == 4:
				# decode
				ret += bytes((current[0] << 2 | current[1] >> 4,))
				if current[2] != 65:
					ret += bytes((((current[1] << 4) & 0xf0) | current[2] >> 2,))
				if current[3] != 65:
					ret += bytes((((current[2] << 6) & 0xc0) | current[3],))
		return (ret, data[pos:])
コード例 #15
0
	def _base64_decoder(self, data, final):	# {{{
		ret = b''
		pos = 0
		table = b'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='
		current = []
		while len(data) >= pos + 4 - len(current):
			c = data[pos]
			pos += 1
			if c not in table:
				if c not in b'\r\n':
					log('ignoring invalid character %s in base64 string' % c)
				continue
			current.append(table.index(c))
			if len(current) == 4:
				# decode
				ret += bytes((current[0] << 2 | current[1] >> 4,))
				if current[2] != 65:
					ret += bytes((((current[1] << 4) & 0xf0) | current[2] >> 2,))
				if current[3] != 65:
					ret += bytes((((current[2] << 6) & 0xc0) | current[3],))
		return (ret, data[pos:])
コード例 #16
0
	def _parse_headers(self, message): # {{{
		lines = []
		pos = 0
		while True:
			p = message.index(b'\r\n', pos)
			ln = message[pos:p].decode('utf-8', 'replace')
			pos = p + 2
			if ln == '':
				break
			if ln[0] in ' \t':
				if len(lines) == 0:
					log('header starts with continuation')
				else:
					lines[-1] += ln
			else:
				lines.append(ln)
		ret = {}
		for ln in lines:
			if ':' not in ln:
				log('ignoring header line without ":": %s' % ln)
				continue
			key, value = [x.strip() for x in ln.split(':', 1)]
			if key.lower() in ret:
				log('duplicate key in header: %s' % key)
			ret[key.lower()] = value
		return ret, message[pos:]
コード例 #17
0
	def _parse_headers(self, message): # {{{
		lines = []
		pos = 0
		while True:
			p = message.index(b'\r\n', pos)
			ln = message[pos:p].decode('utf-8', 'replace')
			pos = p + 2
			if ln == '':
				break
			if ln[0] in ' \t':
				if len(lines) == 0:
					log('header starts with continuation')
				else:
					lines[-1] += ln
			else:
				lines.append(ln)
		ret = {}
		for ln in lines:
			if ':' not in ln:
				log('ignoring header line without ":": %s' % ln)
				continue
			key, value = [x.strip() for x in ln.split(':', 1)]
			if key.lower() in ret:
				log('duplicate key in header: %s' % key)
			ret[key.lower()] = value
		return ret, message[pos:]
コード例 #18
0
	def page(self, connection, path = None):	# A non-WebSocket page was requested.  Use connection.address, connection.method, connection.query, connection.headers and connection.body (which should be empty) to find out more.  {{{
		'''Serve a non-websocket page.
		Overload this function for custom behavior.  Call this
		function from the overloaded function if you want the
		default functionality in some cases.
		@param connection: The connection that requests the
			page.  Attributes of interest are
			connection.address, connection.method,
			connection.query, connection.headers and
			connection.body (which should be empty).
		@param path: The requested file.
		@return True to keep the connection open after this
			request, False to close it.
		'''
		if self.httpdirs is None:
			self.reply(connection, 501)
			return
		if path is None:
			path = connection.address.path
		if path == '/':
			address = 'index'
		else:
			address = '/' + unquote(path) + '/'
			while '/../' in address:
				# Don't handle this; just ignore it.
				pos = address.index('/../')
				address = address[:pos] + address[pos + 3:]
			address = address[1:-1]
		if '.' in address.rsplit('/', 1)[-1]:
			base, ext = address.rsplit('.', 1)
			base = base.strip('/')
			if ext not in self.exts and None not in self.exts:
				log('not serving unknown extension %s' % ext)
				self.reply(connection, 404)
				return
			for d in self.httpdirs:
				filename = os.path.join(d, base + os.extsep + ext)
				if os.path.exists(filename):
					break
			else:
				log('file %s not found in %s' % (base + os.extsep + ext, ', '.join(self.httpdirs)))
				self.reply(connection, 404)
				return
		else:
			base = address.strip('/')
			for ext in self.exts:
				for d in self.httpdirs:
					filename = os.path.join(d, base if ext is None else base + os.extsep + ext)
					if os.path.exists(filename):
						break
				else:
					continue
				break
			else:
				log('no file %s (with supported extension) found in %s' % (base, ', '.join(self.httpdirs)))
				self.reply(connection, 404)
				return
		return self.exts[ext](connection, open(filename, 'rb').read())
コード例 #19
0
	def page(self, connection, path = None):	# A non-WebSocket page was requested.  Use connection.address, connection.method, connection.query, connection.headers and connection.body (which should be empty) to find out more.  {{{
		'''Serve a non-websocket page.
		Overload this function for custom behavior.  Call this
		function from the overloaded function if you want the
		default functionality in some cases.
		@param connection: The connection that requests the
			page.  Attributes of interest are
			connection.address, connection.method,
			connection.query, connection.headers and
			connection.body (which should be empty).
		@param path: The requested file.
		@return True to keep the connection open after this
			request, False to close it.
		'''
		if self.httpdirs is None:
			self.reply(connection, 501)
			return
		if path is None:
			path = connection.address.path
		if path == '/':
			address = 'index'
		else:
			address = '/' + path + '/'
			while '/../' in address:
				# Don't handle this; just ignore it.
				pos = address.index('/../')
				address = address[:pos] + address[pos + 3:]
			address = address[1:-1]
		if '.' in address:
			base, ext = address.rsplit('.', 1)
			base = base.strip('/')
			if ext not in self.exts and None not in self.exts:
				log('not serving unknown extension %s' % ext)
				self.reply(connection, 404)
				return
			for d in self.httpdirs:
				filename = os.path.join(d, base + os.extsep + ext)
				if os.path.exists(filename):
					break
			else:
				log('file %s not found in %s' % (base + os.extsep + ext, ', '.join(self.httpdirs)))
				self.reply(connection, 404)
				return
		else:
			base = address.strip('/')
			for ext in self.exts:
				for d in self.httpdirs:
					filename = os.path.join(d, base if ext is None else base + os.extsep + ext)
					if os.path.exists(filename):
						break
				else:
					continue
				break
			else:
				log('no file %s (with supported extension) found in %s' % (base, ', '.join(self.httpdirs)))
				self.reply(connection, 404)
				return
		return self.exts[ext](connection, open(filename, 'rb').read())
コード例 #20
0
	def _quopri_decoder(self, data, final):	# {{{
		ret = b''
		pos = 0
		while b'=' in data[pos:-2]:
			p = data.index(b'=', pos)
			ret += data[:p]
			if data[p + 1:p + 3] == b'\r\n':
				ret += b'\n'
				pos = p + 3
				continue
			if any(x not in b'0123456789ABCDEFabcdef' for x in data[p + 1:p + 3]):
				log('invalid escaped sequence in quoted printable: %s' % data[p:p + 3].encode('utf-8', 'replace'))
				pos = p + 1
				continue
			ret += bytes((int(data[p + 1:p + 3], 16),))
			pos = p + 3
		if final:
			ret += data[pos:]
			pos = len(data)
		elif len(pos) >= 2:
			ret += data[pos:-2]
			pos = len(data) - 2
		return (ret, data[pos:])
コード例 #21
0
	def _quopri_decoder(self, data, final):	# {{{
		ret = b''
		pos = 0
		while b'=' in data[pos:-2]:
			p = data.index(b'=', pos)
			ret += data[:p]
			if data[p + 1:p + 3] == b'\r\n':
				ret += b'\n'
				pos = p + 3
				continue
			if any(x not in b'0123456789ABCDEFabcdef' for x in data[p + 1:p + 3]):
				log('invalid escaped sequence in quoted printable: %s' % data[p:p + 3].encode('utf-8', 'replace'))
				pos = p + 1
				continue
			ret += bytes((int(data[p + 1:p + 3], 16),))
			pos = p + 3
		if final:
			ret += data[pos:]
			pos = len(data)
		elif len(pos) >= 2:
			ret += data[pos:-2]
			pos = len(data) - 2
		return (ret, data[pos:])
コード例 #22
0
	def _recv(self, frame): # {{{
		'''Receive a websocket packet.
		@param frame: The packet.
		@return None.
		'''
		data = self._parse_frame(frame)
		if DEBUG > 1:
			log('packet received: %s' % repr(data))
		if data[0] is None:
			self._send('error', data[1])
			return
		elif data[0] == 'error':
			if DEBUG > 0:
				traceback.print_stack()
			if self._error is not None:
				self._error(data[1])
			else:
				raise ValueError(data[1])
		elif data[0] == 'event':
			# Do nothing with this; the packet is already logged if DEBUG > 1.
			return
		elif data[0] == 'return':
			assert data[1][0] in RPC._calls
			RPC._calls[data[1][0]] (data[1][1])
			return
		elif data[0] == 'call':
			try:
				if self._delayed_calls is not None:
					self._delayed_calls.append(data[1])
				else:
					self._call(data[1][0], data[1][1], data[1][2], data[1][3])
			except:
				traceback.print_exc()
				log('error: %s' % str(sys.exc_info()[1]))
				self._send('error', traceback.format_exc())
		else:
			self._send('error', 'invalid RPC command')
コード例 #23
0
	def _recv(self, frame): # {{{
		'''Receive a websocket packet.
		@param frame: The packet.
		@return None.
		'''
		data = self._parse_frame(frame)
		if DEBUG > 1:
			log('packet received: %s' % repr(data))
		if data[0] is None:
			self._send('error', data[1])
			return
		elif data[0] == 'error':
			if DEBUG > 0:
				traceback.print_stack()
			if self._error is not None:
				self._error(data[1])
			else:
				raise ValueError(data[1])
		elif data[0] == 'event':
			# Do nothing with this; the packet is already logged if DEBUG > 1.
			return
		elif data[0] == 'return':
			assert data[1][0] in RPC._calls
			RPC._calls[data[1][0]] (data[1][1])
			return
		elif data[0] == 'call':
			try:
				if self._delayed_calls is not None:
					self._delayed_calls.append(data[1])
				else:
					self._call(data[1][0], data[1][1], data[1][2], data[1][3])
			except:
				traceback.print_exc()
				log('error: %s' % str(sys.exc_info()[1]))
				self._send('error', traceback.format_exc())
		else:
			self._send('error', 'invalid RPC command')
コード例 #24
0
	def _parse_frame(self, frame): # {{{
		'''Decode an RPC packet.
		@param frame: The packet.
		@return (type, object) or (None, error_message).
		'''
		try:
			# Don't choke on Chrome's junk at the end of packets.
			data = json.JSONDecoder().raw_decode(frame)[0]
		except ValueError:
			log('non-json frame: %s' % repr(frame))
			return(None, 'non-json frame')
		if type(data) is not list or len(data) != 2 or not isinstance(data[0], str):
			log('invalid frame %s' % repr(data))
			return(None, 'invalid frame')
		if data[0] == 'call':
			if self._delayed_calls is None and (not hasattr(self._target, data[1][1]) or not isinstance(getattr(self._target, data[1][1]), collections.Callable)):
				log('invalid call frame %s' % repr(data))
				return(None, 'invalid frame')
		elif data[0] not in ('error', 'return'):
			log('invalid frame type %s' % repr(data))
			return(None, 'invalid frame')
		return data
コード例 #25
0
	def _parse_frame(self, frame): # {{{
		'''Decode an RPC packet.
		@param frame: The packet.
		@return (type, object) or (None, error_message).
		'''
		try:
			# Don't choke on Chrome's junk at the end of packets.
			data = json.JSONDecoder().raw_decode(frame)[0]
		except ValueError:
			log('non-json frame: %s' % repr(frame))
			return(None, 'non-json frame')
		if type(data) is not list or len(data) != 2 or not isinstance(data[0], str):
			log('invalid frame %s' % repr(data))
			return(None, 'invalid frame')
		if data[0] == 'call':
			if self._delayed_calls is None and (not hasattr(self._target, data[1][1]) or not isinstance(getattr(self._target, data[1][1]), collections.Callable)):
				log('invalid call frame %s' % repr(data))
				return(None, 'invalid frame')
		elif data[0] not in ('error', 'return'):
			log('invalid frame type %s' % repr(data))
			return(None, 'invalid frame')
		return data
コード例 #26
0
	def __init__(self, port, url = '/', recv = None, method = 'GET', user = None, password = None, extra = {}, socket = None, mask = (None, True), websockets = None, data = None, real_remote = None, *a, **ka): # {{{
		'''When constructing a Websocket, a connection is made to the
		requested port, and the websocket handshake is performed.  This
		constructor passes any extra arguments to the network.Socket
		constructor (if it is called), in particular "tls" can be used
		to control the use of encryption.  There objects are also
		created by the websockets server.  For that reason, there are
		some arguments that should not be used when calling it
		directly.
		@param port: Host and port to connect to, same format as
			python-network uses.
		@param url: The url to request at the host.
		@param recv: Function to call when a data packet is received
			asynchronously.
		@param method: Connection method to use.
		@param user: Username for authentication.  Only plain text
			authentication is supported; this should only be used
			over a link with TLS encryption.
		@param password: Password for authentication.
		@param extra: Extra headers to pass to the host.
		@param socket: Existing socket to use for connection, or None
			to create a new socket.
		@param mask: Mostly for internal use by the server.  Flag
			whether or not to send and receive masks.  (None, True)
			is the default, which means to accept anything, and
			send masked packets.  Note that the mask that is used
			for sending is always (0,0,0,0), which is effectively
			no mask.  It is sent to follow the protocol.  No real
			mask is sent, because masks give a false sense of
			security and provide no benefit.  The unmasking
			implementation is rather slow.  When communicating
			between two programs using this module, the non-mask is
			detected and the unmasking step is skipped.
		@param websockets: For interal use by the server.  A set to remove the socket from on disconnect.
		@param data: For internal use by the server.  Data to pass through to callback functions.
		@param real_remote: For internal use by the server.  Override detected remote.  Used to have proper remotes behind virtual proxy.
		'''
		self.recv = recv
		self.mask = mask
		self.websockets = websockets
		self.websocket_buffer = b''
		self.websocket_fragments = b''
		self.opcode = None
		self._is_closed = False
		self._pong = True	# If false, we're waiting for a pong.
		if socket is None:
			socket = network.Socket(port, *a, **ka)
		self.socket = socket
		self.remote = [real_remote or socket.remote[0], socket.remote[1]]
		hdrdata = b''
		if url is not None:
			elist = []
			for e in extra:
				elist.append('%s: %s\r\n' % (e, extra[e]))
			if user is not None:
				userpwd = user + ':' + password + '\r\n'
			else:
				userpwd = ''
			socket.send(('''\
%s %s HTTP/1.1\r
Connection: Upgrade\r
Upgrade: websocket\r
Sec-WebSocket-Key: 0\r
%s%s\r
''' % (method, url, userpwd, ''.join(elist))).encode('utf-8'))
			while b'\n' not in hdrdata:
				r = socket.recv()
				if r == b'':
					raise EOFError('EOF while reading reply')
				hdrdata += r
			pos = hdrdata.index(b'\n')
			assert int(hdrdata[:pos].split()[1]) == 101
			hdrdata = hdrdata[pos + 1:]
			data = {}
			while True:
				while b'\n' not in hdrdata:
					r = socket.recv()
					if len(r) == 0:
						raise EOFError('EOF while reading reply')
					hdrdata += r
				pos = hdrdata.index(b'\n')
				line = hdrdata[:pos].strip()
				hdrdata = hdrdata[pos + 1:]
				if len(line) == 0:
					break
				key, value = [x.strip() for x in line.decode('utf-8', 'replace').split(':', 1)]
				data[key] = value
		self.data = data
		self.socket.read(self._websocket_read)
		def disconnect(socket, data):
			if not self._is_closed:
				self._is_closed = True
				if self.websockets is not None:
					self.websockets.remove(self)
				self.closed()
			return b''
		if self.websockets is not None:
			self.websockets.add(self)
		self.socket.disconnect_cb(disconnect)
		self.opened()
		if len(hdrdata) > 0:
			self._websocket_read(hdrdata)
		if DEBUG > 2:
			log('opened websocket')
コード例 #27
0
	def _websocket_read(self, data, sync = False):	# {{{
		# Websocket data consists of:
		# 1 byte:
		#	bit 7: 1 for last(or only) fragment; 0 for other fragments.
		#	bit 6-4: extension stuff; must be 0.
		#	bit 3-0: opcode.
		# 1 byte:
		#	bit 7: 1 if masked, 0 otherwise.
		#	bit 6-0: length or 126 or 127.
		# If 126:
		# 	2 bytes: length
		# If 127:
		#	8 bytes: length
		# If masked:
		#	4 bytes: mask
		# length bytes: (masked) payload

		#log('received: ' + repr(data))
		if DEBUG > 2:
			log('received %d bytes' % len(data))
		if DEBUG > 3:
			log('waiting: ' + ' '.join(['%02x' % x for x in self.websocket_buffer]) + ''.join([chr(x) if 32 <= x < 127 else '.' for x in self.websocket_buffer]))
			log('data: ' + ' '.join(['%02x' % x for x in data]) + ''.join([chr(x) if 32 <= x < 127 else '.' for x in data]))
		self.websocket_buffer += data
		while len(self.websocket_buffer) > 0:
			if self.websocket_buffer[0] & 0x70:
				# Protocol error.
				log('extension stuff %x, not supported!' % self.websocket_buffer[0])
				self.socket.close()
				return None
			if len(self.websocket_buffer) < 2:
				# Not enough data for length bytes.
				if DEBUG > 2:
					log('no length yet')
				return None
			b = self.websocket_buffer[1]
			have_mask = bool(b & 0x80)
			b &= 0x7f
			if have_mask and self.mask[0] is True or not have_mask and self.mask[0] is False:
				# Protocol error.
				log('mask error')
				self.socket.close()
				return None
			if b == 127:
				if len(self.websocket_buffer) < 10:
					# Not enough data for length bytes.
					if DEBUG > 2:
						log('no 4 length yet')
					return None
				l = struct.unpack('!Q', self.websocket_buffer[2:10])[0]
				pos = 10
			elif b == 126:
				if len(self.websocket_buffer) < 4:
					# Not enough data for length bytes.
					if DEBUG > 2:
						log('no 2 length yet')
					return None
				l = struct.unpack('!H', self.websocket_buffer[2:4])[0]
				pos = 4
			else:
				l = b
				pos = 2
			if len(self.websocket_buffer) < pos + (4 if have_mask else 0) + l:
				# Not enough data for packet.
				if DEBUG > 2:
					log('no packet yet(%d < %d)' % (len(self.websocket_buffer), pos + (4 if have_mask else 0) + l))
				# Long packets should not cause ping timeouts.
				self._pong = True
				return None
			header = self.websocket_buffer[:pos]
			opcode = header[0] & 0xf
			if have_mask:
				mask = [x for x in self.websocket_buffer[pos:pos + 4]]
				pos += 4
				data = self.websocket_buffer[pos:pos + l]
				# The following is slow!
				# Don't do it if the mask is 0; this is always true if talking to another program using this module.
				if mask != [0, 0, 0, 0]:
					data = bytes([x ^ mask[i & 3] for i, x in enumerate(data)])
			else:
				data = self.websocket_buffer[pos:pos + l]
			self.websocket_buffer = self.websocket_buffer[pos + l:]
			if self.opcode is None:
				self.opcode = opcode
			elif opcode != 0:
				# Protocol error.
				# Exception: pongs are sometimes sent asynchronously.
				# Theoretically the packet can be fragmented, but that should never happen; asynchronous pongs seem to be a protocol violation anyway...
				if opcode == 10:
					# Pong.
					self._pong = True
				else:
					log('invalid fragment')
					self.socket.close()
				return None
			if (header[0] & 0x80) != 0x80:
				# fragment found; not last.
				self._pong = True
				self.websocket_fragments += data
				if DEBUG > 2:
					log('fragment recorded')
				return None
			# Complete frame has been received.
			data = self.websocket_fragments + data
			self.websocket_fragments = b''
			opcode = self.opcode
			self.opcode = None
			if opcode == 8:
				# Connection close request.
				self.close()
				return None
			elif opcode == 9:
				# Ping.
				self.send(data, 10)	# Pong
			elif opcode == 10:
				# Pong.
				self._pong = True
			elif opcode == 1:
				# Text.
				data = data.decode('utf-8', 'replace')
				if sync:
					return data
				if self.recv:
					self.recv(self, data)
				else:
					log('warning: ignoring incoming websocket frame')
			elif opcode == 2:
				# Binary.
				if sync:
					return data
				if self.recv:
					self.recv(self, data)
				else:
					log('warning: ignoring incoming websocket frame (binary)')
			else:
				log('invalid opcode')
				self.socket.close()
コード例 #28
0
	def _handle_headers(self):	# {{{
		if DEBUG > 4:
			log('Debug: handling headers')
		is_websocket = 'connection' in self.headers and 'upgrade' in self.headers and 'upgrade' in self.headers['connection'].lower() and 'websocket' in self.headers['upgrade'].lower()
		self.data = {}
		self.data['url'] = self.url
		self.data['address'] = self.address
		self.data['query'] = self.query
		self.data['headers'] = self.headers
		msg = self.server.auth_message(self, is_websocket) if callable(self.server.auth_message) else self.server.auth_message
		if msg:
			if 'authorization' not in self.headers:
				self.server.reply(self, 401, headers = {'WWW-Authenticate': 'Basic realm="%s"' % msg.replace('\n', ' ').replace('\r', ' ').replace('"', "'")})
				if 'content-length' not in self.headers or self.headers['content-length'].strip() != '0':
					self.socket.close()
				return
			else:
				auth = self.headers['authorization'].split(None, 1)
				if auth[0].lower() != 'basic':
					self.server.reply(self, 400)
					self.socket.close()
					return
				pwdata = base64.b64decode(auth[1].encode('utf-8')).decode('utf-8', 'replace').split(':', 1)
				if len(pwdata) != 2:
					self.server.reply(self, 400)
					self.socket.close()
					return
				self.data['user'] = pwdata[0]
				self.data['password'] = pwdata[1]
				if not self.server.authenticate(self):
					self.server.reply(self, 401, headers = {'WWW-Authenticate': 'Basic realm="%s"' % msg.replace('\n', ' ').replace('\r', ' ').replace('"', "'")})
					if 'content-length' not in self.headers or self.headers['content-length'].strip() != '0':
						self.socket.close()
					return
		if not is_websocket:
			if DEBUG > 4:
				log('Debug: not a websocket')
			self.body = self.socket.unread()
			if self.method.upper() == 'POST':
				if 'content-type' not in self.headers or self.headers['content-type'].lower().split(';')[0].strip() != 'multipart/form-data':
					log('Invalid Content-Type for POST; must be multipart/form-data (not %s)\n' % (self.headers['content-type'] if 'content-type' in self.headers else 'undefined'))
					self.server.reply(self, 500)
					self.socket.close()
					return
				args = self._parse_args(self.headers['content-type'])[1]
				if 'boundary' not in args:
					log('Invalid Content-Type for POST: missing boundary in %s\n' % (self.headers['content-type'] if 'content-type' in self.headers else 'undefined'))
					self.server.reply(self, 500)
					self.socket.close()
					return
				self.boundary = b'\r\n' + b'--' + args['boundary'].encode('utf-8') + b'\r\n'
				self.endboundary = b'\r\n' + b'--' + args['boundary'].encode('utf-8') + b'--\r\n'
				self.post_state = None
				self.post = [{}, {}]
				self.socket.read(self._post)
				self._post(b'')
			else:
				try:
					if not self.server.page(self):
						self.socket.close()
				except:
					if DEBUG > 0:
						traceback.print_exc()
					log('exception: %s\n' % repr(sys.exc_info()[1]))
					try:
						self.server.reply(self, 500)
					except:
						pass
					self.socket.close()
			return
		# Websocket.
		if self.method.upper() != 'GET' or 'sec-websocket-key' not in self.headers:
			if DEBUG > 2:
				log('Debug: invalid websocket')
			self.server.reply(self, 400)
			self.socket.close()
			return
		newkey = base64.b64encode(hashlib.sha1(self.headers['sec-websocket-key'].strip().encode('utf-8') + b'258EAFA5-E914-47DA-95CA-C5AB0DC85B11').digest()).decode('utf-8')
		headers = {'Sec-WebSocket-Accept': newkey, 'Connection': 'Upgrade', 'Upgrade': 'websocket', 'Sec-WebSocket-Version': '13'}
		self.server.reply(self, 101, None, None, headers)
		self.websocket(None, recv = self.server.recv, url = None, socket = self.socket, error = self.error, mask = (None, False), websockets = self.server.websockets, data = self.data, real_remote = self.headers.get('x-forwarded-for'))
コード例 #29
0
	def _handle_headers(self):	# {{{
		if DEBUG > 4:
			log('Debug: handling headers')
		is_websocket = 'connection' in self.headers and 'upgrade' in self.headers and 'upgrade' in self.headers['connection'].lower() and 'websocket' in self.headers['upgrade'].lower()
		self.data = {}
		self.data['url'] = self.url
		self.data['address'] = self.address
		self.data['query'] = self.query
		msg = self.server.auth_message(self, is_websocket) if callable(self.server.auth_message) else self.server.auth_message
		if msg:
			if 'authorization' not in self.headers:
				self.server.reply(self, 401, headers = {'WWW-Authenticate': 'Basic realm="%s"' % msg.replace('\n', ' ').replace('\r', ' ').replace('"', "'")})
				if 'content-length' not in self.headers or self.headers['content-length'].strip() != '0':
					self.socket.close()
				return
			else:
				auth = self.headers['authorization'].split(None, 1)
				if auth[0].lower() != 'basic':
					self.server.reply(self, 400)
					self.socket.close()
					return
				pwdata = base64.b64decode(auth[1].encode('utf-8')).decode('utf-8', 'replace').split(':', 1)
				if len(pwdata) != 2:
					self.server.reply(self, 400)
					self.socket.close()
					return
				self.data['user'] = pwdata[0]
				self.data['password'] = pwdata[1]
				if not self.server.authenticate(self):
					self.server.reply(self, 401, headers = {'WWW-Authenticate': 'Basic realm="%s"' % msg.replace('\n', ' ').replace('\r', ' ').replace('"', "'")})
					if 'content-length' not in self.headers or self.headers['content-length'].strip() != '0':
						self.socket.close()
					return
		if not is_websocket:
			if DEBUG > 4:
				log('Debug: not a websocket')
			self.body = self.socket.unread()
			if self.method.upper() == 'POST':
				if 'content-type' not in self.headers or self.headers['content-type'].lower().split(';')[0].strip() != 'multipart/form-data':
					log('Invalid Content-Type for POST; must be multipart/form-data (not %s)\n' % (self.headers['content-type'] if 'content-type' in self.headers else 'undefined'))
					self.server.reply(self, 500)
					self.socket.close()
					return
				args = self._parse_args(self.headers['content-type'])[1]
				if 'boundary' not in args:
					log('Invalid Content-Type for POST: missing boundary in %s\n' % (self.headers['content-type'] if 'content-type' in self.headers else 'undefined'))
					self.server.reply(self, 500)
					self.socket.close()
					return
				self.boundary = b'\r\n' + b'--' + args['boundary'].encode('utf-8') + b'\r\n'
				self.endboundary = b'\r\n' + b'--' + args['boundary'].encode('utf-8') + b'--\r\n'
				self.post_state = None
				self.post = [{}, {}]
				self.socket.read(self._post)
				self._post(b'')
			else:
				try:
					if not self.server.page(self):
						self.socket.close()
				except:
					if DEBUG > 0:
						traceback.print_exc()
					log('exception: %s\n' % repr(sys.exc_info()[1]))
					try:
						self.server.reply(self, 500)
					except:
						pass
					self.socket.close()
			return
		# Websocket.
		if self.method.upper() != 'GET' or 'sec-websocket-key' not in self.headers:
			if DEBUG > 2:
				log('Debug: invalid websocket')
			self.server.reply(self, 400)
			self.socket.close()
			return
		newkey = base64.b64encode(hashlib.sha1(self.headers['sec-websocket-key'].strip().encode('utf-8') + b'258EAFA5-E914-47DA-95CA-C5AB0DC85B11').digest()).decode('utf-8')
		headers = {'Sec-WebSocket-Accept': newkey, 'Connection': 'Upgrade', 'Upgrade': 'websocket', 'Sec-WebSocket-Version': '13'}
		self.server.reply(self, 101, None, None, headers)
		self.websocket(None, recv = self.server.recv, url = None, socket = self.socket, error = self.error, mask = (None, False), websockets = self.server.websockets, data = self.data, real_remote = self.headers.get('x-forwarded-for'))
コード例 #30
0
	def _websocket_read(self, data, sync = False):	# {{{
		# Websocket data consists of:
		# 1 byte:
		#	bit 7: 1 for last(or only) fragment; 0 for other fragments.
		#	bit 6-4: extension stuff; must be 0.
		#	bit 3-0: opcode.
		# 1 byte:
		#	bit 7: 1 if masked, 0 otherwise.
		#	bit 6-0: length or 126 or 127.
		# If 126:
		# 	2 bytes: length
		# If 127:
		#	8 bytes: length
		# If masked:
		#	4 bytes: mask
		# length bytes: (masked) payload

		#log('received: ' + repr(data))
		if DEBUG > 2:
			log('received %d bytes' % len(data))
		if DEBUG > 3:
			log('waiting: ' + ' '.join(['%02x' % x for x in self.websocket_buffer]) + ''.join([chr(x) if 32 <= x < 127 else '.' for x in self.websocket_buffer]))
			log('data: ' + ' '.join(['%02x' % x for x in data]) + ''.join([chr(x) if 32 <= x < 127 else '.' for x in data]))
		self.websocket_buffer += data
		while len(self.websocket_buffer) > 0:
			if self.websocket_buffer[0] & 0x70:
				# Protocol error.
				log('extension stuff %x, not supported!' % self.websocket_buffer[0])
				self.socket.close()
				return None
			if len(self.websocket_buffer) < 2:
				# Not enough data for length bytes.
				if DEBUG > 2:
					log('no length yet')
				return None
			b = self.websocket_buffer[1]
			have_mask = bool(b & 0x80)
			b &= 0x7f
			if have_mask and self.mask[0] is True or not have_mask and self.mask[0] is False:
				# Protocol error.
				log('mask error')
				self.socket.close()
				return None
			if b == 127:
				if len(self.websocket_buffer) < 10:
					# Not enough data for length bytes.
					if DEBUG > 2:
						log('no 4 length yet')
					return None
				l = struct.unpack('!Q', self.websocket_buffer[2:10])[0]
				pos = 10
			elif b == 126:
				if len(self.websocket_buffer) < 4:
					# Not enough data for length bytes.
					if DEBUG > 2:
						log('no 2 length yet')
					return None
				l = struct.unpack('!H', self.websocket_buffer[2:4])[0]
				pos = 4
			else:
				l = b
				pos = 2
			if len(self.websocket_buffer) < pos + (4 if have_mask else 0) + l:
				# Not enough data for packet.
				if DEBUG > 2:
					log('no packet yet(%d < %d)' % (len(self.websocket_buffer), pos + (4 if have_mask else 0) + l))
				# Long packets should not cause ping timeouts.
				self._pong = True
				return None
			header = self.websocket_buffer[:pos]
			opcode = header[0] & 0xf
			if have_mask:
				mask = [x for x in self.websocket_buffer[pos:pos + 4]]
				pos += 4
				data = self.websocket_buffer[pos:pos + l]
				# The following is slow!
				# Don't do it if the mask is 0; this is always true if talking to another program using this module.
				if mask != [0, 0, 0, 0]:
					data = bytes([x ^ mask[i & 3] for i, x in enumerate(data)])
			else:
				data = self.websocket_buffer[pos:pos + l]
			self.websocket_buffer = self.websocket_buffer[pos + l:]
			if self.opcode is None:
				self.opcode = opcode
			elif opcode != 0:
				# Protocol error.
				# Exception: pongs are sometimes sent asynchronously.
				# Theoretically the packet can be fragmented, but that should never happen; asynchronous pongs seem to be a protocol violation anyway...
				if opcode == 10:
					# Pong.
					self._pong = True
				else:
					log('invalid fragment')
					self.socket.close()
				return None
			if (header[0] & 0x80) != 0x80:
				# fragment found; not last.
				self._pong = True
				self.websocket_fragments += data
				if DEBUG > 2:
					log('fragment recorded')
				return None
			# Complete frame has been received.
			data = self.websocket_fragments + data
			self.websocket_fragments = b''
			opcode = self.opcode
			self.opcode = None
			if opcode == 8:
				# Connection close request.
				self.close()
				return None
			elif opcode == 9:
				# Ping.
				self.send(data, 10)	# Pong
			elif opcode == 10:
				# Pong.
				self._pong = True
			elif opcode == 1:
				# Text.
				data = data.decode('utf-8', 'replace')
				if sync:
					return data
				if self.recv:
					self.recv(self, data)
				else:
					log('warning: ignoring incoming websocket frame')
			elif opcode == 2:
				# Binary.
				if sync:
					return data
				if self.recv:
					self.recv(self, data)
				else:
					log('warning: ignoring incoming websocket frame (binary)')
			else:
				log('invalid opcode')
				self.socket.close()
コード例 #31
0
	def __init__(self, port, url = '/', recv = None, method = 'GET', user = None, password = None, extra = {}, socket = None, mask = (None, True), websockets = None, data = None, real_remote = None, *a, **ka): # {{{
		'''When constructing a Websocket, a connection is made to the
		requested port, and the websocket handshake is performed.  This
		constructor passes any extra arguments to the network.Socket
		constructor (if it is called), in particular "tls" can be used
		to control the use of encryption.  There objects are also
		created by the websockets server.  For that reason, there are
		some arguments that should not be used when calling it
		directly.
		@param port: Host and port to connect to, same format as
			python-network uses.
		@param url: The url to request at the host.
		@param recv: Function to call when a data packet is received
			asynchronously.
		@param method: Connection method to use.
		@param user: Username for authentication.  Only plain text
			authentication is supported; this should only be used
			over a link with TLS encryption.
		@param password: Password for authentication.
		@param extra: Extra headers to pass to the host.
		@param socket: Existing socket to use for connection, or None
			to create a new socket.
		@param mask: Mostly for internal use by the server.  Flag
			whether or not to send and receive masks.  (None, True)
			is the default, which means to accept anything, and
			send masked packets.  Note that the mask that is used
			for sending is always (0,0,0,0), which is effectively
			no mask.  It is sent to follow the protocol.  No real
			mask is sent, because masks give a false sense of
			security and provide no benefit.  The unmasking
			implementation is rather slow.  When communicating
			between two programs using this module, the non-mask is
			detected and the unmasking step is skipped.
		@param websockets: For interal use by the server.  A set to remove the socket from on disconnect.
		@param data: For internal use by the server.  Data to pass through to callback functions.
		@param real_remote: For internal use by the server.  Override detected remote.  Used to have proper remotes behind virtual proxy.
		'''
		self.recv = recv
		self.mask = mask
		self.websockets = websockets
		self.websocket_buffer = b''
		self.websocket_fragments = b''
		self.opcode = None
		self._is_closed = False
		self._pong = True	# If false, we're waiting for a pong.
		if socket is None:
			socket = network.Socket(port, *a, **ka)
		self.socket = socket
		# Use real_remote if it was provided.
		if real_remote:
			if isinstance(socket.remote, (tuple, list)):
				self.remote = [real_remote, socket.remote[1]]
			else:
				self.remote = [real_remote, None]
		else:
			self.remote = socket.remote
		hdrdata = b''
		if url is not None:
			elist = []
			for e in extra:
				elist.append('%s: %s\r\n' % (e, extra[e]))
			if user is not None:
				userpwd = user + ':' + password + '\r\n'
			else:
				userpwd = ''
			socket.send(('''\
%s %s HTTP/1.1\r
Connection: Upgrade\r
Upgrade: websocket\r
Sec-WebSocket-Key: 0\r
%s%s\r
''' % (method, url, userpwd, ''.join(elist))).encode('utf-8'))
			while b'\n' not in hdrdata:
				r = socket.recv()
				if r == b'':
					raise EOFError('EOF while reading reply')
				hdrdata += r
			pos = hdrdata.index(b'\n')
			assert int(hdrdata[:pos].split()[1]) == 101
			hdrdata = hdrdata[pos + 1:]
			data = {}
			while True:
				while b'\n' not in hdrdata:
					r = socket.recv()
					if len(r) == 0:
						raise EOFError('EOF while reading reply')
					hdrdata += r
				pos = hdrdata.index(b'\n')
				line = hdrdata[:pos].strip()
				hdrdata = hdrdata[pos + 1:]
				if len(line) == 0:
					break
				key, value = [x.strip() for x in line.decode('utf-8', 'replace').split(':', 1)]
				data[key] = value
		self.data = data
		self.socket.read(self._websocket_read)
		def disconnect(socket, data):
			if not self._is_closed:
				self._is_closed = True
				if self.websockets is not None:
					self.websockets.remove(self)
				self.closed()
			return b''
		if self.websockets is not None:
			self.websockets.add(self)
		self.socket.disconnect_cb(disconnect)
		self.opened()
		if len(hdrdata) > 0:
			self._websocket_read(hdrdata)
		if DEBUG > 2:
			log('opened websocket')