HTTP filters represent dynamic modifications of HttpMessages as they are assembled (written).
A number of key design issues is addressed by the implementation of these filters:
Efficiency
The HTTP classes that will utilize these filters were designed to operate efficiently, above
all else. The most important aspect of this is memory allocations. Ideally, no memory
allocations should be required to assemble and write an HttpMessage. As a result, filters
provide their data to the HttpAssembler as a byte[].
Additionally, prior to their first use, filters provide a map to the assembler that describes where and how much data, from the original (source) HttpMessage, will be replaced by the filter. This allows the assembler to sequentially write the bytes from the source message interspersed with bytes provided by filters.
Some filters can be configured to perform a similar operation on many requests. As a result, the design allows the same filter object to be applied to many sources without duplicating the filter - e.g. it is entirely threadsafe without needing synchronization. Note that this implies that some filters cannot maintain any state information that changes during playback or is specific to the command it is operating on. For example, a filter for host redirection knows which header it operates on (Host), but not the specific placement of that header within the HeaderSet for a particular message.
Flexibility
Filters are able to add, remove or change parts of the source HttpMessage.
Data Sources
Filters can supply data from a variety of sources. This implies that all of these sources
are made available to the filter when data is requested from the filter:
Prioritization
Some filters have effects on other filters. A particularly important example is field-value
filters in the content of a POST request. These filters have a direct effect on the
content-length header for the request, which will require a filter.
The use of a filter during a "playback" operation consists of several steps:
Configuration
Configuration is performed either manually by the user or automatically prior to a test.
When a filter is configured, it learns what part of the HttpMessage it will be operating
upon and how it will retrieve it's dynamic value at runtime. Knowledge of the source
is kept as general possible. For instance, a filter to replace the Hostname header
of a message should not know the exact placement of that header in the raw bytes of the
HttpMessage, because future edits of the message would invalidate the configuration.
Mapping
Prior to it's first use, each filter is mapped onto each source message that it
will be applied to. This operation returns a FilterTarget to that indicates where and how much
of the source message will be replaced by this filter. A FilterMap is created for each
HttpMessage that will be assembled. Since this data is identical for every virtual
user and is stateless, it can be shared (along with the source message) among multiple
simultaneous transactions.
Resolution
At each use, each filter is resolved for each message. During this step,
the filter obtains its target data - the data that will be output for the filter. It
returns a reference to the byte[] containing the replacement data. The filters are
resolved in priority order. User-configured filters are always resolved first and
the Content-length filter is always resolved last. Other automatic filters are
resolved anywhere in-between.