diff --git a/anyserver.py b/anyserver.py index b935a1ed..261dd422 100644 --- a/anyserver.py +++ b/anyserver.py @@ -212,16 +212,16 @@ def mongrel2_handler(application, conn, debug=False): while True: if debug: - print "WAITING FOR REQUEST" + print("WAITING FOR REQUEST") # receive a request req = conn.recv() if debug: - print "REQUEST BODY: %r\n" % req.body + print("REQUEST BODY: %r\n" % req.body) if req.is_disconnect(): if debug: - print "DISCONNECT" + print("DISCONNECT") continue # effectively ignore the disconnect from the client # Set a couple of environment attributes a.k.a. header attributes @@ -247,7 +247,7 @@ def mongrel2_handler(application, conn, debug=False): environ['wsgi.input'] = req.body if debug: - print "ENVIRON: %r\n" % environ + print("ENVIRON: %r\n" % environ) # SimpleHandler needs file-like stream objects for # requests, errors and responses @@ -282,10 +282,10 @@ def mongrel2_handler(application, conn, debug=False): # return the response if debug: - print "RESPONSE: %r\n" % response + print("RESPONSE: %r\n" % response) if errors: if debug: - print "ERRORS: %r" % errors + print("ERRORS: %r" % errors) data = "%s\r\n\r\n%s" % (data, errors) conn.reply_http( req, data, code=code, status=status, headers=headers) @@ -355,8 +355,8 @@ def main(): dest='workers', help='number of workers number') (options, args) = parser.parse_args() - print 'starting %s on %s:%s...' % ( - options.server, options.ip, options.port) + print('starting %s on %s:%s...' % ( + options.server, options.ip, options.port)) run(options.server, options.ip, options.port, logging=options.logging, profiler=options.profiler_dir, options=options) diff --git a/gluon/compileapp.py b/gluon/compileapp.py index 0d61afb3..dd7aca00 100644 --- a/gluon/compileapp.py +++ b/gluon/compileapp.py @@ -15,7 +15,7 @@ Note: import re import fnmatch -import os +import os, sys import copy import random from gluon._compat import builtin, PY2, unicodeT, to_native, to_bytes, iteritems, basestring, reduce, xrange, long, reload @@ -52,7 +52,10 @@ is_gae = settings.global_settings.web2py_runtime_gae is_jython = settings.global_settings.is_jython pjoin = os.path.join -marshal_header_size = 8 if PY2 else 12 +if PY2: + marshal_header_size = 8 +else: + marshal_header_size = 16 if sys.version_info[1] >= 7 else 12 TEST_CODE = \ r""" diff --git a/gluon/main.py b/gluon/main.py index 9867c87b..e634de37 100644 --- a/gluon/main.py +++ b/gluon/main.py @@ -31,7 +31,7 @@ from gluon._compat import Cookie, urllib2 from gluon.fileutils import abspath, write_file from gluon.settings import global_settings -from gluon.utils import web2py_uuid +from gluon.utils import web2py_uuid, unlocalised_http_header_date from gluon.admin import add_path_first, create_missing_folders, create_missing_app_folders from gluon.globals import current @@ -199,8 +199,7 @@ def serve_controller(request, response, session): ('Content-Type', contenttype('.' + request.extension)), ('Cache-Control', 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0'), - ('Expires', time.strftime('%a, %d %b %Y %H:%M:%S GMT', - time.gmtime())), + ('Expires', unlocalised_http_header_date(time.gmtime())), ('Pragma', 'no-cache')] for key, value in default_headers: response.headers.setdefault(key, value) diff --git a/gluon/streamer.py b/gluon/streamer.py index 288c71b3..2872f7a5 100644 --- a/gluon/streamer.py +++ b/gluon/streamer.py @@ -16,6 +16,7 @@ import time import re import errno from gluon.http import HTTP +from gluon.utils import unlocalised_http_header_date from gluon.contenttype import contenttype from gluon._compat import PY2 @@ -74,7 +75,7 @@ def stream_file_or_304_or_206( stat_file = os.stat(static_file) fsize = stat_file[stat.ST_SIZE] modified = stat_file[stat.ST_MTIME] - mtime = time.strftime('%a, %d %b %Y %H:%M:%S GMT', time.gmtime(modified)) + mtime = unlocalised_http_header_date(time.gmtime(modified)) headers.setdefault('Content-Type', contenttype(static_file)) headers.setdefault('Last-Modified', mtime) headers.setdefault('Pragma', 'cache') diff --git a/gluon/tools.py b/gluon/tools.py index 5cc4bf40..0910b6c4 100644 --- a/gluon/tools.py +++ b/gluon/tools.py @@ -3447,7 +3447,8 @@ class Auth(AuthAPI): if log is DEFAULT: log = self.messages['reset_password_log'] userfield = self.settings.login_userfield or 'username' \ - if 'username' in table_user.fields else 'email' + if self.settings.login_userfield or 'username' \ + in table_user.fields else 'email' if userfield == 'email': table_user.email.requires = [ IS_EMAIL(error_message=self.messages.invalid_email), @@ -3455,7 +3456,7 @@ class Auth(AuthAPI): error_message=self.messages.invalid_email)] if not self.settings.email_case_sensitive: table_user.email.requires.insert(0, IS_LOWER()) - else: + elif userfield == 'username': table_user.username.requires = [ IS_IN_DB(self.db, table_user.username, error_message=self.messages.invalid_username)] diff --git a/gluon/utils.py b/gluon/utils.py index ea738047..c3e5207f 100644 --- a/gluon/utils.py +++ b/gluon/utils.py @@ -462,3 +462,51 @@ def local_html_escape(data, quote=False): data = data.replace(b'"', b""") data = data.replace(b'\'', b"'") return data + + +def unlocalised_http_header_date(data): + """ + Converts input datetime to format defined by RFC 7231, section 7.1.1.1 + + Previously, %a and %b formats were used for weekday and month names, but + those are not locale-safe. uWSGI requires latin1-encodable headers and + for example in cs_CS locale, fourth day in week is not encodable in latin1, + as it's "Čt". + + Example output: Sun, 06 Nov 1994 08:49:37 GMT + """ + + short_weekday = { + "0": "Sun", + "1": "Mon", + "2": "Tue", + "3": "Wed", + "4": "Thu", + "5": "Fri", + "6": "Sat", + }.get(time.strftime("%w", data)) + + day_of_month = time.strftime("%d", data) + + short_month = { + "01": "Jan", + "02": "Feb", + "03": "Mar", + "04": "Apr", + "05": "May", + "06": "Jun", + "07": "Jul", + "08": "Aug", + "09": "Sep", + "10": "Oct", + "11": "Nov", + "12": "Dec", + }.get(time.strftime("%m", data)) + + year_and_time = time.strftime("%Y %H:%M:%S GMT") + + return "{}, {} {} {}".format( + short_weekday, + day_of_month, + short_month, + year_and_time)