Class: N::App::Request

Inherits:
Object
  • Object
show all
Includes:
CGI::QueryExtension, RequestUtils
Defined in:
lib/n/app/request.rb

Overview

Request

Encapsulates an http protocol request. Generally clones the Apache Request passed by mod_ruby.

Todo:

  • USE libapreq!

  • Dont use env_table (arghhhh!)

  • dont use a separate path and real path

  • MEGA: unify request and request (like a socket, io stream,etc). Evan subclass IO!

Design:

  • uri: the original uri entered to the browser (INCLUDES qs)

gmosx: i included the qs and removed full_uri (always forgot to use it anyway, was very error prone)

  • translated_uri: as translated by the web server (no query string)

  • path: the path to the actual script (or object)

Example:

www.site.com/faq/?id=1 -> uri: /faq/?id=1 translated_uri: /faq/index.sx path: base/site/root/faq/index.sx query_string: id=1

  • querystring should probably include the ?

  • Encapsulate ModRuby/Apache requests

  • Based on resin3.0 excellent code:

com/caucho/server/http/HttpRequest.java

  • use as much of ruby’s default cgi/http code as possible (why reinvent the wheel?)

Constant Summary collapse

METHOD_GET =

request methods enumeration

0
METHOD_POST =
1
METHOD_HEAD =
2
EXCLUDED_PARAMETERS =

exclude those parameters for security

%w{ oid pid name }

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from RequestUtils

#expand_uri, #full_uri, #get_entity, #get_entity_by_name

Constructor Details

#initializeRequest

Returns a new instance of Request.



261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
# File 'lib/n/app/request.rb', line 261

def initialize
	# set to 0 (== top level). When including fragments
	# the level is incremented.
	@level = 0

	# gmosx: it would be good to defere the hash creation,
	# but having the hash always created saves as a LOT of
	# checks in client code, so we create it here.
	#
	# FIXME: WE SHOULD NOT CREATE unneeded hash objects. 
	#
	# @parts = {}

	# request:		
	# provide some fair initialization values.
	set_status(200)
	@content_type = "text/html; charset=iso-8859-7"
	@out = {}
	@out_cookies = {}
end

Instance Attribute Details

#content_typeObject

request content type, default: text/html



162
163
164
# File 'lib/n/app/request.rb', line 162

def content_type
  @content_type
end

#error_logObject

Keep all errors to present them in-page when in admin mode.



224
225
226
# File 'lib/n/app/request.rb', line 224

def error_log
  @error_log
end

#fragment_hashObject

keep the original handler process uri, usefull as a cache key. Typically updated in subrequests only. Investigate if we could use a hash here!



221
222
223
# File 'lib/n/app/request.rb', line 221

def fragment_hash
  @fragment_hash
end

#inObject

the incoming headers gmosx: writer needed for inject (sub-req cloning)



236
237
238
# File 'lib/n/app/request.rb', line 236

def in
  @in
end

#in_cookiesObject

the incoming cookies



239
240
241
# File 'lib/n/app/request.rb', line 239

def in_cookies
  @in_cookies
end

#levelObject

the level of the request (0 = toplevel)



199
200
201
# File 'lib/n/app/request.rb', line 199

def level
  @level
end

#lmObject

last modified cache



230
231
232
# File 'lib/n/app/request.rb', line 230

def lm
  @lm
end

#localeObject

the locale hash for this request.



210
211
212
# File 'lib/n/app/request.rb', line 210

def locale
  @locale
end

#messageObject

HTTP request message



259
260
261
# File 'lib/n/app/request.rb', line 259

def message
  @message
end

#methodObject

request method



159
160
161
# File 'lib/n/app/request.rb', line 159

def method
  @method
end

#outObject

the outgoing headers



244
245
246
# File 'lib/n/app/request.rb', line 244

def out
  @out
end

#out_bufferObject

the outgoing buffer



250
251
252
# File 'lib/n/app/request.rb', line 250

def out_buffer
  @out_buffer
end

#out_cookiesObject

the outgoing cookies



247
248
249
# File 'lib/n/app/request.rb', line 247

def out_cookies
  @out_cookies
end

#parametersObject Also known as: query

The query string is parsed to the parameters hash.



186
187
188
# File 'lib/n/app/request.rb', line 186

def parameters
  @parameters
end

#partsObject

