Allowed HTTP Request Header Types
About
I'm trying to build a reverse proxy but it seems that there are some constraints on the headers that we can add to a HttpRequestMessage.
open System.Net.Http
let client = new HttpClient()
let request = new HttpRequestMessage(HttpMethod.Post, "https://example.com")
let tryAddHeader (message : HttpRequestMessage) (k : string) (v : string) =
match message.Headers.TryAddWithoutValidation(k,v) with
| true -> ()
| false -> eprintfn $"Failed to add header: {k}."
let addHeader (message : HttpRequestMessage) (k : string) (v : string) =
message.Headers.Add(k,v)
tryAddHeader request "Content-Type" "text/plain" // XXX: Failed to add header: Content-Type.
tryAddHeader request "Content-Length" "7" // XXX: Failed to add header: Content-Length.
addHeader request "Content-Length" "7" // XXX: throws an exception.
let response = client.Send(request)Allowed Headers
The TryAddWithoutValidation will verify whether the header is allowed before
adding it.
public bool TryAddWithoutValidation(string name, string? value) =>
TryGetHeaderDescriptor(name, out HeaderDescriptor descriptor) &&
TryAddWithoutValidation(descriptor, value);
The TryGetHeaderDescriptor will categorize the Content-Type and
Content-length as a Content and that's not an allowed header type.
// header type.
{
[Flags]
internal enum HttpHeaderType : byte
{
General = 1,
Request = 2,
Response = 4,
Content = 8,
Custom = 16, // 0x10
NonTrailing = 32, // 0x20
All = NonTrailing | Custom | Content | Response | Request | General, // 0x3F
None = 0,
}
}Message.Content.Headers
I investigated the yarp project to understand how they were proxying the headers
and learned that these headers can be added by using methods on the
req.Content.Headers object.
open System.IO
open System.Net.Http
let client = new HttpClient()
let ms =
let bytes = "helloworld" |> System.Text.Encoding.UTF8.GetBytes
new MemoryStream(bytes)
let buildMessage () =
let req = new HttpRequestMessage(HttpMethod.Post, "http://localhost:1234")
req.Content <- new StreamContent(ms)
req.Content.Headers.TryAddWithoutValidation("Content-Type", "application/augustfeng") |> ignore
req.Content.Headers.TryAddWithoutValidation("Content-Length", "10") |> ignore
req
let message = buildMessage()
let response = client.Send(message)
0
The req.Content.Headers object is an instance of a HttpContentHeaders class
whereas the req.Headers is an HttpRequestHeaders object.