HttpWebRequest, its request stream, and sending data in chunks

I have recently been spending a great deal of time writing code that communicated with RESTful services (Microsoft’s Live Framework, in particular). Towards this end, I’m using the HttpWebRequest class to communicate and, for scalability and responsivenss, I want to perform my I/O operations asynchronously. This has caused me to look into the [Begin]GetRequestStream methods. At first it seemed quite odd to me that there would be a need to get the request stream asynchronously. Afterall, getting the stream just didn’t seem like an I/O operation to me. However, via experimentation, I did notice that acquiring the stream did cause some I/O operations. This led me to start some communication with people at Microsoft to really get to the bottom of what is happenning. The information they shared was certainly news to me and I have never seen it documented anywhere. And so now, I’d like to share this information with you too as I think it is very useful for anyone using the HttpWebRequest class to send request data.

There are basically 3 ways to make an HTTP request that contains a payload:

  1. If the payload is amll, you can call [Begin]GetRequestStream and it will immediately return a memory-backed stream that buffers the request payload. Then, when you call [Begin]GetResponse, the HttpWebRequest object calculates the ContentLength header based on how much data was written to the stream and then it initiates the I/O.
  2. If the payload is large (2GB, for example), then you shouldn’t cache the whole payload in memory before sending it. In this case, you’d set the ContentLength property explicitly to the payload size. Then when you calll [Begin]GetRequestStream, it now performs all of the I/O up to the point of sending the payload. You now write your payload to the stream which now sends its directly to the network (it does not get sent to an in-memory stream). When you call [Begin]GetResponse, it checks that the amount of data sent matches what you specified for the ContentLength property and does no additional I/O in order to start receiving the response.
  3. If the payload is potentially large, generated at runtime, and you don’t know the its size ahead of time then you set HttpWebRequest’s SendChunked property to true (and do not set the ContentLength property). When you call [Begin]GetRequestStream, all of the I/O is performed up to the point of sending the payload data (including a “Trasnsfer-Encoding: Chunked” header). You now write to the stream in chunks and these writes go directly to the network. When you close the stream, a special terminating chunk is sent to notify the server that the payload is done. When you call [Begin]GetResponse, no I/O is performed and you can start receiving the response.

I hope you find this as useful as I did. Now, I can make intelligent decisions about my payload data, its size, how to buffer it, how to set headers, and when I can perform I/O synchronously as opposed to asynchronously. I really feel empowered with this knowledge.

Jeffrey Richter

