Feature: introducing gunicorn as an alternative WSGI HTTP Server (#1721)

* introducing gunicorn as an alternative WSGI HTTP Server

* gunicorn, plain and simple

* dependencies for gunicorn

* correct dependencies

* fix app-context

* adding gunicorn cli

* add uid to welcome-page

* fixing bad take from other PR
This commit is contained in:
Kim Neunert 2022-05-17 10:19:27 +02:00 committed by GitHub
parent cf299ee5b4
commit 0f7677805c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 114 additions and 9 deletions

View file

@ -25,3 +25,4 @@ cbor==1.0.0
mnemonic==0.20
cryptography==3.4.7
Flask-APScheduler==1.12.3
gunicorn==20.1.0

View file

@ -184,6 +184,10 @@ flask_wtf==0.15.1 \
--hash=sha256:6ff7af73458f182180906a37a783e290bdc8a3817fe4ad17227563137ca285bf \
--hash=sha256:ff177185f891302dc253437fe63081e7a46a4e99aca61dfe086fb23e54fff2dc
# via -r requirements.in
gunicorn==20.1.0 \
--hash=sha256:9dcc4547dbb1cb284accfb15ab5667a0e5d1881cc443e0677b4882a4067a807e \
--hash=sha256:e0a968b5ba15f8a328fdfd7ab1fcb5af4470c28aaf7e55df02a99bc13138e6e8
# via -r requirements.in
hidapi==0.10.1 \
--hash=sha256:095798ae1b3d6892fb0eb7ba1ab06054f6fafe6d09bc3714d80fdbf227c98f87 \
--hash=sha256:0c92b398f6907654b07f7dbd7e06661abe9ad6119b403eb5fd3c2af4ce66a3b7 \

View file

@ -7,6 +7,7 @@ import click
from .cli_noded import bitcoind, elementsd
from .cli_ext import ext
from .cli_server import server
from .cli_gunicorn import gunicorn
logger = logging.getLogger(__name__)
@ -59,6 +60,7 @@ def entry_point(config_home, debug=False, tracerpc=False, tracerequests=False):
entry_point.add_command(server)
entry_point.add_command(gunicorn)
entry_point.add_command(ext)
entry_point.add_command(bitcoind)
entry_point.add_command(elementsd)

View file

@ -0,0 +1,65 @@
import logging
import multiprocessing
import os
import signal
import sys
import time
from os import path
from socket import gethostname
from urllib.parse import urlparse
import click
from cryptoadvance.specter.gunicorn import SpecterGunicornApp
from cryptoadvance.specter.server import create_and_init, create_app, init_app
from OpenSSL import SSL, crypto
from stem.control import Controller
from ..server import create_app, init_app
from ..specter_error import SpecterError
from ..util.tor import start_hidden_service, stop_hidden_services
logger = logging.getLogger(__name__)
@click.group()
def cli():
pass
@cli.command()
def gunicorn():
"""uses gunicorn instead of the build in development-server.
This works mostly like in https://docs.gunicorn.org/en/latest/run.html just that you use
this command rather than gunicorn. As recommended there, everything Application
specific needs to be configured via envirnoment Vars. See cryptoadvance.specter.config for
how to configure most specter-sepcific things.
Other than running gunicorn's executable directly, you can't specify command-line
paramaters. Here are some configurations you can do:
```
GUNICORN_CMD_ARGS="--bind=127.0.0.1 --workers=3" python3 -m cryptoadvance.specter gunicorn
```
You can use a config file named gunicorn.conf.py in the same directory:
```
workers=10
```
Commandline paramater trums env-var params.
You can specify hook functions in the gunicorn.conf.py, see this for a list of them:
https://docs.gunicorn.org/en/latest/settings.html#server-hooks
e.g.:
```
def on_starting(server):
print("Called just before the master process is initialized.")
```
More information directly in the gunicorn-Documentation
"""
specter_gunicorn = SpecterGunicornApp(config=None)
specter_gunicorn.run()

View file

@ -248,7 +248,8 @@ class CypressTestConfig(TestConfig):
class ProductionConfig(BaseConfig):
SECRET_KEY = secrets.token_urlsafe(16)
# Injectable, as having a random SECRET-KEY won't work for gunicorn using more than one worker (might have more issues with multiple workers, though)
SECRET_KEY = os.getenv("SECRET_KEY", secrets.token_urlsafe(16))
# There are some really slow machines out there. Creating a 2/4 multisig on an older MacBookAir
# Take already >30secs
BITCOIN_RPC_TIMEOUT = float(os.getenv("BITCOIN_RPC_TIMEOUT", "60"))

View file

@ -0,0 +1,32 @@
import multiprocessing
from cryptoadvance.specter.server import init_app, create_app, create_and_init
from gunicorn.app.wsgiapp import WSGIApplication
class SpecterGunicornApp(WSGIApplication):
def __init__(
self, config="cryptoadvance.specter.config.ProductionConfig", options=None
):
"""
i guess those are the potential options:
usage: __main__.py [-h] [-v] [-c CONFIG] [-b ADDRESS] [--backlog INT] [-w INT] [-k STRING] [--threads INT]
[--worker-connections INT] [--max-requests INT] [--max-requests-jitter INT] [-t INT] [--graceful-timeout INT]
[--keep-alive INT] [--limit-request-line INT] [--limit-request-fields INT] [--limit-request-field_size INT]
[--reload] [--reload-engine STRING] [--reload-extra-file FILES] [--spew] [--check-config] [--print-config]
[--preload] [--no-sendfile] [--reuse-port] [--chdir CHDIR] [-D] [-e ENV] [-p FILE] [--worker-tmp-dir DIR]
[-u USER] [-g GROUP] [-m INT] [--initgroups] [--forwarded-allow-ips STRING] [--access-logfile FILE]
[--disable-redirect-access-to-syslog] [--access-logformat STRING] [--error-logfile FILE] [--log-level LEVEL]
[--capture-output] [--logger-class STRING] [--log-config FILE] [--log-syslog-to SYSLOG_ADDR] [--log-syslog]
[--log-syslog-prefix SYSLOG_PREFIX] [--log-syslog-facility SYSLOG_FACILITY] [-R] [--statsd-host STATSD_ADDR]
[--dogstatsd-tags DOGSTATSD_TAGS] [--statsd-prefix STATSD_PREFIX] [-n STRING] [--pythonpath STRING]
[--paste STRING] [--proxy-protocol] [--proxy-allow-from PROXY_ALLOW_IPS] [--keyfile FILE] [--certfile FILE]
[--ssl-version SSL_VERSION] [--cert-reqs CERT_REQS] [--ca-certs FILE] [--suppress-ragged-eofs]
[--do-handshake-on-connect] [--ciphers CIPHERS] [--paste-global CONF] [--strip-header-spaces]
"""
self.options = options or {}
self.config = config
super().__init__()
def load(self):
return create_and_init(self.config)

View file

@ -93,6 +93,8 @@ def create_app(config=None):
template_folder=get_template_static_folder("templates"),
static_folder=get_template_static_folder("static"),
)
app.tor_service_id = None
app.tor_enabled = False
app.jinja_env.autoescape = select_autoescape(default_for_string=True, default=True)
logger.info(f"Configuration: {config}")
app.config.from_object(config)
@ -255,12 +257,12 @@ def init_app(app: SpecterFlask, hwibridge=False, specter=None):
return app
def create_and_init():
def create_and_init(config="cryptoadvance.specter.config.ProductionConfig"):
"""This method can be used to fill the FLASK_APP-env variable like
export FLASK_APP="src/cryptoadvance/specter/server:create_and_init()"
See Development.md to use this for debugging
"""
app = create_app()
app.app_context().push()
init_app(app)
app = create_app(config)
with app.app_context():
init_app(app)
return app

