Commit 606faf47 authored by xa's avatar xa

add tls

parent 3b0ee487
[run]
omit = */__main__.py
omit =
*/__main__.py
tcp~/*
[report]
exclude_lines =
......
......@@ -35,4 +35,5 @@ class Application(web.Application):
headers = {
'Content-Type': 'application/json',
}
self.log.info('resp %s %s', headers, body)
return web.Response(body=body, headers=headers)
......@@ -2,6 +2,7 @@ import asyncio
from aiohttp import ClientSession, TCPConnector
from .exceptions import TopicNotFound, ChannelNotFound
from .exceptions import HTTPInternalServerError, HTTPNotFound
from aionsq.util import parse_addr
__all__ = ['RequestHandler']
......@@ -9,7 +10,7 @@ __all__ = ['RequestHandler']
class RequestHandler:
def __init__(self, addr, *, loop=None):
self.addr = addr
self.addr = parse_addr(addr, proto='http', host='127.0.0.1')
self.loop = loop or asyncio.get_event_loop()
connector = TCPConnector(verify_ssl=False)
self.session = ClientSession(connector=connector, loop=self.loop)
......
from .client import *
from .connection import *
from .protocol import *
from .exceptions import *
from .handlers import *
from .helpers import *
__all__ = ['NSQReader', 'NSQWriter']
import asyncio
import logging
from .connection import NSQConnection
from .exceptions import NotStartedError
from . import exceptions
from .connection import Connection
from .message import Message
from weakref import WeakKeyDictionary, WeakSet
class TCPHandler:
def __init__(self, addr, *, auth_secret=None, loop=None):
"""
Parameters:
addr (str): NSQ server address.
auth_secret (str): if auth is required by NSQ server.
loop (EventLoop): asyncio event loop.
"""
def __init__(self, addr, *, auth_secret=None, cert=None, loop=None):
self.loop = loop or asyncio.get_event_loop()
self._conn = Connection(addr,
cert=cert,
auth_secret=auth_secret,
loop=self.loop)
self._started = False
self._connection = NSQConnection(addr, auth_secret=auth_secret)
self._openened = asyncio.Lock(loop=self.loop)
self.log = logging.getLogger(__name__)
......@@ -27,12 +25,12 @@ class TCPHandler:
@asyncio.coroutine
def start(self, **opts):
if not self._started:
yield from self._connection.start()
yield from self._connection.connect(**opts)
yield from self._conn.start()
response = yield from self._conn.connect(**opts)
yield from self.start_warmup()
yield from self._openened.acquire() # must be the last stmt
self._started = True
return True
return response
@asyncio.coroutine
def start_warmup(self):
......@@ -49,7 +47,7 @@ class TCPHandler:
def force_close(self):
self.log.debug('force close')
self._connection.close()
self._conn.close()
self._openened.release()
@asyncio.coroutine
......@@ -79,9 +77,9 @@ class NSQWriter(TCPHandler):
message (str): raw message bytes
"""
if not self._started:
raise NotStartedError()
response = yield from self._connection.pub(topic, message)
return response == 'OK'
raise exceptions.NotStartedError()
yield from self._conn.cmd('PUB', topic, body=message)
return True
@asyncio.coroutine
def multi_publish(self, topic, messages):
......@@ -93,15 +91,15 @@ class NSQWriter(TCPHandler):
"""
assert isinstance(messages, (list, set))
if not self._started:
raise NotStartedError()
response = yield from self._connection.mpub(topic, messages)
return response == 'OK'
raise exceptions.NotStartedError()
yield from self._conn.cmd('MPUB', topic, body=messages)
return True
class NSQReader(TCPHandler):
def __init__(self, addr, topic, channel, *, loop=None):
super().__init__(addr, loop=loop)
def __init__(self, addr, topic, channel, *args, **kwargs):
super().__init__(addr, *args, **kwargs)
self.topic = topic
self.channel = channel
self._consumers = WeakKeyDictionary()
......@@ -123,8 +121,8 @@ class NSQReader(TCPHandler):
@asyncio.coroutine
def start_warmup(self):
self._connection.add_message_listener(self.import_message)
yield from self._connection.sub(self.topic, self.channel)
self._conn.add_message_listener(self.import_message)
yield from self._conn.cmd('SUB', self.topic, self.channel)
yield from self.ask_messages(1)
def register(self, message_handler):
......@@ -147,7 +145,7 @@ class NSQReader(TCPHandler):
return message_handler
def import_message(self, frame):
msg = NSQMessage(self._connection, frame)
msg = Message(self._conn, frame)
self.log.info('msg %s', msg)
self._msgs.add(msg)
for consumer in self._consumers.values():
......@@ -160,41 +158,11 @@ class NSQReader(TCPHandler):
@asyncio.coroutine
def ask_messages(self, count):
yield from self._connection.rdy(count)
yield from self._conn.cmd('RDY', count)
self._msg_rdy = count
@asyncio.coroutine
def pre_close(self):
response = yield from self._connection.cls()
return response == 'CLOSE_WAIT'
class NSQMessage:
def __init__(self, connection, frame):
self._connection = connection
self.frame = frame
@property
def body(self):
return self.frame.body
@property
def timestamp(self):
return int(self.frame.timestamp)
@property
def attempts(self):
return int(self.frame.attempts)
@property
def id(self):
return self.frame.id
@asyncio.coroutine
def success(self):
yield from self._connection.fin(self.id)
@asyncio.coroutine
def error(self):
yield from self._connection.req(self.id, 0)
yield from self._conn.cmd('CLS')
# TODO ensure that response == 'CLOSE_WAIT'
return True
This diff is collapsed.
......@@ -11,10 +11,12 @@ class MessageError(ValueError):
class ServerError(Exception):
pass
class AuthFailedError(ServerError):
pass
class IdentifyFailedError(ServerError):
pass
import logging
import snappy
import zlib
class DeflateHandler:
def __init__(self, level):
wbits = -zlib.MAX_WBITS
self._compressor = zlib.compressobj(level, zlib.DEFLATED, wbits)
self._decompressor = zlib.decompressobj(wbits)
self.log = logging.getLogger(__name__)
def compress(self, data):
chunk = self._compressor.compress(data)
return chunk + self._compressor.flush(zlib.Z_SYNC_FLUSH)
def decompress(self, chunk):
try:
data = self._decompressor.decompress(chunk)
self.log.debug('decompress %s to %s', chunk, data)
return data
except zlib.error:
self.log.warning('unable to decompress %s' % chunk)
raise
class SnappyHandler:
def __init__(self):
self._compressor = snappy.StreamCompressor()
self._decompressor = snappy.StreamDecompressor()
self.log = logging.getLogger(__name__)
def compress(self, data):
return self._compressor.add_chunk(data, compress=True)
def decompress(self, chunk):
try:
data = self._decompressor.decompress(chunk)
self.log.debug('decompress %s to %s', chunk, data)
return data
except Exception:
self.log.warning('unable to decompress %s' % chunk)
raise
import struct
from collections import namedtuple
RawResponse = namedtuple('RawResponse', 'type body')
RawError = namedtuple('RawError', 'type error body')
RawMessage = namedtuple('RawMessage', 'type body timestamp attempts id')
def to_size(data):
size, *_ = struct.unpack('>l', data)
return size
def parse_frame(data):
frame_type, *_ = struct.unpack('>l', data[:4])
try:
if frame_type == 0:
return RawResponse(frame_type, data[4:].decode('utf-8'))
elif frame_type == 1:
error, body = data[4:].decode('utf-8').split(None, 1)
return RawError(frame_type, error, body)
elif frame_type == 2:
timestamp = struct.unpack('>q', data[4:12])[0]
attempts = struct.unpack('>h', data[12:14])[0]
message_id = data[14:30].decode('utf-8')
body = data[30:].decode('utf-8')
return RawMessage(frame_type, body, timestamp,
attempts, message_id)
except UnicodeDecodeError as error:
raise MessageError(data) from error
def prepare_cmd(cmd, *params, body=None):
output = bytearray()
output += scalar(cmd)
for param in params:
output += b' '
output += scalar(param)
output += b'\n'
if body is not None:
if isinstance(body, (list, set)):
buf = bytearray()
buf += scalar_len(body)
for o in body:
a = scalar(o)
buf += scalar_len(a)
buf += a
else:
buf = scalar(body)
output += scalar_len(buf)
output += buf
return bytes(output)
def scalar(obj):
orig = obj
if isinstance(obj, str):
return bytearray(obj, encoding='utf-8')
if isinstance(obj, (int, float)):
return bytearray('%s' % obj, encoding='utf-8')
if isinstance(obj, bytes):
return bytearray(obj)
if isinstance(obj, bytearray):
return obj.copy()
msg = "Don't know how to handle this %r" % orig.__class__.__name__
raise NotImplementedError(msg)
def scalar_len(obj):
return struct.pack('>l', len(obj))
import struct
import importlib
from .exceptions import MessageError
from collections import namedtuple
if importlib.find_loader('snappy'):
import snappy
if importlib.find_loader('zlib'):
import zlib
RawResponse = namedtuple('RawResponse', 'type body')
RawError = namedtuple('RawError', 'type error body')
RawMessage = namedtuple('RawMessage', 'type body timestamp attempts id')
class DeflateLayer:
def __init__(self, level):
self.level = level
wbits = -zlib.MAX_WBITS
self._compressor = zlib.compressobj(level, zlib.DEFLATED, wbits)
self._decompressor = zlib.decompressobj(wbits)
def write(self, data):
data = self._compressor.compress(data)
return data + self._compressor.flush(zlib.Z_SYNC_FLUSH)
def read(self, data):
data = self._decompressor.decompress(data)
return data, self._decompressor.unconsumed_tail
class SnappyLayer:
def __init__(self):
self._compressor = snappy.StreamCompressor()
self._decompressor = snappy.StreamDecompressor()
def write(self, data):
if isinstance(data, bytearray):
data = bytes(data)
return self._compressor.add_chunk(data, compress=True)
def read(self, data):
return self._decompressor.decompress(data), b''
class SpecLayer:
def __init__(self):
pass
def parse_protocol(self, data):
frame_type, *_ = struct.unpack('>l', data[:4])
try:
if frame_type == 0:
return RawResponse(frame_type, data[4:].decode('utf-8'))
elif frame_type == 1:
error, body = data[4:].decode('utf-8').split(None, 1)
return RawError(frame_type, error, body)
elif frame_type == 2:
timestamp = struct.unpack('>q', data[4:12])[0]
attempts = struct.unpack('>h', data[12:14])[0]
message_id = data[14:30].decode('utf-8')
body = data[30:].decode('utf-8')
return RawMessage(frame_type, body, timestamp,
attempts, message_id)
except UnicodeDecodeError as error:
raise MessageError(data) from error
def prepare_cmd(self, cmd, *params, body=None):
output = bytearray()
output += scalar(cmd)
for param in params:
output += b' '
output += scalar(param)
output += b'\n'
if body is not None:
if isinstance(body, (list, set)):
buf = bytearray()
buf += scalar_len(body)
for o in body:
a = scalar(o)
buf += scalar_len(a)
buf += a
else:
buf = scalar(body)
output += scalar_len(buf)
output += buf
return output
def scalar(obj):
orig = obj
if isinstance(obj, str):
return bytearray(obj, encoding='utf-8')
if isinstance(obj, (int, float)):
return bytearray('%s' % obj, encoding='utf-8')
if isinstance(obj, bytes):
return bytearray(obj)
if isinstance(obj, bytearray):
return obj.copy()
msg = "Don't know how to handle this %r" % orig.__class__.__name__
raise NotImplementedError(msg)
def scalar_len(obj):
return struct.pack('>l', len(obj))
import asyncio
class Message:
def __init__(self, connection, frame):
self._conn = connection
self.frame = frame
@property
def body(self):
return self.frame.body
@property
def timestamp(self):
return int(self.frame.timestamp)
@property
def attempts(self):
return int(self.frame.attempts)
@property
def id(self):
return self.frame.id
@asyncio.coroutine
def success(self):
yield from self._conn.cmd('FIN', self.id)
@asyncio.coroutine
def error(self):
yield from self._conn.cmd('REQ', self.id, 0)
import asyncio
import logging
import struct
from . import exceptions
from .layers import SpecLayer, DeflateLayer, SnappyLayer
class NSQProtocol(asyncio.Protocol):
def __init__(self):
self.listeners = set()
self.buf1 = bytearray()
self.buf = bytearray()
self.log = logging.getLogger(__name__)
self.spec = SpecLayer()
self.z = None
def upgrade_deflate(self, level):
self.log.info('upgrade deflate')
self.z = DeflateLayer(level)
def upgrade_snappy(self):
self.log.info('upgrade snappy')
self.z = SnappyLayer()
def connection_made(self, transport):
self.transport = transport
def connection_lost(self, exc):
self.transport = None
def data_received(self, data):
if self.z:
self.buf1.extend(data)
data, self.buf1[:] = self.z.read(self.buf1)
self.log.info('read %r', data)
self.buf.extend(data)
self.process_buffer()
def process_buffer(self):
while len(self.buf) > 4:
length, *_ = struct.unpack('>l', self.buf[:4])
part = length + 4
if len(self.buf) < part:
break
data, self.buf[0:part] = self.buf[4:part], []
frame = self.spec.parse_protocol(data)
self.log.info('got %s', frame)
self.notify_listeners(frame)
def notify_listeners(self, frame):
for listener in self.listeners:
listener(frame)
def send_cmd(self, cmd, *params, body=None):
msg = bytes(self.spec.prepare_cmd(cmd, *params, body=body))
self.log.info('send %s', msg)
if self.z:
msg = self.z.write(msg)
self.transport.write(msg)
def add_listener(self, callback):
self.listeners.add(callback)
def blocking(protocol):
return BlockingProtocol(protocol)
class BlockingProtocol:
def __init__(self, protocol):
self.protocol = protocol
self.transport = protocol.transport
self.buf1 = bytearray()
self.buf = bytearray()
self.log = logging.getLogger(__name__)
def __enter__(self):
self.transport.pause_reading()
self.transport._sock.setblocking(True)
return self
def __exit__(self, type, value, traceback):
self.transport._sock.setblocking(False)
self.transport.resume_reading()
def _read(self, size):
while len(self.buf) < size:
data = self.transport._sock.recv(512)
if self.protocol.z:
self.buf1.extend(data)
data, self.buf1[:] = self.protocol.z.read(self.buf1)
self.log.info('read %r', data)
self.buf.extend(data)
response, self.buf[:size] = self.buf[:size], []
return response
def read(self):
data = self._read(4)
size, *_ = struct.unpack('>l', data)
data = self._read(size)
frame = self.protocol.spec.parse_protocol(data)
self.log.info('got %s', frame)
if frame.type == 0:
return frame.body
if frame.type == 1:
raise exc(frame)
raise Exception(frame)
def exc(frame):
if frame.error == 'E_AUTH_FAILED':
return exceptions.AuthFailedError(frame.body)
return exceptions.ServerError(frame.error, frame.body)
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: DES-EDE3-CBC,AE885C3519745DDD
15dfD2SBkWxJJR9hlUdL0CSqNy7VTf599NDsfkJRQQejrz3AZwIMTVNLlTGCqE+V
VCI2+euusp3gCn8f6slWIiMTwEUxXzeS6QWkZyY0Tmd5CDAKYTV/UxozVzYCO1uL
oupbT3/tLQXvF6wSst4YKzJtDJM2u3Ua5Xu/Mhun3y8TlejRz8S2e55JPP/fzjEH
xBZoP1VPQuiQKjNx6IewaRXw4vodRJWhgZEuZG28U9RIKcx9yZsOsKrYf/kRxT79
H6nRLRo/XDj9jutEZ58YBQkIVy88NIW11AmG7y1TBJiX3qawmgYC8yMvHhLkn9QD
pV6JPbN4z+UmwEK5JTUrmYHzuJKLVsqwe8Xqd3qFebTI9GpJDvESDuOhGjm8YDvx
cLbHO1re98FxRVZg/LmErGpflHsSpJEvOgyo1Oq/K6EZYPbpO/SgrHp19nspXKg4
yL2dDBoJaiTsxIs4RZtOP+SGDiuvnP/+ZhGzOMI6+WRAeudnp+0ImneNWNqAeswg
nB9reHTJmqZBYfi+ML38WtHlQ3EEQFKgQsESA+47Zgaft+oLpjW0BxaDJapFbv8D
8ODBBLoq6d/uEXBa+5tGWwYhdpXJzwDDuubbq0BRN8P3Jsk0SpiLUH7IRoYOMly5
XGcv9FoV66mQKrH3wpIKwhx00bhM5OXuFKt+JAtLYCPxlJePsWTj6RIQzxRNNUXV
6EpowgV451BB+aBP54prwM5UELhN2PxMohbzhxzuOQnTUZkFBpRMXb38PDoMsHNg
90dpm25XGvhD+EZQ4mfBO2Xax+7unqovei5n9YHoAVE=
-----END RSA PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
MIIDnDCCAwWgAwIBAgIJANg7HLn2pi6hMA0GCSqGSIb3DQEBBQUAMIGRMQswCQYD
VQQGEwJERTEMMAoGA1UECBMDTlJXMQ4wDAYDVQQHEwVFYXJ0aDEXMBUGA1UEChMO
UmFuZG9tIENvbXBhbnkxCzAJBgNVBAsTAklUMRcwFQYDVQQDEw53d3cucmFuZG9t
LmNvbTElMCMGCSqGSIb3DQEJARYWS3J5cHRvS2luZ3NAcmFuZG9tLmNvbTAeFw0x
NTA0MTMxNzI1MTdaFw0xNjA0MTIxNzI1MTdaMIGRMQswCQYDVQQGEwJERTEMMAoG
A1UECBMDTlJXMQ4wDAYDVQQHEwVFYXJ0aDEXMBUGA1UEChMOUmFuZG9tIENvbXBh
bnkxCzAJBgNVBAsTAklUMRcwFQYDVQQDEw53d3cucmFuZG9tLmNvbTElMCMGCSqG
SIb3DQEJARYWS3J5cHRvS2luZ3NAcmFuZG9tLmNvbTCBnzANBgkqhkiG9w0BAQEF
AAOBjQAwgYkCgYEAyZdYaKNt+3F7PTi3+hDfMxtxb2+seDCM9EgqLpUsvzCYa445
/4WFrlQbGSn+3ynGsP1ggoB2D2peR+1DOLcU3+SLzSsegdtM0lEfOjhdaGfb0cg9
wUGhC+dKVWT2BRQCt7njS3+/rkSYTUFHbnapo/2ag5xfjsJmAuuXdAoSbWMCAwEA
AaOB+TCB9jAdBgNVHQ4EFgQU4MUUj4TibmiCYe9sOx4HW9TOOt8wgcYGA1UdIwSB
vjCBu4AU4MUUj4TibmiCYe9sOx4HW9TOOt+hgZekgZQwgZExCzAJBgNVBAYTAkRF
MQwwCgYDVQQIEwNOUlcxDjAMBgNVBAcTBUVhcnRoMRcwFQYDVQQKEw5SYW5kb20g
Q29tcGFueTELMAkGA1UECxMCSVQxFzAVBgNVBAMTDnd3dy5yYW5kb20uY29tMSUw
IwYJKoZIhvcNAQkBFhZLcnlwdG9LaW5nc0ByYW5kb20uY29tggkA2DscufamLqEw
DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOBgQCTa5K8K+ZTSTIoEy+FyfxL
PEezAZX8iZAyvo6ZuL4kNSXhSDIyqb+tbINL/FHJ0UIhhRHxd1Hov6k/X2b/fYO8
o4hZ8pwNjF80YFkdlW0QyWaB2UC5xX4z7AwrjwEejN8WKYiZtSCTeuM64dCvowLr
Uibi7AO1eCouM9wPwSK6Ow==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIEbjCCA1agAwIBAgIJAK6x7y6AwBmLMA0GCSqGSIb3DQEBBQUAMIGAMQswCQYD
VQQGEwJVUzERMA8GA1UECBMITmV3IFlvcmsxFjAUBgNVBAcTDU5ldyBZb3JrIENp
dHkxDDAKBgNVBAoTA05TUTETMBEGA1UEAxMKdGVzdC5sb2NhbDEjMCEGCSqGSIb3
DQEJARYUbXJlaWZlcnNvbkBnbWFpbC5jb20wHhcNMTMwNjI4MDA0MzQ4WhcNMTYw
NDE3MDA0MzQ4WjCBgDELMAkGA1UEBhMCVVMxETAPBgNVBAgTCE5ldyBZb3JrMRYw
FAYDVQQHEw1OZXcgWW9yayBDaXR5MQwwCgYDVQQKEwNOU1ExEzARBgNVBAMTCnRl
c3QubG9jYWwxIzAhBgkqhkiG9w0BCQEWFG1yZWlmZXJzb25AZ21haWwuY29tMIIB
IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnX0KB+svwy+yHU2qggz/EaGg
craKShagKo+9M9y5HLM852ngk5c+t+tJJbx3N954Wr1FXBuGIv1ltU05rU4zhvBS
25tVP1UIEnT5pBt2TeetLkl199Y7fxh1hKmnwJMG3fy3VZdNXEndBombXMmtXpQY
shuEJHKeUNDbQKz5X+GjEdkTPO/HY/VMHsxS23pbSimQozMg3hvLIdgv0aS3QECz
ydZBgTPThy3uDtHIuCpxCwXd/vDF68ATlYgo3h3lh2vxNwM/pjklIUhzMh4XaKQF
7m3/0KbtUcXfy0QHueeuMr11E9MAFNyRN4xf9Fk1yB97KJ3PJBTC5WD/m1nW+QID
AQABo4HoMIHlMB0GA1UdDgQWBBR3HMBws4lmYYSIgwoZsfW+bbgaMjCBtQYDVR0j
BIGtMIGqgBR3HMBws4lmYYSIgwoZsfW+bbgaMqGBhqSBgzCBgDELMAkGA1UEBhMC
VVMxETAPBgNVBAgTCE5ldyBZb3JrMRYwFAYDVQQHEw1OZXcgWW9yayBDaXR5MQww
CgYDVQQKEwNOU1ExEzARBgNVBAMTCnRlc3QubG9jYWwxIzAhBgkqhkiG9w0BCQEW
FG1yZWlmZXJzb25AZ21haWwuY29tggkArrHvLoDAGYswDAYDVR0TBAUwAwEB/zAN
BgkqhkiG9w0BAQUFAAOCAQEANOYTbanW2iyV1v4oYpcM/y3TWcQKzSME8D2SGFZb
dbMYU81hH3TTlQdvyeh3FAcdjhKE8Xi/RfNNjEslTBscdKXePGpZg6eXRNJzPP5K
KZPf5u6tcpAeUOKrMqbGwbE+h2QixxG1EoVQtE421szsU2P7nHRTdHzKFRnOerfl
Phm3NocR0P40Rv7WKdxpOvqc+XKf0onTruoVYoPWGpwcLixCG0zu4ZQ23/L/Dy18
4u70Hbq6O/6kq9FBFaDNp3IhiEdu2Cq6ZplU6bL9XDF27KIEErHwtuqBHVlMG+zB
oH/k9vZvwH7OwAjHdKp+1yeZFLYC8K5hjFIHqcdwpZCNIg==
-----END CERTIFICATE-----
-----BEGIN RSA PRIVATE KEY-----
MIIEpQIBAAKCAQEAmlH8HxZEd9tKfCd9byA75iA8dqSclD0TVjdHp1dVyJ2p7+cE
rYeVSTl2xpBnU/R7AExjccOzQbaquDreZKS/ROJtqFOTGqgbIWrrprqbnQHaE5v0
Us7CnAXbK84C8Z+WaN/r0islLJPwBJarwjXdpnb+XZ7RuEk+XTazxy45qku7GPTt
GoWChgNUvLJB6o5v2pKxGA2gRllwx9hzuGHPhRPJLTKGhEEgusCqZZnmbt5ZahGR
vuZXm+BP6283474sThXjw/QUvi/bAgK31gOc8OEqDUEKHaaZZAniMcRFAa2eWSqW
TqQudo/h5fPyZcA6Vg9e/QNkvOXZFmCHIUEA8QIDAQABAoIBAHoHIHW4lvWsCy83
zuFDvPnRPd6XdlVmIldHqTpzPhtASTOyGynZD7xyWzDS2VIy/dyTiwNXu/TXrBG4
Q0mUWMbwwcOcKrOmgKWhNayG3Sx/XWGqLb4ZTxwvKUtvQTatejN99a0gfunMJbyL
JFCTZa0PZT7EqpOuH0l4SxrSu7ijpVA1VgB/I0emVkn8my/CWKvtJc/B7u1jpNzU
CF4a+L/4iOTnA9+4BdmuzKzfWvCC6+/S2Pp/Qw/PS5sJABqF8Kw33OThl9LkZrab
DlQtXQyYzm5EXkjE/1V5BEyyVcLWLpLPE9/Jen2FyV0SFRYEr8gzRZ/a+uvN+G0W
nS3gDd0CgYEAzXOcKByuq/BvvKJMQMbAIRczlnvsnpHZfTUYoJUYWfDayPG/tCoa
jzTZeBxo12MHinVj6O51i7g/FffKwmVmkB+dvzi5gYnMH+KvNphSa+jq3RzJ/L6Q
9e0w+e37b7J8hqbGaPWkw3KvUTQ8p97YvDFDorUzuItrKKYkDcAClj8CgYEAwEnc
mCr1oTHqhtOj1M62sBt5sRWaJOzG9MqC5m1sVKjrXcHMmG7omYHjgm6s5dkkRMYA
nG7i7gxVLfxVaP42CuTCL6y5S2kqkEOKqz+o5XX5ZdD1nAotIJCOxUCTTCCxXqD4
VLvsGMQVAznuaN9KOO+4rF0RzJNI4lN7HZbdfM8CgYEAnv4R4aTYs+y8u9Epi5OD
idgnrxQ1+DzMf63AYj0ffDL9TTZwcZ+HsD1o8h+iLtTPOCJCcDdhtf+eqoR5X9kS
9wPQkvP18z5NPnuxtmFIn4O/hd/KnJJpIjgth6zYwQbygU29C+rVV/9/lysCqbK2
LWU+f0MVRIJ4KhvwmMd3+QcCgYEAkZocUJkO/61FcixlvOl+CiHz5rR90QH9sTR7
JDKtOfnip07tNmuc80gYVxapEy52OSFZKxsH+Msb4MNWhTchAuSvadw3PrP2h+Cs
6vk8UewgGgm9QRevjh4IDbLVFmsXA5mOENNvdl1br+K49W5GzJAw3UtoA+lu4fnm
msQ3GJ8CgYEAsfiWFSpFMGQ7YR+2mUW0Vc3kCUzG7Hk9c7Ktv2prDyyI6IagDVll
iMsBP6Sxd3QGG9STWyZ8mxfiUH4Vh/fJZtZjaGom4lZo/sChZBcAfJlqQ0TWkFSQ
4S59CosGXRE7slz+IbgUvPavzydqdcRRRroO9SpMHd0AAG7KDPiS+mk=
-----END RSA PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
MIIDJjCCAo+gAwIBAgIBAjANBgkqhkiG9w0BAQUFADCBkTELMAkGA1UEBhMCREUx
DDAKBgNVBAgTA05SVzEOMAwGA1UEBxMFRWFydGgxFzAVBgNVBAoTDlJhbmRvbSBD
b21wYW55MQswCQYDVQQLEwJJVDEXMBUGA1UEAxMOd3d3LnJhbmRvbS5jb20xJTAj
BgkqhkiG9w0BCQEWFktyeXB0b0tpbmdzQHJhbmRvbS5jb20wHhcNMTUwNDEzMTcy
NTE4WhcNMTYwNDEyMTcyNTE4WjCBgjELMAkGA1UEBhMCREUxDDAKBgNVBAgTA05S
VzEOMAwGA1UEBxMFRWFydGgxFzAVBgNVBAoTDlJhbmRvbSBDb21wYW55MQswCQYD