Class: Web::Connection

Inherits:
Object show all
Includes:
Test::Unit::Assertions
Defined in:
lib/web/testing.rb,
lib/web/validate.rb,
lib/web/connection.rb,
lib/web/traceoutput.rb

Defined Under Namespace

Classes: BufferSet, CallbackBuffer, CaseInsensitiveHash, CookieHash

Constant Summary collapse

MULTIPLE_KEY =
/\[\]\z/
HTTP_STATUS =
{"200" => "OK",
               "206" => "Partial Content",
               "300" => "Multiple Choices",
               "301" => "Moved Permanently",
               "302" => "Found",
               "304" => "Not Modified",
               "400" => "Bad Request",
               "401" => "Authorization Required",
               "403" => "Forbidden",
               "404" => "Not Found",
               "405" => "Method Not Allowed",
               "406" => "Not Acceptable",
               "411" => "Length Required",
               "412" => "Precondition Failed",
               "500" => "Internal Server Error",
               "501" => "Method Not Implemented",
               "502" => "Bad Gateway",
               "506" => "Variant Also Negotiates"
}
ENV_KEYS =
[ :path_info, :document_root, :script_name ]
EOL =
"\r\n"

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Test::Unit::Assertions

#assert_includes

Constructor Details

#initialize(options = {}) ⇒ Connection

Returns a new instance of Connection.



87
88
89
90
91
# File 'lib/web/connection.rb', line 87

def initialize( options={} )
  apply_options( options )
  parse_request
  setup_variables
end

Instance Attribute Details

#application_loadedObject

Returns the value of attribute application_loaded.



176
177
178
# File 'lib/web/connection.rb', line 176

def application_loaded
  @application_loaded
end

#cgdObject (readonly)

Returns the value of attribute cgd.



175
176
177
# File 'lib/web/connection.rb', line 175

def cgd
  @cgd
end

#contentObject (readonly)

Returns the value of attribute content.



175
176
177
# File 'lib/web/connection.rb', line 175

def content
  @content
end

Returns the value of attribute cookie.



175
176
177
# File 'lib/web/connection.rb', line 175

def cookie
  @cookie
end

#envObject (readonly)

Returns the value of attribute env.



175
176
177
# File 'lib/web/connection.rb', line 175

def env
  @env
end

#getObject (readonly)

Returns the value of attribute get.



175
176
177
# File 'lib/web/connection.rb', line 175

def get
  @get
end

#localObject (readonly)

Returns the value of attribute local.



175
176
177
# File 'lib/web/connection.rb', line 175

def local
  @local
end

#optionsObject (readonly)

Returns the value of attribute options.



175
176
177
# File 'lib/web/connection.rb', line 175

def options
  @options
end

#outputObject (readonly)

Returns the value of attribute output.



175
176
177
# File 'lib/web/connection.rb', line 175

def output
  @output
end

#postObject (readonly)

Returns the value of attribute post.



175
176
177
# File 'lib/web/connection.rb', line 175

def post
  @post
end

#raw_post_dataObject (readonly)

Returns the value of attribute raw_post_data.



175
176
177
# File 'lib/web/connection.rb', line 175

def raw_post_data
  @raw_post_data
end

#requestObject (readonly)

Returns the value of attribute request.



175
176
177
# File 'lib/web/connection.rb', line 175

def request
  @request
end

#rulesetObject

Returns the value of attribute ruleset.



19
20
21
# File 'lib/web/validate.rb', line 19

def ruleset
  @ruleset
end

#run_callerObject

Returns the value of attribute run_caller.



176
177
178
# File 'lib/web/connection.rb', line 176

def run_caller
  @run_caller
end

#templatesObject

Returns the value of attribute templates.



176
177
178
# File 'lib/web/connection.rb', line 176

def templates
  @templates
end

Class Method Details

.create(options = {}) ⇒ Object



770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
# File 'lib/web/connection.rb', line 770

def Connection::create(options={})
  if (options[:connection])
    options[:connection]
  else
    case Connection::server_sniff
    when :fcgi
      require 'web/sapi/fastcgi'
      Web::FastCGIConnection.new( options )
    when :mod_ruby
      Web::ModRubyConnection.new( options )
    else
      Connection.new(options)
    end
  end
