##// END OF EJS Templates
configs: fixed IP extraction in gunicorn
super-admin -
r1025:f2b029ea default
parent child Browse files
Show More
@@ -11,6 +11,7 b' import time'
11 import threading
11 import threading
12 import traceback
12 import traceback
13 import random
13 import random
14 import socket
14 from gunicorn.glogging import Logger
15 from gunicorn.glogging import Logger
15
16
16
17
@@ -254,6 +255,74 b' def post_request(worker, req, environ, r'
254 _check_memory_usage(worker)
255 _check_memory_usage(worker)
255
256
256
257
258 def _filter_proxy(ip):
259 """
260 Passed in IP addresses in HEADERS can be in a special format of multiple
261 ips. Those comma separated IPs are passed from various proxies in the
262 chain of request processing. The left-most being the original client.
263 We only care about the first IP which came from the org. client.
264
265 :param ip: ip string from headers
266 """
267 if ',' in ip:
268 _ips = ip.split(',')
269 _first_ip = _ips[0].strip()
270 return _first_ip
271 return ip
272
273
274 def _filter_port(ip):
275 """
276 Removes a port from ip, there are 4 main cases to handle here.
277 - ipv4 eg. 127.0.0.1
278 - ipv6 eg. ::1
279 - ipv4+port eg. 127.0.0.1:8080
280 - ipv6+port eg. [::1]:8080
281
282 :param ip:
283 """
284 def is_ipv6(ip_addr):
285 if hasattr(socket, 'inet_pton'):
286 try:
287 socket.inet_pton(socket.AF_INET6, ip_addr)
288 except socket.error:
289 return False
290 else:
291 return False
292 return True
293
294 if ':' not in ip: # must be ipv4 pure ip
295 return ip
296
297 if '[' in ip and ']' in ip: # ipv6 with port
298 return ip.split(']')[0][1:].lower()
299
300 # must be ipv6 or ipv4 with port
301 if is_ipv6(ip):
302 return ip
303 else:
304 ip, _port = ip.split(':')[:2] # means ipv4+port
305 return ip
306
307
308 def get_ip_addr(environ):
309 proxy_key = 'HTTP_X_REAL_IP'
310 proxy_key2 = 'HTTP_X_FORWARDED_FOR'
311 def_key = 'REMOTE_ADDR'
312 _filters = lambda x: _filter_port(_filter_proxy(x))
313
314 ip = environ.get(proxy_key)
315 if ip:
316 return _filters(ip)
317
318 ip = environ.get(proxy_key2)
319 if ip:
320 return _filters(ip)
321
322 ip = environ.get(def_key, '0.0.0.0')
323 return _filters(ip)
324
325
257 class RhodeCodeLogger(Logger):
326 class RhodeCodeLogger(Logger):
258 """
327 """
259 Custom Logger that allows some customization that gunicorn doesn't allow
328 Custom Logger that allows some customization that gunicorn doesn't allow
@@ -270,5 +339,58 b' class RhodeCodeLogger(Logger):'
270 msecs = int((now - long(now)) * 1000)
339 msecs = int((now - long(now)) * 1000)
271 return time.strftime(self.datefmt, time.localtime(now)) + '.{0:03d}'.format(msecs)
340 return time.strftime(self.datefmt, time.localtime(now)) + '.{0:03d}'.format(msecs)
272
341
342 def atoms(self, resp, req, environ, request_time):
343 """ Gets atoms for log formatting.
344 """
345 status = resp.status
346 if isinstance(status, str):
347 status = status.split(None, 1)[0]
348 atoms = {
349 'h': get_ip_addr(environ),
350 'l': '-',
351 'u': self._get_user(environ) or '-',
352 't': self.now(),
353 'r': "%s %s %s" % (environ['REQUEST_METHOD'],
354 environ['RAW_URI'],
355 environ["SERVER_PROTOCOL"]),
356 's': status,
357 'm': environ.get('REQUEST_METHOD'),
358 'U': environ.get('PATH_INFO'),
359 'q': environ.get('QUERY_STRING'),
360 'H': environ.get('SERVER_PROTOCOL'),
361 'b': getattr(resp, 'sent', None) is not None and str(resp.sent) or '-',
362 'B': getattr(resp, 'sent', None),
363 'f': environ.get('HTTP_REFERER', '-'),
364 'a': environ.get('HTTP_USER_AGENT', '-'),
365 'T': request_time.seconds,
366 'D': (request_time.seconds * 1000000) + request_time.microseconds,
367 'M': (request_time.seconds * 1000) + int(request_time.microseconds/1000),
368 'L': "%d.%06d" % (request_time.seconds, request_time.microseconds),
369 'p': "<%s>" % os.getpid()
370 }
371
372 # add request headers
373 if hasattr(req, 'headers'):
374 req_headers = req.headers
375 else:
376 req_headers = req
377
378 if hasattr(req_headers, "items"):
379 req_headers = req_headers.items()
380
381 atoms.update({"{%s}i" % k.lower(): v for k, v in req_headers})
382
383 resp_headers = resp.headers
384 if hasattr(resp_headers, "items"):
385 resp_headers = resp_headers.items()
386
387 # add response headers
388 atoms.update({"{%s}o" % k.lower(): v for k, v in resp_headers})
389
390 # add environ variables
391 environ_variables = environ.items()
392 atoms.update({"{%s}e" % k.lower(): v for k, v in environ_variables})
393
394 return atoms
273
395
274 logger_class = RhodeCodeLogger
396 logger_class = RhodeCodeLogger
General Comments 0
You need to be logged in to leave comments. Login now