Ticket #4519: twisted-web-request-args-2-rlotun.patch
| File twisted-web-request-args-2-rlotun.patch, 5.6 KB (added by rlotun, 21 months ago) |
|---|
-
twisted/web/http.py
563 563 sentLength = 0 # content-length of response, or total bytes sent via chunking 564 564 etag = None 565 565 lastModified = None 566 args = None 566 _args = None # .args is a property that will parse and store request args here 567 _orig_method = None # http method request originally called with 568 _orig_uri = None # uri request originally called with 567 569 path = None 568 570 content = None 569 571 _forceSSL = 0 … … 725 727 @param version: The HTTP version of this request. 726 728 """ 727 729 self.content.seek(0,0) 728 self.args = {}729 730 self.stack = [] 730 731 731 self.method, self.uri = command, path 732 # since argument parsing is now deferred to a property we want to 733 # have explicit references to the original method and original uri 734 # to parse against in case they are subsequently changed by 735 # application code 736 self.method, self.uri = self._orig_method, self._orig_uri = command, path 732 737 self.clientproto = version 733 738 x = self.uri.split('?', 1) 734 739 … … 736 741 self.path = self.uri 737 742 else: 738 743 self.path, argstring = x 739 self.args = parse_qs(argstring, 1)740 744 741 745 # cache the client and server information, we'll need this later to be 742 746 # serialized and sent with the request so CGIs will work remotely 743 747 self.client = self.channel.transport.getPeer() 744 748 self.host = self.channel.transport.getHost() 745 749 746 # Argument processing 747 args = self.args 750 self.process() 751 752 def args(self): 753 """ 754 Return query or post arguments dict of the request on demand. 755 756 If the arguments have already been parsed, they will be returned 757 immediately, otherwise the first access will parse arguments 758 from the url or body and return it. 759 """ 760 if self._args is not None: 761 return self._args 762 763 # Parse query string arguments 764 x = self._orig_uri.split('?', 1) 765 if len(x) != 1: 766 self._args = parse_qs(x[1], 1) 767 else: 768 self._args = {} 769 770 # Parse post body arguments 771 args = self._args 748 772 ctype = self.requestHeaders.getRawHeaders('content-type') 749 773 if ctype is not None: 750 774 ctype = ctype[0] 751 775 752 if self.method == "POST" and ctype: 776 if self._orig_method == "POST" and ctype: 777 778 # preserve location in content stream 779 curr_loc = self.content.tell() 780 self.content.seek(0, 0) 781 753 782 mfd = 'multipart/form-data' 754 783 key, pdict = cgi.parse_header(ctype) 755 784 if key == 'application/x-www-form-urlencoded': … … 768 797 self.channel.transport.loseConnection() 769 798 return 770 799 raise 771 self.content.seek(0, 0) 800 # restore previous location in content stream 801 self.content.seek(curr_loc, 0) 802 return args 803 args = property(args) 772 804 773 self.process()774 775 776 805 def __repr__(self): 777 806 return '<%s %s %s>'% (self.method, self.uri, self.clientproto) 778 807 -
twisted/web/test/test_http.py
826 826 827 827 self.runRequest(httpRequest, MyRequest) 828 828 829 def test_formNotParsedUntilArgsAccessed(self): 830 """ 831 The post body is not parsed until request.args is accessed. 832 """ 833 query = 'key=value' 834 httpRequest = '''\ 835 POST / HTTP/1.0 836 Content-Length: %d 837 Content-Type: application/x-www-form-urlencoded 838 839 %s''' % (len(query), query) 840 841 testcase = self 842 self.parse_qs_calls = [] 843 orig_parse_qs = http.parse_qs 844 def parse_qs(query_string, *args): 845 testcase.parse_qs_calls.append(query_string) 846 return orig_parse_qs(query_string, *args) 847 self.patch(http, 'parse_qs', parse_qs) 848 class MyRequest(http.Request): 849 def process(self): 850 testcase.assertEquals(self.method, "POST") 851 # Arguments have not been parsed: 852 testcase.assertEquals(testcase.parse_qs_calls, []) 853 # But have been after first access to self.args: 854 testcase.assertEquals(self.args["key"], ["value"]) 855 testcase.assertEquals(testcase.parse_qs_calls, [query]) 856 857 # Reading from the content file-like must produce the entire 858 # request body. 859 testcase.assertEquals(self.content.read(), query) 860 testcase.didRequest = 1 861 self.finish() 862 863 self.runRequest(httpRequest, MyRequest) 864 829 865 def testMissingContentDisposition(self): 830 866 req = '''\ 831 867 POST / HTTP/1.0 … … 864 900 # The tempfile API used to create content returns an 865 901 # instance of a different type depending on what platform 866 902 # we're running on. The point here is to verify that the 867 # request body is in a file that's on the filesystem. 903 # request body is in a file that's on the filesystem. 868 904 # Having a fileno method that returns an int is a somewhat 869 905 # close approximation of this. -exarkun 870 906 testcase.assertIsInstance(self.content.fileno(), int)