end

.downcase_env(env_in) ⇒ Object

returns a hash with downcased keys of the env_in variable



860
861
862
863
864
865
866
# File 'lib/web/connection.rb', line 860

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

.normalize(params) ⇒ Object

normalizes a params hash



869
870
871
872
873
874
875
876
877
# File 'lib/web/connection.rb', line 869

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

.parse_query_string(query) ⇒ Object

Parse a query_string into parameters



881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
# File 'lib/web/connection.rb', line 881

def Connection::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

.server_sniffObject

returns one of these values: :cgi, :fastcgi, :mod_ruby, :webrick



795
796
797
798
799
800
801
802
803
804
805
806
# File 'lib/web/connection.rb', line 795

def Connection::server_sniff
  if( Object.const_defined?( "Apache" ) \
      && Apache.const_defined?( "Request" ) \
      && $stdin.kind_of?( Apache::Request ) )
    :mod_ruby
  elsif ( Object.const_defined?( "FCGI" ) \
          && ! FCGI.is_cgi? )
    :fcgi
  else
    :cgi
  end
end

.trace_output_templateObject



46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/web/traceoutput.rb', line 46

def Connection.trace_output_template
 template = "<div>\n<h1>Request Details</h1><br>\n<table cellspacing=\"0\" cellpadding=\"0\" border=\"1\" style=\"width:100%;border-collapse:collapse;\">\n<tr>\n  <th class=\"alt\" align=\"Left\" colspan=2><h3><b>Request Parameters</b></h3></th></tr>\n  <narf:foreach from=parameters item=parameter>\n<tr><th width=150>{$parameter.key}</th><td>{$parameter.value}</td></tr>\n  </narf:foreach>\n</table>\n<br>\n<table cellspacing=\"0\" cellpadding=\"0\" border=\"1\" style=\"width:100%;border-collapse:collapse;\">\n  <tr><th class=\"alt\" align=\"Left\" colspan=2><h3><b>Cookies</b></h3></th></tr>\n  <narf:foreach from=cookies item=cookie>\n<tr><th  width=150>{$cookie.key}</th><td>{$cookie.value}</td></tr>\n  </narf:foreach>\n</table>\n<br>\n<table cellspacing=\"0\" cellpadding=\"0\" border=\"1\" style=\"width:100%;border-collapse:collapse;\">\n  <tr><th class=\"alt\" align=\"Left\" colspan=2><h3><b>Session</b></h3></th></tr>\n  <narf:foreach from=session item=sessionitem>\n<tr><th  width=150>{$sessionitem.key}</th><td>{$sessionitem.value}</td></tr>\n </table>\n</div>\n"
end

Instance Method Details

#[](key) ⇒ Object

access parameters. If the key is array-style, aka param[], return the array. Otherwise, return the joined string.



204
205
206
207
208
209
210
# File 'lib/web/connection.rb', line 204

def [] (key)
  if (MULTIPLE_KEY =~ key)
    self.request[key]
  else
    single_param(key)
  end
end

#[]=(key, value) ⇒ Object

set param at the given key. This is useful to change state in your app without asking the browser to redirect



214
215
216
217
218
219
# File 'lib/web/connection.rb', line 214

def []= (key, value)
  unless value.kind_of? Array
    value = [value]
  end
  request[key] = value
end

#add_header(name, value) ⇒ Object

There’s a bit of special casing in here, for the follow reasons:

  • some headers should only be sent once. If you add Content-Encoding, Location, or Status, it will overwrite the old headers instead of adding a second header

  • content-type is a strange header. It is a combination of the content-type and charset attributes. setting content-type will cause the content-type attribute to be set; setting charset will cause the charset attribute to be set.

    I don’t know if this is the correct behavior. Should this method assume that the content-type set here is the full content type, and try to split the header into it’s two parts?

  • If the headers have been sent, this will throw a Web::Error



481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
# File 'lib/web/connection.rb', line 481

