locked
How to send POST requests with Sockets?

    Question

  • how can i use Windows.Networking.Sockets to make a POST request to, let's say, microsoft.com.

    i tried MessageWebSocket and StreamWebSocket but i only get them working with the GET method.

    i also tried to reinvent the wheel and used the StreamSocket, where i used "POST http://www.microsoft.com HTTP/1.1" as my first line, but i couldn't read the response?

    if you have a working example of code / app please share.

    if you're asking why i'm doing this: because the XHR does not allow access to some headers, like cookies.

    UPDATE: i made it work but i can't get the input stream size. any ideas? the official examples work only in the case where you write the size being sent on the server side, as a Int (like) in the stream or the case in which the stream is to a local file. I don't have access to this so i'm looking for another way to read the lenght of the input stream.

    here is some code from my app:

    socket.writer.writeString(streamContent);
    socket.writer.storeAsync().done(function () {
          socket.writer.flushAsync().done(function () {
                socket.writer.detachStream();
                readResponse();
          });
    });

    where streamContent is a POST request to a HTTP server.

    now the read input stream function:

    function readResponse() {
            var SIZE = ???
            socket.reader.loadAsync(SIZE).done(function () {
                    var str = socket.reader.readString(socket.reader.UnconsumedBufferLength);
                    return str;
            });
    }

    how can i get the SIZE of the response? (if I give SIZE the value, let's say, 400, i see part of my response - so i know my request works)
    any ideas?

    PS: my writer and reader are declared this way (upon succesfully connecting the socket to the server):

    socket.writer = new Windows.Storage.Streams.DataWriter(socket.clientSocket.outputStream);
    socket.reader = new Windows.Storage.Streams.DataReader(socket.clientSocket.inputStream);


    Thursday, September 13, 2012 10:52 PM

Answers

  • In addition to Matt's suggestion, you could use the following code approach to perform a recursive read. Note that when the unconsumedBufferLength is less than the requested size of LoadAsync, that will indicate that the server side has sent what it was supposed to send.

    You probably also need to implement the HTTP protocol by yourself in terms of handling chunked encoded responses and also deal with timeout errors if the web server does not send the terminating chunk.

    For a very simple example, here is the code approach. I have used a HTTP GET method for simplicity, but a POST request is similar, especially since your question was around how to read the response.

        var clientSocket = null;
        var strOutputBuffer = "";
        function btnUseSocketClickHandler() {  // this is your button's click event
            var strName = "www.microsoft.com";
            var strPath = "/";
            var serverHostName = new Windows.Networking.HostName(strName);
            clientSocket = new Windows.Networking.Sockets.StreamSocket();
            clientSocket.connectAsync(serverHostName, "80").done(function () {
                var writer = new Windows.Storage.Streams.DataWriter(clientSocket.outputStream);
                var string = "GET "+strPath+" HTTP/1.1\r\nHost: "+strName+"\r\nUser-Agent: testAgent\r\n";
                string += "\r\n";
                writer.writeString(string);
                writer.storeAsync().done(function() {
                    writer.detachStream();
                    startReadingResponse(300); // here we start the read operation
                }, function () {
                    var error = "handle write error here";
                });

            }, function () {
                var error = "handle connection error here";
            });
        }

        function startReadingResponse(iSize) {
            var reader = new Windows.Storage.Streams.DataReader(clientSocket.inputStream);

            reader.inputStreamOptions = Windows.Storage.Streams.InputStreamOptions.partial;
            reader.loadAsync(iSize).then(function () {
                var iUnConsumed = reader.unconsumedBufferLength;
                strOutputBuffer += reader.readString(iUnConsumed);
                if (iUnConsumed < iSize) {
                    reader.close();
                    // here is where you can consume your strOutputBuffer as the complete HTTP response.
                } else {
                    // perform a recursive read until the unconsumedBufferLength is
                    // less than the requested length. That will be the terminating condition for the recursion
                    startReadingResponse(iSize);
                }
            }, function () {
                var error = "handle reading error here";
            }, function () {
                var progress = "read progress here";
            });
        }

    Btw, the MessageWebSocket & StreamWebSocket classes you are trying to use are only for the WebSocket protocol, not for the HTTP protocol. For handling raw sockets, you need to use the StreamSocket - for TCP connections and DatagramSocket for UDP connections...


    Wednesday, September 19, 2012 12:51 AM
    Moderator

All replies

  • The response should have a Content-Length header with the length of the server response.

    David Lamb

    Monday, September 17, 2012 11:57 PM
    Moderator
  • The content-length header would only have the length of the response body (not including the headers), not the whole size of the input stream, which i need for the SIZE var.
    Tuesday, September 18, 2012 9:02 AM
  • If there's nowhere to find the total length of the input stream before it's completely received, then you'll have to completely download it first, right? You may have to read it in in chunks with options set to partial before finding the end of the response.  I'm not sure offhand what happens when you try to read past the end of the stream length either, but I think it would likely be easy for you to figure out if this would work.  I don't think there's an elegant solution to this.

    Matt Small - Microsoft Escalation Engineer - Forum Moderator
    If my reply answers your question, please mark this post as answered.

    NOTE: If I ask for code, please provide something that I can drop directly into a project and run (including XAML), or an actual application project. I'm trying to help a lot of people, so I don't have time to figure out weird snippets with undefined objects and unknown namespaces.

    Tuesday, September 18, 2012 5:57 PM
    Moderator
  • In addition to Matt's suggestion, you could use the following code approach to perform a recursive read. Note that when the unconsumedBufferLength is less than the requested size of LoadAsync, that will indicate that the server side has sent what it was supposed to send.

    You probably also need to implement the HTTP protocol by yourself in terms of handling chunked encoded responses and also deal with timeout errors if the web server does not send the terminating chunk.

    For a very simple example, here is the code approach. I have used a HTTP GET method for simplicity, but a POST request is similar, especially since your question was around how to read the response.

        var clientSocket = null;
        var strOutputBuffer = "";
        function btnUseSocketClickHandler() {  // this is your button's click event
            var strName = "www.microsoft.com";
            var strPath = "/";
            var serverHostName = new Windows.Networking.HostName(strName);
            clientSocket = new Windows.Networking.Sockets.StreamSocket();
            clientSocket.connectAsync(serverHostName, "80").done(function () {
                var writer = new Windows.Storage.Streams.DataWriter(clientSocket.outputStream);
                var string = "GET "+strPath+" HTTP/1.1\r\nHost: "+strName+"\r\nUser-Agent: testAgent\r\n";
                string += "\r\n";
                writer.writeString(string);
                writer.storeAsync().done(function() {
                    writer.detachStream();
                    startReadingResponse(300); // here we start the read operation
                }, function () {
                    var error = "handle write error here";
                });

            }, function () {
                var error = "handle connection error here";
            });
        }

        function startReadingResponse(iSize) {
            var reader = new Windows.Storage.Streams.DataReader(clientSocket.inputStream);

            reader.inputStreamOptions = Windows.Storage.Streams.InputStreamOptions.partial;
            reader.loadAsync(iSize).then(function () {
                var iUnConsumed = reader.unconsumedBufferLength;
                strOutputBuffer += reader.readString(iUnConsumed);
                if (iUnConsumed < iSize) {
                    reader.close();
                    // here is where you can consume your strOutputBuffer as the complete HTTP response.
                } else {
                    // perform a recursive read until the unconsumedBufferLength is
                    // less than the requested length. That will be the terminating condition for the recursion
                    startReadingResponse(iSize);
                }
            }, function () {
                var error = "handle reading error here";
            }, function () {
                var progress = "read progress here";
            });
        }

    Btw, the MessageWebSocket & StreamWebSocket classes you are trying to use are only for the WebSocket protocol, not for the HTTP protocol. For handling raw sockets, you need to use the StreamSocket - for TCP connections and DatagramSocket for UDP connections...


    Wednesday, September 19, 2012 12:51 AM
    Moderator
  • @pphadke: I tried your method and it works well on HTTP/1.0 (no chunks) :)

    thanks

    Sunday, September 23, 2012 12:27 AM