shopify-cli 1.5.0 → 1.6.0

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 (179) hide show
  1. checksums.yaml +4 -4
  2. data/.github/PULL_REQUEST_TEMPLATE.md +1 -0
  3. data/.travis.yml +1 -0
  4. data/CHANGELOG.md +9 -0
  5. data/README.md +39 -7
  6. data/Rakefile +2 -0
  7. data/dev.yml +2 -2
  8. data/docs/_config.yml +1 -18
  9. data/docs/app/node/commands/index.md +2 -80
  10. data/docs/app/node/index.md +2 -33
  11. data/docs/app/rails/commands/index.md +2 -78
  12. data/docs/app/rails/index.md +2 -34
  13. data/docs/core/index.md +2 -84
  14. data/docs/getting-started/index.md +2 -25
  15. data/docs/getting-started/install/index.md +1 -118
  16. data/docs/getting-started/migrate/index.md +2 -94
  17. data/docs/getting-started/uninstall/index.md +2 -35
  18. data/docs/getting-started/upgrade/index.md +2 -39
  19. data/docs/help/start-app/index.md +2 -4
  20. data/docs/index.md +2 -24
  21. data/install.sh +1 -1
  22. data/lib/project_types/extension/cli.rb +19 -10
  23. data/lib/project_types/extension/commands/extension_command.rb +2 -2
  24. data/lib/project_types/extension/features/argo.rb +117 -0
  25. data/lib/project_types/extension/forms/create.rb +2 -2
  26. data/lib/project_types/extension/models/specification.rb +35 -0
  27. data/lib/project_types/extension/models/specification_handlers/checkout_post_purchase.rb +19 -0
  28. data/lib/project_types/extension/models/specification_handlers/default.rb +67 -0
  29. data/lib/project_types/extension/models/specifications.rb +77 -0
  30. data/lib/project_types/extension/tasks/configure_features.rb +52 -0
  31. data/lib/project_types/extension/tasks/fetch_specifications.rb +38 -0
  32. data/lib/project_types/node/commands/create.rb +3 -1
  33. data/lib/project_types/node/commands/generate.rb +2 -11
  34. data/lib/project_types/node/messages/messages.rb +9 -44
  35. data/lib/project_types/rails/commands/create.rb +8 -9
  36. data/lib/project_types/rails/forms/create.rb +1 -1
  37. data/lib/project_types/rails/gem.rb +1 -1
  38. data/lib/project_types/rails/messages/messages.rb +1 -1
  39. data/lib/project_types/script/cli.rb +7 -4
  40. data/lib/project_types/script/commands/create.rb +6 -4
  41. data/lib/project_types/script/commands/push.rb +5 -13
  42. data/lib/project_types/script/config/extension_points.yml +9 -5
  43. data/lib/project_types/script/errors.rb +17 -0
  44. data/lib/project_types/script/forms/create.rb +26 -2
  45. data/lib/project_types/script/graphql/app_script_update_or_create.graphql +10 -1
  46. data/lib/project_types/script/layers/application/build_script.rb +9 -4
  47. data/lib/project_types/script/layers/application/create_script.rb +12 -10
  48. data/lib/project_types/script/layers/application/extension_points.rb +24 -0
  49. data/lib/project_types/script/layers/application/push_script.rb +18 -16
  50. data/lib/project_types/script/layers/domain/errors.rb +4 -0
  51. data/lib/project_types/script/layers/domain/extension_point.rb +62 -6
  52. data/lib/project_types/script/layers/domain/metadata.rb +55 -0
  53. data/lib/project_types/script/layers/domain/push_package.rb +25 -6
  54. data/lib/project_types/script/layers/infrastructure/assemblyscript_project_creator.rb +6 -6
  55. data/lib/project_types/script/layers/infrastructure/assemblyscript_task_runner.rb +16 -6
  56. data/lib/project_types/script/layers/infrastructure/extension_point_repository.rb +10 -4
  57. data/lib/project_types/script/layers/infrastructure/project_creator.rb +2 -1
  58. data/lib/project_types/script/layers/infrastructure/push_package_repository.rb +25 -13
  59. data/lib/project_types/script/layers/infrastructure/rust_project_creator.rb +72 -0
  60. data/lib/project_types/script/layers/infrastructure/rust_task_runner.rb +59 -0
  61. data/lib/project_types/script/layers/infrastructure/script_service.rb +7 -1
  62. data/lib/project_types/script/layers/infrastructure/task_runner.rb +4 -3
  63. data/lib/project_types/script/messages/messages.rb +39 -8
  64. data/lib/project_types/script/script_project.rb +25 -16
  65. data/lib/project_types/script/ui/error_handler.rb +34 -1
  66. data/lib/project_types/theme/cli.rb +40 -0
  67. data/lib/project_types/theme/commands/connect.rb +54 -0
  68. data/lib/project_types/theme/commands/create.rb +48 -0
  69. data/lib/project_types/theme/commands/deploy.rb +38 -0
  70. data/lib/project_types/theme/commands/generate.rb +20 -0
  71. data/lib/project_types/theme/commands/generate/env.rb +79 -0
  72. data/lib/project_types/theme/commands/push.rb +55 -0
  73. data/lib/project_types/theme/commands/serve.rb +31 -0
  74. data/lib/project_types/theme/forms/connect.rb +34 -0
  75. data/lib/project_types/theme/forms/create.rb +22 -0
  76. data/lib/project_types/theme/messages/messages.rb +147 -0
  77. data/lib/project_types/theme/tasks/ensure_themekit_installed.rb +78 -0
  78. data/lib/project_types/theme/themekit.rb +113 -0
  79. data/lib/shopify-cli/admin_api.rb +42 -2
  80. data/lib/shopify-cli/api.rb +27 -24
  81. data/lib/shopify-cli/commands/system.rb +1 -1
  82. data/lib/shopify-cli/context.rb +23 -2
  83. data/lib/shopify-cli/feature.rb +0 -2
  84. data/lib/shopify-cli/http_request.rb +20 -8
  85. data/lib/shopify-cli/messages/messages.rb +6 -3
  86. data/lib/shopify-cli/method_object.rb +104 -0
  87. data/lib/shopify-cli/partners_api.rb +8 -2
  88. data/lib/shopify-cli/project_type.rb +1 -1
  89. data/lib/shopify-cli/resolve_constant.rb +25 -0
  90. data/lib/shopify-cli/result.rb +432 -0
  91. data/lib/shopify-cli/shopifolk.rb +3 -2
  92. data/lib/shopify-cli/tasks/select_org_and_shop.rb +6 -5
  93. data/lib/shopify-cli/tunnel.rb +7 -1
  94. data/lib/shopify-cli/version.rb +1 -1
  95. data/lib/shopify_cli.rb +4 -1
  96. data/shopify.fish +1 -1
  97. data/shopify.sh +1 -1
  98. data/vendor/deps/cli-kit/REVISION +1 -1
  99. data/vendor/deps/cli-kit/lib/cli/kit/logger.rb +2 -2
  100. data/vendor/deps/cli-kit/lib/cli/kit/system.rb +3 -3
  101. data/vendor/deps/cli-ui/REVISION +1 -1
  102. data/vendor/deps/cli-ui/lib/cli/ui.rb +26 -22
  103. data/vendor/deps/cli-ui/lib/cli/ui/ansi.rb +4 -6
  104. data/vendor/deps/cli-ui/lib/cli/ui/frame.rb +3 -3
  105. data/vendor/deps/cli-ui/lib/cli/ui/frame/frame_stack.rb +8 -9
  106. data/vendor/deps/cli-ui/lib/cli/ui/frame/frame_style.rb +1 -1
  107. data/vendor/deps/cli-ui/lib/cli/ui/glyph.rb +1 -0
  108. data/vendor/deps/cli-ui/lib/cli/ui/printer.rb +15 -3
  109. data/vendor/deps/cli-ui/lib/cli/ui/prompt/interactive_options.rb +4 -11
  110. data/vendor/deps/cli-ui/lib/cli/ui/spinner.rb +3 -5
  111. data/vendor/deps/cli-ui/lib/cli/ui/terminal.rb +10 -10
  112. data/vendor/deps/cli-ui/lib/cli/ui/version.rb +1 -1
  113. data/vendor/deps/cli-ui/lib/cli/ui/wrap.rb +56 -0
  114. data/vendor/deps/webrick/.gitignore +9 -0
  115. data/vendor/deps/webrick/Gemfile +3 -0
  116. data/vendor/deps/webrick/LICENSE.txt +22 -0
  117. data/vendor/deps/webrick/README.md +61 -0
  118. data/vendor/deps/webrick/Rakefile +10 -0
  119. data/vendor/deps/webrick/lib/webrick.rb +232 -0
  120. data/vendor/deps/webrick/lib/webrick/accesslog.rb +157 -0
  121. data/vendor/deps/webrick/lib/webrick/cgi.rb +313 -0
  122. data/vendor/deps/webrick/lib/webrick/compat.rb +36 -0
  123. data/vendor/deps/webrick/lib/webrick/config.rb +158 -0
  124. data/vendor/deps/webrick/lib/webrick/cookie.rb +172 -0
  125. data/vendor/deps/webrick/lib/webrick/htmlutils.rb +30 -0
  126. data/vendor/deps/webrick/lib/webrick/httpauth.rb +96 -0
  127. data/vendor/deps/webrick/lib/webrick/httpauth/authenticator.rb +117 -0
  128. data/vendor/deps/webrick/lib/webrick/httpauth/basicauth.rb +116 -0
  129. data/vendor/deps/webrick/lib/webrick/httpauth/digestauth.rb +395 -0
  130. data/vendor/deps/webrick/lib/webrick/httpauth/htdigest.rb +132 -0
  131. data/vendor/deps/webrick/lib/webrick/httpauth/htgroup.rb +97 -0
  132. data/vendor/deps/webrick/lib/webrick/httpauth/htpasswd.rb +158 -0
  133. data/vendor/deps/webrick/lib/webrick/httpauth/userdb.rb +53 -0
  134. data/vendor/deps/webrick/lib/webrick/httpproxy.rb +354 -0
  135. data/vendor/deps/webrick/lib/webrick/httprequest.rb +636 -0
  136. data/vendor/deps/webrick/lib/webrick/httpresponse.rb +564 -0
  137. data/vendor/deps/webrick/lib/webrick/https.rb +152 -0
  138. data/vendor/deps/webrick/lib/webrick/httpserver.rb +294 -0
  139. data/vendor/deps/webrick/lib/webrick/httpservlet.rb +23 -0
  140. data/vendor/deps/webrick/lib/webrick/httpservlet/abstract.rb +152 -0
  141. data/vendor/deps/webrick/lib/webrick/httpservlet/cgi_runner.rb +47 -0
  142. data/vendor/deps/webrick/lib/webrick/httpservlet/cgihandler.rb +126 -0
  143. data/vendor/deps/webrick/lib/webrick/httpservlet/erbhandler.rb +88 -0
  144. data/vendor/deps/webrick/lib/webrick/httpservlet/filehandler.rb +552 -0
  145. data/vendor/deps/webrick/lib/webrick/httpservlet/prochandler.rb +47 -0
  146. data/vendor/deps/webrick/lib/webrick/httpstatus.rb +194 -0
  147. data/vendor/deps/webrick/lib/webrick/httputils.rb +512 -0
  148. data/vendor/deps/webrick/lib/webrick/httpversion.rb +76 -0
  149. data/vendor/deps/webrick/lib/webrick/log.rb +156 -0
  150. data/vendor/deps/webrick/lib/webrick/server.rb +381 -0
  151. data/vendor/deps/webrick/lib/webrick/ssl.rb +215 -0
  152. data/vendor/deps/webrick/lib/webrick/utils.rb +265 -0
  153. data/vendor/deps/webrick/lib/webrick/version.rb +18 -0
  154. data/vendor/deps/webrick/webrick.gemspec +74 -0
  155. metadata +70 -26
  156. data/docs/Gemfile +0 -5
  157. data/docs/Gemfile.lock +0 -258
  158. data/docs/_data/nav.yml +0 -35
  159. data/docs/_includes/footer.html +0 -15
  160. data/docs/_includes/head.html +0 -19
  161. data/docs/_includes/sidebar_nav.html +0 -22
  162. data/docs/_includes/toc.html +0 -112
  163. data/docs/_layouts/default.html +0 -79
  164. data/docs/css/docs.css +0 -157
  165. data/docs/images/header.png +0 -0
  166. data/docs/installing-ruby.md +0 -28
  167. data/lib/project_types/extension/features/argo/admin.rb +0 -20
  168. data/lib/project_types/extension/features/argo/base.rb +0 -129
  169. data/lib/project_types/extension/features/argo/checkout.rb +0 -20
  170. data/lib/project_types/extension/models/type.rb +0 -81
  171. data/lib/project_types/extension/models/types/checkout_post_purchase.rb +0 -23
  172. data/lib/project_types/extension/models/types/product_subscription.rb +0 -24
  173. data/lib/project_types/node/commands/generate/billing.rb +0 -39
  174. data/lib/project_types/node/commands/generate/page.rb +0 -59
  175. data/lib/project_types/node/commands/generate/webhook.rb +0 -37
  176. data/lib/project_types/script/layers/domain/script.rb +0 -18
  177. data/lib/project_types/script/layers/infrastructure/script_repository.rb +0 -47
  178. data/lib/project_types/script/templates/ts/as-pect.config.js +0 -27
  179. 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