def add_header(name, value )
  unless header_sent?
    if (/content-encoding/i =~ name  )
      header['Content-Encoding'] = [value]
    elsif( /location/i =~ name )
      header['Location'] = [value]
    elsif( /status/i =~ name )
      if /^\d*$/ =~ value.to_s
        self.status = value
      else
        header['Status'] = [value]
      end
    elsif(/content-type/i =~ name)
      header['Content-Type'] = [value]
    elsif(/charset/i =~ name)
      self.charset = value
    else
      header[name] ||= []
      header[name].push value
    end
  else
    raise Web::Error.new( "Can't add_header after header have been sent to client" )
  end
end

#apply_options(options = {}) ⇒ Object



93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
# File 'lib/web/connection.rb', line 93

def apply_options( options = {} )
  @closed = false
  @header_sent = false
  @templates = [ ]

  @options = options
  options[:buffered] = true unless options.has_key? :buffered

  @raw_post_data  = options[:raw_post_data]  || $stdin
  @output         = options[:out]            || $stdout
  @cookie         = options[:cookie]
  @request        = options[:request]



  # start error handler
  if (options.has_key? :error_handler)
    if ( options[:error_handler] )
      @error_handler = options[:error_handler]
    end
  end
  # end error handler
  
  # start env
  @env          ||= ENV
  @env            = Connection::downcase_env(@env)
  if options[:env] && options[:env].kind_of?(Hash) && !options[:env].keys.empty?
    @env          = Connection::downcase_env( @env.merge( options[:env] ) )
  end
  Web::Connection::ENV_KEYS.each { |symbol|
    env[symbol.to_s.downcase] = options[symbol] if options[symbol]
  }
  # end env
end

#assert_content(expected, message = "") ⇒ Object

for testing: raises Test::Unit exception if content is not set to the provided string.



42
43
44
# File 'lib/web/testing.rb', line 42

def assert_content expected, message=""
  assert_equal( expected, get_content, message );
end

Throws Test::Unit::AssertionFailedException if cookie values are not present.



82
83
84
# File 'lib/web/testing.rb', line 82

def assert_cookie( name, values, message="" )
  assert_equal( [ values ].flatten, [ cookies_sent[name] ].flatten, message )
end

#assert_form_includes(formname, vars) ⇒ Object

assert output content contains a form that includes the given hash of values. See Web::Testing



70
71
72
# File 'lib/web/testing.rb', line 70

def assert_form_includes formname, vars
  assert_includes vars, get_form_fields(formname)
end

#assert_header(name, values, message = "") ⇒ Object

Raises Test::Unit::AssertionFailedError if the header has not been set to the provided value(s)



76
77
78
# File 'lib/web/testing.rb', line 76

def assert_header( name, values, message="" )
  assert_equal( [values].flatten, get_header(name), message )
end

#assert_local_includes(expected) ⇒ Object

Check that these values were included in the output



108
109
110
# File 'lib/web/testing.rb', line 108

def assert_local_includes expected
  assert_includes expected, @local
end

#assert_template_not_used(expected, message = "") ⇒ Object

Check that a particluar template was not used



98
99
100
101
102
103
104
105
# File 'lib/web/testing.rb', line 98

def assert_template_not_used expected, message =""
  _wrap_assertion {
    if (@templates.kind_of?(Array) && @templates.include?(expected) )
      msg = "template:<#{expected}> not supposed to be used in:<#{@templates.inspect}>"
      flunk(msg)
    end
  }
end

#assert_template_used(expected, message = "") ⇒ Object

Check that a particluar template was used



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

def assert_template_used expected, message =""
  _wrap_assertion {
    flunk("No template used") if @templates == nil
    unless @templates.include? expected
      msg = "expected template:<#{expected}> but not one of:<#{@templates.inspect}>"
      flunk(msg)
    end
  }
end

#assert_vars_equalsObject

Raises:



112
113
114
# File 'lib/web/testing.rb', line 112

def assert_vars_equals
  raise Exception.new("Not yet implemented")
end

#call_progress_hook(full, read) ⇒ Object



923
924
925
926
927
928
# File 'lib/web/connection.rb', line 923

def call_progress_hook(full, read)
  if (progress_hook)
    progress_hook.call( full,
                        read )
  end
end

#charsetObject



637
638
639
# File 'lib/web/connection.rb', line 637

def charset
  split_content_type[1]
