Repository: aliyun/alibabacloud-nls-python-sdk (language: python)

Issue:
Revert "update 0.0.1"
Reverts aliyun/alibabacloud-nls-python-sdk#2 keep origin branch


Discussion / hints:

Fix patch:
diff --git a/LICENSE b/LICENSE
index e10b96d..7a4a3ea 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,13 +1,202 @@
-Copyright 1999-present Alibaba Group Holding Ltd.
 
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
 
-http://www.apache.org/licenses/LICENSE-2.0
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
 
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
\ No newline at end of file
diff --git a/NOTICE b/NOTICE
new file mode 100644
index 0000000..0927292
--- /dev/null
+++ b/NOTICE
@@ -0,0 +1,38 @@
+The following notices pertain to this software license.
+
+
+====================================================================
+====================================================================
+websocket-client
+
+Copyright 2018 Hiroki Ohtani.
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+
+3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+====================================================================
+====================================================================
+requests
+
+Copyright 2018 Kenneth Reitz
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       https://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
diff --git a/README b/README
new file mode 100644
index 0000000..45e7b96
--- /dev/null
+++ b/README
@@ -0,0 +1,9 @@
+1. 要求Python版本为3.4+
+2. 支持的服务：获取Token、一句话识别、实时语音识别、语音合成
+3. Client对象保存基本参数
+4. 识别的基本步骤：
+    1）创建识别请求对象
+    2）start()建立连接
+    3）send()发送数据
+    4）stop()结束识别
+    5）close()关闭WebSocket链接
diff --git a/README.md b/README.md
deleted file mode 100644
index ad624f3..0000000
--- a/README.md
+++ /dev/null
@@ -1,13 +0,0 @@
-# alibaba-nls-python-sdk
-
-This is Python SDK for NLS. It supports
-SPEECH-RECOGNIZER/SPEECH-SYNTHESIZER/SPEECH-TRANSLATOR/COMMON-REQUESTS-PROTO.
-
-This module works on Python versions:
-> 3.6 and greater
-
-install requirements:
-> python -m pip install -r requirements.txt
-
-install package:
-> python -m pip install .
diff --git a/ali_speech/__init__.py b/ali_speech/__init__.py
new file mode 100644
index 0000000..4d2d663
--- /dev/null
+++ b/ali_speech/__init__.py
@@ -0,0 +1,19 @@
+"""
+ * Copyright 2015 Alibaba Group Holding Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+"""
+
+from ali_speech._client import NlsClient
+
+__version__ = "0.1.0"
diff --git a/ali_speech/_client.py b/ali_speech/_client.py
new file mode 100644
index 0000000..45b02bb
--- /dev/null
+++ b/ali_speech/_client.py
@@ -0,0 +1,59 @@
+# -*- coding: utf-8 -*-
+
+"""
+ * Copyright 2015 Alibaba Group Holding Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+"""
+
+import websocket
+
+try:
+    import thread
+except ImportError:
+    import _thread as thread
+
+from ali_speech._logging import _log
+from ali_speech._create_token import AccessToken
+from ali_speech._speech_recognizer import SpeechRecognizer
+from ali_speech._speech_transcriber import SpeechTranscriber
+from ali_speech._speech_synthesizer import SpeechSynthesizer
+
+__all__ = ["NlsClient"]
+
+
+class NlsClient:
+    URL_GATEWAY = 'wss://nls-gateway.cn-shanghai.aliyuncs.com/ws/v1'
+
+    def __init__(self):
+        websocket.enableTrace(False)
+
+    @staticmethod
+    def set_log_level(level):
+        _log.setLevel(level)
+
+    @staticmethod
+    def create_token(access_key_id, access_key_secret):
+        return AccessToken.create_token(access_key_id, access_key_secret)
+
+    def create_recognizer(self, callback, gateway_url=URL_GATEWAY):
+        request = SpeechRecognizer(callback, gateway_url)
+        return request
+
+    def create_transcriber(self, callback, gateway_url=URL_GATEWAY):
+        transcriber = SpeechTranscriber(callback, gateway_url)
+        return transcriber
+
+    def create_synthesizer(self, callback, gateway_url=URL_GATEWAY):
+        synthesizer = SpeechSynthesizer(callback, gateway_url)
+        return synthesizer
diff --git a/ali_speech/_constant.py b/ali_speech/_constant.py
new file mode 100644
index 0000000..ca8387a
--- /dev/null
+++ b/ali_speech/_constant.py
@@ -0,0 +1,90 @@
+# -*- coding: utf-8 -*-
+
+"""
+ * Copyright 2015 Alibaba Group Holding Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+"""
+
+
+class Status:
+    # 初始状态
+    STATUS_INIT = 1
+    # websocker 网络连接建立成功，on_open
+    STATUS_WS_CONNECTED = 2
+    # 与gateway服务建立连接中
+    STATUS_STARTING = 3
+    # 与服务建立连接成功，on_message RecognitionStarted
+    STATUS_STARTED = 4
+    # 客户端正主动断开连接
+    STATUS_STOPPING = 5
+    # 与服务已断开连接
+    STATUS_STOPPED = 6
+    # 开启VAD，服务主动返回completed事件
+    STATUS_COMPLETED_WITH_OUT_STOP = 7
+
+
+class Constant:
+    CONTEXT = 'context'
+    CONTEXT_SDK_KEY = 'sdk'
+    CONTEXT_SDK_KEY_NAME = 'name'
+    CONTEXT_SDK_VALUE_NAME = 'nls-sdk-python'
+    CONTEXT_SDK_KEY_VERSION = 'version'
+    CONTEXT_SDK_VALUE_VERSION = '2.0.1'
+
+    HEADER_TOKEN = 'X-NLS-Token'
+
+    HEADER = 'header'
+    HEADER_KEY_NAMESPACE = 'namespace'
+    HEADER_KEY_NAME = 'name'
+    HEADER_KEY_MESSAGE_ID = 'message_id'
+    HEADER_KEY_APPKEY = 'appkey'
+    HEADER_KEY_TASK_ID = 'task_id'
+    HEADER_KEY_STATUS = 'status'
+    HEADER_KEY_STATUS_TEXT = 'status_text'
+
+    PAYLOAD = 'payload'
+    PAYLOAD_KEY_SAMPLE_RATE = 'sample_rate'
+    PAYLOAD_KEY_FORMAT = 'format'
+    PAYLOAD_KEY_ENABLE_ITN = 'enable_inverse_text_normalization'
+    PAYLOAD_KEY_ENABLE_INTERMEDIATE_RESULT = 'enable_intermediate_result'
+    PAYLOAD_KEY_ENABLE_PUNCTUATION_PREDICTION = 'enable_punctuation_prediction'
+
+    PAYLOAD_KEY_VOICE = 'voice'
+    PAYLOAD_KEY_TEXT = 'text'
+    PAYLOAD_KEY_VOLUME = 'volume'
+    PAYLOAD_KEY_SPEECH_RATE = 'speech_rate'
+    PAYLOAD_KEY_PITCH_RATE = 'pitch_rate'
+
+    HEADER_VALUE_ASR_NAMESPACE = 'SpeechRecognizer'
+    HEADER_VALUE_ASR_NAME_START = 'StartRecognition'
+    HEADER_VALUE_ASR_NAME_STOP = 'StopRecognition'
+    HEADER_VALUE_ASR_NAME_STARTED = 'RecognitionStarted'
+    HEADER_VALUE_ASR_NAME_RESULT_CHANGED = 'RecognitionResultChanged'
+    HEADER_VALUE_ASR_NAME_COMPLETED = 'RecognitionCompleted'
+
+    HEADER_VALUE_NAME_TASK_FAILED = 'TaskFailed'
+
+    HEADER_VALUE_TRANS_NAMESPACE = 'SpeechTranscriber'
+    HEADER_VALUE_TRANS_NAME_START = 'StartTranscription'
+    HEADER_VALUE_TRANS_NAME_STOP = 'StopTranscription'
+    HEADER_VALUE_TRANS_NAME_STARTED = 'TranscriptionStarted'
+    HEADER_VALUE_TRANS_NAME_SENTENCE_BEGIN = 'SentenceBegin'
+    HEADER_VALUE_TRANS_NAME_SENTENCE_END = 'SentenceEnd'
+    HEADER_VALUE_TRANS_NAME_RESULT_CHANGE = 'TranscriptionResultChanged'
+    HEADER_VALUE_TRANS_NAME_COMPLETED = 'TranscriptionCompleted'
+
+    HEADER_VALUE_TTS_NAMESPACE = 'SpeechSynthesizer'
+    HEADER_VALUE_TTS_NAME_START = 'StartSynthesis'
+    HEADER_VALUE_TTS_NAME_COMPLETED = 'SynthesisCompleted'
+
diff --git a/ali_speech/_create_token.py b/ali_speech/_create_token.py
new file mode 100644
index 0000000..d7c9a79
--- /dev/null
+++ b/ali_speech/_create_token.py
@@ -0,0 +1,86 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+ * Copyright 2015 Alibaba Group Holding Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+"""
+
+import base64
+import hashlib
+import hmac
+import requests
+import time
+import uuid
+
+from urllib import parse
+from ali_speech._logging import _log
+
+
+class AccessToken:
+    @staticmethod
+    def _encode_text(text):
+        encoded_text = parse.quote_plus(text)
+        return encoded_text.replace('+', '%20').replace('*', '%2A').replace('%7E', '~')
+
+    @staticmethod
+    def _encode_dict(dic):
+        keys = dic.keys()
+        dic_sorted = [(key, dic[key]) for key in sorted(keys)]
+        encoded_text = parse.urlencode(dic_sorted)
+        return encoded_text.replace('+', '%20').replace('*', '%2A').replace('%7E', '~')
+
+    @staticmethod
+    def create_token(access_key_id, access_key_secret):
+        parameters = {'AccessKeyId': access_key_id,
+                      'Action': 'CreateToken',
+                      'Format': 'JSON',
+                      'RegionId': 'cn-shanghai',
+                      'SignatureMethod': 'HMAC-SHA1',
+                      'SignatureNonce': str(uuid.uuid1()),
+                      'SignatureVersion': '1.0',
+                      'Timestamp': time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
+                      'Version': '2019-02-28'}
+        # 构造规范化的请求字符串
+        query_string = AccessToken._encode_dict(parameters)
+        _log.debug('规范化的请求字符串: %s' % query_string)
+        # 构造待签名字符串
+        string_to_sign = 'GET' + '&' + AccessToken._encode_text('/') + '&' + AccessToken._encode_text(query_string)
+        _log.debug('待签名的字符串: %s' % string_to_sign)
+        # 计算签名
+        secreted_string = hmac.new(bytes(access_key_secret + '&', encoding='utf-8'),
+                                   bytes(string_to_sign, encoding='utf-8'),
+                                   hashlib.sha1).digest()
+        signature = base64.b64encode(secreted_string)
+        _log.debug('签名: %s' % signature)
+        # 进行URL编码
+        signature = AccessToken._encode_text(signature)
+        _log.debug('URL编码后的签名: %s' % signature)
+        # 调用服务
+        full_url = 'http://nls-meta.cn-shanghai.aliyuncs.com/?Signature=%s&%s' % (signature, query_string)
+        _log.debug('url: %s' % full_url)
+        # 提交HTTP GET请求
+        response = requests.get(full_url)
+        if response.ok:
+            root_obj = response.json()
+            key = 'Token'
+            if key in root_obj:
+                token = root_obj[key]['Id']
+                expire_time = root_obj[key]['ExpireTime']
+                return token, expire_time
+
+        _log.error(response.text)
+        return None, None
+
+
diff --git a/ali_speech/_logging.py b/ali_speech/_logging.py
new file mode 100644
index 0000000..a16d9cb
--- /dev/null
+++ b/ali_speech/_logging.py
@@ -0,0 +1,31 @@
+# -*- coding:utf-8 -*-
+
+"""
+ * Copyright 2015 Alibaba Group Holding Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+"""
+
+import logging.handlers
+
+__all__ = ['_log']
+
+FORMAT = '%(asctime)15s %(name)s-%(levelname)s  %(funcName)s:%(lineno)s %(message)s'
+logging.basicConfig(level=logging.DEBUG, format=FORMAT)
+_log = logging.getLogger('alispeech')
+
+handler = logging.handlers.RotatingFileHandler('alispeech.log', maxBytes=1024 * 1024,
+                                               backupCount=5, encoding='utf-8')
+handler.setLevel(logging.DEBUG)
+handler.setFormatter(logging.Formatter(FORMAT))
+_log.addHandler(handler)
diff --git a/ali_speech/_speech_recognizer.py b/ali_speech/_speech_recognizer.py
new file mode 100644
index 0000000..08889a3
--- /dev/null
+++ b/ali_speech/_speech_recognizer.py
@@ -0,0 +1,227 @@
+# -*- coding: utf-8 -*-
+
+"""
+ * Copyright 2015 Alibaba Group Holding Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+"""
+
+import json
+import six
+import websocket
+import uuid
+import threading
+import time
+
+from ali_speech._logging import _log
+from ali_speech._constant import Status
+from ali_speech._constant import Constant
+from ali_speech._speech_reqprotocol import SpeechReqProtocol
+
+
+class SpeechRecognizer(SpeechReqProtocol):
+
+    def __init__(self, callback, url):
+        super(SpeechRecognizer, self).__init__(callback, url)
+        self._last_start_retry = False
+        self._is_connected = False
+
+        self._header[Constant.HEADER_KEY_NAMESPACE] = Constant.HEADER_VALUE_ASR_NAMESPACE
+        self._payload[Constant.PAYLOAD_KEY_FORMAT] = "pcm"
+        self._payload[Constant.PAYLOAD_KEY_SAMPLE_RATE] = 16000
+
+    def set_enable_intermediate_result(self, flag):
+        self._payload[Constant.PAYLOAD_KEY_ENABLE_INTERMEDIATE_RESULT] = flag
+
+    def set_enable_punctuation_prediction(self, flag):
+        self._payload[Constant.PAYLOAD_KEY_ENABLE_PUNCTUATION_PREDICTION] = flag
+
+    def set_enable_inverse_text_normalization(self, flag):
+        self._payload[Constant.PAYLOAD_KEY_ENABLE_ITN] = flag
+
+    def start(self, ping_interval=5, ping_timeout=3):
+        """
+        开始识别，新建到服务端的连接
+        :param ping_interval: 自动发送ping命令，指定发送间隔，单位为秒
+        :param ping_timeout: 等待接收pong消息的超时时间，单位为秒
+        :return: 与服务端建立连接成功，返回0
+                 与服务端建立连接失败，返回-1
+        """
+        if self._status == Status.STATUS_INIT:
+            _log.debug('starting recognizer...')
+            self._status = Status.STATUS_STARTING
+        else:
+            _log.error("Illegal status: %s" % self._status)
+            return -1
+
+        def _on_open(ws):
+            _log.debug('websocket connected')
+            self._status = Status.STATUS_WS_CONNECTED
+            self._is_connected = True
+            msg_id = six.u(uuid.uuid1().hex)
+            self._task_id = six.u(uuid.uuid1().hex)
+            self._header[Constant.HEADER_KEY_NAME] = Constant.HEADER_VALUE_ASR_NAME_START
+            self._header[Constant.HEADER_KEY_MESSAGE_ID] = msg_id
+            self._header[Constant.HEADER_KEY_TASK_ID] = self._task_id
+
+            text = self.serialize()
+            _log.info('sending start cmd: ' + text)
+            ws.send(text)
+
+        def _on_message(ws, raw):
+            _log.debug('websocket message received: ' + raw)
+            msg = json.loads(raw)
+            name = msg[Constant.HEADER][Constant.HEADER_KEY_NAME]
+            if name == Constant.HEADER_VALUE_ASR_NAME_STARTED:
+                self._status = Status.STATUS_STARTED
+                _log.debug('callback on_started')
+                self._callback.on_started(msg)
+            elif name == Constant.HEADER_VALUE_ASR_NAME_RESULT_CHANGED:
+                _log.debug('callback on_result_changed')
+                self._callback.on_result_changed(msg)
+            elif name == Constant.HEADER_VALUE_ASR_NAME_COMPLETED:
+                if self._status == Status.STATUS_STOPPING:
+                    # 客户端主动调用stop返回的completed事件
+                    self._status = Status.STATUS_STOPPED
+                else:
+                    # 开启VAD，服务端主动返回的completed事件
+                    self._status = Status.STATUS_COMPLETED_WITH_OUT_STOP
+
+                _log.debug('websocket status changed to stopped')
+                _log.debug('callback on_completed')
+                self._callback.on_completed(msg)
+            elif name == Constant.HEADER_VALUE_NAME_TASK_FAILED:
+                self._status = Status.STATUS_STOPPED
+                _log.error(msg)
+                _log.debug('websocket status changed to stopped')
+                _log.debug('callback on_task_failed')
+                self._callback.on_task_failed(msg)
+
+        def _on_close(ws):
+            _log.debug('callback on_channel_closed')
+            self._callback.on_channel_closed()
+
+        def _on_error(ws, error):
+            if self._is_connected or self._last_start_retry:
+                _log.error(error)
+                self._status = Status.STATUS_STOPPED
+                message = json.loads('{"header":{"namespace":"Default","name":"TaskFailed",'
+                                     '"status":400,"message_id":"0","task_id":"0",'
+                                     '"status_text":"%s"}}'
+                                     % error)
+                self._callback.on_task_failed(message)
+            else:
+                _log.warning('retry start: %s' % error)
+
+        retry_count = 3
+        for count in range(retry_count):
+            self._status = Status.STATUS_STARTING
+            if count == (retry_count - 1):
+                self._last_start_retry = True
+
+            # Init WebSocket
+            self._ws = websocket.WebSocketApp(self._gateway_url,
+                                              on_open=_on_open,
+                                              on_message=_on_message,
+                                              on_error=_on_error,
+                                              on_close=_on_close,
+                                              header={Constant.HEADER_TOKEN: self._token})
+
+            self._thread = threading.Thread(target=self._ws.run_forever,
+                                            args=(None, None, ping_interval, ping_timeout))
+            self._thread.daemon = True
+            self._thread.start()
+            # waite for no more than 10 seconds
+            for i in range(1000):
+                if self._status == Status.STATUS_STARTED or self._status == Status.STATUS_STOPPED:
+                    break
+                else:
+                    time.sleep(0.01)
+
+            if self._status == Status.STATUS_STARTED:
+                # 与服务端连接建立成功
+                _log.debug('start succeed!')
+                return 0
+            else:
+                if self._is_connected or self._last_start_retry:
+                    # 已建立了WebSocket链接但是与服务端的连接失败， 或者是最后一次重试，则返回-1
+                    _log.error("start failed, status: %s" % self._status)
+                    return -1
+                else:
+                    # 尝试重连
+                    continue
+
+    def send(self, audio_data):
+        """
+        发送语音数据到服务端，建议每次发送 1000 ~ 8000 字节
+        :param audio_data: 二进制音频数据
+        :return: 发送成功，返回0
+                 发送失败，返回-1
+        """
+        if self._status == Status.STATUS_STARTED:
+            self._ws.send(audio_data, opcode=websocket.ABNF.OPCODE_BINARY)
+            return 0
+        elif self._status == Status.STATUS_COMPLETED_WITH_OUT_STOP:
+            _log.info('the recognizer finished with VAD, no need to send data anymore!')
+            return -1
+        else:
+            _log.error('should not send data in state %d', self._status)
+            return -1
+
+    def stop(self):
+        """
+        结束识别并关闭与服务端的连接
+        :return: 关闭成功，返回0
+                 关闭失败，返回-1
+        """
+        ret = 0
+        if self._status == Status.STATUS_COMPLETED_WITH_OUT_STOP:
+            _log.info('the recognizer finished with VAD')
+            ret = 0
+        elif self._status == Status.STATUS_STARTED:
+            self._status = Status.STATUS_STOPPING
+            msg_id = six.u(uuid.uuid1().hex)
+            self._header[Constant.HEADER_KEY_NAME] = Constant.HEADER_VALUE_ASR_NAME_STOP
+            self._header[Constant.HEADER_KEY_MESSAGE_ID] = msg_id
+            self._payload.clear()
+            text = self.serialize()
+            _log.info('sending stop cmd: ' + text)
+            self._ws.send(text)
+
+            for i in range(100):
+                if self._status == Status.STATUS_STOPPED:
+                    break
+                else:
+                    time.sleep(0.1)
+                    _log.debug('waite 100ms')
+
+            if self._status != Status.STATUS_STOPPED:
+                ret = -1
+            else:
+                ret = 0
+        else:
+            _log.error('should not stop in state %d', self._status)
+            ret = -1
+
+        return ret
+
+    def close(self):
+        """
+        关闭WebSocket链接
+        """
+        if self._ws:
+            if self._thread and self._thread.is_alive():
+                self._ws.keep_running = False
+                self._thread.join()
+            self._ws.close()
+
diff --git a/ali_speech/_speech_reqprotocol.py b/ali_speech/_speech_reqprotocol.py
new file mode 100644
index 0000000..1f8ffb3
--- /dev/null
+++ b/ali_speech/_speech_reqprotocol.py
@@ -0,0 +1,85 @@
+# -*- coding: utf-8 -*-
+
+"""
+ * Copyright 2015 Alibaba Group Holding Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+"""
+
+import json
+
+from ali_speech._constant import Status
+from ali_speech._constant import Constant
+
+
+class SpeechReqProtocol:
+    def __init__(self, callback, url):
+        self._header = {}
+        self._context = {}
+        self._payload = {}
+        self._token = None
+        self._gateway_url = url
+        self._callback = callback
+        self._status = Status.STATUS_INIT
+        self._ws = None
+        self._thread = None
+        self._task_id = None
+
+        sdk_info = {Constant.CONTEXT_SDK_KEY_NAME: Constant.CONTEXT_SDK_VALUE_NAME,
+                    Constant.CONTEXT_SDK_KEY_VERSION: Constant.CONTEXT_SDK_VALUE_VERSION}
+        self._context[Constant.CONTEXT_SDK_KEY] = sdk_info
+
+    def set_appkey(self, appkey):
+        self._header[Constant.HEADER_KEY_APPKEY] = appkey
+
+    def get_appkey(self):
+        return self._header[Constant.HEADER_KEY_APPKEY]
+
+    def set_token(self, token):
+        self._token = token
+
+    def get_token(self):
+        return self._token
+
+    def set_format(self, format):
+        self._payload[Constant.PAYLOAD_KEY_FORMAT] = format
+
+    def get_format(self):
+        return self._payload[Constant.PAYLOAD_KEY_FORMAT]
+
+    def set_sample_rate(self, sample_rate):
+        self._payload[Constant.PAYLOAD_KEY_SAMPLE_RATE] = sample_rate
+
+    def get_sample_rate(self):
+        return self._payload[Constant.PAYLOAD_KEY_SAMPLE_RATE]
+
+    def get_task_id(self):
+        return self._header[Constant.HEADER_KEY_TASK_ID]
+
+    def put_context(self, key, obj):
+        self._context[key] = obj
+
+    def add_payload_param(self, key, obj):
+        self._payload[key] = obj
+
+    def get_status(self):
+        return self._status
+
+    def serialize(self):
+        root = {Constant.HEADER: self._header}
+
+        if len(self._payload) != 0:
+            root[Constant.CONTEXT] = self._context
+            root[Constant.PAYLOAD] = self._payload
+
+        return json.dumps(root)
diff --git a/ali_speech/_speech_synthesizer.py b/ali_speech/_speech_synthesizer.py
new file mode 100644
index 0000000..97894fc
--- /dev/null
+++ b/ali_speech/_speech_synthesizer.py
@@ -0,0 +1,197 @@
+# -*- coding: utf-8 -*-
+
+"""
+ * Copyright 2015 Alibaba Group Holding Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+"""
+
+import json
+import six
+import websocket
+import uuid
+import threading
+import time
+
+from ali_speech._logging import _log
+from ali_speech._constant import Status
+from ali_speech._constant import Constant
+from ali_speech._speech_reqprotocol import SpeechReqProtocol
+
+
+class SpeechSynthesizer(SpeechReqProtocol):
+
+    def __init__(self, callback, url):
+        super(SpeechSynthesizer, self).__init__(callback, url)
+        self._last_start_retry = False
+        self._is_connected = False
+
+        self._header[Constant.HEADER_KEY_NAMESPACE] = Constant.HEADER_VALUE_TTS_NAMESPACE
+        self._payload[Constant.PAYLOAD_KEY_VOICE] = 'xiaoyun'
+        self._payload[Constant.PAYLOAD_KEY_FORMAT] = 'pcm'
+        self._payload[Constant.PAYLOAD_KEY_SAMPLE_RATE] = 16000
+
+    def set_text(self, text):
+        self._payload[Constant.PAYLOAD_KEY_TEXT] = text
+
+    def set_voice(self, voice):
+        self._payload[Constant.PAYLOAD_KEY_VOICE] = voice
+
+    def set_volume(self, volume):
+        self._payload[Constant.PAYLOAD_KEY_VOLUME] = volume
+
+    def set_speech_rate(self, speech_rate):
+        self._payload[Constant.PAYLOAD_KEY_SPEECH_RATE] = speech_rate
+
+    def set_pitch_rate(self, pitch_rate):
+        self._payload[Constant.PAYLOAD_KEY_PITCH_RATE] = pitch_rate
+
+    def start(self, ping_interval=5, ping_timeout=3):
+        """
+        开始合成，新建到服务端的连接
+        :param ping_interval: 自动发送ping命令，指定发送间隔，单位为秒
+        :param ping_timeout: 等待接收pong消息的超时时间，单位为秒
+        :return: 与服务端建立连接成功，返回0
+                 与服务端建立连接失败，返回-1
+        """
+        if self._status == Status.STATUS_INIT:
+            _log.debug('starting synthesizer...')
+            self._status = Status.STATUS_STARTING
+        else:
+            _log.error("Illegal status: %s" % self._status)
+            return -1
+
+        def _on_open(ws):
+            _log.debug('websocket connected')
+            self._status = Status.STATUS_STARTED
+            self._is_connected = True
+            time.sleep(0.01)
+            msg_id = six.u(uuid.uuid1().hex)
+            self._task_id = six.u(uuid.uuid1().hex)
+            self._header[Constant.HEADER_KEY_NAME] = Constant.HEADER_VALUE_TTS_NAME_START
+            self._header[Constant.HEADER_KEY_MESSAGE_ID] = msg_id
+            self._header[Constant.HEADER_KEY_TASK_ID] = self._task_id
+
+            text = self.serialize()
+            _log.info('sending start cmd: ' + text)
+            ws.send(text)
+
+        def _on_data(ws, raw, opcode, flag):
+            if opcode == websocket.ABNF.OPCODE_BINARY:
+                _log.debug("received binary data, size: %s" % len(raw))
+                self._callback.on_binary_data_received(raw)
+            elif opcode == websocket.ABNF.OPCODE_TEXT:
+                _log.debug("websocket message received: %s" % raw)
+                msg = json.loads(raw)
+                name = msg[Constant.HEADER][Constant.HEADER_KEY_NAME]
+                if name == Constant.HEADER_VALUE_TTS_NAME_COMPLETED:
+                    self._status = Status.STATUS_STOPPED
+                    _log.debug('websocket status changed to stopped')
+                    _log.debug('callback on_completed')
+                    self._callback.on_completed(msg)
+                elif name == Constant.HEADER_VALUE_NAME_TASK_FAILED:
+                    self._status = Status.STATUS_STOPPED
+                    _log.error(msg)
+                    _log.debug('websocket status changed to stopped')
+                    _log.debug('callback on_task_failed')
+                    self._callback.on_task_failed(msg)
+
+        def _on_close(ws):
+            _log.debug('callback on_channel_closed')
+            self._callback.on_channel_closed()
+
+        def _on_error(ws, error):
+            if self._is_connected or self._last_start_retry:
+                _log.error(error)
+                self._status = Status.STATUS_STOPPED
+                message = json.loads('{"header":{"namespace":"Default","name":"TaskFailed",'
+                                     '"status":400,"message_id":"0","task_id":"0",'
+                                     '"status_text":"%s"}}'
+                                     % error)
+                self._callback.on_task_failed(message)
+            else:
+                _log.warning('retry start: %s' % error)
+
+        retry_count = 3
+        for count in range(retry_count):
+            self._status = Status.STATUS_STARTING
+            if count == (retry_count - 1):
+                self._last_start_retry = True
+
+            # Init WebSocket
+            self._ws = websocket.WebSocketApp(self._gateway_url,
+                                              on_open=_on_open,
+                                              on_data=_on_data,
+                                              on_error=_on_error,
+                                              on_close=_on_close,
+                                              header={Constant.HEADER_TOKEN: self._token})
+
+            self._thread = threading.Thread(target=self._ws.run_forever, args=(None, None, ping_interval, ping_timeout))
+            self._thread.daemon = True
+            self._thread.start()
+            # waite for no more than 10 seconds
+            for i in range(1000):
+                if self._status == Status.STATUS_STARTED or self._status == Status.STATUS_STOPPED:
+                    break
+                else:
+                    time.sleep(0.01)
+
+            if self._status == Status.STATUS_STARTED:
+                # 与服务端连接建立成功
+                _log.debug('start succeed!')
+                return 0
+            else:
+                if self._is_connected or self._last_start_retry:
+                    # 已建立了WebSocket链接但是与服务端的连接失败， 或者是最后一次重试，则返回-1
+                    _log.error("start failed, status: %s" % self._status)
+                    return -1
+                else:
+                    # 尝试重连
+                    continue
+
+    def wait_completed(self):
+        """
+        等待合成结束
+        :return: 合成结束，返回0
+                 合成超时，返回-1
+        """
+        ret = 0
+        if self._status == Status.STATUS_STARTED:
+            for i in range(100):
+                if self._status == Status.STATUS_STOPPED:
+                    break
+                else:
+                    time.sleep(0.1)
+                    _log.debug('waite 100ms')
+
+            if self._status != Status.STATUS_STOPPED:
+                ret = -1
+            else:
+                ret = 0
+        else:
+            _log.error('should not wait completed in state %d', self._status)
+            ret = -1
+
+        return ret
+
+    def close(self):
+        """
+        关闭WebSocket连接
+        :return:
+        """
+        if self._ws:
+            if self._thread and self._thread.is_alive():
+                self._ws.keep_running = False
+                self._thread.join()
+            self._ws.close()
+
diff --git a/ali_speech/_speech_transcriber.py b/ali_speech/_speech_transcriber.py
new file mode 100644
index 0000000..e63c6fa
--- /dev/null
+++ b/ali_speech/_speech_transcriber.py
@@ -0,0 +1,216 @@
+# -*- coding: utf-8 -*-
+
+"""
+ * Copyright 2015 Alibaba Group Holding Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+"""
+
+import json
+import six
+import websocket
+import uuid
+import threading
+import time
+
+from ali_speech._logging import _log
+from ali_speech._constant import Status
+from ali_speech._constant import Constant
+from ali_speech._speech_reqprotocol import SpeechReqProtocol
+
+
+class SpeechTranscriber(SpeechReqProtocol):
+    def __init__(self, callback, url):
+        super(SpeechTranscriber, self).__init__(callback, url)
+        self._last_start_retry = False
+        self._is_connected = False
+
+        self._header[Constant.HEADER_KEY_NAMESPACE] = Constant.HEADER_VALUE_TRANS_NAMESPACE
+        self._payload[Constant.PAYLOAD_KEY_FORMAT] = 'pcm'
+        self._payload[Constant.PAYLOAD_KEY_SAMPLE_RATE] = 16000
+
+    def set_enable_intermediate_result(self, flag):
+        self._payload[Constant.PAYLOAD_KEY_ENABLE_INTERMEDIATE_RESULT] = flag
+
+    def set_enable_punctuation_prediction(self, flag):
+        self._payload[Constant.PAYLOAD_KEY_ENABLE_PUNCTUATION_PREDICTION] = flag
+
+    def set_enable_inverse_text_normalization(self, flag):
+        self._payload[Constant.PAYLOAD_KEY_ENABLE_ITN] = flag
+
+    def start(self, ping_interval=5, ping_timeout=3):
+        """
+        开始识别，新建到服务端的连接
+        :param ping_interval: 自动发送ping命令，指定发送间隔，单位为秒
+        :param ping_timeout: 等待接收pong消息的超时时间，单位为秒
+        :return: 与服务端建立连接成功，返回0
+                 与服务端建立连接失败，返回-1
+        """
+        if self._status == Status.STATUS_INIT:
+            _log.debug('starting transcriber...')
+            self._status = Status.STATUS_STARTING
+        else:
+            _log.error("Illegal status: %s" % self._status)
+            return -1
+
+        def _on_open(ws):
+            _log.debug('websocket connected')
+            self._status = Status.STATUS_WS_CONNECTED
+            self._is_connected = True
+            msg_id = six.u(uuid.uuid1().hex)
+            self._task_id = six.u(uuid.uuid1().hex)
+            self._header[Constant.HEADER_KEY_NAME] = Constant.HEADER_VALUE_TRANS_NAME_START
+            self._header[Constant.HEADER_KEY_MESSAGE_ID] = msg_id
+            self._header[Constant.HEADER_KEY_TASK_ID] = self._task_id
+
+            text = self.serialize()
+            _log.info('sending start cmd: ' + text)
+            ws.send(text)
+
+        def _on_message(ws, raw):
+            _log.debug('websocket message received: ' + raw)
+            msg = json.loads(raw)
+            name = msg[Constant.HEADER][Constant.HEADER_KEY_NAME]
+            if name == Constant.HEADER_VALUE_TRANS_NAME_STARTED:
+                self._status = Status.STATUS_STARTED
+                _log.debug('callback on_started')
+                self._callback.on_started(msg)
+            elif name == Constant.HEADER_VALUE_TRANS_NAME_RESULT_CHANGE:
+                _log.debug('callback on_result_changed')
+                self._callback.on_result_changed(msg)
+            elif name == Constant.HEADER_VALUE_TRANS_NAME_SENTENCE_BEGIN:
+                _log.debug('callback on_sentence_begin')
+                self._callback.on_sentence_begin(msg)
+            elif name == Constant.HEADER_VALUE_TRANS_NAME_SENTENCE_END:
+                _log.debug('callback on_sentence_end')
+                self._callback.on_sentence_end(msg)
+            elif name == Constant.HEADER_VALUE_TRANS_NAME_COMPLETED:
+                self._status = Status.STATUS_STOPPED
+                _log.debug('websocket status changed to stopped')
+                _log.debug('callback on_completed')
+                self._callback.on_completed(msg)
+            elif name == Constant.HEADER_VALUE_NAME_TASK_FAILED:
+                self._status = Status.STATUS_STOPPED
+                _log.error(msg)
+                _log.debug('websocket status changed to stopped')
+                _log.debug('callback on_task_failed')
+                self._callback.on_task_failed(msg)
+
+        def _on_close(ws):
+            _log.debug('callback on_channel_closed')
+            self._callback.on_channel_closed()
+
+        def _on_error(ws, error):
+            if self._is_connected or self._last_start_retry:
+                _log.error(error)
+                self._status = Status.STATUS_STOPPED
+                message = json.loads('{"header":{"namespace":"Default","name":"TaskFailed",'
+                                     '"status":400,"message_id":"0","task_id":"0",'
+                                     '"status_text":"%s"}}'
+                                     % error)
+                self._callback.on_task_failed(message)
+            else:
+                _log.warning('retry start: %s' % error)
+
+        retry_count = 3
+        for count in range(retry_count):
+            self._status = Status.STATUS_STARTING
+            if count == (retry_count - 1):
+                self._last_start_retry = True
+
+            # Init WebSocket
+            self._ws = websocket.WebSocketApp(self._gateway_url,
+                                              on_open=_on_open,
+                                              on_message=_on_message,
+                                              on_error=_on_error,
+                                              on_close=_on_close,
+                                              header={Constant.HEADER_TOKEN: self._token})
+
+            self._thread = threading.Thread(target=self._ws.run_forever, args=(None, None, ping_interval, ping_timeout))
+            self._thread.daemon = True
+            self._thread.start()
+            # waite for no more than 3 seconds
+            for i in range(1000):
+                if self._status == Status.STATUS_STARTED or self._status == Status.STATUS_STOPPED:
+                    break
+                else:
+                    time.sleep(0.01)
+
+            if self._status == Status.STATUS_STARTED:
+                _log.debug('start succeed!')
+                return 0
+            else:
+                if self._is_connected or self._last_start_retry:
+                    _log.error("start failed, status: %s" % self._status)
+                    return -1
+                else:
+                    continue
+
+    def send(self, audio_data):
+        """
+        发送语音数据到服务端，建议每次发送 1000 ~ 8000 字节
+        :param audio_data: 二进制音频数据
+        :return: 发送成功，返回0
+                 发送失败，返回-1
+        """
+        if self._status == Status.STATUS_STARTED:
+            self._ws.send(audio_data, opcode=websocket.ABNF.OPCODE_BINARY)
+            return 0
+        else:
+            _log.error('should not send data in state %d', self._status)
+            return -1
+
+    def stop(self):
+        """
+        结束识别并关闭与服务端的连接
+        :return: 关闭成功，返回0
+                 关闭失败，返回-1
+        """
+        ret = 0
+        if self._status == Status.STATUS_STARTED:
+            self._status = Status.STATUS_STOPPING
+            msg_id = six.u(uuid.uuid1().hex)
+            self._header[Constant.HEADER_KEY_NAME] = Constant.HEADER_VALUE_TRANS_NAME_STOP
+            self._header[Constant.HEADER_KEY_MESSAGE_ID] = msg_id
+            self._payload.clear()
+            text = self.serialize()
+            _log.info('sending stop cmd: ' + text)
+            self._ws.send(text)
+
+            for i in range(100):
+                if self._status == Status.STATUS_STOPPED:
+                    break
+                else:
+                    time.sleep(0.1)
+                    _log.debug('waite 100ms')
+
+            if self._status != Status.STATUS_STOPPED:
+                ret = -1
+            else:
+                ret = 0
+        else:
+            _log.error('should not stop in state %d', self._status)
+            ret = -1
+
+        return ret
+
+    def close(self):
+        """
+        关闭WebSocket链接
+        :return:
+        """
+        if self._ws:
+            if self._thread and self._thread.is_alive():
+                self._ws.keep_running = False
+                self._thread.join()
+            self._ws.close()
diff --git a/ali_speech/callbacks.py b/ali_speech/callbacks.py
new file mode 100644
index 0000000..fca77c2
--- /dev/null
+++ b/ali_speech/callbacks.py
@@ -0,0 +1,165 @@
+# -*- coding: utf-8 -*-
+
+"""
+ * Copyright 2015 Alibaba Group Holding Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+"""
+
+
+class SpeechRecognizerCallback:
+    """
+    * @brief 调用start(), 成功与服务建立连接, sdk内部线程上报started事件
+    * @note 请勿在回调函数内部调用stop()操作
+    * @param message 服务返回的响应
+    * @return
+    """
+    def on_started(self, message):
+        raise Exception('Not implemented!')
+
+    """
+    * @brief 设置允许返回中间结果参数, sdk在接收到服务返回到中间结果时, sdk内部线程上报ResultChanged事件
+    * @note 请勿在回调函数内部调用stop()操作
+    * @param message 服务返回的响应
+    * @return
+    """
+    def on_result_changed(self, message):
+        raise Exception('Not implemented!')
+
+    """
+    * @brief sdk在接收到服务返回识别结束消息时, sdk内部线程上报Completed事件
+    * @note 上报Completed事件之后, SDK内部会关闭识别连接通道. 此时调用send()会返回-1, 请停止发送.
+    *       请勿在回调函数内部调用stop()操作
+    * @param message 服务返回的响应
+    * @return
+    """
+    def on_completed(self, message):
+        raise Exception('Not implemented!')
+
+    """
+    * @brief 识别过程(包含start(), send(), stop())发生异常时, sdk内部线程上报TaskFailed事件
+    * @note 上报TaskFailed事件之后, SDK内部会关闭识别连接通道. 此时调用send()会返回-1, 请停止发送.
+    *       请勿在回调函数内部调用stop()操作
+    * @param message 服务返回的响应
+    * @return
+    """
+    def on_task_failed(self, message):
+        raise Exception('Not implemented!')
+
+    """
+    * @brief 识别结束或发生异常时，会关闭websocket连接通道
+    * @note 请勿在回调函数内部调用stop()操作
+    * @return
+    """
+    def on_channel_closed(self):
+        raise Exception('Not implemented!')
+
+
+class SpeechTranscriberCallback:
+    """
+    * @brief 调用start(), 成功与服务建立连接, sdk内部线程上报started事件
+    * @note 请勿在回调函数内部调用stop()操作
+    * @param message 服务返回的响应
+    * @return
+    """
+    def on_started(self, message):
+        raise Exception('Not implemented!')
+
+    """
+    * @brief 设置允许返回中间结果参数, sdk在接收到服务返回到中间结果时, sdk内部线程上报ResultChanged事件
+    * @note 请勿在回调函数内部调用stop()操作
+    * @param message 服务返回的响应
+    * @return
+    """
+    def on_result_changed(self, message):
+        raise Exception('Not implemented!')
+
+    """
+    * @brief sdk在接收到服务返回的识别到一句话的开始, sdk内部线程上报SentenceBegin事件
+    * @note 该事件作为检测到一句话的开始，请勿在回调函数内部调用stop()操作
+    * @param message 服务返回的响应
+    * @return
+    """
+    def on_sentence_begin(self, message):
+        raise Exception('Not implemented!')
+
+    """
+        * @brief sdk在接收到服务返回的识别到一句话的开始, sdk内部线程上报SentenceBegin事件
+        * @note 该事件作为检测到一句话的开始，请勿在回调函数内部调用stop()操作
+        * @param message 服务返回的响应
+        * @return
+        """
+    def on_sentence_end(self, message):
+        raise Exception('Not implemented!')
+
+    """
+    * @brief sdk在接收到服务返回识别结束消息时, sdk内部线程上报Completed事件
+    * @note 上报Completed事件之后, SDK内部会关闭识别连接通道. 此时调用send()会返回-1, 请停止发送.
+    *       请勿在回调函数内部调用stop()操作
+    * @param message 服务返回的响应
+    * @return
+    """
+    def on_completed(self, message):
+        raise Exception('Not implemented!')
+
+    """
+    * @brief 识别过程(包含start(), send(), stop())发生异常时, sdk内部线程上报TaskFailed事件
+    * @note 上报TaskFailed事件之后, SDK内部会关闭识别连接通道. 此时调用send()会返回-1, 请停止发送.
+    *       请勿在回调函数内部调用stop()操作
+    * @param message 服务返回的响应
+    * @return
+    """
+    def on_task_failed(self, message):
+        raise Exception('Not implemented!')
+
+    """
+    * @brief 识别结束或发生异常时，会关闭websocket连接通道
+    * @note 请勿在回调函数内部调用stop()操作
+    * @return
+    """
+    def on_channel_closed(self):
+        raise Exception('Not implemented!')
+
+
+class SpeechSynthesizerCallback:
+
+    def on_binary_data_received(self, raw):
+        raise Exception('Not implemented!')
+
+    """
+    * @brief sdk在接收到服务返回识别结束消息时, sdk内部线程上报Completed事件
+    * @note 上报Completed事件之后, SDK内部会关闭识别连接通道. 此时调用send()会返回-1, 请停止发送.
+    *       请勿在回调函数内部调用stop()操作
+    * @param message 服务返回的响应
+    * @return
+    """
+    def on_completed(self, message):
+        raise Exception('Not implemented!')
+
+    """
+    * @brief 识别过程(包含start(), send(), stop())发生异常时, sdk内部线程上报TaskFailed事件
+    * @note 上报TaskFailed事件之后, SDK内部会关闭识别连接通道. 此时调用send()会返回-1, 请停止发送.
+    *       请勿在回调函数内部调用stop()操作
+    * @param message 服务返回的响应
+    * @return
+    """
+    def on_task_failed(self, message):
+        raise Exception('Not implemented!')
+
+    """
+    * @brief 识别结束或发生异常时，会关闭websocket连接通道
+    * @note 请勿在回调函数内部调用stop()操作
+    * @return
+    """
+    def on_channel_closed(self):
+        raise Exception('Not implemented!')
diff --git a/ali_speech/constant.py b/ali_speech/constant.py
new file mode 100644
index 0000000..2b13410
--- /dev/null
+++ b/ali_speech/constant.py
@@ -0,0 +1,40 @@
+# -*- coding: utf-8 -*-
+
+"""
+ * Copyright 2015 Alibaba Group Holding Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+"""
+
+
+class ASRFormat:
+    PCM = 'pcm'
+    OPUS = 'opus'
+
+
+class TTSFormat:
+    PCM = 'pcm'
+    WAV = 'wav'
+    MP3 = 'mp3'
+
+
+class ASRSampleRate:
+    SAMPLE_RATE_8K = 8000
+    SAMPLE_RATE_16K = 16000
+
+
+class TTSSampleRate:
+    SAMPLE_RATE_8K = 8000
+    SAMPLE_RATE_16K = 16000
+    SAMPLE_RATE_24K = 24000
+
diff --git a/docs/SDK_README.md b/docs/SDK_README.md
deleted file mode 100644
index 40c84ae..0000000
--- a/docs/SDK_README.md
+++ /dev/null
@@ -1,770 +0,0 @@
-# NLS Python SDK说明
-
-
-
-本文介绍如何使用阿里云智能语音服务提供的Python SDK，包括SDK的安装方法及SDK代码示例。
-
-> 说明
->
-> * 当前版本：0.0.1，支持python3
-
-
-
-## 安装
-
-解压SDK压缩包后进入SDK根目录使用如下命令安装SDK依赖：
-
-> python -m pip install -r requirements.txt
-
-依赖安装完成后使用如下命令安装SDK：
-
-> python -m pip install .
-
-> 注意：
->
-> 上述命令需要在SDK根目录中执行
-
-安装完成后通过以下代码可以导入SDK：
-
-> #!/bin/python
->
-> import nls
-
-
-
-## 多线程和多并发
-
-根据Python官方文档：
-
-> **CPython implementation detail:** 在 CPython 中，由于存在 [全局解释器锁](https://docs.python.org/zh-cn/3/glossary.html#term-global-interpreter-lock)，同一时刻只有一个线程可以执行 Python 代码（虽然某些性能导向的库可能会去除此限制）。 如果你想让你的应用更好地利用多核心计算机的计算资源，推荐你使用 [`multiprocessing`](https://docs.python.org/zh-cn/3/library/multiprocessing.html#module-multiprocessing) 或 [`concurrent.futures.ProcessPoolExecutor`](https://docs.python.org/zh-cn/3/library/concurrent.futures.html#concurrent.futures.ProcessPoolExecutor)。 但是，如果你想要同时运行多个 I/O 密集型任务，则多线程仍然是一个合适的模型。
-
-如果单解释器有太多线程，那么线程间切换的消耗会非常客观，有可能会导致SDK出现错误，不建议超过200线程的使用，如果有必要使用multiprocessing技术或者手动使用脚本创建多个解释器
-
-
-
-## 一句话识别
-
-一句话识别对应的类为NlsSpeechRecognizer，其核心方法如下：
-
-### 1. 初始化（\_\_init\_\_)
-
-参数说明:
-
-| 参数              | 类型     | 参数说明                                                     |
-| ----------------- | -------- | ------------------------------------------------------------ |
-| url               | str      | 网关websocket url，默认为wss://nls-gateway.cn-shanghai.aliyuncs.com/ws/v1 |
-| akid              | str      | 账号access id，默认为None，如果需要获取token，则需要提供，如果已有token，则不需要提供 |
-| aksecret          | str      | 账号access secret key，默认为None，如果需要获取token，则需要提供，如果已有token，则不需要提供 |
-| token             | str      | 访问token，参考开发指南—获取Token及获取Token协议说明相关内容 |
-| on_start          | function | 当一句话识别就绪时的回调，回调参数有两个，一个是json形式的字符串，一个是用户自定义参数，见后续callback_args参数说明 |
-| on_result_changed | function | 当一句话识别返回中间结果时回调，回调参数有两个，一个是json形式的字符串，一个是用户自定义参数，见后续callback_args参数说明 |
-| on_completed      | function | 当一句话识别返回最终识别结果时回调，回调参数有两个，一个是json形式的字符串，一个是用户自定义参数，见后续callback_args参数说明 |
-| on_error          | function | 当SDK或云端出现错误时回调，回调参数有两个，一个是json形式的字符串，一个是用户自定义参数，见后续callback_args参数说明 |
-| on_close          | function | 当和云端连接断开时回调，回调参数有一个——用户自定义参数，见后续callback_args参数说明 |
-| callback_args     | list     | 用户自定义参数列表，里面的内容会pack成list传递给各个回调的最后一个参数 |
-
-返回值：
-
-无
-
-### 2. start
-
-> 同步开始一句话识别，该方法会阻塞当前线程直到一句话识别就绪（on_start回调返回）
-
-参数说明：
-
-| 参数                              | 类型 | 参数说明                                                     |
-| --------------------------------- | ---- | ------------------------------------------------------------ |
-| aformat                           | str  | 要识别音频格式，支持pcm，opus，opu，默认pcm，**SDK不会自动将pcm编码成opus或opu，如果需要使用opus或opu，需要用户自行编码** |
-| sample_rate                       | int  | 识别音频采样率，默认16000                                    |
-| ch                                | int  | 音频通道数，默认为1，不需要提供                              |
-| enable_intermediate_result        | bool | 是否返回中间结果，默认False                                  |
-| enable_punctutation_prediction    | bool | 是否进行识别结果标点预测，默认False                          |
-| enable_inverse_text_normalization | bool | 是否进行ITN，默认False                                       |
-| timeout                           | int  | 阻塞超时，默认10秒                                           |
-| ping_interval                     | int  | ping包发送间隔，默认8，不需要可以设置为0或None               |
-| ping_timeout                      | int  | 是否检查pong包超时，默认为None，None为不检查pong包超时       |
-| ex                                | dict | 用户提供的额外参数，该字典内容会以key:value形式合并进请求的payload段中，具体可用参数见一句话语音识别接口说明 |
-
-返回值：
-
-bool类型，False为失败，True为成功
-
-### 3. stop
-
-> 停止一句话识别，并同步等待on_completed回调结束
-
-参数说明：
-
-| 参数    | 类型 | 参数说明           |
-| ------- | ---- | ------------------ |
-| timeout | int  | 阻塞超时，默认10秒 |
-
-返回值：
-
-bool类型，False为失败，True为成功
-
-### 4. shutdown
-
-> 强行关闭当前请求，重复调用无副作用
-
-参数说明：
-
-无
-
-返回值：
-
-无
-
-### 5. send_audio
-
-> 发送二进制音频数据，发送数据的格式需要和start中的aformat对应
-
-参数说明：
-
-| 参数     | 类型  | 参数说明                                                     |
-| -------- | ----- | ------------------------------------------------------------ |
-| pcm_data | bytes | 要发送的二进制音频数据，格式需要和上一次start中的aformat相对应。**SDK不会自动将pcm编码成opus或opu，如果需要使用opus或opu，需要用户自行编码** |
-
-返回值：
-
-bool类型，False为失败，True为成功
-
-
-
-### 代码示例：
-
-```python
-import time
-import threading
-import sys
-
-import nls
-
-
-URL="wss://nls-gateway.cn-shanghai.aliyuncs.com/ws/v1"
-AKID="Your AKID"
-AKKEY="Your AKSECRET"
-APPKEY="Your APPKEY"
-
-#以下代码会根据音频文件内容反复进行一句话识别
-class TestSr:
-    def __init__(self, tid, test_file):
-        self.__th = threading.Thread(target=self.__test_run)
-        self.__id = tid
-        self.__test_file = test_file
-   
-    def loadfile(self, filename):
-        with open(filename, "rb") as f:
-            self.__data = f.read()
-    
-    def start(self):
-        self.loadfile(self.__test_file)
-        self.__th.start()
-
-    def test_on_start(self, message, *args):
-        print("test_on_start:{}".format(message))
-
-    def test_on_error(self, message, *args):
-        print("on_error args=>{}".format(args))
-
-    def test_on_close(self, *args):
-        print("on_close: args=>{}".format(args))
-
-    def test_on_result_chg(self, message, *args):
-        print("test_on_chg:{}".format(message))
-
-    def test_on_completed(self, message, *args):
-        print("on_completed:args=>{} message=>{}".format(args, message))
-
-
-    def __test_run(self):
-        print("thread:{} start..".format(self.__id))
-        
-        sr = nls.NlsSpeechRecognizer(
-                    url=URL,
-                    akid=AKID,
-                    aksecret=AKKEY,
-                    appkey=APPKEY,
-                    on_start=self.test_on_start,
-                    on_result_changed=self.test_on_result_chg,
-                    on_completed=self.test_on_completed,
-                    on_error=self.test_on_error,
-                    on_close=self.test_on_close,
-                    callback_args=[self.__id]
-                )
-        while True:
-            print("{}: session start".format(self.__id))
-            r = sr.start(aformat="pcm", ex={"hello":123})
-           
-            self.__slices = zip(*(iter(self.__data),) * 640)
-            for i in self.__slices:
-                sr.send_audio(bytes(i))
-                time.sleep(0.01)
-
-            r = sr.stop()
-            print("{}: sr stopped:{}".format(self.__id, r))
-            time.sleep(1)
-
-def multiruntest(num=500):
-    for i in range(0, num):
-        name = "thread" + str(i)
-        t = TestSr(name, "tests/test1.pcm")
-        t.start()
-
-nls.enableTrace(True)
-multiruntest(1)
-
-
-
-```
-
-
-
-## 实时语音识别
-
-实时语音识别对应的类为NlsSpeechTranscriber，其核心方法如下：
-
-### 1. 初始化（\_\_init\_\_)
-
-参数说明:
-
-| 参数              | 类型     | 参数说明                                                     |
-| ----------------- | -------- | ------------------------------------------------------------ |
-| url               | str      | 网关websocket url，默认为wss://nls-gateway.cn-shanghai.aliyuncs.com/ws/v1 |
-| akid              | str      | 账号access id，默认为None，如果需要获取token，则需要提供，如果已有token，则不需要提供 |
-| aksecret          | str      | 账号access secret key，默认为None，如果需要获取token，则需要提供，如果已有token，则不需要提供 |
-| token             | str      | 访问token，参考开发指南—获取Token及获取Token协议说明相关内容 |
-| on_start          | function | 当实时识别就绪时的回调，回调参数有两个，一个是json形式的字符串，一个是用户自定义参数，见后续callback_args参数说明 |
-| on_sentence_begin | function | 当实时识别一句话开始时回调，回调参数有两个，一个是json形式的字符串，一个是用户自定义参数，见后续callback_args参数说明 |
-| on_sentence_end   | function | 当实时识别一句话结束时回调，回调参数有两个，一个是json形式的字符串，一个是用户自定义参数，见后续callback_args参数说明 |
-| on_result_changed | function | 当实时识别返回中间结果时回调，回调参数有两个，一个是json形式的字符串，一个是用户自定义参数，见后续callback_args参数说明 |
-| on_completed      | function | 当实时识别返回最终识别结果时回调，回调参数有两个，一个是json形式的字符串，一个是用户自定义参数，见后续callback_args参数说明 |
-| on_error          | function | 当SDK或云端出现错误时回调，回调参数有两个，一个是json形式的字符串，一个是用户自定义参数，见后续callback_args参数说明 |
-| on_close          | function | 当和云端连接断开时回调，回调参数有一个——用户自定义参数，见后续callback_args参数说明 |
-| callback_args     | list     | 用户自定义参数列表，里面的内容会pack成list传递给各个回调的最后一个参数 |
-
-返回值：
-
-无
-
-### 2. start
-
-> 同步开始实时识别，该方法会阻塞当前线程直到实时识别就绪（on_start回调返回）
-
-参数说明：
-
-| 参数                              | 类型 | 参数说明                                                     |
-| --------------------------------- | ---- | ------------------------------------------------------------ |
-| aformat                           | str  | 要识别音频格式，支持pcm，opus，opu，默认pcm，**SDK不会自动将pcm编码成opus或opu，如果需要使用opus或opu，需要用户自行编码** |
-| sample_rate                       | int  | 识别音频采样率，默认16000                                    |
-| ch                                | int  | 音频通道数，默认为1，不需要提供                              |
-| enable_intermediate_result        | bool | 是否返回中间结果，默认False                                  |
-| enable_punctutation_prediction    | bool | 是否进行识别结果标点预测，默认False                          |
-| enable_inverse_text_normalization | bool | 是否进行ITN，默认False                                       |
-| timeout                           | int  | 阻塞超时，默认10秒                                           |
-| ping_interval                     | int  | ping包发送间隔，默认8，不需要可以设置为0或None               |
-| ping_timeout                      | int  | 是否检查pong包超时，默认为None，None为不检查pong包超时       |
-| ex                                | dict | 用户提供的额外参数，该字典内容会以key:value形式合并进请求的payload段中，具体可用参数见实时语音识别接口说明 |
-
-返回值：
-
-bool类型，False为失败，True为成功
-
-### 3. stop
-
-> 停止实时识别，并同步等待on_completed回调结束
-
-参数说明：
-
-| 参数    | 类型 | 参数说明           |
-| ------- | ---- | ------------------ |
-| timeout | int  | 阻塞超时，默认10秒 |
-
-返回值：
-
-bool类型，False为失败，True为成功
-
-### 4. shutdown
-
-> 强行关闭当前请求，重复调用无副作用
-
-参数说明：
-
-无
-
-返回值：
-
-无
-
-### 5. send_audio
-
-> 发送二进制音频数据，发送数据的格式需要和start中的aformat对应
-
-参数说明：
-
-| 参数     | 类型  | 参数说明                                                     |
-| -------- | ----- | ------------------------------------------------------------ |
-| pcm_data | bytes | 要发送的二进制音频数据，格式需要和上一次start中的aformat相对应。**SDK不会自动将pcm编码成opus或opu，如果需要使用opus或opu，需要用户自行编码** |
-
-返回值：
-
-bool类型，False为失败，True为成功
-
-### 6. ctrl
-
-> 发送控制命令，先阅读实时语音识别接口说明
-
-参数说明：
-
-| 参数 | 类型 | 参数说明                                                     |
-| ---- | ---- | ------------------------------------------------------------ |
-| ex   | dict | 自定义控制命令，该字典内容会以key:value形式合并进请求的payload段中 |
-
-返回值：
-
-bool类型，False为失败，True为成功
-
-
-
-### 代码示例
-
-```python
-import time
-import threading
-import sys
-
-import nls
-
-
-URL="wss://nls-gateway.cn-shanghai.aliyuncs.com/ws/v1"
-AKID="Your AKID"
-AKKEY="Your AKSECRET"
-APPKEY="Your APPKEY"
-
-#以下代码会根据音频文件内容反复进行实时识别（文件转写）
-class TestSt:
-    def __init__(self, tid, test_file):
-        self.__th = threading.Thread(target=self.__test_run)
-        self.__id = tid
-        self.__test_file = test_file
-   
-    def loadfile(self, filename):
-        with open(filename, "rb") as f:
-            self.__data = f.read()
-    
-    def start(self):
-        self.loadfile(self.__test_file)
-        self.__th.start()
-
-    def test_on_sentence_begin(self, message, *args):
-        print("test_on_sentence_begin:{}".format(message))
-
-    def test_on_sentence_end(self, message, *args):
-        print("test_on_sentence_end:{}".format(message))
-
-    def test_on_start(self, message, *args):
-        print("test_on_start:{}".format(message))
-
-    def test_on_error(self, message, *args):
-        print("on_error args=>{}".format(args))
-
-    def test_on_close(self, *args):
-        print("on_close: args=>{}".format(args))
-
-    def test_on_result_chg(self, message, *args):
-        print("test_on_chg:{}".format(message))
-
-    def test_on_completed(self, message, *args):
-        print("on_completed:args=>{} message=>{}".format(args, message))
-
-
-    def __test_run(self):
-        print("thread:{} start..".format(self.__id))
-        sr = nls.NlsSpeechTranscriber(
-                    url=URL,
-                    akid=AKID,
-                    aksecret=AKKEY,
-                    appkey=APPKEY,
-                    on_sentence_begin=self.test_on_sentence_begin,
-                    on_sentence_end=self.test_on_sentence_end,
-                    on_start=self.test_on_start,
-                    on_result_changed=self.test_on_result_chg,
-                    on_completed=self.test_on_completed,
-                    on_error=self.test_on_error,
-                    on_close=self.test_on_close,
-                    callback_args=[self.__id]
-                )
-        while True:
-            print("{}: session start".format(self.__id))
-            r = sr.start(aformat="pcm",
-                    enable_intermediate_result=True,
-                    enable_punctutation_prediction=True,
-                    enable_inverse_text_normalization=True)
-
-            self.__slices = zip(*(iter(self.__data),) * 640)
-            for i in self.__slices:
-                sr.send_audio(bytes(i))
-                time.sleep(0.01)
-
-            sr.ctrl(ex={"test":"tttt"})
-            time.sleep(1)
-
-            r = sr.stop()
-            print("{}: sr stopped:{}".format(self.__id, r))
-            time.sleep(1)
-
-def multiruntest(num=500):
-    for i in range(0, num):
-        name = "thread" + str(i)
-        t = TestSt(name, "tests/test1.pcm")
-        t.start()
-
-nls.enableTrace(True)
-multiruntest(1)
-
-```
-
-
-
-## 语音合成
-
-语音合成对应的类为NlsSpeechSynthesizer，其核心方法如下：
-
-### 1. 初始化（\_\_init\_\_)
-
-参数说明:
-
-| 参数          | 类型     | 参数说明                                                     |
-| ------------- | -------- | ------------------------------------------------------------ |
-| url           | str      | 网关websocket url，默认为wss://nls-gateway.cn-shanghai.aliyuncs.com/ws/v1 |
-| akid          | str      | 账号access id，默认为None，如果需要获取token，则需要提供，如果已有token，则不需要提供 |
-| aksecret      | str      | 账号access secret key，默认为None，如果需要获取token，则需要提供，如果已有token，则不需要提供 |
-| token         | str      | 访问token，参考开发指南—获取Token及获取Token协议说明相关内容 |
-| on_metainfo   | function | 如果start中通过ex参数传递enable_subtitle，则会返回对应字幕信息，回调参数有两个，一个是json形式的字符串，一个是用户自定义参数，见后续callback_args参数说明 |
-| on_data       | function | 当存在合成数据后回调，回调参数有两个，一个是对应start方法aformat的二进制音频数据，一个是用户自定义参数，见后续callback_args参数说明 |
-| on_completed  | function | 当合成完毕时候回调，回调参数有两个，一个是json形式的字符串，一个是用户自定义参数，见后续callback_args参数说明 |
-| on_error      | function | 当SDK或云端出现错误时回调，回调参数有两个，一个是json形式的字符串，一个是用户自定义参数，见后续callback_args参数说明 |
-| on_close      | function | 当和云端连接断开时回调，回调参数有一个——用户自定义参数，见后续callback_args参数说明 |
-| callback_args | list     | 用户自定义参数列表，里面的内容会pack成list传递给各个回调的最后一个参数 |
-
-返回值：
-
-无
-
-### 2. start
-
-> 同步开始语音合成，如果wait_complete为True（默认）则会阻塞直到所有音频合成完毕（on_completed返回之后）返回，否则会立即返回
-
-参数说明：
-
-| 参数              | 类型 | 参数说明                                                     |
-| ----------------- | ---- | ------------------------------------------------------------ |
-| text              | str  | 要合成的文字                                                 |
-| aformat           | str  | 合成出来音频的格式，默认为"pcm"                              |
-| voice             | str  | 发音人，默认为”xiaoyun“                                      |
-| sample_rate       | int  | 识别音频采样率，默认16000                                    |
-| volume            | int  | 音量大小，0~100，默认为50                                    |
-| speech_rate       | int  | 语速，-500~500，默认为0                                      |
-| pitch_rate        | int  | 语调，-500~500，默认为0                                      |
-| wait_complete     | bool | 是否阻塞到合成完成                                           |
-| start_timeout     | int  | 和云端连接建立超时，默认10秒                                 |
-| completed_timeout | int  | 从连接建立到合成完成超时，默认60秒                           |
-| ping_interval                     | int  | ping包发送间隔，默认8，不需要可以设置为0或None               |
-| ping_timeout                      | int  | 是否检查pong包超时，默认为None，None为不检查pong包超时       |
-| ex                | dict | 用户提供的额外参数，该字典内容会以key:value形式合并进请求的payload段中，具体可用参数见实时语音识别接口说明 |
-
-返回值：
-
-bool类型，False为失败，True为成功
-
-### 3. shutdown
-
-> 强行关闭当前请求，重复调用无副作用
-
-参数说明：
-
-无
-
-返回值：
-
-无
-
-### 代码示例：
-
-```python
-import time
-import threading
-import sys
-
-import nls
-
-URL="wss://nls-gateway.cn-shanghai.aliyuncs.com/ws/v1"
-AKID="Your AKID"
-AKKEY="Your AKSECRET"
-APPKEY="Your APPKEY"
-
-
-
-TEXT='大壮正想去摘取花瓣，谁知阿丽和阿强突然内讧，阿丽拿去手枪向树干边的阿强射击，两声枪响，阿强直接倒入水中'
-
-#以下代码会根据上述TEXT文本反复进行语音合成
-class TestTts:
-    def __init__(self, tid, test_file):
-        self.__th = threading.Thread(target=self.__test_run)
-        self.__id = tid
-        self.__test_file = test_file
-   
-    def start(self, text):
-        self.__text = text
-        self.__f = open(self.__test_file, "wb")
-        self.__th.start()
-    
-    def test_on_metainfo(self, message, *args):
-        print("on_metainfo message=>{}".format(message))  
-
-    def test_on_error(self, message, *args):
-        print("on_error args=>{}".format(args))
-
-    def test_on_close(self, *args):
-        print("on_close: args=>{}".format(args))
-        try:
-            self.__f.close()
-        except Exception as e:
-            print("close file failed since:", e)
-
-    def test_on_data(self, data, *args):
-        try:
-            self.__f.write(data)
-        except Exception as e:
-            print("write data failed:", e)
-
-    def test_on_completed(self, message, *args):
-        print("on_completed:args=>{} message=>{}".format(args, message))
-
-
-    def __test_run(self):
-        print("thread:{} start..".format(self.__id))
-        tts = nls.NlsSpeechSynthesizer(
-                    url=URL,
-                    akid=AKID,
-                    aksecret=AKKEY,
-                    appkey=APPKEY,
-                    on_metainfo=self.test_on_metainfo,
-                    on_data=self.test_on_data,
-                    on_completed=self.test_on_completed,
-                    on_error=self.test_on_error,
-                    on_close=self.test_on_close,
-                    callback_args=[self.__id]
-                )
-
-        while True:
-            print("{}: session start".format(self.__id))
-            r = tts.start(self.__text, voice="ailun")
-            print("{}: tts done with result:{}".format(self.__id, r))
-            time.sleep(1)
-
-def multiruntest(num=500):
-    for i in range(0, num):
-        name = "thread" + str(i)
-        t = TestTts(name, "tests/test_tts.pcm")
-        t.start(TEXT)
-
-nls.enableTrace(True)
-multiruntest(1)
-
-```
-
-
-
-## 高级用法——使用CommonProto实现任意接口
-
-nls包下面的NlsCommonProto对象可以用来实现智能语音交互的任意接口，其核心方法如下：
-
-### 1. 初始化（\_\_init\_\_)
-
-参数说明:
-
-| 参数          | 类型     | 参数说明                                                     |
-| ------------- | -------- | ------------------------------------------------------------ |
-| url           | str      | 网关websocket url，默认为wss://nls-gateway.cn-shanghai.aliyuncs.com/ws/v1 |
-| akid          | str      | 账号access id，默认为None，如果需要获取token，则需要提供，如果已有token，则不需要提供 |
-| aksecret      | str      | 账号access secret key，默认为None，如果需要获取token，则需要提供，如果已有token，则不需要提供 |
-| token         | str      | 访问token，参考开发指南—获取Token及获取Token协议说明相关内容 |
-| on_open       | function | 当和云端建连完成后回调，回调参数有一个——用户自定义参数，见后续callback_args参数说明 |
-| on_error      | function | 当SDK或云端出现错误时回调，回调参数有两个，一个是json形式的字符串，一个是用户自定义参数，见后续callback_args参数说明 |
-| on_close      | function | 当和云端连接断开时回调，回调参数有一个——用户自定义参数，见后续callback_args参数说明 |
-| on_data       | function | 当云端返回二进制数据时回调，回调参数有两个，一个是二进制数据，一个是用户自定义参数，见后续callback_args参数说明 |
-| user_callback | dict     | 用户自定义回调字典，key为str类型，value为function类型，当SDK收到云端应答后，会根据应答内部header字段下面name字段来索引该字典，如果对应item存在则会回调对应function。该回调包括两个参数，一个是json形式的字符串，一个是用户自定义参数，见后续callback_args参数说明 |
-| callback_args | list     | 用户自定义参数列表，里面的内容会pack成list传递给各个回调的最后一个参数 |
-
-返回值：
-
-无
-
-### 2. start
-
-> **异步**和云端建连并发起一次请求
-
-参数说明：
-
-| 参数    | 类型 | 参数说明                                 |
-| ------- | ---- | ---------------------------------------- |
-| name    | str  | 对应header字段中的name，用于指明请求类型 |
-| payload | dict | 请求中的payload，默认为{}                |
-| context | dict | 请求中的context，默认为{}                |
-| ping_interval                     | int  | ping包发送间隔，默认8，不需要可以设置为0或None               |
-| ping_timeout                      | int  | 是否检查pong包超时，默认为None，None为不检查pong包超时       |
-
-返回值：
-
-bool类型，False为失败，True为成功
-
-### 3. shutdown
-
-> 强行关闭当前请求，重复调用无副作用
-
-参数说明：
-
-无
-
-返回值：
-
-无
-
-### 4. send_text
-
-> 发送文本请求
-
-参数说明：
-
-| 参数    | 类型 | 参数说明                                 |
-| ------- | ---- | ---------------------------------------- |
-| name    | str  | 对应header字段中的name，用于指明请求类型 |
-| payload | dict | 请求中的payload，默认为{}                |
-| context | dict | 请求中的context，默认为{}                |
-
-返回值：
-
-bool类型，False为失败，True为成功
-
-### 5. send_binary
-
-> 发送二进制数据
-
-参数说明：
-
-| 参数     | 类型  | 参数说明           |
-| -------- | ----- | ------------------ |
-| pcm_data | bytes | 要发送的二进频数据 |
-
-返回值：
-
-bool类型，False为失败，True为成功
-
-
-
-### 示例代码：
-
-```python
-import time
-import threading
-import sys
-
-import nls
-
-URL="wss://nls-gateway.cn-shanghai.aliyuncs.com/ws/v1"
-AKID="Your AKID"
-AKKEY="Your AKSECRET"
-APPKEY="Your APPKEY"
-
-#以下代码会通过文件反复进行一句话识别
-class TestSrCommon:
-    def __init__(self, tid, test_file):
-        self.__th = threading.Thread(target=self.__test_run)
-        self.__id = tid
-        self.__test_file = test_file
-   
-    def loadfile(self, filename):
-        with open(filename, "rb") as f:
-            self.__data = f.read()
-    
-    def start(self):
-        self.loadfile(self.__test_file)
-        self.__th.start()
-
-    def test_on_open(self, *args):
-        print("test_on_start")
-
-    def test_on_start(self, message, *args):
-        print("test_on_start:{}".format(message))
-
-    def test_on_data(self, data, *args):
-        pass
-
-    def test_on_error(self, message, *args):
-        print("on_error args=>{}".format(args))
-
-    def test_on_close(self, *args):
-        print("on_close: args=>{}".format(args))
-
-    def test_on_result_chg(self, message, *args):
-        print("test_on_chg:{}".format(message))
-
-    def test_on_completed(self, message, *args):
-        print("on_completed:args=>{} message=>{}".format(args, message))
-        self.__sr.shutdown() 
-
-    def __test_run(self):
-        print("thread:{} start..".format(self.__id))
-        callbacks = {
-                "RecognitionStarted":self.test_on_start,
-                "RecognitionResultChanged":self.test_on_result_chg,
-                "RecognitionCompleted":self.test_on_completed
-                }
-        self.__sr = nls.NlsCommonProto(
-                    url=URL,
-                    akid=AKID,
-                    aksecret=AKKEY,
-                    appkey=APPKEY,
-                    namespace="SpeechRecognizer",
-                    on_open=self.test_on_open,
-                    on_data=self.test_on_data,
-                    on_error=self.test_on_error,
-                    on_close=self.test_on_close,
-                    user_callback=callbacks,
-                    callback_args=[self.__id]
-                )
-
-        while True:
-            print("{}: session start".format(self.__id))
-            r = self.__sr.start(name="StartRecognition",
-                    payload={
-                        "format":"pcm",
-                        "sample_rate":16000
-                        }
-                    )
-            if not r:
-                print("start failed")
-                break
-            time.sleep(1)
-           
-            self.__slices = zip(*(iter(self.__data),) * 640)
-            for i in self.__slices:
-                self.__sr.send_binary(bytes(i))
-                time.sleep(0.01)
-
-            self.__sr.send_text(name="StopRecognition") 
-            time.sleep(1)
-
-def multiruntest(num=500):
-    for i in range(0, num):
-        name = "thread" + str(i)
-        t = TestSrCommon(name, "tests/test1.pcm")
-        t.start()
-
-nls.enableTrace(True)
-multiruntest(1)
-
-```
-
diff --git a/nls/__init__.py b/nls/__init__.py
deleted file mode 100644
index 5e25fad..0000000
--- a/nls/__init__.py
+++ /dev/null
@@ -1,26 +0,0 @@
-"""
-__init__.py
-
-Copyright 1999-present Alibaba Group Holding Ltd.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-"""
-
-from ._logging import *
-from ._speech_recognizer import *
-from ._speech_transcriber import *
-from ._speech_synthesizer import *
-from ._common_proto import *
-from ._util import *
-
-__version__ = "0.0.1"
diff --git a/nls/__pycache__/_logger.cpython-36.pyc b/nls/__pycache__/_logger.cpython-36.pyc
deleted file mode 100644
index fcc25b6..0000000
Binary files a/nls/__pycache__/_logger.cpython-36.pyc and /dev/null differ
diff --git a/nls/__pycache__/_logging.cpython-36.pyc b/nls/__pycache__/_logging.cpython-36.pyc
deleted file mode 100644
index fc8558e..0000000
Binary files a/nls/__pycache__/_logging.cpython-36.pyc and /dev/null differ
diff --git a/nls/_common_proto.py b/nls/_common_proto.py
deleted file mode 100644
index 514f94e..0000000
--- a/nls/_common_proto.py
+++ /dev/null
@@ -1,271 +0,0 @@
-"""
-_common_proto.py
-
-Copyright 1999-present Alibaba Group Holding Ltd.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-"""
-
-import logging
-import uuid
-import json
-import threading
-
-from nls._core import NlsCore
-from . import _logging
-
-__URL__ = "wss://nls-gateway.cn-shanghai.aliyuncs.com/ws/v1"
-__all__ = ["NlsCommonProto"]
-
-
-class NlsCommonProto:
-    """
-    Api for users to define their own protocols correspond to cloud
-    applications. 
-
-    """
-    def __init__(self, url=__URL__,
-                 akid=None, aksecret=None,
-                 token=None, appkey=None,
-                 namespace=None,
-                 on_open=None,
-                 on_error=None,
-                 on_close=None,
-                 on_data=None,
-                 user_callback={},
-                 callback_args=[]):
-        """
-        NlsCommonProto initialization
-
-        Parameters:
-        -----------
-        url: str
-            websocket url.
-        akid: str
-            access id from aliyun. if you provide a token, ignore this argument.
-        aksecret: str
-            access secret key from aliyun. if you provide a token, ignore this
-            argument.
-        token: str
-            access token. if you do not have a token, provide access id and key
-            secret from your aliyun account.
-        appkey: str
-            appkey from aliyun.
-        namespace: str
-            protocol namespace.
-        on_open: function
-            Callback object which is called when connection established.
-            on_start has one argument.
-            The 1st argument is *args which is callback_args.
-        on_error: function
-            Callback object which is called when any error occurs.
-            on_error has two arguments.
-            The 1st argument is message which is a json format string.
-            The 2nd argument is *args which is callback_args.
-        on_close: function
-            Callback object which is called when connection closed.
-            on_close has one arguments.
-            The 1st argument is *args which is callback_args.
-        on_data: function
-            Callback object which is called when partial synthesis result arrived
-            arrived.
-            on_result_changed has two arguments.
-            The 1st argument is binary data corresponding to aformat in start
-            method.
-            The 2nd argument is *args which is callback_args.
-        user_callback: dict
-            Dict for users to define their handlers. Key is name for cloud response
-            "name" field in "head". Value is method to handle this response.
-        callback_args: list
-            callback_args will return in callbacks above for *args.
-        """
-        if not namespace:
-            raise ValueError("no namespace provided")
-        self.__namespace = namespace
-        self.__response_handler__ = {
-            "TaskFailed": self.__task_failed
-        }
-        for key in user_callback:
-            self.__response_handler__[key] = user_callback[key]
-        self.__callback_args = callback_args
-        self.__appkey = appkey
-        self.__url = url
-        self.__akid = akid
-        self.__aksecret = aksecret
-        self.__token = token
-        self.__on_open = on_open
-        self.__on_error = on_error
-        self.__on_close = on_close
-        self.__on_data = on_data
-        
-    def __handle_message(self, message):
-        _logging.debug("__handle_message")
-        try:
-            __result = json.loads(message)
-            if __result["header"]["name"] in self.__response_handler__:
-                __handler = self.__response_handler__[__result["header"]["name"]]
-                __handler(message)
-            else:
-                _logging.error("cannot handle cmd{}".format(
-                    __result["header"]["name"]))
-                return
-        except json.JSONDecodeError:
-            _logging.error("cannot parse message:{}".format(message))
-            return
-
-    def __common_core_on_open(self):
-        _logging.debug("__common_core_on_open")
-        if self.__on_open:
-            self.__on_open(*self.__callback_args)
-
-    def __common_core_on_msg(self, msg, *args):
-        _logging.debug("__common_core_on_msg:msg={} args={}".format(msg, args))
-        self.__handle_message(msg)
-
-    def __common_core_on_data(self, data, opcode, flag):
-        _logging.debug("__common_core_on_data")
-        if self.__on_data:
-            self.__on_data(data, *self.__callback_args)
-
-    def __common_core_on_error(self, msg, *args):
-        _logging.debug("__common_core_on_error:msg={} args={}".format(msg, args))
-
-    def __common_core_on_close(self):
-        _logging.debug("__common_core_on_close")
-        if self.__on_close:
-            self.__on_close(*self.__callback_args)
-
-    def __task_failed(self, message):
-        _logging.debug("__task_failed")
-        with self.__start_cond:
-            self.__start_flag = False
-            self.__start_cond.notify()
-        if self.__on_error:
-            self.__on_error(message, *self.__callback_args)
-
-    def start(self, name="",
-              payload={},
-              context={},
-              ping_interval=8,
-              ping_timeout=None):
-        """
-        Protocol async starts
-
-        Parameters:
-        -----------
-        name: str
-            current request "name" in "header" field
-        payload: dict
-            payload for current request
-        context: dict
-            context for current request
-        ping_interval: int
-            send ping interval, 0 for disable ping send, default is 8
-        ping_timeout: int
-            timeout after send ping and recive pong, set None for disable timeout check and default is None
-        """
-        self.__nls = NlsCore(
-            url=self.__url, akid=self.__akid,
-            aksecret=self.__aksecret, token=self.__token,
-            on_open=self.__common_core_on_open,
-            on_message=self.__common_core_on_msg,
-            on_data=self.__common_core_on_data,
-            on_close=self.__common_core_on_close,
-            on_error=self.__common_core_on_error,
-            asynch=True,
-            callback_args=[])
-
-        if not name:
-            raise ValueError("no name provided")
-        __id4 = uuid.uuid4().hex
-        self.__task_id = uuid.uuid4().hex
-
-        __header = {
-            "message_id": __id4,
-            "task_id": self.__task_id,
-            "namespace": self.__namespace,
-            "name": name,
-            "appkey": self.__appkey
-        }
-
-        __msg = {
-            "header": __header,
-            "payload": payload,
-            "context": context
-        }
-        __jmsg = json.dumps(__msg)
-        return self.__nls.start(__jmsg, ping_interval, ping_timeout)
-    
-    def shutdown(self):
-        """
-        Shutdown connection immediately
-        """
-        self.__nls.shutdown()
-
-
-    def send_text(self, name="", payload={}, context={}):
-        """
-        Send text request to cloud
-
-        Parameters:
-        -----------
-        name: str
-            "name" in "header" field
-        payload: dict
-            "payload" in request
-        context: dict
-            "context" in request
-        """
-        if not name:
-            raise ValueError("no name provided")
-        __id4 = uuid.uuid4().hex
-        __header = {
-            "message_id": __id4,
-            "task_id": self.__task_id,
-            "namespace": self.__namespace,
-            "name": name,
-            "appkey": self.__appkey
-        }
-
-        __msg = {
-            "header": __header,
-            "payload": payload,
-            "context": context
-        }
-        __jmsg = json.dumps(__msg)
-        try:
-            self.__nls.send(__jmsg, False)
-        except ConnectionResetError as e:
-            _logging.error("connection reset")
-            self.__nls.shutdown()
-            raise e
-            return False
-        return True
-
-    def send_binary(self, pcm_data):
-        """
-        Send binary data to cloud
-
-        Parameters:
-        -----------
-        pcm_data: bytes
-            binary data
-        """
-        try:
-            self.__nls.send(pcm_data, True)
-        except ConnectionResetError as __e:
-            _logging.error("connection reset")
-            self.__nls.shutdown()
-            raise __e
-            return False
-        return True
diff --git a/nls/_core.py b/nls/_core.py
deleted file mode 100644
index 3b59fc8..0000000
--- a/nls/_core.py
+++ /dev/null
@@ -1,216 +0,0 @@
-'''
-_core.py
-
-Copyright 1999-present Alibaba Group Holding Ltd.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-'''
-
-
-import logging
-import threading
-
-from enum import Enum, unique
-from queue import Queue
-
-import nls.websocket as websocket
-import nls._token
-
-from . import _logging
-
-__URL__ = "wss://nls-gateway.cn-shanghai.aliyuncs.com/ws/v1"
-__HEADER__ = [
-    "Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==",
-    "Sec-WebSocket-Version: 13",
-]
-
-__FORMAT__ = "%(asctime)s - %(levelname)s - %(message)s"
-#__all__ = ["NlsCore"]
-
-def core_on_msg(ws, message, args):
-    _logging.debug("core_on_msg:{}".format(message))
-    if not args:
-        _logging.error("callback core_on_msg with null args")
-        return
-    nls = args[0]
-    nls._NlsCore__issue_callback("on_message", [message])
-
-def core_on_error(ws, message, args):
-    _logging.debug("core_on_error:{}".format(message))
-    if not args:
-        _logging.error("callback core_on_error with null args")
-        return
-    nls = args[0]
-    nls._NlsCore__issue_callback("on_error", [message])
-
-def core_on_close(ws, close_status_code, close_msg, args):
-    _logging.debug("core_on_close")
-    if not args:
-        _logging.error("callback core_on_close with null args")
-        return
-    nls = args[0]
-    nls._NlsCore__issue_callback("on_close")
-
-def core_on_open(ws, args):
-    _logging.debug("core_on_open:{}".format(args))
-    if not args:
-        _logging.debug("callback with null args")
-        ws.close()
-    elif len(args) != 2:
-        _logging.debug("callback args not 2")
-        ws.close()
-    nls = args[0]
-    nls._NlsCore__notify_on_open()
-    nls.start(args[1], nls._NlsCore__ping_interval, nls._NlsCore__ping_timeout)
-    nls._NlsCore__issue_callback("on_open")
-
-def core_on_data(ws, data, opcode, flag, args):
-    _logging.debug("core_on_data opcode={}".format(opcode))
-    if not args:
-        _logging.error("callback core_on_data with null args")
-        return
-    nls = args[0]
-    nls._NlsCore__issue_callback("on_data", [data, opcode, flag])
-
-@unique
-class NlsConnectionStatus(Enum):
-    Disconnected = 0
-    Connected = 1
-
-
-class NlsCore:
-    """
-    NlsCore
-    """
-    def __init__(self, url=__URL__,
-                 akid=None, aksecret=None, token=None,
-                 on_open=None, on_message=None, on_close=None,
-                 on_error=None, on_data=None, asynch=False, callback_args=[]):
-        self.__url = url
-        self.__async = asynch
-        if not akid or not aksecret:
-            if not token:
-                raise ValueError("akid aksecret and toke are all None")
-            else:
-                self.__get_token = False
-                self.__token = token
-        else:
-            self.__get_token = True
-        self.__callbacks = {}
-        if on_open:
-            self.__callbacks["on_open"] = on_open
-        if on_message:
-            self.__callbacks["on_message"] = on_message
-        if on_close:
-            self.__callbacks["on_close"] = on_close
-        if on_error:
-            self.__callbacks["on_error"] = on_error
-        if on_data:
-            self.__callbacks["on_data"] = on_data
-
-        if not on_open and not on_message and not on_close and not on_error:
-            raise ValueError("all callbacks are None, what are you doing?")
-
-        _logging.debug("callback args:{}".format(callback_args))
-        self.__callback_args = callback_args
-
-        if self.__get_token:
-            token = nls._token.getToken(akid, aksecret)
-            _logging.debug("get token {}".format(token))
-            self.__token = token
-        self.__header = __HEADER__ + ["X-NLS-Token: {}".format(self.__token)]
-        websocket.enableTrace(True)
-        self.__ws = websocket.WebSocketApp(self.__url,
-                                           self.__header,
-                                           on_message=core_on_msg,
-                                           on_data=core_on_data,
-                                           on_error=core_on_error,
-                                           on_close=core_on_close,
-                                           callback_args=[self])
-        self.__ws.on_open = core_on_open
-        self.__lock = threading.Lock()
-        self.__cond = threading.Condition()
-        self.__connection_status = NlsConnectionStatus.Disconnected
-        self.__pending_msg = []
-
-    def start(self, msg, ping_interval, ping_timeout):
-        self.__lock.acquire()
-        self.__ping_interval = ping_interval
-        self.__ping_timeout = ping_timeout
-        if self.__connection_status == NlsConnectionStatus.Disconnected:
-            self.__ws.update_args(self, msg)
-            self.__lock.release()
-            return self.__connect_before_start(ping_interval, ping_timeout)
-        else:
-            self.__lock.release()
-            self.__ws.send(msg)
-            return True
-
-    def __notify_on_open(self):
-        _logging.debug("notify on open")
-        with self.__cond:
-            self.__connection_status = NlsConnectionStatus.Connected
-            self.__cond.notify()
-
-    def __issue_callback(self, which, exargs=[]):
-        if which not in self.__callbacks:
-            _logging.error("no such callback:{}".format(which))
-            return
-        if which is "on_close":
-            with self.__cond:
-                self.__connection_status = NlsConnectionStatus.Disconnected
-                self.__cond.notify()
-        args = exargs+self.__callback_args
-        self.__callbacks[which](*args)
-
-    def send(self, msg, binary):
-        self.__lock.acquire()
-        if self.__connection_status == NlsConnectionStatus.Disconnected:
-            self.__lock.release()
-            _logging.error("start before send")
-            return False
-        else:
-            self.__lock.release()
-            if binary:
-                self.__ws.send(msg, opcode=websocket.ABNF.OPCODE_BINARY)
-            else:
-                _logging.debug("send {}".format(msg))
-                self.__ws.send(msg)
-            return True
-    
-    def shutdown(self):
-        self.__ws.close()
-
-    def __run(self, ping_interval, ping_timeout):
-        _logging.debug("ws run...")
-        self.__ws.run_forever(ping_interval=ping_interval,
-                ping_timeout=ping_timeout)
-        with self.__lock:
-            self.__connection_status = NlsConnectionStatus.Disconnected
-        _logging.debug("ws exit...")
-
-    def __connect_before_start(self, ping_interval, ping_timeout):
-        with self.__cond:
-            self.__th = threading.Thread(target=self.__run,
-                    args=[ping_interval, ping_timeout])
-            self.__th.start()
-            if self.__connection_status == NlsConnectionStatus.Disconnected:
-                _logging.debug("wait cond wakeup")
-                if not self.__async:
-                    if self.__cond.wait(timeout=10):
-                        _logging.debug("wakeup without timeout")
-                        return self.__connection_status == NlsConnectionStatus.Connected
-                    else:
-                        _logging.debug("wakeup with timeout")
-                        return False
-        return True
diff --git a/nls/_logging.py b/nls/_logging.py
deleted file mode 100644
index 2e6a509..0000000
--- a/nls/_logging.py
+++ /dev/null
@@ -1,83 +0,0 @@
-"""
-_logging.py
-
-Copyright 1999-present Alibaba Group Holding Ltd.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-"""
-
-
-
-import logging
-
-_logger = logging.getLogger("nls")
-
-try:
-    from logging import NullHandler
-except ImportError:
-    class NullHandler(logging.Handler):
-        def emit(self, record):
-            pass
-
-_logger.addHandler(NullHandler())
-_traceEnabled = False
-__LOG_FORMAT__ = "%(asctime)s - %(levelname)s - %(message)s"
-
-__all__=["enableTrace", "dump", "error", "warning", "debug", "trace",
-        "isEnabledForError", "isEnabledForDebug", "isEnabledForTrace"]
-
-def enableTrace(traceable, handler=logging.StreamHandler()):
-    """
-    enable log print
-
-    Parameters
-    ----------
-    traceable: bool
-        whether enable log print, default log level is logging.DEBUG
-    handler: Handler object
-        handle how to print out log, default to stdio
-    """
-    global _traceEnabled
-    _traceEnabled = traceable
-    if traceable:
-        _logger.addHandler(handler)
-        _logger.setLevel(logging.DEBUG)
-        handler.setFormatter(logging.Formatter(__LOG_FORMAT__))
-
-def dump(title, message):
-    if _traceEnabled:
-        _logger.debug("### " + title + " ###")
-        _logger.debug(message)
-        _logger.debug("########################################")
-
-def error(msg):
-    _logger.error(msg)
-
-def warning(msg):
-    _logger.warning(msg)
-
-def debug(msg):
-    _logger.debug(msg)
-
-def trace(msg):
-    if _traceEnabled:
-        _logger.debug(msg)
-
-def isEnabledForError():
-    return _logger.isEnabledFor(logging.ERROR)
-
-def isEnabledForDebug():
-    return _logger.isEnabledFor(logging.Debug)
-
-def isEnabledForTrace():
-    return _traceEnabled
diff --git a/nls/_speech_recognizer.py b/nls/_speech_recognizer.py
deleted file mode 100644
index 496555b..0000000
--- a/nls/_speech_recognizer.py
+++ /dev/null
@@ -1,336 +0,0 @@
-"""
-_speech_recognizer.py
-
-Copyright 1999-present Alibaba Group Holding Ltd.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-"""
-
-import logging
-import uuid
-import json
-import threading
-
-
-from nls._core import NlsCore
-from . import _logging
-
-__SPEECH_RECOGNIZER_NAMESPACE__ = "SpeechRecognizer"
-
-__SPEECH_RECOGNIZER_REQUEST_CMD__ = {
-    "start": "StartRecognition",
-    "stop": "StopRecognition"
-}
-
-__URL__ = "wss://nls-gateway.cn-shanghai.aliyuncs.com/ws/v1"
-
-__all__ = ["NlsSpeechRecognizer"]
-
-
-class NlsSpeechRecognizer:
-    """
-    Api for short sentence speech recognition
-
-    """
-
-    def __init__(self, url=__URL__,
-                 akid=None, aksecret=None,
-                 token=None, appkey=None,
-                 on_start=None,
-                 on_result_changed=None,
-                 on_completed=None,
-                 on_error=None, on_close=None,
-                 callback_args=[]):
-        """
-        NlsSpeechRecognizer initialization
-
-        Parameters:
-        -----------
-        url: str
-            websocket url.
-        akid: str
-            access id from aliyun. if you provide a token, ignore this argument.
-        aksecret: str
-            access secret key from aliyun. if you provide a token, ignore this
-            argument.
-        token: str
-            access token. if you do not have a token, provide access id and key
-            secret from your aliyun account.
-        appkey: str
-            appkey from aliyun
-        on_start: function
-            Callback object which is called when recognition started.
-            on_start has two arguments.
-            The 1st argument is message which is a json format string.
-            The 2nd argument is *args which is callback_args.
-        on_result_changed: function
-            Callback object which is called when partial recognition result
-            arrived.
-            on_result_changed has two arguments.
-            The 1st argument is message which is a json format string.
-            The 2nd argument is *args which is callback_args.
-        on_completed: function
-            Callback object which is called when recognition is completed.
-            on_completed has two arguments.
-            The 1st argument is message which is a json format string.
-            The 2nd argument is *args which is callback_args.
-        on_error: function
-            Callback object which is called when any error occurs.
-            on_error has two arguments.
-            The 1st argument is message which is a json format string.
-            The 2nd argument is *args which is callback_args.
-        on_close: function
-            Callback object which is called when connection closed.
-            on_close has one arguments.
-            The 1st argument is *args which is callback_args.
-        callback_args: list
-            callback_args will return in callbacks above for *args.
-        """
-        self.__response_handler__ = {
-            "RecognitionStarted": self.__recognition_started,
-            "RecognitionResultChanged": self.__recognition_result_changed,
-            "RecognitionCompleted": self.__recognition_completed,
-            "TaskFailed": self.__task_failed
-        }
-        self.__callback_args = callback_args
-        self.__appkey = appkey
-        self.__url = url
-        self.__akid = akid
-        self.__aksecret = aksecret
-        self.__token = token
-        self.__start_cond = threading.Condition()
-        self.__start_flag = False
-        self.__on_start = on_start
-        self.__on_result_changed = on_result_changed
-        self.__on_completed = on_completed
-        self.__on_error = on_error
-        self.__on_close = on_close
-        self.__allow_aformat = (
-            "pcm", "opus", "opu"
-        )
-
-    def __handle_message(self, message):
-        _logging.debug("__handle_message")
-        try:
-            __result = json.loads(message)
-            if __result["header"]["name"] in self.__response_handler__:
-                __handler = self.__response_handler__[
-                    __result["header"]["name"]]
-                __handler(message)
-            else:
-                _logging.error("cannot handle cmd{}".format(
-                    __result["header"]["name"]))
-                return
-        except json.JSONDecodeError:
-            _logging.error("cannot parse message:{}".format(message))
-            return
-
-    def __sr_core_on_open(self):
-        _logging.debug("__sr_core_on_open")
-
-    def __sr_core_on_msg(self, msg, *args):
-        _logging.debug("__sr_core_on_msg:msg={} args={}".format(msg, args))
-        self.__handle_message(msg)
-
-    def __sr_core_on_error(self, msg, *args):
-        _logging.debug("__sr_core_on_error:msg={} args={}".format(msg, args))
-
-    def __sr_core_on_close(self):
-        _logging.debug("__sr_core_on_close")
-        if self.__on_close:
-            self.__on_close(*self.__callback_args)
-        with self.__start_cond:
-            self.__start_flag = False
-            self.__start_cond.notify()
-
-    def __recognition_started(self, message):
-        _logging.debug("__recognition_started")
-        if self.__on_start:
-            self.__on_start(message, *self.__callback_args)
-        with self.__start_cond:
-            self.__start_flag = True
-            self.__start_cond.notify()
-
-    def __recognition_result_changed(self, message):
-        _logging.debug("__recognition_result_changed")
-        if self.__on_result_changed:
-            self.__on_result_changed(message, *self.__callback_args)
-
-    def __recognition_completed(self, message):
-        _logging.debug("__recognition_completed")
-        self.__nls.shutdown()
-        _logging.debug("__recognition_completed shutdown done")
-        if self.__on_completed:
-            self.__on_completed(message, *self.__callback_args)
-        with self.__start_cond:
-            self.__start_flag = False
-            self.__start_cond.notify()
-
-    def __task_failed(self, message):
-        _logging.debug("__task_failed")
-        with self.__start_cond:
-            self.__start_flag = False
-            self.__start_cond.notify()
-        if self.__on_error:
-            self.__on_error(message, *self.__callback_args)
-
-    def start(self, aformat="pcm", sample_rate=16000, ch=1,
-              enable_intermediate_result=False,
-              enable_punctutation_prediction=False,
-              enable_inverse_text_normalization=False,
-              timeout=10,
-              ping_interval=8,
-              ping_timeout=None,
-              ex={}):
-        """
-        Recognition start 
-
-        Parameters:
-        -----------
-        aformat: str
-            audio binary format, support: "pcm", "opu", "opus", default is "pcm"
-        sample_rate: int
-            audio sample rate, default is 16000
-        ch: int
-            audio channels, only support mono which is 1
-        enable_intermediate_result: bool
-            whether enable return intermediate recognition result, default is False
-        enable_punctutation_prediction: bool
-            whether enable punctutation prediction, default is False
-        enable_inverse_text_normalization: bool
-            whether enable ITN, default is False
-        timeout: int
-            wait timeout for connection setup
-        ping_interval: int
-            send ping interval, 0 for disable ping send, default is 8
-        ping_timeout: int
-            timeout after send ping and recive pong, set None for disable timeout check and default is None
-        ex: dict
-            dict which will merge into "payload" field in request
-        """
-        self.__nls = NlsCore(
-            url=self.__url, akid=self.__akid,
-            aksecret=self.__aksecret, token=self.__token,
-            on_open=self.__sr_core_on_open,
-            on_message=self.__sr_core_on_msg,
-            on_close=self.__sr_core_on_close,
-            on_error=self.__sr_core_on_error,
-            callback_args=[])
-
-        if ch != 1:
-            raise ValueError("not support channel: {}".format(ch))
-        if aformat not in self.__allow_aformat:
-            raise ValueError("format {} not support".format(aformat))
-        __id4 = uuid.uuid4().hex
-        self.__task_id = uuid.uuid4().hex
-        self.__audio_format = aformat
-
-        __header = {
-            "message_id": __id4,
-            "task_id": self.__task_id,
-            "namespace": __SPEECH_RECOGNIZER_NAMESPACE__,
-            "name": __SPEECH_RECOGNIZER_REQUEST_CMD__["start"],
-            "appkey": self.__appkey
-        }
-        __payload = {
-            "format": aformat,
-            "sample_rate": sample_rate,
-            "enable_intermediate_result": enable_intermediate_result,
-            "enable_punctuation_prediction": enable_punctutation_prediction,
-            "enable_inverse_text_normalization": enable_inverse_text_normalization
-        }
-
-        for key in ex:
-            __payload[key] = ex[key]
-
-        __msg = {
-            "header": __header,
-            "payload": __payload
-        }
-        __jmsg = json.dumps(__msg)
-        with self.__start_cond:
-            if self.__start_flag:
-                _logging.debug("already start...")
-                return False
-            if self.__nls.start(__jmsg, ping_interval, ping_timeout):
-                if self.__start_flag == False:
-                    if self.__start_cond.wait(timeout):
-                        return self.__start_flag == True
-                    else:
-                        return False
-            else:
-                return False
-
-    def stop(self, timeout=10):
-        """
-        Stop recognition and mark session finished
-
-        Parameters:
-        -----------
-        timeout: int
-            timeout for waiting completed message from cloud
-        """
-        __id4 = uuid.uuid4().hex
-        __header = {
-            "message_id": __id4,
-            "task_id": self.__task_id,
-            "namespace": __SPEECH_RECOGNIZER_NAMESPACE__,
-            "name": __SPEECH_RECOGNIZER_REQUEST_CMD__["stop"],
-            "appkey": self.__appkey
-        }
-        __msg = {
-            "header": __header
-        }
-        __jmsg = json.dumps(__msg)
-        with self.__start_cond:
-            if not self.__start_flag:
-                _logging.debug("not start yet...")
-                return False
-            self.__nls.send(__jmsg, False)
-            if self.__start_flag == True:
-                _logging.debug("stop wait..")
-                if self.__start_cond.wait(timeout):
-                    return self.__start_flag == False
-                else:
-                    return False
-            else:
-                return True
-
-    def shutdown(self):
-        """
-        Shutdown connection immediately
-        """
-        self.__nls.shutdown()
-
-    def send_audio(self, pcm_data):
-        """
-        Send audio binary, audio size prefer 20ms length 
-
-        Parameters:
-        -----------
-        pcm_data: bytes
-            audio binary which format is "aformat" in start method 
-        """
-        __data = pcm_data
-        with self.__start_cond:
-            if not self.__start_flag:
-                return False
-        try:
-            self.__nls.send(__data, True)
-        except ConnectionResetError as __e:
-            _logging.error("connection reset")
-            self.__start_flag = False
-            self.__nls.shutdown()
-            raise __e
-            return False
-        return True
diff --git a/nls/_speech_synthesizer.py b/nls/_speech_synthesizer.py
deleted file mode 100644
index c16ae28..0000000
--- a/nls/_speech_synthesizer.py
+++ /dev/null
@@ -1,310 +0,0 @@
-"""
-_speech_synthesizer.py
-
-Copyright 1999-present Alibaba Group Holding Ltd.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-"""
-
-
-import logging
-import uuid
-import json
-import threading
-
-from nls._core import NlsCore
-from . import _logging
-
-__SPEECH_SYNTHESIZER_NAMESPACE__ = "SpeechSynthesizer"
-
-__SPEECH_SYNTHESIZER_REQUEST_CMD__ = {
-    "start": "StartSynthesis"
-}
-
-__URL__ = "wss://nls-gateway.cn-shanghai.aliyuncs.com/ws/v1"
-
-__all__ = ["NlsSpeechSynthesizer"]
-
-
-class NlsSpeechSynthesizer:
-    """
-    Api for text-to-speech 
-
-    """
-    def __init__(self, url=__URL__,
-                 akid=None, aksecret=None,
-                 token=None, appkey=None,
-                 on_metainfo=None,
-                 on_data=None,
-                 on_completed=None,
-                 on_error=None, on_close=None,
-                 callback_args=[]):
-        """
-        NlsSpeechSynthesizer initialization
-
-        Parameters:
-        -----------
-        url: str
-            websocket url.
-        akid: str
-            access id from aliyun. if you provide a token, ignore this argument.
-        aksecret: str
-            access secret key from aliyun. if you provide a token, ignore this
-            argument.
-        token: str
-            access token. if you do not have a token, provide access id and key
-            secret from your aliyun account.
-        appkey: str
-            appkey from aliyun
-        on_metainfo: function
-            Callback object which is called when recognition started.
-            on_start has two arguments.
-            The 1st argument is message which is a json format string.
-            The 2nd argument is *args which is callback_args.
-        on_data: function
-            Callback object which is called when partial synthesis result arrived
-            arrived.
-            on_result_changed has two arguments.
-            The 1st argument is binary data corresponding to aformat in start
-            method.
-            The 2nd argument is *args which is callback_args.
-        on_completed: function
-            Callback object which is called when recognition is completed.
-            on_completed has two arguments.
-            The 1st argument is message which is a json format string.
-            The 2nd argument is *args which is callback_args.
-        on_error: function
-            Callback object which is called when any error occurs.
-            on_error has two arguments.
-            The 1st argument is message which is a json format string.
-            The 2nd argument is *args which is callback_args.
-        on_close: function
-            Callback object which is called when connection closed.
-            on_close has one arguments.
-            The 1st argument is *args which is callback_args.
-        callback_args: list
-            callback_args will return in callbacks above for *args.
-        """
-
-        self.__response_handler__ = {
-            "MetaInfo": self.__metainfo,
-            "SynthesisCompleted": self.__synthesis_completed,
-            "TaskFailed": self.__task_failed
-        }
-        self.__callback_args = callback_args
-        self.__url = url
-        self.__appkey = appkey
-        self.__akid = akid
-        self.__aksecret = aksecret
-        self.__token = token
-        self.__start_cond = threading.Condition()
-        self.__start_flag = False
-        self.__on_metainfo = on_metainfo
-        self.__on_data = on_data
-        self.__on_completed = on_completed
-        self.__on_error = on_error
-        self.__on_close = on_close
-        self.__allow_aformat = (
-            "pcm", "wav", "mp3"
-                )
-        self.__allow_sample_rate = (
-            8000, 11025, 16000, 22050,
-            24000, 32000, 44100, 48000
-                )
-
-    def __handle_message(self, message):
-        _logging.debug("__handle_message")
-        try:
-            __result = json.loads(message)
-            if __result["header"]["name"] in self.__response_handler__:
-                __handler = self.__response_handler__[__result["header"]["name"]]
-                __handler(message)
-            else:
-                _logging.error("cannot handle cmd{}".format(
-                    __result["header"]["name"]))
-                return
-        except json.JSONDecodeError:
-            _logging.error("cannot parse message:{}".format(message))
-            return
-
-    def __syn_core_on_open(self):
-        _logging.debug("__syn_core_on_open")
-        with self.__start_cond:
-            self.__start_flag = True
-            self.__start_cond.notify()
-
-    def __syn_core_on_data(self, data, opcode, flag):
-        _logging.debug("__syn_core_on_data")
-        if self.__on_data:
-            self.__on_data(data, *self.__callback_args)
-
-    def __syn_core_on_msg(self, msg, *args):
-        _logging.debug("__syn_core_on_msg:msg={} args={}".format(msg, args))
-        self.__handle_message(msg)
-
-    def __syn_core_on_error(self, msg, *args):
-        _logging.debug("__sr_core_on_error:msg={} args={}".format(msg, args))
-
-    def __syn_core_on_close(self):
-        _logging.debug("__sr_core_on_close")
-        if self.__on_close:
-            self.__on_close(*self.__callback_args)
-        with self.__start_cond:
-            self.__start_flag = False
-            self.__start_cond.notify()
-
-    def __metainfo(self, message):
-        _logging.debug("__metainfo")
-        if self.__on_metainfo:
-            self.__on_metainfo(message, *self.__callback_args)
-
-    def __synthesis_completed(self, message):
-        _logging.debug("__synthesis_completed")
-        self.__nls.shutdown()
-        _logging.debug("__synthesis_completed shutdown done")
-        if self.__on_completed:
-            self.__on_completed(message, *self.__callback_args)
-        with self.__start_cond:
-            self.__start_flag = False
-            self.__start_cond.notify()
-
-    def __task_failed(self, message):
-        _logging.debug("__task_failed")
-        with self.__start_cond:
-            self.__start_flag = False
-            self.__start_cond.notify()
-        if self.__on_error:
-            self.__on_error(message, *self.__callback_args)
-
-    def start(self, text="", voice="xiaoyun",
-              aformat="pcm", sample_rate=16000,
-              volume=50, speech_rate=0, pitch_rate=0,
-              wait_complete=True,
-              start_timeout=10,
-              completed_timeout=60,
-              ex={}):
-        """
-        Synthesis start 
-
-        Parameters:
-        -----------
-        text: str
-            utf-8 text
-        voice: str
-            voice for text-to-speech, default is xiaoyun
-        aformat: str
-            audio binary format, support: "pcm", "wav", "mp3", default is "pcm"
-        sample_rate: int
-            audio sample rate, default is 16000, support:8000, 11025, 16000, 22050,
-            24000, 32000, 44100, 48000
-        volume: int
-            audio volume, from 0~100, default is 50
-        speech_rate: int
-            speech rate from -500~500, default is 0
-        pitch_rate: int
-            pitch for voice from -500~500, default is 0
-        wait_complete: bool
-            whether block until syntheis completed or timeout for completed timeout
-        start_timeout: int
-            timeout for connection established
-        completed_timeout: int
-            timeout for waiting synthesis completed from connection established
-        ex: dict
-            dict which will merge into "payload" field in request
-        """
-        self.__nls = NlsCore(
-            url=self.__url, akid=self.__akid,
-            aksecret=self.__aksecret,
-            token=self.__token,
-            on_open=self.__syn_core_on_open,
-            on_message=self.__syn_core_on_msg,
-            on_data=self.__syn_core_on_data,
-            on_close=self.__syn_core_on_close,
-            on_error=self.__syn_core_on_error,
-            callback_args=[])
-
-        if aformat not in self.__allow_aformat:
-            raise ValueError("format {} not support".format(aformat))
-        if sample_rate not in self.__allow_sample_rate:
-            raise ValueError("samplerate {} not support".format(sample_rate))
-        
-        if volume < 0 or volume > 100:
-            raise ValueError("volume {} not support".format(volume))
-        
-        if speech_rate < -500 or speech_rate > 500:
-            raise ValueError("speech_rate {} not support".format(speech_rate))
-        
-        if pitch_rate < -500 or pitch_rate > 500:
-            raise ValueError("pitch rate {} not support".format(pitch_rate))
-
-        __id4 = uuid.uuid4().hex
-        self.__task_id = uuid.uuid4().hex
-
-        __header = {
-            "message_id": __id4,
-            "task_id": self.__task_id,
-            "namespace": __SPEECH_SYNTHESIZER_NAMESPACE__,
-            "name": __SPEECH_SYNTHESIZER_REQUEST_CMD__["start"],
-            "appkey": self.__appkey
-        }
-        __payload = {
-            "text": text,
-            "voice": voice,
-            "format": aformat,
-            "sample_rate": sample_rate,
-            "volume": volume,
-            "speech_rate": speech_rate,
-            "pitch_rate": pitch_rate
-        }
-
-        for key in ex:
-            __payload[key] = ex[key]
-
-        __msg = {
-            "header": __header,
-            "payload": __payload
-        }
-        __jmsg = json.dumps(__msg)
-        with self.__start_cond:
-            if self.__start_flag:
-                _logging.debug("already start...")
-                return False
-            if self.__nls.start(__jmsg, ping_interval=0, ping_timeout=None):
-                if self.__start_flag == False:
-                    if not self.__start_cond.wait(start_timeout):
-                        _logging.debug("syn start timeout")
-                        return False
-                    if not wait_complete:
-                        _logging.debug("do not wait completed")
-                        return self.__start_flag == True
-                    if not self.__start_flag:
-                        _logging.debug("started but flag not true")
-                        return False
-            else:
-                _logging.debug("nls core start failed")
-                return False
-            if self.__start_flag:
-                if not self.__start_cond.wait(completed_timeout):
-                    _logging.debug("wait completed timeout")
-                    return False
-                else:
-                    return self.__start_flag == False
-            else:
-                _logging.debug("wait completed but start flag is false")
-                return True
-
-    def shutdown(self):
-        """
-        Shutdown connection immediately
-        """
-        self.__nls.shutdown()
diff --git a/nls/_speech_transcriber.py b/nls/_speech_transcriber.py
deleted file mode 100644
index d6eeb0c..0000000
--- a/nls/_speech_transcriber.py
+++ /dev/null
@@ -1,391 +0,0 @@
-"""
-_speech_transcriber.py
-
-Copyright 1999-present Alibaba Group Holding Ltd.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-"""
-
-import logging
-import uuid
-import json
-import threading
-
-from nls._core import NlsCore
-from . import _logging
-
-__SPEECH_TRANSCRIBER_NAMESPACE__ = "SpeechTranscriber"
-
-__SPEECH_TRANSCRIBER_REQUEST_CMD__ = {
-    "start": "StartTranscription",
-    "stop": "StopTranscription",
-    "control": "ControlTranscriber"
-}
-
-__URL__ = "wss://nls-gateway.cn-shanghai.aliyuncs.com/ws/v1"
-__all__ = ["NlsSpeechTranscriber"]
-
-
-class NlsSpeechTranscriber:
-    """
-    Api for realtime speech transcription
-
-    """
-
-    def __init__(self, url=__URL__,
-                 akid=None, aksecret=None,
-                 token=None, appkey=None,
-                 on_start=None,
-                 on_sentence_begin=None,
-                 on_sentence_end=None,
-                 on_result_changed=None,
-                 on_completed=None,
-                 on_error=None, on_close=None,
-                 callback_args=[]):
-        """
-        NlsSpeechTranscriber initialization
-
-        Parameters:
-        -----------
-        url: str
-            websocket url.
-        akid: str
-            access id from aliyun. if you provide a token, ignore this argument.
-        aksecret: str
-            access secret key from aliyun. if you provide a token, ignore this
-            argument.
-        token: str
-            access token. if you do not have a token, provide access id and key
-            secret from your aliyun account.
-        appkey: str
-            appkey from aliyun
-        on_start: function
-            Callback object which is called when recognition started.
-            on_start has two arguments.
-            The 1st argument is message which is a json format string.
-            The 2nd argument is *args which is callback_args.
-        on_sentence_begin: function
-            Callback object which is called when one sentence started.
-            on_sentence_begin has two arguments.
-            The 1st argument is message which is a json format string.
-            The 2nd argument is *args which is callback_args.
-        on_sentence_end: function
-            Callback object which is called when sentence is end.
-            on_sentence_end has two arguments.
-            The 1st argument is message which is a json format string.
-            The 2nd argument is *args which is callback_args.
-        on_result_changed: function
-            Callback object which is called when partial recognition result
-            arrived.
-            on_result_changed has two arguments.
-            The 1st argument is message which is a json format string.
-            The 2nd argument is *args which is callback_args.
-        on_completed: function
-            Callback object which is called when recognition is completed.
-            on_completed has two arguments.
-            The 1st argument is message which is a json format string.
-            The 2nd argument is *args which is callback_args.
-        on_error: function
-            Callback object which is called when any error occurs.
-            on_error has two arguments.
-            The 1st argument is message which is a json format string.
-            The 2nd argument is *args which is callback_args.
-        on_close: function
-            Callback object which is called when connection closed.
-            on_close has one arguments.
-            The 1st argument is *args which is callback_args.
-        callback_args: list
-            callback_args will return in callbacks above for *args.
-        """
-        self.__response_handler__ = {
-            "SentenceBegin": self.__sentence_begin,
-            "SentenceEnd": self.__sentence_end,
-            "TranscriptionStarted": self.__transcription_started,
-            "TranscriptionResultChanged": self.__transcription_result_changed,
-            "TranscriptionCompleted": self.__transcription_completed,
-            "TaskFailed": self.__task_failed
-        }
-        self.__callback_args = callback_args
-        self.__url = url
-        self.__akid = akid
-        self.__aksecret = aksecret
-        self.__appkey = appkey
-        self.__token = token
-        self.__start_cond = threading.Condition()
-        self.__start_flag = False
-        self.__on_start = on_start
-        self.__on_sentence_begin = on_sentence_begin
-        self.__on_sentence_end = on_sentence_end
-        self.__on_result_changed = on_result_changed
-        self.__on_completed = on_completed
-        self.__on_error = on_error
-        self.__on_close = on_close
-        self.__allow_aformat = (
-            "pcm", "opus", "opu"
-        )
-
-    def __handle_message(self, message):
-        _logging.debug("__handle_message")
-        try:
-            __result = json.loads(message)
-            if __result["header"]["name"] in self.__response_handler__:
-                __handler = self.__response_handler__[
-                    __result["header"]["name"]]
-                __handler(message)
-            else:
-                _logging.error("cannot handle cmd{}".format(
-                    __result["header"]["name"]))
-                return
-        except json.JSONDecodeError:
-            _logging.error("cannot parse message:{}".format(message))
-            return
-
-    def __tr_core_on_open(self):
-        _logging.debug("__tr_core_on_open")
-
-    def __tr_core_on_msg(self, msg, *args):
-        _logging.debug("__tr_core_on_msg:msg={} args={}".format(msg, args))
-        self.__handle_message(msg)
-
-    def __tr_core_on_error(self, msg, *args):
-        _logging.debug("__tr_core_on_error:msg={} args={}".format(msg, args))
-
-    def __tr_core_on_close(self):
-        _logging.debug("__tr_core_on_close")
-        if self.__on_close:
-            self.__on_close(*self.__callback_args)
-        with self.__start_cond:
-            self.__start_flag = False
-            self.__start_cond.notify()
-
-    def __sentence_begin(self, message):
-        _logging.debug("__sentence_begin")
-        if self.__on_sentence_begin:
-            self.__on_sentence_begin(message, *self.__callback_args)
-
-    def __sentence_end(self, message):
-        _logging.debug("__sentence_end")
-        if self.__on_sentence_end:
-            self.__on_sentence_end(message, *self.__callback_args)
-
-    def __transcription_started(self, message):
-        _logging.debug("__transcription_started")
-        if self.__on_start:
-            self.__on_start(message, *self.__callback_args)
-        with self.__start_cond:
-            self.__start_flag = True
-            self.__start_cond.notify()
-
-    def __transcription_result_changed(self, message):
-        _logging.debug("__transcription_result_changed")
-        if self.__on_result_changed:
-            self.__on_result_changed(message, *self.__callback_args)
-
-    def __transcription_completed(self, message):
-        _logging.debug("__transcription_completed")
-        self.__nls.shutdown()
-        _logging.debug("__transcription_completed shutdown done")
-        if self.__on_completed:
-            self.__on_completed(message, *self.__callback_args)
-        with self.__start_cond:
-            self.__start_flag = False
-            self.__start_cond.notify()
-
-    def __task_failed(self, message):
-        _logging.debug("__task_failed")
-        with self.__start_cond:
-            self.__start_flag = False
-            self.__start_cond.notify()
-        if self.__on_error:
-            self.__on_error(message, *self.__callback_args)
-
-    def start(self, aformat="pcm", sample_rate=16000, ch=1,
-              enable_intermediate_result=False,
-              enable_punctutation_prediction=False,
-              enable_inverse_text_normalization=False,
-              timeout=10,
-              ping_interval=8,
-              ping_timeout=None,
-              ex={}):
-        """
-        Transcription start 
-
-        Parameters:
-        -----------
-        aformat: str
-            audio binary format, support: "pcm", "opu", "opus", default is "pcm"
-        sample_rate: int
-            audio sample rate, default is 16000
-        ch: int
-            audio channels, only support mono which is 1
-        enable_intermediate_result: bool
-            whether enable return intermediate recognition result, default is False
-        enable_punctutation_prediction: bool
-            whether enable punctutation prediction, default is False
-        enable_inverse_text_normalization: bool
-            whether enable ITN, default is False
-        timeout: int
-            wait timeout for connection setup
-        ping_interval: int
-            send ping interval, 0 for disable ping send, default is 8
-        ping_timeout: int
-            timeout after send ping and recive pong, set None for disable timeout check and default is None
-        ex: dict
-            dict which will merge into "payload" field in request
-        """
-        self.__nls = NlsCore(
-            url=self.__url, akid=self.__akid, aksecret=self.__aksecret,
-            token=self.__token,
-            on_open=self.__tr_core_on_open,
-            on_message=self.__tr_core_on_msg,
-            on_close=self.__tr_core_on_close,
-            on_error=self.__tr_core_on_error,
-            callback_args=[])
-
-        if ch != 1:
-            raise ValueError("not support channel: {}".format(ch))
-        if aformat not in self.__allow_aformat:
-            raise ValueError("format {} not support".format(aformat))
-        __id4 = uuid.uuid4().hex
-        self.__task_id = uuid.uuid4().hex
-        self.__audio_format = aformat
-
-        __header = {
-            "message_id": __id4,
-            "task_id": self.__task_id,
-            "namespace": __SPEECH_TRANSCRIBER_NAMESPACE__,
-            "name": __SPEECH_TRANSCRIBER_REQUEST_CMD__["start"],
-            "appkey": self.__appkey
-        }
-        __payload = {
-            "format": aformat,
-            "sample_rate": sample_rate,
-            "enable_intermediate_result": enable_intermediate_result,
-            "enable_punctuation_prediction": enable_punctutation_prediction,
-            "enable_inverse_text_normalization": enable_inverse_text_normalization
-        }
-
-        for key in ex:
-            __payload[key] = ex[key]
-
-        __msg = {
-            "header": __header,
-            "payload": __payload
-        }
-        __jmsg = json.dumps(__msg)
-        with self.__start_cond:
-            if self.__start_flag:
-                _logging.debug("already start...")
-                return False
-            if self.__nls.start(__jmsg, ping_interval, ping_timeout):
-                if self.__start_flag == False:
-                    if self.__start_cond.wait(timeout):
-                        return self.__start_flag == True
-                    else:
-                        return False
-            else:
-                return False
-
-    def stop(self, timeout=10):
-        """
-        Stop transcription and mark session finished
-
-        Parameters:
-        -----------
-        timeout: int
-            timeout for waiting completed message from cloud
-        """
-        __id4 = uuid.uuid4().hex
-        __header = {
-            "message_id": __id4,
-            "task_id": self.__task_id,
-            "namespace": __SPEECH_TRANSCRIBER_NAMESPACE__,
-            "name": __SPEECH_TRANSCRIBER_REQUEST_CMD__["stop"],
-            "appkey": self.__appkey
-        }
-        __msg = {
-            "header": __header
-        }
-        __jmsg = json.dumps(__msg)
-        with self.__start_cond:
-            if not self.__start_flag:
-                _logging.debug("not start yet...")
-                return False
-            self.__nls.send(__jmsg, False)
-            if self.__start_flag == True:
-                _logging.debug("stop wait..")
-                if self.__start_cond.wait(timeout):
-                    return self.__start_flag == False
-                else:
-                    return False
-            else:
-                return True
-
-    def ctrl(self, ex={}):
-        """
-        Send control message to cloud
-
-        Parameters:
-        -----------
-        ex: dict
-            dict which will merge into "payload" field in request
-        """
-        __id4 = uuid.uuid4().hex
-        __header = {
-            "message_id": __id4,
-            "task_id": self.__task_id,
-            "namespace": __SPEECH_TRANSCRIBER_NAMESPACE__,
-            "name": __SPEECH_TRANSCRIBER_REQUEST_CMD__["control"],
-            "appkey": self.__appkey
-        }
-        __msg = {
-            "header": __header,
-            "payload": ex
-        }
-        __jmsg = json.dumps(__msg)
-        with self.__start_cond:
-            if not self.__start_flag:
-                _logging.debug("not start yet...")
-                return False
-            self.__nls.send(__jmsg, False)
-            return True
-
-    def shutdown(self):
-        """
-        Shutdown connection immediately
-        """
-        self.__nls.shutdown()
-
-    def send_audio(self, pcm_data):
-        """
-        Send audio binary, audio size prefer 20ms length 
-
-        Parameters:
-        -----------
-        pcm_data: bytes
-            audio binary which format is "aformat" in start method 
-        """
-
-        __data = pcm_data
-        with self.__start_cond:
-            if not self.__start_flag:
-                return False
-        try:
-            self.__nls.send(__data, True)
-        except ConnectionResetError as __e:
-            _logging.error("connection reset")
-            self.__start_flag = False
-            self.__nls.shutdown()
-            raise __e
-            return False
-        return True
diff --git a/nls/_token.py b/nls/_token.py
deleted file mode 100644
index e993d1d..0000000
--- a/nls/_token.py
+++ /dev/null
@@ -1,64 +0,0 @@
-"""
-_token.py
-
-Copyright 1999-present Alibaba Group Holding Ltd.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-"""
-
-
-from aliyunsdkcore.client import AcsClient
-from aliyunsdkcore.request import CommonRequest
-
-import json
-
-__all__ = ["getToken"]
-
-
-def getToken(akid, aksecret, domain="cn-shanghai",
-             version="2019-02-28",
-             url="nls-meta.cn-shanghai.aliyuncs.com"):
-    """
-    Help methods to get token from aliyun by giving access id and access secret
-    key
-
-    Parameters:
-    -----------
-    akid: str
-        access id from aliyun
-    aksecret: str
-        access secret key from aliyun
-    domain: str:
-        default is cn-shanghai
-    version: str:
-        default is 2019-02-28
-    url: str
-        full url for getting token, default is
-        nls-meta.cn-shanghai.aliyuncs.com
-    """
-    client = AcsClient(akid, aksecret, domain)
-    request = CommonRequest()
-    request.set_method('POST')
-    request.set_domain(url)
-    request.set_version(version)
-    request.set_action_name('CreateToken')
-    response = client.do_action_with_exception(request)
-    response_json = json.loads(response)
-    if "Token" in response_json:
-        token = response_json["Token"]
-        if "Id" in token:
-            return token["Id"]
-        else:
-            print(f"No id in token:{token}")
-    else:
-        print(f"Token not in response:{response_json}")
diff --git a/nls/_util.py b/nls/_util.py
deleted file mode 100644
index e2b85c5..0000000
--- a/nls/_util.py
+++ /dev/null
@@ -1,49 +0,0 @@
-"""
-_util.py
-
-Copyright 1999-present Alibaba Group Holding Ltd.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-"""
-
-
-
-from struct import *
-
-__all__=["wav2pcm"]
-
-def wav2pcm(wavfile, pcmfile):
-    """
-    Turn wav into pcm
-    
-    Parameters
-    ----------
-    wavfile: str
-        wav file path
-    pcmfile: str
-        output pcm file path
-    """
-    with open(wavfile, "rb") as i, open(pcmfile, "wb") as o:
-        i.seek(0)
-        _id = i.read(4)
-        _id = unpack('>I', _id)
-        _size = i.read(4)
-        _size = unpack('<I', _size)
-        _type = i.read(4)
-        _type = unpack(">I", _type)
-        if _id[0] != 0x52494646 or _type[0] != 0x57415645:
-            raise ValueError("not a wav!")
-        i.read(32)
-        result = i.read()
-        o.write(result)
-
diff --git a/nls/websocket/__init__.py b/nls/websocket/__init__.py
deleted file mode 100644
index a9fa463..0000000
--- a/nls/websocket/__init__.py
+++ /dev/null
@@ -1,26 +0,0 @@
-"""
-__init__.py
-websocket - WebSocket client library for Python
-
-Copyright 2021 engn33r
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-"""
-from ._abnf import *
-from ._app import WebSocketApp
-from ._core import *
-from ._exceptions import *
-from ._logging import *
-from ._socket import *
-
-__version__ = "1.2.1"
diff --git a/nls/websocket/_abnf.py b/nls/websocket/_abnf.py
deleted file mode 100644
index da4ba6e..0000000
--- a/nls/websocket/_abnf.py
+++ /dev/null
@@ -1,423 +0,0 @@
-"""
-
-"""
-
-"""
-_abnf.py
-websocket - WebSocket client library for Python
-
-Copyright 2021 engn33r
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-"""
-import array
-import os
-import struct
-import sys
-
-from ._exceptions import *
-from ._utils import validate_utf8
-from threading import Lock
-
-try:
-    # If wsaccel is available, use compiled routines to mask data.
-    # wsaccel only provides around a 10% speed boost compared
-    # to the websocket-client _mask() implementation.
-    # Note that wsaccel is unmaintained.
-    from wsaccel.xormask import XorMaskerSimple
-
-    def _mask(_m, _d):
-        return XorMaskerSimple(_m).process(_d)
-
-except ImportError:
-    # wsaccel is not available, use websocket-client _mask()
-    native_byteorder = sys.byteorder
-
-    def _mask(mask_value, data_value):
-        datalen = len(data_value)
-        data_value = int.from_bytes(data_value, native_byteorder)
-        mask_value = int.from_bytes(mask_value * (datalen // 4) + mask_value[: datalen % 4], native_byteorder)
-        return (data_value ^ mask_value).to_bytes(datalen, native_byteorder)
-
-
-__all__ = [
-    'ABNF', 'continuous_frame', 'frame_buffer',
-    'STATUS_NORMAL',
-    'STATUS_GOING_AWAY',
-    'STATUS_PROTOCOL_ERROR',
-    'STATUS_UNSUPPORTED_DATA_TYPE',
-    'STATUS_STATUS_NOT_AVAILABLE',
-    'STATUS_ABNORMAL_CLOSED',
-    'STATUS_INVALID_PAYLOAD',
-    'STATUS_POLICY_VIOLATION',
-    'STATUS_MESSAGE_TOO_BIG',
-    'STATUS_INVALID_EXTENSION',
-    'STATUS_UNEXPECTED_CONDITION',
-    'STATUS_BAD_GATEWAY',
-    'STATUS_TLS_HANDSHAKE_ERROR',
-]
-
-# closing frame status codes.
-STATUS_NORMAL = 1000
-STATUS_GOING_AWAY = 1001
-STATUS_PROTOCOL_ERROR = 1002
-STATUS_UNSUPPORTED_DATA_TYPE = 1003
-STATUS_STATUS_NOT_AVAILABLE = 1005
-STATUS_ABNORMAL_CLOSED = 1006
-STATUS_INVALID_PAYLOAD = 1007
-STATUS_POLICY_VIOLATION = 1008
-STATUS_MESSAGE_TOO_BIG = 1009
-STATUS_INVALID_EXTENSION = 1010
-STATUS_UNEXPECTED_CONDITION = 1011
-STATUS_BAD_GATEWAY = 1014
-STATUS_TLS_HANDSHAKE_ERROR = 1015
-
-VALID_CLOSE_STATUS = (
-    STATUS_NORMAL,
-    STATUS_GOING_AWAY,
-    STATUS_PROTOCOL_ERROR,
-    STATUS_UNSUPPORTED_DATA_TYPE,
-    STATUS_INVALID_PAYLOAD,
-    STATUS_POLICY_VIOLATION,
-    STATUS_MESSAGE_TOO_BIG,
-    STATUS_INVALID_EXTENSION,
-    STATUS_UNEXPECTED_CONDITION,
-    STATUS_BAD_GATEWAY,
-)
-
-
-class ABNF:
-    """
-    ABNF frame class.
-    See http://tools.ietf.org/html/rfc5234
-    and http://tools.ietf.org/html/rfc6455#section-5.2
-    """
-
-    # operation code values.
-    OPCODE_CONT = 0x0
-    OPCODE_TEXT = 0x1
-    OPCODE_BINARY = 0x2
-    OPCODE_CLOSE = 0x8
-    OPCODE_PING = 0x9
-    OPCODE_PONG = 0xa
-
-    # available operation code value tuple
-    OPCODES = (OPCODE_CONT, OPCODE_TEXT, OPCODE_BINARY, OPCODE_CLOSE,
-               OPCODE_PING, OPCODE_PONG)
-
-    # opcode human readable string
-    OPCODE_MAP = {
-        OPCODE_CONT: "cont",
-        OPCODE_TEXT: "text",
-        OPCODE_BINARY: "binary",
-        OPCODE_CLOSE: "close",
-        OPCODE_PING: "ping",
-        OPCODE_PONG: "pong"
-    }
-
-    # data length threshold.
-    LENGTH_7 = 0x7e
-    LENGTH_16 = 1 << 16
-    LENGTH_63 = 1 << 63
-
-    def __init__(self, fin=0, rsv1=0, rsv2=0, rsv3=0,
-                 opcode=OPCODE_TEXT, mask=1, data=""):
-        """
-        Constructor for ABNF. Please check RFC for arguments.
-        """
-        self.fin = fin
-        self.rsv1 = rsv1
-        self.rsv2 = rsv2
-        self.rsv3 = rsv3
-        self.opcode = opcode
-        self.mask = mask
-        if data is None:
-            data = ""
-        self.data = data
-        self.get_mask_key = os.urandom
-
-    def validate(self, skip_utf8_validation=False):
-        """
-        Validate the ABNF frame.
-
-        Parameters
-        ----------
-        skip_utf8_validation: skip utf8 validation.
-        """
-        if self.rsv1 or self.rsv2 or self.rsv3:
-            raise WebSocketProtocolException("rsv is not implemented, yet")
-
-        if self.opcode not in ABNF.OPCODES:
-            raise WebSocketProtocolException("Invalid opcode %r", self.opcode)
-
-        if self.opcode == ABNF.OPCODE_PING and not self.fin:
-            raise WebSocketProtocolException("Invalid ping frame.")
-
-        if self.opcode == ABNF.OPCODE_CLOSE:
-            l = len(self.data)
-            if not l:
-                return
-            if l == 1 or l >= 126:
-                raise WebSocketProtocolException("Invalid close frame.")
-            if l > 2 and not skip_utf8_validation and not validate_utf8(self.data[2:]):
-                raise WebSocketProtocolException("Invalid close frame.")
-
-            code = 256 * self.data[0] + self.data[1]
-            if not self._is_valid_close_status(code):
-                raise WebSocketProtocolException("Invalid close opcode.")
-
-    @staticmethod
-    def _is_valid_close_status(code):
-        return code in VALID_CLOSE_STATUS or (3000 <= code < 5000)
-
-    def __str__(self):
-        return "fin=" + str(self.fin) \
-            + " opcode=" + str(self.opcode) \
-            + " data=" + str(self.data)
-
-    @staticmethod
-    def create_frame(data, opcode, fin=1):
-        """
-        Create frame to send text, binary and other data.
-
-        Parameters
-        ----------
-        data: <type>
-            data to send. This is string value(byte array).
-            If opcode is OPCODE_TEXT and this value is unicode,
-            data value is converted into unicode string, automatically.
-        opcode: <type>
-            operation code. please see OPCODE_XXX.
-        fin: <type>
-            fin flag. if set to 0, create continue fragmentation.
-        """
-        if opcode == ABNF.OPCODE_TEXT and isinstance(data, str):
-            data = data.encode("utf-8")
-        # mask must be set if send data from client
-        return ABNF(fin, 0, 0, 0, opcode, 1, data)
-
-    def format(self):
-        """
-        Format this object to string(byte array) to send data to server.
-        """
-        if any(x not in (0, 1) for x in [self.fin, self.rsv1, self.rsv2, self.rsv3]):
-            raise ValueError("not 0 or 1")
-        if self.opcode not in ABNF.OPCODES:
-            raise ValueError("Invalid OPCODE")
-        length = len(self.data)
-        if length >= ABNF.LENGTH_63:
-            raise ValueError("data is too long")
-
-        frame_header = chr(self.fin << 7 |
-                           self.rsv1 << 6 | self.rsv2 << 5 | self.rsv3 << 4 |
-                           self.opcode).encode('latin-1')
-        if length < ABNF.LENGTH_7:
-            frame_header += chr(self.mask << 7 | length).encode('latin-1')
-        elif length < ABNF.LENGTH_16:
-            frame_header += chr(self.mask << 7 | 0x7e).encode('latin-1')
-            frame_header += struct.pack("!H", length)
-        else:
-            frame_header += chr(self.mask << 7 | 0x7f).encode('latin-1')
-            frame_header += struct.pack("!Q", length)
-
-        if not self.mask:
-            return frame_header + self.data
-        else:
-            mask_key = self.get_mask_key(4)
-            return frame_header + self._get_masked(mask_key)
-
-    def _get_masked(self, mask_key):
-        s = ABNF.mask(mask_key, self.data)
-
-        if isinstance(mask_key, str):
-            mask_key = mask_key.encode('utf-8')
-
-        return mask_key + s
-
-    @staticmethod
-    def mask(mask_key, data):
-        """
-        Mask or unmask data. Just do xor for each byte
-
-        Parameters
-        ----------
-        mask_key: <type>
-            4 byte string.
-        data: <type>
-            data to mask/unmask.
-        """
-        if data is None:
-            data = ""
-
-        if isinstance(mask_key, str):
-            mask_key = mask_key.encode('latin-1')
-
-        if isinstance(data, str):
-            data = data.encode('latin-1')
-
-        return _mask(array.array("B", mask_key), array.array("B", data))
-
-
-class frame_buffer:
-    _HEADER_MASK_INDEX = 5
-    _HEADER_LENGTH_INDEX = 6
-
-    def __init__(self, recv_fn, skip_utf8_validation):
-        self.recv = recv_fn
-        self.skip_utf8_validation = skip_utf8_validation
-        # Buffers over the packets from the layer beneath until desired amount
-        # bytes of bytes are received.
-        self.recv_buffer = []
-        self.clear()
-        self.lock = Lock()
-
-    def clear(self):
-        self.header = None
-        self.length = None
-        self.mask = None
-
-    def has_received_header(self):
-        return self.header is None
-
-    def recv_header(self):
-        header = self.recv_strict(2)
-        b1 = header[0]
-        fin = b1 >> 7 & 1
-        rsv1 = b1 >> 6 & 1
-        rsv2 = b1 >> 5 & 1
-        rsv3 = b1 >> 4 & 1
-        opcode = b1 & 0xf
-        b2 = header[1]
-        has_mask = b2 >> 7 & 1
-        length_bits = b2 & 0x7f
-
-        self.header = (fin, rsv1, rsv2, rsv3, opcode, has_mask, length_bits)
-
-    def has_mask(self):
-        if not self.header:
-            return False
-        return self.header[frame_buffer._HEADER_MASK_INDEX]
-
-    def has_received_length(self):
-        return self.length is None
-
-    def recv_length(self):
-        bits = self.header[frame_buffer._HEADER_LENGTH_INDEX]
-        length_bits = bits & 0x7f
-        if length_bits == 0x7e:
-            v = self.recv_strict(2)
-            self.length = struct.unpack("!H", v)[0]
-        elif length_bits == 0x7f:
-            v = self.recv_strict(8)
-            self.length = struct.unpack("!Q", v)[0]
-        else:
-            self.length = length_bits
-
-    def has_received_mask(self):
-        return self.mask is None
-
-    def recv_mask(self):
-        self.mask = self.recv_strict(4) if self.has_mask() else ""
-
-    def recv_frame(self):
-
-        with self.lock:
-            # Header
-            if self.has_received_header():
-                self.recv_header()
-            (fin, rsv1, rsv2, rsv3, opcode, has_mask, _) = self.header
-
-            # Frame length
-            if self.has_received_length():
-                self.recv_length()
-            length = self.length
-
-            # Mask
-            if self.has_received_mask():
-                self.recv_mask()
-            mask = self.mask
-
-            # Payload
-            payload = self.recv_strict(length)
-            if has_mask:
-                payload = ABNF.mask(mask, payload)
-
-            # Reset for next frame
-            self.clear()
-
-            frame = ABNF(fin, rsv1, rsv2, rsv3, opcode, has_mask, payload)
-            frame.validate(self.skip_utf8_validation)
-
-        return frame
-
-    def recv_strict(self, bufsize):
-        shortage = bufsize - sum(map(len, self.recv_buffer))
-        while shortage > 0:
-            # Limit buffer size that we pass to socket.recv() to avoid
-            # fragmenting the heap -- the number of bytes recv() actually
-            # reads is limited by socket buffer and is relatively small,
-            # yet passing large numbers repeatedly causes lots of large
-            # buffers allocated and then shrunk, which results in
-            # fragmentation.
-            bytes_ = self.recv(min(16384, shortage))
-            self.recv_buffer.append(bytes_)
-            shortage -= len(bytes_)
-
-        unified = bytes("", 'utf-8').join(self.recv_buffer)
-
-        if shortage == 0:
-            self.recv_buffer = []
-            return unified
-        else:
-            self.recv_buffer = [unified[bufsize:]]
-            return unified[:bufsize]
-
-
-class continuous_frame:
-
-    def __init__(self, fire_cont_frame, skip_utf8_validation):
-        self.fire_cont_frame = fire_cont_frame
-        self.skip_utf8_validation = skip_utf8_validation
-        self.cont_data = None
-        self.recving_frames = None
-
-    def validate(self, frame):
-        if not self.recving_frames and frame.opcode == ABNF.OPCODE_CONT:
-            raise WebSocketProtocolException("Illegal frame")
-        if self.recving_frames and \
-                frame.opcode in (ABNF.OPCODE_TEXT, ABNF.OPCODE_BINARY):
-            raise WebSocketProtocolException("Illegal frame")
-
-    def add(self, frame):
-        if self.cont_data:
-            self.cont_data[1] += frame.data
-        else:
-            if frame.opcode in (ABNF.OPCODE_TEXT, ABNF.OPCODE_BINARY):
-                self.recving_frames = frame.opcode
-            self.cont_data = [frame.opcode, frame.data]
-
-        if frame.fin:
-            self.recving_frames = None
-
-    def is_fire(self, frame):
-        return frame.fin or self.fire_cont_frame
-
-    def extract(self, frame):
-        data = self.cont_data
-        self.cont_data = None
-        frame.data = data[1]
-        if not self.fire_cont_frame and data[0] == ABNF.OPCODE_TEXT and not self.skip_utf8_validation and not validate_utf8(frame.data):
-            raise WebSocketPayloadException(
-                "cannot decode: " + repr(frame.data))
-
-        return [data[0], frame]
diff --git a/nls/websocket/_app.py b/nls/websocket/_app.py
deleted file mode 100644
index 1c13165..0000000
--- a/nls/websocket/_app.py
+++ /dev/null
@@ -1,423 +0,0 @@
-"""
-
-"""
-
-"""
-_app.py
-websocket - WebSocket client library for Python
-
-Copyright 2021 engn33r
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-"""
-import selectors
-import sys
-import threading
-import time
-import traceback
-from ._abnf import ABNF
-from ._core import WebSocket, getdefaulttimeout
-from ._exceptions import *
-from . import _logging
-
-
-__all__ = ["WebSocketApp"]
-
-
-class Dispatcher:
-    """
-    Dispatcher
-    """
-    def __init__(self, app, ping_timeout):
-        self.app = app
-        self.ping_timeout = ping_timeout
-
-    def read(self, sock, read_callback, check_callback):
-        while self.app.keep_running:
-            sel = selectors.DefaultSelector()
-            sel.register(self.app.sock.sock, selectors.EVENT_READ)
-
-            r = sel.select(self.ping_timeout)
-            if r:
-                if not read_callback():
-                    break
-            check_callback()
-            sel.close()
-
-
-class SSLDispatcher:
-    """
-    SSLDispatcher
-    """
-    def __init__(self, app, ping_timeout):
-        self.app = app
-        self.ping_timeout = ping_timeout
-
-    def read(self, sock, read_callback, check_callback):
-        while self.app.keep_running:
-            r = self.select()
-            if r:
-                if not read_callback():
-                    break
-            check_callback()
-
-    def select(self):
-        sock = self.app.sock.sock
-        if sock.pending():
-            return [sock,]
-
-        sel = selectors.DefaultSelector()
-        sel.register(sock, selectors.EVENT_READ)
-
-        r = sel.select(self.ping_timeout)
-        sel.close()
-
-        if len(r) > 0:
-            return r[0][0]
-
-
-class WebSocketApp:
-    """
-    Higher level of APIs are provided. The interface is like JavaScript WebSocket object.
-    """
-
-    def __init__(self, url, header=None,
-                 on_open=None, on_message=None, on_error=None,
-                 on_close=None, on_ping=None, on_pong=None,
-                 on_cont_message=None,
-                 keep_running=True, get_mask_key=None, cookie=None,
-                 subprotocols=None,
-                 on_data=None, callback_args=[]):
-        """
-        WebSocketApp initialization
-
-        Parameters
-        ----------
-        url: str
-            Websocket url.
-        header: list or dict
-            Custom header for websocket handshake.
-        on_open: function
-            Callback object which is called at opening websocket.
-            on_open has one argument.
-            The 1st argument is this class object.
-        on_message: function
-            Callback object which is called when received data.
-            on_message has 2 arguments.
-            The 1st argument is this class object.
-            The 2nd argument is utf-8 data received from the server.
-        on_error: function
-            Callback object which is called when we get error.
-            on_error has 2 arguments.
-            The 1st argument is this class object.
-            The 2nd argument is exception object.
-        on_close: function
-            Callback object which is called when connection is closed.
-            on_close has 3 arguments.
-            The 1st argument is this class object.
-            The 2nd argument is close_status_code.
-            The 3rd argument is close_msg.
-        on_cont_message: function
-            Callback object which is called when a continuation
-            frame is received.
-            on_cont_message has 3 arguments.
-            The 1st argument is this class object.
-            The 2nd argument is utf-8 string which we get from the server.
-            The 3rd argument is continue flag. if 0, the data continue
-            to next frame data
-        on_data: function
-            Callback object which is called when a message received.
-            This is called before on_message or on_cont_message,
-            and then on_message or on_cont_message is called.
-            on_data has 4 argument.
-            The 1st argument is this class object.
-            The 2nd argument is utf-8 string which we get from the server.
-            The 3rd argument is data type. ABNF.OPCODE_TEXT or ABNF.OPCODE_BINARY will be came.
-            The 4th argument is continue flag. If 0, the data continue
-        keep_running: bool
-            This parameter is obsolete and ignored.
-        get_mask_key: function
-            A callable function to get new mask keys, see the
-            WebSocket.set_mask_key's docstring for more information.
-        cookie: str
-            Cookie value.
-        subprotocols: list
-            List of available sub protocols. Default is None.
-        """
-        self.url = url
-        self.header = header if header is not None else []
-        self.cookie = cookie
-
-        self.on_open = on_open
-        self.on_message = on_message
-        self.on_data = on_data
-        self.on_error = on_error
-        self.on_close = on_close
-        self.on_ping = on_ping
-        self.on_pong = on_pong
-        self.on_cont_message = on_cont_message
-        self.keep_running = False
-        self.get_mask_key = get_mask_key
-        self.sock = None
-        self.last_ping_tm = 0
-        self.last_pong_tm = 0
-        self.subprotocols = subprotocols
-        self.callback_args = callback_args
-
-    def update_args(self, *args):
-        self.callback_args = args
-        #print(self.callback_args)
-
-    def send(self, data, opcode=ABNF.OPCODE_TEXT):
-        """
-        send message
-
-        Parameters
-        ----------
-        data: str
-            Message to send. If you set opcode to OPCODE_TEXT,
-            data must be utf-8 string or unicode.
-        opcode: int
-            Operation code of data. Default is OPCODE_TEXT.
-        """
-
-        if not self.sock or self.sock.send(data, opcode) == 0:
-            raise WebSocketConnectionClosedException(
-                "Connection is already closed.")
-
-    def close(self, **kwargs):
-        """
-        Close websocket connection.
-        """
-        self.keep_running = False
-        if self.sock:
-            self.sock.close(**kwargs)
-            self.sock = None
-
-    def _send_ping(self, interval, event, payload):
-        while not event.wait(interval):
-            self.last_ping_tm = time.time()
-            if self.sock:
-                try:
-                    self.sock.ping(payload)
-                except Exception as ex:
-                    _logging.warning("send_ping routine terminated: {}".format(ex))
-                    break
-
-    def run_forever(self, sockopt=None, sslopt=None,
-                    ping_interval=0, ping_timeout=None,
-                    ping_payload="",
-                    http_proxy_host=None, http_proxy_port=None,
-                    http_no_proxy=None, http_proxy_auth=None,
-                    skip_utf8_validation=False,
-                    host=None, origin=None, dispatcher=None,
-                    suppress_origin=False, proxy_type=None):
-        """
-        Run event loop for WebSocket framework.
-
-        This loop is an infinite loop and is alive while websocket is available.
-
-        Parameters
-        ----------
-        sockopt: tuple
-            Values for socket.setsockopt.
-            sockopt must be tuple
-            and each element is argument of sock.setsockopt.
-        sslopt: dict
-            Optional dict object for ssl socket option.
-        ping_interval: int or float
-            Automatically send "ping" command
-            every specified period (in seconds).
-            If set to 0, no ping is sent periodically.
-        ping_timeout: int or float
-            Timeout (in seconds) if the pong message is not received.
-        ping_payload: str
-            Payload message to send with each ping.
-        http_proxy_host: str
-            HTTP proxy host name.
-        http_proxy_port: int or str
-            HTTP proxy port. If not set, set to 80.
-        http_no_proxy: list
-            Whitelisted host names that don't use the proxy.
-        skip_utf8_validation: bool
-            skip utf8 validation.
-        host: str
-            update host header.
-        origin: str
-            update origin header.
-        dispatcher: Dispatcher object
-            customize reading data from socket.
-        suppress_origin: bool
-            suppress outputting origin header.
-
-        Returns
-        -------
-        teardown: bool
-            False if caught KeyboardInterrupt, True if other exception was raised during a loop
-        """
-
-        if ping_timeout is not None and ping_timeout <= 0:
-            raise WebSocketException("Ensure ping_timeout > 0")
-        if ping_interval is not None and ping_interval < 0:
-            raise WebSocketException("Ensure ping_interval >= 0")
-        if ping_timeout and ping_interval and ping_interval <= ping_timeout:
-            raise WebSocketException("Ensure ping_interval > ping_timeout")
-        if not sockopt:
-            sockopt = []
-        if not sslopt:
-            sslopt = {}
-        if self.sock:
-            raise WebSocketException("socket is already opened")
-        thread = None
-        self.keep_running = True
-        self.last_ping_tm = 0
-        self.last_pong_tm = 0
-
-        def teardown(close_frame=None):
-            """
-            Tears down the connection.
-
-            Parameters
-            ----------
-            close_frame: ABNF frame
-                If close_frame is set, the on_close handler is invoked
-                with the statusCode and reason from the provided frame.
-            """
-
-            if thread and thread.is_alive():
-                event.set()
-                thread.join()
-            self.keep_running = False
-            if self.sock:
-                self.sock.close()
-            close_status_code, close_reason = self._get_close_args(
-                close_frame if close_frame else None)
-            self.sock = None
-
-            # Finally call the callback AFTER all teardown is complete
-            self._callback(self.on_close, close_status_code, close_reason,
-                    self.callback_args)
-
-        try:
-            self.sock = WebSocket(
-                self.get_mask_key, sockopt=sockopt, sslopt=sslopt,
-                fire_cont_frame=self.on_cont_message is not None,
-                skip_utf8_validation=skip_utf8_validation,
-                enable_multithread=True)
-            self.sock.settimeout(getdefaulttimeout())
-            self.sock.connect(
-                self.url, header=self.header, cookie=self.cookie,
-                http_proxy_host=http_proxy_host,
-                http_proxy_port=http_proxy_port, http_no_proxy=http_no_proxy,
-                http_proxy_auth=http_proxy_auth, subprotocols=self.subprotocols,
-                host=host, origin=origin, suppress_origin=suppress_origin,
-                proxy_type=proxy_type)
-            if not dispatcher:
-                dispatcher = self.create_dispatcher(ping_timeout)
-
-            self._callback(self.on_open, self.callback_args)
-
-            if ping_interval:
-                event = threading.Event()
-                thread = threading.Thread(
-                    target=self._send_ping, args=(ping_interval, event, ping_payload))
-                thread.daemon = True
-                thread.start()
-
-            def read():
-                if not self.keep_running:
-                    return teardown()
-
-                op_code, frame = self.sock.recv_data_frame(True)
-                if op_code == ABNF.OPCODE_CLOSE:
-                    return teardown(frame)
-                elif op_code == ABNF.OPCODE_PING:
-                    self._callback(self.on_ping, frame.data, self.callback_args)
-                elif op_code == ABNF.OPCODE_PONG:
-                    self.last_pong_tm = time.time()
-                    self._callback(self.on_pong, frame.data, self.callback_args)
-                elif op_code == ABNF.OPCODE_CONT and self.on_cont_message:
-                    self._callback(self.on_data, frame.data,
-                                   frame.opcode, frame.fin, self.callback_args)
-                    self._callback(self.on_cont_message,
-                                   frame.data, frame.fin, self.callback_args)
-                else:
-                    data = frame.data
-                    if op_code == ABNF.OPCODE_TEXT:
-                        data = data.decode("utf-8")
-                        self._callback(self.on_message, data, self.callback_args)
-                    else:
-                        self._callback(self.on_data, data, frame.opcode, True,
-                            self.callback_args)
-
-                return True
-
-            def check():
-                if (ping_timeout):
-                    has_timeout_expired = time.time() - self.last_ping_tm > ping_timeout
-                    has_pong_not_arrived_after_last_ping = self.last_pong_tm - self.last_ping_tm < 0
-                    has_pong_arrived_too_late = self.last_pong_tm - self.last_ping_tm > ping_timeout
-
-                    if (self.last_ping_tm and
-                            has_timeout_expired and
-                            (has_pong_not_arrived_after_last_ping or has_pong_arrived_too_late)):
-                        raise WebSocketTimeoutException("ping/pong timed out")
-                return True
-
-            dispatcher.read(self.sock.sock, read, check)
-        except (Exception, KeyboardInterrupt, SystemExit) as e:
-            self._callback(self.on_error, e, self.callback_args)
-            if isinstance(e, SystemExit):
-                # propagate SystemExit further
-                raise
-            teardown()
-            return not isinstance(e, KeyboardInterrupt)
-        else:
-            teardown()
-            return True
-
-    def create_dispatcher(self, ping_timeout):
-        timeout = ping_timeout or 10
-        if self.sock.is_ssl():
-            return SSLDispatcher(self, timeout)
-
-        return Dispatcher(self, timeout)
-
-    def _get_close_args(self, close_frame):
-        """
-        _get_close_args extracts the close code and reason from the close body
-        if it exists (RFC6455 says WebSocket Connection Close Code is optional)
-        """
-        # Need to catch the case where close_frame is None
-        # Otherwise the following if statement causes an error
-        if not self.on_close or not close_frame:
-            return [None, None]
-
-        # Extract close frame status code
-        if close_frame.data and len(close_frame.data) >= 2:
-            close_status_code = 256 * close_frame.data[0] + close_frame.data[1]
-            reason = close_frame.data[2:].decode('utf-8')
-            return [close_status_code, reason]
-        else:
-            # Most likely reached this because len(close_frame_data.data) < 2
-            return [None, None]
-
-    def _callback(self, callback, *args):
-        if callback:
-            try:
-                callback(self, *args)
-
-            except Exception as e:
-                _logging.error("error from callback {}: {}".format(callback, e))
-                if self.on_error:
-                    self.on_error(self, e)
diff --git a/nls/websocket/_cookiejar.py b/nls/websocket/_cookiejar.py
deleted file mode 100644
index 8785383..0000000
--- a/nls/websocket/_cookiejar.py
+++ /dev/null
@@ -1,67 +0,0 @@
-"""
-
-"""
-
-"""
-_cookiejar.py
-websocket - WebSocket client library for Python
-
-Copyright 2021 engn33r
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-"""
-import http.cookies
-
-
-class SimpleCookieJar:
-    def __init__(self):
-        self.jar = dict()
-
-    def add(self, set_cookie):
-        if set_cookie:
-            simpleCookie = http.cookies.SimpleCookie(set_cookie)
-
-            for k, v in simpleCookie.items():
-                domain = v.get("domain")
-                if domain:
-                    if not domain.startswith("."):
-                        domain = "." + domain
-                    cookie = self.jar.get(domain) if self.jar.get(domain) else http.cookies.SimpleCookie()
-                    cookie.update(simpleCookie)
-                    self.jar[domain.lower()] = cookie
-
-    def set(self, set_cookie):
-        if set_cookie:
-            simpleCookie = http.cookies.SimpleCookie(set_cookie)
-
-            for k, v in simpleCookie.items():
-                domain = v.get("domain")
-                if domain:
-                    if not domain.startswith("."):
-                        domain = "." + domain
-                    self.jar[domain.lower()] = simpleCookie
-
-    def get(self, host):
-        if not host:
-            return ""
-
-        cookies = []
-        for domain, simpleCookie in self.jar.items():
-            host = host.lower()
-            if host.endswith(domain) or host == domain[1:]:
-                cookies.append(self.jar.get(domain))
-
-        return "; ".join(filter(
-            None, sorted(
-                ["%s=%s" % (k, v.value) for cookie in filter(None, cookies) for k, v in cookie.items()]
-            )))
diff --git a/nls/websocket/_core.py b/nls/websocket/_core.py
deleted file mode 100644
index 0bfb37c..0000000
--- a/nls/websocket/_core.py
+++ /dev/null
@@ -1,607 +0,0 @@
-"""
-_core.py
-====================================
-WebSocket Python client
-"""
-
-"""
-_core.py
-websocket - WebSocket client library for Python
-
-Copyright 2021 engn33r
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-"""
-import socket
-import struct
-import threading
-import time
-
-# websocket modules
-from ._abnf import *
-from ._exceptions import *
-from ._handshake import *
-from ._http import *
-from ._logging import *
-from ._socket import *
-from ._ssl_compat import *
-from ._utils import *
-
-__all__ = ['WebSocket', 'create_connection']
-
-
-class WebSocket:
-    """
-    Low level WebSocket interface.
-
-    This class is based on the WebSocket protocol `draft-hixie-thewebsocketprotocol-76 <http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-76>`_
-
-    We can connect to the websocket server and send/receive data.
-    The following example is an echo client.
-
-    >>> import websocket
-    >>> ws = websocket.WebSocket()
-    >>> ws.connect("ws://echo.websocket.org")
-    >>> ws.send("Hello, Server")
-    >>> ws.recv()
-    'Hello, Server'
-    >>> ws.close()
-
-    Parameters
-    ----------
-    get_mask_key: func
-        A callable function to get new mask keys, see the
-        WebSocket.set_mask_key's docstring for more information.
-    sockopt: tuple
-        Values for socket.setsockopt.
-        sockopt must be tuple and each element is argument of sock.setsockopt.
-    sslopt: dict
-        Optional dict object for ssl socket options. See FAQ for details.
-    fire_cont_frame: bool
-        Fire recv event for each cont frame. Default is False.
-    enable_multithread: bool
-        If set to True, lock send method.
-    skip_utf8_validation: bool
-        Skip utf8 validation.
-    """
-
-    def __init__(self, get_mask_key=None, sockopt=None, sslopt=None,
-                 fire_cont_frame=False, enable_multithread=True,
-                 skip_utf8_validation=False, **_):
-        """
-        Initialize WebSocket object.
-
-        Parameters
-        ----------
-        sslopt: dict
-            Optional dict object for ssl socket options. See FAQ for details.
-        """
-        self.sock_opt = sock_opt(sockopt, sslopt)
-        self.handshake_response = None
-        self.sock = None
-
-        self.connected = False
-        self.get_mask_key = get_mask_key
-        # These buffer over the build-up of a single frame.
-        self.frame_buffer = frame_buffer(self._recv, skip_utf8_validation)
-        self.cont_frame = continuous_frame(
-            fire_cont_frame, skip_utf8_validation)
-
-        if enable_multithread:
-            self.lock = threading.Lock()
-            self.readlock = threading.Lock()
-        else:
-            self.lock = NoLock()
-            self.readlock = NoLock()
-
-    def __iter__(self):
-        """
-        Allow iteration over websocket, implying sequential `recv` executions.
-        """
-        while True:
-            yield self.recv()
-
-    def __next__(self):
-        return self.recv()
-
-    def next(self):
-        return self.__next__()
-
-    def fileno(self):
-        return self.sock.fileno()
-
-    def set_mask_key(self, func):
-        """
-        Set function to create mask key. You can customize mask key generator.
-        Mainly, this is for testing purpose.
-
-        Parameters
-        ----------
-        func: func
-            callable object. the func takes 1 argument as integer.
-            The argument means length of mask key.
-            This func must return string(byte array),
-            which length is argument specified.
-        """
-        self.get_mask_key = func
-
-    def gettimeout(self):
-        """
-        Get the websocket timeout (in seconds) as an int or float
-
-        Returns
-        ----------
-        timeout: int or float
-             returns timeout value (in seconds). This value could be either float/integer.
-        """
-        return self.sock_opt.timeout
-
-    def settimeout(self, timeout):
-        """
-        Set the timeout to the websocket.
-
-        Parameters
-        ----------
-        timeout: int or float
-            timeout time (in seconds). This value could be either float/integer.
-        """
-        self.sock_opt.timeout = timeout
-        if self.sock:
-            self.sock.settimeout(timeout)
-
-    timeout = property(gettimeout, settimeout)
-
-    def getsubprotocol(self):
-        """
-        Get subprotocol
-        """
-        if self.handshake_response:
-            return self.handshake_response.subprotocol
-        else:
-            return None
-
-    subprotocol = property(getsubprotocol)
-
-    def getstatus(self):
-        """
-        Get handshake status
-        """
-        if self.handshake_response:
-            return self.handshake_response.status
-        else:
-            return None
-
-    status = property(getstatus)
-
-    def getheaders(self):
-        """
-        Get handshake response header
-        """
-        if self.handshake_response:
-            return self.handshake_response.headers
-        else:
-            return None
-
-    def is_ssl(self):
-        try:
-            return isinstance(self.sock, ssl.SSLSocket)
-        except:
-            return False
-
-    headers = property(getheaders)
-
-    def connect(self, url, **options):
-        """
-        Connect to url. url is websocket url scheme.
-        ie. ws://host:port/resource
-        You can customize using 'options'.
-        If you set "header" list object, you can set your own custom header.
-
-        >>> ws = WebSocket()
-        >>> ws.connect("ws://echo.websocket.org/",
-                ...     header=["User-Agent: MyProgram",
-                ...             "x-custom: header"])
-
-        Parameters
-        ----------
-        header: list or dict
-            Custom http header list or dict.
-        cookie: str
-            Cookie value.
-        origin: str
-            Custom origin url.
-        connection: str
-            Custom connection header value.
-            Default value "Upgrade" set in _handshake.py
-        suppress_origin: bool
-            Suppress outputting origin header.
-        host: str
-            Custom host header string.
-        timeout: int or float
-            Socket timeout time. This value is an integer or float.
-            If you set None for this value, it means "use default_timeout value"
-        http_proxy_host: str
-            HTTP proxy host name.
-        http_proxy_port: str or int
-            HTTP proxy port. Default is 80.
-        http_no_proxy: list
-            Whitelisted host names that don't use the proxy.
-        http_proxy_auth: tuple
-            HTTP proxy auth information. Tuple of username and password. Default is None.
-        redirect_limit: int
-            Number of redirects to follow.
-        subprotocols: list
-            List of available subprotocols. Default is None.
-        socket: socket
-            Pre-initialized stream socket.
-        """
-        self.sock_opt.timeout = options.get('timeout', self.sock_opt.timeout)
-        self.sock, addrs = connect(url, self.sock_opt, proxy_info(**options),
-                                   options.pop('socket', None))
-
-        try:
-            self.handshake_response = handshake(self.sock, *addrs, **options)
-            for attempt in range(options.pop('redirect_limit', 3)):
-                if self.handshake_response.status in SUPPORTED_REDIRECT_STATUSES:
-                    url = self.handshake_response.headers['location']
-                    self.sock.close()
-                    self.sock, addrs = connect(url, self.sock_opt, proxy_info(**options),
-                                               options.pop('socket', None))
-                    self.handshake_response = handshake(self.sock, *addrs, **options)
-            self.connected = True
-        except:
-            if self.sock:
-                self.sock.close()
-                self.sock = None
-            raise
-
-    def send(self, payload, opcode=ABNF.OPCODE_TEXT):
-        """
-        Send the data as string.
-
-        Parameters
-        ----------
-        payload: str
-            Payload must be utf-8 string or unicode,
-            If the opcode is OPCODE_TEXT.
-            Otherwise, it must be string(byte array).
-        opcode: int
-            Operation code (opcode) to send.
-        """
-        
-        frame = ABNF.create_frame(payload, opcode)
-        return self.send_frame(frame)
-
-    def send_frame(self, frame):
-        """
-        Send the data frame.
-
-        >>> ws = create_connection("ws://echo.websocket.org/")
-        >>> frame = ABNF.create_frame("Hello", ABNF.OPCODE_TEXT)
-        >>> ws.send_frame(frame)
-        >>> cont_frame = ABNF.create_frame("My name is ", ABNF.OPCODE_CONT, 0)
-        >>> ws.send_frame(frame)
-        >>> cont_frame = ABNF.create_frame("Foo Bar", ABNF.OPCODE_CONT, 1)
-        >>> ws.send_frame(frame)
-
-        Parameters
-        ----------
-        frame: ABNF frame
-            frame data created by ABNF.create_frame
-        """
-        if self.get_mask_key:
-            frame.get_mask_key = self.get_mask_key
-        data = frame.format()
-        length = len(data)
-        #if (isEnabledForTrace() and f):
-            #trace("++Sent raw: " + repr(data))
-            #trace("++Sent decoded: " + frame.__str__())
-        with self.lock:
-            while data:
-                l = self._send(data)
-                data = data[l:]
-
-        return length
-
-    def send_binary(self, payload):
-        """
-        Send a binary message (OPCODE_BINARY).
-
-        Parameters
-        ----------
-        payload: bytes
-            payload of message to send.
-        """
-        return self.send(payload, ABNF.OPCODE_BINARY)
-
-    def ping(self, payload=""):
-        """
-        Send ping data.
-
-        Parameters
-        ----------
-        payload: str
-            data payload to send server.
-        """
-        if isinstance(payload, str):
-            payload = payload.encode("utf-8")
-        self.send(payload, ABNF.OPCODE_PING)
-
-    def pong(self, payload=""):
-        """
-        Send pong data.
-
-        Parameters
-        ----------
-        payload: str
-            data payload to send server.
-        """
-        if isinstance(payload, str):
-            payload = payload.encode("utf-8")
-        self.send(payload, ABNF.OPCODE_PONG)
-
-    def recv(self):
-        """
-        Receive string data(byte array) from the server.
-
-        Returns
-        ----------
-        data: string (byte array) value.
-        """
-        with self.readlock:
-            opcode, data = self.recv_data()
-        if opcode == ABNF.OPCODE_TEXT:
-            return data.decode("utf-8")
-        elif opcode == ABNF.OPCODE_TEXT or opcode == ABNF.OPCODE_BINARY:
-            return data
-        else:
-            return ''
-
-    def recv_data(self, control_frame=False):
-        """
-        Receive data with operation code.
-
-        Parameters
-        ----------
-        control_frame: bool
-            a boolean flag indicating whether to return control frame
-            data, defaults to False
-
-        Returns
-        -------
-        opcode, frame.data: tuple
-            tuple of operation code and string(byte array) value.
-        """
-        opcode, frame = self.recv_data_frame(control_frame)
-        return opcode, frame.data
-
-    def recv_data_frame(self, control_frame=False):
-        """
-        Receive data with operation code.
-
-        If a valid ping message is received, a pong response is sent.
-
-        Parameters
-        ----------
-        control_frame: bool
-            a boolean flag indicating whether to return control frame
-            data, defaults to False
-
-        Returns
-        -------
-        frame.opcode, frame: tuple
-            tuple of operation code and string(byte array) value.
-        """
-        while True:
-            frame = self.recv_frame()
-            #if (isEnabledForTrace()):
-                #trace("++Rcv raw: " + repr(frame.format()))
-                #trace("++Rcv decoded: " + frame.__str__())
-            if not frame:
-                # handle error:
-                # 'NoneType' object has no attribute 'opcode'
-                raise WebSocketProtocolException(
-                    "Not a valid frame %s" % frame)
-            elif frame.opcode in (ABNF.OPCODE_TEXT, ABNF.OPCODE_BINARY, ABNF.OPCODE_CONT):
-                self.cont_frame.validate(frame)
-                self.cont_frame.add(frame)
-
-                if self.cont_frame.is_fire(frame):
-                    return self.cont_frame.extract(frame)
-
-            elif frame.opcode == ABNF.OPCODE_CLOSE:
-                self.send_close()
-                return frame.opcode, frame
-            elif frame.opcode == ABNF.OPCODE_PING:
-                if len(frame.data) < 126:
-                    self.pong(frame.data)
-                else:
-                    raise WebSocketProtocolException(
-                        "Ping message is too long")
-                if control_frame:
-                    return frame.opcode, frame
-            elif frame.opcode == ABNF.OPCODE_PONG:
-                if control_frame:
-                    return frame.opcode, frame
-
-    def recv_frame(self):
-        """
-        Receive data as frame from server.
-
-        Returns
-        -------
-        self.frame_buffer.recv_frame(): ABNF frame object
-        """
-        return self.frame_buffer.recv_frame()
-
-    def send_close(self, status=STATUS_NORMAL, reason=bytes('', encoding='utf-8')):
-        """
-        Send close data to the server.
-
-        Parameters
-        ----------
-        status: int
-            Status code to send. See STATUS_XXX.
-        reason: str or bytes
-            The reason to close. This must be string or bytes.
-        """
-        if status < 0 or status >= ABNF.LENGTH_16:
-            raise ValueError("code is invalid range")
-        self.connected = False
-        self.send(struct.pack('!H', status) + reason, ABNF.OPCODE_CLOSE)
-
-    def close(self, status=STATUS_NORMAL, reason=bytes('', encoding='utf-8'), timeout=3):
-        """
-        Close Websocket object
-
-        Parameters
-        ----------
-        status: int
-            Status code to send. See STATUS_XXX.
-        reason: bytes
-            The reason to close.
-        timeout: int or float
-            Timeout until receive a close frame.
-            If None, it will wait forever until receive a close frame.
-        """
-        if self.connected:
-            if status < 0 or status >= ABNF.LENGTH_16:
-                raise ValueError("code is invalid range")
-
-            try:
-                self.connected = False
-                self.send(struct.pack('!H', status) + reason, ABNF.OPCODE_CLOSE)
-                sock_timeout = self.sock.gettimeout()
-                self.sock.settimeout(timeout)
-                start_time = time.time()
-                while timeout is None or time.time() - start_time < timeout:
-                    try:
-                        frame = self.recv_frame()
-                        if frame.opcode != ABNF.OPCODE_CLOSE:
-                            continue
-                        if isEnabledForError():
-                            recv_status = struct.unpack("!H", frame.data[0:2])[0]
-                            if recv_status >= 3000 and recv_status <= 4999:
-                                debug("close status: " + repr(recv_status))
-                            elif recv_status != STATUS_NORMAL:
-                                error("close status: " + repr(recv_status))
-                        break
-                    except:
-                        break
-                self.sock.settimeout(sock_timeout)
-                self.sock.shutdown(socket.SHUT_RDWR)
-            except:
-                pass
-
-            self.shutdown()
-
-    def abort(self):
-        """
-        Low-level asynchronous abort, wakes up other threads that are waiting in recv_*
-        """
-        if self.connected:
-            self.sock.shutdown(socket.SHUT_RDWR)
-
-    def shutdown(self):
-        """
-        close socket, immediately.
-        """
-        if self.sock:
-            self.sock.close()
-            self.sock = None
-            self.connected = False
-
-    def _send(self, data):
-        return send(self.sock, data)
-
-    def _recv(self, bufsize):
-        try:
-            return recv(self.sock, bufsize)
-        except WebSocketConnectionClosedException:
-            if self.sock:
-                self.sock.close()
-            self.sock = None
-            self.connected = False
-            raise
-
-
-def create_connection(url, timeout=None, class_=WebSocket, **options):
-    """
-    Connect to url and return websocket object.
-
-    Connect to url and return the WebSocket object.
-    Passing optional timeout parameter will set the timeout on the socket.
-    If no timeout is supplied,
-    the global default timeout setting returned by getdefaulttimeout() is used.
-    You can customize using 'options'.
-    If you set "header" list object, you can set your own custom header.
-
-    >>> conn = create_connection("ws://echo.websocket.org/",
-         ...     header=["User-Agent: MyProgram",
-         ...             "x-custom: header"])
-
-    Parameters
-    ----------
-    class_: class
-        class to instantiate when creating the connection. It has to implement
-        settimeout and connect. It's __init__ should be compatible with
-        WebSocket.__init__, i.e. accept all of it's kwargs.
-    header: list or dict
-        custom http header list or dict.
-    cookie: str
-        Cookie value.
-    origin: str
-        custom origin url.
-    suppress_origin: bool
-        suppress outputting origin header.
-    host: str
-        custom host header string.
-    timeout: int or float
-        socket timeout time. This value could be either float/integer.
-        If set to None, it uses the default_timeout value.
-    http_proxy_host: str
-        HTTP proxy host name.
-    http_proxy_port: str or int
-        HTTP proxy port. If not set, set to 80.
-    http_no_proxy: list
-        Whitelisted host names that don't use the proxy.
-    http_proxy_auth: tuple
-        HTTP proxy auth information. tuple of username and password. Default is None.
-    enable_multithread: bool
-        Enable lock for multithread.
-    redirect_limit: int
-        Number of redirects to follow.
-    sockopt: tuple
-        Values for socket.setsockopt.
-        sockopt must be a tuple and each element is an argument of sock.setsockopt.
-    sslopt: dict
-        Optional dict object for ssl socket options. See FAQ for details.
-    subprotocols: list
-        List of available subprotocols. Default is None.
-    skip_utf8_validation: bool
-        Skip utf8 validation.
-    socket: socket
-        Pre-initialized stream socket.
-    """
-    sockopt = options.pop("sockopt", [])
-    sslopt = options.pop("sslopt", {})
-    fire_cont_frame = options.pop("fire_cont_frame", False)
-    enable_multithread = options.pop("enable_multithread", True)
-    skip_utf8_validation = options.pop("skip_utf8_validation", False)
-    websock = class_(sockopt=sockopt, sslopt=sslopt,
-                     fire_cont_frame=fire_cont_frame,
-                     enable_multithread=enable_multithread,
-                     skip_utf8_validation=skip_utf8_validation, **options)
-    websock.settimeout(timeout if timeout is not None else getdefaulttimeout())
-    websock.connect(url, **options)
-    return websock
diff --git a/nls/websocket/_exceptions.py b/nls/websocket/_exceptions.py
deleted file mode 100644
index b92b1f4..0000000
--- a/nls/websocket/_exceptions.py
+++ /dev/null
@@ -1,84 +0,0 @@
-"""
-Define WebSocket exceptions
-"""
-
-"""
-_exceptions.py
-websocket - WebSocket client library for Python
-
-Copyright 2021 engn33r
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-"""
-
-
-class WebSocketException(Exception):
-    """
-    WebSocket exception class.
-    """
-    pass
-
-
-class WebSocketProtocolException(WebSocketException):
-    """
-    If the WebSocket protocol is invalid, this exception will be raised.
-    """
-    pass
-
-
-class WebSocketPayloadException(WebSocketException):
-    """
-    If the WebSocket payload is invalid, this exception will be raised.
-    """
-    pass
-
-
-class WebSocketConnectionClosedException(WebSocketException):
-    """
-    If remote host closed the connection or some network error happened,
-    this exception will be raised.
-    """
-    pass
-
-
-class WebSocketTimeoutException(WebSocketException):
-    """
-    WebSocketTimeoutException will be raised at socket timeout during read/write data.
-    """
-    pass
-
-
-class WebSocketProxyException(WebSocketException):
-    """
-    WebSocketProxyException will be raised when proxy error occurred.
-    """
-    pass
-
-
-class WebSocketBadStatusException(WebSocketException):
-    """
-    WebSocketBadStatusException will be raised when we get bad handshake status code.
-    """
-
-    def __init__(self, message, status_code, status_message=None, resp_headers=None):
-        msg = message % (status_code, status_message)
-        super().__init__(msg)
-        self.status_code = status_code
-        self.resp_headers = resp_headers
-
-
-class WebSocketAddressException(WebSocketException):
-    """
-    If the websocket address info cannot be found, this exception will be raised.
-    """
-    pass
diff --git a/nls/websocket/_handshake.py b/nls/websocket/_handshake.py
deleted file mode 100644
index b2d7dfc..0000000
--- a/nls/websocket/_handshake.py
+++ /dev/null
@@ -1,191 +0,0 @@
-"""
-_handshake.py
-websocket - WebSocket client library for Python
-
-Copyright 2021 engn33r
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-"""
-import hashlib
-import hmac
-import os
-from base64 import encodebytes as base64encode
-from http import client as HTTPStatus
-from ._cookiejar import SimpleCookieJar
-from ._exceptions import *
-from ._http import *
-from ._logging import *
-from ._socket import *
-
-__all__ = ["handshake_response", "handshake", "SUPPORTED_REDIRECT_STATUSES"]
-
-# websocket supported version.
-VERSION = 13
-
-SUPPORTED_REDIRECT_STATUSES = (HTTPStatus.MOVED_PERMANENTLY, HTTPStatus.FOUND, HTTPStatus.SEE_OTHER,)
-SUCCESS_STATUSES = SUPPORTED_REDIRECT_STATUSES + (HTTPStatus.SWITCHING_PROTOCOLS,)
-
-CookieJar = SimpleCookieJar()
-
-
-class handshake_response:
-
-    def __init__(self, status, headers, subprotocol):
-        self.status = status
-        self.headers = headers
-        self.subprotocol = subprotocol
-        CookieJar.add(headers.get("set-cookie"))
-
-
-def handshake(sock, hostname, port, resource, **options):
-    headers, key = _get_handshake_headers(resource, hostname, port, options)
-
-    header_str = "\r\n".join(headers)
-    send(sock, header_str)
-    dump("request header", header_str)
-    #print("request header:", header_str)
-
-    status, resp = _get_resp_headers(sock)
-    if status in SUPPORTED_REDIRECT_STATUSES:
-        return handshake_response(status, resp, None)
-    success, subproto = _validate(resp, key, options.get("subprotocols"))
-    if not success:
-        raise WebSocketException("Invalid WebSocket Header")
-
-    return handshake_response(status, resp, subproto)
-
-
-def _pack_hostname(hostname):
-    # IPv6 address
-    if ':' in hostname:
-        return '[' + hostname + ']'
-
-    return hostname
-
-
-def _get_handshake_headers(resource, host, port, options):
-    headers = [
-        "GET %s HTTP/1.1" % resource,
-        "Upgrade: websocket"
-    ]
-    if port == 80 or port == 443:
-        hostport = _pack_hostname(host)
-    else:
-        hostport = "%s:%d" % (_pack_hostname(host), port)
-    if "host" in options and options["host"] is not None:
-        headers.append("Host: %s" % options["host"])
-    else:
-        headers.append("Host: %s" % hostport)
-
-    if "suppress_origin" not in options or not options["suppress_origin"]:
-        if "origin" in options and options["origin"] is not None:
-            headers.append("Origin: %s" % options["origin"])
-        else:
-            headers.append("Origin: http://%s" % hostport)
-
-    key = _create_sec_websocket_key()
-
-    # Append Sec-WebSocket-Key & Sec-WebSocket-Version if not manually specified
-    if 'header' not in options or 'Sec-WebSocket-Key' not in options['header']:
-        key = _create_sec_websocket_key()
-        headers.append("Sec-WebSocket-Key: %s" % key)
-    else:
-        key = options['header']['Sec-WebSocket-Key']
-
-    if 'header' not in options or 'Sec-WebSocket-Version' not in options['header']:
-        headers.append("Sec-WebSocket-Version: %s" % VERSION)
-
-    if 'connection' not in options or options['connection'] is None:
-        headers.append('Connection: Upgrade')
-    else:
-        headers.append(options['connection'])
-
-    subprotocols = options.get("subprotocols")
-    if subprotocols:
-        headers.append("Sec-WebSocket-Protocol: %s" % ",".join(subprotocols))
-
-    if "header" in options:
-        header = options["header"]
-        if isinstance(header, dict):
-            header = [
-                ": ".join([k, v])
-                for k, v in header.items()
-                if v is not None
-            ]
-        headers.extend(header)
-
-    server_cookie = CookieJar.get(host)
-    client_cookie = options.get("cookie", None)
-
-    cookie = "; ".join(filter(None, [server_cookie, client_cookie]))
-
-    if cookie:
-        headers.append("Cookie: %s" % cookie)
-
-    headers.append("")
-    headers.append("")
-
-    return headers, key
-
-
-def _get_resp_headers(sock, success_statuses=SUCCESS_STATUSES):
-    status, resp_headers, status_message = read_headers(sock)
-    if status not in success_statuses:
-        raise WebSocketBadStatusException("Handshake status %d %s", status, status_message, resp_headers)
-    return status, resp_headers
-
-
-_HEADERS_TO_CHECK = {
-    "upgrade": "websocket",
-    "connection": "upgrade",
-}
-
-
-def _validate(headers, key, subprotocols):
-    subproto = None
-    for k, v in _HEADERS_TO_CHECK.items():
-        r = headers.get(k, None)
-        if not r:
-            return False, None
-        r = [x.strip().lower() for x in r.split(',')]
-        if v not in r:
-            return False, None
-
-    if subprotocols:
-        subproto = headers.get("sec-websocket-protocol", None)
-        if not subproto or subproto.lower() not in [s.lower() for s in subprotocols]:
-            error("Invalid subprotocol: " + str(subprotocols))
-            return False, None
-        subproto = subproto.lower()
-
-    result = headers.get("sec-websocket-accept", None)
-    if not result:
-        return False, None
-    result = result.lower()
-
-    if isinstance(result, str):
-        result = result.encode('utf-8')
-
-    value = (key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11").encode('utf-8')
-    hashed = base64encode(hashlib.sha1(value).digest()).strip().lower()
-    success = hmac.compare_digest(hashed, result)
-
-    if success:
-        return True, subproto
-    else:
-        return False, None
-
-
-def _create_sec_websocket_key():
-    randomness = os.urandom(16)
-    return base64encode(randomness).decode('utf-8').strip()
diff --git a/nls/websocket/_http.py b/nls/websocket/_http.py
deleted file mode 100644
index 1996a99..0000000
--- a/nls/websocket/_http.py
+++ /dev/null
@@ -1,335 +0,0 @@
-"""
-_http.py
-websocket - WebSocket client library for Python
-
-Copyright 2021 engn33r
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-"""
-import errno
-import os
-import socket
-import sys
-
-from ._exceptions import *
-from ._logging import *
-from ._socket import*
-from ._ssl_compat import *
-from ._url import *
-
-from base64 import encodebytes as base64encode
-
-__all__ = ["proxy_info", "connect", "read_headers"]
-
-try:
-    from python_socks.sync import Proxy
-    from python_socks._errors import *
-    from python_socks._types import ProxyType
-    HAVE_PYTHON_SOCKS = True
-except:
-    HAVE_PYTHON_SOCKS = False
-
-    class ProxyError(Exception):
-        pass
-
-    class ProxyTimeoutError(Exception):
-        pass
-
-    class ProxyConnectionError(Exception):
-        pass
-
-
-class proxy_info:
-
-    def __init__(self, **options):
-        self.proxy_host = options.get("http_proxy_host", None)
-        if self.proxy_host:
-            self.proxy_port = options.get("http_proxy_port", 0)
-            self.auth = options.get("http_proxy_auth", None)
-            self.no_proxy = options.get("http_no_proxy", None)
-            self.proxy_protocol = options.get("proxy_type", "http")
-            # Note: If timeout not specified, default python-socks timeout is 60 seconds
-            self.proxy_timeout = options.get("timeout", None)
-            if self.proxy_protocol not in ['http', 'socks4', 'socks4a', 'socks5', 'socks5h']:
-                raise ProxyError("Only http, socks4, socks5 proxy protocols are supported")
-        else:
-            self.proxy_port = 0
-            self.auth = None
-            self.no_proxy = None
-            self.proxy_protocol = "http"
-
-
-def _start_proxied_socket(url, options, proxy):
-    if not HAVE_PYTHON_SOCKS:
-        raise WebSocketException("Python Socks is needed for SOCKS proxying but is not available")
-
-    hostname, port, resource, is_secure = parse_url(url)
-
-    if proxy.proxy_protocol == "socks5":
-        rdns = False
-        proxy_type = ProxyType.SOCKS5
-    if proxy.proxy_protocol == "socks4":
-        rdns = False
-        proxy_type = ProxyType.SOCKS4
-    # socks5h and socks4a send DNS through proxy
-    if proxy.proxy_protocol == "socks5h":
-        rdns = True
-        proxy_type = ProxyType.SOCKS5
-    if proxy.proxy_protocol == "socks4a":
-        rdns = True
-        proxy_type = ProxyType.SOCKS4
-
-    ws_proxy = Proxy.create(
-        proxy_type=proxy_type,
-        host=proxy.proxy_host,
-        port=int(proxy.proxy_port),
-        username=proxy.auth[0] if proxy.auth else None,
-        password=proxy.auth[1] if proxy.auth else None,
-        rdns=rdns)
-
-    sock = ws_proxy.connect(hostname, port, timeout=proxy.proxy_timeout)
-
-    if is_secure and HAVE_SSL:
-        sock = _ssl_socket(sock, options.sslopt, hostname)
-    elif is_secure:
-        raise WebSocketException("SSL not available.")
-
-    return sock, (hostname, port, resource)
-
-
-def connect(url, options, proxy, socket):
-    # Use _start_proxied_socket() only for socks4 or socks5 proxy
-    # Use _tunnel() for http proxy
-    # TODO: Use python-socks for http protocol also, to standardize flow
-    if proxy.proxy_host and not socket and not (proxy.proxy_protocol == "http"):
-        return _start_proxied_socket(url, options, proxy)
-
-    hostname, port, resource, is_secure = parse_url(url)
-
-    if socket:
-        return socket, (hostname, port, resource)
-
-    addrinfo_list, need_tunnel, auth = _get_addrinfo_list(
-        hostname, port, is_secure, proxy)
-    if not addrinfo_list:
-        raise WebSocketException(
-            "Host not found.: " + hostname + ":" + str(port))
-
-    sock = None
-    try:
-        sock = _open_socket(addrinfo_list, options.sockopt, options.timeout)
-        if need_tunnel:
-            sock = _tunnel(sock, hostname, port, auth)
-
-        if is_secure:
-            if HAVE_SSL:
-                sock = _ssl_socket(sock, options.sslopt, hostname)
-            else:
-                raise WebSocketException("SSL not available.")
-
-        return sock, (hostname, port, resource)
-    except:
-        if sock:
-            sock.close()
-        raise
-
-
-def _get_addrinfo_list(hostname, port, is_secure, proxy):
-    phost, pport, pauth = get_proxy_info(
-        hostname, is_secure, proxy.proxy_host, proxy.proxy_port, proxy.auth, proxy.no_proxy)
-    try:
-        # when running on windows 10, getaddrinfo without socktype returns a socktype 0.
-        # This generates an error exception: `_on_error: exception Socket type must be stream or datagram, not 0`
-        # or `OSError: [Errno 22] Invalid argument` when creating socket. Force the socket type to SOCK_STREAM.
-        if not phost:
-            addrinfo_list = socket.getaddrinfo(
-                hostname, port, 0, socket.SOCK_STREAM, socket.SOL_TCP)
-            return addrinfo_list, False, None
-        else:
-            pport = pport and pport or 80
-            # when running on windows 10, the getaddrinfo used above
-            # returns a socktype 0. This generates an error exception:
-            # _on_error: exception Socket type must be stream or datagram, not 0
-            # Force the socket type to SOCK_STREAM
-            addrinfo_list = socket.getaddrinfo(phost, pport, 0, socket.SOCK_STREAM, socket.SOL_TCP)
-            return addrinfo_list, True, pauth
-    except socket.gaierror as e:
-        raise WebSocketAddressException(e)
-
-
-def _open_socket(addrinfo_list, sockopt, timeout):
-    err = None
-    for addrinfo in addrinfo_list:
-        family, socktype, proto = addrinfo[:3]
-        sock = socket.socket(family, socktype, proto)
-        sock.settimeout(timeout)
-        for opts in DEFAULT_SOCKET_OPTION:
-            sock.setsockopt(*opts)
-        for opts in sockopt:
-            sock.setsockopt(*opts)
-
-        address = addrinfo[4]
-        err = None
-        while not err:
-            try:
-                sock.connect(address)
-            except socket.error as error:
-                error.remote_ip = str(address[0])
-                try:
-                    eConnRefused = (errno.ECONNREFUSED, errno.WSAECONNREFUSED)
-                except:
-                    eConnRefused = (errno.ECONNREFUSED, )
-                if error.errno == errno.EINTR:
-                    continue
-                elif error.errno in eConnRefused:
-                    err = error
-                    continue
-                else:
-                    if sock:
-                        sock.close()
-                    raise error
-            else:
-                break
-        else:
-            continue
-        break
-    else:
-        if err:
-            raise err
-
-    return sock
-
-
-def _wrap_sni_socket(sock, sslopt, hostname, check_hostname):
-    context = ssl.SSLContext(sslopt.get('ssl_version', ssl.PROTOCOL_TLS))
-
-    if sslopt.get('cert_reqs', ssl.CERT_NONE) != ssl.CERT_NONE:
-        cafile = sslopt.get('ca_certs', None)
-        capath = sslopt.get('ca_cert_path', None)
-        if cafile or capath:
-            context.load_verify_locations(cafile=cafile, capath=capath)
-        elif hasattr(context, 'load_default_certs'):
-            context.load_default_certs(ssl.Purpose.SERVER_AUTH)
-    if sslopt.get('certfile', None):
-        context.load_cert_chain(
-            sslopt['certfile'],
-            sslopt.get('keyfile', None),
-            sslopt.get('password', None),
-        )
-    # see
-    # https://github.com/liris/websocket-client/commit/b96a2e8fa765753e82eea531adb19716b52ca3ca#commitcomment-10803153
-    context.verify_mode = sslopt['cert_reqs']
-    if HAVE_CONTEXT_CHECK_HOSTNAME:
-        context.check_hostname = check_hostname
-    if 'ciphers' in sslopt:
-        context.set_ciphers(sslopt['ciphers'])
-    if 'cert_chain' in sslopt:
-        certfile, keyfile, password = sslopt['cert_chain']
-        context.load_cert_chain(certfile, keyfile, password)
-    if 'ecdh_curve' in sslopt:
-        context.set_ecdh_curve(sslopt['ecdh_curve'])
-
-    return context.wrap_socket(
-        sock,
-        do_handshake_on_connect=sslopt.get('do_handshake_on_connect', True),
-        suppress_ragged_eofs=sslopt.get('suppress_ragged_eofs', True),
-        server_hostname=hostname,
-    )
-
-
-def _ssl_socket(sock, user_sslopt, hostname):
-    sslopt = dict(cert_reqs=ssl.CERT_REQUIRED)
-    sslopt.update(user_sslopt)
-
-    certPath = os.environ.get('WEBSOCKET_CLIENT_CA_BUNDLE')
-    if certPath and os.path.isfile(certPath) \
-            and user_sslopt.get('ca_certs', None) is None:
-        sslopt['ca_certs'] = certPath
-    elif certPath and os.path.isdir(certPath) \
-            and user_sslopt.get('ca_cert_path', None) is None:
-        sslopt['ca_cert_path'] = certPath
-
-    if sslopt.get('server_hostname', None):
-        hostname = sslopt['server_hostname']
-
-    check_hostname = sslopt["cert_reqs"] != ssl.CERT_NONE and sslopt.pop(
-        'check_hostname', True)
-    sock = _wrap_sni_socket(sock, sslopt, hostname, check_hostname)
-
-    if not HAVE_CONTEXT_CHECK_HOSTNAME and check_hostname:
-        match_hostname(sock.getpeercert(), hostname)
-
-    return sock
-
-
-def _tunnel(sock, host, port, auth):
-    debug("Connecting proxy...")
-    connect_header = "CONNECT %s:%d HTTP/1.1\r\n" % (host, port)
-    connect_header += "Host: %s:%d\r\n" % (host, port)
-
-    # TODO: support digest auth.
-    if auth and auth[0]:
-        auth_str = auth[0]
-        if auth[1]:
-            auth_str += ":" + auth[1]
-        encoded_str = base64encode(auth_str.encode()).strip().decode().replace('\n', '')
-        connect_header += "Proxy-Authorization: Basic %s\r\n" % encoded_str
-    connect_header += "\r\n"
-    dump("request header", connect_header)
-
-    send(sock, connect_header)
-
-    try:
-        status, resp_headers, status_message = read_headers(sock)
-    except Exception as e:
-        raise WebSocketProxyException(str(e))
-
-    if status != 200:
-        raise WebSocketProxyException(
-            "failed CONNECT via proxy status: %r" % status)
-
-    return sock
-
-
-def read_headers(sock):
-    status = None
-    status_message = None
-    headers = {}
-    trace("--- response header ---")
-
-    while True:
-        line = recv_line(sock)
-        line = line.decode('utf-8').strip()
-        if not line:
-            break
-        trace(line)
-        if not status:
-
-            status_info = line.split(" ", 2)
-            status = int(status_info[1])
-            if len(status_info) > 2:
-                status_message = status_info[2]
-        else:
-            kv = line.split(":", 1)
-            if len(kv) == 2:
-                key, value = kv
-                if key.lower() == "set-cookie" and headers.get("set-cookie"):
-                    headers["set-cookie"] = headers.get("set-cookie") + "; " + value.strip()
-                else:
-                    headers[key.lower()] = value.strip()
-            else:
-                raise WebSocketException("Invalid header")
-
-    trace("-----------------------")
-
-    return status, headers, status_message
diff --git a/nls/websocket/_logging.py b/nls/websocket/_logging.py
deleted file mode 100644
index 483bf53..0000000
--- a/nls/websocket/_logging.py
+++ /dev/null
@@ -1,90 +0,0 @@
-"""
-
-"""
-
-"""
-_logging.py
-websocket - WebSocket client library for Python
-
-Copyright 2021 engn33r
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-"""
-import logging
-
-_logger = logging.getLogger('websocket')
-try:
-    from logging import NullHandler
-except ImportError:
-    class NullHandler(logging.Handler):
-        def emit(self, record):
-            pass
-
-_logger.addHandler(NullHandler())
-
-_traceEnabled = False
-
-__all__ = ["enableTrace", "dump", "error", "warning", "debug", "trace",
-           "isEnabledForError", "isEnabledForDebug", "isEnabledForTrace"]
-
-
-def enableTrace(traceable, handler=logging.StreamHandler()):
-    """
-    Turn on/off the traceability.
-
-    Parameters
-    ----------
-    traceable: bool
-        If set to True, traceability is enabled.
-    """
-    global _traceEnabled
-    _traceEnabled = traceable
-    if traceable:
-        _logger.addHandler(handler)
-        _logger.setLevel(logging.ERROR)
-
-
-def dump(title, message):
-    if _traceEnabled:
-        _logger.debug("--- " + title + " ---")
-        _logger.debug(message)
-        _logger.debug("-----------------------")
-
-
-def error(msg):
-    _logger.error(msg)
-
-
-def warning(msg):
-    _logger.warning(msg)
-
-
-def debug(msg):
-    _logger.debug(msg)
-
-
-def trace(msg):
-    if _traceEnabled:
-        _logger.debug(msg)
-
-
-def isEnabledForError():
-    return _logger.isEnabledFor(logging.ERROR)
-
-
-def isEnabledForDebug():
-    return _logger.isEnabledFor(logging.DEBUG)
-
-
-def isEnabledForTrace():
-    return _traceEnabled
diff --git a/nls/websocket/_socket.py b/nls/websocket/_socket.py
deleted file mode 100644
index 092126a..0000000
--- a/nls/websocket/_socket.py
+++ /dev/null
@@ -1,182 +0,0 @@
-"""
-
-"""
-
-"""
-_socket.py
-websocket - WebSocket client library for Python
-
-Copyright 2021 engn33r
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-"""
-import errno
-import selectors
-import socket
-
-from ._exceptions import *
-from ._ssl_compat import *
-from ._utils import *
-
-DEFAULT_SOCKET_OPTION = [(socket.SOL_TCP, socket.TCP_NODELAY, 1)]
-#if hasattr(socket, "SO_KEEPALIVE"):
-#    DEFAULT_SOCKET_OPTION.append((socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1))
-#if hasattr(socket, "TCP_KEEPIDLE"):
-#    DEFAULT_SOCKET_OPTION.append((socket.SOL_TCP, socket.TCP_KEEPIDLE, 30))
-#if hasattr(socket, "TCP_KEEPINTVL"):
-#    DEFAULT_SOCKET_OPTION.append((socket.SOL_TCP, socket.TCP_KEEPINTVL, 10))
-#if hasattr(socket, "TCP_KEEPCNT"):
-#    DEFAULT_SOCKET_OPTION.append((socket.SOL_TCP, socket.TCP_KEEPCNT, 3))
-
-_default_timeout = None
-
-__all__ = ["DEFAULT_SOCKET_OPTION", "sock_opt", "setdefaulttimeout", "getdefaulttimeout",
-           "recv", "recv_line", "send"]
-
-
-class sock_opt:
-
-    def __init__(self, sockopt, sslopt):
-        if sockopt is None:
-            sockopt = []
-        if sslopt is None:
-            sslopt = {}
-        self.sockopt = sockopt
-        self.sslopt = sslopt
-        self.timeout = None
-
-
-def setdefaulttimeout(timeout):
-    """
-    Set the global timeout setting to connect.
-
-    Parameters
-    ----------
-    timeout: int or float
-        default socket timeout time (in seconds)
-    """
-    global _default_timeout
-    _default_timeout = timeout
-
-
-def getdefaulttimeout():
-    """
-    Get default timeout
-
-    Returns
-    ----------
-    _default_timeout: int or float
-        Return the global timeout setting (in seconds) to connect.
-    """
-    return _default_timeout
-
-
-def recv(sock, bufsize):
-    if not sock:
-        raise WebSocketConnectionClosedException("socket is already closed.")
-
-    def _recv():
-        try:
-            return sock.recv(bufsize)
-        except SSLWantReadError:
-            pass
-        except socket.error as exc:
-            error_code = extract_error_code(exc)
-            if error_code is None:
-                raise
-            if error_code != errno.EAGAIN or error_code != errno.EWOULDBLOCK:
-                raise
-
-        sel = selectors.DefaultSelector()
-        sel.register(sock, selectors.EVENT_READ)
-
-        r = sel.select(sock.gettimeout())
-        sel.close()
-
-        if r:
-            return sock.recv(bufsize)
-
-    try:
-        if sock.gettimeout() == 0:
-            bytes_ = sock.recv(bufsize)
-        else:
-            bytes_ = _recv()
-    except socket.timeout as e:
-        message = extract_err_message(e)
-        raise WebSocketTimeoutException(message)
-    except SSLError as e:
-        message = extract_err_message(e)
-        if isinstance(message, str) and 'timed out' in message:
-            raise WebSocketTimeoutException(message)
-        else:
-            raise
-
-    if not bytes_:
-        raise WebSocketConnectionClosedException(
-            "Connection to remote host was lost.")
-
-    return bytes_
-
-
-def recv_line(sock):
-    line = []
-    while True:
-        c = recv(sock, 1)
-        line.append(c)
-        if c == b'\n':
-            break
-    return b''.join(line)
-
-
-def send(sock, data):
-    if isinstance(data, str):
-        data = data.encode('utf-8')
-
-    if not sock:
-        raise WebSocketConnectionClosedException("socket is already closed.")
-
-    def _send():
-        try:
-            return sock.send(data)
-        except SSLWantWriteError:
-            pass
-        except socket.error as exc:
-            error_code = extract_error_code(exc)
-            if error_code is None:
-                raise
-            if error_code != errno.EAGAIN or error_code != errno.EWOULDBLOCK:
-                raise
-
-        sel = selectors.DefaultSelector()
-        sel.register(sock, selectors.EVENT_WRITE)
-
-        w = sel.select(sock.gettimeout())
-        sel.close()
-
-        if w:
-            return sock.send(data)
-
-    try:
-        if sock.gettimeout() == 0:
-            return sock.send(data)
-        else:
-            return _send()
-    except socket.timeout as e:
-        message = extract_err_message(e)
-        raise WebSocketTimeoutException(message)
-    except Exception as e:
-        message = extract_err_message(e)
-        if isinstance(message, str) and "timed out" in message:
-            raise WebSocketTimeoutException(message)
-        else:
-            raise
diff --git a/nls/websocket/_ssl_compat.py b/nls/websocket/_ssl_compat.py
deleted file mode 100644
index 9e5460c..0000000
--- a/nls/websocket/_ssl_compat.py
+++ /dev/null
@@ -1,44 +0,0 @@
-"""
-_ssl_compat.py
-websocket - WebSocket client library for Python
-
-Copyright 2021 engn33r
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-"""
-__all__ = ["HAVE_SSL", "ssl", "SSLError", "SSLWantReadError", "SSLWantWriteError"]
-
-try:
-    import ssl
-    from ssl import SSLError
-    from ssl import SSLWantReadError
-    from ssl import SSLWantWriteError
-    HAVE_CONTEXT_CHECK_HOSTNAME = False
-    if hasattr(ssl, 'SSLContext') and hasattr(ssl.SSLContext, 'check_hostname'):
-        HAVE_CONTEXT_CHECK_HOSTNAME = True
-
-    __all__.append("HAVE_CONTEXT_CHECK_HOSTNAME")
-    HAVE_SSL = True
-except ImportError:
-    # dummy class of SSLError for environment without ssl support
-    class SSLError(Exception):
-        pass
-
-    class SSLWantReadError(Exception):
-        pass
-
-    class SSLWantWriteError(Exception):
-        pass
-
-    ssl = None
-    HAVE_SSL = False
diff --git a/nls/websocket/_url.py b/nls/websocket/_url.py
deleted file mode 100644
index f2a5501..0000000
--- a/nls/websocket/_url.py
+++ /dev/null
@@ -1,176 +0,0 @@
-"""
-
-"""
-"""
-_url.py
-websocket - WebSocket client library for Python
-
-Copyright 2021 engn33r
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-"""
-
-import os
-import socket
-import struct
-
-from urllib.parse import unquote, urlparse
-
-
-__all__ = ["parse_url", "get_proxy_info"]
-
-
-def parse_url(url):
-    """
-    parse url and the result is tuple of
-    (hostname, port, resource path and the flag of secure mode)
-
-    Parameters
-    ----------
-    url: str
-        url string.
-    """
-    if ":" not in url:
-        raise ValueError("url is invalid")
-
-    scheme, url = url.split(":", 1)
-
-    parsed = urlparse(url, scheme="http")
-    if parsed.hostname:
-        hostname = parsed.hostname
-    else:
-        raise ValueError("hostname is invalid")
-    port = 0
-    if parsed.port:
-        port = parsed.port
-
-    is_secure = False
-    if scheme == "ws":
-        if not port:
-            port = 80
-    elif scheme == "wss":
-        is_secure = True
-        if not port:
-            port = 443
-    else:
-        raise ValueError("scheme %s is invalid" % scheme)
-
-    if parsed.path:
-        resource = parsed.path
-    else:
-        resource = "/"
-
-    if parsed.query:
-        resource += "?" + parsed.query
-
-    return hostname, port, resource, is_secure
-
-
-DEFAULT_NO_PROXY_HOST = ["localhost", "127.0.0.1"]
-
-
-def _is_ip_address(addr):
-    try:
-        socket.inet_aton(addr)
-    except socket.error:
-        return False
-    else:
-        return True
-
-
-def _is_subnet_address(hostname):
-    try:
-        addr, netmask = hostname.split("/")
-        return _is_ip_address(addr) and 0 <= int(netmask) < 32
-    except ValueError:
-        return False
-
-
-def _is_address_in_network(ip, net):
-    ipaddr = struct.unpack('!I', socket.inet_aton(ip))[0]
-    netaddr, netmask = net.split('/')
-    netaddr = struct.unpack('!I', socket.inet_aton(netaddr))[0]
-
-    netmask = (0xFFFFFFFF << (32 - int(netmask))) & 0xFFFFFFFF
-    return ipaddr & netmask == netaddr
-
-
-def _is_no_proxy_host(hostname, no_proxy):
-    if not no_proxy:
-        v = os.environ.get("no_proxy", os.environ.get("NO_PROXY", "")).replace(" ", "")
-        if v:
-            no_proxy = v.split(",")
-    if not no_proxy:
-        no_proxy = DEFAULT_NO_PROXY_HOST
-
-    if '*' in no_proxy:
-        return True
-    if hostname in no_proxy:
-        return True
-    if _is_ip_address(hostname):
-        return any([_is_address_in_network(hostname, subnet) for subnet in no_proxy if _is_subnet_address(subnet)])
-    for domain in [domain for domain in no_proxy if domain.startswith('.')]:
-        if hostname.endswith(domain):
-            return True
-    return False
-
-
-def get_proxy_info(
-        hostname, is_secure, proxy_host=None, proxy_port=0, proxy_auth=None,
-        no_proxy=None, proxy_type='http'):
-    """
-    Try to retrieve proxy host and port from environment
-    if not provided in options.
-    Result is (proxy_host, proxy_port, proxy_auth).
-    proxy_auth is tuple of username and password
-    of proxy authentication information.
-
-    Parameters
-    ----------
-    hostname: str
-        Websocket server name.
-    is_secure: bool
-        Is the connection secure? (wss) looks for "https_proxy" in env
-        before falling back to "http_proxy"
-    proxy_host: str
-        http proxy host name.
-    http_proxy_port: str or int
-        http proxy port.
-    http_no_proxy: list
-        Whitelisted host names that don't use the proxy.
-    http_proxy_auth: tuple
-        HTTP proxy auth information. Tuple of username and password. Default is None.
-    proxy_type: str
-        Specify the proxy protocol (http, socks4, socks4a, socks5, socks5h). Default is "http".
-        Use socks4a or socks5h if you want to send DNS requests through the proxy.
-    """
-    if _is_no_proxy_host(hostname, no_proxy):
-        return None, 0, None
-
-    if proxy_host:
-        port = proxy_port
-        auth = proxy_auth
-        return proxy_host, port, auth
-
-    env_keys = ["http_proxy"]
-    if is_secure:
-        env_keys.insert(0, "https_proxy")
-
-    for key in env_keys:
-        value = os.environ.get(key, os.environ.get(key.upper(), "")).replace(" ", "")
-        if value:
-            proxy = urlparse(value)
-            auth = (unquote(proxy.username), unquote(proxy.password)) if proxy.username else None
-            return proxy.hostname, proxy.port, auth
-
-    return None, 0, None
diff --git a/nls/websocket/_utils.py b/nls/websocket/_utils.py
deleted file mode 100644
index 21fc437..0000000
--- a/nls/websocket/_utils.py
+++ /dev/null
@@ -1,104 +0,0 @@
-"""
-_url.py
-websocket - WebSocket client library for Python
-
-Copyright 2021 engn33r
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-"""
-__all__ = ["NoLock", "validate_utf8", "extract_err_message", "extract_error_code"]
-
-
-class NoLock:
-
-    def __enter__(self):
-        pass
-
-    def __exit__(self, exc_type, exc_value, traceback):
-        pass
-
-
-try:
-    # If wsaccel is available we use compiled routines to validate UTF-8
-    # strings.
-    from wsaccel.utf8validator import Utf8Validator
-
-    def _validate_utf8(utfbytes):
-        return Utf8Validator().validate(utfbytes)[0]
-
-except ImportError:
-    # UTF-8 validator
-    # python implementation of http://bjoern.hoehrmann.de/utf-8/decoder/dfa/
-
-    _UTF8_ACCEPT = 0
-    _UTF8_REJECT = 12
-
-    _UTF8D = [
-        # The first part of the table maps bytes to character classes that
-        # to reduce the size of the transition table and create bitmasks.
-        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-        1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,  9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
-        7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,  7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
-        8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2,  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
-        10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8,
-
-        # The second part is a transition table that maps a combination
-        # of a state of the automaton and a character class to a state.
-        0,12,24,36,60,96,84,12,12,12,48,72, 12,12,12,12,12,12,12,12,12,12,12,12,
-        12, 0,12,12,12,12,12, 0,12, 0,12,12, 12,24,12,12,12,12,12,24,12,24,12,12,
-        12,12,12,12,12,12,12,24,12,12,12,12, 12,24,12,12,12,12,12,12,12,24,12,12,
-        12,12,12,12,12,12,12,36,12,36,12,12, 12,36,12,12,12,12,12,36,12,36,12,12,
-        12,36,12,12,12,12,12,12,12,12,12,12, ]
-
-    def _decode(state, codep, ch):
-        tp = _UTF8D[ch]
-
-        codep = (ch & 0x3f) | (codep << 6) if (
-            state != _UTF8_ACCEPT) else (0xff >> tp) & ch
-        state = _UTF8D[256 + state + tp]
-
-        return state, codep
-
-    def _validate_utf8(utfbytes):
-        state = _UTF8_ACCEPT
-        codep = 0
-        for i in utfbytes:
-            state, codep = _decode(state, codep, i)
-            if state == _UTF8_REJECT:
-                return False
-
-        return True
-
-
-def validate_utf8(utfbytes):
-    """
-    validate utf8 byte string.
-    utfbytes: utf byte string to check.
-    return value: if valid utf8 string, return true. Otherwise, return false.
-    """
-    return _validate_utf8(utfbytes)
-
-
-def extract_err_message(exception):
-    if exception.args:
-        return exception.args[0]
-    else:
-        return None
-
-
-def extract_error_code(exception):
-    if exception.args and len(exception.args) > 1:
-        return exception.args[0] if isinstance(exception.args[0], int) else None
diff --git a/requirements.txt b/requirements.txt
deleted file mode 100644
index 6ba2298..0000000
--- a/requirements.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-oss2
-aliyun-python-sdk-core==2.13.3
-matplotlib==3.3.4
-
diff --git a/setup.py b/setup.py
index e0f9108..2d969ef 100644
--- a/setup.py
+++ b/setup.py
@@ -1,49 +1,46 @@
 #!/usr/bin/python
-'''
-Licensed to the Apache Software Foundation(ASF) under one
-or more contributor license agreements.  See the NOTICE file
-distributed with this work for additional information
-regarding copyright ownership.  The ASF licenses this file
-to you under the Apache License, Version 2.0 (the
-"License"); you may not use this file except in compliance
-with the License.  You may obtain a copy of the License at
-http: // www.apache.org/licenses/LICENSE-2.0
-Unless required by applicable law or agreed to in writing,
-software distributed under the License is distributed on an
-"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-KIND, either express or implied.  See the License for the
-specific language governing permissions and limitations under
-the License.
-'''
-import os
-import setuptools
+"""
+ * Copyright 2015 Alibaba Group Holding Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+"""
 
-with open("README.md", "r") as f:
-    long_description = f.read()
+try:
+    from setuptools import setup
+except ImportError:
+    from distutils.core import setup
 
-requires = [
-    "oss2",
-    "aliyun-python-sdk-core==2.13.3",
-    "matplotlib==3.3.4"
-]
-
-setup_args = {
-    'version': "0.0.1",
-    'author': "jiaqi.sjq",
-    'author_email': "jiaqi.sjq@alibaba-inc.com",
-    'description': "python sdk for nls",
-    'license':"Apache License 2.0",
-    'long_description': long_description,
-    'long_description_content_type': "text/markdown",
-    'keywords': ["nls", "sdk"],
-    'url': "https://github.com/..",
-    'packages': ["nls", "nls/websocket"],
-    'install_requires': requires,
-    'classifiers': [
-        "Programming Language :: Python :: 3",
-        "License :: OSI Approved :: Apache Software License",
-        "Operating System :: OS Independent",
-    ],
+config = {
+    'name': 'alibabacloud-nls-python-sdk',
+    'version': '2.0.0',
+    'description': 'ali_speech python sdk',
+    'author': 'Alibaba Cloud NLS Team',
+    'author_email': 'nls-system-client@list.alibaba-inc.com',
+    'license': 'Apache License 2.0',
+    'url': 'https://github.com/aliyun/alibabacloud-nls-python-sdk.git',
+    'install_requires': ['websocket-client', 'requests'],
+    'packages': ['ali_speech'],
+    'classifiers': (
+        'Development Status :: 5 - Production/Stable',
+        'Intended Audience :: Developers',
+        'License :: OSI Approved :: Apache Software License',
+        'Programming Language :: Python :: 3',
+        'Programming Language :: Python :: 3.4',
+        'Programming Language :: Python :: 3.5',
+        'Programming Language :: Python :: 3.6',
+        'Programming Language :: Python :: 3.7',
+        'Topic :: Software Development',
+    )
 }
 
-setuptools.setup(name='nls', **setup_args)
+setup(**config)


Test patch:
diff --git a/nls/websocket/tests/__init__.py b/nls/websocket/tests/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/nls/websocket/tests/data/header01.txt b/nls/websocket/tests/data/header01.txt
deleted file mode 100644
index 3142b43..0000000
--- a/nls/websocket/tests/data/header01.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-HTTP/1.1 101 WebSocket Protocol Handshake
-Connection: Upgrade 
-Upgrade: WebSocket
-Sec-WebSocket-Accept: Kxep+hNu9n51529fGidYu7a3wO0=
-some_header: something
-
diff --git a/nls/websocket/tests/data/header02.txt b/nls/websocket/tests/data/header02.txt
deleted file mode 100644
index a9dd2ce..0000000
--- a/nls/websocket/tests/data/header02.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-HTTP/1.1 101 WebSocket Protocol Handshake
-Connection: Upgrade
-Upgrade WebSocket
-Sec-WebSocket-Accept: Kxep+hNu9n51529fGidYu7a3wO0=
-some_header: something
-
diff --git a/nls/websocket/tests/data/header03.txt b/nls/websocket/tests/data/header03.txt
deleted file mode 100644
index 030e13a..0000000
--- a/nls/websocket/tests/data/header03.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-HTTP/1.1 101 WebSocket Protocol Handshake
-Connection: Upgrade, Keep-Alive
-Upgrade: WebSocket
-Sec-WebSocket-Accept: Kxep+hNu9n51529fGidYu7a3wO0=
-Set-Cookie: Token=ABCDE
-some_header: something
-
diff --git a/nls/websocket/tests/echo-server.py b/nls/websocket/tests/echo-server.py
deleted file mode 100644
index 08d108a..0000000
--- a/nls/websocket/tests/echo-server.py
+++ /dev/null
@@ -1,21 +0,0 @@
-#!/usr/bin/env python
-
-# From https://github.com/aaugustin/websockets/blob/main/example/echo.py
-
-import asyncio
-import websockets
-import os
-
-LOCAL_WS_SERVER_PORT = os.environ.get('LOCAL_WS_SERVER_PORT', '8765')
-
-
-async def echo(websocket, path):
-    async for message in websocket:
-        await websocket.send(message)
-
-
-async def main():
-    async with websockets.serve(echo, "localhost", LOCAL_WS_SERVER_PORT):
-        await asyncio.Future()  # run forever
-
-asyncio.run(main())
diff --git a/nls/websocket/tests/test_abnf.py b/nls/websocket/tests/test_abnf.py
deleted file mode 100644
index a218293..0000000
--- a/nls/websocket/tests/test_abnf.py
+++ /dev/null
@@ -1,89 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-"""
-test_abnf.py
-websocket - WebSocket client library for Python
-
-Copyright 2021 engn33r
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-"""
-
-import websocket as ws
-from websocket._abnf import *
-import unittest
-
-
-class ABNFTest(unittest.TestCase):
-
-    def testInit(self):
-        a = ABNF(0,0,0,0, opcode=ABNF.OPCODE_PING)
-        self.assertEqual(a.fin, 0)
-        self.assertEqual(a.rsv1, 0)
-        self.assertEqual(a.rsv2, 0)
-        self.assertEqual(a.rsv3, 0)
-        self.assertEqual(a.opcode, 9)
-        self.assertEqual(a.data, '')
-        a_bad = ABNF(0,1,0,0, opcode=77)
-        self.assertEqual(a_bad.rsv1, 1)
-        self.assertEqual(a_bad.opcode, 77)
-
-    def testValidate(self):
-        a_invalid_ping = ABNF(0,0,0,0, opcode=ABNF.OPCODE_PING)
-        self.assertRaises(ws._exceptions.WebSocketProtocolException, a_invalid_ping.validate, skip_utf8_validation=False)
-        a_bad_rsv_value = ABNF(0,1,0,0, opcode=ABNF.OPCODE_TEXT)
-        self.assertRaises(ws._exceptions.WebSocketProtocolException, a_bad_rsv_value.validate, skip_utf8_validation=False)
-        a_bad_opcode = ABNF(0,0,0,0, opcode=77)
-        self.assertRaises(ws._exceptions.WebSocketProtocolException, a_bad_opcode.validate, skip_utf8_validation=False)
-        a_bad_close_frame = ABNF(0,0,0,0, opcode=ABNF.OPCODE_CLOSE, data=b'\x01')
-        self.assertRaises(ws._exceptions.WebSocketProtocolException, a_bad_close_frame.validate, skip_utf8_validation=False)
-        a_bad_close_frame_2 = ABNF(0,0,0,0, opcode=ABNF.OPCODE_CLOSE, data=b'\x01\x8a\xaa\xff\xdd')
-        self.assertRaises(ws._exceptions.WebSocketProtocolException, a_bad_close_frame_2.validate, skip_utf8_validation=False)
-        a_bad_close_frame_3 = ABNF(0,0,0,0, opcode=ABNF.OPCODE_CLOSE, data=b'\x03\xe7')
-        self.assertRaises(ws._exceptions.WebSocketProtocolException, a_bad_close_frame_3.validate, skip_utf8_validation=True)
-
-    def testMask(self):
-        abnf_none_data = ABNF(0,0,0,0, opcode=ABNF.OPCODE_PING, mask=1, data=None)
-        bytes_val = bytes("aaaa", 'utf-8')
-        self.assertEqual(abnf_none_data._get_masked(bytes_val), bytes_val)
-        abnf_str_data = ABNF(0,0,0,0, opcode=ABNF.OPCODE_PING, mask=1, data="a")
-        self.assertEqual(abnf_str_data._get_masked(bytes_val), b'aaaa\x00')
-
-    def testFormat(self):
-        abnf_bad_rsv_bits = ABNF(2,0,0,0, opcode=ABNF.OPCODE_TEXT)
-        self.assertRaises(ValueError, abnf_bad_rsv_bits.format)
-        abnf_bad_opcode = ABNF(0,0,0,0, opcode=5)
-        self.assertRaises(ValueError, abnf_bad_opcode.format)
-        abnf_length_10 = ABNF(0,0,0,0, opcode=ABNF.OPCODE_TEXT, data="abcdefghij")
-        self.assertEqual(b'\x01', abnf_length_10.format()[0].to_bytes(1, 'big'))
-        self.assertEqual(b'\x8a', abnf_length_10.format()[1].to_bytes(1, 'big'))
-        self.assertEqual("fin=0 opcode=1 data=abcdefghij", abnf_length_10.__str__())
-        abnf_length_20 = ABNF(0,0,0,0, opcode=ABNF.OPCODE_BINARY, data="abcdefghijabcdefghij")
-        self.assertEqual(b'\x02', abnf_length_20.format()[0].to_bytes(1, 'big'))
-        self.assertEqual(b'\x94', abnf_length_20.format()[1].to_bytes(1, 'big'))
-        abnf_no_mask = ABNF(0,0,0,0, opcode=ABNF.OPCODE_TEXT, mask=0, data=b'\x01\x8a\xcc')
-        self.assertEqual(b'\x01\x03\x01\x8a\xcc', abnf_no_mask.format())
-
-    def testFrameBuffer(self):
-        fb = frame_buffer(0, True)
-        self.assertEqual(fb.recv, 0)
-        self.assertEqual(fb.skip_utf8_validation, True)
-        fb.clear
-        self.assertEqual(fb.header, None)
-        self.assertEqual(fb.length, None)
-        self.assertEqual(fb.mask, None)
-        self.assertEqual(fb.has_mask(), False)
-
-
-if __name__ == "__main__":
-    unittest.main()
diff --git a/nls/websocket/tests/test_app.py b/nls/websocket/tests/test_app.py
deleted file mode 100644
index cd1146b..0000000
--- a/nls/websocket/tests/test_app.py
+++ /dev/null
@@ -1,179 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-"""
-test_app.py
-websocket - WebSocket client library for Python
-
-Copyright 2021 engn33r
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-"""
-
-import os
-import os.path
-import websocket as ws
-import ssl
-import unittest
-
-# Skip test to access the internet unless TEST_WITH_INTERNET == 1
-TEST_WITH_INTERNET = os.environ.get('TEST_WITH_INTERNET', '0') == '1'
-# Skip tests relying on local websockets server unless LOCAL_WS_SERVER_PORT != -1
-LOCAL_WS_SERVER_PORT = os.environ.get('LOCAL_WS_SERVER_PORT', '-1')
-TEST_WITH_LOCAL_SERVER = LOCAL_WS_SERVER_PORT != '-1'
-TRACEABLE = True
-
-
-class WebSocketAppTest(unittest.TestCase):
-
-    class NotSetYet:
-        """ A marker class for signalling that a value hasn't been set yet.
-        """
-
-    def setUp(self):
-        ws.enableTrace(TRACEABLE)
-
-        WebSocketAppTest.keep_running_open = WebSocketAppTest.NotSetYet()
-        WebSocketAppTest.keep_running_close = WebSocketAppTest.NotSetYet()
-        WebSocketAppTest.get_mask_key_id = WebSocketAppTest.NotSetYet()
-
-    def tearDown(self):
-        WebSocketAppTest.keep_running_open = WebSocketAppTest.NotSetYet()
-        WebSocketAppTest.keep_running_close = WebSocketAppTest.NotSetYet()
-        WebSocketAppTest.get_mask_key_id = WebSocketAppTest.NotSetYet()
-
-    @unittest.skipUnless(TEST_WITH_LOCAL_SERVER, "Tests using local websocket server are disabled")
-    def testKeepRunning(self):
-        """ A WebSocketApp should keep running as long as its self.keep_running
-        is not False (in the boolean context).
-        """
-
-        def on_open(self, *args, **kwargs):
-            """ Set the keep_running flag for later inspection and immediately
-            close the connection.
-            """
-            self.send("hello!")
-            WebSocketAppTest.keep_running_open = self.keep_running
-            self.keep_running = False
-
-        def on_message(wsapp, message):
-            print(message)
-            self.close()
-
-        def on_close(self, *args, **kwargs):
-            """ Set the keep_running flag for the test to use.
-            """
-            WebSocketAppTest.keep_running_close = self.keep_running
-
-        app = ws.WebSocketApp('ws://127.0.0.1:' + LOCAL_WS_SERVER_PORT, on_open=on_open, on_close=on_close, on_message=on_message)
-        app.run_forever()
-
-    @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled")
-    def testSockMaskKey(self):
-        """ A WebSocketApp should forward the received mask_key function down
-        to the actual socket.
-        """
-
-        def my_mask_key_func():
-            return "\x00\x00\x00\x00"
-
-        app = ws.WebSocketApp('wss://stream.meetup.com/2/rsvps', get_mask_key=my_mask_key_func)
-
-        # if numpy is installed, this assertion fail
-        # Note: We can't use 'is' for comparing the functions directly, need to use 'id'.
-        self.assertEqual(id(app.get_mask_key), id(my_mask_key_func))
-
-    @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled")
-    def testInvalidPingIntervalPingTimeout(self):
-        """ Test exception handling if ping_interval < ping_timeout
-        """
-
-        def on_ping(app, msg):
-            print("Got a ping!")
-            app.close()
-
-        def on_pong(app, msg):
-            print("Got a pong! No need to respond")
-            app.close()
-
-        app = ws.WebSocketApp('wss://api-pub.bitfinex.com/ws/1', on_ping=on_ping, on_pong=on_pong)
-        self.assertRaises(ws.WebSocketException, app.run_forever, ping_interval=1, ping_timeout=2, sslopt={"cert_reqs": ssl.CERT_NONE})
-
-    @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled")
-    def testPingInterval(self):
-        """ Test WebSocketApp proper ping functionality
-        """
-
-        def on_ping(app, msg):
-            print("Got a ping!")
-            app.close()
-
-        def on_pong(app, msg):
-            print("Got a pong! No need to respond")
-            app.close()
-
-        app = ws.WebSocketApp('wss://api-pub.bitfinex.com/ws/1', on_ping=on_ping, on_pong=on_pong)
-        app.run_forever(ping_interval=2, ping_timeout=1, sslopt={"cert_reqs": ssl.CERT_NONE})
-
-    @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled")
-    def testOpcodeClose(self):
-        """ Test WebSocketApp close opcode
-        """
-
-        app = ws.WebSocketApp('wss://tsock.us1.twilio.com/v3/wsconnect')
-        app.run_forever(ping_interval=2, ping_timeout=1, ping_payload="Ping payload")
-
-    @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled")
-    def testOpcodeBinary(self):
-        """ Test WebSocketApp binary opcode
-        """
-
-        app = ws.WebSocketApp('streaming.vn.teslamotors.com/streaming/')
-        app.run_forever(ping_interval=2, ping_timeout=1, ping_payload="Ping payload")
-
-    @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled")
-    def testBadPingInterval(self):
-        """ A WebSocketApp handling of negative ping_interval
-        """
-        app = ws.WebSocketApp('wss://api-pub.bitfinex.com/ws/1')
-        self.assertRaises(ws.WebSocketException, app.run_forever, ping_interval=-5, sslopt={"cert_reqs": ssl.CERT_NONE})
-
-    @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled")
-    def testBadPingTimeout(self):
-        """ A WebSocketApp handling of negative ping_timeout
-        """
-        app = ws.WebSocketApp('wss://api-pub.bitfinex.com/ws/1')
-        self.assertRaises(ws.WebSocketException, app.run_forever, ping_timeout=-3, sslopt={"cert_reqs": ssl.CERT_NONE})
-
-    @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled")
-    def testCloseStatusCode(self):
-        """ Test extraction of close frame status code and close reason in WebSocketApp
-        """
-        def on_close(wsapp, close_status_code, close_msg):
-            print("on_close reached")
-
-        app = ws.WebSocketApp('wss://tsock.us1.twilio.com/v3/wsconnect', on_close=on_close)
-        closeframe = ws.ABNF(opcode=ws.ABNF.OPCODE_CLOSE, data=b'\x03\xe8no-init-from-client')
-        self.assertEqual([1000, 'no-init-from-client'], app._get_close_args(closeframe))
-
-        closeframe = ws.ABNF(opcode=ws.ABNF.OPCODE_CLOSE, data=b'')
-        self.assertEqual([None, None], app._get_close_args(closeframe))
-
-        app2 = ws.WebSocketApp('wss://tsock.us1.twilio.com/v3/wsconnect')
-        closeframe = ws.ABNF(opcode=ws.ABNF.OPCODE_CLOSE, data=b'')
-        self.assertEqual([None, None], app2._get_close_args(closeframe))
-
-        self.assertRaises(ws.WebSocketConnectionClosedException, app.send, data="test if connection is closed")
-
-
-if __name__ == "__main__":
-    unittest.main()
diff --git a/nls/websocket/tests/test_cookiejar.py b/nls/websocket/tests/test_cookiejar.py
deleted file mode 100644
index 5bf1fca..0000000
--- a/nls/websocket/tests/test_cookiejar.py
+++ /dev/null
@@ -1,119 +0,0 @@
-"""
-
-"""
-
-"""
-test_cookiejar.py
-websocket - WebSocket client library for Python
-
-Copyright 2021 engn33r
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-"""
-import unittest
-from websocket._cookiejar import SimpleCookieJar
-
-
-class CookieJarTest(unittest.TestCase):
-    def testAdd(self):
-        cookie_jar = SimpleCookieJar()
-        cookie_jar.add("")
-        self.assertFalse(cookie_jar.jar, "Cookie with no domain should not be added to the jar")
-
-        cookie_jar = SimpleCookieJar()
-        cookie_jar.add("a=b")
-        self.assertFalse(cookie_jar.jar, "Cookie with no domain should not be added to the jar")
-
-        cookie_jar = SimpleCookieJar()
-        cookie_jar.add("a=b; domain=.abc")
-        self.assertTrue(".abc" in cookie_jar.jar)
-
-        cookie_jar = SimpleCookieJar()
-        cookie_jar.add("a=b; domain=abc")
-        self.assertTrue(".abc" in cookie_jar.jar)
-        self.assertTrue("abc" not in cookie_jar.jar)
-
-        cookie_jar = SimpleCookieJar()
-        cookie_jar.add("a=b; c=d; domain=abc")
-        self.assertEqual(cookie_jar.get("abc"), "a=b; c=d")
-        self.assertEqual(cookie_jar.get(None), "")
-
-        cookie_jar = SimpleCookieJar()
-        cookie_jar.add("a=b; c=d; domain=abc")
-        cookie_jar.add("e=f; domain=abc")
-        self.assertEqual(cookie_jar.get("abc"), "a=b; c=d; e=f")
-
-        cookie_jar = SimpleCookieJar()
-        cookie_jar.add("a=b; c=d; domain=abc")
-        cookie_jar.add("e=f; domain=.abc")
-        self.assertEqual(cookie_jar.get("abc"), "a=b; c=d; e=f")
-
-        cookie_jar = SimpleCookieJar()
-        cookie_jar.add("a=b; c=d; domain=abc")
-        cookie_jar.add("e=f; domain=xyz")
-        self.assertEqual(cookie_jar.get("abc"), "a=b; c=d")
-        self.assertEqual(cookie_jar.get("xyz"), "e=f")
-        self.assertEqual(cookie_jar.get("something"), "")
-
-    def testSet(self):
-        cookie_jar = SimpleCookieJar()
-        cookie_jar.set("a=b")
-        self.assertFalse(cookie_jar.jar, "Cookie with no domain should not be added to the jar")
-
-        cookie_jar = SimpleCookieJar()
-        cookie_jar.set("a=b; domain=.abc")
-        self.assertTrue(".abc" in cookie_jar.jar)
-
-        cookie_jar = SimpleCookieJar()
-        cookie_jar.set("a=b; domain=abc")
-        self.assertTrue(".abc" in cookie_jar.jar)
-        self.assertTrue("abc" not in cookie_jar.jar)
-
-        cookie_jar = SimpleCookieJar()
-        cookie_jar.set("a=b; c=d; domain=abc")
-        self.assertEqual(cookie_jar.get("abc"), "a=b; c=d")
-
-        cookie_jar = SimpleCookieJar()
-        cookie_jar.set("a=b; c=d; domain=abc")
-        cookie_jar.set("e=f; domain=abc")
-        self.assertEqual(cookie_jar.get("abc"), "e=f")
-
-        cookie_jar = SimpleCookieJar()
-        cookie_jar.set("a=b; c=d; domain=abc")
-        cookie_jar.set("e=f; domain=.abc")
-        self.assertEqual(cookie_jar.get("abc"), "e=f")
-
-        cookie_jar = SimpleCookieJar()
-        cookie_jar.set("a=b; c=d; domain=abc")
-        cookie_jar.set("e=f; domain=xyz")
-        self.assertEqual(cookie_jar.get("abc"), "a=b; c=d")
-        self.assertEqual(cookie_jar.get("xyz"), "e=f")
-        self.assertEqual(cookie_jar.get("something"), "")
-
-    def testGet(self):
-        cookie_jar = SimpleCookieJar()
-        cookie_jar.set("a=b; c=d; domain=abc.com")
-        self.assertEqual(cookie_jar.get("abc.com"), "a=b; c=d")
-        self.assertEqual(cookie_jar.get("x.abc.com"), "a=b; c=d")
-        self.assertEqual(cookie_jar.get("abc.com.es"), "")
-        self.assertEqual(cookie_jar.get("xabc.com"), "")
-
-        cookie_jar.set("a=b; c=d; domain=.abc.com")
-        self.assertEqual(cookie_jar.get("abc.com"), "a=b; c=d")
-        self.assertEqual(cookie_jar.get("x.abc.com"), "a=b; c=d")
-        self.assertEqual(cookie_jar.get("abc.com.es"), "")
-        self.assertEqual(cookie_jar.get("xabc.com"), "")
-
-
-if __name__ == "__main__":
-    unittest.main()
diff --git a/nls/websocket/tests/test_http.py b/nls/websocket/tests/test_http.py
deleted file mode 100644
index 5e08301..0000000
--- a/nls/websocket/tests/test_http.py
+++ /dev/null
@@ -1,177 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-"""
-test_http.py
-websocket - WebSocket client library for Python
-
-Copyright 2021 engn33r
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-"""
-
-import os
-import os.path
-import websocket as ws
-from websocket._http import proxy_info, read_headers, _start_proxied_socket, _tunnel, _get_addrinfo_list, connect
-import unittest
-import ssl
-import websocket
-import socket
-
-try:
-    from python_socks._errors import ProxyError, ProxyTimeoutError, ProxyConnectionError
-except:
-    from websocket._http import ProxyError, ProxyTimeoutError, ProxyConnectionError
-
-# Skip test to access the internet unless TEST_WITH_INTERNET == 1
-TEST_WITH_INTERNET = os.environ.get('TEST_WITH_INTERNET', '0') == '1'
-TEST_WITH_PROXY = os.environ.get('TEST_WITH_PROXY', '0') == '1'
-# Skip tests relying on local websockets server unless LOCAL_WS_SERVER_PORT != -1
-LOCAL_WS_SERVER_PORT = os.environ.get('LOCAL_WS_SERVER_PORT', '-1')
-TEST_WITH_LOCAL_SERVER = LOCAL_WS_SERVER_PORT != '-1'
-
-
-class SockMock:
-    def __init__(self):
-        self.data = []
-        self.sent = []
-
-    def add_packet(self, data):
-        self.data.append(data)
-
-    def gettimeout(self):
-        return None
-
-    def recv(self, bufsize):
-        if self.data:
-            e = self.data.pop(0)
-            if isinstance(e, Exception):
-                raise e
-            if len(e) > bufsize:
-                self.data.insert(0, e[bufsize:])
-            return e[:bufsize]
-
-    def send(self, data):
-        self.sent.append(data)
-        return len(data)
-
-    def close(self):
-        pass
-
-
-class HeaderSockMock(SockMock):
-
-    def __init__(self, fname):
-        SockMock.__init__(self)
-        path = os.path.join(os.path.dirname(__file__), fname)
-        with open(path, "rb") as f:
-            self.add_packet(f.read())
-
-
-class OptsList():
-
-    def __init__(self):
-        self.timeout = 1
-        self.sockopt = []
-        self.sslopt = {"cert_reqs": ssl.CERT_NONE}
-
-
-class HttpTest(unittest.TestCase):
-
-    def testReadHeader(self):
-        status, header, status_message = read_headers(HeaderSockMock("data/header01.txt"))
-        self.assertEqual(status, 101)
-        self.assertEqual(header["connection"], "Upgrade")
-        # header02.txt is intentionally malformed
-        self.assertRaises(ws.WebSocketException, read_headers, HeaderSockMock("data/header02.txt"))
-
-    def testTunnel(self):
-        self.assertRaises(ws.WebSocketProxyException, _tunnel, HeaderSockMock("data/header01.txt"), "example.com", 80, ("username", "password"))
-        self.assertRaises(ws.WebSocketProxyException, _tunnel, HeaderSockMock("data/header02.txt"), "example.com", 80, ("username", "password"))
-
-    @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled")
-    def testConnect(self):
-        # Not currently testing an actual proxy connection, so just check whether proxy errors are raised. This requires internet for a DNS lookup
-        if ws._http.HAVE_PYTHON_SOCKS:
-            # Need this check, otherwise case where python_socks is not installed triggers
-            # websocket._exceptions.WebSocketException: Python Socks is needed for SOCKS proxying but is not available
-            self.assertRaises(ProxyTimeoutError, _start_proxied_socket, "wss://example.com", OptsList(), proxy_info(http_proxy_host="example.com", http_proxy_port="8080", proxy_type="socks4", timeout=1))
-            self.assertRaises(ProxyTimeoutError, _start_proxied_socket, "wss://example.com", OptsList(), proxy_info(http_proxy_host="example.com", http_proxy_port="8080", proxy_type="socks4a", timeout=1))
-            self.assertRaises(ProxyTimeoutError, _start_proxied_socket, "wss://example.com", OptsList(), proxy_info(http_proxy_host="example.com", http_proxy_port="8080", proxy_type="socks5", timeout=1))
-            self.assertRaises(ProxyTimeoutError, _start_proxied_socket, "wss://example.com", OptsList(), proxy_info(http_proxy_host="example.com", http_proxy_port="8080", proxy_type="socks5h", timeout=1))
-            self.assertRaises(ProxyConnectionError, connect, "wss://example.com", OptsList(), proxy_info(http_proxy_host="127.0.0.1", http_proxy_port=9999, proxy_type="socks4", timeout=1), None)
-
-        self.assertRaises(TypeError, _get_addrinfo_list, None, 80, True, proxy_info(http_proxy_host="127.0.0.1", http_proxy_port="9999", proxy_type="http"))
-        self.assertRaises(TypeError, _get_addrinfo_list, None, 80, True, proxy_info(http_proxy_host="127.0.0.1", http_proxy_port="9999", proxy_type="http"))
-        self.assertRaises(socket.timeout, connect, "wss://google.com", OptsList(), proxy_info(http_proxy_host="8.8.8.8", http_proxy_port=9999, proxy_type="http", timeout=1), None)
-        self.assertEqual(
-            connect("wss://google.com", OptsList(), proxy_info(http_proxy_host="8.8.8.8", http_proxy_port=8080, proxy_type="http"), True),
-            (True, ("google.com", 443, "/")))
-        # The following test fails on Mac OS with a gaierror, not an OverflowError
-        # self.assertRaises(OverflowError, connect, "wss://example.com", OptsList(), proxy_info(http_proxy_host="127.0.0.1", http_proxy_port=99999, proxy_type="socks4", timeout=2), False)
-
-    @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled")
-    @unittest.skipUnless(TEST_WITH_PROXY, "This test requires a HTTP proxy to be running on port 8899")
-    @unittest.skipUnless(TEST_WITH_LOCAL_SERVER, "Tests using local websocket server are disabled")
-    def testProxyConnect(self):
-        ws = websocket.WebSocket()
-        ws.connect("ws://127.0.0.1:" + LOCAL_WS_SERVER_PORT, http_proxy_host="127.0.0.1", http_proxy_port="8899", proxy_type="http")
-        ws.send("Hello, Server")
-        server_response = ws.recv()
-        self.assertEqual(server_response, "Hello, Server")
-        # self.assertEqual(_start_proxied_socket("wss://api.bitfinex.com/ws/2", OptsList(), proxy_info(http_proxy_host="127.0.0.1", http_proxy_port="8899", proxy_type="http"))[1], ("api.bitfinex.com", 443, '/ws/2'))
-        self.assertEqual(_get_addrinfo_list("api.bitfinex.com", 443, True, proxy_info(http_proxy_host="127.0.0.1", http_proxy_port="8899", proxy_type="http")),
-                         (socket.getaddrinfo("127.0.0.1", 8899, 0, socket.SOCK_STREAM, socket.SOL_TCP), True, None))
-        self.assertEqual(connect("wss://api.bitfinex.com/ws/2", OptsList(), proxy_info(http_proxy_host="127.0.0.1", http_proxy_port=8899, proxy_type="http"), None)[1], ("api.bitfinex.com", 443, '/ws/2'))
-        # TODO: Test SOCKS4 and SOCK5 proxies with unit tests
-
-    @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled")
-    def testSSLopt(self):
-        ssloptions = {
-            "cert_reqs": ssl.CERT_NONE,
-            "check_hostname": False,
-            "server_hostname": "ServerName",
-            "ssl_version": ssl.PROTOCOL_TLS,
-            "ciphers": "TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:\
-                        TLS_AES_128_GCM_SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:\
-                        ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:\
-                        ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:\
-                        DHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:\
-                        ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES128-GCM-SHA256:\
-                        ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:\
-                        DHE-RSA-AES256-SHA256:ECDHE-ECDSA-AES128-SHA256:\
-                        ECDHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA256:\
-                        ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA",
-            "ecdh_curve": "prime256v1"
-        }
-        ws_ssl1 = websocket.WebSocket(sslopt=ssloptions)
-        ws_ssl1.connect("wss://api.bitfinex.com/ws/2")
-        ws_ssl1.send("Hello")
-        ws_ssl1.close()
-
-        ws_ssl2 = websocket.WebSocket(sslopt={"check_hostname": True})
-        ws_ssl2.connect("wss://api.bitfinex.com/ws/2")
-        ws_ssl2.close
-
-    def testProxyInfo(self):
-        self.assertEqual(proxy_info(http_proxy_host="127.0.0.1", http_proxy_port="8080", proxy_type="http").proxy_protocol, "http")
-        self.assertRaises(ProxyError, proxy_info, http_proxy_host="127.0.0.1", http_proxy_port="8080", proxy_type="badval")
-        self.assertEqual(proxy_info(http_proxy_host="example.com", http_proxy_port="8080", proxy_type="http").proxy_host, "example.com")
-        self.assertEqual(proxy_info(http_proxy_host="127.0.0.1", http_proxy_port="8080", proxy_type="http").proxy_port, "8080")
-        self.assertEqual(proxy_info(http_proxy_host="127.0.0.1", http_proxy_port="8080", proxy_type="http").auth, None)
-        self.assertEqual(proxy_info(http_proxy_host="127.0.0.1", http_proxy_port="8080", proxy_type="http", http_proxy_auth=("my_username123", "my_pass321")).auth[0], "my_username123")
-        self.assertEqual(proxy_info(http_proxy_host="127.0.0.1", http_proxy_port="8080", proxy_type="http", http_proxy_auth=("my_username123", "my_pass321")).auth[1], "my_pass321")
-
-
-if __name__ == "__main__":
-    unittest.main()
diff --git a/nls/websocket/tests/test_url.py b/nls/websocket/tests/test_url.py
deleted file mode 100644
index ad3a3b1..0000000
--- a/nls/websocket/tests/test_url.py
+++ /dev/null
@@ -1,301 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-"""
-test_url.py
-websocket - WebSocket client library for Python
-
-Copyright 2021 engn33r
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-"""
-
-import os
-import unittest
-from websocket._url import get_proxy_info, parse_url, _is_address_in_network, _is_no_proxy_host
-
-
-class UrlTest(unittest.TestCase):
-
-    def test_address_in_network(self):
-        self.assertTrue(_is_address_in_network('127.0.0.1', '127.0.0.0/8'))
-        self.assertTrue(_is_address_in_network('127.1.0.1', '127.0.0.0/8'))
-        self.assertFalse(_is_address_in_network('127.1.0.1', '127.0.0.0/24'))
-
-    def testParseUrl(self):
-        p = parse_url("ws://www.example.com/r")
-        self.assertEqual(p[0], "www.example.com")
-        self.assertEqual(p[1], 80)
-        self.assertEqual(p[2], "/r")
-        self.assertEqual(p[3], False)
-
-        p = parse_url("ws://www.example.com/r/")
-        self.assertEqual(p[0], "www.example.com")
-        self.assertEqual(p[1], 80)
-        self.assertEqual(p[2], "/r/")
-        self.assertEqual(p[3], False)
-
-        p = parse_url("ws://www.example.com/")
-        self.assertEqual(p[0], "www.example.com")
-        self.assertEqual(p[1], 80)
-        self.assertEqual(p[2], "/")
-        self.assertEqual(p[3], False)
-
-        p = parse_url("ws://www.example.com")
-        self.assertEqual(p[0], "www.example.com")
-        self.assertEqual(p[1], 80)
-        self.assertEqual(p[2], "/")
-        self.assertEqual(p[3], False)
-
-        p = parse_url("ws://www.example.com:8080/r")
-        self.assertEqual(p[0], "www.example.com")
-        self.assertEqual(p[1], 8080)
-        self.assertEqual(p[2], "/r")
-        self.assertEqual(p[3], False)
-
-        p = parse_url("ws://www.example.com:8080/")
-        self.assertEqual(p[0], "www.example.com")
-        self.assertEqual(p[1], 8080)
-        self.assertEqual(p[2], "/")
-        self.assertEqual(p[3], False)
-
-        p = parse_url("ws://www.example.com:8080")
-        self.assertEqual(p[0], "www.example.com")
-        self.assertEqual(p[1], 8080)
-        self.assertEqual(p[2], "/")
-        self.assertEqual(p[3], False)
-
-        p = parse_url("wss://www.example.com:8080/r")
-        self.assertEqual(p[0], "www.example.com")
-        self.assertEqual(p[1], 8080)
-        self.assertEqual(p[2], "/r")
-        self.assertEqual(p[3], True)
-
-        p = parse_url("wss://www.example.com:8080/r?key=value")
-        self.assertEqual(p[0], "www.example.com")
-        self.assertEqual(p[1], 8080)
-        self.assertEqual(p[2], "/r?key=value")
-        self.assertEqual(p[3], True)
-
-        self.assertRaises(ValueError, parse_url, "http://www.example.com/r")
-
-        p = parse_url("ws://[2a03:4000:123:83::3]/r")
-        self.assertEqual(p[0], "2a03:4000:123:83::3")
-        self.assertEqual(p[1], 80)
-        self.assertEqual(p[2], "/r")
-        self.assertEqual(p[3], False)
-
-        p = parse_url("ws://[2a03:4000:123:83::3]:8080/r")
-        self.assertEqual(p[0], "2a03:4000:123:83::3")
-        self.assertEqual(p[1], 8080)
-        self.assertEqual(p[2], "/r")
-        self.assertEqual(p[3], False)
-
-        p = parse_url("wss://[2a03:4000:123:83::3]/r")
-        self.assertEqual(p[0], "2a03:4000:123:83::3")
-        self.assertEqual(p[1], 443)
-        self.assertEqual(p[2], "/r")
-        self.assertEqual(p[3], True)
-
-        p = parse_url("wss://[2a03:4000:123:83::3]:8080/r")
-        self.assertEqual(p[0], "2a03:4000:123:83::3")
-        self.assertEqual(p[1], 8080)
-        self.assertEqual(p[2], "/r")
-        self.assertEqual(p[3], True)
-
-
-class IsNoProxyHostTest(unittest.TestCase):
-    def setUp(self):
-        self.no_proxy = os.environ.get("no_proxy", None)
-        if "no_proxy" in os.environ:
-            del os.environ["no_proxy"]
-
-    def tearDown(self):
-        if self.no_proxy:
-            os.environ["no_proxy"] = self.no_proxy
-        elif "no_proxy" in os.environ:
-            del os.environ["no_proxy"]
-
-    def testMatchAll(self):
-        self.assertTrue(_is_no_proxy_host("any.websocket.org", ['*']))
-        self.assertTrue(_is_no_proxy_host("192.168.0.1", ['*']))
-        self.assertTrue(_is_no_proxy_host("any.websocket.org", ['other.websocket.org', '*']))
-        os.environ['no_proxy'] = '*'
-        self.assertTrue(_is_no_proxy_host("any.websocket.org", None))
-        self.assertTrue(_is_no_proxy_host("192.168.0.1", None))
-        os.environ['no_proxy'] = 'other.websocket.org, *'
-        self.assertTrue(_is_no_proxy_host("any.websocket.org", None))
-
-    def testIpAddress(self):
-        self.assertTrue(_is_no_proxy_host("127.0.0.1", ['127.0.0.1']))
-        self.assertFalse(_is_no_proxy_host("127.0.0.2", ['127.0.0.1']))
-        self.assertTrue(_is_no_proxy_host("127.0.0.1", ['other.websocket.org', '127.0.0.1']))
-        self.assertFalse(_is_no_proxy_host("127.0.0.2", ['other.websocket.org', '127.0.0.1']))
-        os.environ['no_proxy'] = '127.0.0.1'
-        self.assertTrue(_is_no_proxy_host("127.0.0.1", None))
-        self.assertFalse(_is_no_proxy_host("127.0.0.2", None))
-        os.environ['no_proxy'] = 'other.websocket.org, 127.0.0.1'
-        self.assertTrue(_is_no_proxy_host("127.0.0.1", None))
-        self.assertFalse(_is_no_proxy_host("127.0.0.2", None))
-
-    def testIpAddressInRange(self):
-        self.assertTrue(_is_no_proxy_host("127.0.0.1", ['127.0.0.0/8']))
-        self.assertTrue(_is_no_proxy_host("127.0.0.2", ['127.0.0.0/8']))
-        self.assertFalse(_is_no_proxy_host("127.1.0.1", ['127.0.0.0/24']))
-        os.environ['no_proxy'] = '127.0.0.0/8'
-        self.assertTrue(_is_no_proxy_host("127.0.0.1", None))
-        self.assertTrue(_is_no_proxy_host("127.0.0.2", None))
-        os.environ['no_proxy'] = '127.0.0.0/24'
-        self.assertFalse(_is_no_proxy_host("127.1.0.1", None))
-
-    def testHostnameMatch(self):
-        self.assertTrue(_is_no_proxy_host("my.websocket.org", ['my.websocket.org']))
-        self.assertTrue(_is_no_proxy_host("my.websocket.org", ['other.websocket.org', 'my.websocket.org']))
-        self.assertFalse(_is_no_proxy_host("my.websocket.org", ['other.websocket.org']))
-        os.environ['no_proxy'] = 'my.websocket.org'
-        self.assertTrue(_is_no_proxy_host("my.websocket.org", None))
-        self.assertFalse(_is_no_proxy_host("other.websocket.org", None))
-        os.environ['no_proxy'] = 'other.websocket.org, my.websocket.org'
-        self.assertTrue(_is_no_proxy_host("my.websocket.org", None))
-
-    def testHostnameMatchDomain(self):
-        self.assertTrue(_is_no_proxy_host("any.websocket.org", ['.websocket.org']))
-        self.assertTrue(_is_no_proxy_host("my.other.websocket.org", ['.websocket.org']))
-        self.assertTrue(_is_no_proxy_host("any.websocket.org", ['my.websocket.org', '.websocket.org']))
-        self.assertFalse(_is_no_proxy_host("any.websocket.com", ['.websocket.org']))
-        os.environ['no_proxy'] = '.websocket.org'
-        self.assertTrue(_is_no_proxy_host("any.websocket.org", None))
-        self.assertTrue(_is_no_proxy_host("my.other.websocket.org", None))
-        self.assertFalse(_is_no_proxy_host("any.websocket.com", None))
-        os.environ['no_proxy'] = 'my.websocket.org, .websocket.org'
-        self.assertTrue(_is_no_proxy_host("any.websocket.org", None))
-
-
-class ProxyInfoTest(unittest.TestCase):
-    def setUp(self):
-        self.http_proxy = os.environ.get("http_proxy", None)
-        self.https_proxy = os.environ.get("https_proxy", None)
-        self.no_proxy = os.environ.get("no_proxy", None)
-        if "http_proxy" in os.environ:
-            del os.environ["http_proxy"]
-        if "https_proxy" in os.environ:
-            del os.environ["https_proxy"]
-        if "no_proxy" in os.environ:
-            del os.environ["no_proxy"]
-
-    def tearDown(self):
-        if self.http_proxy:
-            os.environ["http_proxy"] = self.http_proxy
-        elif "http_proxy" in os.environ:
-            del os.environ["http_proxy"]
-
-        if self.https_proxy:
-            os.environ["https_proxy"] = self.https_proxy
-        elif "https_proxy" in os.environ:
-            del os.environ["https_proxy"]
-
-        if self.no_proxy:
-            os.environ["no_proxy"] = self.no_proxy
-        elif "no_proxy" in os.environ:
-            del os.environ["no_proxy"]
-
-    def testProxyFromArgs(self):
-        self.assertEqual(get_proxy_info("echo.websocket.org", False, proxy_host="localhost"), ("localhost", 0, None))
-        self.assertEqual(get_proxy_info("echo.websocket.org", False, proxy_host="localhost", proxy_port=3128),
-                         ("localhost", 3128, None))
-        self.assertEqual(get_proxy_info("echo.websocket.org", True, proxy_host="localhost"), ("localhost", 0, None))
-        self.assertEqual(get_proxy_info("echo.websocket.org", True, proxy_host="localhost", proxy_port=3128),
-                         ("localhost", 3128, None))
-
-        self.assertEqual(get_proxy_info("echo.websocket.org", False, proxy_host="localhost", proxy_auth=("a", "b")),
-                         ("localhost", 0, ("a", "b")))
-        self.assertEqual(
-            get_proxy_info("echo.websocket.org", False, proxy_host="localhost", proxy_port=3128, proxy_auth=("a", "b")),
-            ("localhost", 3128, ("a", "b")))
-        self.assertEqual(get_proxy_info("echo.websocket.org", True, proxy_host="localhost", proxy_auth=("a", "b")),
-                         ("localhost", 0, ("a", "b")))
-        self.assertEqual(
-            get_proxy_info("echo.websocket.org", True, proxy_host="localhost", proxy_port=3128, proxy_auth=("a", "b")),
-            ("localhost", 3128, ("a", "b")))
-
-        self.assertEqual(get_proxy_info("echo.websocket.org", True, proxy_host="localhost", proxy_port=3128,
-                                        no_proxy=["example.com"], proxy_auth=("a", "b")),
-                         ("localhost", 3128, ("a", "b")))
-        self.assertEqual(get_proxy_info("echo.websocket.org", True, proxy_host="localhost", proxy_port=3128,
-                                        no_proxy=["echo.websocket.org"], proxy_auth=("a", "b")),
-                         (None, 0, None))
-
-    def testProxyFromEnv(self):
-        os.environ["http_proxy"] = "http://localhost/"
-        self.assertEqual(get_proxy_info("echo.websocket.org", False), ("localhost", None, None))
-        os.environ["http_proxy"] = "http://localhost:3128/"
-        self.assertEqual(get_proxy_info("echo.websocket.org", False), ("localhost", 3128, None))
-
-        os.environ["http_proxy"] = "http://localhost/"
-        os.environ["https_proxy"] = "http://localhost2/"
-        self.assertEqual(get_proxy_info("echo.websocket.org", False), ("localhost", None, None))
-        os.environ["http_proxy"] = "http://localhost:3128/"
-        os.environ["https_proxy"] = "http://localhost2:3128/"
-        self.assertEqual(get_proxy_info("echo.websocket.org", False), ("localhost", 3128, None))
-
-        os.environ["http_proxy"] = "http://localhost/"
-        os.environ["https_proxy"] = "http://localhost2/"
-        self.assertEqual(get_proxy_info("echo.websocket.org", True), ("localhost2", None, None))
-        os.environ["http_proxy"] = "http://localhost:3128/"
-        os.environ["https_proxy"] = "http://localhost2:3128/"
-        self.assertEqual(get_proxy_info("echo.websocket.org", True), ("localhost2", 3128, None))
-
-        os.environ["http_proxy"] = "http://a:b@localhost/"
-        self.assertEqual(get_proxy_info("echo.websocket.org", False), ("localhost", None, ("a", "b")))
-        os.environ["http_proxy"] = "http://a:b@localhost:3128/"
-        self.assertEqual(get_proxy_info("echo.websocket.org", False), ("localhost", 3128, ("a", "b")))
-
-        os.environ["http_proxy"] = "http://a:b@localhost/"
-        os.environ["https_proxy"] = "http://a:b@localhost2/"
-        self.assertEqual(get_proxy_info("echo.websocket.org", False), ("localhost", None, ("a", "b")))
-        os.environ["http_proxy"] = "http://a:b@localhost:3128/"
-        os.environ["https_proxy"] = "http://a:b@localhost2:3128/"
-        self.assertEqual(get_proxy_info("echo.websocket.org", False), ("localhost", 3128, ("a", "b")))
-
-        os.environ["http_proxy"] = "http://a:b@localhost/"
-        os.environ["https_proxy"] = "http://a:b@localhost2/"
-        self.assertEqual(get_proxy_info("echo.websocket.org", True), ("localhost2", None, ("a", "b")))
-        os.environ["http_proxy"] = "http://a:b@localhost:3128/"
-        os.environ["https_proxy"] = "http://a:b@localhost2:3128/"
-        self.assertEqual(get_proxy_info("echo.websocket.org", True), ("localhost2", 3128, ("a", "b")))
-
-        os.environ["http_proxy"] = "http://john%40example.com:P%40SSWORD@localhost:3128/"
-        os.environ["https_proxy"] = "http://john%40example.com:P%40SSWORD@localhost2:3128/"
-        self.assertEqual(get_proxy_info("echo.websocket.org", True), ("localhost2", 3128, ("john@example.com", "P@SSWORD")))
-
-        os.environ["http_proxy"] = "http://a:b@localhost/"
-        os.environ["https_proxy"] = "http://a:b@localhost2/"
-        os.environ["no_proxy"] = "example1.com,example2.com"
-        self.assertEqual(get_proxy_info("example.1.com", True), ("localhost2", None, ("a", "b")))
-        os.environ["http_proxy"] = "http://a:b@localhost:3128/"
-        os.environ["https_proxy"] = "http://a:b@localhost2:3128/"
-        os.environ["no_proxy"] = "example1.com,example2.com, echo.websocket.org"
-        self.assertEqual(get_proxy_info("echo.websocket.org", True), (None, 0, None))
-        os.environ["http_proxy"] = "http://a:b@localhost:3128/"
-        os.environ["https_proxy"] = "http://a:b@localhost2:3128/"
-        os.environ["no_proxy"] = "example1.com,example2.com, .websocket.org"
-        self.assertEqual(get_proxy_info("echo.websocket.org", True), (None, 0, None))
-
-        os.environ["http_proxy"] = "http://a:b@localhost:3128/"
-        os.environ["https_proxy"] = "http://a:b@localhost2:3128/"
-        os.environ["no_proxy"] = "127.0.0.0/8, 192.168.0.0/16"
-        self.assertEqual(get_proxy_info("127.0.0.1", False), (None, 0, None))
-        self.assertEqual(get_proxy_info("192.168.1.1", False), (None, 0, None))
-
-
-if __name__ == "__main__":
-    unittest.main()
diff --git a/nls/websocket/tests/test_websocket.py b/nls/websocket/tests/test_websocket.py
deleted file mode 100644
index d67c700..0000000
--- a/nls/websocket/tests/test_websocket.py
+++ /dev/null
@@ -1,458 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-"""
-
-"""
-
-"""
-test_websocket.py
-websocket - WebSocket client library for Python
-
-Copyright 2021 engn33r
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-"""
-
-import os
-import os.path
-import socket
-import websocket as ws
-from websocket._handshake import _create_sec_websocket_key, \
-    _validate as _validate_header
-from websocket._http import read_headers
-from websocket._utils import validate_utf8
-from base64 import decodebytes as base64decode
-
-import unittest
-
-try:
-    import ssl
-    from ssl import SSLError
-except ImportError:
-    # dummy class of SSLError for ssl none-support environment.
-    class SSLError(Exception):
-        pass
-
-# Skip test to access the internet unless TEST_WITH_INTERNET == 1
-TEST_WITH_INTERNET = os.environ.get('TEST_WITH_INTERNET', '0') == '1'
-# Skip tests relying on local websockets server unless LOCAL_WS_SERVER_PORT != -1
-LOCAL_WS_SERVER_PORT = os.environ.get('LOCAL_WS_SERVER_PORT', '-1')
-TEST_WITH_LOCAL_SERVER = LOCAL_WS_SERVER_PORT != '-1'
-TRACEABLE = True
-
-
-def create_mask_key(_):
-    return "abcd"
-
-
-class SockMock:
-    def __init__(self):
-        self.data = []
-        self.sent = []
-
-    def add_packet(self, data):
-        self.data.append(data)
-
-    def gettimeout(self):
-        return None
-
-    def recv(self, bufsize):
-        if self.data:
-            e = self.data.pop(0)
-            if isinstance(e, Exception):
-                raise e
-            if len(e) > bufsize:
-                self.data.insert(0, e[bufsize:])
-            return e[:bufsize]
-
-    def send(self, data):
-        self.sent.append(data)
-        return len(data)
-
-    def close(self):
-        pass
-
-
-class HeaderSockMock(SockMock):
-
-    def __init__(self, fname):
-        SockMock.__init__(self)
-        path = os.path.join(os.path.dirname(__file__), fname)
-        with open(path, "rb") as f:
-            self.add_packet(f.read())
-
-
-class WebSocketTest(unittest.TestCase):
-    def setUp(self):
-        ws.enableTrace(TRACEABLE)
-
-    def tearDown(self):
-        pass
-
-    def testDefaultTimeout(self):
-        self.assertEqual(ws.getdefaulttimeout(), None)
-        ws.setdefaulttimeout(10)
-        self.assertEqual(ws.getdefaulttimeout(), 10)
-        ws.setdefaulttimeout(None)
-
-    def testWSKey(self):
-        key = _create_sec_websocket_key()
-        self.assertTrue(key != 24)
-        self.assertTrue(str("¥n") not in key)
-
-    def testNonce(self):
-        """ WebSocket key should be a random 16-byte nonce.
-        """
-        key = _create_sec_websocket_key()
-        nonce = base64decode(key.encode("utf-8"))
-        self.assertEqual(16, len(nonce))
-
-    def testWsUtils(self):
-        key = "c6b8hTg4EeGb2gQMztV1/g=="
-        required_header = {
-            "upgrade": "websocket",
-            "connection": "upgrade",
-            "sec-websocket-accept": "Kxep+hNu9n51529fGidYu7a3wO0="}
-        self.assertEqual(_validate_header(required_header, key, None), (True, None))
-
-        header = required_header.copy()
-        header["upgrade"] = "http"
-        self.assertEqual(_validate_header(header, key, None), (False, None))
-        del header["upgrade"]
-        self.assertEqual(_validate_header(header, key, None), (False, None))
-
-        header = required_header.copy()
-        header["connection"] = "something"
-        self.assertEqual(_validate_header(header, key, None), (False, None))
-        del header["connection"]
-        self.assertEqual(_validate_header(header, key, None), (False, None))
-
-        header = required_header.copy()
-        header["sec-websocket-accept"] = "something"
-        self.assertEqual(_validate_header(header, key, None), (False, None))
-        del header["sec-websocket-accept"]
-        self.assertEqual(_validate_header(header, key, None), (False, None))
-
-        header = required_header.copy()
-        header["sec-websocket-protocol"] = "sub1"
-        self.assertEqual(_validate_header(header, key, ["sub1", "sub2"]), (True, "sub1"))
-        # This case will print out a logging error using the error() function, but that is expected
-        self.assertEqual(_validate_header(header, key, ["sub2", "sub3"]), (False, None))
-
-        header = required_header.copy()
-        header["sec-websocket-protocol"] = "sUb1"
-        self.assertEqual(_validate_header(header, key, ["Sub1", "suB2"]), (True, "sub1"))
-
-        header = required_header.copy()
-        # This case will print out a logging error using the error() function, but that is expected
-        self.assertEqual(_validate_header(header, key, ["Sub1", "suB2"]), (False, None))
-
-    def testReadHeader(self):
-        status, header, status_message = read_headers(HeaderSockMock("data/header01.txt"))
-        self.assertEqual(status, 101)
-        self.assertEqual(header["connection"], "Upgrade")
-
-        status, header, status_message = read_headers(HeaderSockMock("data/header03.txt"))
-        self.assertEqual(status, 101)
-        self.assertEqual(header["connection"], "Upgrade, Keep-Alive")
-
-        HeaderSockMock("data/header02.txt")
-        self.assertRaises(ws.WebSocketException, read_headers, HeaderSockMock("data/header02.txt"))
-
-    def testSend(self):
-        # TODO: add longer frame data
-        sock = ws.WebSocket()
-        sock.set_mask_key(create_mask_key)
-        s = sock.sock = HeaderSockMock("data/header01.txt")
-        sock.send("Hello")
-        self.assertEqual(s.sent[0], b'\x81\x85abcd)\x07\x0f\x08\x0e')
-
-        sock.send("こんにちは")
-        self.assertEqual(s.sent[1], b'\x81\x8fabcd\x82\xe3\xf0\x87\xe3\xf1\x80\xe5\xca\x81\xe2\xc5\x82\xe3\xcc')
-
-#        sock.send("x" * 5000)
-#        self.assertEqual(s.sent[1], b'\x81\x8fabcd\x82\xe3\xf0\x87\xe3\xf1\x80\xe5\xca\x81\xe2\xc5\x82\xe3\xcc")
-
-        self.assertEqual(sock.send_binary(b'1111111111101'), 19)
-
-    def testRecv(self):
-        # TODO: add longer frame data
-        sock = ws.WebSocket()
-        s = sock.sock = SockMock()
-        something = b'\x81\x8fabcd\x82\xe3\xf0\x87\xe3\xf1\x80\xe5\xca\x81\xe2\xc5\x82\xe3\xcc'
-        s.add_packet(something)
-        data = sock.recv()
-        self.assertEqual(data, "こんにちは")
-
-        s.add_packet(b'\x81\x85abcd)\x07\x0f\x08\x0e')
-        data = sock.recv()
-        self.assertEqual(data, "Hello")
-
-    @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled")
-    def testIter(self):
-        count = 2
-        for _ in ws.create_connection('wss://stream.meetup.com/2/rsvps'):
-            count -= 1
-            if count == 0:
-                break
-
-    @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled")
-    def testNext(self):
-        sock = ws.create_connection('wss://stream.meetup.com/2/rsvps')
-        self.assertEqual(str, type(next(sock)))
-
-    def testInternalRecvStrict(self):
-        sock = ws.WebSocket()
-        s = sock.sock = SockMock()
-        s.add_packet(b'foo')
-        s.add_packet(socket.timeout())
-        s.add_packet(b'bar')
-        # s.add_packet(SSLError("The read operation timed out"))
-        s.add_packet(b'baz')
-        with self.assertRaises(ws.WebSocketTimeoutException):
-            sock.frame_buffer.recv_strict(9)
-        #     with self.assertRaises(SSLError):
-        #         data = sock._recv_strict(9)
-        data = sock.frame_buffer.recv_strict(9)
-        self.assertEqual(data, b'foobarbaz')
-        with self.assertRaises(ws.WebSocketConnectionClosedException):
-            sock.frame_buffer.recv_strict(1)
-
-    def testRecvTimeout(self):
-        sock = ws.WebSocket()
-        s = sock.sock = SockMock()
-        s.add_packet(b'\x81')
-        s.add_packet(socket.timeout())
-        s.add_packet(b'\x8dabcd\x29\x07\x0f\x08\x0e')
-        s.add_packet(socket.timeout())
-        s.add_packet(b'\x4e\x43\x33\x0e\x10\x0f\x00\x40')
-        with self.assertRaises(ws.WebSocketTimeoutException):
-            sock.recv()
-        with self.assertRaises(ws.WebSocketTimeoutException):
-            sock.recv()
-        data = sock.recv()
-        self.assertEqual(data, "Hello, World!")
-        with self.assertRaises(ws.WebSocketConnectionClosedException):
-            sock.recv()
-
-    def testRecvWithSimpleFragmentation(self):
-        sock = ws.WebSocket()
-        s = sock.sock = SockMock()
-        # OPCODE=TEXT, FIN=0, MSG="Brevity is "
-        s.add_packet(b'\x01\x8babcd#\x10\x06\x12\x08\x16\x1aD\x08\x11C')
-        # OPCODE=CONT, FIN=1, MSG="the soul of wit"
-        s.add_packet(b'\x80\x8fabcd\x15\n\x06D\x12\r\x16\x08A\r\x05D\x16\x0b\x17')
-        data = sock.recv()
-        self.assertEqual(data, "Brevity is the soul of wit")
-        with self.assertRaises(ws.WebSocketConnectionClosedException):
-            sock.recv()
-
-    def testRecvWithFireEventOfFragmentation(self):
-        sock = ws.WebSocket(fire_cont_frame=True)
-        s = sock.sock = SockMock()
-        # OPCODE=TEXT, FIN=0, MSG="Brevity is "
-        s.add_packet(b'\x01\x8babcd#\x10\x06\x12\x08\x16\x1aD\x08\x11C')
-        # OPCODE=CONT, FIN=0, MSG="Brevity is "
-        s.add_packet(b'\x00\x8babcd#\x10\x06\x12\x08\x16\x1aD\x08\x11C')
-        # OPCODE=CONT, FIN=1, MSG="the soul of wit"
-        s.add_packet(b'\x80\x8fabcd\x15\n\x06D\x12\r\x16\x08A\r\x05D\x16\x0b\x17')
-
-        _, data = sock.recv_data()
-        self.assertEqual(data, b'Brevity is ')
-        _, data = sock.recv_data()
-        self.assertEqual(data, b'Brevity is ')
-        _, data = sock.recv_data()
-        self.assertEqual(data, b'the soul of wit')
-
-        # OPCODE=CONT, FIN=0, MSG="Brevity is "
-        s.add_packet(b'\x80\x8babcd#\x10\x06\x12\x08\x16\x1aD\x08\x11C')
-
-        with self.assertRaises(ws.WebSocketException):
-            sock.recv_data()
-
-        with self.assertRaises(ws.WebSocketConnectionClosedException):
-            sock.recv()
-
-    def testClose(self):
-        sock = ws.WebSocket()
-        sock.connected = True
-        sock.close
-
-        sock = ws.WebSocket()
-        s = sock.sock = SockMock()
-        sock.connected = True
-        s.add_packet(b'\x88\x80\x17\x98p\x84')
-        sock.recv()
-        self.assertEqual(sock.connected, False)
-
-    def testRecvContFragmentation(self):
-        sock = ws.WebSocket()
-        s = sock.sock = SockMock()
-        # OPCODE=CONT, FIN=1, MSG="the soul of wit"
-        s.add_packet(b'\x80\x8fabcd\x15\n\x06D\x12\r\x16\x08A\r\x05D\x16\x0b\x17')
-        self.assertRaises(ws.WebSocketException, sock.recv)
-
-    def testRecvWithProlongedFragmentation(self):
-        sock = ws.WebSocket()
-        s = sock.sock = SockMock()
-        # OPCODE=TEXT, FIN=0, MSG="Once more unto the breach, "
-        s.add_packet(b'\x01\x9babcd.\x0c\x00\x01A\x0f\x0c\x16\x04B\x16\n\x15\rC\x10\t\x07C\x06\x13\x07\x02\x07\tNC')
-        # OPCODE=CONT, FIN=0, MSG="dear friends, "
-        s.add_packet(b'\x00\x8eabcd\x05\x07\x02\x16A\x04\x11\r\x04\x0c\x07\x17MB')
-        # OPCODE=CONT, FIN=1, MSG="once more"
-        s.add_packet(b'\x80\x89abcd\x0e\x0c\x00\x01A\x0f\x0c\x16\x04')
-        data = sock.recv()
-        self.assertEqual(
-            data,
-            "Once more unto the breach, dear friends, once more")
-        with self.assertRaises(ws.WebSocketConnectionClosedException):
-            sock.recv()
-
-    def testRecvWithFragmentationAndControlFrame(self):
-        sock = ws.WebSocket()
-        sock.set_mask_key(create_mask_key)
-        s = sock.sock = SockMock()
-        # OPCODE=TEXT, FIN=0, MSG="Too much "
-        s.add_packet(b'\x01\x89abcd5\r\x0cD\x0c\x17\x00\x0cA')
-        # OPCODE=PING, FIN=1, MSG="Please PONG this"
-        s.add_packet(b'\x89\x90abcd1\x0e\x06\x05\x12\x07C4.,$D\x15\n\n\x17')
-        # OPCODE=CONT, FIN=1, MSG="of a good thing"
-        s.add_packet(b'\x80\x8fabcd\x0e\x04C\x05A\x05\x0c\x0b\x05B\x17\x0c\x08\x0c\x04')
-        data = sock.recv()
-        self.assertEqual(data, "Too much of a good thing")
-        with self.assertRaises(ws.WebSocketConnectionClosedException):
-            sock.recv()
-        self.assertEqual(
-            s.sent[0],
-            b'\x8a\x90abcd1\x0e\x06\x05\x12\x07C4.,$D\x15\n\n\x17')
-
-    @unittest.skipUnless(TEST_WITH_LOCAL_SERVER, "Tests using local websocket server are disabled")
-    def testWebSocket(self):
-        s = ws.create_connection("ws://127.0.0.1:" + LOCAL_WS_SERVER_PORT)
-        self.assertNotEqual(s, None)
-        s.send("Hello, World")
-        result = s.next()
-        s.fileno()
-        self.assertEqual(result, "Hello, World")
-
-        s.send("こにゃにゃちは、世界")
-        result = s.recv()
-        self.assertEqual(result, "こにゃにゃちは、世界")
-        self.assertRaises(ValueError, s.send_close, -1, "")
-        s.close()
-
-    @unittest.skipUnless(TEST_WITH_LOCAL_SERVER, "Tests using local websocket server are disabled")
-    def testPingPong(self):
-        s = ws.create_connection("ws://127.0.0.1:" + LOCAL_WS_SERVER_PORT)
-        self.assertNotEqual(s, None)
-        s.ping("Hello")
-        s.pong("Hi")
-        s.close()
-
-    @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled")
-    def testSupportRedirect(self):
-        s = ws.WebSocket()
-        self.assertRaises(ws._exceptions.WebSocketBadStatusException, s.connect, "ws://google.com/")
-        # Need to find a URL that has a redirect code leading to a websocket
-
-    @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled")
-    def testSecureWebSocket(self):
-        import ssl
-        s = ws.create_connection("wss://api.bitfinex.com/ws/2")
-        self.assertNotEqual(s, None)
-        self.assertTrue(isinstance(s.sock, ssl.SSLSocket))
-        self.assertEqual(s.getstatus(), 101)
-        self.assertNotEqual(s.getheaders(), None)
-        s.settimeout(10)
-        self.assertEqual(s.gettimeout(), 10)
-        self.assertEqual(s.getsubprotocol(), None)
-        s.abort()
-
-    @unittest.skipUnless(TEST_WITH_LOCAL_SERVER, "Tests using local websocket server are disabled")
-    def testWebSocketWithCustomHeader(self):
-        s = ws.create_connection("ws://127.0.0.1:" + LOCAL_WS_SERVER_PORT,
-                                 headers={"User-Agent": "PythonWebsocketClient"})
-        self.assertNotEqual(s, None)
-        s.send("Hello, World")
-        result = s.recv()
-        self.assertEqual(result, "Hello, World")
-        self.assertRaises(ValueError, s.close, -1, "")
-        s.close()
-
-    @unittest.skipUnless(TEST_WITH_LOCAL_SERVER, "Tests using local websocket server are disabled")
-    def testAfterClose(self):
-        s = ws.create_connection("ws://127.0.0.1:" + LOCAL_WS_SERVER_PORT)
-        self.assertNotEqual(s, None)
-        s.close()
-        self.assertRaises(ws.WebSocketConnectionClosedException, s.send, "Hello")
-        self.assertRaises(ws.WebSocketConnectionClosedException, s.recv)
-
-
-class SockOptTest(unittest.TestCase):
-    @unittest.skipUnless(TEST_WITH_LOCAL_SERVER, "Tests using local websocket server are disabled")
-    def testSockOpt(self):
-        sockopt = ((socket.IPPROTO_TCP, socket.TCP_NODELAY, 1),)
-        s = ws.create_connection("ws://127.0.0.1:" + LOCAL_WS_SERVER_PORT, sockopt=sockopt)
-        self.assertNotEqual(s.sock.getsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY), 0)
-        s.close()
-
-
-class UtilsTest(unittest.TestCase):
-    def testUtf8Validator(self):
-        state = validate_utf8(b'\xf0\x90\x80\x80')
-        self.assertEqual(state, True)
-        state = validate_utf8(b'\xce\xba\xe1\xbd\xb9\xcf\x83\xce\xbc\xce\xb5\xed\xa0\x80edited')
-        self.assertEqual(state, False)
-        state = validate_utf8(b'')
-        self.assertEqual(state, True)
-
-
-class HandshakeTest(unittest.TestCase):
-    @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled")
-    def test_http_SSL(self):
-        websock1 = ws.WebSocket(sslopt={"cert_chain": ssl.get_default_verify_paths().capath}, enable_multithread=False)
-        self.assertRaises(ValueError,
-                          websock1.connect, "wss://api.bitfinex.com/ws/2")
-        websock2 = ws.WebSocket(sslopt={"certfile": "myNonexistentCertFile"})
-        self.assertRaises(FileNotFoundError,
-                          websock2.connect, "wss://api.bitfinex.com/ws/2")
-
-    @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled")
-    def testManualHeaders(self):
-        websock3 = ws.WebSocket(sslopt={"cert_reqs": ssl.CERT_NONE,
-                                        "ca_certs": ssl.get_default_verify_paths().capath,
-                                        "ca_cert_path": ssl.get_default_verify_paths().openssl_cafile})
-        self.assertRaises(ws._exceptions.WebSocketBadStatusException,
-                          websock3.connect, "wss://api.bitfinex.com/ws/2", cookie="chocolate",
-                          origin="testing_websockets.com",
-                          host="echo.websocket.org/websocket-client-test",
-                          subprotocols=["testproto"],
-                          connection="Upgrade",
-                          header={"CustomHeader1":"123",
-                                  "Cookie":"TestValue",
-                                  "Sec-WebSocket-Key":"k9kFAUWNAMmf5OEMfTlOEA==",
-                                  "Sec-WebSocket-Protocol":"newprotocol"})
-
-    def testIPv6(self):
-        websock2 = ws.WebSocket()
-        self.assertRaises(ValueError, websock2.connect, "2001:4860:4860::8888")
-
-    def testBadURLs(self):
-        websock3 = ws.WebSocket()
-        self.assertRaises(ValueError, websock3.connect, "ws//example.com")
-        self.assertRaises(ws.WebSocketAddressException, websock3.connect, "ws://example")
-        self.assertRaises(ValueError, websock3.connect, "example.com")
-
-
-if __name__ == "__main__":
-    unittest.main()
diff --git a/tests/test0.wav b/tests/test0.wav
deleted file mode 100644
index e015840..0000000
Binary files a/tests/test0.wav and /dev/null differ
diff --git a/tests/test1.pcm b/tests/test1.pcm
deleted file mode 100644
index 9cf4dfa..0000000
Binary files a/tests/test1.pcm and /dev/null differ
diff --git a/tests/test1.wav b/tests/test1.wav
deleted file mode 100644
index 2143906..0000000
Binary files a/tests/test1.wav and /dev/null differ
diff --git a/tests/test1_1.pcm b/tests/test1_1.pcm
deleted file mode 100644
index 716e617..0000000
Binary files a/tests/test1_1.pcm and /dev/null differ
diff --git a/tests/test2.pcm b/tests/test2.pcm
deleted file mode 100644
index e69de29..0000000
diff --git a/tests/test_common.py b/tests/test_common.py
deleted file mode 100644
index 2822cb7..0000000
--- a/tests/test_common.py
+++ /dev/null
@@ -1,99 +0,0 @@
-import time
-import threading
-import sys
-
-import nls
-
-URL="wss://nls-gateway.cn-shanghai.aliyuncs.com/ws/v1"
-AKID="Your AKID"
-AKKEY="Your AKSECRET"
-APPKEY="Your APPKEY"
-
-class TestSrCommon:
-    def __init__(self, tid, test_file):
-        self.__th = threading.Thread(target=self.__test_run)
-        self.__id = tid
-        self.__test_file = test_file
-   
-    def loadfile(self, filename):
-        with open(filename, "rb") as f:
-            self.__data = f.read()
-    
-    def start(self):
-        self.loadfile(self.__test_file)
-        self.__th.start()
-
-    def test_on_open(self, *args):
-        print("test_on_start")
-
-    def test_on_start(self, message, *args):
-        print("test_on_start:{}".format(message))
-
-    def test_on_data(self, data, *args):
-        pass
-
-    def test_on_error(self, message, *args):
-        print("on_error args=>{}".format(args))
-
-    def test_on_close(self, *args):
-        print("on_close: args=>{}".format(args))
-
-    def test_on_result_chg(self, message, *args):
-        print("test_on_chg:{}".format(message))
-
-    def test_on_completed(self, message, *args):
-        print("on_completed:args=>{} message=>{}".format(args, message))
-        self.__sr.shutdown() 
-
-    def __test_run(self):
-        print("thread:{} start..".format(self.__id))
-        callbacks = {
-                "RecognitionStarted":self.test_on_start,
-                "RecognitionResultChanged":self.test_on_result_chg,
-                "RecognitionCompleted":self.test_on_completed
-                }
-        self.__sr = nls.NlsCommonProto(
-                    url=URL,
-                    akid=AKID,
-                    aksecret=AKKEY,
-                    appkey=APPKEY,
-                    namespace="SpeechRecognizer",
-                    on_open=self.test_on_open,
-                    on_data=self.test_on_data,
-                    on_error=self.test_on_error,
-                    on_close=self.test_on_close,
-                    user_callback=callbacks,
-                    callback_args=[self.__id]
-                )
-
-        while True:
-            print("{}: session start".format(self.__id))
-            r = self.__sr.start(name="StartRecognition",
-                    payload={
-                        "format":"pcm",
-                        "sample_rate":16000
-                        }
-                    )
-            if not r:
-                print("start failed")
-                break
-            time.sleep(1)
-           
-            self.__slices = zip(*(iter(self.__data),) * 640)
-            for i in self.__slices:
-                self.__sr.send_binary(bytes(i))
-                time.sleep(0.01)
-
-            self.__sr.send_text(name="StopRecognition") 
-            time.sleep(5)
-
-def multiruntest(num=500):
-    for i in range(0, num):
-        name = "thread" + str(i)
-        t = TestSrCommon(name, "tests/test1.pcm")
-        t.start()
-
-nls.enableTrace(True)
-multiruntest(1)
-
-
diff --git a/tests/test_sr.py b/tests/test_sr.py
deleted file mode 100644
index 1c8d826..0000000
--- a/tests/test_sr.py
+++ /dev/null
@@ -1,80 +0,0 @@
-import time
-import threading
-import sys
-
-import nls
-
-
-URL="wss://nls-gateway.cn-shanghai.aliyuncs.com/ws/v1"
-AKID="Your AKID"
-AKKEY="Your AKSECRET"
-APPKEY="Your APPKEY"
-
-class TestSr:
-    def __init__(self, tid, test_file):
-        self.__th = threading.Thread(target=self.__test_run)
-        self.__id = tid
-        self.__test_file = test_file
-   
-    def loadfile(self, filename):
-        with open(filename, "rb") as f:
-            self.__data = f.read()
-    
-    def start(self):
-        self.loadfile(self.__test_file)
-        self.__th.start()
-
-    def test_on_start(self, message, *args):
-        print("test_on_start:{}".format(message))
-
-    def test_on_error(self, message, *args):
-        print("on_error args=>{}".format(args))
-
-    def test_on_close(self, *args):
-        print("on_close: args=>{}".format(args))
-
-    def test_on_result_chg(self, message, *args):
-        print("test_on_chg:{}".format(message))
-
-    def test_on_completed(self, message, *args):
-        print("on_completed:args=>{} message=>{}".format(args, message))
-
-
-    def __test_run(self):
-        print("thread:{} start..".format(self.__id))
-        
-        sr = nls.NlsSpeechRecognizer(
-                    url=URL,
-                    akid=AKID,
-                    aksecret=AKKEY,
-                    appkey=APPKEY,
-                    on_start=self.test_on_start,
-                    on_result_changed=self.test_on_result_chg,
-                    on_completed=self.test_on_completed,
-                    on_error=self.test_on_error,
-                    on_close=self.test_on_close,
-                    callback_args=[self.__id]
-                )
-        while True:
-            print("{}: session start".format(self.__id))
-            r = sr.start(aformat="pcm", ex={"hello":123})
-           
-            self.__slices = zip(*(iter(self.__data),) * 640)
-            for i in self.__slices:
-                sr.send_audio(bytes(i))
-                time.sleep(0.01)
-
-            r = sr.stop()
-            print("{}: sr stopped:{}".format(self.__id, r))
-            time.sleep(5)
-
-def multiruntest(num=500):
-    for i in range(0, num):
-        name = "thread" + str(i)
-        t = TestSr(name, "tests/test1.pcm")
-        t.start()
-
-nls.enableTrace(True)
-multiruntest(1)
-
-
diff --git a/tests/test_st.py b/tests/test_st.py
deleted file mode 100644
index 17a5bfb..0000000
--- a/tests/test_st.py
+++ /dev/null
@@ -1,93 +0,0 @@
-import time
-import threading
-import sys
-
-import nls
-
-
-URL="wss://nls-gateway.cn-shanghai.aliyuncs.com/ws/v1"
-AKID="Your AKID"
-AKKEY="Your AKSECRET"
-APPKEY="Your APPKEY"
-
-class TestSt:
-    def __init__(self, tid, test_file):
-        self.__th = threading.Thread(target=self.__test_run)
-        self.__id = tid
-        self.__test_file = test_file
-   
-    def loadfile(self, filename):
-        with open(filename, "rb") as f:
-            self.__data = f.read()
-    
-    def start(self):
-        self.loadfile(self.__test_file)
-        self.__th.start()
-
-    def test_on_sentence_begin(self, message, *args):
-        print("test_on_sentence_begin:{}".format(message))
-
-    def test_on_sentence_end(self, message, *args):
-        print("test_on_sentence_end:{}".format(message))
-
-    def test_on_start(self, message, *args):
-        print("test_on_start:{}".format(message))
-
-    def test_on_error(self, message, *args):
-        print("on_error args=>{}".format(args))
-
-    def test_on_close(self, *args):
-        print("on_close: args=>{}".format(args))
-
-    def test_on_result_chg(self, message, *args):
-        print("test_on_chg:{}".format(message))
-
-    def test_on_completed(self, message, *args):
-        print("on_completed:args=>{} message=>{}".format(args, message))
-
-
-    def __test_run(self):
-        print("thread:{} start..".format(self.__id))
-        sr = nls.NlsSpeechTranscriber(
-                    url=URL,
-                    akid=AKID,
-                    aksecret=AKKEY,
-                    appkey=APPKEY,
-                    on_sentence_begin=self.test_on_sentence_begin,
-                    on_sentence_end=self.test_on_sentence_end,
-                    on_start=self.test_on_start,
-                    on_result_changed=self.test_on_result_chg,
-                    on_completed=self.test_on_completed,
-                    on_error=self.test_on_error,
-                    on_close=self.test_on_close,
-                    callback_args=[self.__id]
-                )
-        while True:
-            print("{}: session start".format(self.__id))
-            r = sr.start(aformat="pcm",
-                    enable_intermediate_result=True,
-                    enable_punctutation_prediction=True,
-                    enable_inverse_text_normalization=True)
-
-            self.__slices = zip(*(iter(self.__data),) * 640)
-            for i in self.__slices:
-                sr.send_audio(bytes(i))
-                time.sleep(0.01)
-
-            sr.ctrl(ex={"test":"tttt"})
-            time.sleep(1)
-
-            r = sr.stop()
-            print("{}: sr stopped:{}".format(self.__id, r))
-            time.sleep(5)
-
-def multiruntest(num=500):
-    for i in range(0, num):
-        name = "thread" + str(i)
-        t = TestSt(name, "tests/test1.pcm")
-        t.start()
-
-nls.enableTrace(True)
-multiruntest(1)
-
-
diff --git a/tests/test_tts.pcm b/tests/test_tts.pcm
deleted file mode 100644
index d2033d0..0000000
Binary files a/tests/test_tts.pcm and /dev/null differ
diff --git a/tests/test_tts.py b/tests/test_tts.py
deleted file mode 100644
index c3dce4c..0000000
--- a/tests/test_tts.py
+++ /dev/null
@@ -1,79 +0,0 @@
-import time
-import threading
-import sys
-
-import nls
-
-URL="wss://nls-gateway.cn-shanghai.aliyuncs.com/ws/v1"
-AKID="Your AKID"
-AKKEY="Your AKSECRET"
-APPKEY="Your APPKEY"
-
-
-TEXT='大壮正想去摘取花瓣，谁知阿丽和阿强突然内讧，阿丽拿去手枪向树干边的阿强射击，两声枪响，阿强直接倒入水中'
-
-class TestTts:
-    def __init__(self, tid, test_file):
-        self.__th = threading.Thread(target=self.__test_run)
-        self.__id = tid
-        self.__test_file = test_file
-   
-    def start(self, text):
-        self.__text = text
-        self.__f = open(self.__test_file, "wb")
-        self.__th.start()
-    
-    def test_on_metainfo(self, message, *args):
-        print("on_metainfo message=>{}".format(message))  
-
-    def test_on_error(self, message, *args):
-        print("on_error args=>{}".format(args))
-
-    def test_on_close(self, *args):
-        print("on_close: args=>{}".format(args))
-        try:
-            self.__f.close()
-        except Exception as e:
-            print("close file failed since:", e)
-
-    def test_on_data(self, data, *args):
-        try:
-            self.__f.write(data)
-        except Exception as e:
-            print("write data failed:", e)
-
-    def test_on_completed(self, message, *args):
-        print("on_completed:args=>{} message=>{}".format(args, message))
-
-
-    def __test_run(self):
-        print("thread:{} start..".format(self.__id))
-        tts = nls.NlsSpeechSynthesizer(
-                    url=URL,
-                    akid=AKID,
-                    aksecret=AKKEY,
-                    appkey=APPKEY,
-                    on_metainfo=self.test_on_metainfo,
-                    on_data=self.test_on_data,
-                    on_completed=self.test_on_completed,
-                    on_error=self.test_on_error,
-                    on_close=self.test_on_close,
-                    callback_args=[self.__id]
-                )
-
-        while True:
-            print("{}: session start".format(self.__id))
-            r = tts.start(self.__text, voice="ailun")
-            print("{}: tts done with result:{}".format(self.__id, r))
-            time.sleep(5)
-
-def multiruntest(num=500):
-    for i in range(0, num):
-        name = "thread" + str(i)
-        t = TestTts(name, "tests/test_tts.pcm")
-        t.start(TEXT)
-
-nls.enableTrace(True)
-multiruntest(1)
-
-
diff --git a/tests/test_wav2pcm.py b/tests/test_wav2pcm.py
deleted file mode 100644
index 0698e45..0000000
--- a/tests/test_wav2pcm.py
+++ /dev/null
@@ -1,21 +0,0 @@
-from struct import *
-
-__all__=["wav2pcm"]
-
-def wav2pcm(wavfile, pcmfile):
-    with open(wavfile, "rb") as i, open(pcmfile, "wb") as o:
-        i.seek(0)
-        _id = i.read(4)
-        _id = unpack('>I', _id)
-        _size = i.read(4)
-        _size = unpack('<I', _size)
-        print("size={}".format(_size))
-        _type = i.read(4)
-        _type = unpack(">I", _type)
-        if _id[0] != 0x52494646 or _type[0] != 0x57415645:
-            raise ValueError("not a wav!")
-        i.read(32)
-        result = i.read()
-        o.write(result)
-
-wav2pcm("tests/tts_test.wav", "tests/tts_test.pcm")
diff --git a/tests/tts_test.pcm b/tests/tts_test.pcm
deleted file mode 100644
index 560557d..0000000
Binary files a/tests/tts_test.pcm and /dev/null differ
diff --git a/tests/tts_test.wav b/tests/tts_test.wav
deleted file mode 100644
index b4862b6..0000000
Binary files a/tests/tts_test.wav and /dev/null differ