protected virtual SendResult SaveRequestResults(Stream responseStream, TransportSendStats sendStats, SendIterationContext context) { bool needRetry = false; bool serverDbChanged = false; TransportResponseStats responseStats; using (var streamReader = new StreamReader(responseStream)) using (var jsonReader = new JsonTextReader(streamReader)) { var jsonSerializer = new JsonSerializer(); // сохраним потоки данных конфигураций при десериализации // TODO Доработать configurationsConverter var configurationsConverter = new TransportResponseConfigurationsConverter(this.configurationStore); jsonSerializer .Converters .Add(configurationsConverter); var response = jsonSerializer.Deserialize <TransportResponse>(jsonReader); if (response.ErrorMessage != null) { SendResult.Retry(context.TransportSettings.ErrorRetryTimeout); } // изменеилась статическая конфигурация if (response.StaticConfigData != null) { this.agentInfoService.SetStaticConfig(new TransportStaticConfig { ConfigToken = response.StaticConfigData.ConfigToken, ConfigVersion = response.StaticConfigData.ConfigVersion, }); return(SendResult.Retry(TransportConstants.DefaultUpdateStaticConfigTimeout)); } else { // ConfigurationsByteArraysToFilesConverter используется при сохранении конфигураций this.sendStateStore.ConfigurationsUpdated(); // изменилась бд системы (например сменили сервера) if (response.DbTokenData != null) { serverDbChanged = true; this.agentInfoService.SetDbToken(new TransportDbTokenData { DbToken = response.DbTokenData.DbToken }); //return SendResult.Retry(TransportConstants.DefaultUpdateDbTokenTimeout); } responseStats = new TransportResponseStats() { SendedPacketsStats = sendStats.SendedPacketsStats, // изменилась бд системы TransferedPacketsProcessingResults = response.DbTokenData != null ? new List <TransferedPacketStats>() : sendStats.SendedPacketsStats.Select(x => { var transferedPacketResponse = response.TransferedPackets.First(p => p.PacketId == x.Key.Identity.PacketId && p.ProviderKey == x.Key.ProviderKey); // x.Value.PreviousPartIdentity = new PacketPartIdentity(transferedPacketResponse.StorageToken, transferedPacketResponse.Id); return(new TransferedPacketStats { PacketInfo = x.Key, SendStats = x.Value, Result = transferedPacketResponse.Result, }); }).ToList(), }; } } // сохраняем данные var packets = responseStats.TransferedPacketsProcessingResults; if (serverDbChanged) { this.packetManager.RemoveAll(); } else { foreach (var item in packets) { var packet = item.PacketInfo; if (item.Result == PacketProcessingResult.Saved) { this.packetManager.SaveSendStats(packet.ProviderKey, packet.Identity, item.SendStats); } else if (item.Result == PacketProcessingResult.Error) { needRetry = true; } else if (item.Result == PacketProcessingResult.Resend) { // сбрасываем статистику this.packetManager.SaveSendStats(packet.ProviderKey, packet.Identity, new SendStats()); } // игнорим эти результаты (если потом появится обработчик на сервере или обновиться агент до нормальной версии и в нём будет другая обработка, предполагаю что следующий набор данных будет доставлен успешно) //else if (item.Result == PacketProcessingResult.Unknown) //{ //} //else if (item.Result == PacketProcessingResult.NoProcessor) //{ //} else { this.packetManager.SaveSendStats(packet.ProviderKey, packet.Identity, item.SendStats); } } } return(needRetry ? SendResult.Retry(context.TransportSettings.ErrorRetryTimeout) : SendResult.Success(serverDbChanged, packets, sendStats.IgnoredPackets)); }
protected virtual SendResult SendIteration(IList <ITransportPacketInfo> packetInfos, IList <ConfigurationRequestDataItem> configurationsToDownloadInfos, SendIterationContext context) { var url = this.GetUri(this.GetSendMessageHeaders(context)); string formDataBoundary = string.Format("----------{0:N}", Guid.NewGuid()); string contentType = "multipart/form-data; boundary=" + formDataBoundary; // для net40 #if !NETSTANDARD2_0 try { var req = WebRequest.Create(url) as HttpWebRequest; //req.KeepAlive = true; //req.KeepAlive = false; req.AllowWriteStreamBuffering = false; req.SendChunked = true; req.ServicePoint.Expect100Continue = true; req.Method = "POST"; req.ContentType = contentType; //req.ContinueTimeout = xxx; TransportSendStats sendededStats = null; using (Stream requestStream = req.GetRequestStream()) { sendededStats = this.WriteMessageToBody(formDataBoundary, requestStream, packetInfos, configurationsToDownloadInfos, context, () => req.HaveResponse); // для тестирования... //var length = 1 * 1024 * 1024 * 1024; //var batch = 1 * 1024 * 1024; //for (int i = 0; i < length; i += batch) //{ // var batchBuffer = new List<byte>(); // for (int j = 0; j < batch; j++) // { // batchBuffer.Add(0); // } // if (req.HaveResponse) // { // break; // } // requestStream.Write(batchBuffer.ToArray(), 0, batchBuffer.Count); //} } try { using (var response = req.GetResponse()) using (var resStream = response.GetResponseStream()) using (var streamReader = new StreamReader(resStream)) { // обрабатывает ответ return(this.SaveRequestResults(resStream, sendededStats, context)); } } catch (WebException ex) { var wRespStatusCode = ((HttpWebResponse)ex.Response).StatusCode; // кастомная обработка конфигурации (для проблемы когда тело запроса с агента отправляется, хотя в этом нет смысла - лишние данные ходят по сети, это можно исправить) if ((int)wRespStatusCode == 399) { using (var resStream = ((HttpWebResponse)ex.Response).GetResponseStream()) using (var streamReader = new StreamReader(resStream)) { // обрабатывает ответ return(this.SaveRequestResults(resStream, sendededStats, context)); } } if ((int)wRespStatusCode == 429) { var timeout = int.TryParse(ex.Response.Headers.GetValues("Retry-After").FirstOrDefault(), out var retryTm) ? retryTm : Convert.ToInt64(context.TransportSettings.ErrorRetryTimeout.TotalMilliseconds); return(SendResult.Retry(timeout)); } // при общих ошибках return(SendResult.Retry(context.TransportSettings.ServerErrorRetryTimeout)); } } catch (Exception) { // при общих ошибках return(SendResult.Retry(context.TransportSettings.ErrorRetryTimeout)); } #else try { TransportSendStats sendedStats = null; var reqContent = new HttpRequestMessage(HttpMethod.Post, url) { Content = new WriteToStreamContent((requestStream, ctx) => { sendedStats = this.WriteMessageToBody(formDataBoundary, requestStream, packetInfos, configurationsToDownloadInfos, context, () => false); // для тестирования... //var length = 1 * 1024 * 1024 * 1024; //var batch = 1 * 1024 * 1024; //for (int i = 0; i < length; i += batch) //{ // var batchBuffer = new List<byte>(); // for (int j = 0; j < batch; j++) // { // batchBuffer.Add(0); // } // //if (req.HaveResponse) // //{ // // break; // //} // requestStream.Write(batchBuffer.ToArray(), 0, batchBuffer.Count); //} }), }; // workaround net core 2.2 и тех кто использует библиотеку System.Net.Http под netstandard (с версии 4.2.1.0) // https://github.com/dotnet/corefx/blob/v2.1.5/src/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnection.cs#L537 (версия 2.1.5 указана для примера) // нужно для того чтобы в случаях "отказов" не происходило чтение всего тела запроса, а читались только основные параметры reqContent.Headers.ExpectContinue = true; reqContent.Content.Headers.ContentType = MediaTypeHeaderValue.Parse(contentType); //reqContent.Version = new Version(1, 1); var result = this.httpClient.SendAsync(reqContent, HttpCompletionOption.ResponseHeadersRead).Result; if (result.IsSuccessStatusCode) { using (var stream = result.Content.ReadAsStreamAsync().Result) { // обрабатывает ответ return(this.SaveRequestResults(stream, sendedStats, context)); }; } else { // кастомная обработка конфигурации (для проблемы когда тело запроса с агента отправляется, хотя в этом нет смысла - лишние данные ходят по сети, это можно исправить) if ((int)result.StatusCode == 399) { using (var stream = result.Content.ReadAsStreamAsync().Result) { // обрабатывает ответ return(this.SaveRequestResults(stream, sendedStats, context)); }; } if ((int)result.StatusCode == 429) { var timeout = int.TryParse(result.Headers.GetValues("Retry-After").FirstOrDefault(), out var retryTm) ? retryTm : Convert.ToInt64(context.TransportSettings.ErrorRetryTimeout.TotalMilliseconds); return(SendResult.Retry(timeout)); } return(SendResult.Retry(context.TransportSettings.ServerErrorRetryTimeout)); } } catch (Exception) { // при общих ошибках return(SendResult.Retry(context.TransportSettings.ErrorRetryTimeout)); } #endif }