new_cfoundry 4.8.2

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.
Files changed (269) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +8 -0
  3. data/.gitmodules +3 -0
  4. data/.rspec +2 -0
  5. data/.ruby-gemset +1 -0
  6. data/.ruby-version +1 -0
  7. data/.travis.yml +11 -0
  8. data/Checkfile +2 -0
  9. data/Gemfile +3 -0
  10. data/LICENSE +987 -0
  11. data/NOTICE +10 -0
  12. data/README.md +9 -0
  13. data/Rakefile +14 -0
  14. data/VERSION +1 -0
  15. data/cfoundry.gemspec +40 -0
  16. data/config/locales/en.yml +168 -0
  17. data/lib/cc_api_stub/app_usage_events.rb +11 -0
  18. data/lib/cc_api_stub/applications.rb +57 -0
  19. data/lib/cc_api_stub/domains.rb +32 -0
  20. data/lib/cc_api_stub/events.rb +11 -0
  21. data/lib/cc_api_stub/frameworks.rb +22 -0
  22. data/lib/cc_api_stub/helper.rb +148 -0
  23. data/lib/cc_api_stub/login.rb +21 -0
  24. data/lib/cc_api_stub/organization_users.rb +27 -0
  25. data/lib/cc_api_stub/organizations.rb +82 -0
  26. data/lib/cc_api_stub/routes.rb +26 -0
  27. data/lib/cc_api_stub/runtimes.rb +22 -0
  28. data/lib/cc_api_stub/service_bindings.rb +22 -0
  29. data/lib/cc_api_stub/service_instances.rb +22 -0
  30. data/lib/cc_api_stub/services.rb +21 -0
  31. data/lib/cc_api_stub/space_users.rb +27 -0
  32. data/lib/cc_api_stub/spaces.rb +58 -0
  33. data/lib/cc_api_stub/users.rb +85 -0
  34. data/lib/cc_api_stub.rb +20 -0
  35. data/lib/cfoundry/auth_token.rb +63 -0
  36. data/lib/cfoundry/baseclient.rb +216 -0
  37. data/lib/cfoundry/chatty_hash.rb +46 -0
  38. data/lib/cfoundry/client.rb +42 -0
  39. data/lib/cfoundry/concerns/login_helpers.rb +14 -0
  40. data/lib/cfoundry/concerns/proxy_options.rb +17 -0
  41. data/lib/cfoundry/errors.rb +163 -0
  42. data/lib/cfoundry/rest_client.rb +317 -0
  43. data/lib/cfoundry/test_support.rb +3 -0
  44. data/lib/cfoundry/trace_helpers.rb +64 -0
  45. data/lib/cfoundry/uaaclient.rb +160 -0
  46. data/lib/cfoundry/upload_helpers.rb +222 -0
  47. data/lib/cfoundry/v2/app.rb +303 -0
  48. data/lib/cfoundry/v2/app_event.rb +13 -0
  49. data/lib/cfoundry/v2/app_instance.rb +74 -0
  50. data/lib/cfoundry/v2/app_usage_event.rb +14 -0
  51. data/lib/cfoundry/v2/base.rb +108 -0
  52. data/lib/cfoundry/v2/client.rb +104 -0
  53. data/lib/cfoundry/v2/domain.rb +20 -0
  54. data/lib/cfoundry/v2/event.rb +17 -0
  55. data/lib/cfoundry/v2/helper.rb +11 -0
  56. data/lib/cfoundry/v2/managed_service_instance.rb +12 -0
  57. data/lib/cfoundry/v2/model.rb +211 -0
  58. data/lib/cfoundry/v2/model_magic/attribute.rb +49 -0
  59. data/lib/cfoundry/v2/model_magic/client_extensions.rb +170 -0
  60. data/lib/cfoundry/v2/model_magic/has_summary.rb +49 -0
  61. data/lib/cfoundry/v2/model_magic/query_multi_value_helper.rb +21 -0
  62. data/lib/cfoundry/v2/model_magic/query_value_helper.rb +33 -0
  63. data/lib/cfoundry/v2/model_magic/queryable_by.rb +39 -0
  64. data/lib/cfoundry/v2/model_magic/to_many.rb +138 -0
  65. data/lib/cfoundry/v2/model_magic/to_one.rb +81 -0
  66. data/lib/cfoundry/v2/model_magic.rb +134 -0
  67. data/lib/cfoundry/v2/organization.rb +44 -0
  68. data/lib/cfoundry/v2/quota_definition.rb +12 -0
  69. data/lib/cfoundry/v2/route.rb +25 -0
  70. data/lib/cfoundry/v2/service.rb +22 -0
  71. data/lib/cfoundry/v2/service_auth_token.rb +9 -0
  72. data/lib/cfoundry/v2/service_binding.rb +10 -0
  73. data/lib/cfoundry/v2/service_broker.rb +12 -0
  74. data/lib/cfoundry/v2/service_instance.rb +14 -0
  75. data/lib/cfoundry/v2/service_plan.rb +16 -0
  76. data/lib/cfoundry/v2/space.rb +30 -0
  77. data/lib/cfoundry/v2/stack.rb +10 -0
  78. data/lib/cfoundry/v2/user.rb +93 -0
  79. data/lib/cfoundry/v2/user_provided_service_instance.rb +15 -0
  80. data/lib/cfoundry/validator.rb +41 -0
  81. data/lib/cfoundry/version.rb +4 -0
  82. data/lib/cfoundry/zip.rb +56 -0
  83. data/lib/cfoundry.rb +5 -0
  84. data/release_notes/release_1_5_3.md +177 -0
  85. data/release_notes/release_2.3.1.md +14 -0
  86. data/release_notes/release_2.3.3.md +38 -0
  87. data/release_notes/release_2.3.4.md +16 -0
  88. data/release_notes/release_2.3.5.md +14 -0
  89. data/release_notes/release_2.3.6.md +14 -0
  90. data/release_notes/release_2.3.6.rc1.md +29 -0
  91. data/release_notes/release_2.3.6.rc2.md +19 -0
  92. data/release_notes/release_2.3.7.rc1.md +14 -0
  93. data/release_notes/release_2.4.0.md +41 -0
  94. data/release_notes/release_2.4.1.rc1.md +58 -0
  95. data/release_notes/release_3.0.0.md +21 -0
  96. data/release_notes/release_3.0.1.md +16 -0
  97. data/release_notes/release_3.0.2.rc1.md +19 -0
  98. data/release_notes/release_4.0.0.md +19 -0
  99. data/release_notes/release_4.0.1.md +14 -0
  100. data/release_notes/release_4.0.2.rc1.md +14 -0
  101. data/release_notes/release_4.0.2.rc2.md +11 -0
  102. data/release_notes/release_4.0.2.rc3.md +11 -0
  103. data/release_notes/release_4.0.2.rc4.md +19 -0
  104. data/release_notes/release_4.0.2.rc5.md +14 -0
  105. data/release_notes/release_4.0.3.md +19 -0
  106. data/release_notes/release_4.0.4.rc1.md +14 -0
  107. data/release_notes/release_4.0.4.rc2.md +14 -0
  108. data/release_notes/release_4.1.0.md +19 -0
  109. data/release_notes/release_4.2.0.rc.md +24 -0
  110. data/release_notes/release_4.3.0.md +22 -0
  111. data/release_notes/release_4.3.1.md +14 -0
  112. data/release_notes/release_4.3.10.md +14 -0
  113. data/release_notes/release_4.3.11.md +14 -0
  114. data/release_notes/release_4.3.12.md +14 -0
  115. data/release_notes/release_4.3.2.rc1.md +21 -0
  116. data/release_notes/release_4.3.3.md +24 -0
  117. data/release_notes/release_4.3.4.md +11 -0
  118. data/release_notes/release_4.3.4.rc1.md +24 -0
  119. data/release_notes/release_4.3.5.md +36 -0
  120. data/release_notes/release_4.3.5.rc1.md +21 -0
  121. data/release_notes/release_4.3.6.md +14 -0
  122. data/release_notes/release_4.3.7.md +50 -0
  123. data/release_notes/release_4.3.8.md +19 -0
  124. data/release_notes/release_4.3.9.md +14 -0
  125. data/release_notes/release_4.4.0.md +16 -0
  126. data/release_notes/release_4.5.1.md +11 -0
  127. data/release_notes/release_4.5.2.md +14 -0
  128. data/release_notes/release_4.5.3.md +29 -0
  129. data/release_notes/release_4.6.0.md +24 -0
  130. data/release_notes/release_4.6.1.md +14 -0
  131. data/release_notes/release_4.6.2.md +16 -0
  132. data/release_notes/release_4.6.3.rc1.md +14 -0
  133. data/release_notes/release_4.6.3.rc2.md +14 -0
  134. data/release_notes/release_4.6.3.rc3.md +17 -0
  135. data/release_notes/release_4.7.0.md +11 -0
  136. data/release_notes/release_4.7.1.md +14 -0
  137. data/release_notes/release_4.7.1.rc.1.md +14 -0
  138. data/release_notes/release_4.7.1.rc1.md +19 -0
  139. data/script/gpp +3 -0
  140. data/spec/cc_api_stub/app_usage_events_spec.rb +12 -0
  141. data/spec/cc_api_stub/applications_spec.rb +69 -0
  142. data/spec/cc_api_stub/domains_spec.rb +40 -0
  143. data/spec/cc_api_stub/events_spec.rb +12 -0
  144. data/spec/cc_api_stub/frameworks_spec.rb +19 -0
  145. data/spec/cc_api_stub/login_spec.rb +20 -0
  146. data/spec/cc_api_stub/organization_users_spec.rb +35 -0
  147. data/spec/cc_api_stub/organizations_spec.rb +118 -0
  148. data/spec/cc_api_stub/routes_spec.rb +19 -0
  149. data/spec/cc_api_stub/runtimes_spec.rb +19 -0
  150. data/spec/cc_api_stub/service_bindings_spec.rb +13 -0
  151. data/spec/cc_api_stub/service_instances_spec.rb +19 -0
  152. data/spec/cc_api_stub/services_spec.rb +11 -0
  153. data/spec/cc_api_stub/space_users_spec.rb +35 -0
  154. data/spec/cc_api_stub/spaces_spec.rb +38 -0
  155. data/spec/cc_api_stub/users_spec.rb +107 -0
  156. data/spec/cfoundry/auth_token_spec.rb +154 -0
  157. data/spec/cfoundry/baseclient_spec.rb +298 -0
  158. data/spec/cfoundry/client_spec.rb +13 -0
  159. data/spec/cfoundry/errors_spec.rb +117 -0
  160. data/spec/cfoundry/rest_client_spec.rb +349 -0
  161. data/spec/cfoundry/trace_helpers_spec.rb +91 -0
  162. data/spec/cfoundry/uaaclient_spec.rb +435 -0
  163. data/spec/cfoundry/upload_helpers_spec.rb +182 -0
  164. data/spec/cfoundry/v2/app_event_spec.rb +97 -0
  165. data/spec/cfoundry/v2/app_instance_spec.rb +31 -0
  166. data/spec/cfoundry/v2/app_spec.rb +354 -0
  167. data/spec/cfoundry/v2/app_usage_event_spec.rb +15 -0
  168. data/spec/cfoundry/v2/base_spec.rb +375 -0
  169. data/spec/cfoundry/v2/client_spec.rb +121 -0
  170. data/spec/cfoundry/v2/domain_spec.rb +63 -0
  171. data/spec/cfoundry/v2/event_spec.rb +15 -0
  172. data/spec/cfoundry/v2/managed_service_instance_spec.rb +149 -0
  173. data/spec/cfoundry/v2/model_magic/attribute_spec.rb +123 -0
  174. data/spec/cfoundry/v2/model_magic/has_summary_spec.rb +17 -0
  175. data/spec/cfoundry/v2/model_magic/to_many_spec.rb +53 -0
  176. data/spec/cfoundry/v2/model_magic/to_one_spec.rb +106 -0
  177. data/spec/cfoundry/v2/model_magic_spec.rb +43 -0
  178. data/spec/cfoundry/v2/model_spec.rb +434 -0
  179. data/spec/cfoundry/v2/organization_spec.rb +135 -0
  180. data/spec/cfoundry/v2/quota_definition_spec.rb +50 -0
  181. data/spec/cfoundry/v2/route_spec.rb +42 -0
  182. data/spec/cfoundry/v2/service_plan_spec.rb +53 -0
  183. data/spec/cfoundry/v2/service_spec.rb +58 -0
  184. data/spec/cfoundry/v2/space_spec.rb +13 -0
  185. data/spec/cfoundry/v2/user_provided_service_instance_spec.rb +57 -0
  186. data/spec/cfoundry/v2/user_spec.rb +206 -0
  187. data/spec/cfoundry/validator_spec.rb +94 -0
  188. data/spec/factories/app_events_factory.rb +7 -0
  189. data/spec/factories/app_usage_events_factory.rb +32 -0
  190. data/spec/factories/apps_factory.rb +11 -0
  191. data/spec/factories/clients_factory.rb +7 -0
  192. data/spec/factories/domains_factory.rb +10 -0
  193. data/spec/factories/events_factory.rb +50 -0
  194. data/spec/factories/organizations_factory.rb +12 -0
  195. data/spec/factories/quota_definitions_factory.rb +8 -0
  196. data/spec/factories/routes_factory.rb +10 -0
  197. data/spec/factories/service_instances_factory.rb +10 -0
  198. data/spec/factories/service_plans_factory.rb +10 -0
  199. data/spec/factories/services_factory.rb +10 -0
  200. data/spec/factories/spaces_factory.rb +10 -0
  201. data/spec/factories/user_provided_service_instances_factory.rb +10 -0
  202. data/spec/factories/users_factory.rb +10 -0
  203. data/spec/fixtures/apps/with_cfignore/.cfignore +4 -0
  204. data/spec/fixtures/apps/with_cfignore/.hidden_file +1 -0
  205. data/spec/fixtures/apps/with_cfignore/ambiguous_ignored +0 -0
  206. data/spec/fixtures/apps/with_cfignore/ignored_dir/file_in_ignored_dir.txt +1 -0
  207. data/spec/fixtures/apps/with_cfignore/ignored_file.txt +1 -0
  208. data/spec/fixtures/apps/with_cfignore/non_ignored_dir/file_in_non_ignored_dir.txt +1 -0
  209. data/spec/fixtures/apps/with_cfignore/non_ignored_dir/ignored_file.txt +1 -0
  210. data/spec/fixtures/apps/with_cfignore/non_ignored_dir/toplevel_ignored.txt +0 -0
  211. data/spec/fixtures/apps/with_cfignore/non_ignored_file.txt +1 -0
  212. data/spec/fixtures/apps/with_cfignore/toplevel_ignored.txt +0 -0
  213. data/spec/fixtures/apps/with_dotfiles/.dotfile +1 -0
  214. data/spec/fixtures/apps/with_dotfiles/xyz +1 -0
  215. data/spec/fixtures/apps/with_external_symlink/foo +1 -0
  216. data/spec/fixtures/apps/with_ignored_external_symlink/.cfignore +1 -0
  217. data/spec/fixtures/apps/with_ignored_external_symlink/foo +1 -0
  218. data/spec/fixtures/apps/with_nested_directories/foo/bar/baz/fizz +0 -0
  219. data/spec/fixtures/apps/with_nested_directories/xyz +0 -0
  220. data/spec/fixtures/empty_file +0 -0
  221. data/spec/fixtures/fake_cc_app_usage_events.json +152 -0
  222. data/spec/fixtures/fake_cc_application.json +20 -0
  223. data/spec/fixtures/fake_cc_application_summary.json +56 -0
  224. data/spec/fixtures/fake_cc_created_application.json +11 -0
  225. data/spec/fixtures/fake_cc_created_domain.json +15 -0
  226. data/spec/fixtures/fake_cc_created_organization.json +11 -0
  227. data/spec/fixtures/fake_cc_created_route.json +13 -0
  228. data/spec/fixtures/fake_cc_created_service_instance.json +11 -0
  229. data/spec/fixtures/fake_cc_created_space.json +11 -0
  230. data/spec/fixtures/fake_cc_created_user.json +11 -0
  231. data/spec/fixtures/fake_cc_domain.json +55 -0
  232. data/spec/fixtures/fake_cc_domain_spaces.json +27 -0
  233. data/spec/fixtures/fake_cc_empty_search.json +7 -0
  234. data/spec/fixtures/fake_cc_events.json +419 -0
  235. data/spec/fixtures/fake_cc_frameworks.json +20 -0
  236. data/spec/fixtures/fake_cc_managed_service_instance.json +83 -0
  237. data/spec/fixtures/fake_cc_organization.json +161 -0
  238. data/spec/fixtures/fake_cc_organization_domains.json +59 -0
  239. data/spec/fixtures/fake_cc_organization_search.json +37 -0
  240. data/spec/fixtures/fake_cc_organization_spaces.json +99 -0
  241. data/spec/fixtures/fake_cc_organization_summary.json +20 -0
  242. data/spec/fixtures/fake_cc_organization_users.json +81 -0
  243. data/spec/fixtures/fake_cc_route.json +16 -0
  244. data/spec/fixtures/fake_cc_runtimes.json +20 -0
  245. data/spec/fixtures/fake_cc_service_binding.json +22 -0
  246. data/spec/fixtures/fake_cc_service_bindings.json +24 -0
  247. data/spec/fixtures/fake_cc_service_instance.json +83 -0
  248. data/spec/fixtures/fake_cc_service_instances.json +72 -0
  249. data/spec/fixtures/fake_cc_services.json +160 -0
  250. data/spec/fixtures/fake_cc_space.json +45 -0
  251. data/spec/fixtures/fake_cc_space_apps.json +49 -0
  252. data/spec/fixtures/fake_cc_space_summary.json +84 -0
  253. data/spec/fixtures/fake_cc_spaces.json +92 -0
  254. data/spec/fixtures/fake_cc_stats.json +29 -0
  255. data/spec/fixtures/fake_cc_user.json +139 -0
  256. data/spec/fixtures/fake_cc_user_organizations.json +92 -0
  257. data/spec/fixtures/fake_cc_user_provided_service_instance.json +51 -0
  258. data/spec/fixtures/fake_cc_user_with_managers.json +85 -0
  259. data/spec/integration/client_spec.rb +38 -0
  260. data/spec/spec_helper.rb +22 -0
  261. data/spec/support/factory_girl.rb +6 -0
  262. data/spec/support/shared_examples/cc_api_stub_request_examples.rb +79 -0
  263. data/spec/support/shared_examples/client_login_examples.rb +46 -0
  264. data/spec/support/shared_examples/model_summary_examples.rb +34 -0
  265. data/spec/support/test_model_builder.rb +10 -0
  266. data/vendor/errors/README.md +4 -0
  267. data/vendor/errors/v1.yml +189 -0
  268. data/vendor/errors/v2.yml +384 -0
  269. metadata +693 -0
