plezi 0.9.2 → 0.10.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +30 -0
- data/README.md +44 -31
- data/bin/plezi +3 -3
- data/lib/plezi.rb +21 -43
- data/lib/plezi/common/defer.rb +21 -0
- data/lib/plezi/common/dsl.rb +115 -91
- data/lib/plezi/common/redis.rb +44 -0
- data/lib/plezi/common/settings.rb +58 -0
- data/lib/plezi/handlers/controller_core.rb +132 -0
- data/lib/plezi/handlers/controller_magic.rb +85 -259
- data/lib/plezi/handlers/http_router.rb +139 -60
- data/lib/plezi/handlers/route.rb +9 -178
- data/lib/plezi/handlers/stubs.rb +2 -2
- data/lib/plezi/helpers/http_sender.rb +72 -0
- data/lib/plezi/helpers/magic_helpers.rb +12 -0
- data/lib/plezi/{server → helpers}/mime_types.rb +0 -0
- data/lib/plezi/version.rb +1 -1
- data/plezi.gemspec +3 -11
- data/resources/Gemfile +20 -21
- data/resources/controller.rb +2 -2
- data/resources/oauth_config.rb +1 -1
- data/resources/redis_config.rb +2 -0
- data/test/plezi_tests.rb +39 -46
- metadata +24 -33
- data/lib/plezi/common/logging.rb +0 -60
- data/lib/plezi/eventmachine/connection.rb +0 -190
- data/lib/plezi/eventmachine/em.rb +0 -98
- data/lib/plezi/eventmachine/io.rb +0 -272
- data/lib/plezi/eventmachine/protocol.rb +0 -54
- data/lib/plezi/eventmachine/queue.rb +0 -51
- data/lib/plezi/eventmachine/ssl_connection.rb +0 -144
- data/lib/plezi/eventmachine/timers.rb +0 -117
- data/lib/plezi/eventmachine/workers.rb +0 -33
- data/lib/plezi/handlers/http_echo.rb +0 -27
- data/lib/plezi/handlers/http_host.rb +0 -214
- data/lib/plezi/handlers/magic_helpers.rb +0 -32
- data/lib/plezi/server/http.rb +0 -129
- data/lib/plezi/server/http_protocol.rb +0 -319
- data/lib/plezi/server/http_request.rb +0 -146
- data/lib/plezi/server/http_response.rb +0 -319
- data/lib/plezi/server/websocket.rb +0 -251
- data/lib/plezi/server/websocket_client.rb +0 -178
- data/lib/plezi/server/ws_response.rb +0 -161
@@ -1,32 +0,0 @@
|
|
1
|
-
module Plezi
|
2
|
-
|
3
|
-
# set magic cookies
|
4
|
-
#
|
5
|
-
# magic cookies keep track of both incoming and outgoing cookies, setting the response's cookies as well as the combined cookie respetory (held by the request object).
|
6
|
-
#
|
7
|
-
# use only the []= for magic cookies. merge and update might not set the response cookies.
|
8
|
-
class Cookies < ::Hash
|
9
|
-
# sets the Magic Cookie's controller object (which holds the response object and it's `set_cookie` method).
|
10
|
-
def set_controller controller
|
11
|
-
@controller = controller
|
12
|
-
end
|
13
|
-
# overrides the []= method to set the cookie for the response (by encoding it and preparing it to be sent), as well as to save the cookie in the combined cookie jar (unencoded and available).
|
14
|
-
def []= key, val
|
15
|
-
if key.is_a?(Symbol) && self.has_key?( key.to_s)
|
16
|
-
key = key.to_s
|
17
|
-
elsif self.has_key?( key.to_s.to_sym)
|
18
|
-
key = key.to_s.to_sym
|
19
|
-
end
|
20
|
-
@controller.response.set_cookie key, (val ? val.to_s.dup : nil) if @controller
|
21
|
-
super
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
module Helpers
|
26
|
-
# a proc that allows Hashes to search for key-value pairs while also converting keys from objects to symbols and from symbols to strings.
|
27
|
-
#
|
28
|
-
# (key type agnostic search Hash proc)
|
29
|
-
HASH_SYM_PROC = Proc.new {|h,k| k = (Symbol === k ? k.to_s : k.to_s.to_sym); h[k] if h.has_key?(k) }
|
30
|
-
end
|
31
|
-
|
32
|
-
end
|
data/lib/plezi/server/http.rb
DELETED
@@ -1,129 +0,0 @@
|
|
1
|
-
# encoding: UTF-8
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
module Plezi
|
6
|
-
|
7
|
-
# includes general helper methods for HTTP protocol and related (url encoding etc')
|
8
|
-
module HTTP
|
9
|
-
module_function
|
10
|
-
|
11
|
-
# Based on the WEBRick source code, escapes &, ", > and < in a String object
|
12
|
-
def escape(string)
|
13
|
-
string.gsub(/&/n, '&')
|
14
|
-
.gsub(/\"/n, '"')
|
15
|
-
.gsub(/>/n, '>')
|
16
|
-
.gsub(/</n, '<')
|
17
|
-
end
|
18
|
-
def add_param_to_hash param_name, param_value, target_hash
|
19
|
-
begin
|
20
|
-
a = target_hash
|
21
|
-
p = param_name.gsub(']',' ').split(/\[/)
|
22
|
-
val = rubyfy! param_value
|
23
|
-
p.each_index { |i| p[i].strip! ; n = p[i].match(/^[0-9]+$/) ? p[i].to_i : p[i].to_sym ; p[i+1] ? [ ( a[n] ||= ( p[i+1] == ' ' ? [] : {} ) ), ( a = a[n]) ] : ( a.is_a?(Hash) ? (a[n] ? (a[n].is_a?(Array) ? (a << val) : a[n] = [a[n], val] ) : (a[n] = val) ) : (a << val) ) }
|
24
|
-
rescue Exception => e
|
25
|
-
Plezi.error e
|
26
|
-
Plezi.error "(Silent): parameters parse error for #{param_name} ... maybe conflicts with a different set?"
|
27
|
-
target_hash[param_name] = rubyfy! param_value
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
def decode object, decode_method = :form
|
32
|
-
if object.is_a?(Hash)
|
33
|
-
object.values.each {|v| decode v, decode_method}
|
34
|
-
elsif object.is_a?(Array)
|
35
|
-
object.each {|v| decode v, decode_method}
|
36
|
-
elsif object.is_a?(String)
|
37
|
-
case decode_method
|
38
|
-
when :form
|
39
|
-
object.gsub!('+', '%20')
|
40
|
-
object.gsub!(/\%[0-9a-fA-F][0-9a-fA-F]/) {|m| m[1..2].to_i(16).chr}
|
41
|
-
when :uri, :url
|
42
|
-
object.gsub!(/\%[0-9a-fA-F][0-9a-fA-F]/) {|m| m[1..2].to_i(16).chr}
|
43
|
-
when :html
|
44
|
-
object.gsub!(/&/i, '&')
|
45
|
-
object.gsub!(/"/i, '"')
|
46
|
-
object.gsub!(/>/i, '>')
|
47
|
-
object.gsub!(/</i, '<')
|
48
|
-
when :utf8
|
49
|
-
|
50
|
-
else
|
51
|
-
|
52
|
-
end
|
53
|
-
object.gsub!(/&#([0-9a-fA-F]{2});/) {|m| m.match(/[0-9a-fA-F]{2}/)[0].hex.chr}
|
54
|
-
object.gsub!(/&#([0-9]{4});/) {|m| [m.match(/[0-9]+/)[0].to_i].pack 'U'}
|
55
|
-
make_utf8! object
|
56
|
-
return object
|
57
|
-
elsif object.is_a?(Symbol)
|
58
|
-
str = object.to_str
|
59
|
-
decode str, decode_method
|
60
|
-
return str.to_sym
|
61
|
-
else
|
62
|
-
raise "Plezi Raising Hell (don't misuse us)!"
|
63
|
-
end
|
64
|
-
end
|
65
|
-
def encode object, decode_method = :form
|
66
|
-
if object.is_a?(Hash)
|
67
|
-
object.values.each {|v| encode v, decode_method}
|
68
|
-
elsif object.is_a?(Array)
|
69
|
-
object.each {|v| encode v, decode_method}
|
70
|
-
elsif object.is_a?(String)
|
71
|
-
case decode_method
|
72
|
-
when :uri, :url, :form
|
73
|
-
object.force_encoding 'binary'
|
74
|
-
object.gsub!(/[^a-zA-Z0-9\*\.\_\-]/) {|m| m.ord <= 16 ? "%0#{m.ord.to_s(16)}" : "%#{m.ord.to_s(16)}"}
|
75
|
-
when :html
|
76
|
-
object.gsub!('&', '&')
|
77
|
-
object.gsub!('"', '"')
|
78
|
-
object.gsub!('>', '>')
|
79
|
-
object.gsub!('<', '<')
|
80
|
-
object.gsub!(/[^\sa-zA-Z\d\&\;]/) {|m| '&#%04d;' % m.unpack('U')[0] }
|
81
|
-
# object.gsub!(/[^\s]/) {|m| "&#%04d;" % m.unpack('U')[0] }
|
82
|
-
object.force_encoding 'binary'
|
83
|
-
when :utf8
|
84
|
-
object.gsub!(/[^\sa-zA-Z\d]/) {|m| '&#%04d;' % m.unpack('U')[0] }
|
85
|
-
object.force_encoding 'binary'
|
86
|
-
else
|
87
|
-
|
88
|
-
end
|
89
|
-
return object
|
90
|
-
elsif object.is_a?(Symbol)
|
91
|
-
str = object.to_str
|
92
|
-
encode str, decode_method
|
93
|
-
return str.to_sym
|
94
|
-
else
|
95
|
-
raise "Plezi Raising Hell (don't misuse us)!"
|
96
|
-
end
|
97
|
-
end
|
98
|
-
# extracts parameters from the query
|
99
|
-
def extract_data data, target_hash, decode = :form
|
100
|
-
data.each do |set|
|
101
|
-
list = set.split('=')
|
102
|
-
list.each {|s| HTTP.decode s, decode if s}
|
103
|
-
add_param_to_hash list.shift, list.join('='), target_hash
|
104
|
-
end
|
105
|
-
end
|
106
|
-
|
107
|
-
# re-encodes a string into UTF-8
|
108
|
-
def make_utf8!(string, encoding= 'utf-8')
|
109
|
-
return false unless string
|
110
|
-
string.force_encoding('binary').encode!(encoding, 'binary', invalid: :replace, undef: :replace, replace: '') unless string.force_encoding(encoding).valid_encoding?
|
111
|
-
string
|
112
|
-
end
|
113
|
-
|
114
|
-
# Changes String to a Ruby Object, if it's a special string
|
115
|
-
def rubyfy!(string)
|
116
|
-
return false unless string
|
117
|
-
# make_utf8! string
|
118
|
-
if string == 'true'
|
119
|
-
string = true
|
120
|
-
elsif string == 'false'
|
121
|
-
string = false
|
122
|
-
elsif string.match(/[0-9]/) && !string.match(/[^0-9]/)
|
123
|
-
string = string.to_i
|
124
|
-
end
|
125
|
-
string
|
126
|
-
end
|
127
|
-
|
128
|
-
end
|
129
|
-
end
|
@@ -1,319 +0,0 @@
|
|
1
|
-
module Plezi
|
2
|
-
|
3
|
-
# this module is the protocol (controller) for the HTTP server.
|
4
|
-
#
|
5
|
-
#
|
6
|
-
# to do: implemet logging, support body types: multipart (non-ASCII form data / uploaded files), json & xml
|
7
|
-
class HTTPProtocol < EventMachine::Protocol
|
8
|
-
|
9
|
-
HTTP_METHODS = %w{GET HEAD POST PUT DELETE TRACE OPTIONS}
|
10
|
-
HTTP_METHODS_REGEXP = /^#{HTTP_METHODS.join('|')}/
|
11
|
-
HTTP_FIRE_REQUEST = Proc.new {|handler, request| handler.on_request request}
|
12
|
-
|
13
|
-
def initialize connection, params
|
14
|
-
super
|
15
|
-
@parser_stage = 0
|
16
|
-
@parser_data = {}
|
17
|
-
@parser_body = ''
|
18
|
-
@parser_chunk = ''
|
19
|
-
@parser_length = 0
|
20
|
-
@@rack_dictionary ||= {'HOST'.freeze => :host_name, 'REQUEST_METHOD'.freeze => :method,
|
21
|
-
'PATH_INFO'.freeze => :path, 'QUERY_STRING'.freeze => :query,
|
22
|
-
'SERVER_NAME'.freeze => :host_name, 'SERVER_PORT'.freeze => :port,
|
23
|
-
'rack.url_scheme'.freeze => :requested_protocol}
|
24
|
-
end
|
25
|
-
|
26
|
-
# called when data is recieved.
|
27
|
-
#
|
28
|
-
# this method is called within a lock on the connection (Mutex) - craeful from double locking.
|
29
|
-
#
|
30
|
-
# typically returns an Array with any data not yet processed (to be returned to the in-que)... but here it always processes (or discards) the data.
|
31
|
-
def on_message
|
32
|
-
# parse the request
|
33
|
-
parse_message
|
34
|
-
# if (@parser_stage == 1) && @parser_data[:version] >= 1.1
|
35
|
-
# # send 100 continue message????? doesn't work! both Crome and Safari go crazy if this is sent after the request was sent (but before all the packets were recieved... msgs over 1 Mb).
|
36
|
-
# # Plezi.push_event Proc.new { Plezi.info "sending continue signal."; connection.send_nonblock "100 Continue\r\n\r\n" }
|
37
|
-
# # connection.send_unsafe_interrupt "100 Continue\r\n\r\n" # causes double lock on connection
|
38
|
-
# end
|
39
|
-
true
|
40
|
-
end
|
41
|
-
|
42
|
-
|
43
|
-
# Protocol specific helper methods.
|
44
|
-
|
45
|
-
# parses incoming data
|
46
|
-
def parse_message data = nil
|
47
|
-
data ||= connection.read.to_s.lines.to_a
|
48
|
-
# require 'pry'; binding.pry
|
49
|
-
if @parser_stage == 0
|
50
|
-
return false unless parse_method data
|
51
|
-
end
|
52
|
-
if @parser_stage == 1
|
53
|
-
return false unless parse_head data
|
54
|
-
end
|
55
|
-
if @parser_stage == 2
|
56
|
-
return false unless parse_body data
|
57
|
-
end
|
58
|
-
true
|
59
|
-
end
|
60
|
-
|
61
|
-
# parses the method request (the first line in the HTTP request).
|
62
|
-
def parse_method data
|
63
|
-
return false unless data[0] && data[0].match(HTTP_METHODS_REGEXP)
|
64
|
-
@parser_data[:time_recieved] = Time.now
|
65
|
-
@parser_data[:params] = {}
|
66
|
-
@parser_data[:cookies] = Cookies.new
|
67
|
-
@parser_data[:method] = ''
|
68
|
-
@parser_data[:query] = ''
|
69
|
-
@parser_data[:original_path] = ''
|
70
|
-
@parser_data[:path] = ''
|
71
|
-
if defined? ::Rack
|
72
|
-
@parser_data['rack.version'] = Rack::VERSION
|
73
|
-
@parser_data['rack.multithread'] = true
|
74
|
-
@parser_data['rack.multiprocess'] = false
|
75
|
-
@parser_data['rack.hijack?'] = false
|
76
|
-
@parser_data['rack.logger'] = Plezi.logger
|
77
|
-
end
|
78
|
-
@parser_data[:method], @parser_data[:query], @parser_data[:version] = data.shift.split(/[\s]+/)
|
79
|
-
@parser_data[:version] = (@parser_data[:version] || 'HTTP/1.1').match(/[0-9\.]+/).to_s.to_f
|
80
|
-
data.shift while data[0].to_s.match /^[\r\n]+/
|
81
|
-
@parser_stage = 1
|
82
|
-
end
|
83
|
-
|
84
|
-
#parses the head on a request (headers and values).
|
85
|
-
def parse_head data
|
86
|
-
until data[0].nil? || data[0].match(/^[\r\n]+$/)
|
87
|
-
m = data.shift.match(/^([^:]*):[\s]*([^\r\n]*)/)
|
88
|
-
# move cookies to cookie-jar, all else goes to headers
|
89
|
-
case m[1].downcase
|
90
|
-
when 'cookie'
|
91
|
-
HTTP.extract_data m[2].split(/[;,][\s]?/), @parser_data[:cookies], :uri
|
92
|
-
end
|
93
|
-
@parser_data[ HTTP.make_utf8!(m[1]).downcase ] ? (@parser_data[ HTTP.make_utf8!(m[1]).downcase ] << ", #{HTTP.make_utf8! m[2]}"): (@parser_data[ HTTP.make_utf8!(m[1]).downcase ] = HTTP.make_utf8! m[2])
|
94
|
-
end
|
95
|
-
return false unless data[0]
|
96
|
-
data.shift while data[0] && data[0].match(/^[\r\n]+$/)
|
97
|
-
if @parser_data['transfer-coding'] || (@parser_data['content-length'] && @parser_data['content-length'].to_i != 0) || @parser_data['content-type']
|
98
|
-
@parser_stage = 2
|
99
|
-
else
|
100
|
-
# create request object and hand over to handler
|
101
|
-
complete_request
|
102
|
-
return parse_message data unless data.empty?
|
103
|
-
end
|
104
|
-
true
|
105
|
-
end
|
106
|
-
|
107
|
-
#parses the body of a request.
|
108
|
-
def parse_body data
|
109
|
-
# check for body is needed, if exists and if complete
|
110
|
-
if @parser_data['transfer-coding'] == 'chunked'
|
111
|
-
until data.empty? || data[0].to_s.match(/0(\r)?\n/)
|
112
|
-
if @parser_length == 0
|
113
|
-
@parser_length = data.to_s.shift.match(/^[a-z0-9A-Z]+/).to_i(16)
|
114
|
-
@parser_chunk.clear
|
115
|
-
end
|
116
|
-
unless @parser_length == 0
|
117
|
-
@parser_chunk << data.shift while ( (@parser_length >= @parser_chunk.bytesize) && data[0])
|
118
|
-
end
|
119
|
-
if @parser_length <= @parser_chunk.bytesize
|
120
|
-
@parser_body << @parser_chunk.byteslice(0, @parser_body.bytesize)
|
121
|
-
@parser_length = 0
|
122
|
-
@parser_chunk.clear
|
123
|
-
end
|
124
|
-
end
|
125
|
-
return false unless data[0].to_s.match(/0(\r)?\n/)
|
126
|
-
true until data.empty? || data.shift.match(/^[\r\n]+$/)
|
127
|
-
data.shift while data[0].to_s.match /^[\r\n]+$/
|
128
|
-
elsif @parser_data['content-length'].to_i
|
129
|
-
@parser_length = @parser_data['content-length'].to_i if @parser_length == 0
|
130
|
-
@parser_chunk << data.shift while @parser_length > @parser_chunk.bytesize && data[0]
|
131
|
-
return false if @parser_length > @parser_chunk.bytesize
|
132
|
-
@parser_body = @parser_chunk.byteslice(0, @parser_length)
|
133
|
-
@parser_chunk.clear
|
134
|
-
else
|
135
|
-
Plezi.warn 'bad body request - trying to read'
|
136
|
-
@parser_body << data.shift while data[0] && !data[0].match(/^[\r\n]+$/)
|
137
|
-
end
|
138
|
-
# parse body (POST parameters)
|
139
|
-
read_body
|
140
|
-
|
141
|
-
# complete request
|
142
|
-
complete_request
|
143
|
-
|
144
|
-
#read next request unless data is finished
|
145
|
-
return parse_message data unless data.empty?
|
146
|
-
true
|
147
|
-
end
|
148
|
-
|
149
|
-
# completes the parsing of the request and sends the request to the handler.
|
150
|
-
def complete_request
|
151
|
-
#finalize params and query properties
|
152
|
-
m = @parser_data[:query].match /(([a-z0-9A-Z]+):\/\/)?(([^\/\:]+))?(:([0-9]+))?([^\?\#]*)(\?([^\#]*))?/
|
153
|
-
@parser_data[:requested_protocol] = m[1] || (connection.ssl? ? 'https' : 'http')
|
154
|
-
@parser_data[:host_name] = m[4] || (@parser_data['host'] ? @parser_data['host'].match(/^[^:]*/).to_s : nil)
|
155
|
-
@parser_data[:port] = m[6] || (@parser_data['host'] ? @parser_data['host'].match(/:([0-9]*)/).to_a[1] : nil)
|
156
|
-
@parser_data[:original_path] = HTTP.decode(m[7], :uri) || '/'
|
157
|
-
@parser_data['host'] ||= "#{@parser_data[:host_name]}:#{@parser_data[:port]}"
|
158
|
-
# parse query for params - m[9] is the data part of the query
|
159
|
-
if m[9]
|
160
|
-
HTTP.extract_data m[9].split(/[&;]/), @parser_data[:params]
|
161
|
-
end
|
162
|
-
|
163
|
-
HTTP.make_utf8! @parser_data[:original_path]
|
164
|
-
@parser_data[:path] = @parser_data[:original_path].chomp('/')
|
165
|
-
@parser_data[:original_path].freeze
|
166
|
-
|
167
|
-
# the following can be used to extract the request's 'format':
|
168
|
-
# /(^.*)(\.[^\.\/]*)$/.match @parser_data[:path]
|
169
|
-
# ...? should this be done? Could be limited to certain formats:
|
170
|
-
# /(^.*)(\.(txt|html|json|js|xml|xhtml|csv))$/.match @parser_data[:path]
|
171
|
-
# ... even worst?
|
172
|
-
|
173
|
-
HTTP.make_utf8! @parser_data[:host_name] if @parser_data[:host_name]
|
174
|
-
HTTP.make_utf8! @parser_data[:query]
|
175
|
-
|
176
|
-
@parser_data[:client_ip] = @parser_data['x-forwarded-for'].to_s.split(/,[\s]?/)[0] || (connection.socket.remote_address.ip_address) rescue 'unknown IP'
|
177
|
-
|
178
|
-
@@rack_dictionary.each {|k,v| @parser_data[k] = @parser_data[v]}
|
179
|
-
|
180
|
-
#create request
|
181
|
-
request = HTTPRequest.new connection
|
182
|
-
request.update @parser_data
|
183
|
-
|
184
|
-
#clear current state
|
185
|
-
@parser_data.clear
|
186
|
-
@parser_body.clear
|
187
|
-
@parser_chunk.clear
|
188
|
-
@parser_length = 0
|
189
|
-
@parser_stage = 0
|
190
|
-
|
191
|
-
#check for server-responses
|
192
|
-
case request.request_method
|
193
|
-
when 'TRACE'
|
194
|
-
return true
|
195
|
-
when 'OPTIONS'
|
196
|
-
Plezi.run_async do
|
197
|
-
response = HTTPResponse.new request
|
198
|
-
response[:Allow] = 'GET,HEAD,POST,PUT,DELETE,OPTIONS'
|
199
|
-
response['access-control-allow-origin'] = '*'
|
200
|
-
response['content-length'] = 0
|
201
|
-
response.finish
|
202
|
-
end
|
203
|
-
return true
|
204
|
-
end
|
205
|
-
|
206
|
-
#pass it to the handler or decler error.
|
207
|
-
if connection && connection.handler
|
208
|
-
EventMachine.queue [connection.handler, request], HTTP_FIRE_REQUEST
|
209
|
-
else
|
210
|
-
Plezi.error 'No Handler for this HTTP connection.'
|
211
|
-
end
|
212
|
-
end
|
213
|
-
|
214
|
-
# read the body's data and parse any incoming data.
|
215
|
-
def read_body
|
216
|
-
# parse content
|
217
|
-
case @parser_data['content-type'].to_s
|
218
|
-
when /x-www-form-urlencoded/
|
219
|
-
HTTP.extract_data @parser_body.split(/[&;]/), @parser_data[:params], :form # :uri
|
220
|
-
when /multipart\/form-data/
|
221
|
-
read_multipart @parser_data, @parser_body
|
222
|
-
when /text\/xml/
|
223
|
-
# to-do support xml?
|
224
|
-
@parser_data[:body] = @parser_body.dup
|
225
|
-
when /application\/json/
|
226
|
-
@parser_data[:body] = @parser_body.dup
|
227
|
-
JSON.parse(HTTP.make_utf8! @parser_data[:body]).each {|k, v| HTTP.add_param_to_hash k, v, @parser_data[:params]}
|
228
|
-
else
|
229
|
-
@parser_data[:body] = @parser_body.dup
|
230
|
-
Plezi.error "POST body type (#{@parser_data['content-type']}) cannot be parsed. raw body is kept in the request's data as request[:body]: #{@parser_body}"
|
231
|
-
end
|
232
|
-
end
|
233
|
-
|
234
|
-
# parse a mime/multipart body or part.
|
235
|
-
def read_multipart headers, part, name_prefix = ''
|
236
|
-
if headers['content-type'].to_s.match /multipart/
|
237
|
-
boundry = headers['content-type'].match(/boundary=([^\s]+)/)[1]
|
238
|
-
if headers['content-disposition'].to_s.match /name=/
|
239
|
-
if name_prefix.empty?
|
240
|
-
name_prefix << HTTP.decode(headers['content-disposition'].to_s.match(/name="([^"]*)"/)[1])
|
241
|
-
else
|
242
|
-
name_prefix << "[#{HTTP.decode(headers['content-disposition'].to_s.match(/name="([^"]*)"/)[1])}]"
|
243
|
-
end
|
244
|
-
end
|
245
|
-
part.split(/([\r]?\n)?--#{boundry}(--)?[\r]?\n/).each do |p|
|
246
|
-
unless p.strip.empty? || p=='--'
|
247
|
-
# read headers
|
248
|
-
h = {}
|
249
|
-
p = p.lines
|
250
|
-
while p[0].match(/^[^:]+:[^\r\n]+/)
|
251
|
-
m = p.shift.match(/^([^:]+):[\s]?([^\r\n]+)/)
|
252
|
-
h[m[1].downcase] = m[2]
|
253
|
-
end
|
254
|
-
if p[0].strip.empty?
|
255
|
-
p.shift
|
256
|
-
else
|
257
|
-
Plezi.error 'Expected empty line after last header - empty line missing.'
|
258
|
-
end
|
259
|
-
# send headers and body to be read
|
260
|
-
read_multipart h, p.join, name_prefix
|
261
|
-
end
|
262
|
-
end
|
263
|
-
return
|
264
|
-
end
|
265
|
-
|
266
|
-
# require a part body to exist (data exists) for parsing
|
267
|
-
return true if part.to_s.empty?
|
268
|
-
|
269
|
-
# convert part to `charset` if charset is defined?
|
270
|
-
|
271
|
-
if !headers['content-disposition']
|
272
|
-
Plezi.error "Wrong multipart format with headers: #{headers} and body: #{part}"
|
273
|
-
return
|
274
|
-
end
|
275
|
-
|
276
|
-
cd = {}
|
277
|
-
|
278
|
-
HTTP.extract_data headers['content-disposition'].match(/[^;];([^\r\n]*)/)[1].split(/[;,][\s]?/), cd, :uri
|
279
|
-
|
280
|
-
name = name_prefix.dup
|
281
|
-
|
282
|
-
if name_prefix.empty?
|
283
|
-
name << HTTP.decode(cd[:name][1..-2])
|
284
|
-
else
|
285
|
-
name << "[#{HTTP.decode(cd[:name][1..-2])}]"
|
286
|
-
end
|
287
|
-
if headers['content-type']
|
288
|
-
HTTP.add_param_to_hash "#{name}[data]", part, @parser_data[:params]
|
289
|
-
HTTP.add_param_to_hash "#{name}[type]", HTTP.make_utf8!(headers['content-type']), @parser_data[:params]
|
290
|
-
cd.each {|k,v| HTTP.add_param_to_hash "#{name}[#{k.to_s}]", HTTP.make_utf8!(v[1..-2]), @parser_data[:params] unless k == :name}
|
291
|
-
else
|
292
|
-
HTTP.add_param_to_hash name, HTTP.decode(part, :utf8), @parser_data[:params]
|
293
|
-
end
|
294
|
-
true
|
295
|
-
end
|
296
|
-
end
|
297
|
-
end
|
298
|
-
|
299
|
-
|
300
|
-
# # HTTPProtocol - to do list
|
301
|
-
#
|
302
|
-
# XML::
|
303
|
-
# support for XML HTTP body types?
|
304
|
-
#
|
305
|
-
# Charset::
|
306
|
-
# parse chareset for incoming content-type in the multipart request body? (or leave if binary?)
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
## Heroku/extra headers info
|
311
|
-
|
312
|
-
# All headers are considered to be case-insensitive, as per HTTP Specification.
|
313
|
-
# X-Forwarded-For: the originating IP address of the client connecting to the Heroku router
|
314
|
-
# X-Forwarded-Proto: the originating protocol of the HTTP request (example: https)
|
315
|
-
# X-Forwarded-Port: the originating port of the HTTP request (example: 443)
|
316
|
-
# X-Request-Start: unix timestamp (milliseconds) when the request was received by the router
|
317
|
-
# X-Request-Id: the Heroku HTTP Request ID
|
318
|
-
# Via: a code name for the Heroku router
|
319
|
-
|