/*
* Copyright 2012 Splunk, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"): you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
namespace Splunk
{
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Security;
using System.Net.Sockets;
using System.Security.Cryptography.X509Certificates;
using System.Text;
///
/// The receiver class. This class exposes methods to send events to splunk
/// via the simple or streaming receiver endpoint.
///
public class Receiver
{
///
/// A reference to the attached service.
///
private Service service = null;
///
/// Initializes a new instance of the class.
///
/// The service
public Receiver(Service service)
{
this.service = service;
}
///
/// Gets or sets the RemoteCertificateValidationCallback delegate
/// responsible for validating the certificate supplied by the Splunk
/// server if SSL (i.e. https) is used.
/// If none is set (which is the default),
/// no validation will be performed.
///
public RemoteCertificateValidationCallback
SSLRemoteCertificateValidationCallback
{
get; set;
}
///
/// Gets or sets the LocalCertificateSelectionCallback delegate
/// responsible for selecting the certificate used for authentication
/// with the Splunk server if SSL (i.e. https) is used.
/// If none is set (which is the default),
/// no local certificate is used.
///
public LocalCertificateSelectionCallback
SSLLocalCertificateValidationCallback
{
get; set;
}
///
/// Gets or sets the EncryptionPolicy to use with the Splunk server
/// if SSL (i.e. https) is used.
///
public EncryptionPolicy SSLEncryptionPolicy
{
get; set;
}
///
/// Creates a socket to the splunk server using the default index, and
/// default port.
///
/// The Stream
public Stream Attach()
{
return this.Attach(null, null);
}
///
/// Creates a socket to the splunk server using the named index, and
/// default port.
///
/// The index to write to
/// The Stream
public Stream Attach(string indexName)
{
return this.Attach(indexName, null);
}
///
/// Creates a socket to the splunk server using the default index and
/// variable arguments.
///
/// The variable arguments
/// The Socket
public Stream Attach(Args args)
{
return this.Attach(null, args);
}
///
/// Creates a socket to the splunk server using the named index and
/// variable arguments.
///
/// The index name
/// The variable arguments
/// The Socket
public Stream Attach(string indexName, Args args)
{
Stream stream;
if (this.service.Scheme == HttpService.SchemeHttps)
{
TcpClient tcp = new TcpClient();
tcp.Connect(this.service.Host, this.service.Port);
var sslStream = new SSLStreamWrapper(tcp);
sslStream.AuthenticateAsClient(this.service.Host);
stream = sslStream;
}
else
{
Socket socket = this.service.Open(this.service.Port);
stream = new NetworkStream(socket, true);
}
string postUrl = "POST /services/receivers/stream";
if (indexName != null)
{
postUrl = postUrl + "?index=" + indexName;
}
if (args != null && args.Count > 0)
{
postUrl = postUrl + ((indexName == null) ? "?" : "&");
postUrl = postUrl + args.Encode();
}
string header = string.Format(
"{0} HTTP/1.1\r\n" +
"Host: {1}:{2}\r\n" +
"Accept-Encoding: identity\r\n" +
"Authorization: {3}\r\n" +
"X-Splunk-Input-Mode: Streaming\r\n\r\n",
postUrl,
this.service.Host,
this.service.Port,
this.service.Token);
var bytes = Encoding.UTF8.GetBytes(header);
stream.Write(bytes, 0, bytes.Length);
stream.Flush();
return stream;
}
///
/// Submits the data using HTTP post, to the default index
///
/// The data
public void Submit(string data)
{
this.Submit(null, null, data);
}
///
/// Submits the data using HTTP post, to the named index
///
/// The index name
/// The data
public void Submit(string indexName, string data)
{
this.Submit(indexName, null, data);
}
///
/// Submits the data using HTTP post, using variable arguments to the
/// default index.
///
/// The variable arguments
/// The data
public void Submit(Args args, string data)
{
this.Submit(null, args, data);
}
///
/// Submits the data to the named index using variable arguments.
///
/// The named index
/// The variable arguments
/// The data
public void Submit(string indexName, Args args, string data)
{
string sendString = string.Empty;
RequestMessage request = new RequestMessage("POST");
request.Content = data;
if (indexName != null)
{
sendString = string.Format("?index={0}", indexName);
}
if (args != null && args.Count > 0)
{
sendString = sendString + ((indexName == null) ? "?" : "&");
sendString = sendString + args.Encode();
}
this.service.Send(
this.service.SimpleReceiverEndPoint + sendString, request);
}
///
/// Alias for submit()
///
/// The data
public void Log(string data)
{
this.Submit(data);
}
///
/// Alias for submit()
///
/// The index name
/// The data
public void Log(string indexName, string data)
{
this.Submit(indexName, data);
}
///
/// Alias for submit()
///
/// The arguments
/// The data
public void Log(Args args, string data)
{
this.Submit(args, data);
}
///
/// Alias for submit()
///
/// The index name
/// The arguments
/// The data
public void Log(string indexName, Args args, string data)
{
this.Submit(indexName, args, data);
}
///
/// Wrapper class of SslStream for closing TCP connection
/// when closing the stream
///
private class SSLStreamWrapper : SslStream
{
///
/// The TcpClient object the SSLStream object is based on.
///
private TcpClient tcpClient;
///
/// Initializes a new instance of the class.
///
///
/// A TcpClient object the SSLStream object is based on.
///
public SSLStreamWrapper(
TcpClient tcpClient)
: base(
tcpClient.GetStream(),
false,
ServicePointManager.ServerCertificateValidationCallback)
{
this.tcpClient = tcpClient;
}
///
/// Release resources including the tcpClient.
///
/// True to release both managed and unmanaged resources;
/// false to release only unmanaged resources.
protected override void Dispose(bool disposing)
{
// Dispose(bool disposing) executes in two distinct scenarios.
// If disposing equals true, the method has been called directly
// or indirectly by a user's code. Managed and unmanaged resources
// can be disposed.
// If disposing equals false, the method has been called by the
// runtime from inside the finalizer and you should not reference
// other objects. Only unmanaged resources can be disposed.
// If this is called from the finalizer, don't reference tcpClient.
// It is ok since its manage resources will be cleaned up as part of regular GC and
// the runtime will call its Dispose with 'disposing' being false.
if (disposing)
{
this.tcpClient.Close();
}
}
}
}
}