Skip to content

HTTP Binding Traits

Juli Tera edited this page Apr 19, 2024 · 11 revisions

This wiki contains a mapping between Smithy HTTP binding traits and generated Ruby code.

To support all of these HTTP traits, Hearth provides the following components:

  • HTTP request, response, and header objects
  • HTTP transfer client (Net::HTTP)
  • HTTP request building utilities

http trait

Configures the HTTP bindings of an operation. The http trait has 3 properties: method, uri, and code.

@http(method: "PUT", uri: "/{bucketName}/{key}", code: 200)

See Smithy Documentation for a full breakdown of details.

method

This value represents the actual HTTP method. The method is set onto the HTTP request object in the builder’s top level input structure for the operation.

http_req.http_method = 'PUT'

uri

This value represents the URI pattern of the operation. The uri is appended to the client’s endpoint.

Literal character sequences

For patterns without labels, it is appended to the path of the request.

http_req.append_path('/%<bucketName>s/%<key>s')

Labeled patterns

The uri string will need its values substituted in with labels. Parameters used for the URI are considered required. The Kernel format method is used to substitute the values.

http_req.append_path(format('/%<bucketName>s/%<key>s',
  bucketName: Hearth::HTTP.uri_escape(input[:bucket_name].to_s),
  key: Hearth::HTTP.uri_escape(input[:key].to_s)
))

Query string literals

Query string literals do not contain labels, and are appended to the request.

Given the following Smithy operation:

@http(uri: "/someUri?foo=bar", method: "GET")
operation ConstantAndVariableQueryString {
    ...
}

The generated code is:

http_req.http_method = 'GET'
CGI.parse('foo=bar').each do |k,v|
  v.each { |q_v| http_req.append_query_param(k, q_v) }
end

Greedy labels

The Greedy label must be a string type. These labels are substituted similarly to other labels but "/" is not escaped.

code

This value represents the status code of a successful response. The value is code generated into the Client’s operation on the error parser. The error parser will explicitly check for this status code before determining if an error occurred.

stack.use(
  Hearth::Middleware::Parse,
  error_parser: Hearth::HTTP::ErrorParser.new(
    error_module: Errors,
    success_status_code: 200, errors: [Errors::UnprocessableEntityError])
  data_parser: Parsers::UpdateHighScore
)

httpError trait

Defines an HTTP response code for an operation error.

@error("client")
@httpError(404)
structure MyError {}

This trait does not influence client code generation. Errors are handled by error codes and not status codes.

See Smithy Documentation for more details.

httpHeader trait

Binds a structure member to an HTTP header.

// Sent in the X-Foo header
structure MyOperationInputOutput {
    @httpHeader("X-Foo")
    foo: String
}

The header is set on the HTTP request object in the protocol builder.

http_req.headers['X-Foo'] = input[:foo] unless input[:foo].nil? || input[:foo].empty?

When the trait is applied to a list or set, the protocol builder will append the values to the header with commas.

class Operation
  def self.build(http_req, input:)
    ...
    http_req.headers['X-List'] = input[:foo].join(',')
  end
end

For parsing, the value is split by using Hearth::Http::HeaderListParser.

class Operation
  def self.parse(http_resp)
    data = Types::OperationOutput.new
    unless http_resp.headers['X-List'].nil? || http_resp.headers['X-List'].empty?
          data.foo = Hearth::Http::HeaderListParser.parse_string_list(http_resp.headers['X-List'])
    end
    ...
  end
end

String values with the @mediaType trait are Base64 encoded. Timestamp values are serialized using http-date unless the @timestampFormat trait is applied.

See Smithy Documentation for more details.

httpLabel trait

Binds an operation input structure member to an HTTP label to be used as part of the URI.

structure MyOperationInput {
    @required
    @httpLabel
    foo: String
}

The generated code is:

class MyOperation
  def self.build(http_req, input:)
    ...
    if input[:foo].to_s.empty?
      raise ArgumentError, "HTTP label :foo cannot be empty."
    end
    http_req.append_path(format(
      '/MyOperation/%<foo>s',
            foo: Hearth::HTTP.uri_escape(input[:foo].to_s)
      )
    )
    ...
  end
