#include "StdAfx.h"
#include "04WebServer.h"
#include "connection.h"
#include "threadskeleton.h"
#include "server.h"
#include "dateutility.h"
#include "intervaltimer.h"
#include "StringCompare.h"
#include "Dir.h"
#include "subprocess.h"

#include "systemresponse.h"
#include "normalresponse.h"
#include "directoryresponse.h"
#include "fileresponse.h"
#include "cgiresponse.h"
#include "ssiresponse.h"

#include "davpropfindresponse.h"
#include "fileputresponse.h"
#include "filedeleteresponse.h"
#include "filemoveresponse.h"
#include "optionsresponse.h"
#include "keyswapstring.h"
#include "SSLProtocolSocket.h"
#include "DigestAuthenticate.h"

/*!
	接続を生成

	CConnectionExceptionを外部へ投げる可能性あり
*/
CConnection::CConnection(CServer *server, CAcceptedSocket newSockete)
: m_interrupt(this)
{
	DEBUG_SET_THREAD_CLASS_NAME("CConnection");

	//	変数初期化
	m_server = server;
	m_maxKeepAliveTime = m_server->m_setting.GetConfig("Basic::KeepAliveTime", 20) * 1000;
	m_keepAlive = m_server->m_setting.GetConfig("Basic::EnableKeepAlive", 0);
	m_serverTimeout = m_server->m_setting.GetConfig(_T("Basic::TimeOut"), 300) * 1000;

	//	ソケット設定
	m_rawSocket = newSockete.GetSocket();
	m_rawSocketType = newSockete.GetSocketType();

	//	スレッド開始
	if(StartThread())
		throw CConnectionException("スレッドの開始に失敗しました");
}

/*!
	接続を破棄
*/
CConnection::~CConnection(void)
{
	EndThread();

	SAFE_DELETE(m_protocolSocket);
}


/*!
	中断
*/
void CConnection::Break()
{
	CThreadSkeleton::Break();
}


////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	接続処理
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/*!
	メインループ
*/
void CConnection::ThreadMain()
{
	DEBUG_SET_THREAD_FUNCTION_NAME("CConnection::ThreadMain");

	//	ソケット準備
	m_protocolSocket = CreateProtocolSocket(m_rawSocket, m_rawSocketType);
	if(m_protocolSocket == NULL)
	{
		closesocket(m_rawSocket);
		return;
	}

	//	KeepAliveループ
	while(1)
	{
		DEBUG_SET_THREAD_FUNCTION_NAME("CConnection::ThreadMain::KeepAliveLoop");

		//	アクセスは？
		if(!m_protocolSocket->WaitNextRequest(m_maxKeepAliveTime))
			break;	//	タイムアウトか切断

		//	新しい環境生成
		CWorkspaceAccess context = NewContext();

		//	１リクエストの処理を行う
		try
		{
			//	初期化
			m_sendSize = 0;
			m_recvSize = 0;
			m_recvDataSize = 0;

			//	接続処理
			int KeepAlive = OneRequest(context);

			//	転送量記録
			UpdateTransSize(context);

			//	正常に終了したのでサーバログに出力
			WriteLog(context);

			//	環境削除
			DeleteContext(context);

			//	切断？
			if(KeepAlive == 0)
				break;
		}
		catch(CServerFatalException e)
		{
			//	転送量記録
			UpdateTransSize(context);

			//	切断の必要のあるエラーが発生したので、ログにエラーを出力
			UpdateConnectionStatus(context,  e.GetErrorInfo(), CONNECTION_PROCESS_COMPLETE);
			WriteLog(context);

			//	環境削除
			DeleteContext(context);
			return;
		}

		//	KeepAliveは有効ではない？ サーバは高負荷？
		if(m_keepAlive==0 || m_server->ServerIsOverload())
			break;
	}

	//	ソケットを処分
	SAFE_DELETE(m_protocolSocket);
}


/*!
	対応するソケットラッパ生成
*/
CProtocolSocket *CConnection::CreateProtocolSocket(SOCKET socket, int socketType)
{
	try
	{
		switch(socketType)
		{
			case SOCKET_TYPE_HTTP:
			default:
			{
				CProtocolSocket *protocolSocket = new CProtocolSocket();
				protocolSocket->AttachSocket(socket);
				protocolSocket->SetBreakEvent(GetBreakEvent());
				protocolSocket->SetTimeout(m_serverTimeout);
				return(protocolSocket);
			}

			case SOCKET_TYPE_HTTPS:
			{
				CSSLProtocolSocket *protocolSocket = new CSSLProtocolSocket();
				protocolSocket->AttachSocket(socket);
				protocolSocket->SetBreakEvent(GetBreakEvent());
				protocolSocket->SetTimeout(m_serverTimeout);

				//	SSL開始
				if(protocolSocket->SSLAccept(m_server->GetCTX()))
				{
					delete protocolSocket;
					return(NULL);
				}
				return(protocolSocket);
			}
		}
	}
	catch(CMemoryException *err)
	{
		err->Delete();
		return(NULL);
	}
}


