@@ -16,7 +16,7 @@ namespace BugSplatDotNetStandard.Api
1616{
1717 internal class CrashPostClient : IDisposable
1818 {
19- internal IZipUtils ZipUtils { get ; set ; } = new ZipUtils ( ) ;
19+ public ITempFileFactory TempFileFactory { get ; set ; } = new TempFileFactory ( ) ;
2020 private HttpClient httpClient ;
2121 private IS3Client s3Client ;
2222
@@ -41,15 +41,17 @@ public async Task<HttpResponseMessage> PostException(
4141 ExceptionPostOptions overridePostOptions = null
4242 )
4343 {
44- var inMemoryExceptionFile = new InMemoryFile ( ) { FileName = "Callstack.txt" , Content = Encoding . UTF8 . GetBytes ( stackTrace ) } ;
45- return await PostInMemoryCrashFile (
46- database ,
47- application ,
48- version ,
49- inMemoryExceptionFile ,
50- defaultPostOptions ,
51- overridePostOptions
52- ) ;
44+ using ( var stackTraceTempFile = TempFileFactory . CreateFromBytes ( "Callstack.txt" , Encoding . UTF8 . GetBytes ( stackTrace ) ) )
45+ {
46+ return await PostCrashFile (
47+ database ,
48+ application ,
49+ version ,
50+ stackTraceTempFile . File ,
51+ defaultPostOptions ,
52+ overridePostOptions
53+ ) ;
54+ }
5355 }
5456
5557 public async Task < HttpResponseMessage > PostMinidump (
@@ -61,12 +63,11 @@ public async Task<HttpResponseMessage> PostMinidump(
6163 MinidumpPostOptions overridePostOptions = null
6264 )
6365 {
64- var inMemoryDmpFile = TryCreateInMemoryFileFromFileInfo ( minidumpFileInfo ) ;
65- return await PostInMemoryCrashFile (
66+ return await PostCrashFile (
6667 database ,
6768 application ,
6869 version ,
69- inMemoryDmpFile ,
70+ minidumpFileInfo ,
7071 defaultPostOptions ,
7172 overridePostOptions
7273 ) ;
@@ -81,12 +82,11 @@ public async Task<HttpResponseMessage> PostXmlReport(
8182 XmlPostOptions overridePostOptions = null
8283 )
8384 {
84- var inMemoryXmlFile = TryCreateInMemoryFileFromFileInfo ( xmlFileInfo ) ;
85- return await PostInMemoryCrashFile (
85+ return await PostCrashFile (
8686 database ,
8787 application ,
8888 version ,
89- inMemoryXmlFile ,
89+ xmlFileInfo ,
9090 defaultPostOptions ,
9191 overridePostOptions
9292 ) ;
@@ -101,56 +101,73 @@ public async Task<HttpResponseMessage> PostCrashFile(
101101 BugSplatPostOptions overridePostOptions = null
102102 )
103103 {
104- var inMemoryCrashFile = TryCreateInMemoryFileFromFileInfo ( crashFileInfo ) ;
105- return await PostInMemoryCrashFile (
106- database ,
107- application ,
108- version ,
109- inMemoryCrashFile ,
110- defaultPostOptions ,
111- overridePostOptions
112- ) ;
104+ overridePostOptions = overridePostOptions ?? new MinidumpPostOptions ( ) ;
105+
106+ var additionalFormDataTempFiles = new List < ITempFile > ( ) ;
107+
108+ try
109+ {
110+ additionalFormDataTempFiles = CombineWithDuplicatesRemoved ( defaultPostOptions . FormDataParams , overridePostOptions . FormDataParams , ( param ) => param . Name )
111+ . Where ( file => ! string . IsNullOrEmpty ( file . FileName ) && file . Content != null )
112+ . Select ( param => TempFileFactory . TryCreateFromBytes ( param . FileName , param . Content . ReadAsByteArrayAsync ( ) . Result ) )
113+ . Where ( temp => temp != null && temp . File . Exists )
114+ . ToList ( ) ;
115+
116+ var additionalFormDataFiles = additionalFormDataTempFiles . Select ( temp => temp . File ) . ToList ( ) ;
117+ var attachments = CombineWithDuplicatesRemoved ( defaultPostOptions . Attachments , overridePostOptions . Attachments , ( file ) => file . FullName )
118+ . Where ( file => file != null && file . Exists )
119+ . ToList ( ) ;
120+
121+ attachments . AddRange ( additionalFormDataFiles ) ;
122+
123+ return await PostCrash (
124+ database ,
125+ application ,
126+ version ,
127+ crashFileInfo ,
128+ attachments ,
129+ defaultPostOptions ,
130+ overridePostOptions
131+ ) ;
132+ }
133+ finally
134+ {
135+ foreach ( var tempFile in additionalFormDataTempFiles )
136+ {
137+ tempFile ? . Dispose ( ) ;
138+ }
139+ }
113140 }
114141
115- private async Task < HttpResponseMessage > PostInMemoryCrashFile (
142+ private async Task < HttpResponseMessage > PostCrash (
116143 string database ,
117144 string application ,
118145 string version ,
119- InMemoryFile crashFile ,
146+ FileInfo crashFileInfo ,
147+ IEnumerable < FileInfo > attachments ,
120148 BugSplatPostOptions defaultPostOptions ,
121149 BugSplatPostOptions overridePostOptions = null
122150 )
123151 {
124- overridePostOptions = overridePostOptions ?? new MinidumpPostOptions ( ) ;
125-
126- var files = CombineWithDuplicatesRemoved ( defaultPostOptions . Attachments , overridePostOptions . Attachments , ( file ) => file . FullName )
127- . Select ( attachment => TryCreateInMemoryFileFromFileInfo ( attachment ) )
128- . Where ( file => file != null )
129- . ToList ( ) ;
152+ var files = new List < FileInfo > { crashFileInfo } ;
153+ files . AddRange ( attachments ) ;
130154
131- var additionalFormDataFiles = CombineWithDuplicatesRemoved ( defaultPostOptions . FormDataParams , overridePostOptions . FormDataParams , ( param ) => param . Name )
132- . Where ( file => ! string . IsNullOrEmpty ( file . FileName ) && file . Content != null )
133- . Select ( file => new InMemoryFile ( ) { FileName = file . FileName , Content = file . Content . ReadAsByteArrayAsync ( ) . Result } )
134- . ToList ( ) ;
135-
136- files . Add ( crashFile ) ;
137- files . AddRange ( additionalFormDataFiles ) ;
138-
139- var zipBytes = ZipUtils . CreateInMemoryZipFile ( files ) ;
155+ using ( var tempZipFile = TempFileFactory . CreateTempZip ( files ) )
140156 using (
141157 var crashUploadResponse = await GetCrashUploadUrl (
142158 database ,
143159 application ,
144160 version ,
145- zipBytes . Length
161+ tempZipFile . File . Length
146162 )
147163 )
148164 {
149165 ThrowIfHttpRequestFailed ( crashUploadResponse ) ;
150166
151167 var presignedUrl = await GetPresignedUrlFromResponse ( crashUploadResponse ) ;
152168
153- using ( var uploadFileResponse = await this . s3Client . UploadFileBytesToPresignedURL ( presignedUrl , zipBytes ) )
169+ using ( var zipFileStream = tempZipFile . CreateFileStream ( ) )
170+ using ( var uploadFileResponse = await s3Client . UploadFileStreamToPresignedURL ( presignedUrl , zipFileStream ) )
154171 {
155172 ThrowIfHttpRequestFailed ( uploadFileResponse ) ;
156173
@@ -252,7 +269,7 @@ private async Task<HttpResponseMessage> GetCrashUploadUrl(
252269 string database ,
253270 string application ,
254271 string version ,
255- int crashPostSize
272+ long crashPostSize
256273 )
257274 {
258275 var baseUrl = this . CreateBaseUrlFromDatabase ( database ) ;
@@ -271,31 +288,23 @@ private string GetETagFromResponseHeaders(HttpHeaders headers)
271288
272289 private async Task < Uri > GetPresignedUrlFromResponse ( HttpResponseMessage response )
273290 {
274- try
275- {
276- var json = await response . Content . ReadAsStringAsync ( ) ;
291+ var json = await response . Content . ReadAsStringAsync ( ) ;
277292
278- var jsonObj = new JsonObject ( json ) ;
279- var url = jsonObj . GetValue ( "url" ) ;
293+ var jsonObj = new JsonObject ( json ) ;
294+ var url = jsonObj . TryGetValue ( "url" ) ;
295+ var message = jsonObj . TryGetValue ( "message" ) ;
280296
281- return new Uri ( url ) ;
282- }
283- catch ( Exception ex )
297+ if ( string . IsNullOrEmpty ( url ) && ! string . IsNullOrEmpty ( message ) )
284298 {
285- throw new Exception ( "Failed to parse crash upload url" , ex ) ;
299+ throw new Exception ( $ "Failed to parse upload url: { message } " ) ;
286300 }
287- }
288301
289- private InMemoryFile TryCreateInMemoryFileFromFileInfo ( FileInfo fileInfo )
290- {
291- try
302+ if ( string . IsNullOrEmpty ( url ) )
292303 {
293- return InMemoryFile . FromFileInfo ( fileInfo ) ;
294- }
295- catch
296- {
297- return null ;
304+ throw new Exception ( "Failed to parse upload url" ) ;
298305 }
306+
307+ return new Uri ( url ) ;
299308 }
300309 }
301310}
0 commit comments