Class: Hayabusa::Http_session::Request

Inherits:
Object
  • Object
show all
Defined in:
lib/hayabusa_http_session_request.rb

Overview

This class parses the various HTTP requests into easy programmable objects. Get, post, cookie, meta and so on…

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(args) ⇒ Request

Sets the various required data on the object. Hayabusa, crlf and arguments.



14
15
16
17
18
# File 'lib/hayabusa_http_session_request.rb', line 14

def initialize(args)
  @args = args
  @hb = @args[:hb]
  @crlf = "\r\n"
end

Instance Attribute Details

#clengthObject (readonly)

Returns the value of attribute clength.



11
12
13
# File 'lib/hayabusa_http_session_request.rb', line 11

def clength
  @clength
end

Returns the value of attribute cookie.



11
12
13
# File 'lib/hayabusa_http_session_request.rb', line 11

def cookie
  @cookie
end

#files_arrObject (readonly)

Returns the value of attribute files_arr.



11
12
13
# File 'lib/hayabusa_http_session_request.rb', line 11

def files_arr
  @files_arr
end

#getObject (readonly)

Returns the value of attribute get.



11
12
13
# File 'lib/hayabusa_http_session_request.rb', line 11

def get
  @get
end

#headersObject (readonly)

Returns the value of attribute headers.



11
12
13
# File 'lib/hayabusa_http_session_request.rb', line 11

def headers
  @headers
end

#http_versionObject (readonly)

Returns the value of attribute http_version.



11
12
13
# File 'lib/hayabusa_http_session_request.rb', line 11

def http_version
  @http_version
end

#metaObject (readonly)

Returns the value of attribute meta.



11
12
13
# File 'lib/hayabusa_http_session_request.rb', line 11

def meta
  @meta
end

#page_pathObject (readonly)

Returns the value of attribute page_path.



11
12
13
# File 'lib/hayabusa_http_session_request.rb', line 11

def page_path
  @page_path
end

#percentObject (readonly)

Returns the value of attribute percent.



11
12
13
# File 'lib/hayabusa_http_session_request.rb', line 11

def percent
  @percent
end

#postObject (readonly)

Returns the value of attribute post.



11
12
13
# File 'lib/hayabusa_http_session_request.rb', line 11

def post
  @post
end

#readObject (readonly)

Returns the value of attribute read.



11
12
13
# File 'lib/hayabusa_http_session_request.rb', line 11

def read
  @read
end

#secs_leftObject (readonly)

Returns the value of attribute secs_left.



11
12
13
# File 'lib/hayabusa_http_session_request.rb', line 11

def secs_left
  @secs_left
end

#speedObject (readonly)

Returns the value of attribute speed.



11
12
13
# File 'lib/hayabusa_http_session_request.rb', line 11

def speed
  @speed
end

Class Method Details

.convert_post(seton, post_val, args = {}) ⇒ Object

Converts post-result to the right type of hash.



247
248
249
250
251
# File 'lib/hayabusa_http_session_request.rb', line 247

def self.convert_post(seton, post_val, args = {})
  post_val.each do |varname, value|
    Knj::Web.parse_name(seton, varname, value, args)
  end
end

.parse_post(post_data, post_treated) ⇒ Object

Takes raw post data and puts it into a hash.



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

def self.parse_post(post_data, post_treated)
  post_data.split("&").each do |splitted|
    splitted = splitted.split("=")
    key = Knj::Web.urldec(splitted[0]).to_s.encode("utf-8")
    val = splitted[1].to_s.encode("utf-8")
    post_treated[key] = val
  end
end

Instance Method Details

#delete_tempfilesObject

Deletes all tempfiles created by this object. This is useually called from Http_session at the end of a request.



254
255
256
257
258
259
# File 'lib/hayabusa_http_session_request.rb', line 254

def delete_tempfiles
  @files_arr.delete_if do |tempfile_path|
    File.unlink(tempfile_path) if File.exists?(tempfile_path)
    true
  end
end

#modified_sinceObject

Parses the if-modified-since header and returns it as a Time-object. Returns false is no if-modified-since-header is given or raises an RuntimeError if it cant be parsed.



233
234
235
236
237
238
239
240
241
242
243
244
# File 'lib/hayabusa_http_session_request.rb', line 233

def modified_since
  return @modified_since if @modified_since
  return false if !@meta["HTTP_IF_MODIFIED_SINCE"]
  
  mod_match = @meta["HTTP_IF_MODIFIED_SINCE"].match(/^([A-z]+),\s+(\d+)\s+([A-z]+)\s+(\d+)\s+(\d+):(\d+):(\d+)\s+(.+)$/)
  raise "Could not parse 'HTTP_IF_MODIFIED_SINCE'." if !mod_match
  
  month_no = Datet.month_str_to_no(mod_match[3])
  @modified_since = Time.utc(mod_match[4].to_i, month_no, mod_match[2].to_i, mod_match[5].to_i, mod_match[6].to_i, mod_match[7].to_i)
  
  return @modified_since
end

#read_socket(socket, cont) ⇒ Object

Reads content from the socket until the end of headers. Also does various error-checks.



21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# File 'lib/hayabusa_http_session_request.rb', line 21

def read_socket(socket, cont)
  loop do
    if socket.closed?
      if !cont.empty?
        #This might just be the last allowed request...
        break
      else
        #This should not have happened...
        raise Errno::ECONNRESET, "Socket closed before trying to read."
      end
    end
    
    read = socket.gets
    raise Errno::ECONNRESET, "Socket returned non-string: '#{read.class.name}'." if !read.is_a?(String)
    cont << read
    break if cont[-4..-1] == "\r\n\r\n" or cont[-2..-1] == "\n\n"
  end
