Imported Upstream version 5.16.0.100

Former-commit-id: 38faa55fb9669e35e7d8448b15c25dc447f25767
This commit is contained in:
Xamarin Public Jenkins (auto-signing)
2018-08-07 15:19:03 +00:00
parent 0a9828183b
commit 7d7f676260
4419 changed files with 170950 additions and 90273 deletions

View File

@@ -0,0 +1,69 @@
//
// BufferedReadStream.cs
//
// Author:
// Martin Baulig <mabaul@microsoft.com>
//
// Copyright (c) 2018 Xamarin Inc. (http://www.xamarin.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace System.Net
{
class BufferedReadStream : WebReadStream
{
readonly BufferOffsetSize readBuffer;
public BufferedReadStream (WebOperation operation, Stream innerStream,
BufferOffsetSize readBuffer)
: base (operation, innerStream)
{
this.readBuffer = readBuffer;
}
protected override async Task<int> ProcessReadAsync (
byte[] buffer, int offset, int size,
CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested ();
var remaining = readBuffer?.Size ?? 0;
if (remaining > 0) {
int copy = (remaining > size) ? size : remaining;
Buffer.BlockCopy (readBuffer.Buffer, readBuffer.Offset, buffer, offset, copy);
readBuffer.Offset += copy;
readBuffer.Size -= copy;
offset += copy;
size -= copy;
return copy;
}
if (InnerStream == null)
return 0;
return await InnerStream.ReadAsync (
buffer, offset, size, cancellationToken).ConfigureAwait (false);
}
}
}

View File

