Skip to content

Commit 1671f7f

Browse files
authored
Don't call Read multiple times in _SSLSocket.read (#1770)
* Don't call Read multiple times in _SSLSocket.read * Use ArrayPool instead
1 parent c8be3ef commit 1671f7f

1 file changed

Lines changed: 49 additions & 17 deletions

File tree

Src/IronPython.Modules/_ssl.cs

Lines changed: 49 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#if FEATURE_FULL_NET
66

77
using System;
8+
using System.Buffers;
89
using System.Collections.Generic;
910
using System.Diagnostics;
1011
using System.Globalization;
@@ -593,30 +594,61 @@ public string issuer() {
593594

594595
[Documentation(@"read(size, [buffer])
595596
Read up to size bytes from the SSL socket.")]
596-
public object read(CodeContext/*!*/ context, int size, ByteArray buffer = null) {
597+
public Bytes read(CodeContext/*!*/ context, int size) {
597598
EnsureSslStream(true);
598599

600+
if (size < 0) throw PythonOps.ValueError("size should not be negative");
601+
602+
var buf = ArrayPool.Rent(size);
599603
try {
600-
byte[] buf = new byte[2048];
601-
MemoryStream result = new MemoryStream(size);
602-
while (true) {
603-
int readLength = (size < buf.Length) ? size : buf.Length;
604-
int bytes = _sslStream.Read(buf, 0, readLength);
605-
if (bytes > 0) {
606-
result.Write(buf, 0, bytes);
607-
size -= bytes;
608-
}
604+
int numRead;
605+
try {
606+
numRead = _sslStream.Read(buf, 0, size);
607+
} catch (Exception e) {
608+
throw PythonSocket.MakeException(context, e);
609+
}
610+
611+
var bytes = new byte[numRead];
612+
Array.Copy(buf, bytes, bytes.Length);
613+
return Bytes.Make(bytes);
614+
} finally {
615+
ArrayPool.Return(buf);
616+
}
617+
}
618+
619+
private static ArrayPool<byte> ArrayPool => ArrayPool<byte>.Shared;
620+
621+
[Documentation(@"read(size, [buffer])
622+
Read up to size bytes from the SSL socket.")]
623+
public int read(CodeContext/*!*/ context, int size, [NotNone] IBufferProtocol buffer) {
624+
EnsureSslStream(true);
625+
626+
using var pythonBuffer = buffer.GetBufferNoThrow(BufferFlags.Writable)
627+
?? throw PythonOps.TypeError("read() argument 2 must be read-write bytes-like object, not {0}", PythonOps.GetPythonTypeName(buffer));
609628

610-
if (bytes == 0 || size == 0 || bytes < readLength) {
611-
var res = result.ToArray();
612-
if (buffer == null)
613-
return Bytes.Make(res);
629+
var bufferLen = pythonBuffer.ItemCount;
630+
if (size <= 0 || bufferLen < size) size = bufferLen;
614631

615-
// TODO: get rid of the MemoryStream and write directly to the buffer
616-
buffer[new Slice(0, res.Length)] = res;
617-
return res.Length;
632+
try {
633+
#if NETCOREAPP
634+
return _sslStream.Read(pythonBuffer.AsSpan().Slice(0, size));
635+
#else
636+
var buf = pythonBuffer.AsUnsafeWritableArray();
637+
if (buf is null) {
638+
buf = ArrayPool.Rent(size);
639+
try {
640+
var numRead = _sslStream.Read(buf, 0, size);
641+
buf.AsSpan(0, numRead).CopyTo(pythonBuffer.AsSpan());
642+
return numRead;
643+
}
644+
finally {
645+
ArrayPool.Return(buf);
618646
}
619647
}
648+
else {
649+
return _sslStream.Read(buf, 0, size);
650+
}
651+
#endif
620652
} catch (Exception e) {
621653
throw PythonSocket.MakeException(context, e);
622654
}

0 commit comments

Comments
 (0)