#include "StdAfx.h"
#include "ConnectionControl.h"
#include "HTTPHeaderParser.h"
#include "BandWidthControl.h"

#define	REUSE_CONNECTION_DISABLE	-1
#define	REUSE_CONNECTION_REUSE		1
#define	REUSE_CONNECTION_NOT_REUSE	0

#define	DEFAULT_WAIT_NEXT_REQUEST_TIME	(20 * 1000)
#define	MAX_WAIT_NEXT_REQUEST_TIME		(3600 * 1000)
#define	MIN_WAIT_NEXT_REQUEST_TIME		(1 * 1000)

#define	PROCESS_STATE_START					0
#define	PROCESS_STATE_REQUEST_RECEIVING		1000
#define	PROCESS_STATE_REQUEST_RECEIVED		1100
#define	PROCESS_STATE_RESPONSE_BUFFERING	2000
#define	PROCESS_STATE_RESPONSE_SENDING		3000
#define	PROCESS_STATE_RESPONSE_SENDED		3100

#define	MAX_BUFFERING_SIZE		(1 * 1024 * 1024)
#define	MAX_WORKING_BUF_SIZE	(65536)

CConnectionControl::CConnectionControl(void)
{
}

CConnectionControl::~CConnectionControl(void)
{
	DetacheConnection();
}


/*!
	開始
*/
void CConnectionControl::AttacheConnection(IServerToolIIPtr serverTool, IConnectionThreadStatusIIPtr threadStatus, IClientConnectionTempIIPtr connection, CContext context)
{
	m_setting = serverTool->GetManagerSetting(m_setting.GetSettingName());

	m_threadStatus = threadStatus;
	m_connection = connection;
	m_context = context;
	m_bufferingMode = BUFFRING_MODE_NONE;
	m_processState = PROCESS_STATE_START;
	m_reuseConnection = REUSE_CONNECTION_NOT_REUSE;
	m_waitNextRequestTime = DEFAULT_WAIT_NEXT_REQUEST_TIME;
	m_responseBody.Empty();

	m_requestBodySizeLeft = 0;
	m_responseBodySizeLeft = 0;
	m_receivedSize = 0;
	m_sendedSize = 0;

	m_sendLimit = -1;
	m_recvLimit = -1;

	m_maxBufferingSize = m_setting.GetMaxBufferingSize();
	m_workingBufSize = m_setting.GetBlockSize();

	//	クライアント情報設定
	m_connection->GetClientInfo(m_context.GetClientInfo());
}

/*!
	終了
*/
void CConnectionControl::DetacheConnection()
{
}

//////////////////////////////////////////////////////////////////////////////
//	IConnectionControl
//////////////////////////////////////////////////////////////////////////////
/*!
	接続中か取得する
*/
int CConnectionControl::IsConnecting()
{
	return m_connection->IsConnecting();
}


/*!
	バッファリングモードへ移行する
*/
int CConnectionControl::ChangeBufferingMode(int mode)
{
	if(mode == BUFFRING_MODE_BUFFURING)
		return(EnterBufferingMode());
	else if(mode == BUFFRING_MODE_STREAMING)
		return(EnterStreamingMode());
	else
		return(-1);
}

/*!
	現在のモードを取得する
*/
int CConnectionControl::GetCurrentMode()
{
	return m_bufferingMode;
}


/*!
	KeepAliveを無効に
*/
void CConnectionControl::DisableReuseConnection()
{
	m_reuseConnection = REUSE_CONNECTION_DISABLE;
}

/*!
	KeepAlive状態をセット(DisableReuseConnectionが優先される)
*/
void CConnectionControl::SetReuseConnection(int reuse)
{
	if(m_reuseConnection != REUSE_CONNECTION_DISABLE)
	{
		if(reuse)
			m_reuseConnection = REUSE_CONNECTION_REUSE;
		else
			m_reuseConnection = REUSE_CONNECTION_NOT_REUSE;
	}
}

/*!
	KeepAliveは現在有効か？
*/
int CConnectionControl::CanReuseConnection()
{
	if(m_reuseConnection == REUSE_CONNECTION_REUSE)
		return(TRUE);
	return(FALSE);
}


