class Shrine::UploadEndpoint

Rack application that accepts multipart POST request to the root URL, calls ‘#upload` with the uploaded file, and returns the uploaded file information in JSON format.

Constants

CONTENT_TYPE_JSON
CONTENT_TYPE_TEXT

Public Class Methods

new(options) click to toggle source

Writes given options to instance variables.

# File lib/shrine/plugins/upload_endpoint.rb, line 72
def initialize(options)
  options.each do |name, value|
    instance_variable_set("@#{name}", value)
  end
end

Public Instance Methods

call(env) click to toggle source

Accepts a Rack env hash, routes POST requests to the root URL, and returns a Rack response triple.

If request isn’t to the root URL, a ‘404 Not Found` response is returned. If request verb isn’t GET, a ‘405 Method Not Allowed` response is returned.

# File lib/shrine/plugins/upload_endpoint.rb, line 84
def call(env)
  request = Rack::Request.new(env)

  status, headers, body = catch(:halt) do
    error!(404, "Not Found") unless ["", "/"].include?(request.path_info)
    error!(405, "Method Not Allowed") unless request.post?

    handle_request(request)
  end

  headers = Rack::Headers[headers] if Rack.release >= "3"
  headers["Content-Length"] ||= body.respond_to?(:bytesize) ? body.bytesize.to_s :
                                                              body.map(&:bytesize).inject(0, :+).to_s

  [status, headers, body]
end
inspect() click to toggle source
# File lib/shrine/plugins/upload_endpoint.rb, line 101
def inspect
  "#<#{@shrine_class}::UploadEndpoint(:#{@storage_key})>"
end
Also aliased as: to_s
to_s()
Alias for: inspect

Private Instance Methods

error!(status, message) click to toggle source

Used for early returning an error response.

# File lib/shrine/plugins/upload_endpoint.rb, line 214
def error!(status, message)
  throw :halt, [status, { "Content-Type" => CONTENT_TYPE_TEXT }, [message]]
end
get_context(request) click to toggle source

Returns a hash of information containing ‘:action` and `:request` keys, which is to be passed to `Shrine#upload`. Calls `:upload_context` option if given.

# File lib/shrine/plugins/upload_endpoint.rb, line 153
def get_context(request)
  context = { action: :upload }
  context.merge! @upload_context.call(request) if @upload_context
  context
end
get_io(request) click to toggle source

Retrieves the upload from the request and verifies it.

# File lib/shrine/plugins/upload_endpoint.rb, line 120
def get_io(request)
  file = get_multipart_upload(request)

  verify_size!(file, request)
  verify_checksum!(file, request)

  file
end
get_multipart_upload(request) click to toggle source

Retrieves the file from “file” or “files[]” multipart POST param, and converts it into an IO-like object that can be passed to ‘Shrine#upload`.

# File lib/shrine/plugins/upload_endpoint.rb, line 131
def get_multipart_upload(request)
  if request.params.key?("file")
    value = request.params["file"]
  elsif request.params["files"].is_a?(Array)
    error!(400, "Too Many Files") if request.params["files"].count > 1
    value = request.params["files"].first
  end

  error!(400, "Upload Not Found") if value.nil?

  if value.is_a?(Hash) && value[:tempfile]
    @shrine_class.rack_file(value)
  elsif %i[read rewind eof? close].all? { |m| value.respond_to?(m) }
    value
  else
    error!(400, "Upload Not Valid")
  end
end
handle_request(request) click to toggle source

Accepts a ‘Rack::Request` object, uploads the file, and returns a Rack response.

# File lib/shrine/plugins/upload_endpoint.rb, line 110
def handle_request(request)
  io      = get_io(request)
  context = get_context(request)

  uploaded_file = upload(io, context, request)

  make_response(uploaded_file, request)
end
make_response(uploaded_file, request) click to toggle source

Transforms the uploaded file object into a JSON response. It returns a Rack response triple - an array consisting of a status number, hash of headers, and a body enumerable. If a ‘:rack_response` option is given, calls that instead.

# File lib/shrine/plugins/upload_endpoint.rb, line 174
def make_response(uploaded_file, request)
  if @rack_response
    @rack_response.call(uploaded_file, request)
  else
    if @url
      url  = resolve_url(uploaded_file, request)
      body = { data: uploaded_file, url: url }.to_json
    else
      body = uploaded_file.to_json
    end

    [200, { "Content-Type" => CONTENT_TYPE_JSON }, [body]]
  end
end
resolve_url(uploaded_file, request) click to toggle source
# File lib/shrine/plugins/upload_endpoint.rb, line 189
def resolve_url(uploaded_file, request)
  case @url
  when true then uploaded_file.url
  when Hash then uploaded_file.url(**@url)
  else           @url.call(uploaded_file, request)
  end
end
upload(io, context, request) click to toggle source

Calls ‘Shrine#upload` with the given IO and context, and returns a `Shrine::UploadedFile` object. If `:upload` option is given, calls that instead.

# File lib/shrine/plugins/upload_endpoint.rb, line 162
def upload(io, context, request)
  if @upload
    @upload.call(io, context, request)
  else
    uploader.upload(io, **context)
  end
end
uploader() click to toggle source

Returns the uploader around the specified storage.

# File lib/shrine/plugins/upload_endpoint.rb, line 219
def uploader
  @shrine_class.new(@storage_key)
end
verify_checksum!(file, request) click to toggle source

Verifies the provided checksum against the received file.

# File lib/shrine/plugins/upload_endpoint.rb, line 202
def verify_checksum!(file, request)
  return unless request.env.key?("HTTP_CONTENT_MD5")

  provided_checksum = request.env["HTTP_CONTENT_MD5"]

  error!(400, "The Content-MD5 you specified was invalid") if provided_checksum.length != 24

  calculated_checksum = Digest::MD5.file(file.path).base64digest
  error!(460, "The Content-MD5 you specified did not match what was recieved") if provided_checksum != calculated_checksum
end
verify_size!(file, request) click to toggle source
# File lib/shrine/plugins/upload_endpoint.rb, line 197
def verify_size!(file, request)
  error!(413, "Upload Too Large") if @max_size && file.size > @max_size
end