////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	１リクエストを処理
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/*!
	１リクエストの処理を行う
*/
int CConnection::OneRequest(CWorkspaceAccess context)
{
	DEBUG_SET_THREAD_FUNCTION_NAME("CConnection::OneRequest::Step1");

	/*
		リクエストの受信と解析を行う
	*/
	//	ホスト情報
	context.SetConfig("Host", (CString)m_protocolSocket->GetPeerAddres());

	//	ヘッダーの受信
	CString		header = RecvHeader();

	//	リクエストの解析
	CRequest	request(header, context.GetAccess("RequestInfo"));

	//	ターゲットホスト情報
	context.SetConfig("TargetHost", request.GetOptionAccess().GetConfig("Host", "", TRUE));

	//	パスの解析
	CAlias		alias(request.GetURIObject(), request.GetOptionAccess().GetConfig("Host", "", TRUE), m_server->m_setting);

	//	帯域幅管理
	CBandWidthReference	bandRef(m_server->GetCBandWidthMgr(), m_protocolSocket->GetPeerAddres(), context.GetConfig("ThreadName", ""));

	//	レスポンス
	CResponse *use = NULL;

	//	Keep-Aliveの解析
	int KeepAlive;
	if(request.GetOptionAccess().GetConfig("Connection", "", TRUE).CompareNoCase("keep-alive") == 0)
		KeepAlive = 1;
	else
		KeepAlive = 0;


	DEBUG_SET_THREAD_FUNCTION_NAME("CConnection::OneRequest::Step2");

	//	致命的エラースコープ(500)
	try
	{
		//	サーバーレスポンススコープ(301, 404 etc...)
		try
		{
			//	標準レスポンススコープ
			try
			{
				//	サーバが過負荷状態かチェックする
				CheckServerOverload(context);

				//	アクセス制限をチェックする
				CheckAccess(context, request, alias, bandRef);

				//	ユーザー認証をチェックする
				CheckAuthorize(context, request, alias);

				//	WebDAV
				WebDAV(context, request, alias);

				//	移動？
				CheckMove(context, request, alias);

				/*
					ここ以降、Indexファイルに影響される
				*/
				//	インデックスファイルへ移動
				CheckIndex(context, request, alias);

				//	CGI?
				CGI(context, request, alias);

				//	SSI?
				SSI(context, request, alias);


				//	ディレクトリ表示？
				if(alias.IsDirectory() && !alias.IsIndexExist())
					throw new CDirectoryResponse(m_server, context, request, alias);

				//	最終はファイル
				throw new CFileResponse(m_server, context, request, alias);
			}
			catch(CNormalResponse *response)
			{
				DEBUG_SET_THREAD_FUNCTION_NAME("CConnection::OneRequest::CNormalResponse");

				//	例外発生時、上のスコープで削除
				use = response;

				//	レスポンス送信
				SendResponse(context, request, alias, bandRef, response);
				delete response;

				//	メッセージ
				UpdateConnectionStatus(context, _T("正常に終了しました"), CONNECTION_PROCESS_COMPLETE);

				//	正常終了
				return KeepAlive;
			}
		}
		catch(CSystemResponse *response)
		{
			DEBUG_SET_THREAD_FUNCTION_NAME("CConnection::OneRequest::CSystemResponse");

			//	下のスコープのデータを削除
			SAFE_DELETE(use);

			//	例外発生時、上のスコープで削除
			use = response;

			//	リスタート
			RestartConnectionProcess(context);

			//	レスポンス送信
			SendResponse(context, request, alias, bandRef, response);
			
			//	メッセージ
			UpdateConnectionStatus(context, response->GetInfo(), CONNECTION_PROCESS_COMPLETE);

			delete response;

			//	正常終了
			return KeepAlive;
		}
	}
	catch(CServerFatalException e)
	{
		DEBUG_SET_THREAD_FUNCTION_NAME("CConnection::OneRequest::CServerFatalException");

		//	下のスコープのデータを削除
		SAFE_DELETE(use);

		//	致命的サーバエラー
		if(CanSendResponse(context))	//	まだ送信してない場合のみ
			SendFatalError(e.GetErrorInfo());

		//	エラー登録
		context.SetConfig("ResponseInfo::ResponseCode", 500);
		context.SetConfig("ResponseInfo::ResponseText", CResponse::GetHTTPResponseString(500));

		//	上に投げる
		throw CServerFatalException(e.GetErrorInfo());
	}
	catch(CMemoryException *err)
	{
		DEBUG_SET_THREAD_FUNCTION_NAME("CConnection::OneRequest::CMemoryException");

		err->Delete();

		//	下のスコープのデータを削除
		SAFE_DELETE(use);

		//	致命的サーバエラー
		if(CanSendResponse(context))	//	まだ送信してない場合のみ
			SendFatalError("リクエストの処理中に、メモリ不足が発生しました");

		//	エラー登録
		context.SetConfig("ResponseInfo::ResponseCode", 500);
		context.SetConfig("ResponseInfo::ResponseText", CResponse::GetHTTPResponseString(500));

		//	上に投げる
		throw CServerFatalException("リクエストの処理中に、メモリ不足が発生しました");
	}

	//	ここには来ない筈
	ASSERT(0);
}