/*!
	最大KeepAlive時間を指定
*/
void CConnectionControl::SetWaitNextRequestTime(int time)
{
	if(time > MAX_WAIT_NEXT_REQUEST_TIME)
		time = MAX_WAIT_NEXT_REQUEST_TIME;
	if(time < MIN_WAIT_NEXT_REQUEST_TIME)
		time = MIN_WAIT_NEXT_REQUEST_TIME;

	m_waitNextRequestTime = time;
}


//////////////////////////////////////////////////////////////////////////////////////////////
//	リクエスト受信
//////////////////////////////////////////////////////////////////////////////////////////////
/*!
	ヘッダを受信(throw)
*/
void CConnectionControl::RecvRequestHeader()
{
	//	状態チェック
	ASSERT(m_processState == PROCESS_STATE_START);

	//	状態設定
	m_processState = PROCESS_STATE_REQUEST_RECEIVING;

	//	受信
	InternalRecvHeader();

	//	リクエストボディサイズチェック
	if(m_requestBodySizeLeft == 0)
		m_processState = PROCESS_STATE_REQUEST_RECEIVED;	//	完了状態へ
}

/*!
	ボディを受信、繰り返し(throw)
*/
int CConnectionControl::RecvRequestBody(CBinaryData &body)
{
	//	状態チェック
	ASSERT(m_processState == PROCESS_STATE_REQUEST_RECEIVING || m_processState == PROCESS_STATE_REQUEST_RECEIVED);

	//	完了？
	if(m_requestBodySizeLeft == 0)
		return(0);

	//	受信
	int	recvSize = InternalRecvBody(body, m_requestBodySizeLeft);
	m_requestBodySizeLeft -= recvSize;

	return(recvSize);
}

/*!
	受信処理完了
*/
void CConnectionControl::EndRecvRequest()
{
	//	状態チェック
	ASSERT(m_processState == PROCESS_STATE_REQUEST_RECEIVING || m_processState == PROCESS_STATE_REQUEST_RECEIVED);

	//	エラー？
	if(m_requestBodySizeLeft > 0)
		throw CConnectionErrorException("内部エラー：未受信のリクエストボディがあります");

	m_connection->EndRecvRequest();
	m_processState = PROCESS_STATE_REQUEST_RECEIVED;
}

//////////////////////////////////////////////////////////////////////////////////////////////
//	レスポンス送信
//////////////////////////////////////////////////////////////////////////////////////////////
/*!
	ヘッダを送信(throw)
*/
void CConnectionControl::SendResponseHeader()
{
	//	状態チェック
	ASSERT(m_processState == PROCESS_STATE_REQUEST_RECEIVED);

	//	送信するか？
	if(m_bufferingMode != BUFFRING_MODE_BUFFURING)
	{
		//	状態変更
		m_processState = PROCESS_STATE_RESPONSE_SENDING;
		
			//	サイズ確定？
		if(m_bufferingMode == BUFFRING_MODE_STREAMING)
		{
			//	サイズ未定
			InternalSendHeader(-1);
		}
		else
		{
			//	サイズ決定
			m_responseBodySizeLeft = m_context.GetResponseInfo().GetResponseOption().GetContentLength();	//	ボディサイズの取得
			InternalSendHeader(m_responseBodySizeLeft);
		}
	}
	else
	{
		//	状態変更
		m_processState = PROCESS_STATE_RESPONSE_BUFFERING;

		//	バッファリングモードでは、何もしない
	}
}

/*!
	ボディを送信、繰り返し(throw)
*/
void CConnectionControl::SendResponseBody(CBinaryData &body)
{
	//	送信するか？
	if(m_bufferingMode != BUFFRING_MODE_BUFFURING)
	{
		//	状態チェック
		ASSERT(m_processState == PROCESS_STATE_RESPONSE_SENDING);

		//	サイズのチェック
		if(m_bufferingMode == BUFFRING_MODE_NONE)
		{
			if(body.GetSize() > m_responseBodySizeLeft)
				throw CConnectionErrorException("内部エラー：レスポンスボディのサイズに矛盾が発生しました");
			m_responseBodySizeLeft -= body.GetSize();
		}

		//	送信
		InternalSendBody(body);
	}
	else
	{
		//	状態チェック
		ASSERT(m_processState == PROCESS_STATE_RESPONSE_BUFFERING);

		//	バッファリング
		AddToBodyBuffer(body);
	}
}

