##// 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 11 import threading
12 12 import traceback
13 13 import random
14 import socket
14 15 from gunicorn.glogging import Logger
15 16
16 17
@@ -254,6 +255,74 b' def post_request(worker, req, environ, r'
254 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 326 class RhodeCodeLogger(Logger):
258 327 """
259 328 Custom Logger that allows some customization that gunicorn doesn't allow
@@ -270,5 +339,58 b' class RhodeCodeLogger(Logger):'
270 339 msecs = int((now - long(now)) * 1000)
271 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 396 logger_class = RhodeCodeLogger
General Comments 0
You need to be logged in to leave comments. Login now