00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032 #include <ncbi_pch.hpp>
00033 #include <corelib/ncbienv.hpp>
00034 #include <corelib/rwstream.hpp>
00035 #include <corelib/stream_utils.hpp>
00036 #include <corelib/ncbi_system.hpp>
00037 #include <corelib/rwstream.hpp>
00038 #include <corelib/ncbi_safe_static.hpp>
00039 #include <corelib/request_ctx.hpp>
00040 #include <corelib/ncbi_strings.h>
00041
00042 #include <util/multi_writer.hpp>
00043 #include <util/cache/cache_ref.hpp>
00044
00045 #include <cgi/cgictx.hpp>
00046 #include <cgi/cgi_exception.hpp>
00047 #include <cgi/cgi_serial.hpp>
00048 #include <cgi/error_codes.hpp>
00049
00050 #ifdef NCBI_OS_UNIX
00051 # include <unistd.h>
00052 #endif
00053
00054
00055 #define NCBI_USE_ERRCODE_X Cgi_Application
00056
00057
00058 BEGIN_NCBI_SCOPE
00059
00060
00061 NCBI_PARAM_DECL(bool, CGI, Print_Http_Referer);
00062 NCBI_PARAM_DEF_EX(bool, CGI, Print_Http_Referer, true, eParam_NoThread,
00063 CGI_PRINT_HTTP_REFERER);
00064 static NCBI_PARAM_TYPE(CGI, Print_Http_Referer) s_PrintRefererParam;
00065
00066
00067 NCBI_PARAM_DECL(bool, CGI, Print_User_Agent);
00068 NCBI_PARAM_DEF_EX(bool, CGI, Print_User_Agent, true, eParam_NoThread,
00069 CGI_PRINT_USER_AGENT);
00070 static NCBI_PARAM_TYPE(CGI, Print_User_Agent) s_PrintUserAgentParam;
00071
00072
00073 NCBI_PARAM_DECL(bool, CGI, Print_Self_Url);
00074 NCBI_PARAM_DEF_EX(bool, CGI, Print_Self_Url, true, eParam_NoThread,
00075 CGI_PRINT_SELF_URL);
00076 static NCBI_PARAM_TYPE(CGI, Print_Self_Url) s_PrintSelfUrlParam;
00077
00078
00079
00080
00081
00082
00083
00084 class CCGIStreamReader : public IReader
00085 {
00086 public:
00087 CCGIStreamReader(istream& is) : m_IStr(is) { }
00088
00089 virtual ERW_Result Read(void* buf,
00090 size_t count,
00091 size_t* bytes_read = 0);
00092 virtual ERW_Result PendingCount(size_t* count)
00093 { return eRW_NotImplemented; }
00094
00095 protected:
00096 istream& m_IStr;
00097 };
00098
00099
00100 ERW_Result CCGIStreamReader::Read(void* buf,
00101 size_t count,
00102 size_t* bytes_read)
00103 {
00104 streamsize x_read = CStreamUtils::Readsome(m_IStr, (char*)buf, count);
00105 ERW_Result result;
00106 if (x_read < 0) {
00107 result = eRW_Error;
00108 }
00109 else if (x_read > 0) {
00110 result = eRW_Success;
00111 }
00112 else {
00113 result = eRW_Eof;
00114 }
00115 if (bytes_read) {
00116 *bytes_read = x_read < 0 ? 0 : x_read;
00117 }
00118 return result;
00119 }
00120
00121
00122 class CCGIStreamWriter : public IWriter
00123 {
00124 public:
00125 CCGIStreamWriter(ostream& os) : m_OStr(os) { }
00126
00127 virtual ERW_Result Write(const void* buf,
00128 size_t count,
00129 size_t* bytes_written = 0);
00130
00131 virtual ERW_Result Flush(void)
00132 { return m_OStr.flush() ? eRW_Success : eRW_Error; }
00133
00134 protected:
00135 ostream& m_OStr;
00136 };
00137
00138
00139 ERW_Result CCGIStreamWriter::Write(const void* buf,
00140 size_t count,
00141 size_t* bytes_written)
00142 {
00143 ERW_Result result;
00144 if (!m_OStr.write((char*)buf, count)) {
00145 result = eRW_Error;
00146 }
00147 else {
00148 result = eRW_Success;
00149 }
00150 if (bytes_written) {
00151 *bytes_written = result == eRW_Success ? count : 0;
00152 }
00153 return result;
00154 }
00155
00156
00157
00158
00159
00160
00161
00162 CCgiApplication* CCgiApplication::Instance(void)
00163 {
00164 return dynamic_cast<CCgiApplication*> (CParent::Instance());
00165 }
00166
00167
00168 int CCgiApplication::Run(void)
00169 {
00170
00171 int result;
00172
00173
00174 if ( x_RunFastCGI(&result) ) {
00175 return result;
00176 }
00177
00178
00179
00180
00181 CDiagRestorer diag_restorer;
00182
00183
00184 #if defined(NCBI_OS_UNIX)
00185 PushDiagPostPrefix(NStr::IntToString(getpid()).c_str());
00186 #endif
00187 PushDiagPostPrefix(GetEnvironment().Get(m_DiagPrefixEnv).c_str());
00188
00189
00190 CTime start_time(CTime::eCurrent);
00191
00192
00193 bool is_stat_log = GetConfig().GetBool("CGI", "StatLog", false,
00194 0, CNcbiRegistry::eReturn);
00195 bool skip_stat_log = false;
00196 auto_ptr<CCgiStatistics> stat(is_stat_log ? CreateStat() : 0);
00197
00198 CNcbiOstream* orig_stream = NULL;
00199
00200 CNcbiStrstream result_copy;
00201 auto_ptr<CNcbiOstream> new_stream;
00202
00203 try {
00204 _TRACE("(CGI) CCgiApplication::Run: calling ProcessRequest");
00205 GetDiagContext().SetAppState(eDiagAppState_RequestBegin);
00206
00207 m_Context.reset( CreateContext() );
00208 ConfigureDiagnostics(*m_Context);
00209 x_AddLBCookie();
00210 try {
00211
00212 x_OnEvent(eStartRequest, 0);
00213
00214 VerifyCgiContext(*m_Context);
00215 ProcessHttpReferer();
00216 LogRequest();
00217
00218 try {
00219 m_Cache.reset( GetCacheStorage() );
00220 } catch( exception& ex ) {
00221 ERR_POST_X(1, "Couldn't create cache : " << ex.what());
00222 }
00223 bool skip_process_request = false;
00224 bool caching_needed = IsCachingNeeded(m_Context->GetRequest());
00225 if (m_Cache.get() && caching_needed) {
00226 skip_process_request = GetResultFromCache(m_Context->GetRequest(),
00227 m_Context->GetResponse().out());
00228 }
00229 if (!skip_process_request) {
00230 if( m_Cache.get() ) {
00231 list<CNcbiOstream*> slist;
00232 orig_stream = m_Context->GetResponse().GetOutput();
00233 slist.push_back(orig_stream);
00234 slist.push_back(&result_copy);
00235 new_stream.reset(new CWStream(new CMultiWriter(slist), 0,0,
00236 CRWStreambuf::fOwnWriter));
00237 m_Context->GetResponse().SetOutput(new_stream.get());
00238 }
00239 GetDiagContext().SetAppState(eDiagAppState_Request);
00240 result = ProcessRequest(*m_Context);
00241 GetDiagContext().SetAppState(eDiagAppState_RequestEnd);
00242 if (result != 0) {
00243 SetHTTPStatus(500);
00244 } else {
00245 if (m_Cache.get()) {
00246 m_Context->GetResponse().Flush();
00247 if (m_IsResultReady) {
00248 if(caching_needed)
00249 SaveResultToCache(m_Context->GetRequest(), result_copy);
00250 else {
00251 auto_ptr<CCgiRequest> request(GetSavedRequest(m_RID));
00252 if (request.get())
00253 SaveResultToCache(*request, result_copy);
00254 }
00255 } else if (caching_needed) {
00256 SaveRequest(m_RID, m_Context->GetRequest());
00257 }
00258 }
00259 }
00260 }
00261 }
00262 catch (CCgiException& e) {
00263 if ( e.GetStatusCode() < CCgiException::e200_Ok ||
00264 e.GetStatusCode() >= CCgiException::e400_BadRequest ) {
00265 throw;
00266 }
00267 GetDiagContext().SetAppState(eDiagAppState_RequestEnd);
00268
00269
00270 m_Context->GetResponse().SetStatus(e.GetStatusCode(),
00271 e.GetStatusMessage());
00272 result = 0;
00273 }
00274 _TRACE("CCgiApplication::Run: flushing");
00275 m_Context->GetResponse().Flush();
00276 _TRACE("CCgiApplication::Run: return " << result);
00277 x_OnEvent(result == 0 ? eSuccess : eError, result);
00278 x_OnEvent(eExit, result);
00279 }
00280 catch (exception& e) {
00281 GetDiagContext().SetAppState(eDiagAppState_RequestEnd);
00282
00283 result = OnException(e, NcbiCout);
00284 x_OnEvent(eException, result);
00285
00286
00287 {{
00288 string msg = "(CGI) CCgiApplication::ProcessRequest() failed: ";
00289 msg += e.what();
00290
00291 if ( is_stat_log ) {
00292 stat->Reset(start_time, result, &e);
00293 msg = stat->Compose();
00294 stat->Submit(msg);
00295 skip_stat_log = true;
00296 }
00297 }}
00298
00299
00300 NCBI_REPORT_EXCEPTION_X(13, "(CGI) CCgiApplication::Run", e);
00301 }
00302
00303
00304 if ( is_stat_log && !skip_stat_log ) {
00305 stat->Reset(start_time, result);
00306 string msg = stat->Compose();
00307 stat->Submit(msg);
00308 }
00309
00310 x_OnEvent(eEndRequest, 120);
00311 x_OnEvent(eExit, result);
00312
00313 if (orig_stream)
00314 m_Context->GetResponse().SetOutput(NULL);
00315 return result;
00316 }
00317
00318
00319 const char* kExtraType_CGI = "NCBICGI";
00320
00321 void CCgiApplication::ProcessHttpReferer(void)
00322 {
00323
00324 CCgiContext& ctx = GetContext();
00325 string ref = ctx.GetSelfURL();
00326 if ( !ref.empty() ) {
00327 string args =
00328 ctx.GetRequest().GetProperty(eCgi_QueryString);
00329 if ( !args.empty() ) {
00330 ref += "?" + args;
00331 }
00332 GetConfig().Set("CONN", "HTTP_REFERER", ref);
00333 }
00334 }
00335
00336
00337 void CCgiApplication::LogRequest(void) const
00338 {
00339 const CCgiContext& ctx = GetContext();
00340 string str;
00341 if ( s_PrintSelfUrlParam.Get() ) {
00342
00343 str = ctx.GetSelfURL();
00344 if ( !str.empty() ) {
00345 string args =
00346 ctx.GetRequest().GetProperty(eCgi_QueryString);
00347 if ( !args.empty() ) {
00348 str += "?" + args;
00349 }
00350 GetDiagContext().Extra().
00351
00352 Print("SELF_URL", str);
00353 }
00354 }
00355
00356 if ( s_PrintRefererParam.Get() ) {
00357 str = ctx.GetRequest().GetProperty(eCgi_HttpReferer);
00358 if ( !str.empty() ) {
00359 GetDiagContext().Extra().
00360
00361 Print("HTTP_REFERER", str);
00362 }
00363 }
00364
00365 if ( s_PrintUserAgentParam.Get() ) {
00366 str = ctx.GetRequest().GetProperty(eCgi_HttpUserAgent);
00367 if ( !str.empty() ) {
00368 GetDiagContext().Extra().
00369
00370 Print("USER_AGENT", str);
00371 }
00372 }
00373 }
00374
00375
00376 void CCgiApplication::SetupArgDescriptions(CArgDescriptions* arg_desc)
00377 {
00378 arg_desc->SetArgsType(CArgDescriptions::eCgiArgs);
00379
00380 CParent::SetupArgDescriptions(arg_desc);
00381 }
00382
00383
00384 CCgiContext& CCgiApplication::x_GetContext( void ) const
00385 {
00386 if ( !m_Context.get() ) {
00387 ERR_POST_X(2, "CCgiApplication::GetContext: no context set");
00388 throw runtime_error("no context set");
00389 }
00390 return *m_Context;
00391 }
00392
00393
00394 CNcbiResource& CCgiApplication::x_GetResource( void ) const
00395 {
00396 if ( !m_Resource.get() ) {
00397 ERR_POST_X(3, "CCgiApplication::GetResource: no resource set");
00398 throw runtime_error("no resource set");
00399 }
00400 return *m_Resource;
00401 }
00402
00403
00404 NCBI_PARAM_DECL(bool, CGI, Merge_Log_Lines);
00405 NCBI_PARAM_DEF_EX(bool, CGI, Merge_Log_Lines, true, eParam_NoThread,
00406 CGI_MERGE_LOG_LINES);
00407 static NCBI_PARAM_TYPE(CGI, Merge_Log_Lines) s_MergeLogLines;
00408
00409
00410 void CCgiApplication::Init(void)
00411 {
00412 if ( s_MergeLogLines.Get() ) {
00413
00414 SetDiagPostFlag(eDPF_PreMergeLines);
00415 SetDiagPostFlag(eDPF_MergeLines);
00416 }
00417
00418 CParent::Init();
00419
00420 m_Resource.reset(LoadResource());
00421
00422 m_DiagPrefixEnv = GetConfig().Get("CGI", "DiagPrefixEnv");
00423 }
00424
00425
00426 void CCgiApplication::Exit(void)
00427 {
00428 m_Resource.reset(0);
00429 CParent::Exit();
00430 }
00431
00432
00433 CNcbiResource* CCgiApplication::LoadResource(void)
00434 {
00435 return 0;
00436 }
00437
00438
00439 CCgiServerContext* CCgiApplication::LoadServerContext(CCgiContext& )
00440 {
00441 return 0;
00442 }
00443
00444
00445 NCBI_PARAM_DECL(bool, CGI, Count_Transfered);
00446 NCBI_PARAM_DEF_EX(bool, CGI, Count_Transfered, false, eParam_NoThread,
00447 CGI_COUNT_TRANSFERED);
00448 typedef NCBI_PARAM_TYPE(CGI, Count_Transfered) TCGI_Count_Transfered;
00449
00450
00451 CCgiContext* CCgiApplication::CreateContext
00452 (CNcbiArguments* args,
00453 CNcbiEnvironment* env,
00454 CNcbiIstream* inp,
00455 CNcbiOstream* out,
00456 int ifd,
00457 int ofd)
00458 {
00459 int errbuf_size =
00460 GetConfig().GetInt("CGI", "RequestErrBufSize", 256, 0,
00461 CNcbiRegistry::eReturn);
00462
00463 if ( TCGI_Count_Transfered::GetDefault() ) {
00464 if ( !inp ) {
00465 if ( !m_InputStream.get() ) {
00466 m_InputStream.reset(
00467 new CRStream(new CCGIStreamReader(std::cin),
00468 CRWStreambuf::fOwnReader));
00469 }
00470 inp = m_InputStream.get();
00471 }
00472 if ( !out ) {
00473 if ( !m_OutputStream.get() ) {
00474 m_OutputStream.reset(
00475 new CWStream(new CCGIStreamWriter(std::cout),
00476 CRWStreambuf::fOwnWriter));
00477 }
00478 out = m_OutputStream.get();
00479 if ( m_InputStream.get() ) {
00480
00481 inp->tie(out);
00482 }
00483 }
00484 }
00485 return
00486 new CCgiContext(*this, args, env, inp, out, ifd, ofd,
00487 (errbuf_size >= 0) ? (size_t) errbuf_size : 256,
00488 m_RequestFlags);
00489 }
00490
00491
00492 void CCgiApplication::SetCafService(CCookieAffinity* caf)
00493 {
00494 m_Caf.reset(caf);
00495 }
00496
00497
00498
00499
00500
00501
00502 class CStderrDiagFactory : public CDiagFactory
00503 {
00504 public:
00505 virtual CDiagHandler* New(const string&) {
00506 return new CStreamDiagHandler(&NcbiCerr);
00507 }
00508 };
00509
00510
00511 class CAsBodyDiagFactory : public CDiagFactory
00512 {
00513 public:
00514 CAsBodyDiagFactory(CCgiApplication* app) : m_App(app) {}
00515 virtual CDiagHandler* New(const string&) {
00516 CCgiResponse& response = m_App->GetContext().GetResponse();
00517 CDiagHandler* result = new CStreamDiagHandler(&response.out());
00518 if (!response.IsHeaderWritten()) {
00519 response.SetContentType("text/plain");
00520 response.WriteHeader();
00521 }
00522 response.SetOutput(0);
00523 return result;
00524 }
00525
00526 private:
00527 CCgiApplication* m_App;
00528 };
00529
00530
00531 CCgiApplication::CCgiApplication(void)
00532 : m_RequestFlags(0),
00533 m_HostIP(0),
00534 m_Iteration(0),
00535 m_ArgContextSync(false),
00536 m_IsResultReady(true),
00537 m_ShouldExit(false),
00538 m_RequestStartPrinted(false)
00539 {
00540
00541 CDiagContext::SetUseRootLog();
00542
00543 SuppressSystemMessageBox();
00544
00545
00546 SetDiagPostFlag(eDPF_RequestId);
00547 SetDiagTraceFlag(eDPF_RequestId);
00548
00549 SetStdioFlags(fBinaryCin | fBinaryCout);
00550 DisableArgDescriptions();
00551 RegisterDiagFactory("stderr", new CStderrDiagFactory);
00552 RegisterDiagFactory("asbody", new CAsBodyDiagFactory(this));
00553 }
00554
00555
00556 CCgiApplication::~CCgiApplication(void)
00557 {
00558 ITERATE (TDiagFactoryMap, it, m_DiagFactories) {
00559 delete it->second;
00560 }
00561 if ( m_HostIP )
00562 free(m_HostIP);
00563 }
00564
00565
00566 int CCgiApplication::OnException(exception& e, CNcbiOstream& os)
00567 {
00568
00569 string status_str = "500 Server Error";
00570 string message = "";
00571 SetHTTPStatus(500);
00572 CException* ce = dynamic_cast<CException*> (&e);
00573 if ( ce ) {
00574 message = ce->GetMsg();
00575 CCgiException* cgi_e = dynamic_cast<CCgiException*>(&e);
00576 if ( cgi_e ) {
00577 if ( cgi_e->GetStatusCode() != CCgiException::eStatusNotSet ) {
00578 SetHTTPStatus(cgi_e->GetStatusCode());
00579 status_str = NStr::IntToString(cgi_e->GetStatusCode()) +
00580 " " + cgi_e->GetStatusMessage();
00581 }
00582 else {
00583
00584
00585 if (dynamic_cast<CCgiRequestException*> (&e) ||
00586 dynamic_cast<CCgiArgsException*> (&e)) {
00587 SetHTTPStatus(400);
00588 status_str = "400 Malformed HTTP Request";
00589 }
00590 }
00591 }
00592 }
00593 else {
00594 message = e.what();
00595 }
00596
00597 try {
00598
00599 os << "Status: " << status_str << HTTP_EOL;
00600 os << "Content-Type: text/plain" HTTP_EOL HTTP_EOL;
00601
00602
00603 os << "ERROR: " << status_str << " " HTTP_EOL HTTP_EOL;
00604 os << message;
00605
00606 if ( dynamic_cast<CArgException*> (&e) ) {
00607 string ustr;
00608 const CArgDescriptions* descr = GetArgDescriptions();
00609 if (descr) {
00610 os << descr->PrintUsage(ustr) << HTTP_EOL HTTP_EOL;
00611 }
00612 }
00613
00614
00615
00616 if ( !os.good() ) {
00617 ERR_POST_X(4, "CCgiApplication::OnException() failed to send error page"
00618 " back to the client");
00619 return -1;
00620 }
00621 }
00622 catch (exception& e) {
00623 NCBI_REPORT_EXCEPTION_X(14, "(CGI) CCgiApplication::Run", e);
00624 }
00625 return 0;
00626 }
00627
00628
00629 const CArgs& CCgiApplication::GetArgs(void) const
00630 {
00631
00632 if (!GetArgDescriptions() || !m_Context.get())
00633 return CParent::GetArgs();
00634
00635
00636 if ( m_ArgContextSync )
00637 return *m_CgiArgs;
00638
00639
00640 if ( !m_CgiArgs.get() )
00641 m_CgiArgs.reset(new CArgs());
00642
00643
00644 *m_CgiArgs = CParent::GetArgs();
00645
00646
00647 GetArgDescriptions()->ConvertKeys(m_CgiArgs.get(),
00648 GetContext().GetRequest().GetEntries(),
00649 true );
00650
00651 m_ArgContextSync = true;
00652 return *m_CgiArgs;
00653 }
00654
00655
00656 void CCgiApplication::x_OnEvent(EEvent event, int status)
00657 {
00658 switch ( event ) {
00659 case eStartRequest:
00660 {
00661
00662 const CCgiRequest& req = m_Context->GetRequest();
00663
00664
00665 if ( !CDiagContext::IsSetOldPostFormat() ) {
00666 GetDiagContext().PrintRequestStart(req.GetCGIEntriesStr());
00667 m_RequestStartPrinted = true;
00668 }
00669
00670
00671 SetHTTPStatus(200);
00672
00673 const string& phid = CDiagContext::GetRequestContext().GetHitID();
00674
00675 const CCgiCookie* st = req.GetCookies().Find(
00676 g_GetNcbiString(eNcbiStrings_Stat));
00677 CCgiArgs pg_info;
00678 if ( st ) {
00679 pg_info.SetQueryString(st->GetValue());
00680 }
00681 pg_info.SetValue(g_GetNcbiString(eNcbiStrings_PHID), phid);
00682
00683 CDiagContext_Extra extra = GetDiagContext().Extra();
00684
00685 ITERATE(CCgiArgs::TArgs, it, pg_info.GetArgs()) {
00686 extra.Print(it->name, it->value);
00687 }
00688 extra.Flush();
00689 break;
00690 }
00691 case eSuccess:
00692 case eError:
00693 case eException:
00694 {
00695 CRequestContext& rctx = GetDiagContext().GetRequestContext();
00696 if ( m_InputStream.get() ) {
00697 if ( m_InputStream->eof() ) {
00698 m_InputStream->clear();
00699 }
00700 rctx.SetBytesRd(NcbiStreamposToInt8(m_InputStream->tellg()));
00701 }
00702 if ( m_OutputStream.get() ) {
00703 rctx.SetBytesWr(NcbiStreamposToInt8(m_OutputStream->tellp()));
00704 }
00705 break;
00706 }
00707 case eEndRequest:
00708 {
00709 if ( m_RequestStartPrinted &&
00710 !CDiagContext::IsSetOldPostFormat() ) {
00711
00712 GetDiagContext().PrintRequestStop();
00713 m_RequestStartPrinted = false;
00714 }
00715 break;
00716 }
00717 case eExit:
00718 case eExecutable:
00719 case eWatchFile:
00720 case eExitOnFail:
00721 case eExitRequest:
00722 case eWaiting:
00723 {
00724 break;
00725 }
00726 }
00727
00728 OnEvent(event, status);
00729 }
00730
00731
00732 void CCgiApplication::OnEvent(EEvent ,
00733 int )
00734 {
00735 return;
00736 }
00737
00738
00739 void CCgiApplication::RegisterDiagFactory(const string& key,
00740 CDiagFactory* fact)
00741 {
00742 m_DiagFactories[key] = fact;
00743 }
00744
00745
00746 CDiagFactory* CCgiApplication::FindDiagFactory(const string& key)
00747 {
00748 TDiagFactoryMap::const_iterator it = m_DiagFactories.find(key);
00749 if (it == m_DiagFactories.end())
00750 return 0;
00751 return it->second;
00752 }
00753
00754
00755 void CCgiApplication::ConfigureDiagnostics(CCgiContext& context)
00756 {
00757
00758 ConfigureDiagDestination(context);
00759 ConfigureDiagThreshold(context);
00760 ConfigureDiagFormat(context);
00761 }
00762
00763
00764 void CCgiApplication::ConfigureDiagDestination(CCgiContext& context)
00765 {
00766 const CCgiRequest& request = context.GetRequest();
00767
00768 bool is_set;
00769 string dest = request.GetEntry("diag-destination", &is_set);
00770 if ( !is_set )
00771 return;
00772
00773 SIZE_TYPE colon = dest.find(':');
00774 CDiagFactory* factory = FindDiagFactory(dest.substr(0, colon));
00775 if ( factory ) {
00776 SetDiagHandler(factory->New(dest.substr(colon + 1)));
00777 }
00778 }
00779
00780
00781 void CCgiApplication::ConfigureDiagThreshold(CCgiContext& context)
00782 {
00783 const CCgiRequest& request = context.GetRequest();
00784
00785 bool is_set;
00786 string threshold = request.GetEntry("diag-threshold", &is_set);
00787 if ( !is_set )
00788 return;
00789
00790 if (threshold == "fatal") {
00791 SetDiagPostLevel(eDiag_Fatal);
00792 } else if (threshold == "critical") {
00793 SetDiagPostLevel(eDiag_Critical);
00794 } else if (threshold == "error") {
00795 SetDiagPostLevel(eDiag_Error);
00796 } else if (threshold == "warning") {
00797 SetDiagPostLevel(eDiag_Warning);
00798 } else if (threshold == "info") {
00799 SetDiagPostLevel(eDiag_Info);
00800 } else if (threshold == "trace") {
00801 SetDiagPostLevel(eDiag_Info);
00802 SetDiagTrace(eDT_Enable);
00803 }
00804 }
00805
00806
00807 void CCgiApplication::ConfigureDiagFormat(CCgiContext& context)
00808 {
00809 const CCgiRequest& request = context.GetRequest();
00810
00811 typedef map<string, TDiagPostFlags> TFlagMap;
00812 static CSafeStaticPtr<TFlagMap> s_FlagMap;
00813 TFlagMap& flagmap = s_FlagMap.Get();
00814
00815 TDiagPostFlags defaults = (eDPF_Prefix | eDPF_Severity
00816 | eDPF_ErrCode | eDPF_ErrSubCode);
00817
00818 if ( !CDiagContext::IsSetOldPostFormat() ) {
00819 defaults |= (eDPF_UID | eDPF_PID | eDPF_RequestId |
00820 eDPF_SerialNo | eDPF_ErrorID);
00821 }
00822
00823 TDiagPostFlags new_flags = 0;
00824
00825 bool is_set;
00826 string format = request.GetEntry("diag-format", &is_set);
00827 if ( !is_set )
00828 return;
00829
00830 if (flagmap.empty()) {
00831 flagmap["file"] = eDPF_File;
00832 flagmap["path"] = eDPF_LongFilename;
00833 flagmap["line"] = eDPF_Line;
00834 flagmap["prefix"] = eDPF_Prefix;
00835 flagmap["severity"] = eDPF_Severity;
00836 flagmap["code"] = eDPF_ErrCode;
00837 flagmap["subcode"] = eDPF_ErrSubCode;
00838 flagmap["time"] = eDPF_DateTime;
00839 flagmap["omitinfosev"] = eDPF_OmitInfoSev;
00840 flagmap["all"] = eDPF_All;
00841 flagmap["trace"] = eDPF_Trace;
00842 flagmap["log"] = eDPF_Log;
00843 flagmap["errorid"] = eDPF_ErrorID;
00844 flagmap["location"] = eDPF_Location;
00845 flagmap["pid"] = eDPF_PID;
00846 flagmap["tid"] = eDPF_TID;
00847 flagmap["serial"] = eDPF_SerialNo;
00848 flagmap["serial_thr"] = eDPF_SerialNo_Thread;
00849 flagmap["iteration"] = eDPF_RequestId;
00850 flagmap["uid"] = eDPF_UID;
00851 }
00852 list<string> flags;
00853 NStr::Split(format, " ", flags);
00854 ITERATE(list<string>, flag, flags) {
00855 TFlagMap::const_iterator it;
00856 if ((it = flagmap.find(*flag)) != flagmap.end()) {
00857 new_flags |= it->second;
00858 } else if ((*flag)[0] == '!'
00859 && ((it = flagmap.find(flag->substr(1)))
00860 != flagmap.end())) {
00861 new_flags &= ~(it->second);
00862 } else if (*flag == "default") {
00863 new_flags |= defaults;
00864 }
00865 }
00866 SetDiagPostAllFlags(new_flags);
00867 }
00868
00869
00870 CCgiApplication::ELogOpt CCgiApplication::GetLogOpt() const
00871 {
00872 string log = GetConfig().Get("CGI", "Log");
00873
00874 CCgiApplication::ELogOpt logopt = eNoLog;
00875 if ((NStr::CompareNocase(log, "On") == 0) ||
00876 (NStr::CompareNocase(log, "true") == 0)) {
00877 logopt = eLog;
00878 } else if (NStr::CompareNocase(log, "OnError") == 0) {
00879 logopt = eLogOnError;
00880 }
00881 #ifdef _DEBUG
00882 else if (NStr::CompareNocase(log, "OnDebug") == 0) {
00883 logopt = eLog;
00884 }
00885 #endif
00886
00887 return logopt;
00888 }
00889
00890
00891 CCgiStatistics* CCgiApplication::CreateStat()
00892 {
00893 return new CCgiStatistics(*this);
00894 }
00895
00896 ICgiSessionStorage*
00897 CCgiApplication::GetSessionStorage(CCgiSessionParameters&) const
00898 {
00899 return 0;
00900 }
00901 bool CCgiApplication::IsCachingNeeded(const CCgiRequest& request) const
00902 {
00903 return true;
00904 }
00905
00906 ICache* CCgiApplication::GetCacheStorage() const
00907 {
00908 return NULL;
00909 }
00910
00911
00912 CCgiApplication::EPreparseArgs
00913 CCgiApplication::PreparseArgs(int argc,
00914 const char* const* argv)
00915 {
00916 static const char* s_ArgVersion = "-version";
00917 static const char* s_ArgFullVersion = "-version-full";
00918
00919 if (argc != 2 || !argv[1]) {
00920 return ePreparse_Continue;
00921 }
00922 if ( NStr::strcmp(argv[1], s_ArgVersion) == 0 ) {
00923
00924 cout << GetFullVersion().Print(GetProgramDisplayName(),
00925 CVersion::fVersionInfo | CVersion::fPackageShort);
00926 return ePreparse_Exit;
00927 }
00928 else if ( NStr::strcmp(argv[1], s_ArgFullVersion) == 0 ) {
00929
00930 cout << GetFullVersion().Print(GetProgramDisplayName());
00931 return ePreparse_Exit;
00932 }
00933 return ePreparse_Continue;
00934 }
00935
00936
00937 void CCgiApplication::SetRequestId(const string& rid, bool is_done)
00938 {
00939 m_RID = rid;
00940 m_IsResultReady = is_done;
00941 }
00942 bool CCgiApplication::GetResultFromCache(const CCgiRequest& request, CNcbiOstream& os)
00943 {
00944 string checksum, content;
00945 if (!request.CalcChecksum(checksum, content))
00946 return false;
00947
00948 try {
00949 CCacheHashedContent helper(*m_Cache);
00950 auto_ptr<IReader> reader( helper.GetHashedContent(checksum, content));
00951 if (reader.get()) {
00952
00953 CRStream cache_reader(reader.get());
00954 return NcbiStreamCopy(os, cache_reader);
00955 }
00956 } catch (exception& ex) {
00957 ERR_POST_X(5, "Couldn't read cached request : " << ex.what());
00958 }
00959 return false;
00960 }
00961 void CCgiApplication::SaveResultToCache(const CCgiRequest& request, CNcbiIstream& is)
00962 {
00963 string checksum, content;
00964 if ( !request.CalcChecksum(checksum, content) )
00965 return;
00966 try {
00967 CCacheHashedContent helper(*m_Cache);
00968 auto_ptr<IWriter> writer( helper.StoreHashedContent(checksum, content) );
00969 if (writer.get()) {
00970
00971 CWStream cache_writer(writer.get());
00972 NcbiStreamCopy(cache_writer, is);
00973 }
00974 } catch (exception& ex) {
00975 ERR_POST_X(6, "Couldn't cache request : " << ex.what());
00976 }
00977 }
00978
00979 void CCgiApplication::SaveRequest(const string& rid, const CCgiRequest& request)
00980 {
00981 if (rid.empty())
00982 return;
00983 try {
00984 auto_ptr<IWriter> writer( m_Cache->GetWriteStream(rid, 0, "NS_JID") );
00985 if (writer.get()) {
00986 CWStream cache_stream(writer.get());
00987 request.Serialize(cache_stream);
00988 }
00989 } catch (exception& ex) {
00990 ERR_POST_X(7, "Couldn't save request : " << ex.what());
00991 }
00992 }
00993 CCgiRequest* CCgiApplication::GetSavedRequest(const string& rid)
00994 {
00995 if (rid.empty())
00996 return NULL;
00997 try {
00998 auto_ptr<IReader> reader(m_Cache->GetReadStream(rid, 0, "NS_JID"));
00999 if (reader.get()) {
01000 CRStream cache_stream(reader.get());
01001 auto_ptr<CCgiRequest> request(new CCgiRequest);
01002 request->Deserialize(cache_stream, 0);
01003 return request.release();
01004 }
01005 } catch (exception& ex) {
01006 ERR_POST_X(8, "Couldn't read saved request : " << ex.what());
01007 }
01008 return NULL;
01009 }
01010
01011 void CCgiApplication::x_AddLBCookie()
01012 {
01013 const CNcbiRegistry& reg = GetConfig();
01014
01015 string cookie_name = GetConfig().Get("CGI-LB", "Name");
01016 if ( cookie_name.empty() )
01017 return;
01018
01019 int life_span = reg.GetInt("CGI-LB", "LifeSpan", 0, 0,
01020 CNcbiRegistry::eReturn);
01021
01022 string domain = reg.GetString("CGI-LB", "Domain", ".ncbi.nlm.nih.gov");
01023
01024 if ( domain.empty() ) {
01025 ERR_POST_X(9, "CGI-LB: 'Domain' not specified.");
01026 } else {
01027 if (domain[0] != '.') {
01028 domain.insert(0, ".");
01029 }
01030 }
01031
01032 string path = reg.Get("CGI-LB", "Path");
01033
01034 bool secure = reg.GetBool("CGI-LB", "Secure", false,
01035 0, CNcbiRegistry::eErrPost);
01036
01037 string host;
01038
01039
01040
01041
01042
01043 if ( m_HostIP ) {
01044 host = m_HostIP;
01045 }
01046 else {
01047 host = reg.Get("CGI-LB", "Host");
01048 if ( host.empty() ) {
01049 if ( m_Caf.get() ) {
01050 char host_ip[64] = {0,};
01051 m_Caf->GetHostIP(host_ip, sizeof(host_ip));
01052 m_HostIP = m_Caf->Encode(host_ip, 0);
01053 host = m_HostIP;
01054 }
01055 else {
01056 ERR_POST_X(10, "CGI-LB: 'Host' not specified.");
01057 }
01058 }
01059 }
01060
01061
01062 CCgiCookie cookie(cookie_name, host, domain, path);
01063 if (life_span > 0) {
01064 CTime exp_time(CTime::eCurrent, CTime::eGmt);
01065 exp_time.AddSecond(life_span);
01066 cookie.SetExpTime(exp_time);
01067 }
01068 cookie.SetSecure(secure);
01069
01070 GetContext().GetResponse().Cookies().Add(cookie);
01071 }
01072
01073
01074 void CCgiApplication::VerifyCgiContext(CCgiContext& context)
01075 {
01076 string x_moz = context.GetRequest().GetRandomProperty("X_MOZ");
01077 if ( NStr::EqualNocase(x_moz, "prefetch") ) {
01078 NCBI_EXCEPTION_VAR(ex, CCgiRequestException, eData,
01079 "Prefetch is not allowed for CGIs");
01080 ex.SetStatus(CCgiException::e403_Forbidden);
01081 ex.SetSeverity(eDiag_Info);
01082 NCBI_EXCEPTION_THROW(ex);
01083 }
01084 }
01085
01086
01087 void CCgiApplication::AppStart(void)
01088 {
01089
01090 if ( !CDiagContext::IsSetOldPostFormat() ) {
01091 GetDiagContext().PrintStart(kEmptyStr);
01092 }
01093 }
01094
01095
01096 void CCgiApplication::AppStop(int exit_code)
01097 {
01098 GetDiagContext().SetExitCode(exit_code);
01099 }
01100
01101
01102 const char* kToolkitRcPath = "/etc/toolkitrc";
01103 const char* kWebDirToPort = "Web_dir_to_port";
01104
01105 string CCgiApplication::GetDefaultLogPath(void) const
01106 {
01107 string log_path = "/log/";
01108
01109 string exe_path = GetProgramExecutablePath();
01110 CNcbiIfstream is(kToolkitRcPath, ios::binary);
01111 CNcbiRegistry reg(is);
01112 list<string> entries;
01113 reg.EnumerateEntries(kWebDirToPort, &entries);
01114 size_t min_pos = exe_path.length();
01115 string web_dir;
01116
01117 ITERATE(list<string>, it, entries) {
01118 if (!it->empty() && (*it)[0] != '/') {
01119
01120 string mask = "/" + *it;
01121 if (mask[mask.length() - 1] != '/') {
01122 mask += "/";
01123 }
01124 size_t pos = exe_path.find(mask);
01125 if (pos < min_pos) {
01126 min_pos = pos;
01127 web_dir = *it;
01128 }
01129 }
01130 else {
01131
01132 if (exe_path.substr(0, it->length()) == *it) {
01133 web_dir = *it;
01134 break;
01135 }
01136 }
01137 }
01138 if ( !web_dir.empty() ) {
01139 return log_path + reg.GetString(kWebDirToPort, web_dir, kEmptyStr);
01140 }
01141
01142 const char* port = ::getenv("SERVER_PORT");
01143 return port ? log_path + string(port) : log_path + "srv";
01144 }
01145
01146
01147 void CCgiApplication::SetHTTPStatus(int status)
01148 {
01149 CDiagContext::GetRequestContext().SetRequestStatus(status);
01150 }
01151
01152
01153
01154
01155
01156
01157
01158 CCgiStatistics::CCgiStatistics(CCgiApplication& cgi_app)
01159 : m_CgiApp(cgi_app), m_LogDelim(";")
01160 {
01161 }
01162
01163
01164 CCgiStatistics::~CCgiStatistics()
01165 {
01166 }
01167
01168
01169 void CCgiStatistics::Reset(const CTime& start_time,
01170 int result,
01171 const std::exception* ex)
01172 {
01173 m_StartTime = start_time;
01174 m_Result = result;
01175 m_ErrMsg = ex ? ex->what() : kEmptyStr;
01176 }
01177
01178
01179 string CCgiStatistics::Compose(void)
01180 {
01181 const CNcbiRegistry& reg = m_CgiApp.GetConfig();
01182 CTime end_time(CTime::eCurrent);
01183
01184
01185
01186 TSeconds time_cutoff = reg.GetInt("CGI", "TimeStatCutOff", 0, 0,
01187 CNcbiRegistry::eReturn);
01188 if (time_cutoff > 0) {
01189 TSeconds diff = end_time.DiffSecond(m_StartTime);
01190 if (diff < time_cutoff) {
01191 return kEmptyStr;
01192 }
01193 }
01194
01195 string msg, tmp_str;
01196
01197 tmp_str = Compose_ProgramName();
01198 if ( !tmp_str.empty() ) {
01199 msg.append(tmp_str);
01200 msg.append(m_LogDelim);
01201 }
01202
01203 tmp_str = Compose_Result();
01204 if ( !tmp_str.empty() ) {
01205 msg.append(tmp_str);
01206 msg.append(m_LogDelim);
01207 }
01208
01209 bool is_timing =
01210 reg.GetBool("CGI", "TimeStamp", false, 0, CNcbiRegistry::eErrPost);
01211 if ( is_timing ) {
01212 tmp_str = Compose_Timing(end_time);
01213 if ( !tmp_str.empty() ) {
01214 msg.append(tmp_str);
01215 msg.append(m_LogDelim);
01216 }
01217 }
01218
01219 tmp_str = Compose_Entries();
01220 if ( !tmp_str.empty() ) {
01221 msg.append(tmp_str);
01222 }
01223
01224 tmp_str = Compose_ErrMessage();
01225 if ( !tmp_str.empty() ) {
01226 msg.append(tmp_str);
01227 msg.append(m_LogDelim);
01228 }
01229
01230 return msg;
01231 }
01232
01233
01234 void CCgiStatistics::Submit(const string& message)
01235 {
01236 LOG_POST_X(11, message);
01237 }
01238
01239
01240 string CCgiStatistics::Compose_ProgramName(void)
01241 {
01242 return m_CgiApp.GetArguments().GetProgramName();
01243 }
01244
01245
01246 string CCgiStatistics::Compose_Timing(const CTime& end_time)
01247 {
01248 CTimeSpan elapsed = end_time.DiffTimeSpan(m_StartTime);
01249 return m_StartTime.AsString() + m_LogDelim + elapsed.AsString();
01250 }
01251
01252
01253 string CCgiStatistics::Compose_Entries(void)
01254 {
01255 const CCgiContext* ctx = m_CgiApp.m_Context.get();
01256 if ( !ctx )
01257 return kEmptyStr;
01258
01259 const CCgiRequest& cgi_req = ctx->GetRequest();
01260
01261
01262
01263
01264
01265
01266 const CNcbiRegistry& reg = m_CgiApp.GetConfig();
01267 string log_args = reg.Get("CGI", "LogArgs");
01268 if ( log_args.empty() )
01269 return kEmptyStr;
01270
01271 list<string> vars;
01272 NStr::Split(log_args, ",; \t", vars);
01273
01274 string msg;
01275 ITERATE (list<string>, i, vars) {
01276 bool is_entry_found;
01277 const string& arg = *i;
01278
01279 size_t pos = arg.find_last_of('=');
01280 if (pos == 0) {
01281 return "<misconf>" + m_LogDelim;
01282 } else if (pos != string::npos) {
01283 string key = arg.substr(0, pos);
01284 const CCgiEntry& entry = cgi_req.GetEntry(key, &is_entry_found);
01285 if ( is_entry_found ) {
01286 string alias = arg.substr(pos+1, arg.length());
01287 msg.append(alias);
01288 msg.append("='");
01289 msg.append(entry.GetValue());
01290 msg.append("'");
01291 msg.append(m_LogDelim);
01292 }
01293 } else {
01294 const CCgiEntry& entry = cgi_req.GetEntry(arg, &is_entry_found);
01295 if ( is_entry_found ) {
01296 msg.append(arg);
01297 msg.append("='");
01298 msg.append(entry.GetValue());
01299 msg.append("'");
01300 msg.append(m_LogDelim);
01301 }
01302 }
01303 }
01304
01305 return msg;
01306 }
01307
01308
01309 string CCgiStatistics::Compose_Result(void)
01310 {
01311 return NStr::IntToString(m_Result);
01312 }
01313
01314
01315 string CCgiStatistics::Compose_ErrMessage(void)
01316 {
01317 return m_ErrMsg;
01318 }
01319
01320
01321
01322
01323 NCBI_PARAM_DEF(bool, CGI, DisableTrackingCookie, false);
01324 NCBI_PARAM_DEF(string, CGI, TrackingCookieName, "ncbi_sid");
01325 NCBI_PARAM_DEF(string, CGI, TrackingCookieDomain, ".nih.gov");
01326 NCBI_PARAM_DEF(string, CGI, TrackingCookiePath, "/");
01327
01328
01329 END_NCBI_SCOPE
01330
01331