3
3
4
4
import sys
5
5
from htmlmin .main import minify
6
- from flask import request , Response , make_response , current_app , redirect , json , wrappers , url_for
6
+ from flask import request , Response , make_response , current_app , json , wrappers
7
7
from functools import update_wrapper
8
8
import gzip
9
9
import time
@@ -22,120 +22,83 @@ class FlaskOptimize(object):
22
22
_cache = {}
23
23
_timestamp = {}
24
24
25
- def __init__ (self , app = None , config = None , config_update = {}, redis = None ):
26
- ''' Global config for flask optimize foreach respond return type
25
+ def __init__ (self ,
26
+ app = None ,
27
+ config = None ):
28
+ """
29
+ Global config for flask optimize foreach respond return type
27
30
Args:
28
31
app: flask app object
29
32
config: global configure values
30
- config_update: update into default configure
31
- redis: redis client store limit requests if you enable it
32
- '''
33
+ """
33
34
if config is None :
34
- config = {'html' : {'htmlmin' : True , 'izip' : True , 'cache' : 'GET-84600' },
35
- 'json' : {'htmlmin' : False , 'izip' : True , 'cache' : False },
36
- 'text' : {'htmlmin' : False , 'izip' : True , 'cache' : 84600 },
37
- 'limit' : [100 , 60 , 84600 ],
38
- 'redirect_host' : [],
39
- 'exceed_msg' : None }
40
- config .update (config_update )
35
+ config = {
36
+ 'html' : {'htmlmin' : True , 'compress' : True , 'cache' : 'GET-84600' },
37
+ 'json' : {'htmlmin' : False , 'compress' : True , 'cache' : False },
38
+ 'text' : {'htmlmin' : False , 'compress' : True , 'cache' : 84600 }
39
+ }
41
40
42
41
self .config = config
43
- self .redis = redis
44
42
self .app = app or current_app
45
43
46
44
def optimize (self ,
47
45
dtype = 'html' ,
48
46
htmlmin = None ,
49
- izip = None ,
50
- cache = None ,
51
- limit = False ,
52
- redirect_host = True ,
53
- exceed_msg = True ):
54
- ''' Flask optimize respond using minify html, zip content and mem cache.
47
+ compress = None ,
48
+ cache = None ):
49
+ """
50
+ Flask optimize respond using minify html, zip content and mem cache.
55
51
Elastic optimization and create Cross-site HTTP requests if respond is json
56
52
Args:
57
- dtype: data type of response :
53
+ dtype: response type:
58
54
- `html` (default)
59
55
- `text`
60
56
- `json`
61
57
htmlmin: minify html
62
- None is using global config, True is enable minify html
63
- izip: send content in zip format
64
- None is using global config, True is enable zip respond
58
+ None (default): using global config,
59
+ False: disable minify html
60
+ True: enable minify html
61
+ compress: send content in compress (gzip) format
62
+ None (default): using global config,
63
+ False: disable compress response,
64
+ True: enable compress response
65
65
cache: cache content in RAM
66
- None is using global config, False or 0 to disable cache,
67
- integer value to set time cache (seconds),
68
- or string format: 'METHOD-seconds' to select METHOD cache, eg: 'GET-3600'
69
- limit: limit requests for each windows and set time temporary ban
70
- True if you want using default value,
71
- using this format [requests, window, ban expire] to set value,
72
- False is disable it
73
- redirect_host: you have 2 or more domains and want redirect all to one
74
- True if you want using default value,
75
- using this format [['host1', 'host2], 'host_redirect'] to set value,
76
- False is disable it
77
- exceed_msg: return temporary ban content
78
- True if you want using default value,
66
+ None (default): using global config,
67
+ False: disable cache,
68
+ string value: 'METHOD-seconds' to select METHOD and period cache, eg: 'GET-3600', 'GET|POST-600', ...
79
69
Examples:
80
- @optimize(dtype='html', htmlmin=True, zip =True, cache='GET-84600')
81
- '''
70
+ @optimize(dtype='html', htmlmin=True, compress =True, cache='GET-84600')
71
+ """
82
72
83
73
def _decorating_wrapper (func ):
84
74
85
75
def _optimize_wrapper (* args , ** kwargs ):
86
- try :
87
- load_config = self .config [dtype ]
88
-
89
- htmlmin_arg = load_config ['htmlmin' ] if (htmlmin is None ) else htmlmin
90
- izip_arg = load_config ['izip' ] if (izip is None ) else izip
91
- cache_arg = load_config ['cache' ] if (cache is None ) else cache
92
-
93
- if isinstance (cache_arg , str ) and request .method in cache_arg :
94
- cache_arg = int (cache_arg .split ('-' )[1 ])
95
- elif not isinstance (cache_arg , int ):
96
- cache_arg = 0
97
-
98
- limit_arg = self .config ['limit' ] if (limit is True ) else limit
99
- redirect_host_arg = self .config ['redirect_host' ] if (redirect_host is True ) else redirect_host
100
- exceed_msg_arg = self .config ['exceed_msg' ] if (exceed_msg is True ) else exceed_msg
101
- except :
102
- raise Exception ('Wrong input format' )
103
-
104
- # limit by ip
105
- if limit_arg and self .redis :
106
- limit_key = 'limitip-{}' .format (request .remote_addr )
107
- ban_key = 'banip-{}' .format (request .remote_addr )
108
-
109
- times_requested = int (self .redis .get (limit_key ) or '0' )
110
- if times_requested >= limit_arg [0 ] or self .redis .get (ban_key ):
111
- if times_requested >= limit_arg [0 ]:
112
- self .redis .delete (limit_key )
113
- self .redis .set (ban_key , 1 )
114
- self .redis .expire (ban_key , limit_arg [2 ])
115
-
116
- if self .config ['exceed_msg' ]:
117
- return redirect (url_for (exceed_msg_arg ))
118
- else :
119
- return self .crossdomain ({'status_code' : 429 })
76
+ # default values:
77
+ is_htmlmin = False
78
+ is_comress = False
79
+ period_cache = 0
80
+
81
+ if self .config .get (dtype ):
82
+ is_htmlmin = self .config .get (dtype )['htmlmin' ] if (htmlmin is None ) else htmlmin
83
+ is_comress = self .config .get (dtype )['compress' ] if (compress is None ) else compress
84
+ cache_agrs = self .config .get (dtype )['cache' ] if (cache is None ) else cache
85
+
86
+ if cache is False or cache == 0 :
87
+ period_cache = 0
88
+ elif isinstance (cache_agrs , (str , basestring )) and len (cache_agrs .split ('-' )) == 2 :
89
+ try :
90
+ period_cache = int (cache_agrs .split ('-' )[1 ]) if (request .method in cache_agrs ) else 0
91
+ except (KeyError , ValueError ):
92
+ raise ValueError ('Cache must be string with method and period cache split by "-"' )
120
93
else :
121
- self .redis .incr (limit_key , 1 )
122
- self .redis .expire (limit_key , limit_arg [1 ])
94
+ raise ValueError ('Cache must be False or string with method and period cache split by "-"' )
123
95
124
- # redirect new host
125
- if redirect_host_arg :
126
- for host in redirect_host_arg [0 ]:
127
- if host in request .url_root :
128
- redirect_url = request .url
129
- redirect_url = redirect_url .replace (host , redirect_host_arg [1 ])
130
- return redirect (redirect_url )
96
+ # init cached data
97
+ now = time .time ()
98
+ key_cache = request .url
131
99
132
- # find cached value
133
- if cache_arg :
134
- now = time .time ()
135
- key_cache = request .url
136
-
137
- if self ._timestamp .get (key_cache , now ) > now :
138
- return self ._cache [key_cache ]
100
+ if self ._timestamp .get (key_cache ) > now :
101
+ return self ._cache [key_cache ]
139
102
140
103
resp = func (* args , ** kwargs )
141
104
@@ -145,17 +108,17 @@ def _optimize_wrapper(*args, **kwargs):
145
108
resp = self .crossdomain (resp )
146
109
147
110
# min html
148
- if htmlmin_arg :
111
+ if is_htmlmin :
149
112
resp = self .validate (self .minifier , resp )
150
113
151
- # gzip
152
- if izip_arg :
153
- resp = self .validate (self .zipper , resp )
114
+ # compress
115
+ if is_comress :
116
+ resp = self .validate (self .compress , resp )
154
117
155
118
# cache
156
- if cache_arg :
119
+ if period_cache > 0 :
157
120
self ._cache [key_cache ] = resp
158
- self ._timestamp [key_cache ] = now + cache_arg
121
+ self ._timestamp [key_cache ] = now + period_cache
159
122
160
123
return resp
161
124
@@ -170,7 +133,7 @@ def validate(method, content):
170
133
return method (content )
171
134
elif isinstance (content , tuple ):
172
135
if len (content ) < 2 :
173
- raise Exception ('Content must have larger 2 elements' )
136
+ raise TypeError ('Content must have larger than 2 elements' )
174
137
175
138
return method (content [0 ]), content [1 ]
176
139
@@ -182,12 +145,15 @@ def minifier(content):
182
145
content = unicode (content , 'utf-8' )
183
146
184
147
return minify (content ,
185
- remove_comments = True , reduce_empty_attributes = True , remove_optional_attribute_quotes = False )
148
+ remove_comments = True ,
149
+ reduce_empty_attributes = True ,
150
+ remove_optional_attribute_quotes = False )
186
151
187
152
@staticmethod
188
- def zipper (content ):
189
- ''' Zip str, unicode content
190
- '''
153
+ def compress (content ):
154
+ """
155
+ Compress str, unicode content using gzip
156
+ """
191
157
resp = Response ()
192
158
if isinstance (content , Response ):
193
159
resp = content
@@ -216,8 +182,10 @@ def zipper(content):
216
182
217
183
@staticmethod
218
184
def crossdomain (content ):
219
- ''' create Cross-site HTTP requests
220
- '''
185
+ """
186
+ Create decorator Cross-site HTTP requests
187
+ see more at: http://flask.pocoo.org/snippets/56/
188
+ """
221
189
if isinstance (content , (dict , Response )):
222
190
if isinstance (content , dict ):
223
191
content = json .jsonify (content )
0 commit comments