chore(performance): Removes NetflixSession from the core addon & adds a HTTP proxy...
[plugin.video.netflix.git] / resources / lib / UniversalAnalytics / HTTPLog.py
1 #!/usr/bin/python
2 ###############################################################################
3 # Formatting filter for urllib2's HTTPHandler(debuglevel=1) output
4 # Copyright (c) 2013, Analytics Pros
5
6 # This project is free software, distributed under the BSD license. 
7 # Analytics Pros offers consulting and integration services if your firm needs 
8 # assistance in strategy, implementation, or auditing existing work.
9 ###############################################################################
10
11
12 import sys, re, os
13 from cStringIO import StringIO
14
15
16
17 class BufferTranslator(object):
18     """ Provides a buffer-compatible interface for filtering buffer content.
19     """
20     parsers = []
21
22     def __init__(self, output):
23         self.output = output
24         self.encoding = getattr(output, 'encoding', None)
25
26     def write(self, content):
27         content = self.translate(content)
28         self.output.write(content)
29
30
31     @staticmethod
32     def stripslashes(content):
33         return content.decode('string_escape')
34
35     @staticmethod
36     def addslashes(content):
37         return content.encode('string_escape')
38
39     def translate(self, line):
40         for pattern, method in self.parsers:
41             match = pattern.match(line)
42             if match:
43                 return method(match)
44             
45         return line
46             
47
48
49 class LineBufferTranslator(BufferTranslator):
50     """ Line buffer implementation supports translation of line-format input
51         even when input is not already line-buffered. Caches input until newlines 
52         occur, and then dispatches translated input to output buffer.
53     """
54     def __init__(self, *a, **kw):
55         self._linepending = []
56         super(LineBufferTranslator, self).__init__(*a, **kw)
57     
58     def write(self, _input):
59         lines = _input.splitlines(True)
60         for i in range(0, len(lines)):
61             last = i
62             if lines[i].endswith('\n'):
63                 prefix = len(self._linepending) and ''.join(self._linepending) or ''
64                 self.output.write(self.translate(prefix + lines[i]))
65                 del self._linepending[0:]
66                 last = -1
67                 
68         if last >= 0:
69             self._linepending.append(lines[ last ])
70
71
72     def __del__(self):
73         if len(self._linepending):
74             self.output.write(self.translate(''.join(self._linepending)))
75
76
77 class HTTPTranslator(LineBufferTranslator):
78     """ Translates output from |urllib2| HTTPHandler(debuglevel = 1) into
79         HTTP-compatible, readible text structures for human analysis.
80     """
81
82     RE_LINE_PARSER = re.compile(r'^(?:([a-z]+):)\s*(\'?)([^\r\n]*)\2(?:[\r\n]*)$')
83     RE_LINE_BREAK = re.compile(r'(\r?\n|(?:\\r)?\\n)')
84     RE_HTTP_METHOD = re.compile(r'^(POST|GET|HEAD|DELETE|PUT|TRACE|OPTIONS)')
85     RE_PARAMETER_SPACER = re.compile(r'&([a-z0-9]+)=')
86
87     @classmethod
88     def spacer(cls, line):
89         return cls.RE_PARAMETER_SPACER.sub(r' &\1= ', line)
90
91     def translate(self, line):
92
93         parsed = self.RE_LINE_PARSER.match(line)
94
95         if parsed:
96             value = parsed.group(3)
97             stage = parsed.group(1)
98
99             if stage == 'send': # query string is rendered here
100                 return '\n# HTTP Request:\n' + self.stripslashes(value)
101             elif stage == 'reply':
102                 return '\n\n# HTTP Response:\n' + self.stripslashes(value)
103             elif stage == 'header':
104                 return value + '\n'
105             else:
106                 return value
107
108
109         return line
110
111
112 def consume(outbuffer = None): # Capture standard output
113     sys.stdout = HTTPTranslator(outbuffer or sys.stdout)
114     return sys.stdout
115
116
117 if __name__ == '__main__':
118     consume(sys.stdout).write(sys.stdin.read())
119     print '\n'
120
121 # vim: set nowrap tabstop=4 shiftwidth=4 softtabstop=0 expandtab textwidth=0 filetype=python foldmethod=indent foldcolumn=4