/*!
	リスタート(リスタート不可の場合は、例外を投げる)
*/
void CConnection::RestartConnectionProcess(CWorkspaceAccess &context)
{
	//	リスタート不可能か
	if(GetConnectionProcessStatus(context) > CONNECTION_PROCESS_CREATING_RESPONSE)	//	まだ送信してない場合のみ
		throw CServerFatalException(_T("サーバ内の処理順序に矛盾が発生しました、無効なレスポンス処理のリスタートです"));

	//	リスタート状態に
	UpdateConnectionStatus(context,  _T("再初期化中です..."), CONNECTION_PROCESS_RESTART, TRUE);

	TRACE(_T("レスポンス処理をリスタートしました\n"));
}


/*!
	新しいコンテキストの取得
*/
CWorkspaceAccess CConnection::NewContext()
{
	DEBUG_SET_THREAD_FUNCTION_NAME("CConnection::NewContext");

	//	コンテキスト作成
	CWorkspaceAccess newContext = m_server->GetContextManager()->GetNewContext();

	//	メッセージ設定
	UpdateConnectionStatus(newContext, _T("初期化中です..."), CONNECTION_PROCESS_STARTING);

	//	タイプ設定
	if(m_protocolSocket->GetSocketType() == SOCKET_TYPE_HTTPS)
	{
		newContext.SetConfig("Protocol", "https");
	}
	else
	{
		newContext.SetConfig("Protocol", "http");
	}

	return(newContext);
}


/*!
	コンテキストの破棄
*/
void CConnection::DeleteContext(CWorkspaceAccess context)
{
	DEBUG_SET_THREAD_FUNCTION_NAME("CConnection::DeleteContext");

	//	削除
	m_server->GetContextManager()->DeleteContext(context);
}


/*!
	ログ
*/
void CConnection::WriteLog(CWorkspaceAccess &context)
{
	DEBUG_SET_THREAD_FUNCTION_NAME("CConnection::WriteLog");

	//	置き換えもとソースを設定
	CKeySwapString	swapper;
	CString			format = m_server->m_setting.GetConfig("Log::LogFormatNoTime", "[%Time%] <%Host%> (%RecvSize%, %SendSize%) [%Info%] %Method% %Object%");
	swapper.SetString(format);

	CWorkspaceAccess	request = context.GetAccess("RequestInfo");
	CWorkspaceAccess	response = context.GetAccess("ResponseInfo");

	//	Userが設定されていない場合 "-" に置き換え
	if(request.GetConfig("User", "").IsEmpty())
		swapper.Swap("User", "-");

	//	コンテキスト情報
	swapper.Swap(context);

	//	標準情報
	swapper.Swap(request);
	swapper.Swap(response);

	swapper.Swap(request.GetAccess("Options"));
	swapper.Swap(response.GetAccess("Options"));

	//	改行を削除
	CString	logLine = swapper.GetString("-");
	logLine.Replace("\n", "_");
	logLine.Replace("\r", "_");

	m_server->m_log.WriteLog("%s", logLine);
}


////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	レスポンス作成
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/*!
	高負荷かチェック
*/
void CConnection::CheckServerOverload(CWorkspaceAccess &context)
{
	//	高負荷か？
	if(m_server->ServerIsOverload())
		throw new CSystemResponse(m_server, context, _T("サーバが高負荷状態のため、一時的にサービスを停止しています。"), 503);
}

/*!
 *	アクセスが有効かチェック
 */