The parts attached to this request. A part is typically an uploaded file. By using a separate hash instead of the parameters hash one can easily enumerate parts.



196
197
198
# File 'lib/n/app/request.rb', line 196

def parts
  @parts
end

#pathObject

the path to the actual object (script) gets overriden by sub-requests, is not needed in scripts.



175
176
177
# File 'lib/n/app/request.rb', line 175

def path
  @path
end

#path_infoObject

path info (extra parameters in the uri)



179
180
181
# File 'lib/n/app/request.rb', line 179

def path_info
  @path_info
end

#query_stringObject

the query string part of the uri



182
183
184
# File 'lib/n/app/request.rb', line 182

def query_string
  @query_string
end

#remote_addrObject

the remote address for this request



202
203
204
# File 'lib/n/app/request.rb', line 202

def remote_addr
  @remote_addr
end

#sessionObject

the session this request is part-of



190
191
192
# File 'lib/n/app/request.rb', line 190

def session
  @session
end

#shaderObject

the shader for this request.



213
214
215
# File 'lib/n/app/request.rb', line 213

def shader
  @shader
end

#statusObject

HTTP request status



256
257
258
# File 'lib/n/app/request.rb', line 256

def status
  @status
end

#tagObject

the script hash for this request



216
217
218
# File 'lib/n/app/request.rb', line 216

def tag
  @tag
end

#top_scriptObject

The top level script for this request.



227
228
229
# File 'lib/n/app/request.rb', line 227

def top_script
  @top_script
end

#translated_uriObject

the uri as translated by the web server. For sub-requests keeps the usri of the top level request.



171
172
173
# File 'lib/n/app/request.rb', line 171

def translated_uri
  @translated_uri
end

#uncacheableObject

Is the request cacheable? Set this attribute to ‘true’ to avoid caching the request fragment. Used to avoid caching ‘action’ requests.



207
208
209
# File 'lib/n/app/request.rb', line 207

def uncacheable
  @uncacheable
end

#uriObject

the uri for the request. For sub-requests keeps the uri of the top level request. gmosx: the writer is needed for injects.



167
168
169
# File 'lib/n/app/request.rb', line 167

def uri
  @uri
end

Instance Method Details

#[](name) ⇒ Object

Return the value of a query parameter



340
341
342
# File 'lib/n/app/request.rb', line 340

def [](name)
	return @parameters[name]
end

#[]=(name, value) ⇒ Object

Set the value of a query parameter

FIXME:

  • handle multivalued parameters!



367
368
369
# File 'lib/n/app/request.rb', line 367

def []=(name, value)
	@parameters[name] = value
end

#admin?Boolean

Is this an admin request? FIXME: no longer valid, recode.

Returns:

  • (Boolean)


384
385
386
# File 'lib/n/app/request.rb', line 384

def admin?
	return @parameters.include?("*admin")
end

#anonymous?Boolean

Shorthand for request.session.user.anonymous?

Returns:

  • (Boolean)


396
397
398
# File 'lib/n/app/request.rb', line 396

def anonymous?
	return @session.user.anonymous?
end

Removes a cookie from the client by seting the expire time to the past (epoch).



324
325
326
327
328
329
# File 'lib/n/app/request.rb', line 324

def del_cookie(name)
	cookie = N::App::Cookie.new(name, "nil")
	cookie.path = "/"
	cookie.expires = Time.at(0)
	@out_cookies[name] = cookie
end

#del_tx_entity!(txparam = "txid") ⇒ Object



531
532
533
534
535
# File 'lib/n/app/request.rb', line 531

def del_tx_entity!(txparam = "txid")
	if txid = @parameters[txparam]
		@session.delete(txid)
	end
end

#delete(param) ⇒ Object

Use the delete name to make the request compatible with hashes. Tests is a parameter is passed to the request and removes it! Used in action handlers.



450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
# File 'lib/n/app/request.rb', line 450

def delete(param)
	oparam = param
	if param = @parameters.delete(param)
		# gmosx: remove from querystring too! NEEDED. 
		# perhaps kinda slow but happens seldom and optimizes 
		# another frequent case
		@query_string = @parameters.collect { |k, v| (v && k.is_a?(String)) ? "#{k}=#{v}" : k }.join(";")
		
		# If the parameter exist this is an action request, so
		# do NOT cache the fragment.
		@uncacheable = true
	end
	
	# gmosx: to avoid using param?
	if param.is_a?(String) and (not N::StringUtils.valid?(param))
		return nil
	else
		return param
	end
