Module: Web::Parser

Included in:
CGD
Defined in:
lib/web/parser.rb

Overview

Purpose

This module contains methods to parse web requests. Parser counts on the attributes of a cgd: options, input, env

Constant Summary collapse

EOL =
"\r\n"

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.mod_ruby_query_stringObject

this method solely exists b/c of difficulties setting MOD_RUBY dynamically. so this method exists so I can test the effect separately from the (currently untestable) cause



117
118
119
# File 'lib/web/parser.rb', line 117

def Parser.mod_ruby_query_string     #:nodoc:
  Apache::request.args
end

.normalize(params) ⇒ Object

normalizes a params hash



50
51
52
53
54
55
56
57
58
# File 'lib/web/parser.rb', line 50

def Parser.normalize( params )
  params.each { |key, value|
    unless value.kind_of? Array
      params[key] = [value]
    end
  }
  params.default = []
  params
end

Parse a raw cookie string



65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/web/parser.rb', line 65

def Parser.parse_cookie(raw_cookie)
  cookies = Hash.new([])
  return cookies unless raw_cookie
  
  raw_cookie.split(/; /).each do |pairs|
    name, values = pairs.split('=',2)
    name = Web::unescape(name)
    values ||= ""
    values = values.split('&').collect{|v| Web::unescape(v) }
    if cookies.has_key?(name)
      cookies[name].push(*values)
    else
      cookies[name] = values
    end
  end
  
  cookies
end

.parse_query_string(query) ⇒ Object

Parse a query_string into parameters



126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# File 'lib/web/parser.rb', line 126

def Parser.parse_query_string(query)
  query ||= ""
  params = Hash.new([])
  
  query.split(/[&;]/n).each do |pairs|
    key, value = pairs.split('=',2).collect{|v| Web::unescape(v) }
    if params.has_key?(key)
      params[key].push(value)
    else
      params[key] = [value]
    end
  end
  
  params
end

Instance Method Details

#downcase_env(env_in) ⇒ Object

returns a hash with downcased keys of the env_in variable



41
42
43
44
45
46
47
# File 'lib/web/parser.rb', line 41

def downcase_env( env_in )
  env = CaseInsensitiveHash.new
  env_in.each{ |k, v|
    env[k.downcase] = v
  }
  env
end

#mod_ruby_query_stringObject

:nodoc:



121
122
123
# File 'lib/web/parser.rb', line 121

def mod_ruby_query_string #:nodoc:
  Parser.mod_ruby_query_string
end

#multipart?Boolean

Returns:

  • (Boolean)


162
163
164
165
# File 'lib/web/parser.rb', line 162

def multipart?
  ("POST" == env['request_method']) &&
        (/multipart\/form-data/.match(env['content_type']))
end

#normalize(params) ⇒ Object



60
61
62
# File 'lib/web/parser.rb', line 60

def normalize(params)
  Parser.normalize(params)
end


84
85
86
# File 'lib/web/parser.rb', line 84

def parse_cookie(raw_cookie)
  Parser.parse_cookie(raw_cookie)
end

#parse_multipartObject

parse multipart/form-data



168
169
170
171
172
173
# File 'lib/web/parser.rb', line 168

def parse_multipart
  %r|\Amultipart/form-data.*boundary=\"?([^\";,]+)\"?|n.match(env['content_type'])
  boundary = $1.dup
  
  read_multipart(boundary, Integer(env['content_length']))
end

#parse_paramsObject

parse and return multiple_params



89
90
91
92
93
94
95
# File 'lib/web/parser.rb', line 89

def parse_params
  if (multipart?)
    parse_multipart
  else
    parse_query_string( query_string )
  end
end

#parse_query_string(query) ⇒ Object



142
143
144
# File 'lib/web/parser.rb', line 142

def parse_query_string(query)
  Parser.parse_query_string(query)
end

#parse_query_string_typed(query) ⇒ Object

note this returns the un-arrayified version of the array



147
148
149
150
151
152
153
154
155
156
157
158
159
160
# File 'lib/web/parser.rb', line 147

