// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Diagnostics; using System.Runtime.InteropServices; namespace Microsoft.AspNet.Server.Kestrel.Networking { public abstract class UvStreamHandle : UvHandle { private readonly static Libuv.uv_connection_cb _uv_connection_cb = UvConnectionCb; private readonly static Libuv.uv_alloc_cb _uv_alloc_cb = UvAllocCb; private readonly static Libuv.uv_read_cb _uv_read_cb = UvReadCb; public Action _listenCallback; public object _listenState; private GCHandle _listenVitality; public Func _allocCallback; public Action _readCallback; public object _readState; private GCHandle _readVitality; protected override bool ReleaseHandle() { if (_listenVitality.IsAllocated) { _listenVitality.Free(); } if (_readVitality.IsAllocated) { _readVitality.Free(); } return base.ReleaseHandle(); } public void Listen(int backlog, Action callback, object state) { if (_listenVitality.IsAllocated) { throw new InvalidOperationException("TODO: Listen may not be called more than once"); } try { _listenCallback = callback; _listenState = state; _listenVitality = GCHandle.Alloc(this, GCHandleType.Normal); _uv.listen(this, backlog, _uv_connection_cb); } catch { _listenCallback = null; _listenState = null; if (_listenVitality.IsAllocated) { _listenVitality.Free(); } throw; } } public void Accept(UvStreamHandle handle) { _uv.accept(this, handle); } public void ReadStart( Func allocCallback, Action readCallback, object state) { if (_readVitality.IsAllocated) { throw new InvalidOperationException("TODO: ReadStop must be called before ReadStart may be called again"); } try { _allocCallback = allocCallback; _readCallback = readCallback; _readState = state; _readVitality = GCHandle.Alloc(this, GCHandleType.Normal); _uv.read_start(this, _uv_alloc_cb, _uv_read_cb); } catch { _allocCallback = null; _readCallback = null; _readState = null; if (_readVitality.IsAllocated) { _readVitality.Free(); } throw; } } public void ReadStop() { if (!_readVitality.IsAllocated) { throw new InvalidOperationException("TODO: ReadStart must be called before ReadStop may be called"); } _allocCallback = null; _readCallback = null; _readState = null; _readVitality.Free(); _uv.read_stop(this); } public int TryWrite(Libuv.uv_buf_t buf) { return _uv.try_write(this, new[] { buf }, 1); } private static void UvConnectionCb(IntPtr handle, int status) { var stream = FromIntPtr(handle); Exception error; status = stream.Libuv.Check(status, out error); try { stream._listenCallback(stream, status, error, stream._listenState); } catch (Exception ex) { Trace.WriteLine("UvConnectionCb " + ex.ToString()); } } private static void UvAllocCb(IntPtr handle, int suggested_size, out Libuv.uv_buf_t buf) { var stream = FromIntPtr(handle); try { buf = stream._allocCallback(stream, suggested_size, stream._readState); } catch (Exception ex) { Trace.WriteLine("UvAllocCb " + ex.ToString()); buf = stream.Libuv.buf_init(IntPtr.Zero, 0); throw; } } private static void UvReadCb(IntPtr handle, int nread, ref Libuv.uv_buf_t buf) { var stream = FromIntPtr(handle); try { if (nread < 0) { Exception error; stream._uv.Check(nread, out error); stream._readCallback(stream, 0, error, stream._readState); } else { stream._readCallback(stream, nread, null, stream._readState); } } catch (Exception ex) { Trace.WriteLine("UbReadCb " + ex.ToString()); } } } }