end

#resetObject



40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# File 'lib/hayabusa_http_session_request.rb', line 40

def reset
  @modified_since = nil
  @get = nil
  @post = nil
  @cookie = nil
  @meta = nil
  @page_path = nil
  @headers = nil
  @http_version = nil
  @read = nil
  @clength = nil
  @speec = nil
  @percent = nil
  @secs_left = nil
end

#socket_parse(socket) ⇒ Object

Generates data on object from the given socket.



57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
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
# File 'lib/hayabusa_http_session_request.rb', line 57

def socket_parse(socket)
  self.reset
  cont = ""
  self.read_socket(socket, cont)
  
  #Parse URI (page_path and get).
  match = cont.match(/^(GET|POST|HEAD)\s+(.+)\s+HTTP\/1\.(\d+)\s*/)
  raise "Could not parse request: '#{cont.split("\n").first}'." if !match
  
  @http_version = "1.#{match[3]}"
  
  method = match[1]
  cont = cont.gsub(match[0], "")
  
  uri_raw = match[2]
  uri_raw = "index.rhtml" if uri_raw == ""
  
  if uri = URI.parse(match[2]) rescue nil
    uri = {:path => uri.path.slice(1, 999), :query => uri.query}
  else
    uri = {:path => match[2], :query => ""}
  end
  
  page_filepath = Knj::Web.urldec(uri[:path])
  
  if page_filepath.empty? or page_filepath == "/" or File.directory?("#{@hb.config[:doc_root]}/#{page_filepath}")
    page_filepath = "#{page_filepath}/#{@hb.config[:default_page]}"
  end
  
  @page_path = "#{@hb.config[:doc_root]}/#{page_filepath}"
  @get = Knj::Web.parse_urlquery(uri[:query], :urldecode => true, :force_utf8 => true)
  @files_arr = []
  
  if @get["_hb_httpsession_id"]
    @hb.httpsessions_ids[@get["_hb_httpsession_id"]] = @args[:httpsession]
  end
  
  begin
    #Parse headers, cookies and meta.
    @headers = {}
    @cookie = {}
    @meta = {
      "REQUEST_METHOD" => method,
      "QUERY_STRING" => uri[:query],
      "REQUEST_URI" => match[2],
      "SCRIPT_NAME" => uri[:path]
    }
    
    cont.scan(/^(\S+):\s*(.+)\r\n/) do |header_match|
      key = header_match[0].downcase
      val = header_match[1]
      
      @headers[key] = [] if !@headers.has_key?(key)
      @headers[key] << val
      
      case key
        when "cookie"
          Knj::Web.parse_cookies(val).each do |key, val|
            @cookie[key] = val
          end
        when "content-length"
          @clength = val.to_i
        else
          key = key.upcase.gsub("-", "_")
          @meta["HTTP_#{key}"] = val
      end
    end
    
    
    #Parse post
    @post = {}
    
    if method == "POST"
      post_treated = {}
      
      @speed = nil
      @read = 0
      post_data = ""
      
      Thread.new do
        begin
          time_cur = Time.now
          read_last = 0
          sleep 0.1
          
          while @clength and @read != nil and @read < @clength
            break if !@clength or !@read
            
            time_now = Time.now
            time_betw = time_now.to_f - time_cur.to_f
            read_betw = @read - read_last
            
            time_cur = time_now
            read_last = @read
            
            @percent = @read.to_f / @clength.to_f
            @speed = read_betw.to_f / time_betw.to_f
            
            bytes_left = @clength - read
            
            if @speed > 0 and bytes_left > 0
              @secs_left = bytes_left.to_f / @speed
            else
              @secs_left = false
            end
            
            sleep 2
          end
        rescue => e
          @hb.handle_error(e)
        end
      end
      
      if @headers["content-type"] and match = @headers["content-type"].first.match(/^multipart\/form-data; boundary=(.+)\Z/)
        #This is useually used for file-uploads and is expected to be slower because of disk-access. Use temporary file to spare memory.
        mode = :multipart
        post_data = Tempfile.open("hayabusa_multipart_raw")
      else
        #This is a normal post useually only with strings and such. That can be handeled by memory.
        mode = :normal
        post_data = ""
      end
      
      while @read < @clength
        read_size = @clength - @read
        read_size = 8192 if read_size > 8192
        
        raise Errno::ECONNRESET, "Socket closed." if socket.closed?
        read = socket.read(read_size)
        raise Errno::ECONNRESET, "Socket returned non-string: '#{read.class.name}'." if !read.is_a?(String)
        
        post_data << read
        @read += read.bytesize
      end
      
      if mode == :multipart
        post_data.rewind
        post_treated = Hayabusa::Http_session::Post_multipart.new(
          :io => post_data,
          :boundary => match[1],
          :crlf => @crlf,
          :files_arr => @files_arr
        ).return
        post_data.close(true)
        
        Hayabusa::Http_session::Request.convert_post(@post, post_treated, {:urldecode => false})
      else
        Hayabusa::Http_session::Request.parse_post(post_data, post_treated)
        Hayabusa::Http_session::Request.convert_post(@post, post_treated, {:urldecode => true})
      end
    end
  ensure
    @read = nil
    @speed = nil
    @clength = nil
    @percent = nil
    @secs_left = nil
    
    #If it doesnt get unset we could have a serious memory reference GC problem.
    if @get["_hb_httpsession_id"]
      @hb.httpsessions_ids.delete(@get["_hb_httpsession_id"])
    end
  end
end