//接続単位の処理 protected override void OnSubThread(SockObj sockObj) { Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US"); var sockTcp = (SockTcp)sockObj; var remoteIp = sockTcp.RemoteIp; //opBase 及び loggerはバーチャルホストで変更されるので、 //このポインタを初期化に使用できない bool keepAlive = true;//レスポンスが終了したとき接続を切断しないで継続する //1回目の通信でバーチャルホストの検索を実施する var checkVirtual = true; var request = new Request(Logger,sockTcp);//リクエストライン処理クラス //受信ヘッダ var recvHeader = new Header(); //Ver5.1.x string urlStr = null;//http://example.com //接続が継続している間は、このループの中にいる(継続か否かをkeepAliveで保持する) //「continue」は、次のリクエストを待つ 「break」は、接続を切断する事を意味する WebStream inputStream = null; var outputStream = new WebStream(-1); while (keepAlive && IsLife()) { int responseCode; //*************************************************************** // ドキュメント生成クラスの初期化 //*************************************************************** var contentType = new ContentType(Conf); var document = new Document(Kernel, Logger, Conf, sockTcp, contentType); var authrization = new Authorization(Conf, Logger); var authName = ""; //*************************************************************** //データ取得 //*************************************************************** //リクエスト取得 //ここのタイムアウト値は、大きすぎるとブラウザの切断を取得できないでブロックしてしまう var requestStr = sockTcp.AsciiRecv(Timeout, this); if (requestStr == null) break; //\r\nの削除 requestStr = Inet.TrimCrlf(requestStr); //Ver5.8.8 リクエストの解釈に失敗した場合に、処理を中断する //request.Init(requestStr); if (!request.Init(requestStr)){ break; } //ヘッダ取得(内部データは初期化される) if (!recvHeader.Recv(sockTcp,(int)Conf.Get("timeOut"),this)) break; { //Ver5.1.x var hostStr = recvHeader.GetVal("host"); urlStr = hostStr==null ? null : string.Format("{0}://{1}",(ssl != null)?"https":"http",hostStr); } //入力取得(POST及びPUTの場合) var contentLengthStr = recvHeader.GetVal("Content-Length"); if(contentLengthStr != null) { try{ //max,lenはともにlong var max = Convert.ToInt64(contentLengthStr); if(max!=0){//送信データあり inputStream = new WebStream((256000<max)?-1:(int)max); var errorCount = 0; while(inputStream.Length<max && IsLife()){ var len = max - inputStream.Length; if (len > 51200000) { len = 51200000; } var b = sockTcp.Recv((int)len, (int)Conf.Get("timeOut"),this); if (!inputStream.Add(b)) { errorCount++;//エラー蓄積 Logger.Set(LogKind.Error, null, 41, string.Format("content-Length={0} Recv={1}", max, inputStream.Length)); } else { errorCount = 0;//初期化 } Logger.Set(LogKind.Detail, null,38, string.Format("Content-Length={0} {1}bytes Received.", max, inputStream.Length)); if (errorCount > 5){//5回連続して受信が無かった場合、サーバエラー responseCode = 500; goto SEND;//サーバエラー } Thread.Sleep(10); } Logger.Set(LogKind.Detail, null, 39, string.Format("Content-Length={0} {1}bytes", max, inputStream.Length)); } }catch(Exception ex){ Logger.Set(LogKind.Error, null, 40, ex.Message); } } // /によるパラメータ渡しに対応 //for (int i = 0;i < Option->CgiExt->Count;i++) { // wsprintf(TmpBuf,".%s/",Option->CgiExt->Strings[i]); // strupr(TmpBuf); // strcpy(Buf,Headers->Uri); // strupr(Buf); // if (NULL != (p = strstr(Buf,TmpBuf))) { // i = p - Buf; // i += strlen(TmpBuf) - 1; // p = &Headers->Uri[i]; // *p = '\0'; // p = &Headers->UriNoConversion[i]; // *p = '\0'; // wsprintf(TmpBuf,"/%s",p + 1); // Headers->PathInfo = new char[strlen(TmpBuf) + 1]; // strcpy(Headers->PathInfo,TmpBuf); // break; // } //} //*************************************************************** //バーチャルホストの検索を実施し、opBase、logger及び webDavDb を置き換える //*************************************************************** if (checkVirtual) {//初回のみ ReplaceVirtualHost(recvHeader.GetVal("host"),sockTcp.LocalAddress.Address,sockTcp.LocalAddress.Port); checkVirtual = false; } //*************************************************************** //接続を継続するかどうかの判断 keepAliveの初期化 //*************************************************************** if (ssl != null) { keepAlive = false;//SSL通信では、1回づつコネクションが必要 }else{ if (request.Ver == "HTTP/1.1") {//HTTP1.1はデフォルトで keepAlive=true keepAlive = true; } else { // HTTP/1.1以外の場合、継続接続は、Connection: Keep-Aliveの有無に従う keepAlive = recvHeader.GetVal("Connection") == "Keep-Alive"; } } //*************************************************************** // ドキュメント生成クラスの初期化 //*************************************************************** //var contentType = new ContentType(OneOption); //var document = new Document(kernel,Logger,OneOption,sockTcp,contentType); //*************************************************************** // ログ //*************************************************************** Logger.Set(LogKind.Normal, sockTcp, ssl != null ? 23 : 24, request.LogStr); //*************************************************************** // 認証 //*************************************************************** //var authrization = new Authorization(OneOption,Logger); //string authName = ""; if (!authrization.Check(request.Uri, recvHeader.GetVal("authorization"), ref authName)) { responseCode = 401; keepAlive = false;//切断 goto SEND; } //*************************************************************** // 不正なURIに対するエラー処理 //*************************************************************** //URIを点検して不正な場合はエラーコードを返す responseCode = CheckUri(sockTcp, request, recvHeader); if (responseCode != 200) { keepAlive = false;//切断 goto SEND; } //*************************************************************** //ターゲットオブジェクトの初期化 //*************************************************************** var target = new Target(Conf,Logger); if (target.DocumentRoot == null) { Logger.Set(LogKind.Error,sockTcp,14,string.Format("documentRoot={0}",Conf.Get("documentRoot")));//ドキュメントルートで指定されたフォルダが存在しません(処理を継続できません) break;//ドキュメントルートが無効な場合は、処理を継続できない } target.InitFromUri(request.Uri); //*************************************************************** // 送信ヘッダの追加 //*************************************************************** // 特別拡張 BlackJumboDog経由のリクエストの場合 送信ヘッダにRemoteHostを追加する if ((bool)Conf.Get("useExpansion")) { if (recvHeader.GetVal("Host") != null) { document.AddHeader("RemoteHost",sockTcp.RemoteAddress.Address.ToString()); } } //受信ヘッダに「PathInfo:」が設定されている場合、送信ヘッダに「PathTranslated」を追加する var pathInfo = recvHeader.GetVal("PathInfo"); if (pathInfo != null) { pathInfo = target.DocumentRoot + pathInfo; document.AddHeader("PathTranslated",Util.SwapChar('/','\\',pathInfo)); } //*************************************************************** //メソッドに応じた処理 OPTIONS 対応 Ver5.1.x //*************************************************************** if(WebDav.IsTarget(request.Method)){ var webDav = new WebDav(Logger, _webDavDb, target, document, urlStr, recvHeader.GetVal("Depth"), contentType,(bool)Conf.Get("useEtag")); var inputBuf = new byte[0]; if(inputStream!=null){ inputBuf = inputStream.GetBytes(); } switch(request.Method) { case HttpMethod.Options: responseCode = webDav.Option(); break; case HttpMethod.Delete: responseCode = webDav.Delete(); break; case HttpMethod.Put: responseCode = webDav.Put(inputBuf); break; case HttpMethod.Proppatch: responseCode = webDav.PropPatch(inputBuf); break; case HttpMethod.Propfind: responseCode = webDav.PropFind(); break; case HttpMethod.Mkcol: responseCode = webDav.MkCol(); break; case HttpMethod.Copy: case HttpMethod.Move: responseCode = 405; //Destnationで指定されたファイルは書き込み許可されているか? var dstTarget = new Target(Conf,Logger); string destinationStr = recvHeader.GetVal("Destination"); if(destinationStr != null) { if(destinationStr.IndexOf("://") == -1) { destinationStr = urlStr + destinationStr; } var uri = new Uri(destinationStr); dstTarget.InitFromUri(uri.LocalPath); if(dstTarget.WebDavKind == WebDavKind.Write) { var overwrite = false; var overwriteStr = recvHeader.GetVal("Overwrite"); if(overwriteStr != null) { if(overwriteStr == "F") { overwrite = true; } } responseCode = webDav.MoveCopy(dstTarget,overwrite,request.Method); document.AddHeader("Location",destinationStr); } } break; } //WebDAVに対するリクエストは、ここで処理完了 goto SEND; } //以下 label SENDまでの間は、GET/POSTに関する処理 //*************************************************************** //ターゲットの種類に応じた処理 //*************************************************************** if (target.TargetKind == TargetKind.Non) { //見つからない場合 responseCode = 404; goto SEND; } if (target.TargetKind == TargetKind.Move) { //ターゲットはディレクトリの場合 responseCode = 301; goto SEND; } if (target.TargetKind == TargetKind.Dir) { //ディレクトリ一覧表示の場合 //インデックスドキュメントを生成する if (!document.CreateFromIndex(request, target.FullPath)) break; goto SEND; } //*************************************************************** // 隠し属性のファイルへのアクセス制御 //*************************************************************** if (!(bool)Conf.Get("useHidden")) { if ((target.Attr & FileAttributes.Hidden) == FileAttributes.Hidden) { //エラーキュメントを生成する responseCode = 404; keepAlive = false;//切断 goto SEND; } } if (target.TargetKind == TargetKind.Cgi || target.TargetKind == TargetKind.Ssi) { keepAlive = false;//デフォルトで切断 //環境変数作成 var env = new Env(Kernel,Conf,request, recvHeader,sockTcp, target.FullPath); // 詳細ログ Logger.Set(LogKind.Detail,sockTcp,18,string.Format("{0} {1}",target.CgiCmd,Path.GetFileName(target.FullPath))); if (target.TargetKind == TargetKind.Cgi) { var cgi = new Cgi(); var cgiTimeout = (int)Conf.Get("cgiTimeout"); if (!cgi.Exec(target,request.Param,env,inputStream,out outputStream,cgiTimeout)) { // エラー出力 var errStr = Encoding.ASCII.GetString(outputStream.GetBytes()); Logger.Set(LogKind.Error,sockTcp,16,errStr); responseCode = 500; goto SEND; } //*************************************************** // NPH (Non-Parsed Header CGI)スクリプト nph-で始まる場合、サーバ処理(レスポンスコードやヘッダの追加)を経由しない //*************************************************** if (Path.GetFileName(target.FullPath).IndexOf("nph-") == 0) { sockTcp.SendUseEncode(outputStream.GetBytes());//CGI出力をそのまま送信する break; } // CGIで得られた出力から、本体とヘッダを分離する if(!document.CreateFromCgi(outputStream.GetBytes())) break; // cgi出力で、Location:が含まれる場合、レスポンスコードを302にする if (document.SearchLocation())//Location:ヘッダを含むかどうか responseCode = 302; goto SEND; } //SSI var ssi = new Ssi(Kernel, Logger,Conf, sockTcp, request, recvHeader); if (!ssi.Exec(target,env,outputStream)) { // エラー出力 Logger.Set(LogKind.Error,sockTcp,22,MLang.GetString(outputStream.GetBytes())); responseCode = 500; goto SEND; } document.CreateFromSsi(outputStream.GetBytes(),target.FullPath); goto SEND; } //以下は、通常ファイルの処理 TARGET_KIND.FILE //******************************************************************** //Modified処理 //******************************************************************** if (recvHeader.GetVal("If_Modified_Since") != null) { var dt = Util.Str2Time(recvHeader.GetVal("If-Modified-Since")); if (target.FileInfo.LastWriteTimeUtc.Ticks / 10000000 <= dt.Ticks / 10000000) { responseCode = 304; goto SEND; } } if (recvHeader.GetVal("If_Unmodified_Since") != null) { var dt = Util.Str2Time(recvHeader.GetVal("If_Unmodified_Since")); if (target.FileInfo.LastWriteTimeUtc.Ticks / 10000000 > dt.Ticks / 10000000) { responseCode = 412; goto SEND; } } document.AddHeader("Last-Modified",Util.UtcTime2Str(target.FileInfo.LastWriteTimeUtc)); //******************************************************************** //ETag処理 //******************************************************************** // (1) useEtagがtrueの場合は、送信時にETagを付加する // (2) If-None-Match 若しくはIf-Matchヘッダが指定されている場合は、排除対象かどうかの判断が必要になる if ((bool)Conf.Get("useEtag") || recvHeader.GetVal("If-Match") != null || recvHeader.GetVal("If-None-Match") != null) { //Ver5.1.5 //string etagStr = string.Format("\"{0:x}-{1:x}\"", target.FileInfo.Length, (target.FileInfo.LastWriteTimeUtc.Ticks / 10000000)); var etagStr = WebServerUtil.Etag(target.FileInfo); string str; if (null != (str = recvHeader.GetVal("If-Match"))) { if (str != "*" && str != etagStr) { responseCode = 412; goto SEND; } } if (null != (str = recvHeader.GetVal("If-None-Match"))) { if (str != "*" && str == etagStr) { responseCode = 304; goto SEND; } } if ((bool)Conf.Get("useEtag")) document.AddHeader("ETag",etagStr); } //******************************************************************** //Range処理 //******************************************************************** document.AddHeader("Accept-Range","bytes"); var rangeFrom = 0L;//デフォルトは最初から var rangeTo = target.FileInfo.Length;//デフォルトは最後まで(ファイルサイズ) if (recvHeader.GetVal("Range") != null) {//レンジ指定のあるリクエストの場合 var range = recvHeader.GetVal("Range"); //指定範囲を取得する(マルチ指定には未対応) if (range.IndexOf("bytes=") == 0) { range = range.Substring(6); var tmp = range.Split('-'); //Ver5.3.5 ApacheKiller対処 if (tmp.Length > 20) { Logger.Set(LogKind.Secure, sockTcp,9000054, string.Format("[ Apache Killer ]Range:{0}", range)); AutoDeny(false, remoteIp); responseCode = 503; keepAlive = false;//切断 goto SEND; } if(tmp.Length == 2) { //Ver5.3.6 のデバッグ用 //tmp[1] = "499"; if(tmp[0] != "") { if(tmp[1] != "") {// bytes=0-10 0~10の11バイト //Ver5.5.9 rangeFrom = Convert.ToInt64(tmp[0]); if (tmp[1] != "") { //Ver5.5.9 rangeTo = Convert.ToInt64(tmp[1]); if (target.FileInfo.Length <= rangeTo) { rangeTo = target.FileInfo.Length - 1; } else { document.SetRangeTo = true;//Ver5.4.0 } } } else {// bytes=3- 3~最後まで rangeTo = target.FileInfo.Length - 1; rangeFrom = Convert.ToInt64(tmp[0]); } } else { if(tmp[1] != "") {// bytes=-3 最後から3バイト var len = Convert.ToInt64(tmp[1]); rangeTo = target.FileInfo.Length - 1; rangeFrom = rangeTo-len+1; if(rangeFrom<0) rangeFrom=0; document.SetRangeTo = true;//Ver5.4.0 } } if(rangeFrom <= rangeTo) { //正常に範囲を取得できた場合、事後Rangeモードで動作する document.AddHeader("Content-Range",string.Format("bytes {0}-{1}/{2}",rangeFrom,rangeTo,target.FileInfo.Length)); responseCode = 206; } } } } //通常ファイルのドキュメント if (request.Method != HttpMethod.Head) { if (!document.CreateFromFile(target.FullPath,rangeFrom,rangeTo)) break; } SEND: //レスポンスコードが200以外の場合は、ドキュメント(及び送信ヘッダ)をエラー用に変更する if(responseCode != 200 && responseCode != 302 && responseCode != 206 && responseCode != 207 && responseCode != 204 && responseCode != 201) { //ResponceCodeの応じてエラードキュメントを生成する if (!document.CreateFromErrorCode(request,responseCode)) break; if (responseCode == 301) {//ターゲットがファイルではなくディレクトの間違いの場合 if(urlStr != null) { var str = string.Format("{0}{1}/",urlStr,request.Uri); document.AddHeader("Location",Encoding.UTF8.GetBytes(str)); } } if (responseCode == 304 || responseCode == 301) {//304 or 301 の場合は、ヘッダのみになる document.Clear(); } else { if (responseCode == 401) { document.AddHeader("WWW-Authenticate", string.Format("Basic realm=\"{0}\"", authName)); } } } //Ver5.6.2 request.Send()廃止 var responseStr = request.CreateResponse(responseCode); sockTcp.AsciiSend(responseStr);//レスポンス送信 Logger.Set(LogKind.Detail, sockTcp, 4, responseStr);//ログ document.Send(keepAlive,this);//ドキュメント本体送信 } if(inputStream!=null) inputStream.Dispose(); if (outputStream != null) outputStream.Dispose(); //end://このソケット接続の終了 if (sockTcp != null) { sockTcp.Close(); } }
//ファイルのインクルード bool SsiInclude(string tag, string val, ref string str, Encoding encoding) { var newTarget = CreateTarget(tag, val); if (newTarget == null) return false; //ループに陥るため、自分自身はインクルードできない if (_target.FullPath == newTarget.FullPath) { _logger.Set(LogKind.Error, null, 15, string.Format("{0}", newTarget.FullPath)); return false; } if (newTarget.TargetKind == TargetKind.Cgi) { var cgi = new Cgi(); //TODO 変数削除 リファクタリング対象 //IPAddress remoteAddress = tcpObj.RemoteEndPoint.Address; //string remoteHostName = tcpObj.RemoteHost; //環境変数作成 //Ver5.6.2 //Env env = new Env(kernel, request, recvHeader, remoteAddress, remoteHostName, newTarget.FullPath); var env = new Env(_kernel, _conf,_request, _recvHeader,_sockTcp,newTarget.FullPath); const string param = ""; WebStream output; var cgiTimeout = (int)_conf.Get("cgiTimeout"); cgi.Exec(newTarget, param, env,null, out output, cgiTimeout); str = Encoding.ASCII.GetString(output.GetBytes()); //Ver5.9.1 CGI出力は、ヘッダをカットする var lines = str.Split('\n').ToList(); var index = lines.IndexOf("\r"); if (index != -1) { var sb = new StringBuilder(); for (int i = index + 1; i < lines.Count(); i++) { sb.Append(lines[i] + "\n"); } str = sb.ToString(); } } else if (newTarget.TargetKind == TargetKind.File || newTarget.TargetKind == TargetKind.Ssi) { if (File.Exists(newTarget.FullPath)) { using (var sr = new StreamReader(newTarget.FullPath, encoding)) { str = sr.ReadToEnd(); sr.Close(); } } else { return false; } } else { return false; } return true; }
//プログラム実行 bool SsiExec(string tag, string val, ref string str, Encoding encoding, SockTcp tcpObj) { Target newTarget; var param = ""; if (tag.ToLower() == "cmd") { param = val; newTarget = CreateTarget("comspec", null); if (newTarget == null) { return false; } } else if (tag.ToLower() == "cgi") { var cmd = val; var tmp = val.Split(new[] { ' ' }, 2); if (tmp.Length == 2) { cmd = tmp[0]; param = tmp[1]; } newTarget = CreateTarget("file", cmd); if (newTarget == null) { return false; } if (newTarget.TargetKind != TargetKind.Cgi) { _logger.Set(LogKind.Error, tcpObj, 27, string.Format("<!--#exec cgi=\"{0}\"-->", val)); return false; } } else { // cmd="" cgi="" 以外の場合はエラー _logger.Set(LogKind.Error, tcpObj, 28, ""); return false; } var cgi = new Cgi(); //TODO 変数削除リファクタリング対象 //IPAddress remoteAddress = tcpObj.RemoteEndPoint.Address; //string remoteHost = tcpObj.RemoteHost; //環境変数作成 //Env env = new Env(kernel, request, recvHeader, remoteAddress, remoteHost, newTarget.FullPath); var env = new Env(_kernel,_conf, _request, _recvHeader, tcpObj, newTarget.FullPath); WebStream output;//標準出力 const string err = ""; var cgiTimeout = (int)_conf.Get("cgiTimeout"); if (!cgi.Exec(newTarget, param, env, null, out output,cgiTimeout)) { str = err; } else{ var b = new byte[output.Length]; output.Read(b, 0, b.Length); str = encoding.GetString(b); } return true; }
//ファイルのインクルード bool SsiInclude(string tag, string val, ref string str, Encoding encoding) { var newTarget = CreateTarget(tag, val); if (newTarget == null) { return(false); } //ループに陥るため、自分自身はインクルードできない if (_target.FullPath == newTarget.FullPath) { _logger.Set(LogKind.Error, null, 15, string.Format("{0}", newTarget.FullPath)); return(false); } if (newTarget.TargetKind == TargetKind.Cgi) { var cgi = new Cgi(); //TODO 変数削除 リファクタリング対象 //IPAddress remoteAddress = tcpObj.RemoteEndPoint.Address; //string remoteHostName = tcpObj.RemoteHost; //環境変数作成 //Ver5.6.2 //Env env = new Env(kernel, request, recvHeader, remoteAddress, remoteHostName, newTarget.FullPath); var env = new Env(_kernel, _conf, _request, _recvHeader, _sockTcp, newTarget.FullPath); const string param = ""; WebStream output; var cgiTimeout = (int)_conf.Get("cgiTimeout"); cgi.Exec(newTarget, param, env, null, out output, cgiTimeout); str = Encoding.ASCII.GetString(output.GetBytes()); //Ver5.9.1 CGI出力は、ヘッダをカットする var lines = str.Split('\n').ToList(); var index = lines.IndexOf("\r"); if (index != -1) { var sb = new StringBuilder(); for (int i = index + 1; i < lines.Count(); i++) { sb.Append(lines[i] + "\n"); } str = sb.ToString(); } } else if (newTarget.TargetKind == TargetKind.File || newTarget.TargetKind == TargetKind.Ssi) { if (File.Exists(newTarget.FullPath)) { using (var sr = new StreamReader(newTarget.FullPath, encoding)) { str = sr.ReadToEnd(); sr.Close(); } } else { return(false); } } else { return(false); } return(true); }
//プログラム実行 bool SsiExec(string tag, string val, ref string str, Encoding encoding, SockTcp tcpObj) { Target newTarget; var param = ""; if (tag.ToLower() == "cmd") { param = val; newTarget = CreateTarget("comspec", null); if (newTarget == null) { return(false); } } else if (tag.ToLower() == "cgi") { var cmd = val; var tmp = val.Split(new[] { ' ' }, 2); if (tmp.Length == 2) { cmd = tmp[0]; param = tmp[1]; } newTarget = CreateTarget("file", cmd); if (newTarget == null) { return(false); } if (newTarget.TargetKind != TargetKind.Cgi) { _logger.Set(LogKind.Error, tcpObj, 27, string.Format("<!--#exec cgi=\"{0}\"-->", val)); return(false); } } else // cmd="" cgi="" 以外の場合はエラー { _logger.Set(LogKind.Error, tcpObj, 28, ""); return(false); } var cgi = new Cgi(); //TODO 変数削除リファクタリング対象 //IPAddress remoteAddress = tcpObj.RemoteEndPoint.Address; //string remoteHost = tcpObj.RemoteHost; //環境変数作成 //Env env = new Env(kernel, request, recvHeader, remoteAddress, remoteHost, newTarget.FullPath); var env = new Env(_kernel, _conf, _request, _recvHeader, tcpObj, newTarget.FullPath); WebStream output;//標準出力 const string err = ""; var cgiTimeout = (int)_conf.Get("cgiTimeout"); if (!cgi.Exec(newTarget, param, env, null, out output, cgiTimeout)) { str = err; } else { var b = new byte[output.Length]; output.Read(b, 0, b.Length); str = encoding.GetString(b); } return(true); }
//接続単位の処理 override protected void OnSubThread(SockObj sockObj) { Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US"); var sockTcp = (SockTcp)sockObj; var remoteIp = sockTcp.RemoteIp; //opBase 及び loggerはバーチャルホストで変更されるので、 //このポインタを初期化に使用できない bool keepAlive = true;//レスポンスが終了したとき接続を切断しないで継続する //1回目の通信でバーチャルホストの検索を実施する var checkVirtual = true; var request = new Request(Logger, sockTcp);//リクエストライン処理クラス //受信ヘッダ var recvHeader = new Header(); //Ver5.1.x string urlStr = null;//http://example.com //接続が継続している間は、このループの中にいる(継続か否かをkeepAliveで保持する) //「continue」は、次のリクエストを待つ 「break」は、接続を切断する事を意味する WebStream inputStream = null; var outputStream = new WebStream(-1); while (keepAlive && IsLife()) { int responseCode; //*************************************************************** // ドキュメント生成クラスの初期化 //*************************************************************** var contentType = new ContentType(Conf); var document = new Document(Kernel, Logger, Conf, sockTcp, contentType); var authrization = new Authorization(Conf, Logger); var authName = ""; //*************************************************************** //データ取得 //*************************************************************** //リクエスト取得 //ここのタイムアウト値は、大きすぎるとブラウザの切断を取得できないでブロックしてしまう var requestStr = sockTcp.AsciiRecv(Timeout, this); if (requestStr == null) { break; } //\r\nの削除 requestStr = Inet.TrimCrlf(requestStr); //Ver5.8.8 リクエストの解釈に失敗した場合に、処理を中断する //request.Init(requestStr); if (!request.Init(requestStr)) { break; } //ヘッダ取得(内部データは初期化される) if (!recvHeader.Recv(sockTcp, (int)Conf.Get("timeOut"), this)) { break; } { //Ver5.1.x var hostStr = recvHeader.GetVal("host"); urlStr = hostStr == null ? null : string.Format("{0}://{1}", (ssl != null)?"https":"http", hostStr); } //入力取得(POST及びPUTの場合) var contentLengthStr = recvHeader.GetVal("Content-Length"); if (contentLengthStr != null) { try{ //max,lenはともにlong var max = Convert.ToInt64(contentLengthStr); if (max != 0)//送信データあり { inputStream = new WebStream((256000 < max)?-1:(int)max); var errorCount = 0; while (inputStream.Length < max && IsLife()) { var len = max - inputStream.Length; if (len > 51200000) { len = 51200000; } var b = sockTcp.Recv((int)len, (int)Conf.Get("timeOut"), this); if (!inputStream.Add(b)) { errorCount++;//エラー蓄積 Logger.Set(LogKind.Error, null, 41, string.Format("content-Length={0} Recv={1}", max, inputStream.Length)); } else { errorCount = 0;//初期化 } Logger.Set(LogKind.Detail, null, 38, string.Format("Content-Length={0} {1}bytes Received.", max, inputStream.Length)); if (errorCount > 5) //5回連続して受信が無かった場合、サーバエラー { responseCode = 500; goto SEND;//サーバエラー } Thread.Sleep(10); } Logger.Set(LogKind.Detail, null, 39, string.Format("Content-Length={0} {1}bytes", max, inputStream.Length)); } }catch (Exception ex) { Logger.Set(LogKind.Error, null, 40, ex.Message); } } // /によるパラメータ渡しに対応 //for (int i = 0;i < Option->CgiExt->Count;i++) { // wsprintf(TmpBuf,".%s/",Option->CgiExt->Strings[i]); // strupr(TmpBuf); // strcpy(Buf,Headers->Uri); // strupr(Buf); // if (NULL != (p = strstr(Buf,TmpBuf))) { // i = p - Buf; // i += strlen(TmpBuf) - 1; // p = &Headers->Uri[i]; // *p = '\0'; // p = &Headers->UriNoConversion[i]; // *p = '\0'; // wsprintf(TmpBuf,"/%s",p + 1); // Headers->PathInfo = new char[strlen(TmpBuf) + 1]; // strcpy(Headers->PathInfo,TmpBuf); // break; // } //} //*************************************************************** //バーチャルホストの検索を実施し、opBase、logger及び webDavDb を置き換える //*************************************************************** if (checkVirtual) //初回のみ { ReplaceVirtualHost(recvHeader.GetVal("host"), sockTcp.LocalAddress.Address, sockTcp.LocalAddress.Port); checkVirtual = false; } //*************************************************************** //接続を継続するかどうかの判断 keepAliveの初期化 //*************************************************************** if (ssl != null) { keepAlive = false;//SSL通信では、1回づつコネクションが必要 } else { if (request.Ver == "HTTP/1.1") //HTTP1.1はデフォルトで keepAlive=true { keepAlive = true; } else // HTTP/1.1以外の場合、継続接続は、Connection: Keep-Aliveの有無に従う { keepAlive = recvHeader.GetVal("Connection") == "Keep-Alive"; } } //*************************************************************** // ドキュメント生成クラスの初期化 //*************************************************************** //var contentType = new ContentType(OneOption); //var document = new Document(kernel,Logger,OneOption,sockTcp,contentType); //*************************************************************** // ログ //*************************************************************** Logger.Set(LogKind.Normal, sockTcp, ssl != null ? 23 : 24, request.LogStr); //*************************************************************** // 認証 //*************************************************************** //var authrization = new Authorization(OneOption,Logger); //string authName = ""; if (!authrization.Check(request.Uri, recvHeader.GetVal("authorization"), ref authName)) { responseCode = 401; keepAlive = false;//切断 goto SEND; } //*************************************************************** // 不正なURIに対するエラー処理 //*************************************************************** //URIを点検して不正な場合はエラーコードを返す responseCode = CheckUri(sockTcp, request, recvHeader); if (responseCode != 200) { keepAlive = false;//切断 goto SEND; } //*************************************************************** //ターゲットオブジェクトの初期化 //*************************************************************** var target = new Target(Conf, Logger); if (target.DocumentRoot == null) { Logger.Set(LogKind.Error, sockTcp, 14, string.Format("documentRoot={0}", Conf.Get("documentRoot"))); //ドキュメントルートで指定されたフォルダが存在しません(処理を継続できません) break; //ドキュメントルートが無効な場合は、処理を継続できない } target.InitFromUri(request.Uri); //*************************************************************** // 送信ヘッダの追加 //*************************************************************** // 特別拡張 BlackJumboDog経由のリクエストの場合 送信ヘッダにRemoteHostを追加する if ((bool)Conf.Get("useExpansion")) { if (recvHeader.GetVal("Host") != null) { document.AddHeader("RemoteHost", sockTcp.RemoteAddress.Address.ToString()); } } //受信ヘッダに「PathInfo:」が設定されている場合、送信ヘッダに「PathTranslated」を追加する var pathInfo = recvHeader.GetVal("PathInfo"); if (pathInfo != null) { pathInfo = target.DocumentRoot + pathInfo; document.AddHeader("PathTranslated", Util.SwapChar('/', '\\', pathInfo)); } //*************************************************************** //メソッドに応じた処理 OPTIONS 対応 Ver5.1.x //*************************************************************** if (WebDav.IsTarget(request.Method)) { var webDav = new WebDav(Logger, _webDavDb, target, document, urlStr, recvHeader.GetVal("Depth"), contentType, (bool)Conf.Get("useEtag")); var inputBuf = new byte[0]; if (inputStream != null) { inputBuf = inputStream.GetBytes(); } switch (request.Method) { case HttpMethod.Options: responseCode = webDav.Option(); break; case HttpMethod.Delete: responseCode = webDav.Delete(); break; case HttpMethod.Put: responseCode = webDav.Put(inputBuf); break; case HttpMethod.Proppatch: responseCode = webDav.PropPatch(inputBuf); break; case HttpMethod.Propfind: responseCode = webDav.PropFind(); break; case HttpMethod.Mkcol: responseCode = webDav.MkCol(); break; case HttpMethod.Copy: case HttpMethod.Move: responseCode = 405; //Destnationで指定されたファイルは書き込み許可されているか? var dstTarget = new Target(Conf, Logger); string destinationStr = recvHeader.GetVal("Destination"); if (destinationStr != null) { if (destinationStr.IndexOf("://") == -1) { destinationStr = urlStr + destinationStr; } var uri = new Uri(destinationStr); dstTarget.InitFromUri(uri.LocalPath); if (dstTarget.WebDavKind == WebDavKind.Write) { var overwrite = false; var overwriteStr = recvHeader.GetVal("Overwrite"); if (overwriteStr != null) { if (overwriteStr == "F") { overwrite = true; } } responseCode = webDav.MoveCopy(dstTarget, overwrite, request.Method); document.AddHeader("Location", destinationStr); } } break; } //WebDAVに対するリクエストは、ここで処理完了 goto SEND; } //以下 label SENDまでの間は、GET/POSTに関する処理 //*************************************************************** //ターゲットの種類に応じた処理 //*************************************************************** if (target.TargetKind == TargetKind.Non) //見つからない場合 { responseCode = 404; goto SEND; } if (target.TargetKind == TargetKind.Move) //ターゲットはディレクトリの場合 { responseCode = 301; goto SEND; } if (target.TargetKind == TargetKind.Dir) //ディレクトリ一覧表示の場合 //インデックスドキュメントを生成する { if (!document.CreateFromIndex(request, target.FullPath)) { break; } goto SEND; } //*************************************************************** // 隠し属性のファイルへのアクセス制御 //*************************************************************** if (!(bool)Conf.Get("useHidden")) { if ((target.Attr & FileAttributes.Hidden) == FileAttributes.Hidden) { //エラーキュメントを生成する responseCode = 404; keepAlive = false;//切断 goto SEND; } } if (target.TargetKind == TargetKind.Cgi || target.TargetKind == TargetKind.Ssi) { keepAlive = false;//デフォルトで切断 //環境変数作成 var env = new Env(Kernel, Conf, request, recvHeader, sockTcp, target.FullPath); // 詳細ログ Logger.Set(LogKind.Detail, sockTcp, 18, string.Format("{0} {1}", target.CgiCmd, Path.GetFileName(target.FullPath))); if (target.TargetKind == TargetKind.Cgi) { var cgi = new Cgi(); var cgiTimeout = (int)Conf.Get("cgiTimeout"); if (!cgi.Exec(target, request.Param, env, inputStream, out outputStream, cgiTimeout)) { // エラー出力 var errStr = Encoding.ASCII.GetString(outputStream.GetBytes()); Logger.Set(LogKind.Error, sockTcp, 16, errStr); responseCode = 500; goto SEND; } //*************************************************** // NPH (Non-Parsed Header CGI)スクリプト nph-で始まる場合、サーバ処理(レスポンスコードやヘッダの追加)を経由しない //*************************************************** if (Path.GetFileName(target.FullPath).IndexOf("nph-") == 0) { sockTcp.SendUseEncode(outputStream.GetBytes());//CGI出力をそのまま送信する break; } // CGIで得られた出力から、本体とヘッダを分離する if (!document.CreateFromCgi(outputStream.GetBytes())) { break; } // cgi出力で、Location:が含まれる場合、レスポンスコードを302にする if (document.SearchLocation())//Location:ヘッダを含むかどうか { responseCode = 302; } goto SEND; } //SSI var ssi = new Ssi(Kernel, Logger, Conf, sockTcp, request, recvHeader); if (!ssi.Exec(target, env, outputStream)) { // エラー出力 Logger.Set(LogKind.Error, sockTcp, 22, MLang.GetString(outputStream.GetBytes())); responseCode = 500; goto SEND; } document.CreateFromSsi(outputStream.GetBytes(), target.FullPath); goto SEND; } //以下は、通常ファイルの処理 TARGET_KIND.FILE //******************************************************************** //Modified処理 //******************************************************************** if (recvHeader.GetVal("If_Modified_Since") != null) { var dt = Util.Str2Time(recvHeader.GetVal("If-Modified-Since")); if (target.FileInfo.LastWriteTimeUtc.Ticks / 10000000 <= dt.Ticks / 10000000) { responseCode = 304; goto SEND; } } if (recvHeader.GetVal("If_Unmodified_Since") != null) { var dt = Util.Str2Time(recvHeader.GetVal("If_Unmodified_Since")); if (target.FileInfo.LastWriteTimeUtc.Ticks / 10000000 > dt.Ticks / 10000000) { responseCode = 412; goto SEND; } } document.AddHeader("Last-Modified", Util.UtcTime2Str(target.FileInfo.LastWriteTimeUtc)); //******************************************************************** //ETag処理 //******************************************************************** // (1) useEtagがtrueの場合は、送信時にETagを付加する // (2) If-None-Match 若しくはIf-Matchヘッダが指定されている場合は、排除対象かどうかの判断が必要になる if ((bool)Conf.Get("useEtag") || recvHeader.GetVal("If-Match") != null || recvHeader.GetVal("If-None-Match") != null) { //Ver5.1.5 //string etagStr = string.Format("\"{0:x}-{1:x}\"", target.FileInfo.Length, (target.FileInfo.LastWriteTimeUtc.Ticks / 10000000)); var etagStr = WebServerUtil.Etag(target.FileInfo); string str; if (null != (str = recvHeader.GetVal("If-Match"))) { if (str != "*" && str != etagStr) { responseCode = 412; goto SEND; } } if (null != (str = recvHeader.GetVal("If-None-Match"))) { if (str != "*" && str == etagStr) { responseCode = 304; goto SEND; } } if ((bool)Conf.Get("useEtag")) { document.AddHeader("ETag", etagStr); } } //******************************************************************** //Range処理 //******************************************************************** document.AddHeader("Accept-Range", "bytes"); var rangeFrom = 0L; //デフォルトは最初から var rangeTo = target.FileInfo.Length; //デフォルトは最後まで(ファイルサイズ) if (recvHeader.GetVal("Range") != null) //レンジ指定のあるリクエストの場合 { var range = recvHeader.GetVal("Range"); //指定範囲を取得する(マルチ指定には未対応) if (range.IndexOf("bytes=") == 0) { range = range.Substring(6); var tmp = range.Split('-'); //Ver5.3.5 ApacheKiller対処 if (tmp.Length > 20) { Logger.Set(LogKind.Secure, sockTcp, 9000054, string.Format("[ Apache Killer ]Range:{0}", range)); AutoDeny(false, remoteIp); responseCode = 503; keepAlive = false;//切断 goto SEND; } if (tmp.Length == 2) { //Ver5.3.6 のデバッグ用 //tmp[1] = "499"; if (tmp[0] != "") { if (tmp[1] != "") // bytes=0-10 0~10の11バイト //Ver5.5.9 { rangeFrom = Convert.ToInt64(tmp[0]); if (tmp[1] != "") { //Ver5.5.9 rangeTo = Convert.ToInt64(tmp[1]); if (target.FileInfo.Length <= rangeTo) { rangeTo = target.FileInfo.Length - 1; } else { document.SetRangeTo = true;//Ver5.4.0 } } } else // bytes=3- 3~最後まで { rangeTo = target.FileInfo.Length - 1; rangeFrom = Convert.ToInt64(tmp[0]); } } else { if (tmp[1] != "") // bytes=-3 最後から3バイト { var len = Convert.ToInt64(tmp[1]); rangeTo = target.FileInfo.Length - 1; rangeFrom = rangeTo - len + 1; if (rangeFrom < 0) { rangeFrom = 0; } document.SetRangeTo = true;//Ver5.4.0 } } if (rangeFrom <= rangeTo) { //正常に範囲を取得できた場合、事後Rangeモードで動作する document.AddHeader("Content-Range", string.Format("bytes {0}-{1}/{2}", rangeFrom, rangeTo, target.FileInfo.Length)); responseCode = 206; } } } } //通常ファイルのドキュメント if (request.Method != HttpMethod.Head) { if (!document.CreateFromFile(target.FullPath, rangeFrom, rangeTo)) { break; } } SEND: //レスポンスコードが200以外の場合は、ドキュメント(及び送信ヘッダ)をエラー用に変更する if (responseCode != 200 && responseCode != 302 && responseCode != 206 && responseCode != 207 && responseCode != 204 && responseCode != 201) { //ResponceCodeの応じてエラードキュメントを生成する if (!document.CreateFromErrorCode(request, responseCode)) { break; } if (responseCode == 301) //ターゲットがファイルではなくディレクトの間違いの場合 { if (urlStr != null) { var str = string.Format("{0}{1}/", urlStr, request.Uri); document.AddHeader("Location", Encoding.UTF8.GetBytes(str)); } } if (responseCode == 304 || responseCode == 301) //304 or 301 の場合は、ヘッダのみになる { document.Clear(); } else { if (responseCode == 401) { document.AddHeader("WWW-Authenticate", string.Format("Basic realm=\"{0}\"", authName)); } } } //Ver5.6.2 request.Send()廃止 var responseStr = request.CreateResponse(responseCode); sockTcp.AsciiSend(responseStr); //レスポンス送信 Logger.Set(LogKind.Detail, sockTcp, 4, responseStr); //ログ document.Send(keepAlive, this);//ドキュメント本体送信 } if (inputStream != null) { inputStream.Dispose(); } if (outputStream != null) { outputStream.Dispose(); } //end://このソケット接続の終了 if (sockTcp != null) { sockTcp.Close(); } }