// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNet.Server.Kestrel.Networking;
using System;
using System.Runtime.InteropServices;
namespace Microsoft.AspNet.Server.Kestrel.Http
{
///
/// Operations performed for buffered socket output
///
public interface ISocketOutput
{
void Write(ArraySegment buffer, Action callback, object state);
}
public class SocketOutput : ISocketOutput
{
private readonly KestrelThread _thread;
private readonly UvStreamHandle _socket;
public SocketOutput(KestrelThread thread, UvStreamHandle socket)
{
_thread = thread;
_socket = socket;
}
public void Write(ArraySegment buffer, Action callback, object state)
{
KestrelTrace.Log.ConnectionWrite(0, buffer.Count);
var req = new ThisWriteReq();
req.Init(_thread.Loop);
req.Contextualize(this, _socket, buffer, callback, state);
_thread.Post(x =>
{
((ThisWriteReq)x).Write();
}, req);
}
public class ThisWriteReq : UvWriteReq
{
private static readonly Action _writeCallback = WriteCallback;
private static void WriteCallback(UvWriteReq req, int status, object state)
{
((ThisWriteReq)state).OnWrite(req, status);
}
SocketOutput _self;
ArraySegment _buffer;
Action _drained;
UvStreamHandle _socket;
Action _callback;
object _state;
GCHandle _pin;
internal void Contextualize(
SocketOutput socketOutput,
UvStreamHandle socket,
ArraySegment buffer,
Action callback,
object state)
{
_self = socketOutput;
_socket = socket;
_buffer = buffer;
_callback = callback;
_state = state;
}
public void Write()
{
_pin = GCHandle.Alloc(_buffer.Array, GCHandleType.Pinned);
var buf = new Libuv.uv_buf_t
{
len = (uint)_buffer.Count,
memory = _pin.AddrOfPinnedObject() + _buffer.Offset
};
Write(
_socket,
new[] { buf },
1,
_writeCallback,
this);
}
private void OnWrite(UvWriteReq req, int status)
{
_pin.Free();
KestrelTrace.Log.ConnectionWriteCallback(0, status);
//NOTE: pool this?
Dispose();
_callback(_state);
}
}
public bool Flush(Action drained)
{
return false;
}
}
}