void CConnection::CheckAccess(CWorkspaceAccess &context, CRequest &request, CAlias &alias, CBandWidthReference &bandRef)
{
	DEBUG_SET_THREAD_FUNCTION_NAME("CConnection::CheckAccess");

	//	HTTP不許可？
	if(m_protocolSocket->GetSocketType() == SOCKET_TYPE_HTTP && alias.GetPathOptions().GetConfig("AcceptHTTP", 1)==0)
		throw new CSystemResponse(m_server, context, m_server->m_setting.GetConfig("InfoMessage::DeniedHTTP", "HTTP からのアクセスは、許可されていません"), m_server->m_setting.GetConfig("InfoMessage::DeniedHTTPCode", 403));

	//	HTTPS不許可？
	if(m_protocolSocket->GetSocketType() == SOCKET_TYPE_HTTPS && alias.GetPathOptions().GetConfig("AcceptHTTPS", 1)==0)
		throw new CSystemResponse(m_server, context, m_server->m_setting.GetConfig("InfoMessage::DeniedHTTPS", "HTTPS からのアクセスは、許可されていません"), m_server->m_setting.GetConfig("InfoMessage::DeniedHTTPSCode", 403));

	//	キック？
	if(CheckKickHost(context, request, alias))
		throw new CSystemResponse(m_server, context, m_server->m_setting.GetConfig("InfoMessage::KickHost", "アクセスは、許可されていません"), m_server->m_setting.GetConfig("InfoMessage::KickHostCode", 403));
		
	//	接続制限チェック
	if(bandRef.IsLimitOver())
		throw new CSystemResponse(m_server, context, m_server->m_setting.GetConfig("InfoMessage::ConnectLimit", "１ホストの最大接続数を超えました"), m_server->m_setting.GetConfig("InfoMessage::ConnectLimitCode", 503));

	//	隠しファイルは無効？
	if(alias.GetPathOptions().GetConfig("EnableHiddenFile", 0) == 0 && alias.IsHidden())
		throw new CSystemResponse(m_server, context, m_server->m_setting.GetConfig("InfoMessage::HiddenDisable", "このファイル・ディレクトリへのアクセスは、許可されていません"), m_server->m_setting.GetConfig("InfoMessage::HiddenDisableCode", 403));
}

/*!
 *	認証のチェック
 *
 */
void CConnection::CheckAuthorize(CWorkspaceAccess &context, CRequest &request, CAlias &alias)
{
	DEBUG_SET_THREAD_FUNCTION_NAME("CConnection::CheckAuthorize");

	if(alias.GetPathOptions().GetConfig("EnableAuthenticate", 0))
	{
		CDigestAuthenticate	auth;

		AUTH_PARSE_STATUS ret = auth.ParseAuthMethod(m_server->GetAuthManager(), alias, request);
		if(ret == AUTH_PARSE_STATUS_ERROR)
		{
			throw new CSystemResponse(m_server, context, _T("認証の要求が異常です"), 400);
		}
		else
		{
			//	認証OK？
			if(ret == AUTH_PARSE_STATUS_OK)
				if(auth.CheckAuthUser(m_server->GetAuthManager(), alias, request))
					return;

			//	認証を要求
			CSystemResponse *move = new CSystemResponse(m_server, context, _T("認証が必要です"), 401);
			switch(alias.GetPathOptions().GetConfig("AuthenticateType", 1))
			{
			case 0:
			default:
				move->m_responseOptions.SetConfig("WWW-Authenticate", auth.GetBasicAuthOptionLine(alias));
				throw move;
				break;

			case 1:
				move->m_responseOptions.SetConfig("WWW-Authenticate", auth.GetDigestAuthOptionLine(m_server->GetAuthManager(), alias));
				throw move;
				break;
			}
		}
	}
}

/*!
	移動の確認
*/
void CConnection::CheckMove(CWorkspaceAccess &context, CRequest &request, CAlias &alias)
{
	DEBUG_SET_THREAD_FUNCTION_NAME("CConnection::CheckMove");

	if(alias.IsMove())
	{
		CSystemResponse *move = new CSystemResponse(m_server, context, alias.GetMoveTo() + " に移動してください", 301);
		move->m_responseOptions.SetConfig("Location", alias.GetMoveTo());
		throw move;
	}
}


/*!
 *	WebDAV
 */
void CConnection::WebDAV(CWorkspaceAccess &context, CRequest &request, CAlias &alias)
{
	DEBUG_SET_THREAD_FUNCTION_NAME("CConnection::WebDAV");

	//	Options
	if(request.GetMethod() == "OPTIONS")
		throw new COptionsResponse(m_server, context, request, alias);

	//	WebDAVは有効?
	if(alias.GetPathOptions().GetConfig("EnableWebDAV", 0))
	{
		if(request.GetMethod() == "PROPFIND")
			throw new CDAVPropfindResponse(m_server, context, request, alias);
		else if(request.GetMethod() == "PROPPATCH")
			throw new CSystemResponse(m_server, context, _T("指定されたメソッドは、サポートしていません。"), 500);
		else if(request.GetMethod() == "LOCK")
			throw new CSystemResponse(m_server, context, _T("指定されたメソッドは、サポートしていません。"), 500);
		else if(request.GetMethod() == "UNLOCK")
			throw new CSystemResponse(m_server, context, _T("指定されたメソッドは、サポートしていません。"), 500);
	}

	//	書き込みは有効か？
	if(alias.GetPathOptions().GetConfig("EnableWrite", 0))
	{
		if(request.GetMethod() == "PUT")
			throw new CFilePutResponse(m_server, context, request, alias);
		else if(request.GetMethod() == "MKCOL")
			throw new CFilePutResponse(m_server, context, request, alias);
		else if(request.GetMethod() == "DELETE")
			throw new CFileDeleteResponse(m_server, context, request, alias);
		else if(request.GetMethod() == "COPY")
			throw new CFileMoveResponse(m_server, context, request, alias);
		else if(request.GetMethod() == "MOVE")
			throw new CFileMoveResponse(m_server, context, request, alias);
	}
}


