kopia lustrzana https://github.com/cirospaciari/socketify.py
fixed UVLoop, and added better strategy to Loop, added post support, get_data(), get_json(), get_text(), get_cookie(), set_cookie(), pending x-www-form-urlencoded and form-data (maybe only with another package), added more options for keys in response, added fallback to None if decode fails in request, added on_writable and on_data events, pending try_end and stream examples, added upload and post examples
rodzic
98f855e46c
commit
093b0f5c93
|
@ -34,10 +34,15 @@ class Loop:
|
||||||
|
|
||||||
asyncio.set_event_loop(self.loop)
|
asyncio.set_event_loop(self.loop)
|
||||||
self.started = False
|
self.started = False
|
||||||
|
self.last_defer = False
|
||||||
# self.loop_thread = None
|
# self.loop_thread = None
|
||||||
|
|
||||||
|
def create_future(self):
|
||||||
|
return self.loop.create_future()
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
self.started = True
|
self.started = True
|
||||||
|
#start relaxed until first task
|
||||||
self.timer = self.uv_loop.create_timer(0, 100, lambda loop: loop.run_once_asyncio(), self)
|
self.timer = self.uv_loop.create_timer(0, 100, lambda loop: loop.run_once_asyncio(), self)
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
|
@ -50,6 +55,13 @@ class Loop:
|
||||||
#run only one step
|
#run only one step
|
||||||
self.loop.call_soon(self.loop.stop)
|
self.loop.call_soon(self.loop.stop)
|
||||||
self.loop.run_forever()
|
self.loop.run_forever()
|
||||||
|
if self.started:
|
||||||
|
pending = len(asyncio.all_tasks(self.loop))
|
||||||
|
if pending < 1: #relaxed if has no tasks
|
||||||
|
self.timer.set_repeat(100)
|
||||||
|
else: #urge when needs
|
||||||
|
self.timer.set_repeat(1)
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
if(self.started):
|
if(self.started):
|
||||||
self.timer.stop()
|
self.timer.stop()
|
||||||
|
@ -59,6 +71,7 @@ class Loop:
|
||||||
if self.loop.is_running():
|
if self.loop.is_running():
|
||||||
self.loop.stop()
|
self.loop.stop()
|
||||||
|
|
||||||
|
self.last_defer = None
|
||||||
# Find all running tasks in main thread:
|
# Find all running tasks in main thread:
|
||||||
pending = asyncio.all_tasks(self.loop)
|
pending = asyncio.all_tasks(self.loop)
|
||||||
# Run loop until tasks done
|
# Run loop until tasks done
|
||||||
|
@ -74,6 +87,9 @@ class Loop:
|
||||||
|
|
||||||
#with threads
|
#with threads
|
||||||
future.add_done_callback(lambda f: future_handler(f, self.loop, self.exception_handler, response))
|
future.add_done_callback(lambda f: future_handler(f, self.loop, self.exception_handler, response))
|
||||||
|
|
||||||
|
#force asyncio run once to enable req in async functions before first await
|
||||||
|
self.run_once_asyncio()
|
||||||
return future
|
return future
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -89,7 +89,7 @@ void socketify_destroy_loop(socketify_loop* loop){
|
||||||
free(loop);
|
free(loop);
|
||||||
}
|
}
|
||||||
|
|
||||||
socketify_timer* socketify_create_timer(socketify_loop* loop, int64_t timeout, int64_t repeat, socketify_timer_handler handler, void* user_data){
|
socketify_timer* socketify_create_timer(socketify_loop* loop, uint64_t timeout, uint64_t repeat, socketify_timer_handler handler, void* user_data){
|
||||||
|
|
||||||
uv_timer_t* uv_timer = malloc(sizeof(uv_timer_t));
|
uv_timer_t* uv_timer = malloc(sizeof(uv_timer_t));
|
||||||
// uv_timer_init(loop->uv_loop, uv_timer);
|
// uv_timer_init(loop->uv_loop, uv_timer);
|
||||||
|
@ -109,6 +109,12 @@ socketify_timer* socketify_create_timer(socketify_loop* loop, int64_t timeout, i
|
||||||
return timer;
|
return timer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void socketify_timer_set_repeat(socketify_timer* timer, uint64_t repeat){
|
||||||
|
uv_timer_set_repeat( timer->uv_timer_ptr, repeat);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//stops and destroy timer info
|
//stops and destroy timer info
|
||||||
void socketify_timer_destroy(socketify_timer* timer){
|
void socketify_timer_destroy(socketify_timer* timer){
|
||||||
uv_timer_stop(timer->uv_timer_ptr);
|
uv_timer_stop(timer->uv_timer_ptr);
|
||||||
|
|
|
@ -26,6 +26,8 @@ typedef struct{
|
||||||
void* user_data;
|
void* user_data;
|
||||||
} socketify_timer;
|
} socketify_timer;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
socketify_loop * socketify_create_loop();
|
socketify_loop * socketify_create_loop();
|
||||||
bool socketify_constructor_failed(socketify_loop* loop);
|
bool socketify_constructor_failed(socketify_loop* loop);
|
||||||
bool socketify_on_prepare(socketify_loop* loop, socketify_prepare_handler handler, void* user_data);
|
bool socketify_on_prepare(socketify_loop* loop, socketify_prepare_handler handler, void* user_data);
|
||||||
|
@ -36,6 +38,8 @@ void* socketify_get_native_loop(socketify_loop* loop);
|
||||||
int socketify_loop_run(socketify_loop* loop, socketify_run_mode mode);
|
int socketify_loop_run(socketify_loop* loop, socketify_run_mode mode);
|
||||||
void socketify_loop_stop(socketify_loop* loop);
|
void socketify_loop_stop(socketify_loop* loop);
|
||||||
|
|
||||||
socketify_timer* socketify_create_timer(socketify_loop* loop, int64_t timeout, int64_t repeat, socketify_timer_handler handler, void* user_data);
|
socketify_timer* socketify_create_timer(socketify_loop* loop, uint64_t timeout, uint64_t repeat, socketify_timer_handler handler, void* user_data);
|
||||||
void socketify_timer_destroy(socketify_timer* timer);
|
void socketify_timer_destroy(socketify_timer* timer);
|
||||||
|
void socketify_timer_set_repeat(socketify_timer* timer, uint64_t repeat);
|
||||||
|
|
||||||
#endif
|
#endif
|
|
@ -8,6 +8,7 @@ ffi.cdef("""
|
||||||
|
|
||||||
typedef void (*socketify_prepare_handler)(void* user_data);
|
typedef void (*socketify_prepare_handler)(void* user_data);
|
||||||
typedef void (*socketify_timer_handler)(void* user_data);
|
typedef void (*socketify_timer_handler)(void* user_data);
|
||||||
|
typedef void (*socketify_async_handler)(void* user_data);
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
SOCKETIFY_RUN_DEFAULT = 0,
|
SOCKETIFY_RUN_DEFAULT = 0,
|
||||||
|
@ -28,6 +29,12 @@ typedef struct{
|
||||||
void* user_data;
|
void* user_data;
|
||||||
} socketify_timer;
|
} socketify_timer;
|
||||||
|
|
||||||
|
typedef struct{
|
||||||
|
void* uv_async_ptr;
|
||||||
|
socketify_async_handler handler;
|
||||||
|
void* user_data;
|
||||||
|
} socketify_async;
|
||||||
|
|
||||||
socketify_loop * socketify_create_loop();
|
socketify_loop * socketify_create_loop();
|
||||||
bool socketify_constructor_failed(socketify_loop* loop);
|
bool socketify_constructor_failed(socketify_loop* loop);
|
||||||
bool socketify_on_prepare(socketify_loop* loop, socketify_prepare_handler handler, void* user_data);
|
bool socketify_on_prepare(socketify_loop* loop, socketify_prepare_handler handler, void* user_data);
|
||||||
|
@ -38,9 +45,10 @@ void* socketify_get_native_loop(socketify_loop* loop);
|
||||||
int socketify_loop_run(socketify_loop* loop, socketify_run_mode mode);
|
int socketify_loop_run(socketify_loop* loop, socketify_run_mode mode);
|
||||||
void socketify_loop_stop(socketify_loop* loop);
|
void socketify_loop_stop(socketify_loop* loop);
|
||||||
|
|
||||||
socketify_timer* socketify_create_timer(socketify_loop* loop, int64_t timeout, int64_t repeat, socketify_timer_handler handler, void* user_data);
|
socketify_timer* socketify_create_timer(socketify_loop* loop, uint64_t timeout, uint64_t repeat, socketify_timer_handler handler, void* user_data);
|
||||||
void socketify_timer_destroy(socketify_timer* timer);
|
void socketify_timer_destroy(socketify_timer* timer);
|
||||||
|
bool socketify_async_call(socketify_loop* loop, socketify_async_handler handler, void* user_data);
|
||||||
|
void socketify_timer_set_repeat(socketify_timer* timer, uint64_t repeat);
|
||||||
|
|
||||||
""")
|
""")
|
||||||
library_path = os.path.join(os.path.dirname(__file__), "libsocketify.so")
|
library_path = os.path.join(os.path.dirname(__file__), "libsocketify.so")
|
||||||
|
@ -56,17 +64,20 @@ def socketify_generic_handler(data):
|
||||||
class UVTimer:
|
class UVTimer:
|
||||||
def __init__(self, loop, timeout, repeat, handler, user_data):
|
def __init__(self, loop, timeout, repeat, handler, user_data):
|
||||||
self._handler_data = ffi.new_handle((handler, user_data))
|
self._handler_data = ffi.new_handle((handler, user_data))
|
||||||
self._ptr = lib.socketify_create_timer(loop, ffi.cast("int64_t", timeout), ffi.cast("int64_t", repeat), socketify_generic_handler, self._handler_data)
|
self._ptr = lib.socketify_create_timer(loop, ffi.cast("uint64_t", timeout), ffi.cast("uint64_t", repeat), socketify_generic_handler, self._handler_data)
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
lib.socketify_timer_destroy(self._ptr)
|
lib.socketify_timer_destroy(self._ptr)
|
||||||
self._handler_data = None
|
self._handler_data = None
|
||||||
self._ptr = ffi.NULL
|
self._ptr = ffi.NULL
|
||||||
|
|
||||||
|
def set_repeat(self, repeat):
|
||||||
|
lib.socketify_timer_set_repeat(self._ptr, ffi.cast("uint64_t", repeat))
|
||||||
|
|
||||||
def __del__(self):
|
def __del__(self):
|
||||||
if self._ptr != ffi.NULL:
|
if self._ptr != ffi.NULL:
|
||||||
lib.socketify_timer_destroy(self._ptr)
|
lib.socketify_timer_destroy(self._ptr)
|
||||||
self.self._handler_data = None
|
self._handler_data = None
|
||||||
|
|
||||||
|
|
||||||
class UVLoop:
|
class UVLoop:
|
||||||
|
@ -90,7 +101,7 @@ class UVLoop:
|
||||||
|
|
||||||
def __del__(self):
|
def __del__(self):
|
||||||
lib.socketify_destroy_loop(self._loop)
|
lib.socketify_destroy_loop(self._loop)
|
||||||
self.self._handler_data = None
|
self._handler_data = None
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
return lib.socketify_loop_run(self._loop, lib.SOCKETIFY_RUN_DEFAULT)
|
return lib.socketify_loop_run(self._loop, lib.SOCKETIFY_RUN_DEFAULT)
|
||||||
|
|
|
@ -5,7 +5,8 @@ from .status_codes import status_codes
|
||||||
import json
|
import json
|
||||||
import inspect
|
import inspect
|
||||||
import signal
|
import signal
|
||||||
|
from http import cookies
|
||||||
|
from datetime import datetime
|
||||||
ffi = cffi.FFI()
|
ffi = cffi.FFI()
|
||||||
ffi.cdef("""
|
ffi.cdef("""
|
||||||
|
|
||||||
|
@ -220,7 +221,7 @@ def uws_generic_method_handler(res, req, user_data):
|
||||||
try:
|
try:
|
||||||
if inspect.iscoroutinefunction(handler):
|
if inspect.iscoroutinefunction(handler):
|
||||||
response.grab_aborted_handler()
|
response.grab_aborted_handler()
|
||||||
app.run_async(handler(response, request), response)
|
response.run_async(handler(response, request))
|
||||||
else:
|
else:
|
||||||
handler(response, request)
|
handler(response, request)
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
|
@ -236,7 +237,7 @@ def uws_generic_ssl_method_handler(res, req, user_data):
|
||||||
try:
|
try:
|
||||||
if inspect.iscoroutinefunction(handler):
|
if inspect.iscoroutinefunction(handler):
|
||||||
response.grab_aborted_handler()
|
response.grab_aborted_handler()
|
||||||
app.run_async(handler(response, request), response)
|
response.run_async(handler(response, request))
|
||||||
else:
|
else:
|
||||||
handler(response, request)
|
handler(response, request)
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
|
@ -261,46 +262,112 @@ def uws_generic_aborted_handler(response, user_data):
|
||||||
res = ffi.from_handle(user_data)
|
res = ffi.from_handle(user_data)
|
||||||
res.trigger_aborted()
|
res.trigger_aborted()
|
||||||
|
|
||||||
|
@ffi.callback("void(uws_res_t *, const char *, size_t, bool, void*)")
|
||||||
|
def uws_generic_on_data_handler(res, chunk, chunk_length, is_end, user_data):
|
||||||
|
if not user_data == ffi.NULL:
|
||||||
|
res = ffi.from_handle(user_data)
|
||||||
|
if chunk == ffi.NULL:
|
||||||
|
data = None
|
||||||
|
else:
|
||||||
|
data = ffi.unpack(chunk, chunk_length)
|
||||||
|
|
||||||
|
res.trigger_data_handler(data, bool(is_end))
|
||||||
|
|
||||||
|
@ffi.callback("bool(uws_res_t *, uintmax_t, void*)")
|
||||||
|
def uws_generic_on_writable_handler(res, offset, user_data):
|
||||||
|
if not user_data == ffi.NULL:
|
||||||
|
res = ffi.from_handle(user_data)
|
||||||
|
return res.trigger_writable_handler(offset)
|
||||||
|
return False
|
||||||
|
|
||||||
class AppRequest:
|
class AppRequest:
|
||||||
def __init__(self, request):
|
def __init__(self, request):
|
||||||
self.req = request
|
self.req = request
|
||||||
|
self.read_jar = None
|
||||||
|
self.jar_parsed = False
|
||||||
|
|
||||||
|
def get_cookie(self, name):
|
||||||
|
if self.read_jar == None:
|
||||||
|
if self.jar_parsed:
|
||||||
|
return None
|
||||||
|
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
|
||||||
|
try:
|
||||||
|
return self.read_jar[name].value
|
||||||
|
except Exception:
|
||||||
|
return None
|
||||||
|
|
||||||
def get_url(self):
|
def get_url(self):
|
||||||
buffer = ffi.new("char**")
|
buffer = ffi.new("char**")
|
||||||
length = lib.uws_req_get_url(self.req, buffer)
|
length = lib.uws_req_get_url(self.req, buffer)
|
||||||
buffer_address = ffi.addressof(buffer, 0)[0]
|
buffer_address = ffi.addressof(buffer, 0)[0]
|
||||||
if buffer_address == ffi.NULL:
|
if buffer_address == ffi.NULL:
|
||||||
return None
|
return None
|
||||||
|
try:
|
||||||
return ffi.unpack(buffer_address, length).decode("utf-8")
|
return ffi.unpack(buffer_address, length).decode("utf-8")
|
||||||
|
except Exception: #invalid utf-8
|
||||||
|
return None
|
||||||
def get_method(self):
|
def get_method(self):
|
||||||
buffer = ffi.new("char**")
|
buffer = ffi.new("char**")
|
||||||
length = lib.uws_req_get_method(self.req, buffer)
|
length = lib.uws_req_get_method(self.req, buffer)
|
||||||
buffer_address = ffi.addressof(buffer, 0)[0]
|
buffer_address = ffi.addressof(buffer, 0)[0]
|
||||||
if buffer_address == ffi.NULL:
|
if buffer_address == ffi.NULL:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
try:
|
||||||
return ffi.unpack(buffer_address, length).decode("utf-8")
|
return ffi.unpack(buffer_address, length).decode("utf-8")
|
||||||
|
except Exception: #invalid utf-8
|
||||||
|
return None
|
||||||
def get_header(self, lower_case_header):
|
def get_header(self, lower_case_header):
|
||||||
buffer = ffi.new("char**")
|
if isinstance(lower_case_header, str):
|
||||||
data = lower_case_header.encode("utf-8")
|
data = lower_case_header.encode("utf-8")
|
||||||
|
elif isinstance(lower_case_header, bytes):
|
||||||
|
data = lower_case_header
|
||||||
|
else:
|
||||||
|
data = json.dumps(lower_case_header).encode("utf-8")
|
||||||
|
|
||||||
|
buffer = ffi.new("char**")
|
||||||
length = lib.uws_req_get_header(self.req, data, len(data), buffer)
|
length = lib.uws_req_get_header(self.req, data, len(data), buffer)
|
||||||
buffer_address = ffi.addressof(buffer, 0)[0]
|
buffer_address = ffi.addressof(buffer, 0)[0]
|
||||||
if buffer_address == ffi.NULL:
|
if buffer_address == ffi.NULL:
|
||||||
return None
|
return None
|
||||||
|
try:
|
||||||
return ffi.unpack(buffer_address, length).decode("utf-8")
|
return ffi.unpack(buffer_address, length).decode("utf-8")
|
||||||
|
except Exception: #invalid utf-8
|
||||||
|
return None
|
||||||
def get_query(self, key):
|
def get_query(self, key):
|
||||||
buffer = ffi.new("char**")
|
buffer = ffi.new("char**")
|
||||||
data = key.encode("utf-8")
|
|
||||||
length = lib.uws_req_get_query(self.req, data, len(data), buffer)
|
if isinstance(key, str):
|
||||||
|
key_data = key.encode("utf-8")
|
||||||
|
elif isinstance(key, bytes):
|
||||||
|
key_data = key
|
||||||
|
else:
|
||||||
|
key_data = json.dumps(key).encode("utf-8")
|
||||||
|
|
||||||
|
length = lib.uws_req_get_query(self.req, key_data, len(key_data), buffer)
|
||||||
buffer_address = ffi.addressof(buffer, 0)[0]
|
buffer_address = ffi.addressof(buffer, 0)[0]
|
||||||
if buffer_address == ffi.NULL:
|
if buffer_address == ffi.NULL:
|
||||||
return None
|
return None
|
||||||
|
try:
|
||||||
return ffi.unpack(buffer_address, length).decode("utf-8")
|
return ffi.unpack(buffer_address, length).decode("utf-8")
|
||||||
|
except Exception: #invalid utf-8
|
||||||
|
return None
|
||||||
def get_parameter(self, index):
|
def get_parameter(self, index):
|
||||||
buffer = ffi.new("char**")
|
buffer = ffi.new("char**")
|
||||||
length = lib.uws_req_get_parameter(self.req, ffi.cast("unsigned short", index), buffer)
|
length = lib.uws_req_get_parameter(self.req, ffi.cast("unsigned short", index), buffer)
|
||||||
buffer_address = ffi.addressof(buffer, 0)[0]
|
buffer_address = ffi.addressof(buffer, 0)[0]
|
||||||
if buffer_address == ffi.NULL:
|
if buffer_address == ffi.NULL:
|
||||||
return None
|
return None
|
||||||
|
try:
|
||||||
return ffi.unpack(buffer_address, length).decode("utf-8")
|
return ffi.unpack(buffer_address, length).decode("utf-8")
|
||||||
|
except Exception: #invalid utf-8
|
||||||
|
return None
|
||||||
def set_yield(self, has_yield):
|
def set_yield(self, has_yield):
|
||||||
lib.uws_req_set_field(self.req, 1 if has_yield else 0)
|
lib.uws_req_set_field(self.req, 1 if has_yield else 0)
|
||||||
def get_yield(self):
|
def get_yield(self):
|
||||||
|
@ -316,23 +383,105 @@ class AppResponse:
|
||||||
self.aborted = False
|
self.aborted = False
|
||||||
self._ptr = ffi.NULL
|
self._ptr = ffi.NULL
|
||||||
self.loop = loop
|
self.loop = loop
|
||||||
|
self._aborted_handler = None
|
||||||
|
self._writable_handler = None
|
||||||
|
self._data_handler = None
|
||||||
|
self._ptr = ffi.new_handle(self)
|
||||||
|
self._grabed_abort_handler_once = False
|
||||||
|
self._write_jar = None
|
||||||
|
|
||||||
|
def set_cookie(self, name, value, options={}):
|
||||||
|
if self._write_jar == None:
|
||||||
|
self._write_jar = cookies.SimpleCookie()
|
||||||
|
self._write_jar[name] = value
|
||||||
|
if isinstance(options, dict):
|
||||||
|
for key in options:
|
||||||
|
if key == "expires" and isinstance(options[key], datetime):
|
||||||
|
self._write_jar[name][key] = options[key].strftime("%a, %d %b %Y %H:%M:%S GMT")
|
||||||
|
else:
|
||||||
|
self._write_jar[name][key] = options[key]
|
||||||
|
|
||||||
def trigger_aborted(self):
|
def trigger_aborted(self):
|
||||||
self.aborted = True
|
self.aborted = True
|
||||||
self.res = ffi.NULL
|
self.res = ffi.NULL
|
||||||
self._ptr = ffi.NULL
|
self._ptr = ffi.NULL
|
||||||
if hasattr(self, "_aborted_handler") and hasattr(self._aborted_handler, '__call__'):
|
if hasattr(self, "_aborted_handler") and hasattr(self._aborted_handler, '__call__'):
|
||||||
self._aborted_handler()
|
try:
|
||||||
|
if inspect.iscoroutinefunction(self._aborted_handler):
|
||||||
|
self.run_async(self._aborted_handler(self))
|
||||||
|
else:
|
||||||
|
self._aborted_handler(self)
|
||||||
|
except Exception as err:
|
||||||
|
print("Error on abort handler %s" % str(err))
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
def trigger_data_handler(self, data, is_end):
|
||||||
|
if self.aborted:
|
||||||
|
return self
|
||||||
|
if hasattr(self, "_data_handler") and hasattr(self._data_handler, '__call__'):
|
||||||
|
try:
|
||||||
|
if inspect.iscoroutinefunction(self._data_handler):
|
||||||
|
self.run_async(self._data_handler(self, data, is_end))
|
||||||
|
else:
|
||||||
|
self._data_handler(self, data, is_end)
|
||||||
|
except Exception as err:
|
||||||
|
print("Error on data handler %s" % str(err))
|
||||||
|
|
||||||
|
return self
|
||||||
|
|
||||||
|
def trigger_writable_handler(self, offset):
|
||||||
|
if self.aborted:
|
||||||
|
return False
|
||||||
|
if hasattr(self, "_writable_handler") and hasattr(self._writable_handler, '__call__'):
|
||||||
|
try:
|
||||||
|
if inspect.iscoroutinefunction(self._writable_handler):
|
||||||
|
raise RuntimeError("AppResponse.on_writable must be synchronous")
|
||||||
|
return self._writable_handler(self, offset)
|
||||||
|
except Exception as err:
|
||||||
|
print("Error on writable handler %s" % str(err))
|
||||||
|
return False
|
||||||
|
return False
|
||||||
|
|
||||||
def run_async(self, task):
|
def run_async(self, task):
|
||||||
self.grab_aborted_handler()
|
self.grab_aborted_handler()
|
||||||
return self.loop.run_async(task, self)
|
return self.loop.run_async(task, self)
|
||||||
|
|
||||||
|
|
||||||
|
async def get_text(self, encoding='utf-8'):
|
||||||
|
data = await self.get_data()
|
||||||
|
try:
|
||||||
|
return b''.join(data).decode(encoding)
|
||||||
|
except Exception:
|
||||||
|
return None #invalid encoding
|
||||||
|
|
||||||
|
async def get_json(self):
|
||||||
|
data = await self.get_data()
|
||||||
|
try:
|
||||||
|
return json.loads(b''.join(data).decode('utf-8'))
|
||||||
|
except Exception:
|
||||||
|
return None #invalid json
|
||||||
|
|
||||||
|
|
||||||
|
def get_data(self):
|
||||||
|
future = self.loop.create_future()
|
||||||
|
data = []
|
||||||
|
def is_aborted(res):
|
||||||
|
future.set_result(data)
|
||||||
|
|
||||||
|
def get_chunks(res, chunk, is_end):
|
||||||
|
data.append(chunk)
|
||||||
|
if is_end:
|
||||||
|
future.set_result(data)
|
||||||
|
|
||||||
|
self.on_aborted(is_aborted)
|
||||||
|
self.on_data(get_chunks)
|
||||||
|
return future
|
||||||
|
|
||||||
|
|
||||||
def grab_aborted_handler(self):
|
def grab_aborted_handler(self):
|
||||||
#only needed if is async
|
#only needed if is async
|
||||||
if self._ptr == ffi.NULL and not self.aborted:
|
if not self.aborted and not self._grabed_abort_handler_once:
|
||||||
self._ptr = ffi.new_handle(self)
|
self._grabed_abort_handler_once = True
|
||||||
lib.uws_res_on_aborted(self.SSL, self.res, uws_generic_aborted_handler, self._ptr)
|
lib.uws_res_on_aborted(self.SSL, self.res, uws_generic_aborted_handler, self._ptr)
|
||||||
|
|
||||||
def redirect(self, location, status_code=302):
|
def redirect(self, location, status_code=302):
|
||||||
|
@ -342,6 +491,9 @@ class AppResponse:
|
||||||
|
|
||||||
def end(self, message, end_connection=False):
|
def end(self, message, end_connection=False):
|
||||||
if not self.aborted:
|
if not self.aborted:
|
||||||
|
if self._write_jar != None:
|
||||||
|
self.write_header("Set-Cookie", self._write_jar.output(header=""))
|
||||||
|
|
||||||
if isinstance(message, str):
|
if isinstance(message, str):
|
||||||
data = message.encode("utf-8")
|
data = message.encode("utf-8")
|
||||||
elif isinstance(message, bytes):
|
elif isinstance(message, bytes):
|
||||||
|
@ -350,7 +502,7 @@ class AppResponse:
|
||||||
self.end_without_body(end_connection)
|
self.end_without_body(end_connection)
|
||||||
return self
|
return self
|
||||||
else:
|
else:
|
||||||
self.write_header("Content-Type", "application/json")
|
self.write_header(b'Content-Type', b'application/json')
|
||||||
data = json.dumps(message).encode("utf-8")
|
data = json.dumps(message).encode("utf-8")
|
||||||
lib.uws_res_end(self.SSL, self.res, data, len(data), 1 if end_connection else 0)
|
lib.uws_res_end(self.SSL, self.res, data, len(data), 1 if end_connection else 0)
|
||||||
return self
|
return self
|
||||||
|
@ -370,11 +522,17 @@ class AppResponse:
|
||||||
lib.uws_res_write_continue(self.SSL, self.res)
|
lib.uws_res_write_continue(self.SSL, self.res)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
# /* Try and end the response. Returns [true, true] on success.
|
||||||
|
# * Starts a timeout in some cases. Returns [ok, hasResponded] */
|
||||||
|
# std::pair<bool, bool> tryEnd(std::string_view data, uintmax_t totalSize = 0) {
|
||||||
|
# return {internalEnd(data, totalSize, true), hasResponded()};
|
||||||
|
# }
|
||||||
|
|
||||||
def write_status(self, status_or_status_text):
|
def write_status(self, status_or_status_text):
|
||||||
if not self.aborted:
|
if not self.aborted:
|
||||||
if isinstance(status_or_status_text, int):
|
if isinstance(status_or_status_text, int):
|
||||||
try:
|
try:
|
||||||
data = status_codes[status_or_status_text].encode("utf-8")
|
data = status_codes[status_or_status_text]
|
||||||
except: #invalid status
|
except: #invalid status
|
||||||
raise RuntimeError("\"%d\" Is not an valid Status Code" % status_or_status_text)
|
raise RuntimeError("\"%d\" Is not an valid Status Code" % status_or_status_text)
|
||||||
elif isinstance(status_text, str):
|
elif isinstance(status_text, str):
|
||||||
|
@ -389,7 +547,13 @@ class AppResponse:
|
||||||
|
|
||||||
def write_header(self, key, value):
|
def write_header(self, key, value):
|
||||||
if not self.aborted:
|
if not self.aborted:
|
||||||
|
if isinstance(key, str):
|
||||||
key_data = key.encode("utf-8")
|
key_data = key.encode("utf-8")
|
||||||
|
elif isinstance(key, bytes):
|
||||||
|
key_data = key
|
||||||
|
else:
|
||||||
|
key_data = json.dumps(key).encode("utf-8")
|
||||||
|
|
||||||
if isinstance(value, int):
|
if isinstance(value, int):
|
||||||
lib.uws_res_write_header_int(self.SSL, self.res, key_data, len(key_data), ffi.cast("uint64_t", value))
|
lib.uws_res_write_header_int(self.SSL, self.res, key_data, len(key_data), ffi.cast("uint64_t", value))
|
||||||
elif isinstance(value, str):
|
elif isinstance(value, str):
|
||||||
|
@ -403,6 +567,8 @@ class AppResponse:
|
||||||
|
|
||||||
def end_without_body(self, end_connection=False):
|
def end_without_body(self, end_connection=False):
|
||||||
if not self.aborted:
|
if not self.aborted:
|
||||||
|
if self._write_jar != None:
|
||||||
|
self.write_header("Set-Cookie", self._write_jar.output(header=""))
|
||||||
lib.uws_res_end_without_body(self.SSL, self.res, 1 if end_connection else 0)
|
lib.uws_res_end_without_body(self.SSL, self.res, 1 if end_connection else 0)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
@ -439,6 +605,22 @@ class AppResponse:
|
||||||
self._aborted_handler = handler
|
self._aborted_handler = handler
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
def on_data(self, handler):
|
||||||
|
if not self.aborted:
|
||||||
|
if hasattr(handler, '__call__'):
|
||||||
|
self.grab_aborted_handler()
|
||||||
|
self._data_handler = handler
|
||||||
|
lib.uws_res_on_data(self.SSL, self.res, uws_generic_on_data_handler, self._ptr)
|
||||||
|
return self
|
||||||
|
|
||||||
|
def on_writable(self, handler):
|
||||||
|
if not self.aborted:
|
||||||
|
if hasattr(handler, '__call__'):
|
||||||
|
self.grab_aborted_handler()
|
||||||
|
self._writable_handler = handler
|
||||||
|
lib.uws_res_on_writable(self.SSL, self.res, uws_generic_on_writable_handler, self._ptr)
|
||||||
|
return self
|
||||||
|
|
||||||
def __del__(self):
|
def __del__(self):
|
||||||
self.res = ffi.NULL
|
self.res = ffi.NULL
|
||||||
self._ptr = ffi.NULL
|
self._ptr = ffi.NULL
|
||||||
|
@ -547,9 +729,6 @@ class App:
|
||||||
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def get_loop():
|
|
||||||
return self.loop.loop
|
|
||||||
|
|
||||||
def run_async(self, task, response=None):
|
def run_async(self, task, response=None):
|
||||||
return self.loop.run_async(task, response)
|
return self.loop.run_async(task, response)
|
||||||
|
|
||||||
|
@ -557,7 +736,6 @@ class App:
|
||||||
signal.signal(signal.SIGINT, lambda sig, frame: self.close())
|
signal.signal(signal.SIGINT, lambda sig, frame: self.close())
|
||||||
self.loop.start()
|
self.loop.start()
|
||||||
self.loop.run()
|
self.loop.run()
|
||||||
# lib.uws_app_run(self.SSL, self.app)
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
|
|
|
@ -1,65 +1,65 @@
|
||||||
status_codes = {
|
status_codes = {
|
||||||
100 : "100 Continue",
|
100 : b'100 Continue',
|
||||||
101 : "101 Switching Protocols",
|
101 : b'101 Switching Protocols',
|
||||||
102 : "102 Processing",
|
102 : b'102 Processing',
|
||||||
103 : "103 Early Hints",
|
103 : b'103 Early Hints',
|
||||||
200 : "200 OK",
|
200 : b'200 OK',
|
||||||
201 : "201 Created",
|
201 : b'201 Created',
|
||||||
202 : "202 Accepted",
|
202 : b'202 Accepted',
|
||||||
203 : "203 Non-Authoritative Information",
|
203 : b'203 Non-Authoritative Information',
|
||||||
204 : "204 No Content",
|
204 : b'204 No Content',
|
||||||
205 : "205 Reset Content",
|
205 : b'205 Reset Content',
|
||||||
206 : "206 Partial Content",
|
206 : b'206 Partial Content',
|
||||||
207 : "207 Multi-Status",
|
207 : b'207 Multi-Status',
|
||||||
208 : "208 Already Reported",
|
208 : b'208 Already Reported',
|
||||||
226 : "226 IM Used (HTTP Delta encoding)",
|
226 : b'226 IM Used (HTTP Delta encoding)',
|
||||||
300 : "300 Multiple Choices",
|
300 : b'300 Multiple Choices',
|
||||||
301 : "301 Moved Permanently",
|
301 : b'301 Moved Permanently',
|
||||||
302 : "302 Found",
|
302 : b'302 Found',
|
||||||
303 : "303 See Other",
|
303 : b'303 See Other',
|
||||||
304 : "304 Not Modified",
|
304 : b'304 Not Modified',
|
||||||
305 : "305 Use Proxy Deprecated",
|
305 : b'305 Use Proxy Deprecated',
|
||||||
306 : "306 unused",
|
306 : b'306 unused',
|
||||||
307 : "307 Temporary Redirect",
|
307 : b'307 Temporary Redirect',
|
||||||
308 : "308 Permanent Redirect",
|
308 : b'308 Permanent Redirect',
|
||||||
400 : "400 Bad Request",
|
400 : b'400 Bad Request',
|
||||||
401 : "401 Unauthorized",
|
401 : b'401 Unauthorized',
|
||||||
402 : "402 Payment Required Experimental",
|
402 : b'402 Payment Required Experimental',
|
||||||
403 : "403 Forbidden",
|
403 : b'403 Forbidden',
|
||||||
404 : "404 Not Found",
|
404 : b'404 Not Found',
|
||||||
405 : "405 Method Not Allowed",
|
405 : b'405 Method Not Allowed',
|
||||||
406 : "406 Not Acceptable",
|
406 : b'406 Not Acceptable',
|
||||||
407 : "407 Proxy Authentication Required",
|
407 : b'407 Proxy Authentication Required',
|
||||||
408 : "408 Request Timeout",
|
408 : b'408 Request Timeout',
|
||||||
409 : "409 Conflict",
|
409 : b'409 Conflict',
|
||||||
410 : "410 Gone",
|
410 : b'410 Gone',
|
||||||
411 : "411 Length Required",
|
411 : b'411 Length Required',
|
||||||
412 : "412 Precondition Failed",
|
412 : b'412 Precondition Failed',
|
||||||
413 : "413 Payload Too Large",
|
413 : b'413 Payload Too Large',
|
||||||
414 : "414 URI Too Long",
|
414 : b'414 URI Too Long',
|
||||||
415 : "415 Unsupported Media Type",
|
415 : b'415 Unsupported Media Type',
|
||||||
416 : "416 Range Not Satisfiable",
|
416 : b'416 Range Not Satisfiable',
|
||||||
417 : "417 Expectation Failed",
|
417 : b'417 Expectation Failed',
|
||||||
418 : "418 I'm a teapot",
|
418 : b'418 I\'m a teapot',
|
||||||
421 : "421 Misdirected Request",
|
421 : b'421 Misdirected Request',
|
||||||
422 : "422 Unprocessable Entity",
|
422 : b'422 Unprocessable Entity',
|
||||||
423 : "423 Locked",
|
423 : b'423 Locked',
|
||||||
424 : "424 Failed Dependency",
|
424 : b'424 Failed Dependency',
|
||||||
425 : "425 Too Early Experimental",
|
425 : b'425 Too Early Experimental',
|
||||||
426 : "426 Upgrade Required",
|
426 : b'426 Upgrade Required',
|
||||||
428 : "428 Precondition Required",
|
428 : b'428 Precondition Required',
|
||||||
429 : "429 Too Many Requests",
|
429 : b'429 Too Many Requests',
|
||||||
431 : "431 Request Header Fields Too Large",
|
431 : b'431 Request Header Fields Too Large',
|
||||||
451 : "451 Unavailable For Legal Reasons",
|
451 : b'451 Unavailable For Legal Reasons',
|
||||||
500 : "500 Internal Server Error",
|
500 : b'500 Internal Server Error',
|
||||||
501 : "501 Not Implemented",
|
501 : b'501 Not Implemented',
|
||||||
502 : "502 Bad Gateway",
|
502 : b'502 Bad Gateway',
|
||||||
503 : "503 Service Unavailable",
|
503 : b'503 Service Unavailable',
|
||||||
504 : "504 Gateway Timeout",
|
504 : b'504 Gateway Timeout',
|
||||||
505 : "505 HTTP Version Not Supported",
|
505 : b'505 HTTP Version Not Supported',
|
||||||
506 : "506 Variant Also Negotiates",
|
506 : b'506 Variant Also Negotiates',
|
||||||
507 : "507 Insufficient Storage",
|
507 : b'507 Insufficient Storage',
|
||||||
508 : "508 Loop Detected",
|
508 : b'508 Loop Detected',
|
||||||
510 : "510 Not Extended",
|
510 : b'510 Not Extended',
|
||||||
511 : "511 Network Authentication Required"
|
511 : b'511 Network Authentication Required'
|
||||||
}
|
}
|
|
@ -8,8 +8,8 @@ from helpers.twolevel_cache import TwoLevelCache
|
||||||
redis_pool = redis.ConnectionPool(host='localhost', port=6379, db=0)
|
redis_pool = redis.ConnectionPool(host='localhost', port=6379, db=0)
|
||||||
redis_conection = redis.Redis(connection_pool=redis_pool)
|
redis_conection = redis.Redis(connection_pool=redis_pool)
|
||||||
# 2 LEVEL CACHE (Redis to share amoung workers, Memory to be much faster)
|
# 2 LEVEL CACHE (Redis to share amoung workers, Memory to be much faster)
|
||||||
# cache in memory is 5s, cache in redis is 10s duration
|
# cache in memory is 30s, cache in redis is 60s duration
|
||||||
cache = TwoLevelCache(redis_conection, 5, 10)
|
cache = TwoLevelCache(redis_conection, 30, 60)
|
||||||
|
|
||||||
###
|
###
|
||||||
# Model
|
# Model
|
||||||
|
@ -19,17 +19,17 @@ async def get_pokemon(number):
|
||||||
async with aiohttp.ClientSession() as session:
|
async with aiohttp.ClientSession() as session:
|
||||||
async with session.get(f'https://pokeapi.co/api/v2/pokemon/{number}') as response:
|
async with session.get(f'https://pokeapi.co/api/v2/pokemon/{number}') as response:
|
||||||
pokemon = await response.text()
|
pokemon = await response.text()
|
||||||
#cache only works with strings/bytes/buffer
|
#cache only works with strings/bytes
|
||||||
#we will not change nothing here so no needs to parse json
|
#we will not change nothing here so no needs to parse json
|
||||||
return pokemon
|
return pokemon.encode("utf-8")
|
||||||
|
|
||||||
async def get_original_pokemons():
|
async def get_original_pokemons():
|
||||||
async with aiohttp.ClientSession() as session:
|
async with aiohttp.ClientSession() as session:
|
||||||
async with session.get(f'https://pokeapi.co/api/v2/pokemon?limit=151') as response:
|
async with session.get(f'https://pokeapi.co/api/v2/pokemon?limit=151') as response:
|
||||||
#cache only works with strings/bytes/buffer
|
#cache only works with strings/bytes
|
||||||
#we will not change nothing here so no needs to parse json
|
#we will not change nothing here so no needs to parse json
|
||||||
pokemons = await response.text()
|
pokemons = await response.text()
|
||||||
return pokemons
|
return pokemons.encode("utf-8")
|
||||||
|
|
||||||
|
|
||||||
###
|
###
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
|
|
||||||
from socketify import App, AppOptions, AppListenOptions
|
from socketify import App, AppOptions, AppListenOptions
|
||||||
import asyncio
|
import asyncio
|
||||||
|
from datetime import datetime
|
||||||
|
from datetime import timedelta
|
||||||
app = App()
|
app = App()
|
||||||
|
|
||||||
def home(res, req):
|
def home(res, req):
|
||||||
|
@ -10,6 +11,27 @@ def home(res, req):
|
||||||
def anything(res, req):
|
def anything(res, req):
|
||||||
res.end("Any route with method: %s" % req.get_method())
|
res.end("Any route with method: %s" % req.get_method())
|
||||||
|
|
||||||
|
def cookies(res, req):
|
||||||
|
#cookies are writen after end
|
||||||
|
res.set_cookie("spaciari", "1234567890",{
|
||||||
|
# expires
|
||||||
|
# path
|
||||||
|
# comment
|
||||||
|
# domain
|
||||||
|
# max-age
|
||||||
|
# secure
|
||||||
|
# version
|
||||||
|
# httponly
|
||||||
|
# samesite
|
||||||
|
"path": "/",
|
||||||
|
# "domain": "*.test.com",
|
||||||
|
"httponly": True,
|
||||||
|
"samesite": "None",
|
||||||
|
"secure": True,
|
||||||
|
"expires": datetime.utcnow() + timedelta(minutes=30)
|
||||||
|
})
|
||||||
|
res.end("Your session_id cookie is: %s" % req.get_cookie('session_id'));
|
||||||
|
|
||||||
def useragent(res,req):
|
def useragent(res,req):
|
||||||
res.end("Your user agent is: %s" % req.get_header('user-agent'));
|
res.end("Your user agent is: %s" % req.get_header('user-agent'));
|
||||||
|
|
||||||
|
@ -77,9 +99,11 @@ app.get("/delayed", delayed)
|
||||||
app.get("/json", json)
|
app.get("/json", json)
|
||||||
app.get("/sleepy", sleepy_json)
|
app.get("/sleepy", sleepy_json)
|
||||||
app.get("/custom_header", custom_header)
|
app.get("/custom_header", custom_header)
|
||||||
|
app.get("/cookies", cookies)
|
||||||
app.get("/send_in_parts", send_in_parts)
|
app.get("/send_in_parts", send_in_parts)
|
||||||
app.get("/redirect", redirect)
|
app.get("/redirect", redirect)
|
||||||
app.get("/redirected", redirected)
|
app.get("/redirected", redirected)
|
||||||
|
# too see about app.post go to ./upload_or_post.py :D
|
||||||
# Wildcard at last always :)
|
# Wildcard at last always :)
|
||||||
app.any("/*", not_found)
|
app.any("/*", not_found)
|
||||||
|
|
||||||
|
|
Ładowanie…
Reference in New Issue