none
"Bad Request" with Redirected POST RRS feed

  • Question

  • I am having trouble with using HttpWebRequest with a POST. When the C# client connects to a C# server that uses the HTTPAPI, the server redirects the client to another URL with a session ID encoded in the URL. However, when the client attempts to connect to the server at the new URL, the client gets a "400 Bad Request (Invalid Verb)" response back. The service is never contacted for the redirected URL (I dump requests to the console, and the redirected request is never dumped). I have a Java client that can connect and be redirected without any "Bad Request" messages. Using "Microsoft Network Monitor 3.3" I see that the C# client does not send the HTTP Body immediately, while the Java client does.

    A really strange quirk of the server side code is that if, in the server, I put a break-point right after receiving an HttpListenerContext, the C# client can connect and the redirected re-connect works properly (no "Bad Request"). What's going on? Is this a bug in the HTTPAPI? or in the HttpWebRequest? How do I fix this without having to put in a break-point in the server?

    Server-Side code
    public static void Start()
    {
        HttpListener listener = new HttpListener();
        listener.AuthenticationSchemes = AuthenticationSchemes.Anonymous;
        listener.Prefixes.Add("http://+:80/listeningUrl/");
        listener.Start();
        listener.BeginGetContext(new AsyncCallback(ListenerCallback), listener);
    }
    
    private static void ListenerCallback(IAsyncResult result)
    {
        HttpListener listener = (HttpListener)result.AsyncState;
        listener.BeginGetContext(new AsyncCallback(ListenerCallback), listener);//proces another request
        HttpListenerContext exchange = listener.EndGetContext(result);
        // Put a breakpoint here, and the client works fine.
        processExchange(exchange); // This will create a session, encode it in the URL and redirect.
    }
    
    Client-Side Code (simplified)

    public static string makeAttempt(String method, String message, HttpWebRequest con)
    {
        con.AllowWriteStreamBuffering = false;
        con.PreAuthenticate = false;
        con.AllowAutoRedirect = true; //Must be true, or the WebException is not thrown.
        try
        {
            return writeRequest(method, message, con);
        }
        catch (WebException ex)
        {
            if (ex.Status == WebExceptionStatus.ProtocolError)
            {
                using (HttpWebResponse response = (HttpWebResponse)ex.Response)
                {
                    if (response.StatusCode == HttpStatusCode.TemporaryRedirect)
                    {
                        return handleRedirect(method, message, con, ex, response);
                    }
                    else if (response.StatusCode == HttpStatusCode.BadRequest)
                    {
                        handleBadRequest(ex, response);
                        throw ex;
                    }
                    else
                        throw ex; //We did not handle it, so just throw it.
                }
            }
            else
                throw ex; //We did not handle it, so just throw it.
        }
    }
    
    private static string handleRedirect(String method, String message, HttpWebRequest con, WebException ex, HttpWebResponse response)
    {
        String value = response.Headers.Get("Location");
        if (value != null)
        {
            value = Uri.EscapeUriString(value);
            Uri newLocation = new Uri(con.RequestUri, value);
            response.Close();
            con = (HttpWebRequest)WebRequest.Create(newLocation);
            //Attempt to POST that message again, to the new URL.
            return makeAttempt(method, message, con);
        }
        else
            throw ex; //We did not handle it, so just throw it.
    }
    
    private static void handleBadRequest(WebException ex, HttpWebResponse response)
    {
        if (response.ContentLength > 0)
        {
            string result = readResponse(response);
            Console.WriteLine(result);
        }
        Console.WriteLine("Received Bad Request!");
    }
    
    Thank you,
    Richard
    Monday, August 31, 2009 6:42 PM

Answers

  • Apparently, if the client expects the POST to be redirected, then it MUST buffer the output (AllowWriteStreamBuffering = false). I, as a developer, cannot take charge of following a redirection myself and force my code to regenerate output on the few occasions that I need it to. Thankfully, I know when redirection is likely to happen so I can turn off buffering when I need to.
    • Marked as answer by Richard Arthur Tuesday, September 1, 2009 12:02 AM
    Tuesday, September 1, 2009 12:02 AM