vcap_common 2.0.8 → 2.0.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/lib/services/api.rb +0 -1
- data/lib/services/api/const.rb +0 -1
- data/lib/services/api/messages.rb +49 -6
- data/lib/vcap/component.rb +80 -18
- metadata +72 -26
- data/lib/services/api/clients/sds_client.rb +0 -84
- data/lib/services/api/multipart.rb +0 -191
data/lib/services/api.rb
CHANGED
data/lib/services/api/const.rb
CHANGED
@@ -26,7 +26,19 @@ module VCAP
|
|
26
26
|
optional :description, String
|
27
27
|
optional :info_url, URI::regexp(%w(http https))
|
28
28
|
optional :tags, [String]
|
29
|
+
optional :plan_details do
|
30
|
+
[
|
31
|
+
{
|
32
|
+
"name" => String,
|
33
|
+
"free" => bool,
|
34
|
+
optional("description") => String,
|
35
|
+
optional("extra") => String,
|
36
|
+
optional("unique_id") => String,
|
37
|
+
}
|
38
|
+
]
|
39
|
+
end
|
29
40
|
optional :plans, [String]
|
41
|
+
optional :plan_descriptions
|
30
42
|
optional :cf_plan_id
|
31
43
|
optional :plan_options
|
32
44
|
optional :binding_options
|
@@ -35,6 +47,8 @@ module VCAP
|
|
35
47
|
optional :timeout, Integer
|
36
48
|
optional :provider, String
|
37
49
|
optional :default_plan, String
|
50
|
+
optional :extra, String
|
51
|
+
optional :unique_id, String
|
38
52
|
end
|
39
53
|
|
40
54
|
class ProxiedServiceOfferingRequest < JsonMessage
|
@@ -49,6 +63,12 @@ module VCAP
|
|
49
63
|
required :credentials
|
50
64
|
end
|
51
65
|
|
66
|
+
class HandleUpdateRequestV2 < JsonMessage
|
67
|
+
required :token, String
|
68
|
+
required :gateway_data
|
69
|
+
required :credentials
|
70
|
+
end
|
71
|
+
|
52
72
|
class ListHandlesResponse < JsonMessage
|
53
73
|
required :handles, [Object]
|
54
74
|
end
|
@@ -72,12 +92,16 @@ module VCAP
|
|
72
92
|
end
|
73
93
|
|
74
94
|
class GatewayProvisionRequest < JsonMessage
|
75
|
-
required :
|
76
|
-
required :name,
|
77
|
-
required :
|
78
|
-
|
79
|
-
|
80
|
-
|
95
|
+
required :unique_id, String
|
96
|
+
required :name, String
|
97
|
+
required :email, String
|
98
|
+
|
99
|
+
optional :provider, String
|
100
|
+
optional :label, String
|
101
|
+
optional :plan, String
|
102
|
+
optional :version, String
|
103
|
+
optional :organization_guid, String
|
104
|
+
optional :space_guid, String
|
81
105
|
optional :plan_option
|
82
106
|
end
|
83
107
|
|
@@ -86,6 +110,7 @@ module VCAP
|
|
86
110
|
required :service_id, String
|
87
111
|
required :configuration
|
88
112
|
required :credentials
|
113
|
+
optional :dashboard_url, String
|
89
114
|
end
|
90
115
|
|
91
116
|
#
|
@@ -137,6 +162,24 @@ module VCAP
|
|
137
162
|
required :snapshots, [Object]
|
138
163
|
end
|
139
164
|
|
165
|
+
class CreateSnapshotV2Request < JsonMessage
|
166
|
+
required :name, /./
|
167
|
+
end
|
168
|
+
|
169
|
+
class SnapshotV2 < JsonMessage
|
170
|
+
required :snapshot_id, String
|
171
|
+
required :name, String
|
172
|
+
required :state, String
|
173
|
+
required :size, Integer
|
174
|
+
|
175
|
+
optional :created_time, String
|
176
|
+
optional :restored_time, String
|
177
|
+
end
|
178
|
+
|
179
|
+
class SnapshotListV2 < JsonMessage
|
180
|
+
required :snapshots, [Object]
|
181
|
+
end
|
182
|
+
|
140
183
|
class UpdateSnapshotNameRequest < JsonMessage
|
141
184
|
required :name, String
|
142
185
|
end
|
data/lib/vcap/component.rb
CHANGED
@@ -1,10 +1,12 @@
|
|
1
1
|
# Copyright (c) 2009-2011 VMware, Inc.
|
2
|
+
require "base64"
|
2
3
|
require "eventmachine"
|
3
|
-
require
|
4
|
-
require "yajl"
|
4
|
+
require "monitor"
|
5
5
|
require "nats/client"
|
6
|
-
require "
|
7
|
-
require
|
6
|
+
require "set"
|
7
|
+
require "thin"
|
8
|
+
require "yajl"
|
9
|
+
require "vmstat"
|
8
10
|
|
9
11
|
module VCAP
|
10
12
|
|
@@ -48,34 +50,85 @@ module VCAP
|
|
48
50
|
CONFIG_SUPPRESS = Set.new([:mbus, :service_mbus, :keys, :database_environment, :password, :pass, :token])
|
49
51
|
|
50
52
|
class << self
|
53
|
+
class SafeHash < BasicObject
|
54
|
+
def initialize(hash = {})
|
55
|
+
@hash = hash
|
56
|
+
end
|
57
|
+
|
58
|
+
def threadsafe!
|
59
|
+
@monitor = ::Monitor.new
|
60
|
+
end
|
61
|
+
|
62
|
+
def synchronize
|
63
|
+
if @monitor
|
64
|
+
@monitor.synchronize do
|
65
|
+
begin
|
66
|
+
@thread = ::Thread.current
|
67
|
+
yield
|
68
|
+
ensure
|
69
|
+
@thread = nil
|
70
|
+
end
|
71
|
+
end
|
72
|
+
else
|
73
|
+
yield
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def method_missing(sym, *args, &blk)
|
78
|
+
if @monitor && @thread != ::Thread.current
|
79
|
+
::Kernel.raise "Lock required"
|
80
|
+
end
|
81
|
+
|
82
|
+
@hash.__send__(sym, *args, &blk)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def varz
|
87
|
+
@varz ||= SafeHash.new
|
88
|
+
end
|
51
89
|
|
52
|
-
attr_reader :varz
|
53
90
|
attr_accessor :healthz
|
54
91
|
|
55
92
|
def updated_varz
|
56
93
|
@last_varz_update ||= 0
|
57
|
-
if Time.now.to_f - @last_varz_update >= 1
|
58
|
-
# Snapshot uptime
|
59
|
-
@varz[:uptime] = VCAP.uptime_string(Time.now - @varz[:start])
|
60
94
|
|
61
|
-
|
95
|
+
if Time.now.to_f - @last_varz_update >= 1
|
96
|
+
# Grab current cpu and memory usage
|
62
97
|
rss, pcpu = `ps -o rss=,pcpu= -p #{Process.pid}`.split
|
63
|
-
@varz[:mem] = rss.to_i
|
64
|
-
@varz[:cpu] = pcpu.to_f
|
65
98
|
|
66
|
-
|
99
|
+
# Update varz
|
100
|
+
varz.synchronize do
|
101
|
+
@last_varz_update = Time.now.to_f
|
102
|
+
|
103
|
+
varz[:uptime] = VCAP.uptime_string(Time.now - varz[:start])
|
104
|
+
varz[:mem] = rss.to_i
|
105
|
+
varz[:cpu] = pcpu.to_f
|
106
|
+
|
107
|
+
memory = Vmstat.memory
|
108
|
+
varz[:mem_used_bytes] = memory.active_bytes + memory.wired_bytes
|
109
|
+
varz[:mem_free_bytes] = memory.inactive_bytes + memory.free_bytes
|
110
|
+
|
111
|
+
varz[:cpu_load_avg] = Vmstat.load_average.one_minute
|
112
|
+
|
113
|
+
# Return duplicate while holding lock
|
114
|
+
return varz.dup
|
115
|
+
end
|
116
|
+
else
|
117
|
+
# Return duplicate while holding lock
|
118
|
+
varz.synchronize do
|
119
|
+
return varz.dup
|
120
|
+
end
|
67
121
|
end
|
68
|
-
varz
|
69
122
|
end
|
70
123
|
|
71
124
|
def updated_healthz
|
72
125
|
@last_healthz_update ||= 0
|
126
|
+
|
73
127
|
if Time.now.to_f - @last_healthz_update >= 1
|
74
|
-
# ...
|
75
128
|
@last_healthz_update = Time.now.to_f
|
76
129
|
end
|
77
130
|
|
78
|
-
healthz
|
131
|
+
healthz.dup
|
79
132
|
end
|
80
133
|
|
81
134
|
def start_http_server(host, port, auth, logger)
|
@@ -98,6 +151,9 @@ module VCAP
|
|
98
151
|
@discover[:uuid]
|
99
152
|
end
|
100
153
|
|
154
|
+
# Announces the availability of this component to NATS.
|
155
|
+
# Returns the published configuration of the component,
|
156
|
+
# including the ephemeral port and credentials.
|
101
157
|
def register(opts)
|
102
158
|
uuid = VCAP.secure_uuid
|
103
159
|
type = opts[:type]
|
@@ -108,6 +164,7 @@ module VCAP
|
|
108
164
|
nats = opts[:nats] || NATS
|
109
165
|
auth = [opts[:user] || VCAP.secure_uuid, opts[:password] || VCAP.secure_uuid]
|
110
166
|
logger = opts[:logger] || Logger.new(nil)
|
167
|
+
log_counter = opts[:log_counter]
|
111
168
|
|
112
169
|
# Discover message limited
|
113
170
|
@discover = {
|
@@ -120,9 +177,12 @@ module VCAP
|
|
120
177
|
}
|
121
178
|
|
122
179
|
# Varz is customizable
|
123
|
-
|
124
|
-
|
125
|
-
|
180
|
+
varz.synchronize do
|
181
|
+
varz.merge!(@discover.dup)
|
182
|
+
varz[:num_cores] = VCAP.num_cores
|
183
|
+
varz[:config] = sanitize_config(opts[:config]) if opts[:config]
|
184
|
+
varz[:log_counter] = log_counter if log_counter
|
185
|
+
end
|
126
186
|
|
127
187
|
@healthz = "ok\n".freeze
|
128
188
|
|
@@ -140,6 +200,8 @@ module VCAP
|
|
140
200
|
|
141
201
|
# Also announce ourselves on startup..
|
142
202
|
nats.publish('vcap.component.announce', @discover.to_json)
|
203
|
+
|
204
|
+
@discover
|
143
205
|
end
|
144
206
|
|
145
207
|
def update_discover_uptime
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: vcap_common
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.
|
4
|
+
version: 2.0.11
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-10-25 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: eventmachine
|
@@ -130,7 +130,7 @@ dependencies:
|
|
130
130
|
requirements:
|
131
131
|
- - ~>
|
132
132
|
- !ruby/object:Gem::Version
|
133
|
-
version: 1.0
|
133
|
+
version: '1.0'
|
134
134
|
type: :runtime
|
135
135
|
prerelease: false
|
136
136
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -138,7 +138,7 @@ dependencies:
|
|
138
138
|
requirements:
|
139
139
|
- - ~>
|
140
140
|
- !ruby/object:Gem::Version
|
141
|
-
version: 1.0
|
141
|
+
version: '1.0'
|
142
142
|
- !ruby/object:Gem::Dependency
|
143
143
|
name: multipart-post
|
144
144
|
requirement: !ruby/object:Gem::Requirement
|
@@ -171,6 +171,22 @@ dependencies:
|
|
171
171
|
- - ! '>='
|
172
172
|
- !ruby/object:Gem::Version
|
173
173
|
version: '0'
|
174
|
+
- !ruby/object:Gem::Dependency
|
175
|
+
name: vmstat
|
176
|
+
requirement: !ruby/object:Gem::Requirement
|
177
|
+
none: false
|
178
|
+
requirements:
|
179
|
+
- - ~>
|
180
|
+
- !ruby/object:Gem::Version
|
181
|
+
version: '2.0'
|
182
|
+
type: :runtime
|
183
|
+
prerelease: false
|
184
|
+
version_requirements: !ruby/object:Gem::Requirement
|
185
|
+
none: false
|
186
|
+
requirements:
|
187
|
+
- - ~>
|
188
|
+
- !ruby/object:Gem::Version
|
189
|
+
version: '2.0'
|
174
190
|
- !ruby/object:Gem::Dependency
|
175
191
|
name: rake
|
176
192
|
requirement: !ruby/object:Gem::Requirement
|
@@ -187,6 +203,38 @@ dependencies:
|
|
187
203
|
- - ~>
|
188
204
|
- !ruby/object:Gem::Version
|
189
205
|
version: 0.9.2
|
206
|
+
- !ruby/object:Gem::Dependency
|
207
|
+
name: rspec
|
208
|
+
requirement: !ruby/object:Gem::Requirement
|
209
|
+
none: false
|
210
|
+
requirements:
|
211
|
+
- - ! '>='
|
212
|
+
- !ruby/object:Gem::Version
|
213
|
+
version: '0'
|
214
|
+
type: :development
|
215
|
+
prerelease: false
|
216
|
+
version_requirements: !ruby/object:Gem::Requirement
|
217
|
+
none: false
|
218
|
+
requirements:
|
219
|
+
- - ! '>='
|
220
|
+
- !ruby/object:Gem::Version
|
221
|
+
version: '0'
|
222
|
+
- !ruby/object:Gem::Dependency
|
223
|
+
name: sinatra
|
224
|
+
requirement: !ruby/object:Gem::Requirement
|
225
|
+
none: false
|
226
|
+
requirements:
|
227
|
+
- - ! '>='
|
228
|
+
- !ruby/object:Gem::Version
|
229
|
+
version: '0'
|
230
|
+
type: :development
|
231
|
+
prerelease: false
|
232
|
+
version_requirements: !ruby/object:Gem::Requirement
|
233
|
+
none: false
|
234
|
+
requirements:
|
235
|
+
- - ! '>='
|
236
|
+
- !ruby/object:Gem::Version
|
237
|
+
version: '0'
|
190
238
|
description: common vcap classes/methods
|
191
239
|
email:
|
192
240
|
- derek.collison@gmail.com
|
@@ -194,32 +242,30 @@ executables: []
|
|
194
242
|
extensions: []
|
195
243
|
extra_rdoc_files: []
|
196
244
|
files:
|
197
|
-
- lib/
|
198
|
-
- lib/
|
199
|
-
- lib/
|
200
|
-
- lib/
|
201
|
-
- lib/
|
202
|
-
- lib/
|
203
|
-
- lib/
|
204
|
-
- lib/vcap/spec/forked_component/nats_server.rb
|
205
|
-
- lib/vcap/priority_queue.rb
|
245
|
+
- lib/json_message.rb
|
246
|
+
- lib/services/api/async_requests.rb
|
247
|
+
- lib/services/api/clients/service_gateway_client.rb
|
248
|
+
- lib/services/api/const.rb
|
249
|
+
- lib/services/api/messages.rb
|
250
|
+
- lib/services/api/util.rb
|
251
|
+
- lib/services/api.rb
|
206
252
|
- lib/vcap/common.rb
|
253
|
+
- lib/vcap/component.rb
|
254
|
+
- lib/vcap/config.rb
|
207
255
|
- lib/vcap/fiber_tracing.rb
|
208
|
-
- lib/vcap/
|
209
|
-
- lib/vcap/quota.rb
|
256
|
+
- lib/vcap/priority_queue.rb
|
210
257
|
- lib/vcap/process_utils.rb
|
258
|
+
- lib/vcap/quota.rb
|
211
259
|
- lib/vcap/rolling_metric.rb
|
212
|
-
- lib/vcap/config.rb
|
213
260
|
- lib/vcap/sorted_set_utils.rb
|
214
|
-
- lib/
|
215
|
-
- lib/
|
216
|
-
- lib/
|
217
|
-
- lib/
|
218
|
-
- lib/
|
219
|
-
- lib/
|
220
|
-
- lib/
|
221
|
-
- lib/
|
222
|
-
- lib/services/api/clients/service_gateway_client.rb
|
261
|
+
- lib/vcap/spec/em.rb
|
262
|
+
- lib/vcap/spec/forked_component/base.rb
|
263
|
+
- lib/vcap/spec/forked_component/nats_server.rb
|
264
|
+
- lib/vcap/spec/forked_component.rb
|
265
|
+
- lib/vcap/subprocess.rb
|
266
|
+
- lib/vcap/user_pools/user_ops.rb
|
267
|
+
- lib/vcap/user_pools/user_pool.rb
|
268
|
+
- lib/vcap/user_pools/user_pool_util.rb
|
223
269
|
homepage: http://github.com/vmware-ac/core
|
224
270
|
licenses: []
|
225
271
|
post_install_message:
|
@@ -240,7 +286,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
240
286
|
version: '0'
|
241
287
|
requirements: []
|
242
288
|
rubyforge_project:
|
243
|
-
rubygems_version: 1.8.
|
289
|
+
rubygems_version: 1.8.23
|
244
290
|
signing_key:
|
245
291
|
specification_version: 3
|
246
292
|
summary: vcap common
|
@@ -1,84 +0,0 @@
|
|
1
|
-
# Copyright (c) 2009-2012 VMware, Inc.
|
2
|
-
require 'net/http'
|
3
|
-
require 'net/http/post/multipart'
|
4
|
-
require 'mime/types'
|
5
|
-
require 'uri'
|
6
|
-
|
7
|
-
require 'services/api/const'
|
8
|
-
require 'services/api/messages'
|
9
|
-
require 'services/api/multipart'
|
10
|
-
|
11
|
-
module VCAP
|
12
|
-
module Services
|
13
|
-
module Api
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
module VCAP::Services::Api
|
19
|
-
class SDSClient
|
20
|
-
|
21
|
-
class SDSErrorResponse < StandardError; end
|
22
|
-
class UnexpectedResponse < StandardError; end
|
23
|
-
|
24
|
-
def initialize(url, upload_token, timeout=60, opts={})
|
25
|
-
@url = url
|
26
|
-
@timeout = timeout
|
27
|
-
@hdrs = {
|
28
|
-
'Content-Type' => 'application/json',
|
29
|
-
}
|
30
|
-
@upload_hdrs = {
|
31
|
-
'Content-Type' => 'multipart/form-data',
|
32
|
-
SDS_UPLOAD_TOKEN_HEADER => upload_token
|
33
|
-
}
|
34
|
-
end
|
35
|
-
|
36
|
-
def import_from_data(args)
|
37
|
-
resp = perform_multipart_upload("/serialized/#{args[:service]}/#{args[:service_id]}/serialized/data", args[:msg])
|
38
|
-
SerializedURL.decode(resp)
|
39
|
-
end
|
40
|
-
|
41
|
-
protected
|
42
|
-
|
43
|
-
def perform_multipart_upload(path, file_path)
|
44
|
-
# upload file using multipart/form data
|
45
|
-
result = nil
|
46
|
-
uri = URI.parse(@url)
|
47
|
-
|
48
|
-
mime_types = MIME::Types.type_for(file_path) || []
|
49
|
-
mime_types << "application/octet-stream" if mime_types.empty?
|
50
|
-
|
51
|
-
if EM.reactor_running?
|
52
|
-
payload = {:_method => 'put', :data_file => EM::StreamUploadIO.new(file_path, mime_types[0])}
|
53
|
-
multipart = EM::Multipart.new(payload, @upload_hdrs)
|
54
|
-
url = URI.parse(uri.to_s + path)
|
55
|
-
http = AsyncHttpMultiPartUpload.fibered(url, @timeout, multipart)
|
56
|
-
raise UnexpectedResponse, "Error uploading #{file_path} to serialized_data_server #{@url}: #{http.error}" unless http.error.empty?
|
57
|
-
code = http.response_header.status.to_i
|
58
|
-
body = http.response
|
59
|
-
else
|
60
|
-
payload = {:_method => 'put', :data_file => UploadIO.new(file_path, mime_types[0])}
|
61
|
-
req = Net::HTTP::Post::Multipart.new(path, payload, @upload_hdrs)
|
62
|
-
resp = Net::HTTP.new(uri.host, uri.port).start do |http|
|
63
|
-
http.request(req)
|
64
|
-
end
|
65
|
-
code = resp.code.to_i
|
66
|
-
body = resp.body
|
67
|
-
end
|
68
|
-
case code
|
69
|
-
when 200
|
70
|
-
body
|
71
|
-
when 400
|
72
|
-
raise SDSErrorResponse, "Fail to upload the file to serialization_data_server."
|
73
|
-
when 403
|
74
|
-
raise SDSErrorResponse, "You are forbidden to access serialization_data_server."
|
75
|
-
when 404
|
76
|
-
raise SDSErrorResponse, "Not found in serialization_data_server."
|
77
|
-
when 501
|
78
|
-
raise SDSErrorResponse, "Serialized data file is recognized, but file not found in serialization_data_server."
|
79
|
-
else
|
80
|
-
raise UnexpectedResponse, "Unexpected exception in serialization_data_server: #{(uri.to_s + path)} #{code} #{body}"
|
81
|
-
end
|
82
|
-
end
|
83
|
-
end
|
84
|
-
end
|
@@ -1,191 +0,0 @@
|
|
1
|
-
# Copyright (c) 2009-2011 VMware, Inc.
|
2
|
-
require 'eventmachine'
|
3
|
-
require 'em-http-request'
|
4
|
-
|
5
|
-
# monkey-patch for em-http-request to support multipart file upload
|
6
|
-
|
7
|
-
module EventMachine
|
8
|
-
class StreamUploadIO
|
9
|
-
attr_reader :args, :filename, :basename, :size, :content_type
|
10
|
-
def initialize(filename, content_type, args={})
|
11
|
-
# disable http chunking
|
12
|
-
@args = args.merge({:http_chunks => false})
|
13
|
-
@filename = filename
|
14
|
-
# FIXME how to catch exception and log it
|
15
|
-
begin
|
16
|
-
@basename = File.basename(filename)
|
17
|
-
@size = File.size(filename)
|
18
|
-
rescue => e
|
19
|
-
# size == 0, the part will be injected
|
20
|
-
@size = 0
|
21
|
-
end
|
22
|
-
@content_type = content_type
|
23
|
-
end
|
24
|
-
|
25
|
-
def add_extra_size(extra_size)
|
26
|
-
@size += extra_size
|
27
|
-
end
|
28
|
-
|
29
|
-
def length
|
30
|
-
@size
|
31
|
-
end
|
32
|
-
|
33
|
-
def stream_file_data
|
34
|
-
true
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
module Part
|
39
|
-
def self.create(boundary, k, v)
|
40
|
-
if v.respond_to?(:stream_file_data)
|
41
|
-
FilePart.new(boundary, k, v)
|
42
|
-
else
|
43
|
-
ParamPart.new(boundary, k, v)
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
def to_io
|
48
|
-
@io
|
49
|
-
end
|
50
|
-
|
51
|
-
def length
|
52
|
-
@io.size
|
53
|
-
end
|
54
|
-
|
55
|
-
def send_part(conn, parts, idx)
|
56
|
-
end
|
57
|
-
|
58
|
-
def get_next_part(parts, idx)
|
59
|
-
next_idx = idx.to_i + 1
|
60
|
-
if parts && next_idx < parts.size && next_idx >=0
|
61
|
-
next_part = parts[next_idx]
|
62
|
-
else
|
63
|
-
nil
|
64
|
-
end
|
65
|
-
next_part
|
66
|
-
end
|
67
|
-
|
68
|
-
def send_next_part(conn, parts, idx)
|
69
|
-
next_part = get_next_part(parts, idx)
|
70
|
-
next_part.send_part(conn, parts, idx+1) if next_part
|
71
|
-
end
|
72
|
-
|
73
|
-
end
|
74
|
-
|
75
|
-
class ParamPart
|
76
|
-
include Part
|
77
|
-
def initialize(boundary, name, value)
|
78
|
-
@boundary = boundary
|
79
|
-
@name = name
|
80
|
-
part = ''
|
81
|
-
part << "--#{@boundary}\r\n"
|
82
|
-
part << "Content-Disposition: form-data; name=\"#{@name.to_s}\"\r\n"
|
83
|
-
part << "\r\n"
|
84
|
-
part << "#{value.to_s}\r\n"
|
85
|
-
@io = StringIO.new(part)
|
86
|
-
end
|
87
|
-
|
88
|
-
def send_part(conn, parts, idx)
|
89
|
-
conn.send_data @io.string if conn
|
90
|
-
send_next_part(conn, parts, idx)
|
91
|
-
end
|
92
|
-
end
|
93
|
-
|
94
|
-
class EpiloguePart
|
95
|
-
include Part
|
96
|
-
def initialize(boundary)
|
97
|
-
@io = StringIO.new("--#{boundary}--\r\n") #\r\n or \r\n\r\n
|
98
|
-
end
|
99
|
-
|
100
|
-
def send_part(conn, parts, idx)
|
101
|
-
conn.send_data @io.string if conn
|
102
|
-
# this part should be the last part
|
103
|
-
end
|
104
|
-
end
|
105
|
-
|
106
|
-
class FilePart
|
107
|
-
include Part
|
108
|
-
def initialize(boundary, name, upload_io)
|
109
|
-
@boundary = boundary
|
110
|
-
@name = name
|
111
|
-
@io = upload_io
|
112
|
-
@part = ''
|
113
|
-
@part << "--#{boundary}\r\n"
|
114
|
-
@part << "Content-Disposition: form-data; name=\"#{name.to_s}\"; filename=\"#{@io.filename}\"\r\n"
|
115
|
-
@part << "Content-Length: #{@io.size}\r\n"
|
116
|
-
@part << "Content-Type: #{@io.content_type}\r\n"
|
117
|
-
@part << "Content-Transfer-Encoding: binary\r\n"
|
118
|
-
@part << "\r\n"
|
119
|
-
@end_part ="\r\n"
|
120
|
-
@io.add_extra_size(@part.size + @end_part.size)
|
121
|
-
end
|
122
|
-
|
123
|
-
def send_part(conn, parts, idx)
|
124
|
-
conn.send_data @part
|
125
|
-
streamer = EM::FileStreamer.new(conn, @io.filename, @io.args)
|
126
|
-
streamer.callback {
|
127
|
-
conn.send_data @end_part
|
128
|
-
send_next_part(conn, parts, idx)
|
129
|
-
}
|
130
|
-
end
|
131
|
-
end
|
132
|
-
|
133
|
-
class Multipart
|
134
|
-
DEFAULT_BOUNDARY = "-----------RubyEMMultiPartPost"
|
135
|
-
attr_reader :parts, :ps, :content_type, :content_length, :boundary, :headers
|
136
|
-
def initialize(params, headers={}, boundary=DEFAULT_BOUNDARY)
|
137
|
-
@parts = params.map{ |k,v| Part.create(boundary, k, v) }
|
138
|
-
@parts << EpiloguePart.new(boundary)
|
139
|
-
# inject the part with length = 0
|
140
|
-
@ps = @parts.select{ |part| part.length > 0 }
|
141
|
-
@content_type = "multipart/form-data; boundary=#{boundary}"
|
142
|
-
@content_length = 0
|
143
|
-
@parts.each do |part|
|
144
|
-
@content_length += part.length
|
145
|
-
end
|
146
|
-
@boundary = boundary
|
147
|
-
@headers = headers
|
148
|
-
end
|
149
|
-
|
150
|
-
def send_body(conn)
|
151
|
-
if conn && conn.error.nil? && @parts.size > 0
|
152
|
-
part = @parts.first
|
153
|
-
part.send_part(conn, @parts, 0)
|
154
|
-
end
|
155
|
-
end
|
156
|
-
end
|
157
|
-
end
|
158
|
-
|
159
|
-
## Support to streaming the file when sending body
|
160
|
-
## TODO FIXME this patch whether depends on specified version???
|
161
|
-
module EventMachine
|
162
|
-
class HttpClient
|
163
|
-
alias_method :original_send_request, :send_request
|
164
|
-
def multipart_request?
|
165
|
-
(@req.method == 'POST' or @req.method == 'PUT') and @options[:multipart]
|
166
|
-
end
|
167
|
-
|
168
|
-
def send_request(head, body)
|
169
|
-
unless multipart_request?
|
170
|
-
original_send_request(head, body)
|
171
|
-
else
|
172
|
-
body = normalize_body(body)
|
173
|
-
multipart = @options[:multipart]
|
174
|
-
query = @options[:query]
|
175
|
-
|
176
|
-
head['content-length'] = multipart.content_length
|
177
|
-
head['content-type'] = multipart.content_type
|
178
|
-
extra_headers = {}
|
179
|
-
extra_headers = multipart.headers.reject { |k, v| %w(content-length content-type).include?(k.to_s.downcase) }
|
180
|
-
head.merge! extra_headers
|
181
|
-
|
182
|
-
request_header ||= encode_request(@req.method, @req.uri, query, @conn.opts.proxy)
|
183
|
-
request_header << encode_headers(head)
|
184
|
-
request_header << CRLF
|
185
|
-
@conn.send_data request_header
|
186
|
-
|
187
|
-
multipart.send_body(@conn)
|
188
|
-
end
|
189
|
-
end
|
190
|
-
end
|
191
|
-
end
|