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.

httpHeader trait

Binds a structure member to an HTTP header.

// Sent in the X-Foo header
@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]

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-Foo'] = input[:foo].join(',')
  end
end

For parsing, the value is split by comma and set on the structure.

class Operation
  def self.parse(http_resp)
    ...
    data = Types::OperationOutput.new
    data.foo = http_req.headers['X-Foo'].split(',')
  end
end

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

httpLabel trait

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

@required
@httpLabel
foo: String

The required value is inserted into the URI. See the HTTP trait uri section.

httpPayload trait

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

@httpPayload
content: Blob

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

class Operation
  def self.build(http_req, input:)
    ...
    body = Base64.encode64(input[:content])
    http_req.body = StringIO.new(body)
  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
    data.content = Base64.decode64(http_resp.body)
    data
  end
end

httpPrefixHeaders trait

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

@httpPrefixHeaders("X-Foo-")
headers: 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:

input[:headers].each do |key, val|
  http_req.headers["X-Foo-#{key}"] = val
end

httpQuery trait

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

@httpQuery("color")
color: String

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

http_req.append_query_param('color', input[:color])

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:)
    input[:my_params].each do |key, value|
      http_req.append_query_param(key, value)
    end
    # if map had 'color', it's set correctly here
    http_req.append_query_param('color', input[:color])
  end
end

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)

cors trait

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