/*!
	Index確認
*/
void CConnection::CheckIndex(CWorkspaceAccess &context, CRequest &request, CAlias &alias)
{
	DEBUG_SET_THREAD_FUNCTION_NAME("CConnection::CheckIndex");

	//	インデックスがある？
	if(alias.IsIndexExist())
	{
		//	ブラウザの方で移動する？
		if(m_server->m_setting.GetConfig("Basic::IndexNoMove", 0))
		{
			//	移動しないので、内部移動
			alias.InternalMove(alias.GetIndexFile());
		}
		else
		{
			//	ブラウザの方で移動
			CSystemResponse *move = new CSystemResponse(m_server, context, alias.GetIndexFile() + " に移動してください", 301);
			move->m_responseOptions.SetConfig("Location", alias.GetIndexFile());
			throw move;
		}
	}
}


/*!
	CGI
*/
void CConnection::CGI(CWorkspaceAccess &context, CRequest &request, CAlias &alias)
{
	DEBUG_SET_THREAD_FUNCTION_NAME("CConnection::CGI");

	//	ファイルはあるのか？
	if(!alias.IsExist()  && !alias.IsExtarExist())
		return;

	//	CGIは許可されているのか？
	if(alias.GetPathOptions().GetConfig("EnableSSICGI", 0))
	{
		CString command;

		//	エクストラパス
		if(alias.IsExtarExist())
			command = CSubProcess::GetCGICommand(m_server, alias.GetExtraTarget(), request.GetQueryDecoded());
		else
			command = CSubProcess::GetCGICommand(m_server, alias.GetTarget(), request.GetQueryDecoded());

		if(!command.IsEmpty())
		{
			context.SetConfig("ScriptCommand", command);
			throw new CCGIResponse(m_server, context, request, alias, m_interrupt);
		}
	}
}


/*!
	SSI
*/
void CConnection::SSI(CWorkspaceAccess &context, CRequest &request, CAlias &alias)
{
	DEBUG_SET_THREAD_FUNCTION_NAME("CConnection::SSI");

	//	ファイルはあるのか？
	if(!alias.IsExist())
		return;

	//	SSIは許可されているのか？
	if(alias.GetPathOptions().GetConfig("EnableSSICGI", 0))
	{
		//	拡張子の取得
		CString	ext = CDir::PathToExt(alias.GetTarget());

		//	設定リストを取得
		CWorkspaceAccess	ssiSetting = m_server->m_setting.GetAccess("SSI");
		CStringArray	ssiList;
		ssiSetting.GetAllKey(ssiList);

		//	各設定を処理
		for(int i=0;i<ssiList.GetSize();i++)
		{
			if(ext.CompareNoCase(ssiSetting.GetConfig(ssiList[i], "")) == 0)
				throw new CSSIResponse(m_server, context, request, alias, m_interrupt);
		}
	}
}

/*!
	キックするホストかチェック
*/
int CConnection::CheckKickHost(CWorkspaceAccess &context, CRequest &request, CAlias &alias)
{
	DEBUG_SET_THREAD_FUNCTION_NAME("CConnection::CheckKickHost");

	CWorkspaceAccess	kick = alias.GetPathInfo().GetAccess("Kick");
	CString				hostIP = m_protocolSocket->GetPeerAddres();

	//	有効？
	if(alias.GetPathOptions().GetConfig("EnableHostKick", 0))
	{
		//	まずは禁止されているかチェック
		CWorkspaceAccess	denied = kick.GetAccess("Denied");
		CStringArray	list;
		denied.GetAllKey(list);

		for(int i=0;i<list.GetSize();i++)
		{
			if(CStringCompare::WildCompare(denied.GetConfig(list[i], ""), hostIP))
				return(1);
		}

		//	許可されているかチェック
		CWorkspaceAccess	accept = kick.GetAccess("Accept");
		accept.GetAllKey(list);

		for(int i=0;i<list.GetSize();i++)
		{
			if(CStringCompare::WildCompare(accept.GetConfig(list[i], ""), hostIP))
				return(0);
		}

		return(1);
	}

	return(0);
}




////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	通信処理
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/*!
 *	ヘッダーの受信
 *
 */
