You've already forked linux-packaging-mono
Imported Upstream version 5.16.0.100
Former-commit-id: 38faa55fb9669e35e7d8448b15c25dc447f25767
This commit is contained in:
parent
0a9828183b
commit
7d7f676260
69
mcs/class/System/System.Net/BufferedReadStream.cs
Normal file
69
mcs/class/System/System.Net/BufferedReadStream.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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; }
|
||||
}
|
||||
|
92
mcs/class/System/System.Net/ContentDecodeStream.cs
Normal file
92
mcs/class/System/System.Net/ContentDecodeStream.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
73
mcs/class/System/System.Net/FixedSizeReadStream.cs
Normal file
73
mcs/class/System/System.Net/FixedSizeReadStream.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
356
mcs/class/System/System.Net/MonoChunkParser.cs
Normal file
356
mcs/class/System/System.Net/MonoChunkParser.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -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
|
||||
//
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -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)
|
||||
|
@@ -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); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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)
|
||||
|
220
mcs/class/System/System.Net/WebReadStream.cs
Normal file
220
mcs/class/System/System.Net/WebReadStream.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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
Reference in New Issue
Block a user