shopify-cli 1.3.0 → 1.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (206) hide show
  1. checksums.yaml +4 -4
  2. data/.github/CODEOWNERS +2 -2
  3. data/.github/CONTRIBUTING.md +9 -1
  4. data/.github/PULL_REQUEST_TEMPLATE.md +10 -1
  5. data/.github/workflows/release.yml +61 -0
  6. data/.github/workflows/triage.yml +22 -0
  7. data/.gitignore +0 -1
  8. data/.rubocop.yml +61 -8
  9. data/.rubocop_todo.yml +11 -0
  10. data/.travis.yml +1 -0
  11. data/CHANGELOG.md +30 -0
  12. data/Gemfile +3 -2
  13. data/Gemfile.lock +39 -37
  14. data/README.md +39 -7
  15. data/RELEASING.md +19 -29
  16. data/Rakefile +2 -0
  17. data/dev.yml +2 -2
  18. data/docs/_config.yml +1 -18
  19. data/docs/app/node/commands/index.md +2 -80
  20. data/docs/app/node/index.md +2 -33
  21. data/docs/app/rails/commands/index.md +2 -78
  22. data/docs/app/rails/index.md +2 -34
  23. data/docs/core/index.md +2 -84
  24. data/docs/getting-started/index.md +2 -25
  25. data/docs/getting-started/install/index.md +1 -118
  26. data/docs/getting-started/migrate/index.md +2 -94
  27. data/docs/getting-started/uninstall/index.md +2 -35
  28. data/docs/getting-started/upgrade/index.md +2 -39
  29. data/docs/help/start-app/index.md +2 -4
  30. data/docs/index.md +2 -24
  31. data/install.sh +1 -1
  32. data/lib/project_types/extension/cli.rb +21 -11
  33. data/lib/project_types/extension/commands/extension_command.rb +2 -2
  34. data/lib/project_types/extension/features/argo.rb +117 -0
  35. data/lib/project_types/extension/forms/create.rb +2 -2
  36. data/lib/project_types/extension/models/specification.rb +35 -0
  37. data/lib/project_types/extension/models/specification_handlers/checkout_post_purchase.rb +19 -0
  38. data/lib/project_types/extension/models/specification_handlers/default.rb +67 -0
  39. data/lib/project_types/extension/models/specifications.rb +77 -0
  40. data/lib/project_types/extension/tasks/configure_features.rb +52 -0
  41. data/lib/project_types/extension/tasks/fetch_specifications.rb +38 -0
  42. data/lib/project_types/node/cli.rb +4 -1
  43. data/lib/project_types/node/commands/connect.rb +15 -0
  44. data/lib/project_types/node/commands/create.rb +10 -4
  45. data/lib/project_types/node/commands/generate.rb +2 -11
  46. data/lib/project_types/node/messages/messages.rb +16 -50
  47. data/lib/project_types/rails/cli.rb +4 -1
  48. data/lib/project_types/rails/commands/connect.rb +15 -0
  49. data/lib/project_types/rails/commands/create.rb +15 -12
  50. data/lib/project_types/rails/forms/create.rb +1 -1
  51. data/lib/project_types/rails/gem.rb +1 -1
  52. data/lib/project_types/rails/messages/messages.rb +8 -5
  53. data/lib/project_types/script/cli.rb +9 -5
  54. data/lib/project_types/script/commands/create.rb +6 -4
  55. data/lib/project_types/script/commands/enable.rb +12 -4
  56. data/lib/project_types/script/commands/push.rb +5 -13
  57. data/lib/project_types/script/config/extension_points.yml +17 -12
  58. data/lib/project_types/script/errors.rb +21 -0
  59. data/lib/project_types/script/forms/create.rb +26 -2
  60. data/lib/project_types/script/graphql/app_script_update_or_create.graphql +10 -1
  61. data/lib/project_types/script/layers/application/build_script.rb +18 -17
  62. data/lib/project_types/script/layers/application/create_script.rb +12 -10
  63. data/lib/project_types/script/layers/application/extension_points.rb +24 -0
  64. data/lib/project_types/script/layers/application/push_script.rb +18 -16
  65. data/lib/project_types/script/layers/domain/errors.rb +7 -0
  66. data/lib/project_types/script/layers/domain/extension_point.rb +62 -7
  67. data/lib/project_types/script/layers/domain/metadata.rb +55 -0
  68. data/lib/project_types/script/layers/domain/push_package.rb +25 -6
  69. data/lib/project_types/script/layers/infrastructure/assemblyscript_project_creator.rb +17 -52
  70. data/lib/project_types/script/layers/infrastructure/assemblyscript_task_runner.rb +42 -11
  71. data/lib/project_types/script/layers/infrastructure/errors.rb +16 -0
  72. data/lib/project_types/script/layers/infrastructure/extension_point_repository.rb +10 -4
  73. data/lib/project_types/script/layers/infrastructure/project_creator.rb +2 -1
  74. data/lib/project_types/script/layers/infrastructure/push_package_repository.rb +25 -13
  75. data/lib/project_types/script/layers/infrastructure/rust_project_creator.rb +72 -0
  76. data/lib/project_types/script/layers/infrastructure/rust_task_runner.rb +59 -0
  77. data/lib/project_types/script/layers/infrastructure/script_service.rb +9 -1
  78. data/lib/project_types/script/layers/infrastructure/task_runner.rb +4 -3
  79. data/lib/project_types/script/messages/messages.rb +55 -4
  80. data/lib/project_types/script/script_project.rb +25 -16
  81. data/lib/project_types/script/ui/error_handler.rb +59 -1
  82. data/lib/project_types/theme/cli.rb +40 -0
  83. data/lib/project_types/theme/commands/connect.rb +54 -0
  84. data/lib/project_types/theme/commands/create.rb +48 -0
  85. data/lib/project_types/theme/commands/deploy.rb +38 -0
  86. data/lib/project_types/theme/commands/generate.rb +20 -0
  87. data/lib/project_types/theme/commands/generate/env.rb +79 -0
  88. data/lib/project_types/theme/commands/push.rb +55 -0
  89. data/lib/project_types/theme/commands/serve.rb +31 -0
  90. data/lib/project_types/theme/forms/connect.rb +34 -0
  91. data/lib/project_types/theme/forms/create.rb +22 -0
  92. data/lib/project_types/theme/messages/messages.rb +147 -0
  93. data/lib/project_types/theme/tasks/ensure_themekit_installed.rb +78 -0
  94. data/lib/project_types/theme/themekit.rb +113 -0
  95. data/lib/shopify-cli/admin_api.rb +42 -2
  96. data/lib/shopify-cli/api.rb +34 -33
  97. data/lib/shopify-cli/commands/config.rb +24 -0
  98. data/lib/shopify-cli/commands/connect.rb +32 -15
  99. data/lib/shopify-cli/commands/system.rb +10 -1
  100. data/lib/shopify-cli/context.rb +23 -2
  101. data/lib/shopify-cli/core/entry_point.rb +1 -1
  102. data/lib/shopify-cli/core/monorail.rb +6 -4
  103. data/lib/shopify-cli/feature.rb +0 -2
  104. data/lib/shopify-cli/http_request.rb +27 -0
  105. data/lib/shopify-cli/js_deps.rb +1 -1
  106. data/lib/shopify-cli/messages/messages.rb +31 -7
  107. data/lib/shopify-cli/method_object.rb +104 -0
  108. data/lib/shopify-cli/partners_api.rb +25 -3
  109. data/lib/shopify-cli/process_supervision.rb +1 -1
  110. data/lib/shopify-cli/project.rb +12 -8
  111. data/lib/shopify-cli/project_type.rb +18 -2
  112. data/lib/shopify-cli/resolve_constant.rb +25 -0
  113. data/lib/shopify-cli/result.rb +432 -0
  114. data/lib/shopify-cli/shopifolk.rb +87 -0
  115. data/lib/shopify-cli/task.rb +8 -0
  116. data/lib/shopify-cli/tasks/create_api_client.rb +13 -2
  117. data/lib/shopify-cli/tasks/ensure_env.rb +3 -0
  118. data/lib/shopify-cli/tasks/select_org_and_shop.rb +10 -5
  119. data/lib/shopify-cli/tunnel.rb +8 -2
  120. data/lib/shopify-cli/version.rb +1 -1
  121. data/lib/shopify_cli.rb +5 -1
  122. data/shopify.fish +1 -1
  123. data/shopify.sh +1 -1
  124. data/vendor/deps/cli-kit/REVISION +1 -1
  125. data/vendor/deps/cli-kit/lib/cli/kit/logger.rb +2 -2
  126. data/vendor/deps/cli-kit/lib/cli/kit/system.rb +3 -3
  127. data/vendor/deps/cli-ui/REVISION +1 -1
  128. data/vendor/deps/cli-ui/lib/cli/ui.rb +26 -22
  129. data/vendor/deps/cli-ui/lib/cli/ui/ansi.rb +4 -6
  130. data/vendor/deps/cli-ui/lib/cli/ui/frame.rb +3 -3
  131. data/vendor/deps/cli-ui/lib/cli/ui/frame/frame_stack.rb +8 -9
  132. data/vendor/deps/cli-ui/lib/cli/ui/frame/frame_style.rb +1 -1
  133. data/vendor/deps/cli-ui/lib/cli/ui/glyph.rb +1 -0
  134. data/vendor/deps/cli-ui/lib/cli/ui/printer.rb +15 -3
  135. data/vendor/deps/cli-ui/lib/cli/ui/prompt/interactive_options.rb +4 -11
  136. data/vendor/deps/cli-ui/lib/cli/ui/spinner.rb +3 -5
  137. data/vendor/deps/cli-ui/lib/cli/ui/terminal.rb +10 -10
  138. data/vendor/deps/cli-ui/lib/cli/ui/version.rb +1 -1
  139. data/vendor/deps/cli-ui/lib/cli/ui/wrap.rb +56 -0
  140. data/vendor/deps/webrick/.gitignore +9 -0
  141. data/vendor/deps/webrick/Gemfile +3 -0
  142. data/vendor/deps/webrick/LICENSE.txt +22 -0
  143. data/vendor/deps/webrick/README.md +61 -0
  144. data/vendor/deps/webrick/Rakefile +10 -0
  145. data/vendor/deps/webrick/lib/webrick.rb +232 -0
  146. data/vendor/deps/webrick/lib/webrick/accesslog.rb +157 -0
  147. data/vendor/deps/webrick/lib/webrick/cgi.rb +313 -0
  148. data/vendor/deps/webrick/lib/webrick/compat.rb +36 -0
  149. data/vendor/deps/webrick/lib/webrick/config.rb +158 -0
  150. data/vendor/deps/webrick/lib/webrick/cookie.rb +172 -0
  151. data/vendor/deps/webrick/lib/webrick/htmlutils.rb +30 -0
  152. data/vendor/deps/webrick/lib/webrick/httpauth.rb +96 -0
  153. data/vendor/deps/webrick/lib/webrick/httpauth/authenticator.rb +117 -0
  154. data/vendor/deps/webrick/lib/webrick/httpauth/basicauth.rb +116 -0
  155. data/vendor/deps/webrick/lib/webrick/httpauth/digestauth.rb +395 -0
  156. data/vendor/deps/webrick/lib/webrick/httpauth/htdigest.rb +132 -0
  157. data/vendor/deps/webrick/lib/webrick/httpauth/htgroup.rb +97 -0
  158. data/vendor/deps/webrick/lib/webrick/httpauth/htpasswd.rb +158 -0
  159. data/vendor/deps/webrick/lib/webrick/httpauth/userdb.rb +53 -0
  160. data/vendor/deps/webrick/lib/webrick/httpproxy.rb +354 -0
  161. data/vendor/deps/webrick/lib/webrick/httprequest.rb +636 -0
  162. data/vendor/deps/webrick/lib/webrick/httpresponse.rb +564 -0
  163. data/vendor/deps/webrick/lib/webrick/https.rb +152 -0
  164. data/vendor/deps/webrick/lib/webrick/httpserver.rb +294 -0
  165. data/vendor/deps/webrick/lib/webrick/httpservlet.rb +23 -0
  166. data/vendor/deps/webrick/lib/webrick/httpservlet/abstract.rb +152 -0
  167. data/vendor/deps/webrick/lib/webrick/httpservlet/cgi_runner.rb +47 -0
  168. data/vendor/deps/webrick/lib/webrick/httpservlet/cgihandler.rb +126 -0
  169. data/vendor/deps/webrick/lib/webrick/httpservlet/erbhandler.rb +88 -0
  170. data/vendor/deps/webrick/lib/webrick/httpservlet/filehandler.rb +552 -0
  171. data/vendor/deps/webrick/lib/webrick/httpservlet/prochandler.rb +47 -0
  172. data/vendor/deps/webrick/lib/webrick/httpstatus.rb +194 -0
  173. data/vendor/deps/webrick/lib/webrick/httputils.rb +512 -0
  174. data/vendor/deps/webrick/lib/webrick/httpversion.rb +76 -0
  175. data/vendor/deps/webrick/lib/webrick/log.rb +156 -0
  176. data/vendor/deps/webrick/lib/webrick/server.rb +381 -0
  177. data/vendor/deps/webrick/lib/webrick/ssl.rb +215 -0
  178. data/vendor/deps/webrick/lib/webrick/utils.rb +265 -0
  179. data/vendor/deps/webrick/lib/webrick/version.rb +18 -0
  180. data/vendor/deps/webrick/webrick.gemspec +74 -0
  181. metadata +77 -27
  182. data/docs/Gemfile +0 -5
  183. data/docs/Gemfile.lock +0 -258
  184. data/docs/_data/nav.yml +0 -35
  185. data/docs/_includes/footer.html +0 -15
  186. data/docs/_includes/head.html +0 -19
  187. data/docs/_includes/sidebar_nav.html +0 -22
  188. data/docs/_includes/toc.html +0 -112
  189. data/docs/_layouts/default.html +0 -79
  190. data/docs/css/docs.css +0 -157
  191. data/docs/images/header.png +0 -0
  192. data/docs/installing-ruby.md +0 -28
  193. data/lib/project_types/extension/features/argo/admin.rb +0 -20
  194. data/lib/project_types/extension/features/argo/base.rb +0 -129
  195. data/lib/project_types/extension/features/argo/checkout.rb +0 -20
  196. data/lib/project_types/extension/models/type.rb +0 -81
  197. data/lib/project_types/extension/models/types/checkout_post_purchase.rb +0 -23
  198. data/lib/project_types/extension/models/types/product_subscription.rb +0 -24
  199. data/lib/project_types/node/commands/generate/billing.rb +0 -39
  200. data/lib/project_types/node/commands/generate/page.rb +0 -59
  201. data/lib/project_types/node/commands/generate/webhook.rb +0 -37
  202. data/lib/project_types/script/layers/domain/script.rb +0 -18
  203. data/lib/project_types/script/layers/infrastructure/assemblyscript_tsconfig.rb +0 -38
  204. data/lib/project_types/script/layers/infrastructure/script_repository.rb +0 -59
  205. data/lib/project_types/script/templates/ts/as-pect.config.js +0 -27
  206. data/lib/project_types/script/templates/ts/as-pect.d.ts +0 -1