end

#errorsObject

Shorthand

Output: nil if no errors.



411
412
413
# File 'lib/n/app/request.rb', line 411

def errors
	return del_tx_entity!("errid")
end

#errors_to_aObject Also known as: errors_list

Returns the errors as an array.



417
418
419
420
421
422
# File 'lib/n/app/request.rb', line 417

def errors_to_a
	if errors = del_tx_entity!("errid")
		return errors.values
	end
	return nil
end

#expires!(exp_time) ⇒ Object

Utility method to set the expires header.



643
644
645
# File 'lib/n/app/request.rb', line 643

def expires!(exp_time)
	@out["Expires"] = N::HttpUtils.time_to_string(exp_time)
end

#expires?Boolean

Returns:

  • (Boolean)


647
648
649
# File 'lib/n/app/request.rb', line 647

def expires?
	return @out["Expires"]
end

#get(key, default = nil) ⇒ Object

Same as [] but enforces a default value to! Also tries to guess the parameters type from the default value. It works like delete (ie returns nil for ‘empty’ String parameters).



349
350
351
352
353
354
355
356
357
358
359
360
# File 'lib/n/app/request.rb', line 349

def get(key, default=nil)
	val = @parameters[key]

	if !val or (val.is_a?(String) and (not N::StringUtils.valid?(val)))
		@parameters[key] = default
		return default			
	elsif default.is_a?(Integer)
		return val.to_i
	else
		return val
	end
end

Input:

the cookie name

Output:

  • the cookie value, or an array of values for multivalued cookies.

  • nil if the cookie doesnt exist.

Example:

nsid = request.get_cookie(“nsid”)



314
315
316
317
318
319
# File 'lib/n/app/request.rb', line 314

def get_cookie(cookie_name)
	return nil unless @in_cookies
	cookie = @in_cookies[cookie_name]
	return nil unless cookie
	return CGI.unescape(cookie.value)
end

#get_tx_entity(txparam = "txid") ⇒ Object



522
523
524
525
526
527
# File 'lib/n/app/request.rb', line 522

def get_tx_entity(txparam = "txid")
	if txid = @parameters[txparam]
		return @session[txid]
	end
	return nil
end

#include?(param) ⇒ Boolean

Check if a parameter exists! Example: url:www.mysite.com/page.sx?admin request.include?(admin) => true

Returns:

  • (Boolean)


437
438
439
# File 'lib/n/app/request.rb', line 437

def include?(param)
	return @parameters.include?(param)
end

#internal_redirect(url) ⇒ Object

Internal redirect

FIXME: implement me



638
639
# File 'lib/n/app/request.rb', line 638

def internal_redirect(url)
end

#is_top?Boolean

Returns true for the top-level request, but false for any inject or forward

Returns:

  • (Boolean)


377
378
379
# File 'lib/n/app/request.rb', line 377

def is_top?
	return 0 == @level
end

#log_error(str) ⇒ Object



542
543
544
545
546
# File 'lib/n/app/request.rb', line 542

def log_error(str)
	@error_log = [] unless @error_log
	@error_log << str if @error_log.size < 200 # gmosx: dod attack!
	$log.error str
end

#new_tx_entity!(entity, txparam = "txid") ⇒ Object

The tx sequence increases, the count of transactions / session is usually bounded.



498
499
500
501
502
503
504
505
506
507
508
# File 'lib/n/app/request.rb', line 498

def new_tx_entity!(entity, txparam = "txid")
	unless seq = @session["TXSEQ"]
		seq = 0
	end
	seq += 1
	@session["TXSEQ"] = seq
	txid = "TX#{seq}"
	@session[txid] = entity
	@parameters[txparam] = txid
	return txid
end

#param?(param) ⇒ Boolean Also known as: action?

Check if a parameter is valid

Returns:

  • (Boolean)


427
428
429
# File 'lib/n/app/request.rb', line 427

def param?(param)
	return N::StringUtils.valid?(self[param])
end

#parse_cookies(cookie_string) ⇒ Object Also known as: parse_cookie_string

this method is also usefull for probing (testing) the request class. FIXME: optimize this (libapreq)



299
300
301
# File 'lib/n/app/request.rb', line 299

