Changesets can be listed by changeset number.
The Git repository is here.
- Revision:
- 265
- Log:
Fixed silly bug whereby HTTP headers had both the header name (key) and
header value converted to lower case, causing redirections to fail if
the path contained any upper case letters. Fixes Ticket #196.
- Author:
- rool
- Date:
- Thu Apr 23 18:57:07 +0100 2009
- Size:
- 6598 Bytes
1 | # RCVSweb - a Ruby On Rails wrapper around the Perl-based FreeBSD |
2 | # version of the CVSweb and Python-based CVShistory. |
3 | # |
4 | # See "http://www.freebsd.org/projects/cvsweb.html" and |
5 | # "http://www.jamwt.com/CVSHistory/" |
6 | # |
7 | # This wrapper was created for the sole purpose of embedding CVSweb |
8 | # output into a Rails-provided layout. This layout is shared between |
9 | # different Rails applications on one host. Using the wrapper means |
10 | # that it is not necessary to create a derived copy of the layout |
11 | # expressed in a form that CVSweb understands - instead, the layout |
12 | # can be used directly. |
13 | # |
14 | # Later extensions to the application gave it the ability to wrap |
15 | # CVShistory output too. |
16 | |
17 | class ApplicationController < ActionController::Base |
18 | |
19 | # Hub single sign-on support. |
20 | |
21 | require 'hub_sso_lib' |
22 | include HubSsoLib::Core |
23 | before_filter :hubssolib_beforehand |
24 | after_filter :hubssolib_afterwards |
25 | |
26 | # Turn of session management. |
27 | |
28 | session :off |
29 | |
30 | # The root URL action. |
31 | # |
32 | def index |
33 | redirect_to url_for(:controller => 'rcvsweb', :action => 'run') |
34 | end |
35 | |
36 | private |
37 | |
38 | # Pass the fully qualified pathname of the script that is to be |
39 | # executed and a path prefix from routing (e.g. "/view"). |
40 | |
41 | def capture_script_output(script_location, extra_prefix) |
42 | # Get the request URI in a way that works for FCGI and regular |
43 | # CGI, at least for LightTPD. Strip off the PATH_PREFIX (location |
44 | # of the Rails application) if present. |
45 | |
46 | uri = @request.env['REQUEST_URI'].dup # NOT a full URI |
47 | uri.slice!(PATH_PREFIX + '/') |
48 | |
49 | # Split off the query string section, if there is one. |
50 | |
51 | (path_info, query) = uri.split('?') |
52 | |
53 | path_info = URI.decode(path_info) unless path_info.nil? |
54 | query = URI.decode(query) unless query.nil? |
55 | |
56 | # The CGI script expects certain variables to be set up in a |
57 | # certain way. "Slow" CGI does this but FastCGI does not because |
58 | # the script executes under a different process environment |
59 | # entirely, without the benefit of server-set variables. We must |
60 | # therefore emulate the required environment by setting system |
61 | # variables before executing the CVSweb script. |
62 | |
63 | command = '' |
64 | needed = %w( |
65 | HTTP_USER_AGENT HTTP_ACCEPT_ENCODING |
66 | MOD_PERL PATH_INFO |
67 | SCRIPT_NAME SCRIPT_FILENAME |
68 | QUERY_STRING SERVER_PROTOCOL |
69 | SERVER_PORT SERVER_NAME |
70 | ) |
71 | |
72 | needed.each do |key| |
73 | |
74 | # Override certain items where we know we want a particular |
75 | # result, else use a server-set value if there is one. |
76 | |
77 | case key |
78 | when 'SCRIPT_NAME' |
79 | value = PATH_PREFIX + extra_prefix |
80 | when 'SCRIPT_FILENAME' |
81 | value = "#{RAILS_ROOT}/public/dispatch.cgi" |
82 | when 'PATH_INFO' |
83 | value = path_info || '' |
84 | value = '/' + value unless (value[0] == '/') |
85 | value = value[extra_prefix.length..-1] if (value[0..(extra_prefix.length - 1)] == extra_prefix) |
86 | when 'QUERY_STRING' |
87 | value = query |
88 | else |
89 | value = @request.env[key] || '' |
90 | end |
91 | |
92 | # Add the variable initialisation statement to the command string. |
93 | |
94 | command += "#{key}=\"#{value}\" " |
95 | |
96 | end # From needed.each |
97 | |
98 | # Add the CVSweb command to the command string and execute it. |
99 | # Return the output of the command. |
100 | |
101 | command += "#{script_location}" |
102 | return `#{command}` |
103 | end |
104 | |
105 | # Parse script output - pass the raw output data from the script and a |
106 | # filename to use in the event that the output isn't of a recognised type |
107 | # and has to be sent raw to the client. |
108 | |
109 | def parse_script_output(output, filename) |
110 | # The Views expect to use the @output instance variable, so we operate |
111 | # on that from the beginning. |
112 | |
113 | @output = output |
114 | |
115 | # The command should have included HTTP headers; split the two. |
116 | |
117 | pos = @output.index("\r\n\r\n") |
118 | short = true unless pos |
119 | pos = @output.index("\n\n") unless pos |
120 | headstr = @output.slice!(0..pos + (short ? 1 : 3)) if pos |
121 | headers = {} |
122 | |
123 | if (headstr) |
124 | |
125 | # There are indeed some headers. Create a hash from them. |
126 | |
127 | headstr.split(short ? "\n" : "\r\n").each do |str| |
128 | pos = str.index(':') |
129 | headers[str.slice!(0..pos - 1).strip.downcase] = str[1..-1].strip if (pos > 1) |
130 | end |
131 | |
132 | # If we find a Status header with a 300-series code, check for a |
133 | # Location header too. If found, redirect to that location. |
134 | |
135 | if (headers['status']) |
136 | code = headers['status'].to_i |
137 | |
138 | if (code >= 300 and code < 400 and headers['location']) |
139 | redirect_to headers['location'] |
140 | return |
141 | end |
142 | end |
143 | |
144 | # For a content type of 'text/html', render within a View. Otherwise |
145 | # send the data directly without a surrounding template. |
146 | |
147 | if ([ 'text/html', 'text/x-html' ].include? headers['content-type']) |
148 | |
149 | # Almost there - extract a title if we can, and chop off the header |
150 | # and footer (HTML prologue and epilogue) to attempt to produce |
151 | # valid (X)HTML. The version of CVSweb in use at the time of writing |
152 | # always writes body and title container tags in lower case which |
153 | # helps save a bit of effort. |
154 | |
155 | title_tag = @output.slice(/<title.*\/title>/) |
156 | title_tag.gsub!(/<title.*?>/, '') |
157 | @title = title_tag.gsub(/<\/title>/, '') || 'CVS Repository' |
158 | |
159 | # Chop everything from the front of the output string up to the end |
160 | # of the opening body tag, inclusive. |
161 | |
162 | body_tag = @output.slice(/<body.*?>/) |
163 | body_pos = @output.index('<body') |
164 | |
165 | if (body_pos && body_tag && body_tag.length > 0) |
166 | @output.slice!(0..body_pos + body_tag.length - 1) |
167 | end |
168 | |
169 | # Chop off anything after the closing body tag too. |
170 | |
171 | body_pos = @output.index('</body') |
172 | @output.slice!(body_pos..-1) if body_pos |
173 | |
174 | # Render the default layout to send the template-based output. |
175 | |
176 | render :layout => 'default' |
177 | |
178 | else |
179 | |
180 | # Apparently, not HTML; send the data directly. |
181 | |
182 | type = headers['content-type'] || 'application/octet-stream' |
183 | send_data @output, |
184 | :type => type, |
185 | :disposition => 'inline' |
186 | end |
187 | |
188 | else |
189 | |
190 | # CVSweb output had no HTTP header - this is unexpected. We don't |
191 | # understand its output so just send this to the browser as a |
192 | # stream of binary data. |
193 | |
194 | send_data @output, |
195 | :filename => filename, |
196 | :type => 'application/octet-stream' |
197 | end |
198 | end |
199 | end |