Skitai is a Python WSGI/HTTP Server for UNIX.
And simple to run:
Install,
pip3 install -U skitai
Create and mount your app,
# serve.py
def app (env, start_response):
start_response ("200 OK", [("Content-Type", "text/plain")])
return 'Hello World'
if __name__ == "__main__":
import skitai
skitai.mount ('/', app)
skitai.mount ('/', '/var/www/statics')
skitai.run (address = "127.0.0.1", port = 5000)
And run.
python3 serve.py
Your app will work for your thousands or miliions of customers.
Features
- Mount multiple apps: Flask and Django can be mounted at the same time
- Websocket
- HTTP 2.0 h2c protocol based on h2
Python 3.6+
Skitai and other core base dependent libraries is developing on single milestone, install/upgrade all at once. Otherwise it is highly possible to meet some errors.
With pip
pip3 install -U skitai rs4
With git
pip3 install -U rs4
git clone https://gitlab.com/hansroh/skitai.git
cd skitai
pip3 install -e .
import skitai
if __name__ == "__main__":
# mount Flask something
skitai.mount ('/', 'my_flask/myapp:app')
# mount Django
skitai.mount ('/admin', 'my_django/my_django/wsgi:application')
skitai.run (ip = '0.0.0.0', port = 5000)
skitai.mount ('/', 'my_proejct/root')
skitai.mount ('/static', 'my_proejct/static')
skitai.mount ('/media', 'my_proejct/media')
with skitai.preference () as pref:
pref.debug = True
pref.use_reloader = True
skitai.mount ('/', 'my_flask/myapp:app', pref)
skitai.run ()
All attributes of pref will overwrite your app attrubutes.
- address
- port
- quic
- threads
- workers
- tasks
- backlog
python3 serve.py
python3 serve.py start # start service
python3 serve.py stop
python3 serve.py restart
Give a name like myservice
skitai.run (ip = '0.0.0.0', port = 5000, name = 'myservice')
# create systemd config in /etc/systemd/system/myservice.service
python3 serve.py install
# update /etc/systemd/system/myservice.service
python3 serve.py update
# start as root, but fallback into current sudoer after started up
sudo python3 serve.py install
# start as root, but change into user skitai, group www
sudo python3 serve.py --user skitai --group www install
# to keep root privilege
sudo python3 serve.py --user root install
sudo python3 serve.py uninstall
To startup or stop,
sudo systemctl start myservice
sudo systemctl stop myservice
sudo systemctl restart myservice
docker run -d --gpus all \
--name mayservice
--user ubuntu
-v /home/ubuntu/myhome:/home/ubuntu
-p 5000:5000
hansroh/myservice:latest ~/myapis/serve.py
python3 serve.py --help
skitai.run parameters,
skitai.run (ip = '0.0.0.0', port = 5000, thread = 1, workers = 4, tasks = 8)
But command line options have more high priority.
python3 serve.py --workers=2 --threads=4 --tasks=16 --port=3800
To adding custom options,
skitai.add_option ("-q", '--qtran', 'start queued transaction service')
skitai.add_option (None, '--temp-dir=TEMP_DIR', 'temp directory', default = '/var/tmp/myservice')
if "--qtran" in skitai.options:
tasks.add ('transaction')
os.mkdir (skitai.options ['--temp-dir'])
python3 serve.py --help
Usage: ./serve.py [OPTION]... [COMMAND]
COMMAND can be one of [start|stop|status|restart|install|uninstall|remove|update]
Mandatory arguments to long options are mandatory for short options too.
...
-q, --qtran start queued transaction daemo
--temp-dir=TEMP_DIR temp directory
To update systemd service,
sudo python3 serve.py --user skitai --qtran --temp-dir=/path/to update
sudo systemctl restart myservice
It will works only if HTTP/2.0 h2c requests. Otherwise ignored.
from flask import render_template
@app.route ('/promise')
def promise ():
skitai.push ('/img/logo.png')
skitai.push ('/img/main.png')
# index.j2 contains above images
return render_template ('index.j2')
Flask app example,
# flask app example
from flask import request
@app.route ("/echo")
@skitai.websocket (timeout = 60)
def echo ():
ws = request.environ ["websocket"]
ws.send ("hello")
while 1:
message = yield
if not message:
return #strop iterating
yield "ECHO:" + message
CAUTION: DO NOT use any blocking tasks in this function.
If you use blocking task like databaase query, use like this.
from flask import request
def onopen ():
return 'Welcome Client 0'
@app.route ("/echo")
@skitai.websocket ("message", 60, onopen = onopen)
def echo ():
ws = request.environ ["websocket"]
ws.send ('1st: ' + request.args.get ("message", ""))
return "2nd: " + request.args.get ("message", "")
Some browsers do not support WWW-Authenticate on websocket like Safari, then Skitai currently disables WWW-Authenticate for websocket, so you should be careful for requiring secured messages.
@skitai.on ("event")
def on_event (k):
assert k == 100
skitai.emit ("event", 100)
skitai.register_g ('DB_STATUS')
skitai.register_g ('TOTAL_CLIENTS')
skitai.mount ('/', app)
skitai.run ()
Your app can use like dictionary.
skitai.g ['DB_STATUS'] = 4
This object will be shared by all workers.
skitai.add_thread_task ()
returns Future
like object. It also has
fetch ()
, one ()
and wait ()
methods. If exception has been occured,
it raised immedately.
import skitai
import time
def hello (name):
time.sleep (1)
return f'hello, {name}'
@app.route ("/")
def index ():
task1 = skitai.add_thread_task (hello, 'hans')
task2 = skitai.add_process_task (hello, 'roh')
task3 = skitai.add_subprocess_task ('ls -al')
return "\n.join ([
task1.fetch (),
task2.fetch (),
task3.fetch ()
])
# turned off starting with
skitai.log_off ('/static/')
# turned off ending with
skitai.log_off ('*.css')
# you can multiple args
skitai.log_off ('*.css', '/static/images/', '/static/js/')
skitai.enalbe_file_logging (path = '/var/log/skitai')
Note: /var/log/skitai
should have writable permission.
Request log file will be created at /var/log/skitai/{SERVICE_NAME},
- request.log
By default server/app log messages are displayed on stdout because for syslog
.
But if you want to log to files for these,
skitai.enalbe_file_logging (path = '/var/log/skitai', ['request', 'app', 'server'])
Also log files will be created at /var/log/skitai/{SERVICE_NAME},
- server.log
- app.log
Blank seperated items of log line are,
- log date
- log time
- client ip or proxy ip
- request host: default '-' if not available
- request methods
- request uri
- request version
- request body size
- reply code
- reply body size
- global transaction ID: for backtracing request if multiple backends related
- local transaction ID: for backtracing request if multiple backends related
- username when HTTP auth: default '-', wrapped by double quotations if value available
- bearer token when HTTP bearer auth
- referer: default '-', wrapped by double quotations if value available
- user agent: default '-', wrapped by double quotations if value available
- x-forwared-for, real client ip before through proxy
- Skitai engine's worker ID like W0, W1 (Worker #0, #1,...)
- current number of active connections include not only clients but your backend/upstream servers
- duration ms for request handling
- duration ms for transfering response data
To test full mounted resources with many protocols.
import skitai
import route_guide_pb2
def test_myservice ():
with skitai.test_client ("./serve.py", 6000) as cli:
resp = cli.get ("/")
assert resp.status_code == 200
assert "something" in resp.text
resp = cli.get ("/static/app.js")
assert resp.status_code == 200
# test JSON API
resp = cli.axios.get ('/pets/45')
assert resp.status_code == 200
assert resp.josn ()['id']== 45
# test thru HTTP/2
resp = cli.http2.get ("/")
assert resp.status_code == 200
assert "something" in resp.text
with cli.stub ('/api') as stub:
resp = stub.get ('/profiles')
assert resp.status_code == 200
# test RPCs
with cli.jsonrpc ("/rpc2") as stub:
assert stub.add (4, 10) == 14
with cli.xmlrpc ("/rpc2") as stub:
assert stub.add (4, 10) == 14
with cli.grpc () as stub:
summary = stub.RecordRoute (point_iter ())
assert isinstance (summary, route_guide_pb2.RouteSummary)
For end-to-end test using Chrome driver,
sudo apt update
sudo apt -y install chromium-chromedriver
sudo pip3 install -Ur selenium lxml==4.4.0 cssselect==0.9.1 html5lib==0.999999999
import skitai
def test_myservice ():
with skitai.test_client ("./serve.py", 6000) as cli:
# E2E test
with cli.driver as driver:
driver.navigate ('/apis/auth/login?return_url=%2Farticles%3Fcategory%3D3')
email_input = driver.one ("input[type=text]", 'visible')
assert email_input, "cannot find username input"
email_input.send_keys ("{}\t{}\n".format (user, pw))
driver.one ('img[src="/assets/image/icon_home.png"]', 'visible')
e = driver.one ('header')
assert 'My Page' in e.text
driver.capture ()
To test already running server on port 6000, remove script path,
with skitai.test_client (port = 6000) as cli:
resp = cli.get ("/")
- skitai.add_async_task (coro, after_request_callback = None)
after_request_callback
spec is:
def after_request_callback (context, content, exc_info = None):
...
if not has_hooks and not depends:
return context.send_content_async (content)
context.thread_executor.submit (postprocess, context, content, exc_info, depends, hooks)
Refer usage for APIs.
-
0.56 (Jan, 2023)
- improving sync router performance
- improving http2/3 request processing performance
-
0.55 (Dec, 2022)
- remove websocket spec. WS_GROUPCHAT
-
0.52 (Sep, 2022)
- remove
--autoconf
option
- remove
-
0.49 (Aug, 2022)
- change
was
conventional argument intocontext
- change
-
0.48 (Jul, 2022)
- add
skitai.add_async_task (coro, after_request_callback = None, response_callback = None)
- add
was.send_content_async (content, threading = False)
- add
skitai.add_thread_task (func, *args, **kargs)
- add
skitai.add_process_task (func, *args, **kargs)
- add
skitai.add_subprocess_task (shell_command)
- add
-
0.47 (Jul, 2022)
- refactor
skitai.tasks
, it keeps core task objects and executor and remains has been moved toatila.coroutine.tasks
- refactor
-
0.46 (Apr, 2022)
- response 503 on request timeout
- integrate wsgi_apps.bus and skitai.EVBUS
- add skitai.emit () and @skitai.on ()
- fix django reloading bug
- remove features: async services, api gateway, proxypass, proxy, cluster, cachefs
-
0.45 (Apr, 2022)
- refactor websocket and gRPC bistreaming
-
0.43 (Apr, 2022)
- move coroutine code to Atila
-
0.42 (Mar, 2022)
- add async/await routing feature
-
0.41 (Mar, 2022)
- deprecate was.db (...)
-
0.38 (May, 2021)
- revoke disconnected requests before passing app
- refactoring package structure
- update README
- generating Nginx base configurations
- add
-disable-static
option - fix infinite retry loop when app loading failed
-
0.36 (Apr, 2021)
- add Preference.set_static () and Preference.set_media ()
- change default export script from export.py to wsgi.py
- rewrite this documentation
- rename coreauest to Task
-
0.35 (Feb 2020)
- drop support win32 platform officially
- add coroutine with corequest
- add was.cursor ()
- add skitai.set_503_estimated_timeout (timeout)
- multiple Atila apps and ONE Not-Atila app can be mounted to same path
- add was.stub ()
- drop supporing Python 3.5 officially
- add was.flashfile with auto deletion file name
- disable pebble_ executor by process cleanup problem
- drop officicial support for pypy3, few perf. improvement but some compatable errors
- use pebble_ for timeout managed process pool
- remove ujson-ia dependency, it may have memory leak
- reengineering threading locks for asynconnect, http_channels and http2/3 handlers
- from version 0.35.2, required aioquic>=0.9 if you need HTTP/3
- add dict () method to was.Tasks
- add 'filter' argument to was.Thread, Process and Subprocess
- change dependency: ujosn => ujson-ia
- add app.config.PRETTY_JSON (default is False, 2 spaces indented JSON format)
- drop support Python 3.5 officially
- catch SIGHUP for reloading worker process
- add skitai command: install, remove and update for systemctl script
- add sktai.get_option (*options)
- make aioquic installation optional
- update Django reloader for 2.xx
- fix corequest cache expiring
- support h3-25, h3-26
- add skitai.set_max_was_clones_per_thread (val)
- fix corequest cache
- deprecate corequest.Task(s) keyword arguments, instead add meta argument
- enable Range header to WSGI content
-
0.34 (Jan 2020)
- fix proxy and proxypass for PATCH method
- add
--devel
and--silent
runtime options - remove
--production
runtime options - lower version comaptible: change app bootstraping function name: bootstrap -> __setup__
-
0.32 (Oct 2019)
- initiate HTTP3+QUIC, you can test HTTP/3 with Chrome Canary
-
0.31 (Sep 2019)
- change handling command line options, required rs4>=0.2.5.0
- add skitai.set_smtp ()
- remove protobuf, redis, pymongo and psycopg2 from requirements, if you need these, install them maually
- skitai.preference () can be used with context
- fix http/2 response delaying when body is not exist
- skitai.enable_forward () can forward to single domain
- add dropping root privileges when Skitai run with sudo for using under 1024 ports etc.
- refix: master process does not drop root privileges for clean resources
- fix reloading for file mounted apps
- confirmed to work on PyPy3
-
0.30 (Sep 2019)
- skitai.websocket spec changed, lower version compatable
-
0.29 (Aug 2019)
- add was.Subprocess
- add handlers for Range, If-Range, If-Unmodified-Since, If-Match headers
- asyncore and asynchat are vendored as rs4.asyncore and chat, because they will be exsanguinated from standard Python library. Mr. Rossum has been listed up on my mortal enemy list
- deprecated: was.Future and was.Futures, it doesn't need. for using returning (), use corequest.returning () and was.Tasks.returning ()
- new corequest.pth package
- over 100 unit tests
-
0.28 (Feb 2019)
- fix auto reloading bug in case multiple apps are mounted
- add was.Thread () and was.Process ()
- add @skitai.states () decorator
- rename skitai.deflu () => skitai.register_states ()
- add corequest object explaination and corequest based model example
- drop SQLAlchemy query statement object
- fix https proxypass, and add proxypass remapping
- add was.transaction ()
- update psycopg2 connection parameter: async => async_ for Py3.7 compatablity
- replace from data_or_thow (), one_or_throw () to fetch (), one ()
- fix HTTP2 server push and add was.push ()
- getwait () and getswait () are integrated into dispatch ()
- add data_or_throw () and one_or_throw ()
- was.promise has been deprecated, use was.futures: see Atila documentation
- reinstate gc.collect () schedule
- fix GTXID
- fix app reloader
- remove gc.collect () schedule
- support SQLAlchemy query statement object
- removed sugar methods: was.getjson, getxml, postjson, ..., instead use headers parameter or app.config.default_request_type
- skitai.win32service has been moved to rs4.psutil.win32service
- improve 'was' magic method search speed
- seperate skitai.saddle into atila
-
0.27.6 (Jan 2019)
- rename directory decorative to services
- change from skital.saddle.contrib.decorative to skital.saddle.contrib.services
-
0.27.3 (May 2018)
- remove -v option from skitai and smtpda
- add script: skitai
- remove scripts: skitai-smtpda and skitai-cron
- remove skitai.enable_smtpda (), skitai.cron ()
-
0.27.2 (May 2018)
- add was.request.get_real_ip () and was.request.is_private_ip ()
- fix CORS preflight
-
0.27.1 (May 2018)
- sqlphile bug fixed and change requirements
-
0.27 (Apr 2018)
- add app.setup_sqlphile ()
- add @app.mounted_or_reloaded decorator
- removed @app.auth_required, added @app.authorization_required (auth_type)
- rename @app.preworks -> @app.run_before and @app.postworks -> @app.run_after
- add @app.bearer_handler
- add was.mkjwt and was.dejwt
- add was.timestamp amd was.uniqid
- renamed was.token -> was.mktoken
- renamed api -> API, for_api -> Fault
- skitai.use_django_models has been deprecated, use skitai.alias
- functions are integrated skitai.mount_django into skitai.mount, skitai.alias_django into skitai.alias
- fix empty payload posting
- add was.partial and was.basepath
- raise NameError when non-exists funtion name to was.ap
- fix default arg is missing on was.ab
- add skitai.launch and saddle.make_client for unittest
0.26 (May 2017)
-
0.26.18 (Jan 2018)
- fix HTTP2 trailers
- fix HTTP2 flow control window
- remove was.response.traceback(), use was.response.for_ap (traceback = True)
- rename was.sqlmap to was.sql
- add @app.auth_required and @app.auth_not_required decorator
- change default export script to export.py
- remove app reloading progress:
- before:
- before_umount (was)
- umounted (wac)
- before_remount (wac): deprecated
- remounted (was): deprecated
- now:
- before_reload (was)
- reloaded (was)
- before:
- change app.model_signal () to app.redirect_signal (), add @app.on_signal ()
- change skitai.addlu to skitai.deflu (args, ...)
- add @app.if_file_modified
- add @app.preworks and @app.postworks
- fix HTTP/2 remote flow control window
- fix app.before_mount decorator exxcute point
- add was.gentemp () for generating temp file name
- add was.response.throw (), was.response.for_api() and was.response.traceback()
- add @app.websocket_config (spec, timeout, onopen_func, onclose_func, encoding)
- was.request.get_remote_addr considers X-Forwarded-For header value if exists
- add param keep param to was.csrf_verify()
- add and changed app life cycle decorators:
- before_mount (wac)
- mounted (was)
- before_remount (wac)
- remounted (was)
- before_umount (was)
- umounted (wac)
- add skitai.saddle.contrib.django,auth for integrating Django authorization
- change was.token(),was.detoken(), was.rmtoken()
- add jsonrpc executor
- add some methods to was.djnago: login (), logout (), authenticate () and update_session_auth_hash ()
- add app.testpass_required decorator
- add decorative concept
-
0.26.17 (Dec 2017)
- can run SMTP Delivery Agent and Task Scheduler with config file
- add error_handler (prev errorhandler) decorator
- add default_error_handler (prev defaulterrorhandler) decorator
- add login_handler, login_required decorator
- add permission_handler, permission_required decorator
- add app events emitting
- add was.csrf_token_input, was.csrf_token and was.csrf_verify()
- make session iterable
- prevent changing function spec by decorator
- change params of use_django_models: (settings_path, alias), skitai.mount_django (point, wsgi_path, pref = pref (True), dbalias = None, host = "default")
-
0.26.16 (Oct 2017)
- add app.sqlmaps
- add use_django_models (settings_path), skitai.mount_django (point, wsgi_path, pref = pref (True), host = "default")
- fix mbox, add app.max_client_body_size
- add skitai.addlu (args, ...)
- fix promise and proxing was objects
- change method name from skitai.set_network_timeout to set_erquest_timeout
- fix getwait, getswait. get timeout mis-working
- fix backend_keep_alive default value from 10 to 1200
- fix dbi reraise on error
- JSON as arguments
-
0.26.15
- added request.form () and request.dict ()
- support Django auto reload by restarting workers
- change DNS query default protocol from TCP to UDP (posix only)
- add skitai.set_proxy_keep_alive (channel = 60, tunnel = 600) and change default proxy keep alive to same values
- increase https tunnel keep alive timeout to 600 sec.
- fix broad event bus
- add getjson, deletejson, this request automatically add header 'Accept: application/json'
- change default request content-type from json to form data, if you post/put json data, you should change postjson/putjson
- add skitai.trackers (args,...) that is equivalant to skitai.lukeys ([args])
- fix mounting module
- app.storage had been remove officially, I cannot find any usage. but unoficially it will be remains by some day
- add skitai.lukeys () and fix inconsistency of was.setlu & was.getlu between multi workers
- was.storage had been remove
- add skitai.set_worker_critical_point ()
- fix result object caching
- add app.model_signal (), was.setlu () and was.getlu ()
-
0.26.14
- add app.storage and was.storage
- removed wac._backend and wac._upstream, use @app.mounted and @app.umount
- replaced app.listen by app.on_broadcast
-
0.26.13
- add skitai.log_off (path,...)
- add reply content-type to request log, and change log format
- change posix process display name
-
0.26.12
- change event decorator: @app.listen -> @app.on_broadcast
- adaptation to h2 3.0.1
- fix http2 flow controling
- fix errorhandler and add defaulterrorhandler
- fix WSGI response handler
- fix cross app URL building
- Django can be mounted
- fix smtpda & default var directory
- optimize HTTP/2 response data
- fix HTTP/2 logging when empty response body
- http_response.outgoing is replaced by deque
- change default mime-type from text/plain to application/octet-stream in response header
- HTTP response optimized
-
0.26.10
- start making pytest scripts
- add was-wide broadcast event bus: @app.listen (event), was.broadcast (event, args...) and @was.broadcast_after (event)
- add app-wide event bus: @app.on (event), was.emit (event, args...) and @was.emit_after (event)
- remove @app.listento (event) and was.emit (event, args...)
-
0.26.9
- add event bus: @app.listento (event) and was.emit (event, args...)
-
0.26.8
- fix websocket GROUPCHAT
- add was.apps
- was.ab works between apps are mounted seperatly
-
0.26.7
- add custom error template on Saddle
- add win32 service tools
- change class method name from make_request () to backend ()
- retry once if database is disconnected by keep-live timeout
- drop wac.make_dbo () and wac.make_stub ()
-
0.26.6
- add wac.make_dbo (), wac.make_stub () and wac.make_request ()
- wac.ajob () has been removed
- change repr name from wasc to wac
- websocket design spec, WEBSOCKET_DEDICATE_THREADSAFE has been removed and WEBSOCKET_THREADSAFE is added
- fix websocket, http2, https proxy tunnel timeout, related set_network_timeout () is recently added
-
0.26.4.1: add set_network_timeout (timoutout = 30) and change default keep alive timeout from 2 to 30
-
0.26.4: fix incomplete sending when resuested with connection: close header
-
0.26.3.7: enforce response to HTTP version 1.1 for 1.0 CONNECT with 1.0 request
-
0.26.3.5: revert multiworkers
-
0.26.3.2: fix multiworkers
-
0.26.3.1: update making for self-signing certification
-
0.26.3: add skitai.enable_forward
-
0.26.2.1: remove was.promise.render_all (), change method name from was.promise.push () to send ()
-
0.26.2: change name from was.aresponse to was.promise
-
0.26.1.1: add skitai.abspath (*args)
-
0.26.1: fix proxy & proxypass, add was.request.scheme and update examples
-
change development status to Beta
-
fix Saddlery routing
-
disable WWW-Authenticate on websocket protocol
-
support CORS (Cross Origin Resource Sharing)
-
support PATCH method
-
runtime app preferences and add init.bootstrap (preference)
-
fix route caching
-
auto reload sub modules in package directory, if app.use_reloader = True
-
new was.request.json ()
-
integrated with skitaid package, single app file can contain all configure options
-
level down developement status to alpha
-
fix sqlite3 closing
0.25 (Feb 2017)
- 0.25.7: fix fancy url, non content-type header post/put request
- 0.25.6: add Chameleon_ template engine
- 0.25.5: app.jinja_overlay ()'s default args become jinja2 default
- 0.25.4.8: fix proxy retrying
- 0.25.4 license changed from BSD to MIT, fix websocket init at single thread
- 0.25.3 handler of promise args spec changed, class name is changed from AsyncResponse to Promise
- 0.25.2 fix promise exception handling, promise can send streaming chunk data
- 0.25.1 change app.jinja_overlay () default values and number of args, remove raw line statement
- project name chnaged: Skitai Library => Skitai App Engine
0.24 (Jan 2017)
- 0.24.9 bearer token handler spec changed
- 0.24.8 add async response, fix await_fifo bug
- 0.24.7 fix websocket shutdown
- 0.24.5 eliminate client arg from websocket config
- 0.24.5 eliminate event arg from websocket config
- fix proxy tunnel
- fix websocket cleanup
- change websocket initializing, not lower version compatible
- WEBSOCKET_MULTICAST deprecated, and new WEBSOCKET_GROUPCHAT does not create new thread any more
0.23 (Jan 2017)
- ready_producer_fifo only activated when proxy or reverse proxy is enabled, default deque will be used
- encoding argument was eliminated from REST call
- changed RPC, DBO request spec
- added gRPC as server and client
- support static files with http2
- fix POST method on reverse proxying
0.22 (Jan 2017)
-
0.22.7 fix was.upload(), was.post*()
-
0.22.5 fix xml-rpc service
-
0.22.4 fix proxy
-
0.22.3
- fix https REST, XML-RPC call
- fix DB pool
-
0.22
- Skitai REST/RPC call now uses HTTP2 if possible
- Fix HTTP2 opening with POST method
- Add logging on disconnecting of Websocket, HTTP2, Proxy Tunnel channels
- See News
0.21 (Dec 2016)
- 0.21.17 - fix JWT base64 padding problem
- 0.21.8 - connected with MongoDB asynchronously
- 0.21.3 - add JWT (JSON Web Token) handler, see
Skitai WSGI App Engine Daemon
_ - 0.21.2 - applied global/local-transaction-ID to app logging: was.log (msg, logtype), was.traceback ()
- 0.21 - change request log format, add global/local-transaction-ID to log file for backtrace
0.20 (Dec 2016)
- 0.20.15 - minor optimize asynconnect, I wish
- 0.20.14 - fix Redis connector's threading related error
- 0.20.4 - add Redis connector
- 0.20 - add API Gateway access handler
0.19 (Dec 2016)
- Reengineering was.request methods, fix disk caching
0.18 (Dec 2016)
- 0.18.11 - default content-type of was.post(), was.put() has been changed from 'application/x-www-form-urlencoded' to 'application/json'. if you use this method currently, you SHOULD change method name to was.postform()
- 0.18.7 - response contents caching has been applied to all was.request services (except websocket requests).
0.17 (Oct 2016)
Skitai WSGI App Engine Daemon
_ is seperated
0.16 (Sep 2016)
- 0.16.20 fix SSL proxy and divide into package for proxy & websocket_handler
- 0.16.19 fix HTTP2 cookie
- 0.16.18 fix handle large request body
- 0.16.13 fix thread locking for h2.Connection
- 0.16.11 fix pushing promise and response on Firefox
- 0.16.8 fix pushing promise and response
- 0.16.6 add several configs to was.app.config for limiting post body size from client
- 0.16.5 add method: was.response.hint_promise (uri) for sending HTP/2 PUSH PROMISE frame
- 0.16.3 fix flow control window
- 0.16.2 fix HTTP/2 Uprading for "http" URIs (RFC 7540 Section 3.2)
- 0.16 HTTP/2.0 implemented with hyper-h2_
0.15 (Mar 2016)
- fixed fancy URL routing
- add Websocket design spec: WEBSOCKET_DEDICATE_THREADSAFE
- fixed Websocket keep-alive timeout
- fixed fancy URL routing
- 'was.cookie.set()' method prototype has been changed.
- added Named Session & Messaging Box
- fix select error when closed socket, thanks to spam-proxy-bots
- add mimetypes for .css .js
- fix debug output
- fix asynconnect.maintern
- fix loosing end of compressed content
- fix app reloading, @shutdown
- fix XMLRPC response and POST length
- add was.mbox.search (), change spec was.mbox.get ()
- fix routing bugs & was.ab()
- add saddle.Saddlery class for app packaging
- @app.startup, @app.onreload, @app.shutdown arguments has been changed
0.14 (Feb 2016)
- fix proxy occupies CPU on POST method failing
- was.log(), was.traceback() added
- fix valid time in message box
- changed @failed_request arguments and can return custom error page
- changed skitaid.py command line options, see 'skitaid.py --help'
- batch task scheduler added
- e-mail sending fixed
- was.session.getv () added
- was.response spec. changed
- SQLite3 DB connection added
0.13 (Feb 2016)
- was.mbox, was.g, was.redirect, was.render added
- SQLite3 DB connection added
0.12 (Jan 2016) - Re-engineering 'was' networking, PostgreSQL & proxy modules
0.11 (Jan 2016) - Websocket implemeted
0.10 (Dec 2015) - WSGI support