def parse_cookies(cookie_string)
	@cookies = Cookie.parse(cookie_string)
end

#parse_query_stringObject


Query



334
335
336
# File 'lib/n/app/request.rb', line 334

def parse_query_string
	return N::UriUtils.query_string_to_hash(@query_string)
end

#redirect(url = nil, force_exit = false, status = 302) ⇒ Object

302 is the redirect status!

Status 303:

The request to the request can be found under a different URI and SHOULD be retrieved using a GET method on that resource. This method exists primarily to allow the output of a POST-activated script to redirect the user agent to a selected resource. The new URI is not a substitute reference for the originally requested resource. The 303 request MUST NOT be cached, but the request to the second (redirected) request might be cacheable.

Note: Many pre-HTTP/1.1 user agents do not understand the 303 status. When interoperability with such clients is a concern, the 302 status code may be used instead, since most user agents react to a 302 request as described here for 303.

WARNING:

Konqueror always performs a 307 redirect ARGH!

Redesign:

Use one redirect method with an optional status parameter, that reads messages from the status constants.

Input:

  • url to redirect to

  • if force_exit == true raises a ScriptExitException

  • status (303 or 307) default = 303



605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
# File 'lib/n/app/request.rb', line 605

def redirect(url = nil, force_exit = false, status = 302)
	# FIXME: normalize the url
   # url = $srv_url + url if url =~ /^\//om
	# FIXME: check arguments

	# enforce a meaningfull default
	url ||= self["_go"] || referer()
	
	# the url should have a leading "/"
	# enforce it to be sure. FIXME: optimize this!
	url = "/#{url}".squeeze("/") unless url =~ /^http/

	@out["Location"] = url
	# gmosx: NOT needed? see the exceprt from the spec.
	# @out['Cache-Control'] = "max-age=1"

	set_status(status)
	@out_buffer = "The URL has moved <a href='#{url}'>here</a>"

	if force_exit
		# Stop rendering the script immediately!
		# This is the default behaviour!
		raise N::ScriptExitException
	end
	
	# for unit testing
	return url
end

#refererObject Also known as: referrer

Return the referer to this resource. For the initial page in the clickstream there is no referer, set “/” by default.



288
289
290
# File 'lib/n/app/request.rb', line 288

def referer
	return @in["REFERER"] || "/"
end

#set_errors(errors) ⇒ Object

Set errors as a transaction entity. Returns the txid for the errors



402
403
404
# File 'lib/n/app/request.rb', line 402

def set_errors(errors)
	new_tx_entity!(errors, "errid") unless errors.empty?
end

#set_not_modified!Object

Set the HTTP NOT_MODIFIED status code. Usefull for HTTP Caching.



564
565
566
567
# File 'lib/n/app/request.rb', line 564

def set_not_modified!
	@status = 304
	@message = N::HTTP::STATUS_STRINGS[status]
end

#set_status(status = 200) ⇒ Object

Set the request HTTP status and lookup the corresponding request status message.



556
557
558
559
# File 'lib/n/app/request.rb', line 556

def set_status(status = 200)
	@status = status
	@message = HTTP::STATUS_STRINGS[status]
end

#set_tx_entity!(entity, txparam = "txid") ⇒ Object Also known as: update_tx_entity!

Set (update) an existing tx entity. Does NOT increase the tx sequence.



513
514
515
516
517
# File 'lib/n/app/request.rb', line 513

def set_tx_entity!(entity, txparam = "txid")
	if txid = @parameters[txparam]
		@session[txid] = entity
	end
end

#update(*params) ⇒ Object



441
442
443
# File 'lib/n/app/request.rb', line 441

def update(*params)
	@parameters.update(*params)
end

#update_entity(entity) ⇒ Object

gmosx: hmm this is a really dangerous method, the EXCLUDED params above dont seem enough :(



477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
# File 'lib/n/app/request.rb', line 477

def update_entity(entity)
	@parameters.each { |param, val|
		begin
			# gmosx: DO NOT escape by default !!!
			# gmosx: We need to get non valid params
			if (not EXCLUDED_PARAMETERS.include?(param))
				entity.send("__force_#{param}", val)
			end
		rescue NameError
			next
		end
	}
	
	return entity
end

#userObject

Shorthand for request.session.user



390
391
392
# File 'lib/n/app/request.rb', line 390

def user
	return @session.user
end