end

#charset=(new_charset) ⇒ Object



641
642
643
# File 'lib/web/connection.rb', line 641

def charset= new_charset
  set_full_content_type( content_type, new_charset )
end

#clearObject

Reset output buffer. Fails if headers have been sent.



293
294
295
296
297
298
299
300
301
302
# File 'lib/web/connection.rb', line 293

def clear()
  if header_sent?
    raise( Exception.new( "Can't call Web::clear()" ) )
  else
    unless unbuffered?
      @buffer = BufferSet.new
      @content = StringIO.new
    end
  end
end

#closeObject

TODO: at_close handlers flushes the output and, if applicable, saves the session



267
268
269
270
271
272
# File 'lib/web/connection.rb', line 267

def close
  flush
  @session ||= nil
  @session.save if (@session.respond_to? :save)
  @closed = true
end

#closed?Boolean

Returns:

  • (Boolean)


274
275
276
# File 'lib/web/connection.rb', line 274

def closed?
  @closed
end

#content_typeObject

the default content-type is “text/html”



625
626
627
# File 'lib/web/connection.rb', line 625

def content_type
  split_content_type[0]
end

#content_type=(new_content_type) ⇒ Object



629
630
631
632
633
634
635
# File 'lib/web/connection.rb', line 629

def content_type= new_content_type
  content1, charset1 = split_content_type
  content2, charset2 = split_content_type( new_content_type )
  
  set_full_content_type( content2 || content1,
                         charset2 || charset1 )
end

#cookies_sentObject

returns a hash of all the cookie n/v pairs that have been set on the cgi



700
701
702
703
704
705
706
707
708
# File 'lib/web/connection.rb', line 700

def cookies_sent
  cookies = {}
  get_header("Set-Cookie").each do |cookie|
    /\A(.*?)=([^;]*)/ =~ cookie
    cookies[$1] ||= [ ]
    cookies[$1].push $2
  end
  cookies
end

#encodingObject



602
603
604
# File 'lib/web/connection.rb', line 602

def encoding
  get_header( "Content-Encoding" ).first
end

#encoding=(new_encoding) ⇒ Object



606
607
608
# File 'lib/web/connection.rb', line 606

def encoding=( new_encoding )
  add_header( "Content-Encoding", new_encoding )
end

#flushObject

send header to the client, and flush any buffered output



250
251
252
253
254
255
256
257
258
259
260
261
262
263
# File 'lib/web/connection.rb', line 250

def flush
  unless unbuffered?
    begin
      content = ob_flush
      send_header
      @output << content
    rescue Exception => e
      # protect against errors in output buffers
      @buffer = BufferSet.new
      Web::report_error(e)
      self.flush
    end
  end
end

#full_content_typeObject

The content type header is a combination of the content_type and the charset. This method returns that combination.



616
617
618
# File 'lib/web/connection.rb', line 616

def full_content_type
  get_header( "Content-Type" ).first
end

#get_contentObject

returns the body content of the response (sans headers).



325
326
327
328
# File 'lib/web/connection.rb', line 325

def get_content
  #flush_buffers # (do I need to flush the buffers?)
  @content.string
end

returns an array of cookie values that have been set. path / expires / etc. info is currently not returned, should be added in?



690
691
692
693
694
695
696
# File 'lib/web/connection.rb', line 690

def get_cookie key
  #get_header("Set-Cookie").collect{ |cookie|
  #  /\A(#{ key })=([^;]*)/ =~ cookie
  #  $2
  #}.compact
  self.cookie[key]
end

#get_form(name) ⇒ Object

:nodoc:



50
51
52
# File 'lib/web/testing.rb', line 50

def get_form( name ) #:nodoc:
  get_formreader[name]
end

#get_form_fields(name) ⇒ Object

:nodoc:



54
55
56
# File 'lib/web/testing.rb', line 54

def get_form_fields(name) #:nodoc:
  get_formreader.get_fields(name)
end

#get_form_value(formname, name) ⇒ Object

:nodoc:



58
59
60
61
62
63
64
65
66
# File 'lib/web/testing.rb', line 58

