##// END OF EJS Templates
hgweb: Quote filenames when downloading raw files.
Thomas Arendsen Hein -
r6137:1c0e7afe default
parent child Browse files
Show More
@@ -1,100 +1,102
1 # hgweb/request.py - An http request from either CGI or the standalone server.
1 # hgweb/request.py - An http request from either CGI or the standalone server.
2 #
2 #
3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
4 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
4 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
5 #
5 #
6 # This software may be used and distributed according to the terms
6 # This software may be used and distributed according to the terms
7 # of the GNU General Public License, incorporated herein by reference.
7 # of the GNU General Public License, incorporated herein by reference.
8
8
9 import socket, cgi, errno
9 import socket, cgi, errno
10 from mercurial.i18n import gettext as _
10 from mercurial.i18n import gettext as _
11 from common import ErrorResponse, statusmessage
11 from common import ErrorResponse, statusmessage
12
12
13 class wsgirequest(object):
13 class wsgirequest(object):
14 def __init__(self, wsgienv, start_response):
14 def __init__(self, wsgienv, start_response):
15 version = wsgienv['wsgi.version']
15 version = wsgienv['wsgi.version']
16 if (version < (1, 0)) or (version >= (2, 0)):
16 if (version < (1, 0)) or (version >= (2, 0)):
17 raise RuntimeError("Unknown and unsupported WSGI version %d.%d"
17 raise RuntimeError("Unknown and unsupported WSGI version %d.%d"
18 % version)
18 % version)
19 self.inp = wsgienv['wsgi.input']
19 self.inp = wsgienv['wsgi.input']
20 self.err = wsgienv['wsgi.errors']
20 self.err = wsgienv['wsgi.errors']
21 self.threaded = wsgienv['wsgi.multithread']
21 self.threaded = wsgienv['wsgi.multithread']
22 self.multiprocess = wsgienv['wsgi.multiprocess']
22 self.multiprocess = wsgienv['wsgi.multiprocess']
23 self.run_once = wsgienv['wsgi.run_once']
23 self.run_once = wsgienv['wsgi.run_once']
24 self.env = wsgienv
24 self.env = wsgienv
25 self.form = cgi.parse(self.inp, self.env, keep_blank_values=1)
25 self.form = cgi.parse(self.inp, self.env, keep_blank_values=1)
26 self._start_response = start_response
26 self._start_response = start_response
27 self.server_write = None
27 self.server_write = None
28 self.headers = []
28 self.headers = []
29
29
30 def __iter__(self):
30 def __iter__(self):
31 return iter([])
31 return iter([])
32
32
33 def read(self, count=-1):
33 def read(self, count=-1):
34 return self.inp.read(count)
34 return self.inp.read(count)
35
35
36 def respond(self, status, type=None, filename=None, length=0):
36 def respond(self, status, type=None, filename=None, length=0):
37 if self._start_response is not None:
37 if self._start_response is not None:
38
38
39 self.httphdr(type, filename, length)
39 self.httphdr(type, filename, length)
40 if not self.headers:
40 if not self.headers:
41 raise RuntimeError("request.write called before headers sent")
41 raise RuntimeError("request.write called before headers sent")
42
42
43 for k, v in self.headers:
43 for k, v in self.headers:
44 if not isinstance(v, str):
44 if not isinstance(v, str):
45 raise TypeError('header value must be string: %r' % v)
45 raise TypeError('header value must be string: %r' % v)
46
46
47 if isinstance(status, ErrorResponse):
47 if isinstance(status, ErrorResponse):
48 status = statusmessage(status.code)
48 status = statusmessage(status.code)
49 elif status == 200:
49 elif status == 200:
50 status = '200 Script output follows'
50 status = '200 Script output follows'
51 elif isinstance(status, int):
51 elif isinstance(status, int):
52 status = statusmessage(status)
52 status = statusmessage(status)
53
53
54 self.server_write = self._start_response(status, self.headers)
54 self.server_write = self._start_response(status, self.headers)
55 self._start_response = None
55 self._start_response = None
56 self.headers = []
56 self.headers = []
57
57
58 def write(self, thing):
58 def write(self, thing):
59 if hasattr(thing, "__iter__"):
59 if hasattr(thing, "__iter__"):
60 for part in thing:
60 for part in thing:
61 self.write(part)
61 self.write(part)
62 else:
62 else:
63 thing = str(thing)
63 thing = str(thing)
64 try:
64 try:
65 self.server_write(thing)
65 self.server_write(thing)
66 except socket.error, inst:
66 except socket.error, inst:
67 if inst[0] != errno.ECONNRESET:
67 if inst[0] != errno.ECONNRESET:
68 raise
68 raise
69
69
70 def writelines(self, lines):
70 def writelines(self, lines):
71 for line in lines:
71 for line in lines:
72 self.write(line)
72 self.write(line)
73
73
74 def flush(self):
74 def flush(self):
75 return None
75 return None
76
76
77 def close(self):
77 def close(self):
78 return None
78 return None
79
79
80 def header(self, headers=[('Content-Type','text/html')]):
80 def header(self, headers=[('Content-Type','text/html')]):
81 self.headers.extend(headers)
81 self.headers.extend(headers)
82
82
83 def httphdr(self, type=None, filename=None, length=0, headers={}):
83 def httphdr(self, type=None, filename=None, length=0, headers={}):
84 headers = headers.items()
84 headers = headers.items()
85 if type is not None:
85 if type is not None:
86 headers.append(('Content-Type', type))
86 headers.append(('Content-Type', type))
87 if filename:
87 if filename:
88 filename = (filename.split('/')[-1]
89 .replace('\\', '\\\\').replace('"', '\\"'))
88 headers.append(('Content-Disposition',
90 headers.append(('Content-Disposition',
89 'inline; filename=%s' % filename.split('/')[-1]))
91 'inline; filename="%s"' % filename))
90 if length:
92 if length:
91 headers.append(('Content-Length', str(length)))
93 headers.append(('Content-Length', str(length)))
92 self.header(headers)
94 self.header(headers)
93
95
94 def wsgiapplication(app_maker):
96 def wsgiapplication(app_maker):
95 '''For compatibility with old CGI scripts. A plain hgweb() or hgwebdir()
97 '''For compatibility with old CGI scripts. A plain hgweb() or hgwebdir()
96 can and should now be used as a WSGI application.'''
98 can and should now be used as a WSGI application.'''
97 application = app_maker()
99 application = app_maker()
98 def run_wsgi(env, respond):
100 def run_wsgi(env, respond):
99 return application(env, respond)
101 return application(env, respond)
100 return run_wsgi
102 return run_wsgi
@@ -1,23 +1,23
1 #!/bin/sh
1 #!/bin/sh
2
2
3 hg init test
3 hg init test
4 cd test
4 cd test
5 mkdir sub
5 mkdir sub
6 cat >sub/sometext.txt <<ENDSOME
6 cat >'sub/some "text".txt' <<ENDSOME
7 This is just some random text
7 This is just some random text
8 that will go inside the file and take a few lines.
8 that will go inside the file and take a few lines.
9 It is very boring to read, but computers don't
9 It is very boring to read, but computers don't
10 care about things like that.
10 care about things like that.
11 ENDSOME
11 ENDSOME
12 hg add sub/sometext.txt
12 hg add 'sub/some "text".txt'
13 hg commit -d "1 0" -m "Just some text"
13 hg commit -d "1 0" -m "Just some text"
14 hg serve -p $HGPORT -A access.log -E error.log -d --pid-file=hg.pid
14 hg serve -p $HGPORT -A access.log -E error.log -d --pid-file=hg.pid
15 cat hg.pid >> $DAEMON_PIDS
15 cat hg.pid >> $DAEMON_PIDS
16 ("$TESTDIR/get-with-headers.py" localhost:$HGPORT '/?f=37afcac6d393;file=sub/sometext.txt;style=raw' content-type content-length content-disposition) >getoutput.txt &
16 ("$TESTDIR/get-with-headers.py" localhost:$HGPORT '/?f=a23bf1310f6e;file=sub/some%20%22text%22.txt;style=raw' content-type content-length content-disposition) >getoutput.txt &
17
17
18 sleep 5
18 sleep 5
19 kill `cat hg.pid`
19 kill `cat hg.pid`
20 sleep 1 # wait for server to scream and die
20 sleep 1 # wait for server to scream and die
21 cat getoutput.txt
21 cat getoutput.txt
22 cat access.log error.log | \
22 cat access.log error.log | \
23 sed 's/^[^ ]*\( [^[]*\[\)[^]]*\(\].*\)$/host\1date\2/'
23 sed 's/^[^ ]*\( [^[]*\[\)[^]]*\(\].*\)$/host\1date\2/'
@@ -1,10 +1,10
1 200 Script output follows
1 200 Script output follows
2 content-type: text/plain
2 content-type: text/plain
3 content-length: 157
3 content-length: 157
4 content-disposition: inline; filename=sometext.txt
4 content-disposition: inline; filename="some \"text\".txt"
5
5
6 This is just some random text
6 This is just some random text
7 that will go inside the file and take a few lines.
7 that will go inside the file and take a few lines.
8 It is very boring to read, but computers don't
8 It is very boring to read, but computers don't
9 care about things like that.
9 care about things like that.
10 host - - [date] "GET /?f=37afcac6d393;file=sub/sometext.txt;style=raw HTTP/1.1" 200 -
10 host - - [date] "GET /?f=a23bf1310f6e;file=sub/some%20%22text%22.txt;style=raw HTTP/1.1" 200 -
General Comments 0
You need to be logged in to leave comments. Login now