end

The required value is inserted into the URI. See the HTTP Trait - URI section.

See Smithy Documentation for more details.

httpPayload trait

Binds a single structure member to the body of an HTTP message.

structure MyOperationInputOutput {
    @httpPayload
    blob: Blob
}

When building the request, the blob is set to the request body.

class Operation
  def self.build(http_req, input:)
    ...
    http_req.body = StringIO.new(input[:blob] || '')
  end
end

For parsing, the entire response body is set on the output structure.

class Operation
  def self.parse(http_resp)
    data = Types::OperationOutput.new
    payload = http_resp.body.read
    data.blob = payload unless payload.empty?
    data
  end
end

See Smithy Documentation for more details.

httpPrefixHeaders trait

Binds a map of key-value pairs to prefixed HTTP headers.

structure MyOperationInput {
    @httpPrefixHeaders("X-Foo-")
    fooMap: StringMap,
}

map StringMap {
    key: String,
    value: String
}

For each key-value pair, the headers are set on the HTTP request object in the protocol builder. The generated code is:

# builders.rb
class MyOperation
  def self.build(http_req, input:)
    ...
    input[:foo_map]&.each do |key, value|
      http_req.headers["X-Foo-#{key}"] = value unless value.nil? || value.empty?
    end
  end
end

See Smithy Documentation for more details.

httpQuery trait

Binds an operation input structure member to a query string parameter.

structure MyOperationInput {
    @httpQuery("baz")
    baz: String,

    @httpQuery("maybeSet")
    maybeSet: String,
}

The query parameter is appended to the HTTP request URI in the protocol builder. The generated code is:

class ConstantAndVariableQueryString
  def self.build(http_req, input:)
    ...
    params = Hearth::Query::ParamList.new
    params['baz'] = input[:baz].to_s unless input[:baz].nil?
    params['maybeSet'] = input[:maybe_set].to_s unless input[:maybe_set].nil?
  end
end

See Smithy Documentation for more details.

httpQueryParams trait

Binds a map of key-value pairs to query string parameters.

structure ListThingsInput {
    @httpQueryParams()
    myParams: MapOfStrings,

    @httpQuery("color")
    color: String
}

map MapOfStrings {
    key: String,
    value: String
}

For an Input shape, the keys and values of my_params are iterated and appended to the query string. Query params set this way are done before individual httpQuery bindings, so that user data from the map does not wipe away necessary bindings. The generated code is:

class ListThings
  def self.build(http_req, input:)
    ...
    params = Hearth::Query::ParamList.new
    unless input[:my_params].nil? || input[:my_params].empty?
      input[:my_params].each do |k, v|
        params[k] = v.to_s unless v.nil?
      end
    end
    params['color'] = input[:color].to_s unless input[:color].nil?
    http_req.append_query_param_list(params)
  end
end

See Smithy Documentation for more details.

httpResponseCode trait

Binds a structure member to the HTTP response status code so that an HTTP response status code can be set dynamically at runtime to something other than code of the http trait.

structure HttpResponseCodeOutput {
  @httpResponseCode
  Status: Integer
}

The generated code is:

class HttpResponseCode
  def self.parse(http_resp)
    data = Types::HttpResponseCode.new
    data.status = http_resp.status_code
    data
  end
end

httpChecksumRequired trait

Indicates that an operation requires a checksum in its HTTP request. By default, the checksum used for a service is a MD5 checksum passed in the Content-MD5 header. The checksum is calculated and applied to the header in the request builders. The Hearth::Checksums module is used for calculating checksums.

@httpChecksumRequired
operation PutSomething {
    input: PutSomethingInput,
    output: PutSomethingOutput
}

The generated code is:

# client.rb in some operation
stack.use(Hearth::HTTP::Middleware::ContentMD5)

See Smithy Documentation for more details.

cors trait

Defines how a service supports cross-origin resource sharing. This trait does not influence client code generation.

See Smithy Documentation for more details.

Clone this wiki locally