23 import random |
23 import random |
24 import base64 |
24 import base64 |
25 from hashlib import sha1 # pylint: disable=E0611 |
25 from hashlib import sha1 # pylint: disable=E0611 |
26 from Cookie import SimpleCookie |
26 from Cookie import SimpleCookie |
27 from calendar import timegm |
27 from calendar import timegm |
28 from datetime import date |
28 from datetime import date, datetime |
29 from urlparse import urlsplit |
29 from urlparse import urlsplit |
30 from itertools import count |
30 from itertools import count |
31 from warnings import warn |
31 from warnings import warn |
32 |
32 |
33 from rql.utils import rqlvar_maker |
33 from rql.utils import rqlvar_maker |
84 """abstract HTTP request, should be extended according to the HTTP backend |
84 """abstract HTTP request, should be extended according to the HTTP backend |
85 Immutable attributes that describe the received query and generic configuration |
85 Immutable attributes that describe the received query and generic configuration |
86 """ |
86 """ |
87 ajax_request = False # to be set to True by ajax controllers |
87 ajax_request = False # to be set to True by ajax controllers |
88 |
88 |
89 def __init__(self, vreg, https=False, form=None): |
89 def __init__(self, vreg, https=False, form=None, headers={}): |
90 """ |
90 """ |
91 :vreg: Vregistry, |
91 :vreg: Vregistry, |
92 :https: boolean, s this a https request |
92 :https: boolean, s this a https request |
93 :form: Forms value |
93 :form: Forms value |
94 """ |
94 """ |
105 else: |
105 else: |
106 self.uiprops = vreg.config.uiprops |
106 self.uiprops = vreg.config.uiprops |
107 self.datadir_url = vreg.config.datadir_url |
107 self.datadir_url = vreg.config.datadir_url |
108 #: raw html headers that can be added from any view |
108 #: raw html headers that can be added from any view |
109 self.html_headers = HTMLHead(self) |
109 self.html_headers = HTMLHead(self) |
|
110 #: received headers |
|
111 self._headers_in = Headers() |
|
112 for k, v in headers.iteritems(): |
|
113 self._headers_in.addRawHeader(k, v) |
110 #: form parameters |
114 #: form parameters |
111 self.setup_params(form) |
115 self.setup_params(form) |
112 #: dictionary that may be used to store request data that has to be |
116 #: dictionary that may be used to store request data that has to be |
113 #: shared among various components used to publish the request (views, |
117 #: shared among various components used to publish the request (views, |
114 #: controller, application...) |
118 #: controller, application...) |
776 :param includeparams: |
779 :param includeparams: |
777 boolean indicating if GET form parameters should be kept in the path |
780 boolean indicating if GET form parameters should be kept in the path |
778 """ |
781 """ |
779 raise NotImplementedError() |
782 raise NotImplementedError() |
780 |
783 |
781 def get_header(self, header, default=None): |
784 # http headers ############################################################ |
782 """return the value associated with the given input HTTP header, |
785 |
783 raise KeyError if the header is not set |
786 ### incoming headers |
784 """ |
787 |
785 raise NotImplementedError() |
788 def get_header(self, header, default=None, raw=True): |
786 |
789 """return the value associated with the given input header, raise |
|
790 KeyError if the header is not set |
|
791 """ |
|
792 if raw: |
|
793 return self._headers_in.getRawHeaders(header, [default])[0] |
|
794 return self._headers_in.getHeader(header, default) |
|
795 |
|
796 def header_accept_language(self): |
|
797 """returns an ordered list of preferred languages""" |
|
798 acceptedlangs = self.get_header('Accept-Language', raw=False) or {} |
|
799 for lang, _ in sorted(acceptedlangs.iteritems(), key=lambda x: x[1], |
|
800 reverse=True): |
|
801 lang = lang.split('-')[0] |
|
802 yield lang |
|
803 |
|
804 def header_if_modified_since(self): |
|
805 """If the HTTP header If-modified-since is set, return the equivalent |
|
806 date time value (GMT), else return None |
|
807 """ |
|
808 mtime = self.get_header('If-modified-since', raw=False) |
|
809 if mtime: |
|
810 # :/ twisted is returned a localized time stamp |
|
811 return datetime.fromtimestamp(mtime) + GMTOFFSET |
|
812 return None |
|
813 |
|
814 ### outcoming headers |
787 def set_header(self, header, value, raw=True): |
815 def set_header(self, header, value, raw=True): |
788 """set an output HTTP header""" |
816 """set an output HTTP header""" |
789 if raw: |
817 if raw: |
790 # adding encoded header is important, else page content |
818 # adding encoded header is important, else page content |
791 # will be reconverted back to unicode and apart unefficiency, this |
819 # will be reconverted back to unicode and apart unefficiency, this |
829 value_parser = value_sort_key = None |
857 value_parser = value_sort_key = None |
830 accepteds = self.get_header(header, '') |
858 accepteds = self.get_header(header, '') |
831 values = _parse_accept_header(accepteds, value_parser, value_sort_key) |
859 values = _parse_accept_header(accepteds, value_parser, value_sort_key) |
832 return (raw_value for (raw_value, parsed_value, score) in values) |
860 return (raw_value for (raw_value, parsed_value, score) in values) |
833 |
861 |
834 def header_if_modified_since(self): |
|
835 """If the HTTP header If-modified-since is set, return the equivalent |
|
836 mx date time value (GMT), else return None |
|
837 """ |
|
838 raise NotImplementedError() |
|
839 |
|
840 def demote_to_html(self): |
862 def demote_to_html(self): |
841 """helper method to dynamically set request content type to text/html |
863 """helper method to dynamically set request content type to text/html |
842 |
864 |
843 The global doctype and xmldec must also be changed otherwise the browser |
865 The global doctype and xmldec must also be changed otherwise the browser |
844 will display '<[' at the beginning of the page |
866 will display '<[' at the beginning of the page |
848 raise Exception("Can't demote to html from an ajax context. You " |
870 raise Exception("Can't demote to html from an ajax context. You " |
849 "should change force-html-content-type to yes " |
871 "should change force-html-content-type to yes " |
850 "in the instance configuration file.") |
872 "in the instance configuration file.") |
851 self.set_content_type('text/html') |
873 self.set_content_type('text/html') |
852 self.main_stream.set_doctype(TRANSITIONAL_DOCTYPE_NOEXT) |
874 self.main_stream.set_doctype(TRANSITIONAL_DOCTYPE_NOEXT) |
|
875 |
|
876 # xml doctype ############################################################# |
853 |
877 |
854 def set_doctype(self, doctype, reset_xmldecl=True): |
878 def set_doctype(self, doctype, reset_xmldecl=True): |
855 """helper method to dynamically change page doctype |
879 """helper method to dynamically change page doctype |
856 |
880 |
857 :param doctype: the new doctype, e.g. '<!DOCTYPE html>' |
881 :param doctype: the new doctype, e.g. '<!DOCTYPE html>' |