def get_form_value formname, name #:nodoc:
  form = get_form_fields(formname)
  if form == nil
    raise Web::Error.new("Form '#{formname}' does not exist") 
  end
  
  value = form[name]
  value
end

#get_formreaderObject

:nodoc:



46
47
48
# File 'lib/web/testing.rb', line 46

def get_formreader #:nodoc:
  return @form_fields_cache ||= FormReader.new( get_content )
end

#get_header(name) ⇒ Object

returns an array of header values set with the given name.



507
508
509
510
511
512
513
# File 'lib/web/connection.rb', line 507

def get_header name
  header.keys.find_all { |key|
    key.downcase == name.downcase
  }.collect{ |key|
    header[key].dup
  }.flatten
end

#headerObject



552
553
554
555
556
# File 'lib/web/connection.rb', line 552

def header 
  @header ||= {"Content-Type" => ["text/html"],
               "Status" => ["200 OK"] }
  @header
end

#header_sent?Boolean

Returns:

  • (Boolean)


548
549
550
# File 'lib/web/connection.rb', line 548

def header_sent?
  @header_sent
end

#key?(key) ⇒ Boolean

test whether a param was submitted

Returns:

  • (Boolean)


241
242
243
# File 'lib/web/connection.rb', line 241

def key? (key)
  request.has_key? key
end

#keysObject

list the submitted params



236
237
238
# File 'lib/web/connection.rb', line 236

def keys
  request.keys
end

#locationObject



587
588
589
# File 'lib/web/connection.rb', line 587

def location
  get_header( "Location" ).first
end

#location=(location) ⇒ Object

see set redirect



592
593
594
# File 'lib/web/connection.rb', line 592

def location= location
  add_header( "Location", location )
end

#merge_get_and_postObject



152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
# File 'lib/web/connection.rb', line 152

def merge_get_and_post
  var_order = [ :post, :get, :cookie ]
  
  if env["request_method"] == "POST"
    var_order = [ :get, :post, :cookie ]
  end
  
  @request = Hash.new( [ ] )
  var_order.each do |namespace|
    variables = self.send( namespace )
    if variables
      variables.each do |k,v|
        @request[k] = [ ]
        v.each do |e|
          @request[k].push(e.dup) if e
        end
      end
    end
  end
  
  @request
end

#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



900
901
902
# File 'lib/web/connection.rb', line 900

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

#multipart?Boolean

Returns:

  • (Boolean)


904
905
906
907
# File 'lib/web/connection.rb', line 904

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

#nph?Boolean

Aside from when :nph is set in the options, scripts running in IIS always use nph mode. This code will probably be affected as cgi is re-organized to support multiple backends.

Returns:

  • (Boolean)


713
714
715
# File 'lib/web/connection.rb', line 713

def nph?
  @cgd.nph?
end

#ob_get_flushObject



335
336
337
338
339
# File 'lib/web/connection.rb', line 335

def ob_get_flush
  result = @buffer.last.get_contents
  ob_flush
  result
end

#ob_start(&callback) ⇒ Object Also known as: filter



330
331
332
# File 'lib/web/connection.rb', line 330

def ob_start( &callback )
  @buffer.ob_start(&callback)
end

#parse_multipartObject

parse multipart/form-data



910
911
912
913
914
# File 'lib/web/connection.rb', line 910

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_requestObject

It will set @cookie, @get, @post, and @request



810
811
812
813
814
815
816
817
818
819
820
821
# File 'lib/web/connection.rb', line 810

def parse_request
  @cookie ||= CookieHash.new( env['http_cookie'] || env['cookie'] )
  @get    ||= Connection::parse_query_string(env["query_string"])
  @post   ||= if (multipart?)
                parse_multipart
              else
                raw_post_data.binmode
                query_string = raw_post_data.read(Integer(env['content_length'])) || ''
                @raw_post_data = StringIO.new(query_string)
                Connection::parse_query_string( query_string )
              end
end

#progress_hookObject



920
921
922
# File 'lib/web/connection.rb', line 920

def progress_hook
  Web::config::multipart_progress_hook
end

#read_multipart(boundary, content_length) ⇒ Object



930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
# File 'lib/web/connection.rb', line 930