/*!
	送信処理完了
*/
void CConnectionControl::EndSendResponse()
{
	//	バッファ中？
	if(m_bufferingMode != BUFFRING_MODE_BUFFURING)
	{
		//	状態チェック
		ASSERT(m_processState == PROCESS_STATE_RESPONSE_SENDING);

		//	サイズのチェック
		if(m_bufferingMode == BUFFRING_MODE_NONE)
		{
			if(m_responseBodySizeLeft > 0)
				throw CConnectionErrorException("内部エラー：レスポンスボディのサイズに矛盾が発生しました");
		}
	}
	else
	{
		//	データ吐き出し
		FlashBuffer(TRUE);
	}

	//	終了通知
	m_connection->EndSendResponse();
	m_processState = PROCESS_STATE_RESPONSE_SENDED;
}

//////////////////////////////////////////////////////////////////////////////////////////////
//	サーバレスポンス送信
//////////////////////////////////////////////////////////////////////////////////////////////
/*!
	サーバレスポンス送信
*/
void CConnectionControl::SendServerResponse(CBinaryData &body)
{
	//	安全に続行可能？
	if(m_processState != PROCESS_STATE_REQUEST_RECEIVED)
		DisableReuseConnection();	//	再利用無効

	//	レスポンス送信を試みる
	try
	{
		//	接続中か？
		if(IsConnecting())
		{
			//	ヘッダ送信前か？
			if(m_processState < PROCESS_STATE_RESPONSE_SENDING)
				InternalSendHeader(body.GetSize());	//	レスポンス送信

			//	データ送信
			InternalSendBody(body);
		}
	}
	catch(CConnectionErrorException err)
	{
		//	サーバレスポンスの通信エラーは無視する
		DisableReuseConnection();
	}

	//	状態設定
	m_processState = PROCESS_STATE_RESPONSE_SENDED;
}


//////////////////////////////////////////////////////////////////////////////
//	ツール関数
//////////////////////////////////////////////////////////////////////////////
/*!
	バッファへ追加
*/
void CConnectionControl::AddToBodyBuffer(CBinaryData &body)
{
	//	追加
	m_responseBody.AppendBinary(body);

	//	バッファがあふれた？
	if(m_responseBody.GetSize() >= m_maxBufferingSize)
		EnterStreamingMode();
}

/*!
	バッファリングモードへ移行する
*/
int CConnectionControl::EnterBufferingMode()
{
	if(m_processState >= PROCESS_STATE_RESPONSE_SENDING)
		throw CConnectionErrorException("レスポンス送信開始後にバッファリングモードを変更しました");

	//	変更可能？
	if(m_bufferingMode == BUFFRING_MODE_NONE)
	{
		//	長さを消す
		m_context.GetResponseInfo().GetResponseOption().ClearContentLength();

		//	モード変更
		m_bufferingMode = BUFFRING_MODE_BUFFURING;
		return(0);
	}
	else if(m_bufferingMode == BUFFRING_MODE_BUFFURING)
	{
		return(0);
	}
	else
	{
		return(-1);
	}
}


/*!
	ストリーミングモードへ移行する
*/
int CConnectionControl::EnterStreamingMode()
{
	if(m_processState >= PROCESS_STATE_RESPONSE_SENDING)
		throw CConnectionErrorException("レスポンス送信開始後にバッファリングモードを変更しました");

	//	現在のモードは？
	if(m_bufferingMode == BUFFRING_MODE_NONE)
	{
		//	モード変更
		m_bufferingMode = BUFFRING_MODE_STREAMING;
		DisableReuseConnection();

		//	長さを消す
		m_context.GetResponseInfo().GetResponseOption().ClearContentLength();
		return(0);
	}
	else if(m_bufferingMode == BUFFRING_MODE_BUFFURING)
	{
		//	モード変更
		m_bufferingMode = BUFFRING_MODE_STREAMING;
		DisableReuseConnection();

		//	バッファ内容をフラッシュ
		FlashBuffer(FALSE);
		return(0);
	}
	else if(m_bufferingMode == BUFFRING_MODE_STREAMING)
	{
		return(0);
	}
	else
	{
		return(-1);
	}
}


