Porównaj commity

...

11 Commity

Autor SHA1 Wiadomość Data
Carlos Sousa 2ed7ec1403
add all examples to docs (#222) 2025-05-28 16:21:14 -07:00
nazo b370876074
fix #218 (#219) 2025-05-22 16:11:14 -07:00
Ciro Spaciari 464804edef
Update README.md (#205) 2024-11-05 12:26:02 -08:00
Ciro Spaciari d2f26c682c fix wsgi 2024-10-29 12:13:23 -07:00
Kian-Meng Ang 328e249c2c
fix typos again (#203)
Found via `codespell -H`
2024-10-27 12:27:07 -07:00
Ciro Spaciari d73ed582d5 bump + fix asgi 2024-10-23 11:22:29 -07:00
Carlos Sousa 226ae66f6d
fix high cpu consumption in idle mode, update idle_relaxation_time back to 0.01 (#198) 2024-10-11 12:51:52 -07:00
Ciro Spaciari 63b1546bbb
opsie 2024-07-23 08:53:24 -07:00
Ciro Spaciari 90b08b55d3
bump + add get_cookies 2024-07-17 06:29:41 -07:00
Ciro Spaciari 2f9c267e01
tls only with cert_file_name for now 2024-07-13 18:40:57 -07:00
Ciro Spaciari 30cec004e0
fix WSGI query starting with ? 2024-07-13 15:42:07 -07:00
16 zmienionych plików z 106 dodań i 170 usunięć

Wyświetl plik

@ -4,11 +4,6 @@
<a href="https://github.com/cirospaciari/socketify.py"><img src="https://raw.githubusercontent.com/cirospaciari/socketify.py/main/misc/logo.png" alt="Logo" height=170></a>
<br />
<br />
<a href="https://github.com/cirospaciari/socketify.py/actions/workflows/linux.yml" target="_blank"><img src="https://github.com/cirospaciari/socketify.py/actions/workflows/linux.yml/badge.svg" /></a>
<a href="https://github.com/cirospaciari/socketify.py/actions/workflows/windows.yml" target="_blank"><img src="https://github.com/cirospaciari/socketify.py/actions/workflows/windows.yml/badge.svg" /></a>
<a href="https://github.com/cirospaciari/socketify.py/actions/workflows/macos.yml" target="_blank"><img src="https://github.com/cirospaciari/socketify.py/actions/workflows/macos.yml/badge.svg" /></a>
<a href="https://github.com/cirospaciari/socketify.py/actions/workflows/macos_arm64.yml" target="_blank"><img src="https://github.com/cirospaciari/socketify.py/actions/workflows/macos_arm64.yml/badge.svg" /></a>
<br/>
<a href='https://github.com/cirospaciari/socketify.py'><img alt='GitHub Clones' src='https://img.shields.io/badge/dynamic/json?color=success&label=Clones&query=count&url=https://gist.githubusercontent.com/cirospaciari/2243d59951f4abe4fd2000f1e20bc561/raw/clone.json&logo=github'></a>
<a href='https://pypi.org/project/socketify/' target="_blank"><img alt='PyPI Downloads' src='https://static.pepy.tech/personalized-badge/socketify?period=total&units=international_system&left_color=grey&right_color=brightgreen&left_text=Downloads'></a>
<a href="https://github.com/sponsors/cirospaciari/" target="_blank"><img src="https://img.shields.io/static/v1?label=Sponsor&message=%E2%9D%A4&logo=GitHub&link=https://github.com/sponsors/cirospaciari"/></a>

Wyświetl plik

@ -15,7 +15,7 @@ class SSGIHttpResponse:
# send chunk of data, can be used to perform with less backpressure than using send
# total_size is the sum of all lengths in bytes of all chunks to be sended
# connection will end when total_size is met
# returns tuple(bool, bool) first bool represents if the chunk is succefully sended, the second if the connection has ended
# returns tuple(bool, bool) first bool represents if the chunk is successfully sended, the second if the connection has ended
def send_chunk(self, chunk: Union[str, bytes, bytearray, memoryview], total_size: int = False) -> Awaitable:
pass

Wyświetl plik

@ -64,7 +64,7 @@ if __name__ == "__main__":
# run_app() # run app on the main process too :)
# # sigint everything to gracefull shutdown
# # sigint everything to graceful shutdown
# import signal
# for pid in pid_list:
# os.kill(pid, signal.SIGINT)

Wyświetl plik

@ -30,7 +30,7 @@ Options:
--ws-close-on-backpressure-limit BOOLEAN Close connections that hits maximum backpressure [default: False]
--lifespan [auto|on|off] Lifespan implementation. [default: auto]
--interface [auto|asgi|asgi3|wsgi|ssgi|socketify] Select ASGI (same as ASGI3), ASGI3, WSGI or SSGI as the application interface. [default: auto]
--disable-listen-log BOOLEAN Disable log when start listenning [default: False]
--disable-listen-log BOOLEAN Disable log when start listening [default: False]
--version or -v Display the socketify.py version and exit.
--ssl-keyfile TEXT SSL key file
--ssl-certfile TEXT SSL certificate file

Wyświetl plik

@ -1,158 +1,81 @@
# Examples
## 📚 Examples
Middleware
```python
from socketify import App, middleware
All examples are located in the [`examples`](https://github.com/cirospaciari/socketify.py/tree/main/examples) directory.
### 🚀 Getting Started
async def get_user(authorization):
if authorization:
# you can do something async here
return {"greeting": "Hello, World"}
return None
- [`hello_world.py`](https://github.com/cirospaciari/socketify.py/tree/main/examples/hello_world.py) - Basic HTTP server setup
- [`hello_world_cli.py`](https://github.com/cirospaciari/socketify.py/tree/main/examples/hello_world_cli.py) - Command-line interface example
- [`hello_world_cli_ws.py`](https://github.com/cirospaciari/socketify.py/tree/main/examples/hello_world_cli_ws.py) - CLI with WebSocket support
- [`hello_world_unix_domain.py`](https://github.com/cirospaciari/socketify.py/tree/main/examples/hello_world_unix_domain.py) - Unix domain socket example
### 🔒 Security & HTTPS
async def auth(res, req, data=None):
user = await get_user(req.get_header("authorization"))
if not user:
res.write_status(403).end("not authorized")
# returning Falsy in middlewares just stop the execution of the next middleware
return False
- [`https.py`](https://github.com/cirospaciari/socketify.py/tree/main/examples/https.py) - HTTPS server with SSL/TLS configuration
# returns extra data
return user
### 🌐 WebSocket Examples
- [`websockets.py`](https://github.com/cirospaciari/socketify.py/tree/main/examples/websockets.py) - Basic WebSocket implementation
- [`ws_close_connection.py`](https://github.com/cirospaciari/socketify.py/tree/main/examples/ws_close_connection.py) - WebSocket connection management
- [`chat/`](https://github.com/cirospaciari/socketify.py/tree/main/examples/chat) - Real-time chat application
- [`broadcast.py`](https://github.com/cirospaciari/socketify.py/tree/main/examples/broadcast.py) - Broadcasting messages to multiple clients
- [`backpressure.py`](https://github.com/cirospaciari/socketify.py/tree/main/examples/backpressure.py) - Handling WebSocket backpressure
def another_middie(res, req, data=None):
# now we can mix sync and async and change the data here
if isinstance(data, dict):
gretting = data.get("greeting", "")
data["greeting"] = f"{gretting} from another middie ;)"
return data
### ⚙️ Middleware & Routing
- [`middleware.py`](https://github.com/cirospaciari/socketify.py/tree/main/examples/middleware.py) - Basic middleware implementation
- [`middleware_async.py`](https://github.com/cirospaciari/socketify.py/tree/main/examples/middleware_async.py) - Asynchronous middleware
- [`middleware_sync.py`](https://github.com/cirospaciari/socketify.py/tree/main/examples/middleware_sync.py) - Synchronous middleware
- [`middleware_router.py`](https://github.com/cirospaciari/socketify.py/tree/main/examples/middleware_router.py) - Router-based middleware
- [`router_and_basics.py`](https://github.com/cirospaciari/socketify.py/tree/main/examples/router_and_basics.py) - Routing fundamentals
def home(res, req, user=None):
res.cork_end(user.get("greeting", None))
### 🔄 Async/Sync Programming
- [`async.py`](https://github.com/cirospaciari/socketify.py/tree/main/examples/async.py) - Asynchronous request handling
- [`upgrade.py`](https://github.com/cirospaciari/socketify.py/tree/main/examples/upgrade.py) - Protocol upgrade examples
- [`upgrade_async.py`](https://github.com/cirospaciari/socketify.py/tree/main/examples/upgrade_async.py) - Asynchronous protocol upgrades
app = App()
app.get("/", middleware(auth, another_middie, home))
app.listen(
3000,
lambda config: print("Listening on port http://localhost:%d now\n" % config.port),
)
app.run()
### 📁 File Handling & Static Content
# You can also take a loop on MiddlewareRouter in middleware_router.py ;)
```
- [`static_files.py`](https://github.com/cirospaciari/socketify.py/tree/main/examples/static_files.py) - Serving static files
- [`file_stream.py`](https://github.com/cirospaciari/socketify.py/tree/main/examples/file_stream.py) - File streaming capabilities
- [`upload_or_post.py`](https://github.com/cirospaciari/socketify.py/tree/main/examples/upload_or_post.py) - File uploads and POST data handling
Broadcast
```python
from socketify import App, AppOptions, OpCode, CompressOptions
### 🎨 Template Engines
- [`template_jinja2.py`](https://github.com/cirospaciari/socketify.py/tree/main/examples/template_jinja2.py) - Jinja2 template integration
- [`template_mako.py`](https://github.com/cirospaciari/socketify.py/tree/main/examples/template_mako.py) - Mako template integration
- [`templates/`](https://github.com/cirospaciari/socketify.py/tree/main/examples/templates) - Template examples and resources
def ws_open(ws):
print("A WebSocket got connected!")
# Let this client listen to topic "broadcast"
ws.subscribe("broadcast")
### 🛠️ Advanced Features
- [`custom_json_serializer.py`](https://github.com/cirospaciari/socketify.py/tree/main/examples/custom_json_serializer.py) - Custom JSON serialization
- [`http_request_cache.py`](https://github.com/cirospaciari/socketify.py/tree/main/examples/http_request_cache.py) - HTTP request caching
- [`proxy.py`](https://github.com/cirospaciari/socketify.py/tree/main/examples/proxy.py) - Proxy server implementation
- [`automatic_port_selection.py`](https://github.com/cirospaciari/socketify.py/tree/main/examples/automatic_port_selection.py) - Dynamic port selection
def ws_message(ws, message, opcode):
# Broadcast this message
ws.publish("broadcast", message, opcode)
### 🔧 Server Configuration
app = App()
app.ws(
"/*",
{
"compression": CompressOptions.SHARED_COMPRESSOR,
"max_payload_length": 16 * 1024 * 1024,
"idle_timeout": 60,
"open": ws_open,
"message": ws_message,
# The library guarantees proper unsubscription at close
"close": lambda ws, code, message: print("WebSocket closed"),
"subscription": lambda ws, topic, subscriptions, subscriptions_before: print(f'subscription/unsubscription on topic {topic} {subscriptions} {subscriptions_before}'),
},
)
app.any("/", lambda res, req: res.end("Nothing to see here!"))
app.listen(
3000,
lambda config: print("Listening on port http://localhost:%d now\n" % (config.port)),
)
app.run()
```
- [`listen_options.py`](https://github.com/cirospaciari/socketify.py/tree/main/examples/listen_options.py) - Server listening options
- [`graceful_shutdown.py`](https://github.com/cirospaciari/socketify.py/tree/main/examples/graceful_shutdown.py) - Graceful server shutdown
- [`forks.py`](https://github.com/cirospaciari/socketify.py/tree/main/examples/forks.py) - Multi-process server setup
HTTPS
```python
from socketify import App, AppOptions
### 📊 GraphQL Integration
app = App(
AppOptions(
key_file_name="./misc/key.pem",
cert_file_name="./misc/cert.pem",
passphrase="1234",
)
)
app.get("/", lambda res, req: res.end("Hello World socketify from Python!"))
app.listen(
54321,
lambda config: print("Listening on port https://localhost:%d now\n" % config.port),
)
app.run()
- [`graphiql.py`](https://github.com/cirospaciari/socketify.py/tree/main/examples/graphiql.py) - GraphiQL interface setup
- [`graphiql_raw.py`](https://github.com/cirospaciari/socketify.py/tree/main/examples/graphiql_raw.py) - Raw GraphQL implementation
# mkdir misc
# openssl req -newkey rsa:2048 -new -nodes -x509 -days 3650 -passout pass:1234 -keyout ./misc/key.pem -out ./misc/cert.pem
```
### 🐳 Development & Deployment
Backpressure
```python
from socketify import App, AppOptions, OpCode, CompressOptions
- [`docker/`](https://github.com/cirospaciari/socketify.py/tree/main/examples/docker) - Docker containerization examples
- [`requirements.txt`](https://github.com/cirospaciari/socketify.py/tree/main/examples/requirements.txt) - Example dependencies
# Number between ok and not ok
backpressure = 1024
### 🛡️ Error Handling & Logging
# Used for statistics
messages = 0
message_number = 0
- [`error_handler.py`](https://github.com/cirospaciari/socketify.py/tree/main/examples/error_handler.py) - Error handling strategies
- [`better_logging.py`](https://github.com/cirospaciari/socketify.py/tree/main/examples/better_logging.py) - Advanced logging setup
- [`not_found.py`](https://github.com/cirospaciari/socketify.py/tree/main/examples/not_found.py) - Custom 404 error pages
### 🔨 Utilities & Helpers
def ws_open(ws):
print("A WebSocket got connected!")
# We begin our example by sending until we have backpressure
global message_number
global messages
while ws.get_buffered_amount() < backpressure:
ws.send("This is a message, let's call it %i" % message_number)
message_number = message_number + 1
messages = messages + 1
def ws_drain(ws):
# Continue sending when we have drained (some)
global message_number
global messages
while ws.get_buffered_amount() < backpressure:
ws.send("This is a message, let's call it %i" % message_number)
message_number = message_number + 1
messages = messages + 1
app = App()
app.ws(
"/*",
{
"compression": CompressOptions.DISABLED,
"max_payload_length": 16 * 1024 * 1024,
"idle_timeout": 60,
"open": ws_open,
"drain": ws_drain,
},
)
app.any("/", lambda res, req: res.end("Nothing to see here!"))
app.listen(
3000,
lambda config: print("Listening on port http://localhost:%d now\n" % (config.port)),
)
app.run()
```
- [`helpers/`](https://github.com/cirospaciari/socketify.py/tree/main/examples/helpers) - Utility functions and helper modules

Wyświetl plik

@ -27,7 +27,7 @@ for _ in range(1, multiprocessing.cpu_count()):
run_app() # run app on the main process too :)
# sigint everything to gracefull shutdown
# sigint everything to graceful shutdown
import signal
for pid in pid_list:
os.kill(pid, signal.SIGINT)

Wyświetl plik

@ -6,7 +6,7 @@ from socketify import App
def middleware(*functions):
def middleware_route(res, req):
data = None
# cicle to all middlewares
# circle to all middlewares
for function in functions:
# call middlewares
data = function(res, req, data)

Wyświetl plik

@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "socketify"
version = "0.0.28"
version = "0.0.31"
dynamic = ["dependencies"]
authors = [
{ name="Ciro Spaciari", email="ciro.spaciari@gmail.com" },

Wyświetl plik

@ -58,7 +58,7 @@ with open("README.md", "r", encoding="utf-8") as fh:
setuptools.setup(
name="socketify",
version="0.0.28",
version="0.0.31",
platforms=["any"],
author="Ciro Spaciari",
author_email="ciro.spaciari@gmail.com",

Wyświetl plik

@ -107,7 +107,7 @@ def ws_upgrade(ssl, response, info, socket_context, user_data):
"root_path": "",
"path": url.decode("utf8"),
"raw_path": url,
"query_string": ffi.unpack(info.query_string, info.query_string_size),
"query_string": ffi.unpack(info.query_string, info.query_string_size)[1:],
"headers": headers,
"subprotocols": [protocol] if protocol else [],
"extensions": {
@ -471,7 +471,7 @@ def asgi(ssl, response, info, user_data):
"root_path": "",
"path": url.decode("utf8"),
"raw_path": url,
"query_string": ffi.unpack(info.query_string, info.query_string_size),
"query_string": ffi.unpack(info.query_string, info.query_string_size)[1:],
"headers": headers,
}
@ -573,8 +573,8 @@ class _ASGI:
self.server = App(options, task_factory_max_items=0)
self.SERVER_PORT = None
self.SERVER_HOST = ""
self.SERVER_SCHEME = "https" if self.server.options else "http"
self.SERVER_WS_SCHEME = "wss" if self.server.options else "ws"
self.SERVER_SCHEME = "https" if options and options.cert_file_name is not None else "http"
self.SERVER_WS_SCHEME = "wss" if options and options.cert_file_name is not None else "ws"
self.task_factory_max_items = task_factory_max_items
self.lifespan = lifespan
@ -688,7 +688,7 @@ class _ASGI:
asgi_app = self
self.is_starting = True
self.is_stopped = False
self.status = 0 # 0 starting, 1 ok, 2 error, 3 stoping, 4 stopped, 5 stopped with error, 6 no lifespan
self.status = 0 # 0 starting, 1 ok, 2 error, 3 stopping, 4 stopped, 5 stopped with error, 6 no lifespan
self.status_message = ""
self.stop_future = self.server.loop.create_future()
@ -850,7 +850,7 @@ class ASGI:
if block:
run_task() # run app on the main process too :)
# sigint everything to gracefull shutdown
# sigint everything to graceful shutdown
import signal
for pid in pid_list:
os.kill(pid, signal.SIGINT)

Wyświetl plik

@ -28,7 +28,7 @@ Options:
--ws-close-on-backpressure-limit BOOLEAN Close connections that hits maximum backpressure [default: False]
--lifespan [auto|on|off] Lifespan implementation. [default: auto]
--interface [auto|asgi|asgi3|wsgi|ssgi|socketify] Select ASGI (same as ASGI3), ASGI3, WSGI or SSGI as the application interface. [default: auto]
--disable-listen-log BOOLEAN Disable log when start listenning [default: False]
--disable-listen-log BOOLEAN Disable log when start listening [default: False]
--version or -v Display the socketify.py version and exit.
--ssl-keyfile TEXT SSL key file
--ssl-certfile TEXT SSL certificate file
@ -188,10 +188,10 @@ def execute(args):
elif interface == "ssgi":
# if not is_ssgi(module):
# return print("SSGI is in development yet but is comming soon")
# return print("SSGI is in development yet but is coming soon")
# from . import SSGI as Interface
# interface = "ssgi"
return print("SSGI is in development yet but is comming soon")
return print("SSGI is in development yet but is coming soon")
elif interface != "socketify":
return print(f"{interface} interface is not supported yet")
@ -322,7 +322,7 @@ def execute(args):
run_app() # run app on the main process too :)
# sigint everything to gracefull shutdown
# sigint everything to graceful shutdown
import signal
for pid in pid_list:
os.kill(pid, signal.SIGINT)

Wyświetl plik

@ -70,7 +70,7 @@ async def sendfile(res, req, filename):
def send_headers(res):
res.write_status(status)
# tells the broswer the last modified date
# tells the browser the last modified date
res.write_header(b"Last-Modified", last_modified)
# tells the browser that we support range
@ -147,7 +147,7 @@ def middleware(*functions):
# we use Optional data=None at the end so you can use and middleware inside a middleware
def optimized_middleware_route(res, req, data=None):
# cicle to all middlewares
# circle to all middlewares
for function in syncs:
# call middlewares
data = function(res, req, data)
@ -156,7 +156,7 @@ def middleware(*functions):
return
async def wrapper(res, req, data):
# cicle to all middlewares
# circle to all middlewares
for function in asyncs:
# detect if is coroutine or not
if inspect.iscoroutinefunction(function):
@ -182,7 +182,7 @@ def middleware(*functions):
def sync_middleware(*functions):
# we use Optional data=None at the end so you can use and middleware inside a middleware
def middleware_route(res, req, data=None):
# cicle to all middlewares
# circle to all middlewares
for function in functions:
# call middlewares
data = function(res, req, data)
@ -198,7 +198,7 @@ def async_middleware(*functions):
# we use Optional data=None at the end so you can use and middleware inside a middleware
async def middleware_route(res, req, data=None):
some_async_as_run = False
# cicle to all middlewares
# circle to all middlewares
for function in functions:
# detect if is coroutine or not
if inspect.iscoroutinefunction(function):

Wyświetl plik

@ -101,7 +101,7 @@ class Loop:
self.loop.call_later(self.idle_relaxation_time, self._keep_alive)
else:
self.uv_loop.run_nowait()
# be more agressive when needed
# be more aggressive when needed
self.loop.call_soon(self._keep_alive)
def create_task(self, *args, **kwargs):
@ -194,7 +194,7 @@ class Loop:
return self.uv_loop.get_native_loop()
def _run_async_pypy(self, task, response=None):
# this garanties error 500 in case of uncaught exceptions, and can trigger the custom error handler
# this guarantees error 500 in case of uncaught exceptions, and can trigger the custom error handler
# using an coroutine wrapper generates less overhead than using add_done_callback
# this is an custom task/future with less overhead and that calls the first step
future = self._task_factory(
@ -203,7 +203,7 @@ class Loop:
return None # this future maybe already done and reused not safe to await
def _run_async_cpython(self, task, response=None):
# this garanties error 500 in case of uncaught exceptions, and can trigger the custom error handler
# this guarantees error 500 in case of uncaught exceptions, and can trigger the custom error handler
# using an coroutine wrapper generates less overhead than using add_done_callback
# this is an custom task/future with less overhead and that calls the first step
future = create_task(self.loop, task_wrapper(self.exception_handler, self.loop, response, task))

Wyświetl plik

@ -2371,6 +2371,24 @@ class AppRequest:
return self.read_jar[name].value
except Exception:
return None
def get_cookies(self):
if self.read_jar is None:
if self.jar_parsed:
return None
if self._headers:
raw_cookies = self._headers.get("cookie", None)
else:
raw_cookies = self.get_header("cookie")
if raw_cookies:
self.jar_parsed = True
self.read_jar = cookies.SimpleCookie(raw_cookies)
else:
self.jar_parsed = True
return None
return self.read_jar
def get_url(self):
if self._url:
@ -2599,7 +2617,7 @@ class App:
websocket_factory_max_items=0,
task_factory_max_items=100_000,
lifespan=True,
idle_relaxation_time=0,
idle_relaxation_time=0.01,
):
socket_options_ptr = ffi.new("struct us_socket_context_options_t *")

Wyświetl plik

@ -25,7 +25,7 @@ class SSGIHttpResponse:
# send chunk of data, can be used to perform with less backpressure than using send
# total_size is the sum of all lengths in bytes of all chunks to be sended
# connection will end when total_size is met
# returns tuple(bool, bool) first bool represents if the chunk is succefully sended, the second if the connection has ended
# returns tuple(bool, bool) first bool represents if the chunk is successfully sended, the second if the connection has ended
def send_chunk(self, chunk: Union[bytes, bytearray, memoryview], total_size: int) -> Awaitable:
return self.res.send_chunk(chunk, total_size)

Wyświetl plik

@ -238,7 +238,7 @@ def wsgi(ssl, response, info, user_data):
environ["PATH_INFO"] = ffi.unpack(info.url, info.url_size).decode("utf8")
environ["QUERY_STRING"] = ffi.unpack(
info.query_string, info.query_string_size
).decode("utf8")
).decode("utf8")[1:]
if info.remote_address != ffi.NULL:
environ["REMOTE_ADDR"] = ffi.unpack(
info.remote_address, info.remote_address_size
@ -444,7 +444,7 @@ def wsgi(ssl, response, info, user_data):
app._data_refs[_id] = data_retry
lib.uws_res_on_aborted(ssl, response, wsgi_on_data_ref_abort_handler, data_retry._ptr)
lib.uws_res_on_writable(ssl, response, wsgi_on_writable_handler, data_retry._ptr)
elif result is None or (not bool(result.has_responded) and bool(result.ok)): # not reachs Content-Length
elif result is None or (not bool(result.has_responded) and bool(result.ok)): # not reaches Content-Length
logging.error(AssertionError("Content-Length do not match sended content"))
lib.uws_res_close(
ssl,
@ -495,7 +495,7 @@ def wsgi(ssl, response, info, user_data):
content_length,
0,
)
# this should be very very rare fot HTTP
# this should be very very rare for HTTP
if not bool(result.ok):
failed_chunks = []
# just mark the chunks
@ -522,7 +522,7 @@ def wsgi(ssl, response, info, user_data):
app._data_refs[_id] = data_retry
lib.uws_res_on_aborted(ssl, response, wsgi_on_data_ref_abort_handler, data_retry._ptr)
lib.uws_res_on_writable(ssl, response, wsgi_on_writable_handler, data_retry._ptr)
elif result is None or (not bool(result.has_responded) and bool(result.ok)): # not reachs Content-Length
elif result is None or (not bool(result.has_responded) and bool(result.ok)): # not reaches Content-Length
logging.error(AssertionError("Content-Length do not match sended content"))
lib.uws_res_close(
ssl,
@ -548,7 +548,7 @@ class _WSGI:
self.server = App(options, task_factory_max_items=0)
self.SERVER_HOST = None
self.SERVER_PORT = None
self.SERVER_WS_SCHEME = "wss" if self.server.options else "ws"
self.SERVER_WS_SCHEME = "wss" if self.server._options else "ws"
self.wsgi = app
self.EMPTY_WSGI_BODY = WSGIBody(BytesIO())
self.BASIC_ENVIRON = dict(os.environ)
@ -671,7 +671,7 @@ class _WSGI:
"wsgi.errors": sys.stderr,
"wsgi.version": (1, 0),
"wsgi.run_once": False,
"wsgi.url_scheme": "https" if self.server.options else "http",
"wsgi.url_scheme": "https" if self.server._options and self.server._options.cert_file_name is not None else "http",
"wsgi.multithread": False,
"wsgi.multiprocess": False,
"wsgi.file_wrapper": None, # No file wrapper support for now
@ -768,7 +768,7 @@ class WSGI:
if block:
run_task() # run app on the main process too :)
# sigint everything to gracefull shutdown
# sigint everything to graceful shutdown
import signal
for pid in pid_list:
os.kill(pid, signal.SIGINT)