@@ -70,67 +70,69 @@ async Task<IResult> IngestAsync(HttpContext context)
70
70
71
71
async Task < IResult > IngestCompactFormatAsync ( HttpContext context )
72
72
{
73
+ byte [ ] ? rented = null ;
74
+
73
75
try
74
76
{
75
- var cts = CancellationTokenSource . CreateLinkedTokenSource ( context . RequestAborted ) ;
77
+ using var cts = CancellationTokenSource . CreateLinkedTokenSource ( context . RequestAborted ) ;
76
78
cts . CancelAfter ( TimeSpan . FromSeconds ( 5 ) ) ;
77
-
79
+
78
80
var requestApiKey = GetApiKey ( context . Request ) ;
79
- var log = _forwardingChannels . GetForwardingChannel ( requestApiKey ) ;
80
-
81
+ var log = _forwardingChannels . GetForwardingChannel ( requestApiKey ) ;
82
+
81
83
// Add one for the extra newline that we have to insert at the end of batches.
82
84
var bufferSize = _config . Connection . BatchSizeLimitBytes + 1 ;
83
- var rented = ArrayPool < byte > . Shared . Rent ( bufferSize ) ;
84
- var buffer = rented [ .. bufferSize ] ;
85
+ rented = ArrayPool < byte > . Shared . Rent ( bufferSize ) ;
86
+ var buffer = new ArraySegment < byte > ( rented , 0 , bufferSize ) ;
85
87
var writeHead = 0 ;
86
88
var readHead = 0 ;
87
-
89
+
88
90
var done = false ;
89
91
while ( ! done )
90
92
{
91
93
// Fill the memory buffer from as much of the incoming request payload as possible; buffering in memory increases the
92
94
// size of write batches.
93
95
while ( ! done )
94
96
{
95
- var remaining = buffer . Length - 1 - writeHead ;
97
+ var remaining = buffer . Count - 1 - writeHead ;
96
98
if ( remaining == 0 )
97
99
{
98
100
IngestionLog . ForClient ( context . Connection . RemoteIpAddress )
99
101
. Error ( "An incoming request exceeded the configured batch size limit" ) ;
100
102
return Error ( HttpStatusCode . RequestEntityTooLarge , "the request is too large to process" ) ;
101
103
}
102
-
104
+
103
105
var read = await context . Request . Body . ReadAsync ( buffer . AsMemory ( writeHead , remaining ) , cts . Token ) ;
104
106
if ( read == 0 )
105
107
{
106
108
done = true ;
107
109
}
108
-
110
+
109
111
writeHead += read ;
110
-
112
+
111
113
// Ingested batches must be terminated with `\n`, but this isn't an API requirement.
112
- if ( done && writeHead > 0 && writeHead < buffer . Length && buffer [ writeHead - 1 ] != ( byte ) '\n ' )
114
+ if ( done && writeHead > 0 && writeHead < buffer . Count && buffer [ writeHead - 1 ] != ( byte ) '\n ' )
113
115
{
114
116
buffer [ writeHead ] = ( byte ) '\n ' ;
115
117
writeHead += 1 ;
116
118
}
117
119
}
118
-
120
+
119
121
// Validate what we read, marking out a batch of one or more complete newline-delimited events.
120
122
var batchStart = readHead ;
121
123
var batchEnd = readHead ;
122
124
while ( batchEnd < writeHead )
123
125
{
124
126
var eventStart = batchEnd ;
125
127
var nlIndex = buffer . AsSpan ( ) [ eventStart ..] . IndexOf ( ( byte ) '\n ' ) ;
126
-
128
+
127
129
if ( nlIndex == - 1 )
128
130
{
129
131
break ;
130
132
}
131
-
133
+
132
134
var eventEnd = eventStart + nlIndex + 1 ;
133
-
135
+
134
136
batchEnd = eventEnd ;
135
137
readHead = batchEnd ;
136
138
@@ -142,12 +144,12 @@ async Task<IResult> IngestCompactFormatAsync(HttpContext context)
142
144
return Error ( HttpStatusCode . BadRequest , $ "Payload validation failed: { error } .") ;
143
145
}
144
146
}
145
-
147
+
146
148
if ( batchStart != batchEnd )
147
149
{
148
- await Write ( log , ArrayPool < byte > . Shared , buffer , batchStart ..batchEnd , cts . Token ) ;
150
+ await log . WriteAsync ( buffer [ batchStart ..batchEnd ] , cts . Token ) ;
149
151
}
150
-
152
+
151
153
// Copy any unprocessed data into our buffer and continue
152
154
if ( ! done && readHead != 0 )
153
155
{
@@ -157,10 +159,7 @@ async Task<IResult> IngestCompactFormatAsync(HttpContext context)
157
159
writeHead = retain ;
158
160
}
159
161
}
160
-
161
- // Exception cases are handled by `Write`
162
- ArrayPool < byte > . Shared . Return ( rented ) ;
163
-
162
+
164
163
return SuccessfulIngestion ( ) ;
165
164
}
166
165
catch ( Exception ex )
@@ -169,6 +168,13 @@ async Task<IResult> IngestCompactFormatAsync(HttpContext context)
169
168
. Error ( ex , "Ingestion failed" ) ;
170
169
return Error ( HttpStatusCode . InternalServerError , "Ingestion failed." ) ;
171
170
}
171
+ finally
172
+ {
173
+ if ( rented != null )
174
+ {
175
+ ArrayPool < byte > . Shared . Return ( rented ) ;
176
+ }
177
+ }
172
178
}
173
179
174
180
static bool DefaultedBoolQuery ( HttpRequest request , string queryParameterName )
@@ -263,19 +269,6 @@ bool ValidateClef(Span<byte> evt, [NotNullWhen(false)] out string? errorFragment
263
269
errorFragment = null ;
264
270
return true ;
265
271
}
266
-
267
- static async Task Write ( ForwardingChannel forwardingChannel , ArrayPool < byte > pool , byte [ ] storage , Range range , CancellationToken cancellationToken )
268
- {
269
- try
270
- {
271
- await forwardingChannel . WriteAsync ( storage , range , cancellationToken ) ;
272
- }
273
- catch
274
- {
275
- pool . Return ( storage ) ;
276
- throw ;
277
- }
278
- }
279
272
280
273
static IResult Error ( HttpStatusCode statusCode , string message )
281
274
{
0 commit comments