View file

@ -108,9 +108,7 @@
{% include "includes/language/language_select.jinja" %}
</div>
<h1 style="font-size: 1.8em; line-height: 1em;">
{{ _('Welcome to Specter Desktop') }}
{{ _('Welcome to Specter Desktop') }}
</h1>
{% if ('://localhost:' not in url_for('index', _external=True) and '://127.0.0.1:' not in url_for('index', _external=True)) and specter.hwi_bridge_url != "http://127.0.0.1:25441/hwi/api/" %}
<div style="margin: auto; display: block;">
@ -261,6 +259,7 @@
<a href="https://github.com/cryptoadvance/" target="_blank"><img src="{{ url_for('static', filename='img/github.svg') }}" style="width: 30px; margin: 10px; position: relative; z-index: 1;"></a>
<a href="https://twitter.com/specterwallet/" target="_blank"><img src="{{ url_for('static', filename='img/twitter.svg') }}" style="width: 30px; margin: 10px; position: relative; z-index: 1;"></a>
</div>
<span style="font-size: 0.8em;">uid: {{ specter.config_manager.data["uid"] }}</span>
</div>
{% endif %}
{% endblock %}

View file

@ -116,7 +116,6 @@ class VersionChecker:
NewConnectionError,
) as e:
logger.error(f"{e} while checking for new pypi version")
raise SpecterError("Muuh")
except Exception as e:
logger.exception(e)
latest = "unknown"