CString CConnection::RecvHeader()
{
	DEBUG_SET_THREAD_FUNCTION_NAME("CConnection::RecvHeader");

	//	ヘッダ受信
	CString	header;
	if(m_protocolSocket->RecvProtocolHeader(header, _T("\r\n\r\n"), MAX_HTTP_HEADER_SIZE, FALSE))
		throw CServerFatalException("ヘッダー受信中に" + m_protocolSocket->GetLastErrorString());

	//	受信サイズ
	m_recvSize += header.GetLength();

	return(header);
}

/*!
	レスポンスの処理
	リクエストボディ受信、レスポンス生成、レスポンスヘッダ送信、レスポンスボディ送信
*/
void CConnection::SendResponse(CWorkspaceAccess &context, CRequest &request, CAlias &alias, CBandWidthReference &bandRef, CResponse *response)
{
	DEBUG_SET_THREAD_FUNCTION_NAME("CConnection::SendResponse");

	//	レスポンス生成準備
	UpdateConnectionStatus(context, _T("レスポンスデータの前処理中..."), CONNECTION_PROCESS_PRECREATING_RESPONSE);
	response->PreCreateResponse();

	//	リクエストボディーの受信
	UpdateConnectionStatus(context, _T("データの受信を開始しています..."), CONNECTION_PROCESS_RECVING_BODY);
	RecvData(context, request, alias, bandRef, response);

	//	レスポンスを生成する
	UpdateConnectionStatus(context, _T("レスポンスデータの生成中..."), CONNECTION_PROCESS_CREATING_RESPONSE);
	response->CreateResponse();

	//	レスポンス情報の取得
	CWorkspaceAccess responseInfo = response->GetResponseInfo();
	CWorkspaceAccess responseOptions = responseInfo.GetAccess("Options");

	//	長さ情報の取得
	__int64 length = response->GetResponseSize();
	responseOptions.SetConfig("Content-Length", length);

	//	ヘッダの生成
	CString header = MakeHeaderString(responseInfo);

	//	ヘッダの送信
	UpdateConnectionStatus(context, _T("ヘッダーの送信中..."), CONNECTION_PROCESS_SENDING_HEADER);
	if(m_protocolSocket->SendString(header)<0)
		throw CServerFatalException(_T("ヘッダー送信中に") + m_protocolSocket->GetLastErrorString());

	//	ヘッダサイズ
	m_sendSize += header.GetLength();

	//	ボディー送信
	UpdateConnectionStatus(context, _T("データの送信を開始しています..."), CONNECTION_PROCESS_SENDING_BODY);
	SendData(context, request, alias, bandRef, response, length);
}

/*!
	リクエストボディーを受信
*/
void CConnection::RecvData(CWorkspaceAccess &context, CRequest &request, CAlias &alias, CBandWidthReference &bandRef, CResponse *response)
{
	DEBUG_SET_THREAD_FUNCTION_NAME("CConnection::RecvData");

	//	受信サイズは？
	__int64 len = _atoi64(request.GetOptionAccess().GetConfig("Content-Length", "0", TRUE));
	__int64	orgLen;

	//	2回目の呼び出しの可能性を考慮(残りのデータのみを取得する)
	len -= m_recvDataSize;
	orgLen = len;

	//	リミットの設定
	bandRef.SetRecvLimit(alias.GetPathOptions().GetConfig("EnableBandWidthLimit", 0));

	//	受信
	CBuffer				recvBuf;
	CBandWidthLimit		limiter;
	CIntervalTimer		timer(1000);
	while(len > 0)
	{
		//	受信
		int recv = m_protocolSocket->RecvBinary(recvBuf, RECV_BUF_SIZE);
		if(recv < 0)
			throw CServerFatalException(_T("データ受信中に") + m_protocolSocket->GetLastErrorString());
		else if(recv == 0)
			continue;

		//	受信処理
		response->SetNextData(recvBuf);

		//	カウント
		len -= recv;
		m_recvSize += recv;
		m_recvDataSize += recv;

		//	帯域制限によるウェイト
		Delay(limiter.GetWaitTick(bandRef.GetCurrentRecvLimit(), recv));
		limiter.OneTracsEnd();

		//	処理状況
		if(timer)
		{
			int limit = bandRef.GetCurrentRecvLimit();

			CString	str;
			if(!limit)
				str.Format("受信中 : %I64dK/%I64dK [%I64d%%] (%0.1fK/Sec)", orgLen - len, orgLen, (orgLen - len) * 100 / orgLen, (double)limiter.GetCurSpeed()/1024.0);
			else
				str.Format("受信中 : %I64dK/%I64dK [%I64d%%] (%0.1fK/S, Limit: %0.1fK/S)", orgLen - len, orgLen, (orgLen - len) * 100 / orgLen, (double)limiter.GetCurSpeed()/1024.0, (double)limit/1024.0);

			//	メッセージ設定
			UpdateConnectionStatus(context, str);
		}
	}

	//	リミット解除
	bandRef.SetRecvLimit(0);
}

