ADC

Content-length header behavior in a streaming rewrite action

Content-Length header is one of the ways to indicate the length of the message (in bytes) in an HTTP request or response. Apart from the Content-Length header, you can also specify the length of the message by using one of the following ways:

  • Chunked encoding
  • FIN Termination

In a streaming process, the NetScaler sends data continuously after processing the rewrite action. As the data is sent continuously and not held by the NetScaler, the actual length of the message that would be sent to the client is unknown. Therefore, the correct value of the Content-Length header cannot be mentioned in the response.

To support the streaming process, the rewrite feature of NetScaler converts the way to indicate the length of the message from the Content-Length header to the FIN termination. As part of the conversion, the NetScaler corrupts the Content-Length header by rearranging the first four characters of the header name.

In HTTP, the client is expected to ignore headers that it does not understand. So the client does not understand the corrupted Content-Length header name and therefore ignores the header. To improve the performance of the NetScaler, the header is corrupted instead of deleted. Corrupting the header name instead of deleting avoids recomputing the checksum because the checksum is not changed if the same bytes are in a different order.

For example, consider the following HTTP request:

GET / HTTP/1.1
Accept: application/x-ms-application, image/jpeg, application/xaml+xml, image/gif, image/pjpeg, application/x-ms-xbap, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, /
Accept-Language: en-GB
User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; Trident/7.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; CMDTDF; MS-RTC LM 8)
Accept-Encoding: gzip, deflate
Host: test.example.net
Connection: Keep-Alive
<!--NeedCopy-->

In a working scenario, the response between NetScaler and the back-end server for this HTTP request is as follows:

HTTP/1.1 200 OK
Content-Length: 10967
Connection: close
var SERVER_URL = 'https\x3a\x2f\x2ftest.example.net\x2f';
var WEB_SERVER_HOST = 'test.example.net';
<!--NeedCopy-->

But the response received by the client from NetScaler in a non-working scenario is as follows. The Content-Length header is renamed to ntcoent-Length.

HTTP/1.1 200 OK
ntCoent-Length: 10967
nnCoection: close
var SERVER_URL = 'https\x3a\x2f\x2ftest.example.net\x2f';
var WEB_SERVER_HOST = 'test.example.net';
<!--NeedCopy-->

In general, the client applications support all the three ways of transaction - Content-Length header, Chunked encoding, and FIN Termination. So, the conversion from Content-Length header to FIN Termination must not cause any issue. But, if the application doesn’t work because of this modification, then you must Disable the streaming process.

How to disable the streaming process in a rewrite policy

You can disable the streaming process in a rewrite policy using one of the following ways:

  1. Add a non-streaming action associated with a rewrite policy that is bound at a higher priority. The action must be in such a way that it doesn’t modify the response.

    For example:

    add rewrite action non_stream_act replace_all HTTP.RES.BODY(1000000) HTTP.RES.FULL_HEADER -search text("pattern_which_will_not_match_in_body")

    The value of the body in this rewrite action must be more than the value on which the current streaming action is working.

  2. Instead of the streaming configuration, use the non-streaming configuration.

    Note:

    Moving from streaming to non-streaming processing might impact the performance of the NetScaler.

    For example, a streaming configuration can be converted to a non-streaming configuration as follows:

    Streaming Configuration:

    add rewrite action rw_act_1 replace_all HTTP.RES.BODY(1000) "\"http\"" -search text("http")
    
    add policy patset pat_list
    bind policy patset pat_list abcd
    bind policy patset pat_list defg
    
    add rewrite action rw_act_2 replace_all HTTP.RES.BODY(1000) "\"replaced_data\"" -search patset("pat_list")
    <!--NeedCopy-->
    

    Non-streaming configuration:

    add rewrite action rw_act_1 replace_all HTTP.RES.BODY(1000) "\"http\"" -search regex(re/http/)
    
    add rewrite action rw_act_1 replace_all HTTP.RES.BODY(1000) "\"http\"" -search regex(re/abcd|defg/)
    <!--NeedCopy-->
    
Content-length header behavior in a streaming rewrite action