def parse_query_string_typed(query) #:nodoc: :notest:
  params = parse_query_string query
  params2 = {}
  params.collect do |k,v|
    if ! (k =~ /type/)
      if (atype = params["#{k}#type"][0]) 
        params2[k] = Module.const_get(atype.intern).unencode(v[0])
      else
        params2[k] = v[0]
      end
    end
  end
  params2
end

#parse_requestObject

This method counts on the attributes of a cgd: options, input, env It will set @cookies and @multiple_params



30
31
32
33
34
35
36
37
38
# File 'lib/web/parser.rb', line 30

def parse_request
  @cookies  ||= normalize(options[:cookies] ||
                          parse_cookie( env['http_cookie'] ||
                                        env['cookie'] ) )

  @multiple_params ||= normalize( options[:params] ||
              parse_params )

end

#query_stringObject

:nodoc:



97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/web/parser.rb', line 97

def query_string #:nodoc:
  case env["request_method"]
  when "GET", "HEAD"
    if (defined? MOD_RUBY) # <= this check is untested.
      #    search the test files for
      #    test_parse_get_with_mod_ruby
      #    for an explanation
      mod_ruby_query_string
    else
      env["query_string"]
    end
  when "POST"
    input.binmode
    input.read(Integer(env['content_length']))
  end
end

#read_multipart(boundary, content_length) ⇒ Object

:nodoc:



177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
# File 'lib/web/parser.rb', line 177

def read_multipart(boundary, content_length)  #:nodoc:
  params = Hash.new([])
  boundary = "--" + boundary
  buf = ""
  bufsize = 10 * 1024
  
  # start multipart/form-data
  input.binmode
  boundary_size = boundary.size + EOL.size
  content_length -= boundary_size
  status = input.read(boundary_size)
  if nil == status
    raise EOFError, "no content body"
  end
  
  # ok... so what the hell does this do?
  # I promise never to denigrate the accomplishments
  # of my predecessors again :-)
  #    ~ pat
  until -1 == content_length
    head = nil
    body = Tempfile.new("Web")
    body.binmode
    
    # until we have:
    #   * a header
    #   * and a buffer that has a boundary
    # so far, make sense to me.
    until head and /#{boundary}(?:#{EOL}|--)/n.match(buf)
      # if we have a header....
      if head
        # !??!??!?!?!
        trim_size = (EOL + boundary + EOL).size
        if trim_size < buf.size
          body.print buf[0 ... (buf.size - trim_size)]
          buf[0 ... (buf.size - trim_size)] = ""
        end
      
      # If we have a double space (the smell of a header...)
      elsif /#{EOL}#{EOL}/n.match(buf)
        # extract the header, and erase it from the buffer
        buf = buf.sub(/\A((?:.|\n)*?#{EOL})#{EOL}/n) do
          head = $1.dup
          ""
        end
        next
      end
      
      # read a chunk from the input
      c = if bufsize < content_length
            input.read(bufsize) or ''
          else
            input.read(content_length) or ''
          end
      # add it to the input, reduce our countdown
      buf.concat c
      content_length -= c.size
    end
    
   /Content-Disposition:.* filename="?([^\";]*)"?/ni.match(head)
    filename = ($1 or "").dup
    if /Mac/ni.match(env['http_user_agent']) and
        /Mozilla/ni.match(env['http_user_agent']) and
        (not /MSIE/ni.match(env['http_user_agent']))
      filename = Web::unescape(filename)
    end
    
    /Content-Type: (.*)/ni.match(head)
    content_type = ($1 or "").strip
    
    # is this the part that is eating too much?
    #buf = buf.sub(/\A(.*?)(?:#{EOL})?#{boundary}(#{EOL}|--)/mn) do
    buf = buf.sub(/\A((?:.|\n)*?)(?:[\r\n]{1,2})?#{boundary}([\r\n]{1,2}|--)/n) do
      body.print $1
      if "--" == $2
        content_length = -1
      end
      ""
    end
    
    body.rewind

 
    if (content_type.empty?)
      upload = body.read
    else
      upload = Web::Upload.new( body, content_type, filename )
    end

    /Content-Disposition:.* name="?([^\";]*)"?/ni.match(head)
    name = $1.dup


    body.rewind

    if params.has_key?(name)
      params[name].push(upload)
    else
      params[name] = [upload]
    end
    
  end
  
  params
end