@@ -31,7 +31,7 @@ using System.Runtime.InteropServices;
namespace System.Net {
class ChunkedInputStream : RequestStream {
bool disposed;
MonoChunkStream decoder;
MonoChunkParser decoder;
HttpListenerContext context;
bool no_more_data;
@@ -58,10 +58,10 @@ namespace System.Net {
{
this.context = context;
WebHeaderCollection coll = (WebHeaderCollection) context.Request.Headers;
decoder = new MonoChunkStream (coll);
decoder = new MonoChunkParser (coll);
}
public MonoChunkStream Decoder {
public MonoChunkParser Decoder {
get { return decoder; }
set { decoder = value; }
}

View File

@@ -0,0 +1,92 @@
//
// ContentDecodeStream.cs
//
// Author:
// Martin Baulig <mabaul@microsoft.com>
//
// Copyright (c) 2018 Xamarin Inc. (http://www.xamarin.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
using System;
using System.IO;
using System.IO.Compression;
using System.Threading;
using System.Threading.Tasks;
namespace System.Net
{
class ContentDecodeStream : WebReadStream
{
internal enum Mode
{
GZip,
Deflate
}
public static ContentDecodeStream Create (
WebOperation operation, Stream innerStream, Mode mode)
{
Stream decodeStream;
if (mode == Mode.GZip)
decodeStream = new GZipStream (innerStream, CompressionMode.Decompress);
else
decodeStream = new DeflateStream (innerStream, CompressionMode.Decompress);
return new ContentDecodeStream (operation, decodeStream, innerStream);
}
Stream OriginalInnerStream {
get;
}
ContentDecodeStream (WebOperation operation, Stream decodeStream,
Stream originalInnerStream)
: base (operation, decodeStream)
{
/*
* We pass the GZipStream/DeflateStream to the base .ctor,
* so it can Dispose() it when we're done.
*
* Save the original inner stream here for FinishReading().
*/
OriginalInnerStream = originalInnerStream;
}
protected override Task<int> ProcessReadAsync (
byte[] buffer, int offset, int size,
CancellationToken cancellationToken)
{
return InnerStream.ReadAsync (buffer, offset, size, cancellationToken);
}
internal override Task FinishReading (CancellationToken cancellationToken)
{
/*
* Call FinishReading() on the original inner stream.
*
* Since GZipStream/DeflateStream knows the exact number of
* bytes that it wants to receive from it, it may not have
* read the chunk trailer yet.
*/
if (OriginalInnerStream is WebReadStream innerReadStream)
return innerReadStream.FinishReading (cancellationToken);
return Task.CompletedTask;
}
}
}

View File

@@ -0,0 +1,73 @@
//
// FixedSizeReadStream.cs
//
// Author:
// Martin Baulig <mabaul@microsoft.com>
//
// Copyright (c) 2018 Xamarin Inc. (http://www.xamarin.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace System.Net
{
class FixedSizeReadStream : WebReadStream
{
public long ContentLength {
get;
}
long position;
public FixedSizeReadStream (WebOperation operation, Stream innerStream,
long contentLength)
: base (operation, innerStream)
{
ContentLength = contentLength;
}
protected override async Task<int> ProcessReadAsync (
byte[] buffer, int offset, int size,
CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested ();
var remaining = ContentLength - position;
WebConnection.Debug ($"{ME} READ: position={position} length={ContentLength} size={size} remaining={remaining}");
if (remaining == 0)
return 0;
var readSize = (int)Math.Min (remaining, size);
WebConnection.Debug ($"{ME} READ #1: readSize={readSize}");
var ret = await InnerStream.ReadAsync (
buffer, offset, readSize, cancellationToken).ConfigureAwait (false);
WebConnection.Debug ($"{ME} READ #2: ret={ret}");
if (ret <= 0)
return ret;
position += ret;
return ret;
}
}
}

View File

@@ -335,6 +335,14 @@ namespace Mono.Net
return (int) CFStringGetLength (Handle);
}
}
[DllImport (CoreFoundationLibrary)]
extern static int CFStringCompare (IntPtr theString1, IntPtr theString2, int compareOptions);
public static int Compare (IntPtr string1, IntPtr string2, int compareOptions = 0)
{
return CFStringCompare (string1, string2, compareOptions);
}
[DllImport (CoreFoundationLibrary)]
extern static IntPtr CFStringGetCharactersPtr (IntPtr handle);
@@ -688,6 +696,25 @@ namespace Mono.Net
if (type == kCFProxyTypeSOCKS)
return CFProxyType.SOCKS;
//in OSX 10.13 pointer comparison didn't work for kCFProxyTypeAutoConfigurationURL
if (CFString.Compare (type, kCFProxyTypeAutoConfigurationJavaScript) == 0)
return CFProxyType.AutoConfigurationJavaScript;
if (CFString.Compare (type, kCFProxyTypeAutoConfigurationURL) == 0)
return CFProxyType.AutoConfigurationUrl;
if (CFString.Compare (type, kCFProxyTypeFTP) == 0)
return CFProxyType.FTP;
if (CFString.Compare (type, kCFProxyTypeHTTP) == 0)
return CFProxyType.HTTP;
if (CFString.Compare (type, kCFProxyTypeHTTPS) == 0)
return CFProxyType.HTTPS;
if (CFString.Compare (type, kCFProxyTypeSOCKS) == 0)
return CFProxyType.SOCKS;
return CFProxyType.None;
}

View File

@@ -0,0 +1,356 @@
//
// System.Net.MonoChunkParser
//
// Authors:
// Gonzalo Paniagua Javier (gonzalo@ximian.com)
//
// (C) 2003 Ximian, Inc (http://www.ximian.com)
//
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Text;
namespace System.Net
{
class MonoChunkParser
{
enum State {
None,
PartialSize,
Body,
BodyFinished,
Trailer
}
class Chunk {
public byte [] Bytes;
public int Offset;
public Chunk (byte [] chunk)
{
this.Bytes = chunk;
}
public int Read (byte [] buffer, int offset, int size)
{
int nread = (size > Bytes.Length - Offset) ? Bytes.Length - Offset : size;
Buffer.BlockCopy (Bytes, Offset, buffer, offset, nread);
Offset += nread;
return nread;
}
}
WebHeaderCollection headers;
int chunkSize;
int chunkRead;
int totalWritten;
State state;
StringBuilder saved;
bool sawCR;
bool gotit;
int trailerState;
ArrayList chunks;
public MonoChunkParser (WebHeaderCollection headers)
{
this.headers = headers;
saved = new StringBuilder ();
chunks = new ArrayList ();
chunkSize = -1;
totalWritten = 0;
}
public void WriteAndReadBack (byte [] buffer, int offset, int size, ref int read)
{
if (offset + read > 0)
Write (buffer, offset, offset+read);
read = Read (buffer, offset, size);
}
public int Read (byte [] buffer, int offset, int size)
{
return ReadFromChunks (buffer, offset, size);
}
int ReadFromChunks (byte [] buffer, int offset, int size)
{
int count = chunks.Count;
int nread = 0;
var chunksForRemoving = new List<Chunk>(count);
for (int i = 0; i < count; i++) {
Chunk chunk = (Chunk) chunks [i];
if (chunk.Offset == chunk.Bytes.Length) {
chunksForRemoving.Add(chunk);
continue;
}
nread += chunk.Read (buffer, offset + nread, size - nread);
if (nread == size)
break;
}
foreach (var chunk in chunksForRemoving)
chunks.Remove(chunk);
return nread;
}
public void Write (byte [] buffer, int offset, int size)
{
if (offset < size)
InternalWrite (buffer, ref offset, size);
}
void InternalWrite (byte [] buffer, ref int offset, int size)
{
if (state == State.None || state == State.PartialSize) {
state = GetChunkSize (buffer, ref offset, size);
if (state == State.PartialSize)
return;
saved.Length = 0;
sawCR = false;
gotit = false;
}
if (state == State.Body && offset < size) {
state = ReadBody (buffer, ref offset, size);
if (state == State.Body)
return;
}
if (state == State.BodyFinished && offset < size) {
state = ReadCRLF (buffer, ref offset, size);
if (state == State.BodyFinished)
return;
sawCR = false;
}
if (state == State.Trailer && offset < size) {
state = ReadTrailer (buffer, ref offset, size);
if (state == State.Trailer)
return;
saved.Length = 0;
sawCR = false;
gotit = false;
}
if (offset < size)
InternalWrite (buffer, ref offset, size);
}
public bool WantMore {
get { return (chunkRead != chunkSize || chunkSize != 0 || state != State.None); }
}
public bool DataAvailable {
get {
int count = chunks.Count;
for (int i = 0; i < count; i++) {
Chunk ch = (Chunk) chunks [i];
if (ch == null || ch.Bytes == null)
continue;
if (ch.Bytes.Length > 0 && ch.Offset < ch.Bytes.Length)
return (state != State.Body);
}
return false;
}
}
public int TotalDataSize {
get { return totalWritten; }
}
public int ChunkLeft {
get { return chunkSize - chunkRead; }
}
State ReadBody (byte [] buffer, ref int offset, int size)
{
if (chunkSize == 0)
return State.BodyFinished;
int diff = size - offset;
if (diff + chunkRead > chunkSize)
diff = chunkSize - chunkRead;
byte [] chunk = new byte [diff];
Buffer.BlockCopy (buffer, offset, chunk, 0, diff);
chunks.Add (new Chunk (chunk));
offset += diff;
chunkRead += diff;
totalWritten += diff;
return (chunkRead == chunkSize) ? State.BodyFinished : State.Body;
}
State GetChunkSize (byte [] buffer, ref int offset, int size)
{
chunkRead = 0;
chunkSize = 0;
char c = '\0';
while (offset < size) {
c = (char) buffer [offset++];
if (c == '\r') {
if (sawCR)
ThrowProtocolViolation ("2 CR found");
sawCR = true;
continue;
}
if (sawCR && c == '\n')
break;
if (c == ' ')
gotit = true;
if (!gotit)
saved.Append (c);
if (saved.Length > 20)
ThrowProtocolViolation ("chunk size too long.");
}
if (!sawCR || c != '\n') {
if (offset < size)
ThrowProtocolViolation ("Missing \\n");
try {
if (saved.Length > 0) {
chunkSize = Int32.Parse (RemoveChunkExtension (saved.ToString ()), NumberStyles.HexNumber);
}
} catch (Exception) {
ThrowProtocolViolation ("Cannot parse chunk size.");
}
return State.PartialSize;
}
chunkRead = 0;
try {
chunkSize = Int32.Parse (RemoveChunkExtension (saved.ToString ()), NumberStyles.HexNumber);
} catch (Exception) {
ThrowProtocolViolation ("Cannot parse chunk size.");
}
if (chunkSize == 0) {
trailerState = 2;
return State.Trailer;
}
return State.Body;
}
static string RemoveChunkExtension (string input)
{
int idx = input.IndexOf (';');
if (idx == -1)
return input;
return input.Substring (0, idx);
}
State ReadCRLF (byte [] buffer, ref int offset, int size)
{
if (!sawCR) {
if ((char) buffer [offset++] != '\r')
ThrowProtocolViolation ("Expecting \\r");
sawCR = true;
if (offset == size)
return State.BodyFinished;
}
if (sawCR && (char) buffer [offset++] != '\n')
ThrowProtocolViolation ("Expecting \\n");
return State.None;
}
State ReadTrailer (byte [] buffer, ref int offset, int size)
{
char c = '\0';
// short path
if (trailerState == 2 && (char) buffer [offset] == '\r' && saved.Length == 0) {
offset++;
if (offset < size && (char) buffer [offset] == '\n') {
offset++;
return State.None;
}
offset--;
}
int st = trailerState;
while (offset < size && st < 4) {
c = (char) buffer [offset++];
if ((st == 0 || st == 2) && c == '\r') {
st++;
continue;
}
if ((st == 1 || st == 3) && c == '\n') {
st++;
continue;
}
if (st >= 0) {
saved.Append (c);
st = 0;
if (saved.Length > 4196)
ThrowProtocolViolation ("Error reading trailer (too long).");
}
}
if (st < 4) {
trailerState = st;
if (offset < size)
ThrowProtocolViolation ("Error reading trailer.");
return State.Trailer;
}
StringReader reader = new StringReader (saved.ToString ());
string line;
while ((line = reader.ReadLine ()) != null && line != "")
headers.Add (line);
return State.None;
}
static void ThrowProtocolViolation (string message)
{
WebException we = new WebException (message, null, WebExceptionStatus.ServerProtocolViolation, null);
throw we;
}
}
}

View File

@@ -1,372 +1,123 @@
//
// System.Net.MonoChunkStream
// MonoChunkStream.cs
//
// Authors:
// Gonzalo Paniagua Javier (gonzalo@ximian.com)
// Author:
// Martin Baulig <mabaul@microsoft.com>
//
// (C) 2003 Ximian, Inc (http://www.ximian.com)
// Copyright (c) 2018 Xamarin Inc. (http://www.xamarin.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
using System;
using System.IO;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace System.Net
{
class MonoChunkStream
class MonoChunkStream : WebReadStream
{
enum State {
None,
PartialSize,
Body,
BodyFinished,
Trailer
protected WebHeaderCollection Headers {
get;
}
class Chunk {
public byte [] Bytes;
public int Offset;
public Chunk (byte [] chunk)
{
this.Bytes = chunk;
}
public int Read (byte [] buffer, int offset, int size)
{
int nread = (size > Bytes.Length - Offset) ? Bytes.Length - Offset : size;
Buffer.BlockCopy (Bytes, Offset, buffer, offset, nread);
Offset += nread;
return nread;
}
protected MonoChunkParser Decoder {
get;
}
internal WebHeaderCollection headers;
int chunkSize;
int chunkRead;
int totalWritten;
State state;
//byte [] waitBuffer;
StringBuilder saved;
bool sawCR;
bool gotit;
int trailerState;
ArrayList chunks;
public MonoChunkStream (byte [] buffer, int offset, int size, WebHeaderCollection headers)
: this (headers)
public MonoChunkStream (WebOperation operation, Stream innerStream,
WebHeaderCollection headers)
: base (operation, innerStream)
{
Write (buffer, offset, size);
Headers = headers;
Decoder = new MonoChunkParser (headers);
}
public MonoChunkStream (WebHeaderCollection headers)
protected override async Task<int> ProcessReadAsync (
byte[] buffer, int offset, int size,
CancellationToken cancellationToken)
{
this.headers = headers;
saved = new StringBuilder ();
chunks = new ArrayList ();
chunkSize = -1;
totalWritten = 0;
cancellationToken.ThrowIfCancellationRequested ();
if (Decoder.DataAvailable)
return Decoder.Read (buffer, offset, size);
int ret = 0;
byte[] moreBytes = null;
while (ret == 0 && Decoder.WantMore) {
int localSize = Decoder.ChunkLeft;
if (localSize <= 0) // not read chunk size yet
localSize = 1024;
else if (localSize > 16384)
localSize = 16384;
if (moreBytes == null || moreBytes.Length < localSize)
moreBytes = new byte[localSize];
ret = await InnerStream.ReadAsync (
moreBytes, 0, localSize, cancellationToken).ConfigureAwait (false);
if (ret <= 0)
return ret;
Decoder.Write (moreBytes, 0, ret);
ret = Decoder.Read (buffer, offset, size);
}
return ret;
}
public void ResetBuffer ()
internal override async Task FinishReading (CancellationToken cancellationToken)
{
chunkSize = -1;
chunkRead = 0;
totalWritten = 0;
chunks.Clear ();
await base.FinishReading (cancellationToken).ConfigureAwait (false);
cancellationToken.ThrowIfCancellationRequested ();
/*
* We are expecting the chunk trailer, but no more data.
*/
if (Decoder.DataAvailable)
ThrowExpectingChunkTrailer ();
/*
* Need to loop here since there might be header fields after
* the chunk trailer.
*/
while (Decoder.WantMore) {
var buffer = new byte[256];
int ret = await InnerStream.ReadAsync (
buffer, 0, buffer.Length, cancellationToken).ConfigureAwait (false);
if (ret <= 0)
ThrowExpectingChunkTrailer ();
Decoder.Write (buffer, 0, ret);
ret = Decoder.Read (buffer, 0, 1);
if (ret != 0)
ThrowExpectingChunkTrailer ();
}
}
public void WriteAndReadBack (byte [] buffer, int offset, int size, ref int read)
static void ThrowExpectingChunkTrailer ()
{
if (offset + read > 0)
Write (buffer, offset, offset+read);
read = Read (buffer, offset, size);
}
public int Read (byte [] buffer, int offset, int size)
{
return ReadFromChunks (buffer, offset, size);
}
int ReadFromChunks (byte [] buffer, int offset, int size)
{
int count = chunks.Count;
int nread = 0;
var chunksForRemoving = new List<Chunk>(count);
for (int i = 0; i < count; i++) {
Chunk chunk = (Chunk) chunks [i];
if (chunk.Offset == chunk.Bytes.Length) {
chunksForRemoving.Add(chunk);
continue;
}
nread += chunk.Read (buffer, offset + nread, size - nread);
if (nread == size)
break;
}
foreach (var chunk in chunksForRemoving)
chunks.Remove(chunk);
return nread;
}
public void Write (byte [] buffer, int offset, int size)
{
if (offset < size)
InternalWrite (buffer, ref offset, size);
}
void InternalWrite (byte [] buffer, ref int offset, int size)
{
if (state == State.None || state == State.PartialSize) {
state = GetChunkSize (buffer, ref offset, size);
if (state == State.PartialSize)
return;
saved.Length = 0;
sawCR = false;
gotit = false;
}
if (state == State.Body && offset < size) {
state = ReadBody (buffer, ref offset, size);
if (state == State.Body)
return;
}
if (state == State.BodyFinished && offset < size) {
state = ReadCRLF (buffer, ref offset, size);
if (state == State.BodyFinished)
return;
sawCR = false;
}
if (state == State.Trailer && offset < size) {
state = ReadTrailer (buffer, ref offset, size);
if (state == State.Trailer)
return;
saved.Length = 0;
sawCR = false;
gotit = false;
}
if (offset < size)
InternalWrite (buffer, ref offset, size);
}
public bool WantMore {
get { return (chunkRead != chunkSize || chunkSize != 0 || state != State.None); }
}
public bool DataAvailable {
get {
int count = chunks.Count;
for (int i = 0; i < count; i++) {
Chunk ch = (Chunk) chunks [i];
if (ch == null || ch.Bytes == null)
continue;
if (ch.Bytes.Length > 0 && ch.Offset < ch.Bytes.Length)
return (state != State.Body);
}
return false;
}
}
public int TotalDataSize {
get { return totalWritten; }
}
public int ChunkLeft {
get { return chunkSize - chunkRead; }
}
State ReadBody (byte [] buffer, ref int offset, int size)
{
if (chunkSize == 0)
return State.BodyFinished;
int diff = size - offset;
if (diff + chunkRead > chunkSize)
diff = chunkSize - chunkRead;
byte [] chunk = new byte [diff];
Buffer.BlockCopy (buffer, offset, chunk, 0, diff);
chunks.Add (new Chunk (chunk));
offset += diff;
chunkRead += diff;
totalWritten += diff;
return (chunkRead == chunkSize) ? State.BodyFinished : State.Body;
}
State GetChunkSize (byte [] buffer, ref int offset, int size)
{
chunkRead = 0;
chunkSize = 0;
char c = '\0';
while (offset < size) {
c = (char) buffer [offset++];
if (c == '\r') {
if (sawCR)
ThrowProtocolViolation ("2 CR found");
sawCR = true;
continue;
}
if (sawCR && c == '\n')
break;
if (c == ' ')
gotit = true;
if (!gotit)
saved.Append (c);
if (saved.Length > 20)
ThrowProtocolViolation ("chunk size too long.");
}
if (!sawCR || c != '\n') {
if (offset < size)
ThrowProtocolViolation ("Missing \\n");
try {
if (saved.Length > 0) {
chunkSize = Int32.Parse (RemoveChunkExtension (saved.ToString ()), NumberStyles.HexNumber);
}
} catch (Exception) {
ThrowProtocolViolation ("Cannot parse chunk size.");
}
return State.PartialSize;
}
chunkRead = 0;
try {
chunkSize = Int32.Parse (RemoveChunkExtension (saved.ToString ()), NumberStyles.HexNumber);
} catch (Exception) {
ThrowProtocolViolation ("Cannot parse chunk size.");
}
if (chunkSize == 0) {
trailerState = 2;
return State.Trailer;
}
return State.Body;
}
static string RemoveChunkExtension (string input)
{
int idx = input.IndexOf (';');
if (idx == -1)
return input;
return input.Substring (0, idx);
}
State ReadCRLF (byte [] buffer, ref int offset, int size)
{
if (!sawCR) {
if ((char) buffer [offset++] != '\r')
ThrowProtocolViolation ("Expecting \\r");
sawCR = true;
if (offset == size)
return State.BodyFinished;
}
if (sawCR && (char) buffer [offset++] != '\n')
ThrowProtocolViolation ("Expecting \\n");
return State.None;
}
State ReadTrailer (byte [] buffer, ref int offset, int size)
{
char c = '\0';
// short path
if (trailerState == 2 && (char) buffer [offset] == '\r' && saved.Length == 0) {
offset++;
if (offset < size && (char) buffer [offset] == '\n') {
offset++;
return State.None;
}
offset--;
}
int st = trailerState;
string stString = "\r\n\r";
while (offset < size && st < 4) {
c = (char) buffer [offset++];
if ((st == 0 || st == 2) && c == '\r') {
st++;
continue;
}
if ((st == 1 || st == 3) && c == '\n') {
st++;
continue;
}
if (st > 0) {
saved.Append (stString.Substring (0, saved.Length == 0? st-2: st));
st = 0;
if (saved.Length > 4196)
ThrowProtocolViolation ("Error reading trailer (too long).");
}
}
if (st < 4) {
trailerState = st;
if (offset < size)
ThrowProtocolViolation ("Error reading trailer.");
return State.Trailer;
}
StringReader reader = new StringReader (saved.ToString ());
string line;
while ((line = reader.ReadLine ()) != null && line != "")
headers.Add (line);
return State.None;
}
static void ThrowProtocolViolation (string message)
{
WebException we = new WebException (message, null, WebExceptionStatus.ServerProtocolViolation, null);
throw we;
throw new WebException (
"Expecting chunk trailer.", null,
WebExceptionStatus.ServerProtocolViolation, null);
}
}
}

View File

@@ -55,20 +55,28 @@ namespace System.Net
bool tcp_keepalive;
int tcp_keepalive_time;
int tcp_keepalive_interval;
bool disposed;
// Constructors
internal ServicePoint (Uri uri, int connectionLimit, int maxIdleTime)
internal ServicePoint (ServicePointManager.SPKey key, Uri uri, int connectionLimit, int maxIdleTime)
{
Key = key;
this.uri = uri;
this.connectionLimit = connectionLimit;
this.maxIdleTime = maxIdleTime;
Scheduler = new ServicePointScheduler (this, connectionLimit, maxIdleTime);
}
internal ServicePointScheduler Scheduler {
internal ServicePointManager.SPKey Key {
get;
}
ServicePointScheduler Scheduler {
get; set;
}
// Properties
public Uri Address {
@@ -95,9 +103,16 @@ namespace System.Net
}
}
int connectionLimit;
int maxIdleTime;
public int ConnectionLimit {
get { return Scheduler.ConnectionLimit; }
set { Scheduler.ConnectionLimit = value; }
get { return connectionLimit; }
set {
connectionLimit = value;
if (!disposed)
Scheduler.ConnectionLimit = value;
}
}
public string ConnectionName {
@@ -106,19 +121,25 @@ namespace System.Net
public int CurrentConnections {
get {
return Scheduler.CurrentConnections;
return disposed ? 0 : Scheduler.CurrentConnections;
}
}
public DateTime IdleSince {
get {
if (disposed)
return DateTime.MinValue;
return Scheduler.IdleSince.ToLocalTime ();
}
}
public int MaxIdleTime {
get { return Scheduler.MaxIdleTime; }
set { Scheduler.MaxIdleTime = value; }
get { return maxIdleTime; }
set {
maxIdleTime = value;
if (!disposed)
Scheduler.MaxIdleTime = value;
}
}
public virtual Version ProtocolVersion {
@@ -265,6 +286,8 @@ namespace System.Net
internal void SendRequest (WebOperation operation, string groupName)
{
lock (this) {
if (disposed)
throw new ObjectDisposedException (typeof (ServicePoint).FullName);
Scheduler.SendRequest (operation, groupName);
}
}
@@ -272,10 +295,18 @@ namespace System.Net
public bool CloseConnectionGroup (string connectionGroupName)
{
lock (this) {
if (disposed)
return true;
return Scheduler.CloseConnectionGroup (connectionGroupName);
}
}
internal void FreeServicePoint ()
{
disposed = true;
Scheduler = null;
}
//
// Copied from the referencesource
//

View File

@@ -33,6 +33,7 @@ using System;
using System.Threading;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Collections.Specialized;
using System.Configuration;
using System.Net.Configuration;
@@ -63,7 +64,7 @@ using System.Diagnostics;
namespace System.Net
{
public partial class ServicePointManager {
class SPKey {
internal class SPKey {
Uri uri; // schema/host/port
Uri proxy;
bool use_connect;
@@ -110,8 +111,8 @@ namespace System.Net
}
}
private static HybridDictionary servicePoints = new HybridDictionary ();
static ConcurrentDictionary<SPKey, ServicePoint> servicePoints = new ConcurrentDictionary<SPKey, ServicePoint> ();
// Static properties
private static ICertificatePolicy policy;
@@ -120,7 +121,7 @@ namespace System.Net
private static int maxServicePoints = 0;
private static int dnsRefreshTimeout = 2 * 60 * 1000;
private static bool _checkCRL = false;
private static SecurityProtocolType _securityProtocol = SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12;
private static SecurityProtocolType _securityProtocol = SecurityProtocolType.SystemDefault;
static bool expectContinue = true;
static bool useNagle;
@@ -361,11 +362,9 @@ namespace System.Net
address = new Uri (address.Scheme + "://" + address.Authority);
ServicePoint sp = null;
SPKey key = new SPKey (origAddress, usesProxy ? address : null, useConnect);
var key = new SPKey (origAddress, usesProxy ? address : null, useConnect);
lock (servicePoints) {
sp = servicePoints [key] as ServicePoint;
if (sp != null)
if (servicePoints.TryGetValue (key, out var sp))
return sp;
if (maxServicePoints > 0 && servicePoints.Count >= maxServicePoints)
@@ -378,16 +377,15 @@ namespace System.Net
string addr = address.ToString ();
limit = (int) manager.GetMaxConnections (addr);
#endif
sp = new ServicePoint (address, limit, maxServicePointIdleTime);
sp = new ServicePoint (key, address, limit, maxServicePointIdleTime);
sp.Expect100Continue = expectContinue;
sp.UseNagleAlgorithm = useNagle;
sp.UsesProxy = usesProxy;
sp.UseConnect = useConnect;
sp.SetTcpKeepAlive (tcp_keepalive, tcp_keepalive_time, tcp_keepalive_interval);
servicePoints.Add (key, sp);
return servicePoints.GetOrAdd (key, sp);
}
return sp;
}
internal static void CloseConnectionGroup (string connectionGroupName)
@@ -398,6 +396,11 @@ namespace System.Net
}
}
}
internal static void RemoveServicePoint (ServicePoint sp)
{
servicePoints.TryRemove (sp.Key, out var value);
}
}
}

View File

@@ -36,8 +36,8 @@ namespace System.Net
{
class ServicePointScheduler
{
public ServicePoint ServicePoint {
get;
ServicePoint ServicePoint {
get; set;
}
public int MaxIdleTime {
@@ -80,12 +80,6 @@ namespace System.Net
idleSince = DateTime.UtcNow;
}
[Conditional ("MONO_WEB_DEBUG")]
void Debug (string message, params object[] args)
{
WebConnection.Debug ($"SPS({ID}): {string.Format (message, args)}");
}
[Conditional ("MONO_WEB_DEBUG")]
void Debug (string message)
{
@@ -124,15 +118,14 @@ namespace System.Net
public void Run ()
{
lock (ServicePoint) {
if (Interlocked.CompareExchange (ref running, 1, 0) == 0)
StartScheduler ();
Debug ($"RUN");
if (Interlocked.CompareExchange (ref running, 1, 0) == 0)
Task.Run (() => RunScheduler ());
schedulerEvent.Set ();
}
schedulerEvent.Set ();
}
async void StartScheduler ()
async Task RunScheduler ()
{
idleSince = DateTime.UtcNow + TimeSpan.FromDays (3650);
@@ -143,36 +136,47 @@ namespace System.Net
ValueTuple<ConnectionGroup, WebOperation>[] operationArray;
ValueTuple<ConnectionGroup, WebConnection, Task>[] idleArray;
var taskList = new List<Task> ();
Task<bool> schedulerTask;
bool finalCleanup = false;
lock (ServicePoint) {
Cleanup ();
if (groups == null && defaultGroup.IsEmpty () && operations.Count == 0 && idleConnections.Count == 0) {
Debug ($"MAIN LOOP DONE");
running = 0;
idleSince = DateTime.UtcNow;
schedulerEvent.Reset ();
return;
}
operationArray = new ValueTuple<ConnectionGroup, WebOperation>[operations.Count];
operations.CopyTo (operationArray, 0);
idleArray = new ValueTuple<ConnectionGroup, WebConnection, Task>[idleConnections.Count];
idleConnections.CopyTo (idleArray, 0);
taskList.Add (schedulerEvent.WaitAsync (maxIdleTime));
foreach (var item in operationArray)
taskList.Add (item.Item2.Finished.Task);
foreach (var item in idleArray)
taskList.Add (item.Item3);
schedulerTask = schedulerEvent.WaitAsync (maxIdleTime);
taskList.Add (schedulerTask);
if (groups == null && defaultGroup.IsEmpty () && operations.Count == 0 && idleConnections.Count == 0) {
Debug ($"MAIN LOOP DONE");
idleSince = DateTime.UtcNow;
finalCleanup = true;
} else {
foreach (var item in operationArray)
taskList.Add (item.Item2.Finished.Task);
foreach (var item in idleArray)
taskList.Add (item.Item3);
}
}
Debug ($"MAIN LOOP #1: operations={operationArray.Length} idle={idleArray.Length}");
Debug ($"MAIN LOOP #1: operations={operationArray.Length} idle={idleArray.Length} maxIdleTime={maxIdleTime} finalCleanup={finalCleanup}");
var ret = await Task.WhenAny (taskList).ConfigureAwait (false);
lock (ServicePoint) {
bool runMaster = false;
if (ret == taskList[0])
if (finalCleanup) {
if (schedulerTask.Result)
runMaster = true;
else {
FinalCleanup ();
return;
}
} else if (ret == taskList[0]) {
runMaster = true;
}
/*
* We discard the `taskList` at this point as it is only used to wake us up.
@@ -252,7 +256,7 @@ namespace System.Net
repeat = SchedulerIteration (defaultGroup);
Debug ($"ITERATION #1: {repeat} {groups != null}");
Debug ($"ITERATION #1: repeat={repeat} groups={groups?.Count}");
if (groups != null) {
foreach (var group in groups) {
@@ -261,7 +265,7 @@ namespace System.Net
}
}
Debug ($"ITERATION #3: {repeat}");
Debug ($"ITERATION #3: repeat={repeat}");
} while (repeat);
}
@@ -378,6 +382,20 @@ namespace System.Net
}
}
void FinalCleanup ()
{
Debug ($"FINAL CLEANUP");
groups = null;
operations = null;
idleConnections = null;
defaultGroup = null;
ServicePoint.FreeServicePoint ();
ServicePointManager.RemoveServicePoint (ServicePoint);
ServicePoint = null;
}
public void SendRequest (WebOperation operation, string groupName)
{
lock (ServicePoint) {
@@ -391,25 +409,23 @@ namespace System.Net
public bool CloseConnectionGroup (string groupName)
{
lock (ServicePoint) {
ConnectionGroup group;
if (string.IsNullOrEmpty (groupName))
group = defaultGroup;
else if (groups == null || !groups.TryGetValue (groupName, out group))
return false;
ConnectionGroup group;
if (string.IsNullOrEmpty (groupName))
group = defaultGroup;
else if (groups == null || !groups.TryGetValue (groupName, out group))
return false;
Debug ($"CLOSE CONNECTION GROUP: group={group.ID}");
Debug ($"CLOSE CONNECTION GROUP: group={group.ID}");
if (group != defaultGroup) {
groups.Remove (groupName);
if (groups.Count == 0)
groups = null;
}
group.Close ();
Run ();
return true;
if (group != defaultGroup) {
groups.Remove (groupName);
if (groups.Count == 0)
groups = null;
}
group.Close ();
Run ();
return true;
}
ConnectionGroup GetConnectionGroup (string name)

View File

@@ -48,12 +48,11 @@ namespace System.Net
int write_timeout;
internal bool IgnoreIOErrors;
protected WebConnectionStream (WebConnection cnc, WebOperation operation, Stream stream)
protected WebConnectionStream (WebConnection cnc, WebOperation operation)
{
Connection = cnc;
Operation = operation;
Request = operation.Request;
InnerStream = stream;
read_timeout = Request.ReadWriteTimeout;
write_timeout = read_timeout;
@@ -73,10 +72,6 @@ namespace System.Net
internal ServicePoint ServicePoint => Connection.ServicePoint;
internal Stream InnerStream {
get;
}
public override bool CanTimeout {
get { return true; }
}
@@ -248,23 +243,21 @@ namespace System.Net
public override long Seek (long a, SeekOrigin b)
{
throw new NotSupportedException ();
throw new NotSupportedException (SR.net_noseek);
}
public override void SetLength (long a)
{
throw new NotSupportedException ();
throw new NotSupportedException (SR.net_noseek);
}
public override bool CanSeek {
get {
return false;
}
}
public override bool CanSeek => false;
public override long Length => throw new NotSupportedException (SR.net_noseek);
public override long Position {
get { throw new NotSupportedException (); }
set { throw new NotSupportedException (); }
get { throw new NotSupportedException (SR.net_noseek); }
set { throw new NotSupportedException (SR.net_noseek); }
}
}
}

View File

@@ -60,10 +60,10 @@ namespace System.Net
#if MONO_WEB_DEBUG
static int nextID;
internal readonly int ID = ++nextID;
string ME => $"WO({ID},{Connection?.ID ?? -1})";
internal string ME => $"WO({ID},{Connection?.ID ?? -1})";
#else
internal readonly int ID;
string ME;
internal string ME => null;
#endif
public WebOperation (HttpWebRequest request, BufferOffsetSize writeBuffer, bool isNtlmChallenge, CancellationToken cancellationToken)

View File

@@ -0,0 +1,220 @@
//
// WebReadStream.cs
//
// Author:
// Martin Baulig <mabaul@microsoft.com>
//
// Copyright (c) 2018 Xamarin Inc. (http://www.xamarin.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace System.Net
{
abstract class WebReadStream : Stream
{
public WebOperation Operation {
get;
}
protected Stream InnerStream {
get;
}
#if MONO_WEB_DEBUG
internal string ME => $"WRS({GetType ().Name}:Op={operation.ID})";
#else
internal string ME => null;
#endif
public WebReadStream (WebOperation operation, Stream innerStream)
{
Operation = operation;
InnerStream = innerStream;
}
public override long Length => throw new NotSupportedException ();
public override long Position {
get => throw new NotSupportedException ();
set => throw new NotSupportedException ();
}
public override bool CanSeek => false;
public override bool CanRead => true;
public override bool CanWrite => false;
public override void SetLength (long value)
{
throw new NotSupportedException ();
}
public override long Seek (long offset, SeekOrigin origin)
{
throw new NotSupportedException ();
}
public override void Write (byte[] buffer, int offset, int count)
{
throw new NotSupportedException ();
}
public override void Flush ()
{
throw new NotSupportedException ();
}
protected Exception GetException (Exception e)
{
e = HttpWebRequest.FlattenException (e);
if (e is WebException)
return e;
if (Operation.Aborted || e is OperationCanceledException || e is ObjectDisposedException)
return HttpWebRequest.CreateRequestAbortedException ();
return e;
}
public override int Read (byte[] buffer, int offset, int size)
{
if (!CanRead)
throw new NotSupportedException (SR.net_writeonlystream);
Operation.ThrowIfClosedOrDisposed ();
if (buffer == null)
throw new ArgumentNullException (nameof (buffer));
int length = buffer.Length;
if (offset < 0 || length < offset)
throw new ArgumentOutOfRangeException (nameof (offset));
if (size < 0 || (length - offset) < size)
throw new ArgumentOutOfRangeException (nameof (size));
try {
return ReadAsync (buffer, offset, size, CancellationToken.None).Result;
} catch (Exception e) {
throw GetException (e);
}
}
public override IAsyncResult BeginRead (byte[] buffer, int offset, int size,
AsyncCallback cb, object state)
{
if (!CanRead)
throw new NotSupportedException (SR.net_writeonlystream);
Operation.ThrowIfClosedOrDisposed ();
if (buffer == null)
throw new ArgumentNullException (nameof (buffer));
int length = buffer.Length;
if (offset < 0 || length < offset)
throw new ArgumentOutOfRangeException (nameof (offset));
if (size < 0 || (length - offset) < size)
throw new ArgumentOutOfRangeException (nameof (size));
var task = ReadAsync (buffer, offset, size, CancellationToken.None);
return TaskToApm.Begin (task, cb, state);
}
public override int EndRead (IAsyncResult r)
{
if (r == null)
throw new ArgumentNullException (nameof (r));
try {
return TaskToApm.End<int> (r);
} catch (Exception e) {
throw GetException (e);
}
}
public sealed override async Task<int> ReadAsync (
byte[] buffer, int offset, int size,
CancellationToken cancellationToken)
{
Operation.ThrowIfDisposed (cancellationToken);
if (buffer == null)
throw new ArgumentNullException (nameof (buffer));
int length = buffer.Length;
if (offset < 0 || length < offset)
throw new ArgumentOutOfRangeException (nameof (offset));
if (size < 0 || (length - offset) < size)
throw new ArgumentOutOfRangeException (nameof (size));
WebConnection.Debug ($"{ME} READ");
int nread;
try {
nread = await ProcessReadAsync (
buffer, offset, size, cancellationToken).ConfigureAwait (false);
WebConnection.Debug ($"{ME} READ DONE: nread={nread}");
if (nread != 0)
return nread;
await FinishReading (cancellationToken).ConfigureAwait (false);
return 0;
} catch (OperationCanceledException) {
WebConnection.Debug ($"{ME} READ CANCELED");
throw;
} catch (Exception ex) {
WebConnection.Debug ($"{ME} READ ERROR: {ex.GetType ().Name}");
throw;
} finally {
WebConnection.Debug ($"{ME} READ FINISHED");
}
}
protected abstract Task<int> ProcessReadAsync (
byte[] buffer, int offset, int size,
CancellationToken cancellationToken);
internal virtual Task FinishReading (CancellationToken cancellationToken)
{
Operation.ThrowIfDisposed (cancellationToken);
WebConnection.Debug ($"{ME} FINISH READING: InnerStream={InnerStream?.GetType ()?.Name}!");
if (InnerStream is WebReadStream innerReadStream)
return innerReadStream.FinishReading (cancellationToken);
return Task.CompletedTask;
}
bool disposed;
protected override void Dispose (bool disposing)
{
if (disposing && !disposed) {
disposed = true;
if (InnerStream != null)
InnerStream.Dispose ();
}
base.Dispose (disposing);
}
}
}

View File

@@ -50,8 +50,10 @@ namespace System.Net
public WebRequestStream (WebConnection connection, WebOperation operation,
Stream stream, WebConnectionTunnel tunnel)
: base (connection, operation, stream)
: base (connection, operation)
{
InnerStream = stream;
allowBuffering = operation.Request.InternalAllowBuffering;
sendChunked = operation.Request.SendChunked && operation.WriteBuffer == null;
if (!sendChunked && allowBuffering && operation.WriteBuffer == null)
@@ -66,14 +68,12 @@ namespace System.Net
#endif
}
public bool KeepAlive {
internal Stream InnerStream {
get;
}
public override long Length {
get {
throw new NotSupportedException ();
}
public bool KeepAlive {
get;
}
public override bool CanRead => false;

File diff suppressed because it is too large Load Diff