Копирование Http-запроса InputStream

Я реализую метод действия прокси, который пересылает входящий веб-запрос и перенаправляет его на другую веб-страницу, добавляя несколько заголовков. Метод действия работает с файлом для запросов GET, но я все еще борюсь с пересылкой входящего запроса POST.

Проблема в том, что я не знаю, как правильно записать тело запроса в исходящий поток HTTP-запросов.

Вот сокращенная версия того, что у меня есть на данный момент:

//the incoming request stream
var requestStream=HttpContext.Current.Request.InputStream;
//the outgoing web request
var webRequest = (HttpWebRequest)WebRequest.Create(url);
...

//copy incoming request body to outgoing request
if (requestStream != null && requestStream.Length>0)
            {
                long length = requestStream.Length;
                webRequest.ContentLength = length;
                requestStream.CopyTo(webRequest.GetRequestStream())                    
            }

//THE NEXT LINE THROWS A ProtocolViolationException
 using (HttpWebResponse response = (HttpWebResponse)webRequest.GetResponse())
                {
                    ...
                }

Как только я вызываю GetResponse по исходящему http-запросу, я получаю следующее исключение:

ProtocolViolationException: You must write ContentLength bytes to the request stream before calling [Begin]GetResponse.

Я не понимаю, почему это происходит, поскольку requestStream.CopyTo должен был позаботиться о записи нужного количества байтов.

Любые предложения будут ценны.

Спасибо,

Адриан


person Adrian Grigore    schedule 10.08.2010    source источник
comment
связанный вопрос - stackoverflow .com / questions / 226784 /.   -  person James Manning    schedule 10.08.2010
comment
@ Джеймс Мэннинг: Спасибо за ссылку, но я уже прошел через это. Мой прокси отлично работает для всех типов запросов GET. У меня все еще возникают проблемы с телом запроса POST.   -  person Adrian Grigore    schedule 10.08.2010
comment
Вы пробовали вызвать Stream.Flush () в потоке, возвращаемом webRequest.GetRequestStream (), прежде чем переходить к вызову webRequest.GetResponse ()?   -  person Mattias S    schedule 10.08.2010
comment
@Mattias S: Я только что сделал, но, похоже, это не имеет никакого значения.   -  person Adrian Grigore    schedule 10.08.2010
comment
в целях отладки я бы, вероятно, изменил его, чтобы записать поток в промежуточный массив байтов (поток памяти, затем массив), проверить его содержимое и длину, а затем записать массив байтов. Кроме того, IMHO, вы должны сделать webRequest.GetRequestStream (), назначенный локальной переменной с использованием, чтобы вы закрывали поток запроса перед записью, поэтому что-то вроде using (var rs = webRequest.GetRequestStream ()) {requestStream.CopyTo ( rs); } (или массив байтов, если вы пойдете по этому маршруту). У меня было много ошибок, которые исчезли, как только я действительно избавляюсь от потоков, когда должен (и происходит промывка / закрытие)   -  person James Manning    schedule 11.08.2010


Ответы (3)


Да, .Net очень привередлив в этом. Чтобы решить эту проблему, нужно одновременно очистить и поток. Другими словами:

Stream webStream = null;

try
{
    //copy incoming request body to outgoing request
    if (requestStream != null && requestStream.Length>0)
    {
        long length = requestStream.Length;
        webRequest.ContentLength = length;
        webStream = webRequest.GetRequestStream();
        requestStream.CopyTo(webStream);
    }
}
finally
{
    if (null != webStream)
    {
        webStream.Flush();
        webStream.Close();    // might need additional exception handling here
    }
}

// No more ProtocolViolationException!
using (HttpWebResponse response = (HttpWebResponse)webRequest.GetResponse())
{
    ...
}
person Brian    schedule 12.01.2011
comment
Великий пост, Брайан. Работает как шарм. Однако кажется, что .CopyTo () поддерживается только в .NET 4.0+ - person Sage; 26.10.2011
comment
Спасибо. Это устранило мою проблему. Один вопрос: есть ли причина, по которой вы использовали Flush и Close в блоке finally, а не обертывали webStream в блоке using? Есть ли побочные эффекты в отношении webRequest? - person David; 06.07.2012
comment
Я почти уверен, что промывка ничего не меняет ... GetRequestStream возвращает экземпляр ConnectStream, и этот класс переопределяет Flush с помощью пустого метода, поэтому вызов Flush для него вообще не имеет никакого эффекта. - person Thomas Levesque; 24.01.2014

Ответ @brian работает, однако я обнаружил, что после вызова requestStream.CopyTo (stream) мой HttpWebResponse срабатывает. Это было проблемой, поскольку я не был готов отправить запрос. Итак, если у кого-то возникла проблема с отправкой не всех заголовков запроса или других данных, это потому, что CopyTo запускает ваш запрос.

person Seth    schedule 01.05.2012

попробуйте изменить блок внутри оператора if

long length = requestStream.Length;
webRequest.ContentLength = length;
requestStream.CopyTo(webRequest.GetRequestStream())

с участием

webRequest.Method = "POST";
webRequest.ContentLength = requestStream.Length;
webRequest.ContentType = "application/x-www-form-urlencoded";
Stream stream = webRequest.GetRequestStream();
requestStream.CopyTo(stream);
stream.Close();
person ajay_whiz    schedule 10.08.2010
comment
Я уже получил это (это в ... части). Как я уже сказал, это просто сокращенная версия. Остальное работает нормально, поэтому я не думаю, что это имеет отношение к этой проблеме. - person Adrian Grigore; 10.08.2010