def read_multipart(boundary, content_length)
  require 'tempfile'

  params = Hash.new([])
  boundary = "--" + boundary
  buf = ""
  cached_raw_post_data = Tempfile.new("Web")
  bufsize = 10 * 1024
  
  # start multipart/form-data
  raw_post_data.binmode
  boundary_size = boundary.size + EOL.size
  content_length -= boundary_size
  status = raw_post_data.read(boundary_size)
  if nil == status
    raise EOFError, "no content body"
  end
  cached_raw_post_data << status
  
  # ok... so what the hell does this do?
  # I promise never to denigrate the accomplishments
  # of my predecessors again :-)
  #    ~ pat
  full_content_length = content_length
  until -1 == content_length
    call_progress_hook( full_content_length,
                        full_content_length - 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.write 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 raw_post_data
      c = if bufsize < content_length
            raw_post_data.read(bufsize) or ''
          else
            raw_post_data.read(content_length) or ''
          end
      # add it to the raw_post_data, reduce our countdown
      cached_raw_post_data << c
      buf.concat c
      ### debugging for making multipart_asserts work ~ pat
      #$defout.puts [bufsize, content_length, c.size].inspect, raw_post_data.read(bufsize).inspect
      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

  call_progress_hook( full_content_length,
                      full_content_length - content_length - 1)
  
  @raw_post_data = cached_raw_post_data
  params
end

#report_error(error) ⇒ Object


implementation details, aka refactoring targets below




729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
# File 'lib/web/connection.rb', line 729

def report_error( error )
  self.status = 500 unless self.header_sent?
  self.content_type = "text/html"
  
  Web::config::describe( :error_style => "Controls error reporting.  One of three values: :development, :production, or :custom" )
  Web::config[:error_style] ||= :development
  
  case Web::config::error_style

  when :development
    
    Web::print_message( error.class.to_s,
                        error.rbw_html + error.rbw_backtrace_html )
    self.report_error2stderr( error )

  when :production
    
    Web::print_message( error.class.to_s, 
                        error.rbw_html )
    self.report_error2stderr( error )
    
  when :custom
  
    Web::config::error_handler.call( error )

  else
    
    raise error
  
  end
end

#report_error2stderr(err) ⇒ Object



761
762
763
764
765
766
# File 'lib/web/connection.rb', line 761

def report_error2stderr( err )
  unless Web::env['server_software'] =~ /Microsoft-IIS/
    $stderr.binmode
    $stderr.puts err.rbw_to_s
  end
end

#send_file(filename) ⇒ Object

This method will replace the contents of the output buffer by reading from the given filename. If the content-type has not already been upset, it will try and guess an appropriate mime-type from the extension of the file.



310
311
312
313
314
315
316
317
# File 'lib/web/connection.rb', line 310

def send_file( filename )
  clear()
  write( File.open( filename, 'rb' ) { |f| f.read } )
  if self.content_type == "text/html"
    self.content_type = Web::Mime::get_mimetype(filename)
  end
  Web::close()
end

#send_headerObject

Send header to the client. No more header values can be sent after this method is called!



517
518
519
520
521
522
523
524
# File 'lib/web/connection.rb', line 517

def send_header
  unless header_sent?
    unless @options[:noheader]
      send_header_implementation
    end
    @header_sent = true
  end
end

#send_header_implementationObject



526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
# File 'lib/web/connection.rb', line 526

def send_header_implementation
  if @options[:nph] || /IIS/.match(env["server_software"])
    output << "#{ env['server_protocol'] || 'HTTP/1.0' }: "
    output << "#{ header['Status'] }\r\n"
    output << "Date: #{ Web::rfc1123_date(Time.now) }\r\n"
    
    header.delete("Status")
    
    unless header.has_key? "Server"
      header["Server"] = [ env["server_software"] || "" ]
    end
  end
  
  header.sort.each { |name, value|
    value.each { |v|
      output << "#{ name }: #{ v }\r\n"
    }
  }
  
  output << "\r\n"
end

#sessionObject



178
179
180
181
182
183
184
185
186
# File 'lib/web/connection.rb', line 178

def session
  # start session
  @session ||= if (options.has_key? :session)
                 options[:session]
               else
                 Session.new( self, options )
               end
  # end session
end

Cookies require a name and a value. You can also use these optional keyword arguments:

:path => <i>string<i>

path (need better description)

:domain => string

domain (need better description)

:expires => date

date this cookie should expire

:secure => true || false

whether this cookie should be tagged as secure.



652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
# File 'lib/web/connection.rb', line 652

def set_cookie( name, value, options={} )
  #self.cookie[name] = [ ] unless self.cookie.has_key? name
  #self.cookie[name].push value

  value = Array(value).collect{ |field|
    Web::escape(field)
  }.join("&")
  
  cookie_text = "#{ name }=#{ value }"
  
  
  path = if (options[:path])
           options[:path]
         else
           %r|^(.*/)|.match(env["script_name"])
           ($1 or "")
         end
  
  cookie_text += "; path=#{ path }"
  
  if (options[:domain])
    cookie_text += "; domain=#{ options[:domain] }"
  end
  
  if (options[:expires])
    cookie_text += "; expires=#{ Web::rfc1123_date( options[:expires] ) }"
  end
  
  if (options[:secure])
    cookie_text += "; secure"
  end
  
  add_header( "Set-Cookie", cookie_text )
end

#set_full_content_type(new_content, new_charset) ⇒ Object



620
621
622
# File 'lib/web/connection.rb', line 620

def set_full_content_type( new_content, new_charset )
  add_header( "Content-type", [ new_content, new_charset ].compact.join('; charset=') )
end

#set_redirect(new_location) ⇒ Object

Sets the status and the location appropriately.



597
598
599
600
# File 'lib/web/connection.rb', line 597

def set_redirect( new_location )
  self.status = "302"
  self.location = new_location
end

#setup_variablesObject



128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
# File 'lib/web/connection.rb', line 128

def setup_variables
  @local = {}
  @application_loaded = false
  
  @cookie  = Connection::normalize(@cookie)
  @post    = Connection::normalize(@post)
  @get     = Connection::normalize(@get)
  @request = Connection::normalize(@request || merge_get_and_post )

  raw_post_data.rewind

  # start output buffer
  @content = StringIO.new
  if (unbuffered?)
    @buffer = @output
  else
    @buffer = BufferSet.new
  end
  @buffer.binmode if @buffer.respond_to? :binmode
  # end output buffer
  

end

#single_param(key) ⇒ Object

If request[0] is a Web::Upload, returns that value. Otherwise it returns request.join( “,” )



223
224
225
226
227
228
229
# File 'lib/web/connection.rb', line 223

def single_param(key)
  if (request[key].first.kind_of? Web::Upload)
    request[key].first
  else
    request[key].join( "," )
  end
end

#split_content_type(target = full_content_type) ⇒ Object



610
611
612
# File 'lib/web/connection.rb', line 610

def split_content_type( target = full_content_type )
  target.split( Regexp.new('; charset=') )
end

#split_paramsObject

:nodoc:



231
232
233
# File 'lib/web/connection.rb', line 231

def split_params #:nodoc:
  Web::Testing::MultiHashTree.new(request).fields
end

#statusObject

the default status is 200



559
560
561
562
# File 'lib/web/connection.rb', line 559

def status
  get_header( "Status" ).first =~ /^(\d+)( .*)?$/ 
  $1
end

#status=(new_status) ⇒ Object



583
584
585
# File 'lib/web/connection.rb', line 583

def status= new_status
  add_header( "Status", "#{ new_status } #{ HTTP_STATUS[new_status.to_s] }" )
end

#trace_outputObject



188
189
190
191
192
193
194
195
196
197
# File 'lib/web/connection.rb', line 188

def trace_output
  templater = Narflates.new(CGI.trace_output_template,{})
  templater.parse(self,{ "parameters" => 
                        request.collect { |key,value| { "key" => key, "value" => value } },
                        "cookie" =>
                        cookie.collect { |key,value| { "key" => key, "value" => value } },
                        "session" =>
                        session.collect { |key,value| { "key" => key, "value" => value } } })
  flush
end

#unbuffered?Boolean

Returns:

  • (Boolean)


319
320
321
# File 'lib/web/connection.rb', line 319

def unbuffered?
  ! @options[:buffered]
end