A standalone test case for the API thread #1900

Closed
g1itch wants to merge 3 commits from api-standalone into v0.6
Showing only changes of commit a44ce07949 - Show all commits

View File

@ -57,28 +57,25 @@ For further examples please reference `.tests.test_api`.
""" """
import base64 import base64
import ConfigParser
import errno import errno
import hashlib import hashlib
import httplib
import json import json
import random # nosec import random # nosec
import socket import socket
import subprocess import subprocess
import time import time
import xmlrpclib
from binascii import hexlify, unhexlify from binascii import hexlify, unhexlify
from SimpleXMLRPCServer import SimpleXMLRPCRequestHandler, SimpleXMLRPCServer
from struct import pack from struct import pack
from six.moves import configparser, http_client, queue, xmlrpc_server
import defaults import defaults
import helper_inbox import helper_inbox
import helper_sent import helper_sent
import network.stats
import proofofwork import proofofwork
import queues import queues
import shared import shared
import shutdown
import state import state
from addresses import ( from addresses import (
addBMIfNotPresent, addBMIfNotPresent,
@ -90,9 +87,21 @@ from addresses import (
from bmconfigparser import BMConfigParser from bmconfigparser import BMConfigParser
from debug import logger from debug import logger
from helper_sql import SqlBulkExecute, sqlExecute, sqlQuery, sqlStoredProcedure, sql_ready from helper_sql import SqlBulkExecute, sqlExecute, sqlQuery, sqlStoredProcedure, sql_ready
from inventory import Inventory
# TODO: solve the issue with next two imports and remove the cutout
# FIXME: perhaps this module shouldn't use the Inventory at all
try:
import shutdown
from inventory import Inventory
except ImportError:
Inventory = None
try:
import network.stats as network_stats
except ImportError:
network_stats = None
from network.threads import StoppableThread from network.threads import StoppableThread
from six.moves import queue
from version import softwareVersion from version import softwareVersion
try: # TODO: write tests for XML vulnerabilities try: # TODO: write tests for XML vulnerabilities
@ -162,7 +171,7 @@ class ErrorCodes(type):
return result return result
class APIError(xmlrpclib.Fault): class APIError(xmlrpc_server.Fault):
""" """
APIError exception class APIError exception class
@ -210,7 +219,7 @@ class singleAPI(StoppableThread):
except AttributeError: except AttributeError:
errno.WSAEADDRINUSE = errno.EADDRINUSE errno.WSAEADDRINUSE = errno.EADDRINUSE
RPCServerBase = SimpleXMLRPCServer RPCServerBase = xmlrpc_server.SimpleXMLRPCServer
ct = 'text/xml' ct = 'text/xml'
if BMConfigParser().safeGet( if BMConfigParser().safeGet(
'bitmessagesettings', 'apivariant') == 'json': 'bitmessagesettings', 'apivariant') == 'json':
@ -351,7 +360,7 @@ class command(object): # pylint: disable=too-few-public-methods
# Modified by Jonathan Warren (Atheros). # Modified by Jonathan Warren (Atheros).
# Further modified by the Bitmessage developers # Further modified by the Bitmessage developers
# http://code.activestate.com/recipes/501148 # http://code.activestate.com/recipes/501148
class BMXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): class BMXMLRPCRequestHandler(xmlrpc_server.SimpleXMLRPCRequestHandler):
"""The main API handler""" """The main API handler"""
# pylint: disable=protected-access # pylint: disable=protected-access
@ -392,7 +401,7 @@ class BMXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
validuser = self.APIAuthenticateClient() validuser = self.APIAuthenticateClient()
if not validuser: if not validuser:
time.sleep(2) time.sleep(2)
self.send_response(httplib.UNAUTHORIZED) self.send_response(http_client.UNAUTHORIZED)
self.end_headers() self.end_headers()
return return
# "RPC Username or password incorrect or HTTP header" # "RPC Username or password incorrect or HTTP header"
@ -409,11 +418,11 @@ class BMXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
) )
except Exception: # This should only happen if the module is buggy except Exception: # This should only happen if the module is buggy
# internal error, report as HTTP server error # internal error, report as HTTP server error
self.send_response(httplib.INTERNAL_SERVER_ERROR) self.send_response(http_client.INTERNAL_SERVER_ERROR)
self.end_headers() self.end_headers()
else: else:
# got a valid XML RPC response # got a valid XML RPC response
self.send_response(httplib.OK) self.send_response(http_client.OK)
self.send_header("Content-type", self.server.content_type) self.send_header("Content-type", self.server.content_type)
self.send_header("Content-length", str(len(response))) self.send_header("Content-length", str(len(response)))
@ -858,7 +867,7 @@ class BMRPCDispatcher(object):
' Use deleteAddress API call instead.') ' Use deleteAddress API call instead.')
try: try:
self.config.remove_section(address) self.config.remove_section(address)
except ConfigParser.NoSectionError: except configparser.NoSectionError:
raise APIError( raise APIError(
13, 'Could not find this address in your keys.dat file.') 13, 'Could not find this address in your keys.dat file.')
self.config.save() self.config.save()
@ -875,7 +884,7 @@ class BMRPCDispatcher(object):
address = addBMIfNotPresent(address) address = addBMIfNotPresent(address)
try: try:
self.config.remove_section(address) self.config.remove_section(address)
except ConfigParser.NoSectionError: except configparser.NoSectionError:
raise APIError( raise APIError(
13, 'Could not find this address in your keys.dat file.') 13, 'Could not find this address in your keys.dat file.')
self.config.save() self.config.save()
@ -1266,6 +1275,8 @@ class BMRPCDispatcher(object):
requiredPayloadLengthExtraBytes): requiredPayloadLengthExtraBytes):
"""Handle a request to disseminate an encrypted message""" """Handle a request to disseminate an encrypted message"""
if Inventory is None:
raise APIError(21, 'Could not import Inventory.')
# The device issuing this command to PyBitmessage supplies a msg # The device issuing this command to PyBitmessage supplies a msg
# object that has already been encrypted but which still needs the POW # object that has already been encrypted but which still needs the POW
# to be done. PyBitmessage accepts this msg object and sends it out # to be done. PyBitmessage accepts this msg object and sends it out
@ -1322,6 +1333,8 @@ class BMRPCDispatcher(object):
def HandleDissimatePubKey(self, payload): def HandleDissimatePubKey(self, payload):
"""Handle a request to disseminate a public key""" """Handle a request to disseminate a public key"""
if Inventory is None:
raise APIError(21, 'Could not import Inventory.')
# The device issuing this command to PyBitmessage supplies a pubkey # The device issuing this command to PyBitmessage supplies a pubkey
# object to be disseminated to the rest of the Bitmessage network. # object to be disseminated to the rest of the Bitmessage network.
# PyBitmessage accepts this pubkey object and sends it out to the rest # PyBitmessage accepts this pubkey object and sends it out to the rest
@ -1411,7 +1424,10 @@ class BMRPCDispatcher(object):
or "connectedAndReceivingIncomingConnections". or "connectedAndReceivingIncomingConnections".
""" """
connections_num = len(network.stats.connectedHostsList()) try:
connections_num = len(network_stats.connectedHostsList())
except AttributeError:
raise APIError(21, 'Could not import network_stats.')
if connections_num == 0: if connections_num == 0:
networkStatus = 'notConnected' networkStatus = 'notConnected'
elif state.clientHasReceivedIncomingConnections: elif state.clientHasReceivedIncomingConnections:
@ -1423,7 +1439,7 @@ class BMRPCDispatcher(object):
'numberOfMessagesProcessed': state.numberOfMessagesProcessed, 'numberOfMessagesProcessed': state.numberOfMessagesProcessed,
'numberOfBroadcastsProcessed': state.numberOfBroadcastsProcessed, 'numberOfBroadcastsProcessed': state.numberOfBroadcastsProcessed,
'numberOfPubkeysProcessed': state.numberOfPubkeysProcessed, 'numberOfPubkeysProcessed': state.numberOfPubkeysProcessed,
'pendingDownload': network.stats.pendingDownload(), 'pendingDownload': network_stats.pendingDownload(),
'networkStatus': networkStatus, 'networkStatus': networkStatus,
'softwareName': 'PyBitmessage', 'softwareName': 'PyBitmessage',
'softwareVersion': softwareVersion 'softwareVersion': softwareVersion
@ -1475,6 +1491,8 @@ class BMRPCDispatcher(object):
@command('shutdown') @command('shutdown')
def HandleShutdown(self): def HandleShutdown(self):
"""Shutdown the bitmessage. Returns 'done'.""" """Shutdown the bitmessage. Returns 'done'."""
if Inventory is None:
raise APIError(21, 'Could not import shutdown.')
# backward compatible trick because False == 0 is True # backward compatible trick because False == 0 is True
state.shutdown = False state.shutdown = False
return 'done' return 'done'