/*!
	レスポンスボディーを送信
*/
void CConnection::SendData(CWorkspaceAccess &context, CRequest &request, CAlias &alias, CBandWidthReference &bandRef, CResponse *response, __int64 length)
{
	DEBUG_SET_THREAD_FUNCTION_NAME("CConnection::SendData");

	//	リミットの設定
	bandRef.SetSendLimit(alias.GetPathOptions().GetConfig("EnableBandWidthLimit", 0));


	//	Bodyの送信
	CBuffer			sendBuf(SEND_BUF_SIZE);
	CBandWidthLimit	limiter;
	CIntervalTimer	timer(1000);
	__int64			sended = 0;
	while(1)
	{
		//	データ受け取り
		int size = response->GetNextData(sendBuf);
		if(size <= 0)
			break;

		//	送信処理
		if(size > 0)
		{
			if(m_protocolSocket->Send(sendBuf, size)<0)
				throw CServerFatalException(_T("データ送信中に") + m_protocolSocket->GetLastErrorString());

			//	カウント
			m_sendSize += size;
			sended += size;
		}

		//	帯域制限によるウェイト
		Delay(limiter.GetWaitTick(bandRef.GetCurrentSendLimit(), size));
		limiter.OneTracsEnd();

		//	処理状況
		if(timer)
		{
			int limit = bandRef.GetCurrentSendLimit();

			CString	str;
			if(!limit)
				str.Format("送信中 : %I64dK/%I64dK [%I64d%%] (%0.1fK/Sec)", sended, length, sended * 100 / length, (double)limiter.GetCurSpeed()/1024.0);
			else
				str.Format("送信中 : %I64dK/%I64dK [%I64d%%] (%0.1fK/S, Limit: %0.1lfK/S)", sended, length, sended * 100 / length, (double)limiter.GetCurSpeed()/1024.0, (double)limit/1024.0);

			//	メッセージ設定
			UpdateConnectionStatus(context, str);
		}
	}

	//	リミット解除
	bandRef.SetRecvLimit(0);
}



/*!
	致命的エラーの処理
*/
void CConnection::SendFatalError(CString info)
{
	DEBUG_SET_THREAD_FUNCTION_NAME("CConnection::SendFatalError");

	CString	body;
	body.Format("<HTML><HEAD>"\
				"<TITLE>500 Internal Server Error</TITLE>"\
				"<meta http-equiv=\"Content-Type\" content=\"text/html;charset=Shift_JIS\"></HEAD>\n"\
				"<BODY><H1>Error : 500 Internal Server Error</H1>"\
				"<PRE>%s</PRE>"\
				"<HR><ADDRESS>04WebServer/"SERVER_VER"</ADDRESS>"\
				"</BODY></HTML>", info);

	CString	head;
	head.Format("HTTP/1.0 500 Internal Server Error\r\n"\
				"Server: 04WebServer/"SERVER_VER"\r\n"\
				"Content-Type: text/html\r\n"\
				"Connection: Close\r\n"\
				"Content-length: %d\r\n"\
				"\r\n", body.GetLength());

	m_protocolSocket->SendString(head);
	m_protocolSocket->SendString(body);
	m_protocolSocket->Close();
}


////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	補助関数
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/*!
	ヘッダの生成
*/
CString CConnection::MakeHeaderString(CWorkspaceAccess responseInfo)
{
	DEBUG_SET_THREAD_FUNCTION_NAME("CConnection::MakeHeaderString");

	CWorkspaceAccess responseOptions = responseInfo.GetAccess("Options");
	CString	header, line;

	//	レスポンスライン
	line.Format("HTTP/1.1 %s\r\n", responseInfo.GetConfig("ResponseText", "500 Internal Server Error"));
	header += line;

	//	オプションライン
	CStringArray	keyList;
	responseOptions.GetAllKey(keyList);
	for(int i=0;i<keyList.GetSize();i++)
	{
		line.Format("%s: %s\r\n", keyList[i], responseOptions.GetConfig(keyList[i], ""));
		header += line;
	}

	//	CGI用ヘッダ
	CString cgiHeader = responseInfo.GetConfig("CGIHeader", "");
	header += cgiHeader;
	header += "\r\n";

	return(header);
}




/*!
 *	ユーザーのチェック
 */
int CConnection::CheckUser(CString userName, CString passwd, CAlias alias)
{
	DEBUG_SET_THREAD_FUNCTION_NAME("CConnection::CheckUser");

	CWorkspaceAccess	pathUser = alias.GetPathInfo().GetAccess("Users");
	CWorkspaceAccess	enableUser = m_server->m_setting.GetAccess("User::Enable");

	//	検索
	CStringArray	userList;
	pathUser.GetAllKey(userList);
	for(int i=0;i<userList.GetSize();i++)
	{
		if(pathUser.GetConfig(userList[i], "") == userName)
			goto next;
	}
	return 0;

next:

	//	検索
	enableUser.GetAllSubNode(userList);
	for(int i=0;i<userList.GetSize();i++)
	{
		CWorkspaceAccess user = enableUser.GetAccess(userList[i]);
		if(user.GetConfig("User", "") == userName && user.GetConfig("Passwd", "") == passwd)
			return 1;
	}
	return 0;
}