@@ -0,0 +1,17 @@
1
+ module CFoundry
2
+ module ProxyOptions
3
+ def proxy_options_for(uri)
4
+ ssl = uri.is_a?(URI::HTTPS)
5
+ proxy_to_use = (ssl ? https_proxy : http_proxy)
6
+
7
+ if proxy_to_use.blank?
8
+ []
9
+ else
10
+ proxy_to_use = "proto://#{proxy_to_use}" unless proxy_to_use =~ /:\/\//
11
+ proxy_uri = URI.parse(proxy_to_use)
12
+ proxy_user, proxy_password = proxy_uri.userinfo.split(/:/) if proxy_uri.userinfo
13
+ [proxy_uri.host, proxy_uri.port, proxy_user, proxy_password]
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,163 @@
1
+ require "net/https"
2
+ require "multi_json"
3
+ require "yaml"
4
+
5
+ module CFoundry
6
+ # Base class for CFoundry errors (not from the server).
7
+ class Error < RuntimeError
8
+ end
9
+
10
+ class Deprecated < Error
11
+ end
12
+
13
+ class Mismatch < Error
14
+ def initialize(expected, got)
15
+ @expected = expected
16
+ @got = got
17
+ end
18
+
19
+ def to_s
20
+ "Invalid value type; expected #{@expected.inspect}, got #{@got.inspect}"
21
+ end
22
+ end
23
+
24
+ class InvalidTarget < Error
25
+ attr_reader :target
26
+
27
+ def initialize(target)
28
+ @target = target
29
+ end
30
+
31
+ def to_s
32
+ "Invalid target URI: #{@target}"
33
+ end
34
+ end
35
+
36
+ class TargetRefused < Error
37
+ # Error message.
38
+ attr_reader :message
39
+
40
+ # Message varies as this represents various network errors.
41
+ def initialize(message)
42
+ @message = message
43
+ end
44
+
45
+ # Exception message.
46
+ def to_s
47
+ "target refused connection (#@message)"
48
+ end
49
+ end
50
+
51
+ class Timeout < Timeout::Error
52
+ attr_reader :method, :uri, :parent
53
+
54
+ def initialize(method, uri, parent = nil)
55
+ @method = method
56
+ @uri = uri
57
+ @parent = parent
58
+ super(to_s)
59
+ end
60
+
61
+ def to_s
62
+ "#{method} #{uri} timed out"
63
+ end
64
+ end
65
+
66
+ # Exception representing errors returned by the API.
67
+ class APIError < RuntimeError
68
+ include TraceHelpers
69
+
70
+ class << self
71
+ def error_classes
72
+ @error_classes ||= {}
73
+ end
74
+ end
75
+
76
+ attr_reader :error_code, :description, :request, :response
77
+
78
+ # Create an APIError with a given request and response.
79
+ def initialize(description = nil, error_code = nil, request = nil, response = nil)
80
+ @response = response
81
+ @request = request
82
+ @error_code = error_code || (response ? response[:status] : nil)
83
+ @description = description || parse_description
84
+ end
85
+
86
+ # Exception message.
87
+ def to_s
88
+ "#{error_code}: #{description}"
89
+ end
90
+
91
+ def request_trace
92
+ super(request)
93
+ end
94
+
95
+ def response_trace
96
+ super(response)
97
+ end
98
+
99
+ private
100
+
101
+ def parse_description
102
+ return unless response
103
+
104
+ parse_json(response[:body])[:description]
105
+ rescue MultiJson::DecodeError
106
+ response[:body]
107
+ end
108
+
109
+ def parse_json(x)
110
+ if x.empty?
111
+ raise MultiJson::DecodeError.new("Empty JSON string", [], "")
112
+ else
113
+ MultiJson.load(x, :symbolize_keys => true)
114
+ end
115
+ end
116
+ end
117
+
118
+ class NotFound < APIError
119
+ end
120
+
121
+ class Denied < APIError
122
+ end
123
+
124
+ class Unauthorized < APIError
125
+ end
126
+
127
+ class BadResponse < APIError
128
+ end
129
+
130
+ class UAAError < APIError
131
+ end
132
+
133
+ def self.define_error(class_name, code)
134
+ base =
135
+ case class_name
136
+ when /NotFound$/
137
+ NotFound
138
+ else
139
+ APIError
140
+ end
141
+
142
+ klass =
143
+ if const_defined?(class_name)
144
+ const_get(class_name)
145
+ else
146
+ Class.new(base)
147
+ end
148
+
149
+ APIError.error_classes[code] = klass
150
+
151
+ unless const_defined?(class_name)
152
+ const_set(class_name, klass)
153
+ end
154
+ end
155
+
156
+ VENDOR_DIR = File.expand_path("../../../vendor", __FILE__)
157
+
158
+ %w{errors/v1.yml errors/v2.yml}.each do |errors|
159
+ YAML.load_file("#{VENDOR_DIR}/#{errors}").each do |code, meta|
160
+ define_error(meta["name"], code)
161
+ end
162
+ end
163
+ end
@@ -0,0 +1,317 @@
1
+ require "cfoundry/trace_helpers"
2
+ require "net/https"
3
+ require "net/http/post/multipart"
4
+ require "multi_json"
5
+ require "fileutils"
6
+
7
+ module CFoundry
8
+ class RestClient
9
+ class HTTPFactory
10
+ def self.create(uri, proxy_options = [])
11
+ http = Net::HTTP.new(uri.host, uri.port, *proxy_options)
12
+
13
+ if uri.is_a?(URI::HTTPS)
14
+ http.use_ssl = true
15
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
16
+ end
17
+
18
+ return http
19
+ end
20
+ end
21
+
22
+ include CFoundry::TraceHelpers
23
+ include CFoundry::ProxyOptions
24
+
25
+ LOG_LENGTH = 10
26
+
27
+ HTTP_METHODS = {
28
+ "GET" => Net::HTTP::Get,
29
+ "PUT" => Net::HTTP::Put,
30
+ "POST" => Net::HTTP::Post,
31
+ "DELETE" => Net::HTTP::Delete,
32
+ "HEAD" => Net::HTTP::Head,
33
+ }
34
+
35
+ DEFAULT_OPTIONS = {
36
+ :follow_redirects => true
37
+ }
38
+
39
+ attr_reader :target
40
+
41
+ attr_accessor :trace, :backtrace, :log,
42
+ :request_id, :token, :http_proxy, :https_proxy
43
+
44
+ def initialize(target, token = nil)
45
+ @target = target
46
+ @token = token
47
+ @trace = false
48
+ @backtrace = false
49
+ @log = false
50
+ end
51
+
52
+ def target=(target)
53
+ return if target == @target
54
+
55
+ @target = target
56
+ @token = nil
57
+ end
58
+
59
+ def request(method, path, options = {})
60
+ request_uri(method, construct_url(path), DEFAULT_OPTIONS.merge(options))
61
+ end
62
+
63
+ def generate_headers(payload, options)
64
+ headers = {}
65
+
66
+ if payload.is_a?(String)
67
+ headers["Content-Length"] = payload.size
68
+ elsif !payload
69
+ headers["Content-Length"] = 0
70
+ end
71
+
72
+ headers["X-Request-Id"] = @request_id if @request_id
73
+ headers["Authorization"] = @token.auth_header if @token
74
+
75
+ if accept_type = mimetype(options[:accept])
76
+ headers["Accept"] = accept_type
77
+ end
78
+
79
+ if content_type = mimetype(options[:content])
80
+ headers["Content-Type"] = content_type
81
+ end
82
+
83
+ headers.merge!(options[:headers]) if options[:headers]
84
+ headers
85
+ end
86
+
87
+ private
88
+
89
+ def request_uri(method, uri, options = {})
90
+ uri = URI.parse(uri)
91
+
92
+ unless uri.is_a?(URI::HTTP)
93
+ raise InvalidTarget.new(@target)
94
+ end
95
+
96
+ # keep original options in case there's a redirect to follow
97
+ original_options = options.dup
98
+ payload = options[:payload]
99
+
100
+ if options[:params]
101
+ encoded_params = encode_params(options[:params])
102
+ if encoded_params.respond_to?(:empty?) ? !encoded_params.empty? : encoded_params
103
+ if uri.query
104
+ uri.query += "&" + encoded_params
105
+ else
106
+ uri.query = encoded_params
107
+ end
108
+ end
109
+ end
110
+
111
+ unless payload.is_a?(String)
112
+ case options[:content]
113
+ when :json
114
+ payload = MultiJson.dump(payload)
115
+ when :form
116
+ payload = encode_params(payload)
117
+ end
118
+ end
119
+
120
+ method_class = get_method_class(method)
121
+ if payload.is_a?(Hash)
122
+ multipart = method_class.const_get(:Multipart)
123
+ request = multipart.new(uri.request_uri, payload)
124
+ else
125
+ request = method_class.new(uri.request_uri)
126
+ request.body = payload if payload
127
+ end
128
+
129
+ headers = generate_headers(payload, options)
130
+
131
+ request_hash = {
132
+ :url => uri.to_s,
133
+ :method => method,
134
+ :headers => headers,
135
+ :body => payload
136
+ }
137
+
138
+ print_request(request_hash) if @trace
139
+
140
+ add_headers(request, headers)
141
+
142
+ http = HTTPFactory.create(uri, proxy_options_for(uri))
143
+
144
+ # TODO remove this when staging returns streaming responses
145
+ http.read_timeout = 300
146
+
147
+ before = Time.now
148
+ http.start do
149
+ response = http.request(request)
150
+ time = Time.now - before
151
+
152
+ response_hash = {
153
+ :headers => sane_headers(response),
154
+ :status => response.code,
155
+ :body => response.body
156
+ }
157
+
158
+ print_response(response_hash) if @trace
159
+ print_backtrace(caller) if @trace
160
+
161
+ log_request(time, request, response)
162
+
163
+ if response.is_a?(Net::HTTPRedirection) && options[:follow_redirects]
164
+ request_uri("GET", response["location"], original_options)
165
+ else
166
+ return request_hash, response_hash
167
+ end
168
+ end
169
+ rescue ::Timeout::Error => e
170
+ raise Timeout.new(method, uri, e)
171
+ rescue SocketError, Errno::ECONNREFUSED => e
172
+ raise TargetRefused, e.message
173
+ rescue URI::InvalidURIError
174
+ raise InvalidTarget.new(@target)
175
+ end
176
+
177
+ def construct_url(path)
178
+ uri = URI.parse(path)
179
+ return path if uri.scheme
180
+
181
+ path = "/#{path}" unless path[0] == ?\/
182
+ target + path
183
+ end
184
+
185
+ def get_method_class(method_string)
186
+ HTTP_METHODS[method_string.upcase]
187
+ end
188
+
189
+ def add_headers(request, headers)
190
+ headers.each { |key, value| request[key] = value }
191
+ end
192
+
193
+ def mimetype(content)
194
+ case content
195
+ when String
196
+ content
197
+ when :json
198
+ "application/json"
199
+ when :form
200
+ "application/x-www-form-urlencoded"
201
+ when nil
202
+ nil
203
+ # return request headers (not really Accept)
204
+ else
205
+ raise CFoundry::Error, "Unknown mimetype '#{content.inspect}'"
206
+ end
207
+ end
208
+
209
+ def encode_params(hash, escape = true)
210
+ hash.keys.map do |k|
211
+ v = hash[k]
212
+ v = MultiJson.dump(v) if v.is_a?(Hash)
213
+ v = URI.escape(v.to_s, /[^#{URI::PATTERN::UNRESERVED}]/) if escape
214
+ "#{k}=#{v}"
215
+ end.join("&")
216
+ end
217
+
218
+ def log_data(time, request, response)
219
+ { :time => time,
220
+ :request => {
221
+ :method => request.method,
222
+ :url => request.path,
223
+ :headers => sane_headers(request)
224
+ },
225
+ :response => {
226
+ :code => response.code,
227
+ :headers => sane_headers(response)
228
+ }
229
+ }
230
+ end
231
+
232
+ def log_line(io, data)
233
+ io.printf(
234
+ "[%s] %0.3fs %6s -> %d %s\n",
235
+ Time.now.strftime("%F %T"),
236
+ data[:time],
237
+ data[:request][:method].to_s.upcase,
238
+ data[:response][:code],
239
+ data[:request][:url])
240
+ end
241
+
242
+ def log_request(time, request, response)
243
+ return unless @log
244
+
245
+ data = log_data(time, request, response)
246
+
247
+ case @log
248
+ when IO
249
+ log_line(@log, data)
250
+ return
251
+ when String
252
+ if File.exists?(@log)
253
+ log = File.readlines(@log).last(LOG_LENGTH - 1)
254
+ elsif !File.exists?(File.dirname(@log))
255
+ FileUtils.mkdir_p(File.dirname(@log))
256
+ end
257
+
258
+ File.open(@log, "w") do |io|
259
+ log.each { |l| io.print l } if log
260
+ log_line(io, data)
261
+ end
262
+
263
+ return
264
+ end
265
+
266
+ if @log.respond_to?(:call)
267
+ @log.call(data)
268
+ return
269
+ end
270
+
271
+ if @log.respond_to?(:<<)
272
+ @log << data
273
+ return
274
+ end
275
+ end
276
+
277
+ def print_request(request)
278
+ $stderr.puts ">>>"
279
+ $stderr.puts request_trace(request)
280
+ end
281
+
282
+ def print_response(response)
283
+ $stderr.puts response_trace(response)
284
+ $stderr.puts "<<<"
285
+ end
286
+
287
+ def print_backtrace(locs)
288
+ return unless @backtrace
289
+
290
+ interesting_locs = locs.drop_while { |loc|
291
+ loc =~ /\/(cfoundry\/|restclient\/|net\/http)/
292
+ }
293
+
294
+ $stderr.puts "--- backtrace:"
295
+
296
+ $stderr.puts "... (boring)" unless locs == interesting_locs
297
+
298
+ trimmed_locs = interesting_locs[0..5]
299
+
300
+ trimmed_locs.each do |loc|
301
+ $stderr.puts "=== #{loc}"
302
+ end
303
+
304
+ $stderr.puts "... (trimmed)" unless trimmed_locs == interesting_locs
305
+ end
306
+
307
+ def sane_headers(obj)
308
+ hds = {}
309
+
310
+ obj.each_header do |k, v|
311
+ hds[k] = v
312
+ end
313
+
314
+ hds
315
+ end
316
+ end
317
+ end
@@ -0,0 +1,3 @@
1
+ Dir[File.expand_path('../../../spec/{support}/**/*.rb', __FILE__)].each do |file|
2
+ require file unless file =~ /factory_girl/
3
+ end
@@ -0,0 +1,64 @@
1
+ require "net/https"
2
+ require "multi_json"
3
+
4
+ module CFoundry
5
+ module TraceHelpers
6
+ PROTECTED_ATTRIBUTES = ['Authorization', 'credentials']
7
+
8
+ def request_trace(request)
9
+ return nil unless request
10
+ info = ["REQUEST: #{request[:method]} #{request[:url]}"]
11
+ info << "REQUEST_HEADERS:"
12
+ info << header_trace(request[:headers])
13
+ info << "REQUEST_BODY: #{request[:body]}" if request[:body]
14
+ info.join("\n")
15
+ end
16
+
17
+
18
+ def response_trace(response)
19
+ return nil unless response
20
+ info = ["RESPONSE: [#{response[:status]}]"]
21
+ info << "RESPONSE_HEADERS:"
22
+ info << header_trace(response[:headers])
23
+ info << "RESPONSE_BODY:"
24
+ begin
25
+ parsed_body = MultiJson.load(response[:body])
26
+ filter_protected_attributes(parsed_body)
27
+ info << MultiJson.dump(parsed_body, :pretty => true)
28
+ rescue
29
+ info << "#{response[:body]}"
30
+ end
31
+ info.join("\n")
32
+ end
33
+
34
+ private
35
+
36
+ def header_trace(headers)
37
+ headers.sort.map do |key, value|
38
+ unless PROTECTED_ATTRIBUTES.include?(key)
39
+ " #{key} : #{value}"
40
+ else
41
+ " #{key} : [PRIVATE DATA HIDDEN]"
42
+ end
43
+ end
44
+ end
45
+
46
+ def filter_protected_attributes(hash_or_array)
47
+ if hash_or_array.is_a? Array
48
+ hash_or_array.each do |value|
49
+ filter_protected_attributes(value)
50
+ end
51
+ else
52
+ hash_or_array.each do |key, value|
53
+ if PROTECTED_ATTRIBUTES.include? key
54
+ hash_or_array[key] = "[PRIVATE DATA HIDDEN]"
55
+ else
56
+ if value.is_a?(Hash) || value.is_a?(Array)
57
+ filter_protected_attributes(value)
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end