/*!
	バッファリング中の内容を送信
*/
void CConnectionControl::FlashBuffer(int bodyIsCompleted)
{
	//	状態チェック
	ASSERT(m_processState == PROCESS_STATE_RESPONSE_BUFFERING);

	//	状態変更
	m_processState = PROCESS_STATE_RESPONSE_SENDING;

	//	ヘッダ送信
	if(bodyIsCompleted)
		InternalSendHeader(m_responseBody.GetSize());
	else
		InternalSendHeader(-1);

	//	データ送信
	InternalSendBody(m_responseBody);
	m_responseBody.Empty();
}


//////////////////////////////////////////////////////////////////////////////
//	通信関数
//////////////////////////////////////////////////////////////////////////////
/*!
	ヘッダ受信を実行
*/
void CConnectionControl::InternalRecvHeader()
{
	//	ヘッダ領域取得
	CContextRequestInfo requestInfo = m_context.GetRequestInfo();

	//	ヘッダ受信
	CString	header;
	m_connection->RecvRequestHeader(header);
	m_receivedSize += header.GetLength();

	//	ヘッダ解析
	if(CHTTPHeaderParser::ParseRequestHeader(header, requestInfo))
		throw CConnectionErrorException("異常なHTTPヘッダを受信しました");

	//	リクエストボディサイズを取得
	m_requestBodySizeLeft = requestInfo.GetRequestOption().GetContentLength();
}

/*!
	ボディ受信を実行
*/
int CConnectionControl::InternalRecvBody(CBinaryData &data, __int64 maxRecvSize)
{
	CBandWidthControl	bandLimit;

	//	受信サイズ
	int	blockSize = m_workingBufSize;
	if(blockSize > maxRecvSize)
		blockSize = (int)maxRecvSize;

	//	受信
	blockSize = m_connection->RecvRequestBody(data, blockSize);
	m_receivedSize += blockSize;

	//	受信速度制限
	bandLimit.BandWidthWait(m_recvLimit, blockSize, m_threadStatus->GetBreakEvent());

	return blockSize;
}



/*!
	ヘッダ送信を実行

	\param	contentLength	-1:サイズ未定
*/
void CConnectionControl::InternalSendHeader(__int64 contentLength)
{
	//	ヘッダ情報取得
	CContextResponseInfo responseInfo = m_context.GetResponseInfo();

	//	サイズを設定
	if(contentLength >= 0)
		responseInfo.GetResponseOption().SetContentLength(contentLength);
	else
		responseInfo.GetResponseOption().ClearContentLength();

	//	ヘッダ送信
	CString	header = CHTTPHeaderParser::GetResponseHeader(responseInfo);
	m_connection->SendResponseHeader(header);
	m_sendedSize += header.GetLength();
}

/*!
	ボディ送信を実行
*/
int CConnectionControl::InternalSendBody(CBinaryData &data)
{
	//	一度に送れる？
	if(data.GetSize() < m_workingBufSize)
	{
		CBandWidthControl	bandLimit;

		//	データ送信
		m_connection->SendResponseBody(data);
		m_sendedSize += data.GetSize();

		//	速度制限
		bandLimit.BandWidthWait(m_sendLimit, data.GetSize(), m_threadStatus->GetBreakEvent());
	}
	else
	{
		char	*ptr = data.GetPtr();
		int		left = data.GetSize();

		while(left > 0)
		{
			CBandWidthControl	bandLimit;

			//	送信サイズ
			int	blockSize = m_workingBufSize;
			if(blockSize > left)
				blockSize = left;

			//	送信部切り出し
			CBinaryData	sendBuf;
			sendBuf.Append(ptr, blockSize);

			//	データ送信
			m_connection->SendResponseBody(sendBuf);
			ptr += blockSize;
			left -= blockSize;
			m_sendedSize += blockSize;
			ASSERT(left >= 0);

			//	速度制限
			bandLimit.BandWidthWait(m_sendLimit, blockSize, m_threadStatus->GetBreakEvent());
		}
	}

	return(0);
}

