bisocket 0.0.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
bisocket-0.0.1/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Daniel Olson
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,3 @@
1
+ include README.md
2
+ include LICENSE
3
+ include requirements.txt
@@ -0,0 +1,234 @@
1
+ Metadata-Version: 2.4
2
+ Name: bisocket
3
+ Version: 0.0.1
4
+ Summary: bisocket is a high-level Python library for simple, secure, and truly bidirectional socket communication, using a dual-socket architecture to enable non-blocking, full-duplex I/O. It provides automatic AES-GCM encryption and supports both synchronous (threading) and asynchronous (asyncio) client-server applications
5
+ Home-page:
6
+ Author: Daniel Olson
7
+ Author-email: daniel@orphos.cloud
8
+ Keywords: socket bidirectional
9
+ Classifier: Development Status :: 3 - Alpha
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Requires-Python: >=3.10
18
+ Description-Content-Type: text/markdown
19
+ License-File: LICENSE
20
+ Requires-Dist: cryptography
21
+ Dynamic: author
22
+ Dynamic: author-email
23
+ Dynamic: classifier
24
+ Dynamic: description
25
+ Dynamic: description-content-type
26
+ Dynamic: keywords
27
+ Dynamic: license-file
28
+ Dynamic: requires-dist
29
+ Dynamic: requires-python
30
+ Dynamic: summary
31
+
32
+ # `bisocket`: Simple, Secure, Bidirectional Python Sockets
33
+
34
+ [](https://www.google.com/search?q=https://badge.fury.io/py/bisocket)
35
+ [](https://opensource.org/licenses/MIT)
36
+
37
+ `bisocket` is a high-level Python library that simplifies bidirectional (two-way) communication over sockets. It provides a robust framework for building client-server applications that require sending and receiving data simultaneously without blocking.
38
+
39
+ It comes with built-in **AES-GCM end-to-end encryption** and **bz2 compression**, ensuring your data is secure and transmitted efficiently. The library offers both synchronous (threading-based) and asynchronous (`asyncio`) APIs, making it versatile for various application architectures.
40
+
41
+ -----
42
+
43
+ ## ✨ Features
44
+
45
+ - **True Bidirectional Communication**: Uses separate sockets for sending and receiving, enabling non-blocking, full-duplex communication.
46
+ - **End-to-End Encryption**: Automatic AES-GCM encryption for all messages ensures data privacy and integrity.
47
+ - **Data Compression**: Automatic `bz2` compression reduces bandwidth usage for large payloads.
48
+ - **Sync & Async Support**: Provides both a standard threading API and a modern `asyncio` API.
49
+ - **Simple Handler-Based API**: Use a clean handler function on the server and an `on_receive` callback on the client to process messages.
50
+ - **Unique Client Identification**: Manages clients using unique UUIDs, making it easy to track connections.
51
+
52
+ -----
53
+
54
+ ## βš™οΈ Installation
55
+
56
+ Install `bisocket` directly from PyPI:
57
+
58
+ ```bash
59
+ pip install bisocket
60
+ ```
61
+
62
+ The only dependency is the `cryptography` library for encryption.
63
+
64
+ -----
65
+
66
+ ## πŸš€ Quick Start
67
+
68
+ Here’s a simple echo client and server to get you started.
69
+
70
+ ### 1\. Set the Encryption Key
71
+
72
+ For security, `bisocket` requires an encryption key. Set it as an environment variable. If it's not set, the library will use a default, **insecure** key suitable only for testing.
73
+
74
+ ```bash
75
+ export CRYPTO_KEY='your-super-secret-and-long-encryption-key'
76
+ ```
77
+
78
+ ### 2\. Synchronous Example
79
+
80
+ #### Server (`server.py`)
81
+
82
+ ```python
83
+ from bisocket import Server, ServerRequest
84
+
85
+ # Define a handler to process incoming requests.
86
+ def handler(request: ServerRequest):
87
+ print(f"Received method '{request.method}' with data: {request.data.decode()}")
88
+
89
+ if request.method == 'echo':
90
+ # Send the received data back to the client.
91
+ request.send_data(request.data)
92
+ elif request.method == 'ping':
93
+ request.send_data(b'pong')
94
+
95
+ # Create and start the server.
96
+ if __name__ == "__main__":
97
+ server = Server(host='127.0.0.1', port=65432, handler=handler)
98
+ print("Starting synchronous server on port 65432...")
99
+ server.start()
100
+ ```
101
+
102
+ #### Client (`client.py`)
103
+
104
+ ```python
105
+ import time
106
+ from bisocket import Client, Message
107
+
108
+ # Define a callback to handle messages from the server.
109
+ def on_receive(msg: Message):
110
+ print(f"Received response for request ID {msg.request_id}: {msg.data.decode()}")
111
+
112
+ # Use the Client as a context manager for clean connection handling.
113
+ with Client(host='127.0.0.1', port=65432, on_receive=on_receive) as client:
114
+ print("Client connected.")
115
+
116
+ # Send an 'echo' request.
117
+ request_id_1 = client.send('echo', b'Hello, World!')
118
+ print(f"Sent 'echo' request with ID: {request_id_1}")
119
+
120
+ time.sleep(1) # Wait for the response.
121
+
122
+ # Send a 'ping' request.
123
+ request_id_2 = client.send('ping', b'')
124
+ print(f"Sent 'ping' request with ID: {request_id_2}")
125
+
126
+ time.sleep(2) # Give time for messages to be processed before exiting.
127
+
128
+ print("Client disconnected.")
129
+ ```
130
+
131
+ -----
132
+
133
+ ### 3\. Asynchronous Example
134
+
135
+ #### Async Server (`async_server.py`)
136
+
137
+ ```python
138
+ import asyncio
139
+ from bisocket import Server, ServerRequest
140
+
141
+ # Define an async handler for non-blocking operations.
142
+ async def ahandler(request: ServerRequest):
143
+ print(f"Received method '{request.method}' with data: {request.data.decode()}")
144
+
145
+ if request.method == 'echo':
146
+ await asyncio.sleep(0.5) # Simulate I/O-bound work.
147
+ request.send_data(request.data)
148
+
149
+ # Create and run the async server.
150
+ async def main():
151
+ server = Server(host='127.0.0.1', port=65432, handler=ahandler)
152
+ print("Starting asynchronous server on port 65432...")
153
+ await server.astart()
154
+
155
+ if __name__ == "__main__":
156
+ try:
157
+ asyncio.run(main())
158
+ except KeyboardInterrupt:
159
+ print("Server shutting down.")
160
+ ```
161
+
162
+ #### Async Client (`async_client.py`)
163
+
164
+ ```python
165
+ import asyncio
166
+ from bisocket import Client, Message
167
+
168
+ # Define an async callback to process server messages.
169
+ async def aon_receive(msg: Message):
170
+ print(f"Received response for request ID {msg.request_id}: {msg.data.decode()}")
171
+
172
+ async def main():
173
+ # Use the async context manager for the client.
174
+ async with Client(host='127.0.0.1', port=65432, on_receive=aon_receive) as client:
175
+ print("Async client connected.")
176
+
177
+ # Send multiple requests concurrently.
178
+ tasks = [
179
+ client.asend('echo', b'First async message'),
180
+ client.asend('echo', b'Second async message')
181
+ ]
182
+ request_ids = await asyncio.gather(*tasks)
183
+ print(f"Sent requests with IDs: {request_ids}")
184
+
185
+ await asyncio.sleep(2) # Keep client running to receive responses.
186
+
187
+ if __name__ == "__main__":
188
+ asyncio.run(main())
189
+ ```
190
+
191
+ -----
192
+
193
+ ## 🧠 How It Works
194
+
195
+ Traditional socket programming can be tricky when you need to send and receive data at the same time, often leading to blocking calls or complex multiplexing.
196
+
197
+ `bisocket` simplifies this by establishing **two separate socket connections** for each client:
198
+
199
+ 1. **Send Socket**: The client uses this connection exclusively to send data *to* the server.
200
+ 2. **Receive Socket**: The client uses this connection exclusively to receive data *from* the server.
201
+
202
+ This architecture allows the client and server to communicate in full-duplex mode without one operation blocking the other. The library manages these connections, message framing, encryption, and compression internally, so you can focus on your application logic.
203
+
204
+ - **On the Client**: The `Client` runs a background thread (or `asyncio` task) to listen for incoming messages on the receive socket. These messages are passed to your `on_receive` callback.
205
+ - **On the Server**: The `Server` manages a pool of client connections. It receives a request from a client's "send" socket, processes it in your handler, and then queues the response to be sent back via that same client's "receive" socket.
206
+
207
+ -----
208
+
209
+ ## πŸ” Security
210
+
211
+ All data transmitted by `bisocket` is encrypted using **AES-256-GCM**, an authenticated encryption scheme that provides confidentiality and integrity. The 256-bit encryption key is derived from the string you provide via the `CRYPTO_KEY` environment variable using SHA-256.
212
+
213
+ **⚠️ It is crucial to set a strong, unique secret key for your application.**
214
+
215
+ You can generate a cryptographically secure key using OpenSSL:
216
+
217
+ ```bash
218
+ # This command generates a 32-byte (256-bit) random key in hex format.
219
+ export CRYPTO_KEY=$(openssl rand -hex 32)
220
+ ```
221
+
222
+ If `CRYPTO_KEY` is not set, a default, insecure key (`'secret-lol'`) is used, and a warning is printed. This is intended **only for local testing and development**.
223
+
224
+ -----
225
+
226
+ ## πŸ“„ License
227
+
228
+ This project is licensed under the MIT License. See the `LICENSE` file for details.
229
+
230
+ -----
231
+
232
+ ## πŸ™ Contributing
233
+
234
+ Contributions are welcome\! Please feel free to submit a pull request or open an issue to discuss new features or bugs.
@@ -0,0 +1,203 @@
1
+ # `bisocket`: Simple, Secure, Bidirectional Python Sockets
2
+
3
+ [](https://www.google.com/search?q=https://badge.fury.io/py/bisocket)
4
+ [](https://opensource.org/licenses/MIT)
5
+
6
+ `bisocket` is a high-level Python library that simplifies bidirectional (two-way) communication over sockets. It provides a robust framework for building client-server applications that require sending and receiving data simultaneously without blocking.
7
+
8
+ It comes with built-in **AES-GCM end-to-end encryption** and **bz2 compression**, ensuring your data is secure and transmitted efficiently. The library offers both synchronous (threading-based) and asynchronous (`asyncio`) APIs, making it versatile for various application architectures.
9
+
10
+ -----
11
+
12
+ ## ✨ Features
13
+
14
+ - **True Bidirectional Communication**: Uses separate sockets for sending and receiving, enabling non-blocking, full-duplex communication.
15
+ - **End-to-End Encryption**: Automatic AES-GCM encryption for all messages ensures data privacy and integrity.
16
+ - **Data Compression**: Automatic `bz2` compression reduces bandwidth usage for large payloads.
17
+ - **Sync & Async Support**: Provides both a standard threading API and a modern `asyncio` API.
18
+ - **Simple Handler-Based API**: Use a clean handler function on the server and an `on_receive` callback on the client to process messages.
19
+ - **Unique Client Identification**: Manages clients using unique UUIDs, making it easy to track connections.
20
+
21
+ -----
22
+
23
+ ## βš™οΈ Installation
24
+
25
+ Install `bisocket` directly from PyPI:
26
+
27
+ ```bash
28
+ pip install bisocket
29
+ ```
30
+
31
+ The only dependency is the `cryptography` library for encryption.
32
+
33
+ -----
34
+
35
+ ## πŸš€ Quick Start
36
+
37
+ Here’s a simple echo client and server to get you started.
38
+
39
+ ### 1\. Set the Encryption Key
40
+
41
+ For security, `bisocket` requires an encryption key. Set it as an environment variable. If it's not set, the library will use a default, **insecure** key suitable only for testing.
42
+
43
+ ```bash
44
+ export CRYPTO_KEY='your-super-secret-and-long-encryption-key'
45
+ ```
46
+
47
+ ### 2\. Synchronous Example
48
+
49
+ #### Server (`server.py`)
50
+
51
+ ```python
52
+ from bisocket import Server, ServerRequest
53
+
54
+ # Define a handler to process incoming requests.
55
+ def handler(request: ServerRequest):
56
+ print(f"Received method '{request.method}' with data: {request.data.decode()}")
57
+
58
+ if request.method == 'echo':
59
+ # Send the received data back to the client.
60
+ request.send_data(request.data)
61
+ elif request.method == 'ping':
62
+ request.send_data(b'pong')
63
+
64
+ # Create and start the server.
65
+ if __name__ == "__main__":
66
+ server = Server(host='127.0.0.1', port=65432, handler=handler)
67
+ print("Starting synchronous server on port 65432...")
68
+ server.start()
69
+ ```
70
+
71
+ #### Client (`client.py`)
72
+
73
+ ```python
74
+ import time
75
+ from bisocket import Client, Message
76
+
77
+ # Define a callback to handle messages from the server.
78
+ def on_receive(msg: Message):
79
+ print(f"Received response for request ID {msg.request_id}: {msg.data.decode()}")
80
+
81
+ # Use the Client as a context manager for clean connection handling.
82
+ with Client(host='127.0.0.1', port=65432, on_receive=on_receive) as client:
83
+ print("Client connected.")
84
+
85
+ # Send an 'echo' request.
86
+ request_id_1 = client.send('echo', b'Hello, World!')
87
+ print(f"Sent 'echo' request with ID: {request_id_1}")
88
+
89
+ time.sleep(1) # Wait for the response.
90
+
91
+ # Send a 'ping' request.
92
+ request_id_2 = client.send('ping', b'')
93
+ print(f"Sent 'ping' request with ID: {request_id_2}")
94
+
95
+ time.sleep(2) # Give time for messages to be processed before exiting.
96
+
97
+ print("Client disconnected.")
98
+ ```
99
+
100
+ -----
101
+
102
+ ### 3\. Asynchronous Example
103
+
104
+ #### Async Server (`async_server.py`)
105
+
106
+ ```python
107
+ import asyncio
108
+ from bisocket import Server, ServerRequest
109
+
110
+ # Define an async handler for non-blocking operations.
111
+ async def ahandler(request: ServerRequest):
112
+ print(f"Received method '{request.method}' with data: {request.data.decode()}")
113
+
114
+ if request.method == 'echo':
115
+ await asyncio.sleep(0.5) # Simulate I/O-bound work.
116
+ request.send_data(request.data)
117
+
118
+ # Create and run the async server.
119
+ async def main():
120
+ server = Server(host='127.0.0.1', port=65432, handler=ahandler)
121
+ print("Starting asynchronous server on port 65432...")
122
+ await server.astart()
123
+
124
+ if __name__ == "__main__":
125
+ try:
126
+ asyncio.run(main())
127
+ except KeyboardInterrupt:
128
+ print("Server shutting down.")
129
+ ```
130
+
131
+ #### Async Client (`async_client.py`)
132
+
133
+ ```python
134
+ import asyncio
135
+ from bisocket import Client, Message
136
+
137
+ # Define an async callback to process server messages.
138
+ async def aon_receive(msg: Message):
139
+ print(f"Received response for request ID {msg.request_id}: {msg.data.decode()}")
140
+
141
+ async def main():
142
+ # Use the async context manager for the client.
143
+ async with Client(host='127.0.0.1', port=65432, on_receive=aon_receive) as client:
144
+ print("Async client connected.")
145
+
146
+ # Send multiple requests concurrently.
147
+ tasks = [
148
+ client.asend('echo', b'First async message'),
149
+ client.asend('echo', b'Second async message')
150
+ ]
151
+ request_ids = await asyncio.gather(*tasks)
152
+ print(f"Sent requests with IDs: {request_ids}")
153
+
154
+ await asyncio.sleep(2) # Keep client running to receive responses.
155
+
156
+ if __name__ == "__main__":
157
+ asyncio.run(main())
158
+ ```
159
+
160
+ -----
161
+
162
+ ## 🧠 How It Works
163
+
164
+ Traditional socket programming can be tricky when you need to send and receive data at the same time, often leading to blocking calls or complex multiplexing.
165
+
166
+ `bisocket` simplifies this by establishing **two separate socket connections** for each client:
167
+
168
+ 1. **Send Socket**: The client uses this connection exclusively to send data *to* the server.
169
+ 2. **Receive Socket**: The client uses this connection exclusively to receive data *from* the server.
170
+
171
+ This architecture allows the client and server to communicate in full-duplex mode without one operation blocking the other. The library manages these connections, message framing, encryption, and compression internally, so you can focus on your application logic.
172
+
173
+ - **On the Client**: The `Client` runs a background thread (or `asyncio` task) to listen for incoming messages on the receive socket. These messages are passed to your `on_receive` callback.
174
+ - **On the Server**: The `Server` manages a pool of client connections. It receives a request from a client's "send" socket, processes it in your handler, and then queues the response to be sent back via that same client's "receive" socket.
175
+
176
+ -----
177
+
178
+ ## πŸ” Security
179
+
180
+ All data transmitted by `bisocket` is encrypted using **AES-256-GCM**, an authenticated encryption scheme that provides confidentiality and integrity. The 256-bit encryption key is derived from the string you provide via the `CRYPTO_KEY` environment variable using SHA-256.
181
+
182
+ **⚠️ It is crucial to set a strong, unique secret key for your application.**
183
+
184
+ You can generate a cryptographically secure key using OpenSSL:
185
+
186
+ ```bash
187
+ # This command generates a 32-byte (256-bit) random key in hex format.
188
+ export CRYPTO_KEY=$(openssl rand -hex 32)
189
+ ```
190
+
191
+ If `CRYPTO_KEY` is not set, a default, insecure key (`'secret-lol'`) is used, and a warning is printed. This is intended **only for local testing and development**.
192
+
193
+ -----
194
+
195
+ ## πŸ“„ License
196
+
197
+ This project is licensed under the MIT License. See the `LICENSE` file for details.
198
+
199
+ -----
200
+
201
+ ## πŸ™ Contributing
202
+
203
+ Contributions are welcome\! Please feel free to submit a pull request or open an issue to discuss new features or bugs.
@@ -0,0 +1,15 @@
1
+ # Import modules from subpackages
2
+ try:
3
+ from .cython import c_main as main
4
+ except:
5
+ from . import main
6
+
7
+ try:
8
+ from .cython.c_main import Client, Server, Message, ServerRequest, server_handler_example
9
+ except:
10
+ from .main import Client, Server, Message, ServerRequest, server_handler_example
11
+
12
+ # Define the public API
13
+ __all__ = [
14
+ 'main',
15
+ ]