source: titan/mediathek/localhoster/lib/net.py @ 42564

Last change on this file since 42564 was 42564, checked in by obi, 16 months ago

tithek optimize cloudflare and switch foxx to cloudflare

File size: 27.5 KB
Line 
1'''
2    common XBMC Module
3    Copyright (C) 2011 t0mm0
4
5    This program is free software: you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation, either version 3 of the License, or
8    (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program.  If not, see <http://www.gnu.org/licenses/>.
17'''
18
19from __future__ import division
20import urllib, urllib2, re, sys
21from time import sleep
22
23import random
24import cookielib
25import gzip
26import re
27import StringIO
28import urllib
29import urllib2
30import socket
31from urlparse import urlparse
32from urlparse import urlunparse
33import time
34
35BR_VERS = [
36    ['%s.0' % i for i in xrange(18, 50)],
37    ['37.0.2062.103', '37.0.2062.120', '37.0.2062.124', '38.0.2125.101', '38.0.2125.104', '38.0.2125.111', '39.0.2171.71', '39.0.2171.95', '39.0.2171.99', '40.0.2214.93', '40.0.2214.111',
38     '40.0.2214.115', '42.0.2311.90', '42.0.2311.135', '42.0.2311.152', '43.0.2357.81', '43.0.2357.124', '44.0.2403.155', '44.0.2403.157', '45.0.2454.101', '45.0.2454.85', '46.0.2490.71',
39     '46.0.2490.80', '46.0.2490.86', '47.0.2526.73', '47.0.2526.80', '48.0.2564.116', '49.0.2623.112', '50.0.2661.86'],
40    ['11.0'],
41    ['8.0', '9.0', '10.0', '10.6']]
42WIN_VERS = ['Windows NT 10.0', 'Windows NT 7.0', 'Windows NT 6.3', 'Windows NT 6.2', 'Windows NT 6.1', 'Windows NT 6.0', 'Windows NT 5.1', 'Windows NT 5.0']
43FEATURES = ['; WOW64', '; Win64; IA64', '; Win64; x64', '']
44RAND_UAS = ['Mozilla/5.0 ({win_ver}{feature}; rv:{br_ver}) Gecko/20100101 Firefox/{br_ver}',
45            'Mozilla/5.0 ({win_ver}{feature}) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/{br_ver} Safari/537.36',
46            'Mozilla/5.0 ({win_ver}{feature}; Trident/7.0; rv:{br_ver}) like Gecko',
47            'Mozilla/5.0 (compatible; MSIE {br_ver}; {win_ver}{feature}; Trident/6.0)']
48def get_ua():
49#    try: last_gen = int(kodi.get_setting('last_ua_create'))
50    try: last_gen = 0
51    except: last_gen = 0
52#    if not kodi.get_setting('current_ua') or last_gen < (time.time() - (7 * 24 * 60 * 60)):
53    index = random.randrange(len(RAND_UAS))
54    versions = {'win_ver': random.choice(WIN_VERS), 'feature': random.choice(FEATURES), 'br_ver': random.choice(BR_VERS[index])}
55    user_agent = RAND_UAS[index].format(**versions)
56        # logger.log('Creating New User Agent: %s' % (user_agent), log_utils.LOGDEBUG)
57#        kodi.set_setting('current_ua', user_agent)
58#        kodi.set_setting('last_ua_create', str(int(time.time())))
59#    else:
60#        user_agent = kodi.get_setting('current_ua')
61    return user_agent
62
63class HeadRequest(urllib2.Request):
64    '''A Request class that sends HEAD requests'''
65    def get_method(self):
66        return 'HEAD'
67
68class Net:
69    '''
70    This class wraps :mod:`urllib2` and provides an easy way to make http
71    requests while taking care of cookies, proxies, gzip compression and
72    character encoding.
73   
74    Example::
75   
76        from addon.common.net import Net
77        net = Net()
78        response = net.http_GET('http://xbmc.org')
79        print response.content
80    '''
81    IE_USER_AGENT = 'Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; AS; rv:11.0) like Gecko'
82    FF_USER_AGENT = 'Mozilla/5.0 (Windows NT 6.3; rv:36.0) Gecko/20100101 Firefox/36.0'
83    IOS_USER_AGENT = 'Mozilla/5.0 (iPhone; CPU iPhone OS 6_0 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/6.0 Mobile/10A5376e Safari/8536.25'
84    ANDROID_USER_AGENT = 'Mozilla/5.0 (Linux; Android 4.4.2; Nexus 4 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.114 Mobile Safari/537.36'
85
86    _cj = cookielib.MozillaCookieJar()
87
88    _proxy = None
89    _user_agent = 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.72 Safari/537.36'
90    _accept = 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8'
91    _http_debug = False
92    _socket_timeout = 60
93
94    def __init__(self, cookie_file='', proxy='', user_agent='', 
95                 http_debug=False, accept=_accept, socket_timeout=_socket_timeout, cloudflare=False):
96        '''
97        Kwargs:
98            cookie_file (str): Full path to a file to be used to load and save
99            cookies to.
100           
101            proxy (str): Proxy setting (eg.
102            ``'http://user:pass@example.com:1234'``)
103           
104            user_agent (str): String to use as the User Agent header. If not
105            supplied the class will use a default user agent (chrome)
106           
107            http_debug (bool): Set ``True`` to have HTTP header info written to
108            the XBMC log for all requests.
109           
110            accept (str) : String to use as HTTP Request Accept header.
111           
112            socket_timeout (int): time in seconds for socket connections to wait until time out
113
114            cloudflare (bool): Set ``True`` to check all requests that raise HTTPError 503 for Cloudflare challenge and solve
115            This can be changed per request as well, see http_GET, http_PUSH
116        '''
117   
118        #Set socket timeout - Useful for slow connections
119        socket.setdefaulttimeout(socket_timeout)
120
121        # empty jar for each instance rather than scope of the import
122        self._cloudflare_jar = cookielib.MozillaCookieJar()
123
124        self.cloudflare = cloudflare
125        if cookie_file:
126            self.set_cookies(cookie_file)
127        if proxy:
128            self.set_proxy(proxy)
129        if user_agent:
130            self.set_user_agent(user_agent)
131        self._http_debug = http_debug
132        self._update_opener()
133       
134   
135    def set_cookies(self, cookie_file):
136        '''
137        Set the cookie file and try to load cookies from it if it exists.
138       
139        Args:
140            cookie_file (str): Full path to a file to be used to load and save
141            cookies to.
142        '''
143        try:
144            self._cj.load(cookie_file, ignore_discard=True)
145            self._update_opener()
146            return True
147        except:
148            return False
149       
150   
151    def get_cookies(self):
152        '''Returns A dictionary containing all cookie information by domain.'''
153        return self._cj._cookies
154
155
156    def save_cookies(self, cookie_file):
157        '''
158        Saves cookies to a file.
159       
160        Args:
161            cookie_file (str): Full path to a file to save cookies to.
162        '''
163        self._cj.save(cookie_file, ignore_discard=True)       
164
165       
166    def set_proxy(self, proxy):
167        '''
168        Args:
169            proxy (str): Proxy setting (eg.
170            ``'http://user:pass@example.com:1234'``)
171        '''
172        self._proxy = proxy
173        self._update_opener()
174
175       
176    def get_proxy(self):
177        '''Returns string containing proxy details.'''
178        return self._proxy
179       
180       
181    def set_user_agent(self, user_agent):
182        '''
183        Args:
184            user_agent (str): String to use as the User Agent header.
185        '''
186        self._user_agent = user_agent
187
188       
189    def get_user_agent(self):
190        '''Returns user agent string.'''
191        return self._user_agent
192
193
194    def _update_opener(self, cloudflare_jar=False):
195        """
196        Builds and installs a new opener to be used by all future calls to
197        :func:`urllib2.urlopen`.
198        """
199        if self._http_debug:
200            http = urllib2.HTTPHandler(debuglevel=1)
201        else:
202            http = urllib2.HTTPHandler()
203
204        if cloudflare_jar:
205            self._cloudflare_jar = cookielib.MozillaCookieJar()
206            jar = self._cloudflare_jar
207        else:
208            jar = self._cj
209
210        if self._proxy:
211            opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(jar),
212                                          urllib2.ProxyHandler({'http':
213                                                                self._proxy}),
214                                          urllib2.HTTPBasicAuthHandler(),
215                                          http)
216
217        else:
218            opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(jar),
219                                          urllib2.HTTPBasicAuthHandler(),
220                                          http)
221        urllib2.install_opener(opener)
222
223
224    def _parseJSString(self, s):
225        """
226        lambda
227        plugin.video.genesis\resources\lib\libraries\cloudflare.py
228        https://offshoregit.com/lambda81/
229        """
230        try:
231            offset=1 if s[0]=='+' else 0
232            val = int(eval(s.replace('!+[]','1').replace('!![]','1').replace('[]','0').replace('(','str(')[offset:]))
233            return val
234        except:
235            raise Exception
236
237    def checkpart(self, s, sens):
238        number = 0
239        p = 0
240        if sens == 1:
241            pos = 0
242        else:
243            pos = len(s) - 1
244
245        try:
246            while 1:
247                c = s[pos]
248
249                if ((c == '(') and (sens == 1)) or ((c == ')') and (sens == -1)):
250                    p = p + 1
251                if ((c == ')') and (sens == 1)) or ((c == '(') and (sens == -1)):
252                    p = p - 1
253                if (c == '+') and (p == 0) and (number > 1):
254                    break
255
256                number += 1
257                pos = pos + sens
258        except:
259            pass
260        if sens == 1:
261            return s[:number], number
262        else:
263            return s[-number:], number
264
265    def parseInt(self, s):
266        offset = 1 if s[0] == '+' else 0
267        chain = s.replace('!+[]', '1').replace('!![]', '1').replace('[]', '0').replace('(', 'str(')[offset:]
268
269        if '/' in chain:
270            val = chain.split('/')
271            links, sizeg = self.checkpart(val[0], -1)
272            rechts, sized = self.checkpart(val[1], 1)
273
274            if rechts.startswith('+') or rechts.startswith('-'):
275                rechts = rechts[1:]
276            gg = eval(links)
277            dd = eval(rechts)
278            chain = val[0][:-sizeg] + str(gg) + '/' + str(dd) + val[1][sized:]
279        val = float(eval(chain))
280        return val
281
282    def _extract_js(self, htmlcontent, domain):
283        line1 = re.findall('var s,t,o,p,b,r,e,a,k,i,n,g,f, (.+?)={"(.+?)":\+*(.+?)};', htmlcontent)
284        varname = line1[0][0] + '.' + line1[0][1]
285        calc = self.parseInt(line1[0][2])
286        AllLines = re.findall(';' + varname + '([*\-+])=([^;]+)', htmlcontent)
287
288        for aEntry in AllLines:
289            calc = eval(format(calc, '.17g') + str(aEntry[0]) + format(self.parseInt(aEntry[1]), '.17g'))
290        rep = calc + len(domain)
291        return format(rep, '.10f')
292
293    def _cloudflare_challenge(self, url, challenge, form_data={}, headers={}, compression=True):
294        """
295        Use _set_cloudflare to call this, not intended to be called directly.
296        Solve challenge and make request with cloudflare cookie jar
297
298        Part from:
299        lambda
300        plugin.video.genesis\resources\lib\libraries\cloudflare.py
301        https://offshoregit.com/lambda81/
302        """
303
304        jschl = re.compile('name="jschl_vc" value="(.+?)"/>').findall(challenge)[0]
305        passw = re.compile('name="pass" value="(.+?)"/>').findall(challenge)[0]
306        js = self._extract_js(challenge, url)
307
308        body = challenge
309        parsed_url = urlparse(url)
310        submit_url = "%s://%s/cdn-cgi/l/chk_jschl" % (parsed_url.scheme, parsed_url.netloc)
311        params = {}
312        try:
313            params["jschl_vc"] = re.search(r'name="jschl_vc" value="(\w+)"', body).group(1)
314            params["pass"] = re.search(r'name="pass" value="(.+?)"', body).group(1)
315            js = self._extract_js(body, parsed_url.netloc)
316        except:
317            return None
318
319        params["jschl_answer"] = js
320        sParameters = urllib.urlencode(params, True)
321        request = urllib2.Request("%s?%s" % (submit_url, sParameters))
322
323        for key in headers:
324            request.add_header(key, headers[key])
325        sleep(5)
326
327        path = urlparse(url).path
328        netloc = urlparse(url).netloc
329
330        if not netloc:
331            netloc = path
332
333#        answer = decrypt_val + len(netloc)
334        answer = js
335
336        url = url.rstrip('/')
337        query = '%s/cdn-cgi/l/chk_jschl?jschl_vc=%s&jschl_answer=%s' % (url, jschl, answer)
338
339        if 'type="hidden" name="pass"' in challenge:
340            passval = re.compile('name="pass" value="(.*?)"').findall(challenge)[0]
341            query = '%s/cdn-cgi/l/chk_jschl?pass=%s&jschl_vc=%s&jschl_answer=%s' % \
342                    (url, urllib.quote_plus(passval), jschl, answer)
343 #           time.sleep(9)
344
345        self._update_opener(cloudflare_jar=True)
346        req = urllib2.Request(query)
347        if form_data:
348            form_data = urllib.urlencode(form_data)
349            req = urllib2.Request(query, form_data)
350        req.add_header('User-Agent', self._user_agent)
351        for k, v in headers.items():
352            req.add_header(k, v)
353        if compression:
354            req.add_header('Accept-Encoding', 'gzip')
355        try:
356            response = urllib2.urlopen(req)
357        except urllib2.HTTPError as e:
358            pass
359
360
361    def _set_cloudflare(self, url, challenge, form_data={}, headers={}, compression=True):
362        """
363        Entry Point for _cloudflare_challenge
364        Calls cloudflare_challenge on netloc, not full url w/ path
365        Puts any cloudflare cookies in the main cookie jar
366        Args:
367            url (str): The URL to site of potential Cloudflare IUA.
368
369            challenge (str): html contents of the page that raised 503, containing potential Cloudflare IUA Challenge
370        Kwargs:
371            form_data (dict): A dictionary of form data if pass-through from POST.
372
373            headers (dict): A dictionary describing any headers you would like
374            to add to the request. (eg. ``{'X-Test': 'testing'}``)
375
376            compression (bool): If ``True`` (default), try to use gzip
377            compression.
378        """
379        print "aaaaaaaaa"
380        netloc = urlparse(url).netloc
381        if not netloc:
382            netloc = urlparse(url).path
383        cloudflare_url = urlunparse((urlparse(url).scheme, netloc, '', '', '', ''))
384        try:
385            print "bbbbbbbbbb"
386
387            self._cloudflare_challenge(cloudflare_url, challenge, form_data, headers, compression)
388            print "bbbbbbbbbb1"
389
390            for c in self._cloudflare_jar:
391                self._cj.set_cookie(c)
392            print "bbbbbbbbbb2"
393
394            self._update_opener()
395            print "bbbbbbbbbb3"
396
397        except:
398            print "ccccccccc"
399
400            # make sure we update to main jar
401            self._update_opener()
402            raise Exception
403
404
405    def url_with_headers(self, url, referer=None, user_agent=None, cookies=None, proxy=None, connection_timeout=None,
406                         encoding='', accept_charset='', sslcipherlist='', noshout='false', seekable='1'):
407        '''
408        Return url with Referer, User-Agent, Cookies, Proxy, Connection-Timeout, Encoding, Accept-Charset,
409        SSLCipherList, NoShout and Seekable
410        Based on: https://github.com/xbmc/xbmc/blob/master/xbmc/filesystem/CurlFile.cpp#L782
411        Args:
412            url (str): The URL to append headers to.
413
414        Kwargs:
415            referer (str): If None (default), urlunparse((urlparse(url).scheme, netloc, path, '', '', '')) is used and append if set
416
417            user_agent (str): If None (default), self._user_agent is used and append if set
418
419            cookies (bool): If ``None`` (default), use self.cloudflare as bool (False as default)
420            Append cookies to URL as well
421
422            proxy (str): If None (default), self.proxy is used and append if set
423
424            connection_timeout (str): If None (default), self._socket_timeout is used and append if set
425
426            encoding (str): append if set
427
428            accept_charset (str): append if set
429
430            sslcipherlist (str): append if set
431
432            noshout (str): 'true'/'false', skip shout, append if 'true' ('false' is kodi default)
433
434            seekable (str): '0'/'1', append if 0 ('1' is kodi default)
435        Returns:
436            http://example.com/myimage.png|Referer=%%%%%&User-Agent=%%%%%...
437        '''
438        kodi_schemes = ('special', 'plugin', 'script', 'profile')
439        if ('://' not in url) or (url.startswith(kodi_schemes)):
440            # don't waste time and return url
441            return url
442
443        _tmp = re.search('(.+?)(?:\|.*|$)', url)
444        if _tmp:
445            # trim any headers that may already be attached to url
446            url = _tmp.group(1)
447
448        if referer is not None:
449            try:
450                referer = str(referer)
451            except:
452                referer = None
453        if referer is None:
454            path = urlparse(url).path
455            netloc = urlparse(url).netloc
456            if not netloc:
457                netloc = path
458                path = ''
459            referer = urlunparse((urlparse(url).scheme, netloc, path, '', '', ''))
460            if referer == url:
461                index = path.rfind('/')
462                if index >= 0:
463                    referer = urlunparse((urlparse(url).scheme, netloc, path[:index], '', '', ''))
464        if user_agent is None:
465            user_agent = self._user_agent
466        else:
467            try:
468                user_agent = str(user_agent)
469            except:
470                user_agent = self._user_agent
471        if cookies is None:
472            cookies = self.cloudflare
473        if proxy is None:
474            proxy = self._proxy
475        if connection_timeout is None:
476            connection_timeout = self._socket_timeout
477        try:
478            connection_timeout = str(connection_timeout)
479        except:
480            connection_timeout = None
481        try:
482            if str(seekable) != '0':
483                seekable = None
484        except:
485            seekable = None
486        try:
487            if str(noshout).lower() != 'true':
488                noshout = None
489        except:
490            noshout = None
491
492        url += '|Referer=' + urllib.quote_plus(referer) + '&User-Agent=' + urllib.quote_plus(user_agent)
493        if proxy:
494            try:
495                url += '&HTTPProxy=' + urllib.quote_plus(str(proxy))
496            except:
497                pass
498        if connection_timeout:
499            url += '&Connection-Timeout=' + urllib.quote_plus(connection_timeout)
500        if encoding:
501            try:
502                url += '&Encoding=' + urllib.quote_plus(str(encoding))
503            except:
504                pass
505        if accept_charset:
506            try:
507                url += '&Accept-Charset=' + urllib.quote_plus(str(accept_charset))
508            except:
509                pass
510        if sslcipherlist:
511            try:
512                url += '&SSLCipherList=' + urllib.quote_plus(str(sslcipherlist))
513            except:
514                pass
515        if noshout:
516            url += '&NoShout=' + urllib.quote_plus(str(noshout).lower())
517        if seekable:
518            url += '&Seekable=' + urllib.quote_plus(str(seekable))
519        if cookies:
520            cookie_string = ''
521            for c in self._cj:
522                if c.domain and (c.domain.lstrip('.') in url):
523                    cookie_string += '%s=%s;' % (c.name, c.value)
524            if cookie_string:
525                url += '&Cookie=' + urllib.quote_plus(cookie_string)
526        return url
527
528
529    def http_GET(self, url, headers={}, compression=True, cloudflare=None):
530        '''
531        Perform an HTTP GET request.
532       
533        Args:
534            url (str): The URL to GET.
535           
536        Kwargs:
537            headers (dict): A dictionary describing any headers you would like
538            to add to the request. (eg. ``{'X-Test': 'testing'}``)
539
540            compression (bool): If ``True`` (default), try to use gzip
541            compression.
542
543            cloudflare (bool): If ``None`` (default), use self.cloudflare as bool (False as default)
544            On HTTPError 503 check for Cloudflare challenge and solve
545        Returns:
546            An :class:`HttpResponse` object containing headers and other
547            meta-information about the page and the page content.
548        '''
549        if cloudflare is None:
550            cloudflare = self.cloudflare
551        return self._fetch(url, headers=headers, compression=compression, cloudflare=cloudflare)
552       
553
554    def http_POST(self, url, form_data, headers={}, compression=True, cloudflare=None):
555        '''
556        Perform an HTTP POST request.
557       
558        Args:
559            url (str): The URL to POST.
560           
561            form_data (dict): A dictionary of form data to POST.
562           
563        Kwargs:
564            headers (dict): A dictionary describing any headers you would like
565            to add to the request. (eg. ``{'X-Test': 'testing'}``)
566
567            compression (bool): If ``True`` (default), try to use gzip
568            compression.
569
570            cloudflare (bool): If ``None`` (default), use self.cloudflare as bool (False as default)
571            On HTTPError 503 check for Cloudflare challenge and solve
572        Returns:
573            An :class:`HttpResponse` object containing headers and other
574            meta-information about the page and the page content.
575        '''
576        if cloudflare is None:
577            cloudflare = self.cloudflare
578        return self._fetch(url, form_data, headers=headers,
579                           compression=compression, cloudflare=cloudflare)
580
581   
582    def http_HEAD(self, url, headers={}):
583        '''
584        Perform an HTTP HEAD request.
585       
586        Args:
587            url (str): The URL to GET.
588       
589        Kwargs:
590            headers (dict): A dictionary describing any headers you would like
591            to add to the request. (eg. ``{'X-Test': 'testing'}``)
592       
593        Returns:
594            An :class:`HttpResponse` object containing headers and other
595            meta-information about the page.
596        '''
597        req = HeadRequest(url)
598        req.add_header('User-Agent', self._user_agent)
599        req.add_header('Accept', self._accept)
600        for k, v in headers.items():
601            req.add_header(k, v)
602        response = urllib2.urlopen(req)
603        return HttpResponse(response)
604
605
606    def _fetch(self, url, form_data={}, headers={}, compression=True, cloudflare=None):
607        '''
608        Perform an HTTP GET or POST request.
609       
610        Args:
611            url (str): The URL to GET or POST.
612           
613            form_data (dict): A dictionary of form data to POST. If empty, the
614            request will be a GET, if it contains form data it will be a POST.
615           
616        Kwargs:
617            headers (dict): A dictionary describing any headers you would like
618            to add to the request. (eg. ``{'X-Test': 'testing'}``)
619
620            compression (bool): If ``True`` (default), try to use gzip
621            compression.
622
623            cloudflare (bool): If ``None`` (default), use self.cloudflare as bool (False as default)
624            On HTTPError 503 check for Cloudflare challenge and solve
625        Returns:
626            An :class:`HttpResponse` object containing headers and other
627            meta-information about the page and the page content.
628        '''
629        if cloudflare is None:
630            cloudflare = self.cloudflare
631        encoding = ''
632        req = urllib2.Request(url)
633        if form_data:
634            form_data = urllib.urlencode(form_data)
635            req = urllib2.Request(url, form_data)
636        req.add_header('User-Agent', self._user_agent)
637        for k, v in headers.items():
638            req.add_header(k, v)
639        if compression:
640            req.add_header('Accept-Encoding', 'gzip')
641        if not cloudflare:
642            response = urllib2.urlopen(req)
643            return HttpResponse(response)
644        else:
645            try:
646                response = urllib2.urlopen(req)
647                return HttpResponse(response)
648            except urllib2.HTTPError as e:
649                if e.code == 503:
650                    print "111111111"
651                    try:
652                        print "222222222"
653                        self._set_cloudflare(url, e.read(), form_data, headers, compression)
654                    except:
655                        print "333333333"
656                        raise urllib2.HTTPError, e
657                    req = urllib2.Request(url)
658                    print "4"
659
660                    if form_data:
661                        form_data = urllib.urlencode(form_data)
662                        req = urllib2.Request(url, form_data)
663                    req.add_header('User-Agent', self._user_agent)
664                    for k, v in headers.items():
665                        req.add_header(k, v)
666                    if compression:
667                        req.add_header('Accept-Encoding', 'gzip')
668                    response = urllib2.urlopen(req)
669                    return HttpResponse(response)
670                else:
671                    raise urllib2.HTTPError, e
672
673
674class HttpResponse:
675    '''
676    This class represents a response from an HTTP request.
677   
678    The content is examined and every attempt is made to properly encode it to
679    Unicode.
680   
681    .. seealso::
682        :meth:`Net.http_GET`, :meth:`Net.http_HEAD` and :meth:`Net.http_POST`
683    '''
684   
685    content = ''
686    '''Unicode encoded string containing the body of the response.'''
687   
688   
689    def __init__(self, response):
690        '''
691        Args:
692            response (:class:`mimetools.Message`): The object returned by a call
693            to :func:`urllib2.urlopen`.
694        '''
695        self._response = response
696        html = response.read()
697        try:
698            if response.headers['content-encoding'].lower() == 'gzip':
699                html = gzip.GzipFile(fileobj=StringIO.StringIO(html)).read()
700        except:
701            pass
702       
703        try:
704            content_type = response.headers['content-type']
705            if 'charset=' in content_type:
706                encoding = content_type.split('charset=')[-1]
707        except:
708            pass
709
710        r = re.search('<meta\s+http-equiv="Content-Type"\s+content="(?:.+?);' +
711                      '\s+charset=(.+?)"', html, re.IGNORECASE)
712        if r:
713            encoding = r.group(1) 
714                   
715        try:
716            html = unicode(html, encoding)
717        except:
718            pass
719       
720        #try:
721        #    if response.headers['content-encoding'].lower() == 'gzip':
722        #        r = re.search('<meta\s+http-equiv="Content-Type"\s+content="(?:.+?);' + '\s+charset=(.+?)"', html, re.IGNORECASE)
723        #        if r:
724        #               encoding = r.group(1)
725        #               try:
726        #                       html = unicode(html, encoding)
727        #               except:
728        #                       pass
729        #except:
730        #    pass
731           
732        self.content = html
733   
734   
735    def get_headers(self):
736        '''Returns a List of headers returned by the server.'''
737        return self._response.info().headers
738   
739       
740    def get_url(self):
741        '''
742        Return the URL of the resource retrieved, commonly used to determine if
743        a redirect was followed.
744        '''
745        return self._response.geturl()
Note: See TracBrowser for help on using the repository browser.