#include "StdAfx.h"
#include "fileresponse.h"
#include "connection.h"
#include "DateUtility.h"

CFileResponse::CFileResponse(CServer *server,CConfigAccess context,CRequest request,CAlias alias)
: CNormalResponse(server,context,request,alias)
{
	//	実は無いとか
	if(alias.IsExist()==0)
		throw new CSystemResponse(server,context,"指定されたファイルは存在しません",404);

	//	メソッドはGET?
	if(request.m_method != "GET" && request.m_method != "HEAD")
		throw new CSystemResponse(server,context,"対応していないメソッドです",405);
}

CFileResponse::~CFileResponse(void)
{
}



/*
	レスポンス生成
*/
void CFileResponse::CreateResponse()
{
	//	レスポンスコード
	SetResponseCode(200);	//	OK

	//	ファイルエラースコープ
	try
	{
		TRACE("File : %s\n",m_alias.GetTarget());
		if(!m_file.Open(m_alias.GetTarget(),	CFile::modeRead | CFile::shareDenyWrite))
			throw CServerFatalException("ファイルのオープンに失敗しました、サーバー上のファイルは、使用中のためロックされている可能性があります");

		//	更新のチェック
		CFileStatus	stat;
		m_file.GetStatus(stat);
		int update = CheckUpdate(stat.m_mtime);

		//	ETagチェック
		int update2 = CheckETag(m_file);

		if(update == 1 || update2 == 1 || (update == -1 && update2 == -1))
		{
			//	レジュームする
			CheckRange();

			//	mimetypeの検索
			CMimeTypeMgr	mgr = m_server->GetCMimeTypeMgr();
			m_responseOptions.SetConfig("Content-Type",mgr.GetMimeTypeByName(m_alias.GetTarget()));										//	タイプ
		}
		else
			throw new CSystemResponse(m_server,m_context,"ターゲットは更新されていません",304);
	}
	catch(CFileException *err)
	{
		TRACE("File error : %s\n",err->m_strFileName);
		err->Delete();
		throw CServerFatalException("ファイルの操作に失敗しました、システムエラーです");
	}
}


/*!
	レスポンスサイズの取得
*/
__int64 CFileResponse::GetResponseSize()
{
	try
	{
		return(m_file.GetLength() - m_file.GetPosition());
	}
	catch(CFileException *err)
	{
		TRACE("File error : %s\n",err->m_strFileName);
		err->Delete();
		throw CServerFatalException("ファイルの操作に失敗しました、システムエラーです");
	}
}

/*!
	レスポンスの取得
*/
int CFileResponse::GetNextData(CBuffer &data)
{
	try
	{
		int size = m_file.Read(data.GetPtr(),data.GetSize());
		data.ReSize(size);
		return(size);
	}
	catch(CFileException *err) 
	{
		TRACE("File error : %s\n",err->m_strFileName);
		err->Delete();
		throw CServerFatalException("ファイルの操作に失敗しました、システムエラーです");
	}
}

/*!
 *	レジューム処理
 *
 *	@return		0:正常
 */
int CFileResponse::CheckRange()
{
	//	Rangeヘッダは？
	CString range = m_requestOptions.GetConfig("Range","");
	if(range != "")
	{
		//	サポートしてるのか？(bytesか？)
		//	Range: bytes=xxxx-
		if(range.Left(6).CompareNoCase("bytes=")==0)
		{
			range = range.Mid(6);
			int index = range.Find("-");
			if(index != -1)
				range = range.Left(index);

			__int64	start = _atoi64(range);
			if(start < 0 || (unsigned)start >= m_file.GetLength())
				return(-1);

			//	レジューム
			m_file.Seek(start,CFile::begin);
			SetResponseCode(206);

			CString	str;
			m_responseOptions.SetConfig("Accept-Ranges","bytes");
			str.Format("bytes %I64d-%I64d/%I64d",start,m_file.GetLength(),m_file.GetLength());
			m_responseOptions.SetConfig("Content-Range",str);
		}
	}
			
	return(0);
}


/*!
 *	更新の確認とLast-modifiedの設定
 *
 *	@param	date	ターゲットの更新日時
 *	@return			1:更新されている
 */
int CFileResponse::CheckUpdate(CTime date)
{
	//	更新日付の出力
	m_responseOptions.SetConfig("Last-modified",CDateUtility::GetGMTString(date));

	//	更新チェック
	CString modified = m_requestOptions.GetConfig("If-Modified-Since","");
	if(modified.IsEmpty())
		return -1;

	CTime lm = CDateUtility::StringToCTime(modified);
	if(date <= lm && lm != 0)
		return(0);

	return 1;
}


/*!
 *	ETagの確認とETagの設定
 *
 *	@param	tag		ターゲットの情報
 *	@return			1:更新されている
 */
int CFileResponse::CheckETag(CFile &file)
{
	//	ETagの出力
	CString tag = GetETag(file);
	m_responseOptions.SetConfig("ETag",tag);

	//	ETagチェック
	CString modified = m_requestOptions.GetConfig("If-None-Match","");
	if(modified.IsEmpty())
		return(-1);

	if(modified == tag)
		return(0);

	return(1);
}


//	ETgaの取得
CString	CFileResponse::GetETag(CFile &file)
{

	CFileStatus	stat;
	 m_file.GetStatus(stat);

	//	04WebServerのETagは、ファイルサイズ、ファイルパス、ファイル日付より取得
	__int64 size = m_file.GetLength();
	CTime	time = stat.m_mtime;
	CString	path = m_file.GetFilePath();

	int	calc=0;
	unsigned char *buf = (unsigned char *)path.GetBuffer();
	for(int i=0;i<path.GetLength();i++)
		calc += buf[i];

	CString str;
	str.Format("\"%I64x-%I64x:%04d\"",size,time.GetTime(),calc);

	return(str);
}