/*!
 *	ウェイト
 */
void CConnection::Delay(int tick)
{
	DEBUG_SET_THREAD_FUNCTION_NAME("CConnection::Delay");

	//	正確に測定
	CIntervalTimer	timer(tick);

	//	指定時間待機
	if(m_interrupt.WaitInterrupt(tick))
		return;

	//	足りなければもっと待つ
	while(!timer.IsTimeout())
	{
		if(m_interrupt.WaitInterrupt(20))
			return;
	}
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	ステータス
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/*!
	送受信サイズを更新
*/
void CConnection::UpdateTransSize(CWorkspaceAccess &context)
{
	DEBUG_SET_THREAD_FUNCTION_NAME("CConnection::UpdateTransSize");

	//	転送量登録
	m_server->AddTrans(m_sendSize, m_recvSize);

	//	サイズの記録
	context.SetConfig("RecvSize", m_recvSize);
	context.SetConfig("SendSize", m_sendSize);
	context.SetConfig("Size", m_recvSize + m_sendSize);
}

/*!
	処理状況変更
*/
void CConnection::UpdateConnectionStatus(CWorkspaceAccess &context, CString statusString, CONNECTION_PROCESS_STATUS statusCode, int isRestart)
{
	//	処理メッセージを設定
	context.SetKeyStr(_T("Info"), statusString);

	//	処理状況を変更
	if(statusCode != CONNECTION_PROCESS_NOCHANGE)
	{
		//	ステータスコードは常に増加のみ
		if(!isRestart && GetConnectionProcessStatus(context) > statusCode)
			throw CServerFatalException(_T("サーバ内の処理順序に矛盾が発生しました、無効なステータス変更です"));

		context.SetKeyInt(_T("ConnectionProcessStatus"), statusCode);
	}
}


/*!
	処理状況を取得
*/
CONNECTION_PROCESS_STATUS CConnection::GetConnectionProcessStatus(CWorkspaceAccess &contex)
{
	return((CONNECTION_PROCESS_STATUS)contex.GetKeyInt(_T("ConnectionProcessStatus"), CONNECTION_PROCESS_STARTING));
}


/*!
	レスポンスを送信可能な状態かチェック

	\return TRUE:送信可能、FALSE:送信不可（すでに送信開始済みか、切断済み）
*/
int CConnection::CanSendResponse(CWorkspaceAccess &context)
{
	//	内部状態チェック
	if(GetConnectionProcessStatus(context) > CONNECTION_PROCESS_CREATING_RESPONSE)	//	まだ送信してない場合のみ
		return(FALSE);

	//	切断チェック
	if(m_protocolSocket->IsDisconnect())
		return(FALSE);

	return(TRUE);
}



////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	割り込み管理クラス
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CConnectionInterrupt::CConnectionInterrupt(CConnection *connection)
{
	m_connection = connection;
}

CConnectionInterrupt::CConnectionInterrupt(CConnectionInterrupt &other)
{
	m_connection = other.m_connection;
	m_interruptInfo = other.m_interruptInfo;
}

/*!
	割り込みを待機

	\return 0:タイムアウト、>1:中断割り込み
*/
int CConnectionInterrupt::WaitInterrupt(int timeout)
{
	//	待ってみる
	switch(CThreadSkeleton::WaitForSomeObjects(timeout, m_connection->m_protocolSocket->GetDisconnectEvent(), m_connection->GetBreakEvent()))
	{
	case 0:
	default:
		return 0;

	case 1:
		m_interruptInfo = _T("切断しました");
		return 1;

	case 2:
		m_interruptInfo = _T("中断しました");
		return 2;
	}
}

/*!
	割り込みか？

	\return 0:割り込みではない、>1割り込み
*/
int CConnectionInterrupt::IsInterrupt()
{
	return(WaitInterrupt(0));
}

/*!
	int型に変換
*/
CConnectionInterrupt::operator int()
{
	return(IsInterrupt());
}

/*!
	割り込み原因取得
*/
CString CConnectionInterrupt::GetInterruptInfo()
{
	return(m_interruptInfo);
}

/*!
	中断処理イベントをすべて取得
*/
void CConnectionInterrupt::GetAllInterruptEvents(CArray<HANDLE> &events)
{
	//	全削除
	events.RemoveAll();

	//	切断
	events.Add(m_connection->m_protocolSocket->GetDisconnectEvent());

	//	中断
	events.Add(m_connection->GetBreakEvent());
}