@@ -0,0 +1,564 @@
1
+ # frozen_string_literal: true
2
+ #
3
+ # httpresponse.rb -- HTTPResponse Class
4
+ #
5
+ # Author: IPR -- Internet Programming with Ruby -- writers
6
+ # Copyright (c) 2000, 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou
7
+ # Copyright (c) 2002 Internet Programming with Ruby writers. All rights
8
+ # reserved.
9
+ #
10
+ # $IPR: httpresponse.rb,v 1.45 2003/07/11 11:02:25 gotoyuzo Exp $
11
+
12
+ require 'time'
13
+ require 'uri'
14
+ require_relative 'httpversion'
15
+ require_relative 'htmlutils'
16
+ require_relative 'httputils'
17
+ require_relative 'httpstatus'
18
+
19
+ module WEBrick
20
+ ##
21
+ # An HTTP response. This is filled in by the service or do_* methods of a
22
+ # WEBrick HTTP Servlet.
23
+
24
+ class HTTPResponse
25
+ class InvalidHeader < StandardError
26
+ end
27
+
28
+ ##
29
+ # HTTP Response version
30
+
31
+ attr_reader :http_version
32
+
33
+ ##
34
+ # Response status code (200)
35
+
36
+ attr_reader :status
37
+
38
+ ##
39
+ # Response header
40
+
41
+ attr_reader :header
42
+
43
+ ##
44
+ # Response cookies
45
+
46
+ attr_reader :cookies
47
+
48
+ ##
49
+ # Response reason phrase ("OK")
50
+
51
+ attr_accessor :reason_phrase
52
+
53
+ ##
54
+ # Body may be:
55
+ # * a String;
56
+ # * an IO-like object that responds to +#read+ and +#readpartial+;
57
+ # * a Proc-like object that responds to +#call+.
58
+ #
59
+ # In the latter case, either #chunked= should be set to +true+,
60
+ # or <code>header['content-length']</code> explicitly provided.
61
+ # Example:
62
+ #
63
+ # server.mount_proc '/' do |req, res|
64
+ # res.chunked = true
65
+ # # or
66
+ # # res.header['content-length'] = 10
67
+ # res.body = proc { |out| out.write(Time.now.to_s) }
68
+ # end
69
+
70
+ attr_accessor :body
71
+
72
+ ##
73
+ # Request method for this response
74
+
75
+ attr_accessor :request_method
76
+
77
+ ##
78
+ # Request URI for this response
79
+
80
+ attr_accessor :request_uri
81
+
82
+ ##
83
+ # Request HTTP version for this response
84
+
85
+ attr_accessor :request_http_version
86
+
87
+ ##
88
+ # Filename of the static file in this response. Only used by the
89
+ # FileHandler servlet.
90
+
91
+ attr_accessor :filename
92
+
93
+ ##
94
+ # Is this a keep-alive response?
95
+
96
+ attr_accessor :keep_alive
97
+
98
+ ##
99
+ # Configuration for this response
100
+
101
+ attr_reader :config
102
+
103
+ ##
104
+ # Bytes sent in this response
105
+
106
+ attr_reader :sent_size
107
+
108
+ ##
109
+ # Creates a new HTTP response object. WEBrick::Config::HTTP is the
110
+ # default configuration.
111
+
112
+ def initialize(config)
113
+ @config = config
114
+ @buffer_size = config[:OutputBufferSize]
115
+ @logger = config[:Logger]
116
+ @header = Hash.new
117
+ @status = HTTPStatus::RC_OK
118
+ @reason_phrase = nil
119
+ @http_version = HTTPVersion::convert(@config[:HTTPVersion])
120
+ @body = ''
121
+ @keep_alive = true
122
+ @cookies = []
123
+ @request_method = nil
124
+ @request_uri = nil
125
+ @request_http_version = @http_version # temporary
126
+ @chunked = false
127
+ @filename = nil
128
+ @sent_size = 0
129
+ @bodytempfile = nil
130
+ end
131
+
132
+ ##
133
+ # The response's HTTP status line
134
+
135
+ def status_line
136
+ "HTTP/#@http_version #@status #@reason_phrase".rstrip << CRLF
137
+ end
138
+
139
+ ##
140
+ # Sets the response's status to the +status+ code
141
+
142
+ def status=(status)
143
+ @status = status
144
+ @reason_phrase = HTTPStatus::reason_phrase(status)
145
+ end
146
+
147
+ ##
148
+ # Retrieves the response header +field+
149
+
150
+ def [](field)
151
+ @header[field.downcase]
152
+ end
153
+
154
+ ##
155
+ # Sets the response header +field+ to +value+
156
+
157
+ def []=(field, value)
158
+ @chunked = value.to_s.downcase == 'chunked' if field.downcase == 'transfer-encoding'
159
+ @header[field.downcase] = value.to_s
160
+ end
161
+
162
+ ##
163
+ # The content-length header
164
+
165
+ def content_length
166
+ if len = self['content-length']
167
+ return Integer(len)
168
+ end
169
+ end
170
+
171
+ ##
172
+ # Sets the content-length header to +len+
173
+
174
+ def content_length=(len)
175
+ self['content-length'] = len.to_s
176
+ end
177
+
178
+ ##
179
+ # The content-type header
180
+
181
+ def content_type
182
+ self['content-type']
183
+ end
184
+
185
+ ##
186
+ # Sets the content-type header to +type+
187
+
188
+ def content_type=(type)
189
+ self['content-type'] = type
190
+ end
191
+
192
+ ##
193
+ # Iterates over each header in the response
194
+
195
+ def each
196
+ @header.each{|field, value| yield(field, value) }
197
+ end
198
+
199
+ ##
200
+ # Will this response body be returned using chunked transfer-encoding?
201
+
202
+ def chunked?
203
+ @chunked
204
+ end
205
+
206
+ ##
207
+ # Enables chunked transfer encoding.
208
+
209
+ def chunked=(val)
210
+ @chunked = val ? true : false
211
+ end
212
+
213
+ ##
214
+ # Will this response's connection be kept alive?
215
+
216
+ def keep_alive?
217
+ @keep_alive
218
+ end
219
+
220
+ ##
221
+ # Sends the response on +socket+
222
+
223
+ def send_response(socket) # :nodoc:
224
+ begin
225
+ setup_header()
226
+ send_header(socket)
227
+ send_body(socket)
228
+ rescue Errno::EPIPE, Errno::ECONNRESET, Errno::ENOTCONN => ex
229
+ @logger.debug(ex)
230
+ @keep_alive = false
231
+ rescue Exception => ex
232
+ @logger.error(ex)
233
+ @keep_alive = false
234
+ end
235
+ end
236
+
237
+ ##
238
+ # Sets up the headers for sending
239
+
240
+ def setup_header() # :nodoc:
241
+ @reason_phrase ||= HTTPStatus::reason_phrase(@status)
242
+ @header['server'] ||= @config[:ServerSoftware]
243
+ @header['date'] ||= Time.now.httpdate
244
+
245
+ # HTTP/0.9 features
246
+ if @request_http_version < "1.0"
247
+ @http_version = HTTPVersion.new("0.9")
248
+ @keep_alive = false
249
+ end
250
+
251
+ # HTTP/1.0 features
252
+ if @request_http_version < "1.1"
253
+ if chunked?
254
+ @chunked = false
255
+ ver = @request_http_version.to_s
256
+ msg = "chunked is set for an HTTP/#{ver} request. (ignored)"
257
+ @logger.warn(msg)
258
+ end
259
+ end
260
+
261
+ # Determine the message length (RFC2616 -- 4.4 Message Length)
262
+ if @status == 304 || @status == 204 || HTTPStatus::info?(@status)
263
+ @header.delete('content-length')
264
+ @body = ""
265
+ elsif chunked?
266
+ @header["transfer-encoding"] = "chunked"
267
+ @header.delete('content-length')
268
+ elsif %r{^multipart/byteranges} =~ @header['content-type']
269
+ @header.delete('content-length')
270
+ elsif @header['content-length'].nil?
271
+ if @body.respond_to? :readpartial
272
+ elsif @body.respond_to? :call
273
+ make_body_tempfile
274
+ else
275
+ @header['content-length'] = (@body ? @body.bytesize : 0).to_s
276
+ end
277
+ end
278
+
279
+ # Keep-Alive connection.
280
+ if @header['connection'] == "close"
281
+ @keep_alive = false
282
+ elsif keep_alive?
283
+ if chunked? || @header['content-length'] || @status == 304 || @status == 204 || HTTPStatus.info?(@status)
284
+ @header['connection'] = "Keep-Alive"
285
+ else
286
+ msg = "Could not determine content-length of response body. Set content-length of the response or set Response#chunked = true"
287
+ @logger.warn(msg)
288
+ @header['connection'] = "close"
289
+ @keep_alive = false
290
+ end
291
+ else
292
+ @header['connection'] = "close"
293
+ end
294
+
295
+ # Location is a single absoluteURI.
296
+ if location = @header['location']
297
+ if @request_uri
298
+ @header['location'] = @request_uri.merge(location).to_s
299
+ end
300
+ end
301
+ end
302
+
303
+ def make_body_tempfile # :nodoc:
304
+ return if @bodytempfile
305
+ bodytempfile = Tempfile.create("webrick")
306
+ if @body.nil?
307
+ # nothing
308
+ elsif @body.respond_to? :readpartial
309
+ IO.copy_stream(@body, bodytempfile)
310
+ @body.close
311
+ elsif @body.respond_to? :call
312
+ @body.call(bodytempfile)
313
+ else
314
+ bodytempfile.write @body
315
+ end
316
+ bodytempfile.rewind
317
+ @body = @bodytempfile = bodytempfile
318
+ @header['content-length'] = bodytempfile.stat.size.to_s
319
+ end
320
+
321
+ def remove_body_tempfile # :nodoc:
322
+ if @bodytempfile
323
+ @bodytempfile.close
324
+ File.unlink @bodytempfile.path
325
+ @bodytempfile = nil
326
+ end
327
+ end
328
+
329
+
330
+ ##
331
+ # Sends the headers on +socket+
332
+
333
+ def send_header(socket) # :nodoc:
334
+ if @http_version.major > 0
335
+ data = status_line().dup
336
+ @header.each{|key, value|
337
+ tmp = key.gsub(/\bwww|^te$|\b\w/){ $&.upcase }
338
+ data << "#{tmp}: #{check_header(value)}" << CRLF
339
+ }
340
+ @cookies.each{|cookie|
341
+ data << "Set-Cookie: " << check_header(cookie.to_s) << CRLF
342
+ }
343
+ data << CRLF
344
+ socket.write(data)
345
+ end
346
+ rescue InvalidHeader => e
347
+ @header.clear
348
+ @cookies.clear
349
+ set_error e
350
+ retry
351
+ end
352
+
353
+ ##
354
+ # Sends the body on +socket+
355
+
356
+ def send_body(socket) # :nodoc:
357
+ if @body.respond_to? :readpartial then
358
+ send_body_io(socket)
359
+ elsif @body.respond_to?(:call) then
360
+ send_body_proc(socket)
361
+ else
362
+ send_body_string(socket)
363
+ end
364
+ end
365
+
366
+ ##
367
+ # Redirects to +url+ with a WEBrick::HTTPStatus::Redirect +status+.
368
+ #
369
+ # Example:
370
+ #
371
+ # res.set_redirect WEBrick::HTTPStatus::TemporaryRedirect
372
+
373
+ def set_redirect(status, url)
374
+ url = URI(url).to_s
375
+ @body = "<HTML><A HREF=\"#{url}\">#{url}</A>.</HTML>\n"
376
+ @header['location'] = url
377
+ raise status
378
+ end
379
+
380
+ ##
381
+ # Creates an error page for exception +ex+ with an optional +backtrace+
382
+
383
+ def set_error(ex, backtrace=false)
384
+ case ex
385
+ when HTTPStatus::Status
386
+ @keep_alive = false if HTTPStatus::error?(ex.code)
387
+ self.status = ex.code
388
+ else
389
+ @keep_alive = false
390
+ self.status = HTTPStatus::RC_INTERNAL_SERVER_ERROR
391
+ end
392
+ @header['content-type'] = "text/html; charset=ISO-8859-1"
393
+
394
+ if respond_to?(:create_error_page)
395
+ create_error_page()
396
+ return
397
+ end
398
+
399
+ if @request_uri
400
+ host, port = @request_uri.host, @request_uri.port
401
+ else
402
+ host, port = @config[:ServerName], @config[:Port]
403
+ end
404
+
405
+ error_body(backtrace, ex, host, port)
406
+ end
407
+
408
+ private
409
+
410
+ def check_header(header_value)
411
+ header_value = header_value.to_s
412
+ if /[\r\n]/ =~ header_value
413
+ raise InvalidHeader
414
+ else
415
+ header_value
416
+ end
417
+ end
418
+
419
+ # :stopdoc:
420
+
421
+ def error_body(backtrace, ex, host, port)
422
+ @body = +''
423
+ @body << <<-_end_of_html_
424
+ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
425
+ <HTML>
426
+ <HEAD><TITLE>#{HTMLUtils::escape(@reason_phrase)}</TITLE></HEAD>
427
+ <BODY>
428
+ <H1>#{HTMLUtils::escape(@reason_phrase)}</H1>
429
+ #{HTMLUtils::escape(ex.message)}
430
+ <HR>
431
+ _end_of_html_
432
+
433
+ if backtrace && $DEBUG
434
+ @body << "backtrace of `#{HTMLUtils::escape(ex.class.to_s)}' "
435
+ @body << "#{HTMLUtils::escape(ex.message)}"
436
+ @body << "<PRE>"
437
+ ex.backtrace.each{|line| @body << "\t#{line}\n"}
438
+ @body << "</PRE><HR>"
439
+ end
440
+
441
+ @body << <<-_end_of_html_
442
+ <ADDRESS>
443
+ #{HTMLUtils::escape(@config[:ServerSoftware])} at
444
+ #{host}:#{port}
445
+ </ADDRESS>
446
+ </BODY>
447
+ </HTML>
448
+ _end_of_html_
449
+ end
450
+
451
+ def send_body_io(socket)
452
+ begin
453
+ if @request_method == "HEAD"
454
+ # do nothing
455
+ elsif chunked?
456
+ buf = +''
457
+ begin
458
+ @body.readpartial(@buffer_size, buf)
459
+ size = buf.bytesize
460
+ data = +"#{size.to_s(16)}#{CRLF}#{buf}#{CRLF}"
461
+ socket.write(data)
462
+ data.clear
463
+ @sent_size += size
464
+ rescue EOFError
465
+ break
466
+ end while true
467
+ buf.clear
468
+ socket.write("0#{CRLF}#{CRLF}")
469
+ else
470
+ if %r{\Abytes (\d+)-(\d+)/\d+\z} =~ @header['content-range']
471
+ offset = $1.to_i
472
+ size = $2.to_i - offset + 1
473
+ else
474
+ offset = nil
475
+ size = @header['content-length']
476
+ size = size.to_i if size
477
+ end
478
+ begin
479
+ @sent_size = IO.copy_stream(@body, socket, size, offset)
480
+ rescue NotImplementedError
481
+ @body.seek(offset, IO::SEEK_SET)
482
+ @sent_size = IO.copy_stream(@body, socket, size)
483
+ end
484
+ end
485
+ ensure
486
+ @body.close
487
+ end
488
+ remove_body_tempfile
489
+ end
490
+
491
+ def send_body_string(socket)
492
+ if @request_method == "HEAD"
493
+ # do nothing
494
+ elsif chunked?
495
+ body ? @body.bytesize : 0
496
+ while buf = @body[@sent_size, @buffer_size]
497
+ break if buf.empty?
498
+ size = buf.bytesize
499
+ data = "#{size.to_s(16)}#{CRLF}#{buf}#{CRLF}"
500
+ buf.clear
501
+ socket.write(data)
502
+ @sent_size += size
503
+ end
504
+ socket.write("0#{CRLF}#{CRLF}")
505
+ else
506
+ if @body && @body.bytesize > 0
507
+ socket.write(@body)
508
+ @sent_size = @body.bytesize
509
+ end
510
+ end
511
+ end
512
+
513
+ def send_body_proc(socket)
514
+ if @request_method == "HEAD"
515
+ # do nothing
516
+ elsif chunked?
517
+ @body.call(ChunkedWrapper.new(socket, self))
518
+ socket.write("0#{CRLF}#{CRLF}")
519
+ else
520
+ size = @header['content-length'].to_i
521
+ if @bodytempfile
522
+ @bodytempfile.rewind
523
+ IO.copy_stream(@bodytempfile, socket)
524
+ else
525
+ @body.call(socket)
526
+ end
527
+ @sent_size = size
528
+ end
529
+ end
530
+
531
+ class ChunkedWrapper
532
+ def initialize(socket, resp)
533
+ @socket = socket
534
+ @resp = resp
535
+ end
536
+
537
+ def write(buf)
538
+ return 0 if buf.empty?
539
+ socket = @socket
540
+ @resp.instance_eval {
541
+ size = buf.bytesize
542
+ data = +"#{size.to_s(16)}#{CRLF}#{buf}#{CRLF}"
543
+ socket.write(data)
544
+ data.clear
545
+ @sent_size += size
546
+ size
547
+ }
548
+ end
549
+
550
+ def <<(*buf)
551
+ write(buf)
552
+ self
553
+ end
554
+ end
555
+
556
+ # preserved for compatibility with some 3rd-party handlers
557
+ def _write_data(socket, data)
558
+ socket << data
559
+ end
560
+
561
+ # :startdoc:
562
+ end
563
+
564
+ end