View Comments

  • Good to note that if you use GetRequestStream (instead of BeginGetRequestStream), then BeginGetResponse will be a synchronous operation. Took me a while to figure that out since the documentation wasn't completely clear.

  • fifth Edition
    in Chapter 19, there is one note:
    Note: It is important to realize that a single address space consists of one executable module and several DLL modules. Some of these modules can link to a static version of the C/ C++ run-time library, some of these modules might link to a DLL version of the C/C++ run-time library, and some of these modules (if not written in C/C++) might not require the C/ C++ run-time library at all. Many developers make a common mistake because they forget that several C/C++ run-time libraries can be present in a single address space. Examine the following code:
    VOID EXEFunc() {
    PVOID pv = DLLFunc();
    // Access the storage pointed to by pv...
    // Assumes that pv is in EXE's C/C++ run-time heap
    free(pv);
    }
    PVOID DLLFunc() {
    // Allocate block from DLL's C/C++ run-time heap
    return(malloc(100));
    }
    So, what do you think? Does the preceding code work correctly? Is the block allocated by the DLL's function freed by the EXE's function? The answer is: maybe. The code shown does not give you enough information. If both the EXE and the DLL link to the DLL C/C++ run-time library, the code works just fine. However, if one or both of the modules link to the static C/C++ run-time library, the call to free fails. I have seen developers write code similar to this too many times, and it has burned them all.
    ===========================================================================
    At first one thing the starup() function do is to initialize a heap used by malloc and free and low-level IO manipulation use in CRT, so when there is two copies CRT liberary in one process adress and there will be two heaps for two groups of malloc and free, and that one block memory grabed by malloc can only be freed by the very free() function in the same copy.
    but what free() is?
    in fact free()'s source code is like this:
    void free(void *ptr)
    {
    struct mem_control_block *free;
    free = ptr - sizeof(struct mem_control_block);
    free->is_available = 1;
    return;
    }
    every malloc()function return pointer to a block,however there are some bytes ahead the block conserved for system information, and this information is stored in a struct mem_control_block, so indeed the pointer malloc()function returned points to the byte after the mem_control_block.
    struct mem_control_block {
    int is_available; //a flag that specify whether this block can be used by any other application
    int size; //the size of the block
    };
    finnally, it seems no error in these code:
    VOID EXEFunc() {
    PVOID pv = DLLFunc();
    // Access the storage pointed to by pv...
    // Assumes that pv is in EXE's C/C++ run-time heap
    free(pv);
    }
    PVOID DLLFunc() {
    // Allocate block from DLL's C/C++ run-time heap
    return(malloc(100));
    }
    if the system doesn't allow a free() to free a block in heap of another copy of CRT, then how the system is able to do it?
    Thanks for your help.
    I'm struggle reading
    Jack

  • Good points about HttpWebRequest. Another way to avoid buffering in HWR is to set the property HttpWebRequest.AllowWriteStreamBuffering=false. This way, the request wont buffer the request payload, no matter what the size of the payload.

  • This is code for upload file via httprequest
    static Stream formDataStream = null;
    private void button3_Click(object sender, EventArgs e)
    {
    DialogResult result = this.openFileDialog1.ShowDialog();
    if (result == DialogResult.OK)
    {
    string fileUrl = openFileDialog1.FileName;
    HttpWebRequest myreq =
    (HttpWebRequest)WebRequest.Create("http://www.ssss.com/video.xml");
    string boundaryString = "----WebKitFormBoundaryrqinj7B0Pj91NMg9";
    // Set the http request header \
    myreq.Method = WebRequestMethods.Http.Post;
    myreq.ContentType = "multipart/form-data; boundary=" + boundaryString;
    myreq.KeepAlive = true;
    myreq.Credentials = new NetworkCredential("username", "passwd");
    formDataStream = getFormDataStream(boundaryString, fileUrl); //get the stream to send via http POST
    myreq.ContentLength = formDataStream.Length;
    // start the asynchronous operation
    myreq.BeginGetRequestStream(new AsyncCallback(GetRequestStreamCallback), myreq);
    }
    }
    private static void GetRequestStreamCallback(IAsyncResult asynchronousResult)
    {
    HttpWebRequest request = (HttpWebRequest)asynchronousResult.AsyncState;
    // End the operation
    Stream reqStream = request.EndGetRequestStream(asynchronousResult);
    formDataStream.Position = 0;
    byte[] buffer = new byte[1024];
    for (int len; (len = formDataStream.Read(buffer, 0, buffer.Length)) > 0; )
    {
    reqStream.Write(buffer, 0, len);
    }
    // Start the asynchronous operation to get the response
    request.BeginGetResponse(new AsyncCallback(GetResponseCallback), request);
    }
    private static void GetResponseCallback(IAsyncResult asynchronousResult)
    {
    HttpWebRequest request = (HttpWebRequest)asynchronousResult.AsyncState;
    // End the operation
    HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(asynchronousResult);
    Stream streamResponse = response.GetResponseStream();
    }
    But i am getting the error
    Message=The underlying connection was closed: An unexpected error occurred on a receive. Message=An established connection was aborted by the software
    in your host machine
    on this line
    byte[] buffer = new byte[1024];
    for (int len; (len = formDataStream.Read(buffer, 0, buffer.Length)) > 0; )
    {
    reqStream.Write(buffer, 0, len);
    }
    I tried the same code with setting KeepAlive as False, but no change in response. What i want is need to upload a large file as small chunks into server via http post from a desktop application
    FormDataStream contains the whole data to send and while i am trying to write the content to httprequest i am getting this error,
    I follow the comments on 2nd point from thi s article

  • I have an application and it seems we have no size limit to the files to be sent whant should we use for small payloads and what is ok to send via non-chunked channel

  • Even though httpwebrequest is probably depreciated, this is some really interesting info that I was curious and confused about; thanks.

Recent Posts

8-Step AWS to Microsoft Azure Migration Strategy

Microsoft Azure and Amazon Web Services (AWS) are two of the most popular cloud platforms.…

2 weeks ago

How to Navigate Azure Governance

 Cloud management is difficult to do manually, especially if you work with multiple cloud…

3 weeks ago

Why Azure’s Scalability is Your Key to Business Growth & Efficiency

Azure’s scalable infrastructure is often cited as one of the primary reasons why it's the…

1 month ago

Unlocking the Power of AI in your Software Development Life Cycle (SDLC)

https://www.youtube.com/watch?v=wDzCN0d8SeA Watch our "Unlocking the Power of AI in your Software Development Life Cycle (SDLC)"…

2 months ago

The Role of FinOps in Accelerating Business Innovation

FinOps is a strategic approach to managing cloud costs. It combines financial management best practices…

2 months ago

Azure Kubernetes Security Best Practices

Using Kubernetes with Azure combines the power of Kubernetes container orchestration and the cloud capabilities…

2 months ago