1 /*  $Id: cgitest.cpp 617534 2020-10-02 15:05:55Z vakatov $
2  * ===========================================================================
3  *
4  *                            PUBLIC DOMAIN NOTICE
5  *               National Center for Biotechnology Information
6  *
7  *  This software/database is a "United States Government Work" under the
8  *  terms of the United States Copyright Act.  It was written as part of
9  *  the author's official duties as a United States Government employee and
10  *  thus cannot be copyrighted.  This software/database is freely available
11  *  to the public for use. The National Library of Medicine and the U.S.
12  *  Government have not placed any restriction on its use or reproduction.
13  *
14  *  Although all reasonable efforts have been taken to ensure the accuracy
15  *  and reliability of the software and data, the NLM and the U.S.
16  *  Government do not and cannot warrant the performance or results that
17  *  may be obtained by using this software or data. The NLM and the U.S.
18  *  Government disclaim all warranties, express or implied, including
19  *  warranties of performance, merchantability or fitness for any particular
20  *  purpose.
21  *
22  *  Please cite the author in any work or product based on this material.
23  *
24  * ===========================================================================
25  *
26  * Author:  Denis Vakatov, Eugene Vasilchenko, Vsevolod Sandomirsky
27  *
28  * File Description:
29  *   TEST for:  NCBI C++ core CGI API
30  *
31  */
32 
33 #include <ncbi_pch.hpp>
34 #include <corelib/ncbiapp.hpp>
35 #include <corelib/ncbienv.hpp>
36 #include <corelib/ncbireg.hpp>
37 #include <corelib/ncbisys.hpp>
38 #include <cgi/ncbires.hpp>
39 #include <cgi/ncbicgir.hpp>
40 #include <cgi/cgi_util.hpp>
41 #include <cgi/cgi_exception.hpp>
42 
43 #include <algorithm>
44 #include <time.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 
48 #include <common/test_assert.h>  /* This header must go last */
49 
50 
51 USING_NCBI_SCOPE;
52 
53 
54 /////////////////////////////////
55 // CGI
56 //
57 
TestCgi_Cookies(void)58 static void TestCgi_Cookies(void)
59 {
60     CCgiCookies cookies("coo1=kie1BAD1;coo2=kie2_ValidPath; ");
61     cookies.Add("  coo1=kie1BAD2;CooT=KieT_ExpTime  ");
62 
63     string str =
64         "eee; BAD COOKIE;  Coo11=Kie11_OK; Coo3=Kie2 SEMI-BAD;"
65         "B COO= 7; X C =3; uuu; coo1=Kie1_OK; Coo6=kie6; iii";
66     cookies.Add(str);
67     cookies.Add("RemoveThisCookie", "BAD");
68     cookies.Add(str);
69 
70     assert(  cookies.Find("eee") );
71     assert( !cookies.Find("BAD COOKIE") );
72     assert(  cookies.Find("Coo11") );
73     assert(  cookies.Find("Coo2") );
74     assert( cookies.Find("Coo3") );
75     assert( !cookies.Find("B COO") );
76     assert( !cookies.Find("X C") );
77     assert( !cookies.Find("X C ") );
78     assert(  cookies.Find("uuu") );
79     assert(  cookies.Find("coo1") );
80     assert(  cookies.Find("CooT") );
81     assert(  cookies.Find("Coo6") );
82     assert( !cookies.Find("Coo2", "qq.rr.oo", NcbiEmptyString) );
83     assert( !cookies.Find("Coo2", "qq.rr.oo", NcbiEmptyString) );
84     assert(cookies.Find("Coo2") == cookies.Find("Coo2", "", ""));
85     cookies.Find("Coo2")->SetValue("Kie2_OK");
86 
87     CCgiCookie c0("Coo5", "Kie5BAD");
88     CCgiCookie c1("Coo5", "Kie", "aaa.bbb.ccc", "/");
89     CCgiCookie c2(c1);
90     c2.SetValue("Kie5_Dom_Sec");
91     c2.SetPath("");
92     c2.SetSecure(true);
93 
94     cookies.Add(c1);
95     cookies.Add(c2);
96 
97     CCgiCookie* c3 = cookies.Find("coo2", NcbiEmptyString, "");
98     c3->SetPath("coo2_ValidPath");
99 
100     assert( !cookies.Remove(cookies.Find("NoSuchCookie")) );
101     assert( cookies.Remove(cookies.Find("RemoveThisCookie")) );
102     assert( !cookies.Remove(cookies.Find("RemoveThisCookie")) );
103     assert( !cookies.Find("RemoveThisCookie") );
104 
105     assert( cookies.Find("CoO5") );
106     assert( cookies.Find("cOo5") == cookies.Find("Coo5", "aaa.bBb.ccC", "") );
107     assert( cookies.Find("Coo5")->GetDomain() == "aaa.bbb.ccc" );
108     assert( cookies.Find("coo2")->GetDomain().empty() );
109     assert( cookies.Find("cOO5")->GetSecure() );
110     assert( !cookies.Find("cOo2")->GetSecure() );
111 
112     time_t timer = time(0);
113     tm *date = gmtime(&timer);
114     CCgiCookie *ct = cookies.Find("CooT");
115     ct->SetExpDate(*date);
116 
117     cookies.Add("AAA", "11", "qq.yy.dd");
118     cookies.Add("AAA", "12", "QQ.yy.Dd");
119     assert( cookies.Find("AAA", "qq.yy.dd", NcbiEmptyString)->GetValue()
120             == "12" );
121 
122     cookies.Add("aaa", "1", "QQ.yy.Dd");
123     assert( cookies.Find("AAA", "qq.yy.dd", NcbiEmptyString)->GetValue()
124             == "1" );
125 
126     cookies.Add("aaa", "19");
127 
128     cookies.Add("aaa", "21", "QQ.yy.Dd", "path");
129     assert( cookies.Find("AAA", "qq.yy.dd", "path")->GetValue() == "21");
130 
131     cookies.Add("aaa", "22", "QQ.yy.Dd", "Path");
132     assert( cookies.Find("AAA", "qq.yy.dd", "path")->GetValue() == "21" );
133     assert( cookies.Find("AAA")->GetValue() == "19" );
134 
135     cookies.Add("AAA", "2", "QQ.yy.Dd", "path");
136     assert( cookies.Find("AAA", "qq.yy.dd", "path")->GetValue() == "2" );
137 
138     cookies.Add("AAA", "3");
139 
140     cookies.Add("BBA", "BBA1");
141     cookies.Add("BBA", "BBA2", "", "path");
142 
143     cookies.Add("BBB", "B1", "oo.pp.yy");
144     cookies.Add("BBB", "B2");
145     cookies.Add("BBB", "B3", "bb.pp.yy", "path3");
146     cookies.Add("BBB", "B3", "cc.pp.yy", "path3");
147 
148     CCgiCookies::TRange range;
149     assert( cookies.Find("BBA", &range)->GetValue() == "BBA1" );
150     assert( cookies.Remove(range) );
151 
152     cookies.Add("BBB", "B3", "dd.pp.yy", "path3");
153     cookies.Add("BBB", "B3", "aa.pp.yy", "path3");
154     cookies.Add("BBB", "B3", "",         "path3");
155 
156     cookies.Add("BBC", "BBC1", "", "path");
157     cookies.Add("BBC", "BBC2", "44.88.99", "path");
158 
159     cookies.Add("BBD", "BBD1",  "", "path");
160     cookies.Add("BBD", "BBD20", "44.88.99", "path");
161     cookies.Add("BBD", "BBD2",  "44.88.99", "path");
162     cookies.Add("BBD", "BBD3",  "77.99.00", "path");
163 
164     assert( cookies.Remove( cookies.Find("BBB", "dd.pp.yy", "path3") ) );
165 
166     cookies.Add("DDD", "DDD1", "aa.bb.cc", "p1/p2");
167     cookies.Add("DDD", "DDD2", "aa.bb.cc");
168     cookies.Add("DDD", "DDD3", "aa.bb.cc", "p3/p4");
169     cookies.Add("DDD", "DDD4", "aa.bb.cc", "p1");
170     cookies.Add("DDD", "DDD5", "aa.bb.cc", "p1/p2/p3");
171     cookies.Add("DDD", "DDD6", "aa.bb.cc", "p1/p4");
172     assert( cookies.Find("DDD", &range)->GetValue() == "DDD2" );
173 
174     assert( cookies.Find("BBD", "44.88.99", "path")->GetValue() == "BBD2" );
175     assert( cookies.Remove(cookies.Find("BBD", "77.99.00", "path")) );
176     assert( cookies.Remove("BBD") == 2 );
177 
178     string enc_name ="name%20%21%22%23";
179     string enc_val = "val%25%26%27";
180     cookies.Add(enc_name + "=" + enc_val);
181     assert( cookies.Find(NStr::URLDecode(enc_name))->GetValue() ==
182         NStr::URLDecode(enc_val));
183     enc_name ="name with bad chars;";
184     enc_val = "value with bad chars;";
185     cookies.Add(enc_name, enc_val);
186     assert( cookies.Find(NStr::URLDecode(enc_name))->GetValue() ==
187         NStr::URLDecode(enc_val));
188 
189     NcbiCerr << "\n\nCookies:\n\n" << cookies << NcbiEndl;
190 
191     cookies.Add("bad name=good_value; good_name=bad value; bad both=bad both",
192         CCgiCookies::eOnBadCookie_StoreAndError);
193     CCgiCookies::TCRange rg = cookies.GetAll();
194     for (CCgiCookies::TCIter it = rg.first; it != rg.second; it++) {
195         if ( !(*it)->IsInvalid() ) {
196             _ASSERT(cookies.Find((*it)->GetName()));
197         }
198     }
199 }
200 
201 
PrintEntries(TCgiEntries & entries)202 static void PrintEntries(TCgiEntries& entries)
203 {
204     for (TCgiEntries::iterator iter = entries.begin();
205          iter != entries.end();  ++iter) {
206         // assert( !NStr::StartsWith(iter->first, "amp;", NStr::eNocase) );
207         NcbiCout << "  (\"" << iter->first << "\", \""
208                  << iter->second << "\")" << NcbiEndl;
209     }
210 }
211 
TestEntries(TCgiEntries & entries,const string & str)212 static bool TestEntries(TCgiEntries& entries, const string& str)
213 {
214     NcbiCout << "\n Entries: `" << str << "'\n";
215     SIZE_TYPE err_pos = CCgiRequest::ParseEntries(str, entries);
216     PrintEntries(entries);
217 
218     if ( err_pos ) {
219         NcbiCout << "-- Error at position #" << err_pos << NcbiEndl;
220         return false;
221     }
222     return true;
223 }
224 
PrintIndexes(TCgiIndexes & indexes)225 static void PrintIndexes(TCgiIndexes& indexes)
226 {
227     for (TCgiIndexes::iterator iter = indexes.begin();
228          iter != indexes.end();  ++iter) {
229         NcbiCout << "  \"" << *iter << "\"    ";
230     }
231     NcbiCout << NcbiEndl;
232 }
233 
TestIndexes(TCgiIndexes & indexes,const string & str)234 static bool TestIndexes(TCgiIndexes& indexes, const string& str)
235 {
236     NcbiCout << "\n Indexes: `" << str << "'\n";
237     SIZE_TYPE err_pos = CCgiRequest::ParseIndexes(str, indexes);
238     PrintIndexes(indexes);
239 
240     if ( err_pos ) {
241         NcbiCout << "-- Error at position #" << err_pos << NcbiEndl;
242         return false;
243     }
244     return true;
245 }
246 
TestCgi_Request_Static(void)247 static void TestCgi_Request_Static(void)
248 {
249     // Test CCgiRequest::ParseEntries()
250     TCgiEntries entries;
251     assert(  TestEntries(entries, "aa=bb&cc=dd") );
252     assert(  TestEntries(entries, "e%20e=f%26f&g%2Ag=h+h%2e") );
253     entries.clear();
254     assert( !TestEntries(entries, " xx=yy") );
255     assert(  TestEntries(entries, "xx=&yy=zz") );
256     assert(  TestEntries(entries, "rr=") );
257     entries.clear();
258 
259     assert(  TestEntries(entries, "&&") );
260     assert(  TestEntries(entries, "aa") );
261     assert(  TestEntries(entries, "bb&") );
262     assert(  TestEntries(entries, "&cc") );
263     assert(  TestEntries(entries, "&dd&") );
264     assert(  TestEntries(entries, "ee&&") );
265     assert(  TestEntries(entries, "&&ff") );
266     assert(  TestEntries(entries, "&&gg&&") );
267     entries.clear();
268 
269     assert(  TestEntries(entries, "aa=") );
270     assert(  TestEntries(entries, "bb=&") );
271     assert(  TestEntries(entries, "&cc=") );
272     assert(  TestEntries(entries, "dd=&&") );
273     assert(  TestEntries(entries, "&&ee=") );
274     entries.clear();
275 
276     assert(  TestEntries(entries, "&aa=uu") );
277     assert(  TestEntries(entries, "bb=vv&") );
278     assert(  TestEntries(entries, "&cc=ww&") );
279     assert(  TestEntries(entries, "&&dd=xx") );
280     assert(  TestEntries(entries, "ee=yy&&") );
281     assert(  TestEntries(entries, "&&ff=zz&&") );
282     entries.clear();
283 
284     assert(  TestEntries(entries, "aa=vv&&bb=ww") );
285     assert(  TestEntries(entries, "cc=&&dd=xx") );
286     assert(  TestEntries(entries, "ee=yy&&ff") );
287     assert(  TestEntries(entries, "gg&&hh=zz") );
288     entries.clear();
289 
290     // assert( !TestEntries(entries, "tt=qq=pp") );
291     assert(  TestEntries(entries, "=ggg&ppp=PPP") );
292     assert(  TestEntries(entries, "a=d&eee") );
293     assert(  TestEntries(entries, "xxx&eee") );
294     assert(  TestEntries(entries, "xxx+eee") );
295     assert(  TestEntries(entries, "UUU") );
296     assert(  TestEntries(entries, "a%21%2f%25aa=%2Fd%2c&eee=%3f") );
297     entries.clear();
298 
299     // some older browsers fail to parse &amp; in HREFs; ensure that
300     // we handle it properly.
301     assert(  TestEntries(entries, "a=b&amp;c=d&amp;e=f") );
302     assert(  TestEntries(entries, "&amp;c=d&amp;e=f") );
303     assert(  TestEntries(entries, "&amp;&amp;c=d&amp;&amp;e=f&amp;&amp;") );
304     assert(  TestEntries(entries, "amp;c=d&amp;amp;e=f") );
305     entries.clear();
306 
307     // Test CCgiRequest::ParseIndexes()
308     TCgiIndexes indexes;
309     assert(  TestIndexes(indexes, "a+bb+ccc+d") );
310     assert(  TestIndexes(indexes, "e%20e+f%26f+g%2Ag+hh%2e") );
311     indexes.clear();
312     assert( !TestIndexes(indexes, " jhg") );
313     // assert( !TestIndexes(indexes, "e%h%2e+3") );
314     assert(  TestIndexes(indexes, "aa+%20+bb") );
315     assert(  TestIndexes(indexes, "aa++bb") );
316     indexes.clear();
317     assert(  TestIndexes(indexes, "+1") );
318     assert(  TestIndexes(indexes, "aa+") );
319     assert( !TestIndexes(indexes, "aa+bb  ") );
320     assert(  TestIndexes(indexes, "c++b") );
321     assert( !TestIndexes(indexes, "ff++ ") );
322     assert(  TestIndexes(indexes, "++") );
323 }
324 
TestCgi_Request_Full(CNcbiIstream * istr,const CNcbiArguments * args=0,CCgiRequest::TFlags flags=0)325 static void TestCgi_Request_Full(CNcbiIstream*         istr,
326                                  const CNcbiArguments* args = 0,
327                                  CCgiRequest::TFlags   flags = 0)
328 {
329     CCgiRequest CCR(args, 0, istr, flags);
330 
331     NcbiCout << "\n\nCCgiRequest::\n";
332 
333     try {
334         NcbiCout << "GetContentLength(): "
335                  << CCR.GetContentLength() << NcbiEndl;
336     }
337     STD_CATCH ("TestCgi_Request_Full");
338 
339     NcbiCout << "GetRandomProperty(\"USER_AGENT\"): "
340              << CCR.GetRandomProperty("USER_AGENT") << NcbiEndl;
341     NcbiCout << "GetRandomProperty(\"MY_RANDOM_PROP\"): "
342              << CCR.GetRandomProperty("MY_RANDOM_PROP") << NcbiEndl;
343 
344     NcbiCout << "GetRandomProperty(\"HTTP_MY_RANDOM_PROP\"): "
345              << CCR.GetRandomProperty("HTTP_MY_RANDOM_PROP")
346              << NcbiEndl;
347     NcbiCout << "GetRandomProperty(\"HTTP_MY_RANDOM_PROP\", false): "
348              << CCR.GetRandomProperty("HTTP_MY_RANDOM_PROP", false)
349              << NcbiEndl;
350 
351     NcbiCout << "\nCCgiRequest::  All properties:\n";
352     for (size_t prop = 0;  prop < (size_t)eCgi_NProperties;  prop++) {
353         NcbiCout << NcbiSetw(24)
354                  << CCgiRequest::GetPropertyName((ECgiProp)prop) << " = \""
355                  << CCR.GetProperty((ECgiProp)prop) << "\"\n";
356     }
357 
358     CCgiCookies cookies;
359     {{  // Just an example of copying the cookies from a request data
360         // Of course, we could use the original request's cookie set
361         // ("x_cookies") if we performed only "const" operations on it
362         const CCgiCookies& x_cookies = CCR.GetCookies();
363         cookies.Add(x_cookies);
364     }}
365     NcbiCout << "\nCCgiRequest::  All cookies:\n";
366     if ( cookies.Empty() )
367         NcbiCout << "No cookies specified" << NcbiEndl;
368     else
369         NcbiCout << cookies << NcbiEndl;
370 
371     TCgiEntries entries = CCR.GetEntries();
372     NcbiCout << "\nCCgiRequest::  All entries:\n";
373     if ( entries.empty() ) {
374         NcbiCout << "No entries specified" << NcbiEndl;
375     } else {
376         PrintEntries(entries);
377 
378         if ( !CCR.GetEntry("get_query1").empty() ) {
379             NcbiCout << "GetEntry() check." << NcbiEndl;
380 
381             assert(CCR.GetEntry("get_query1") == "gq1");
382             bool is_found = false;
383             assert(CCR.GetEntry("get_query1", &is_found) == "gq1");
384             assert(is_found);
385 
386             assert(CCR.GetEntry("get_query2", 0).empty());
387             is_found = false;
388             assert(CCR.GetEntry("get_query2", &is_found).empty());
389             assert(is_found);
390 
391             assert(CCR.GetEntry("qwe1rtyuioop", &is_found).empty());
392             assert(!is_found);
393         }
394     }
395 
396     TCgiIndexes indexes = CCR.GetIndexes();
397     NcbiCout << "\nCCgiRequest::  ISINDEX values:\n";
398     if ( indexes.empty() ) {
399         NcbiCout << "No ISINDEX values specified" << NcbiEndl;
400     } else {
401         PrintIndexes(indexes);
402     }
403 
404     CNcbiIstream* is = CCR.GetInputStream();
405     if ( is ) {
406         NcbiCout << "\nUn-parsed content body:\n";
407         NcbiCout << is->rdbuf() << NcbiEndl << NcbiEndl;
408     }
409 }
410 
TestCgiMisc(void)411 static void TestCgiMisc(void)
412 {
413     const string str("_ _%_;_\n_:_'_*_\\_\"_");
414     {{
415         string url = "qwerty";
416         url = NStr::URLEncode(str);
417         NcbiCout << str << NcbiEndl << url << NcbiEndl;
418         assert( url.compare("_+_%25_%3B_%0A_%3A_'_*_%5C_%22_") == 0 );
419 
420         string str1 = NStr::URLDecode(url);
421         assert( str1 == str );
422 
423         string url1 = NStr::URLEncode(str1);
424         assert( url1 == url );
425     }}
426     {{
427         string url = "qwerty";
428         url = NStr::URLEncode(str, NStr::eUrlEnc_ProcessMarkChars);
429         NcbiCout << str << NcbiEndl << url << NcbiEndl;
430         assert( url.compare("%5F+%5F%25%5F%3B%5F%0A%5F%3A%5F%27%5F%2A%5F%5C%5F%22%5F") == 0 );
431 
432         string str1 = NStr::URLDecode(url);
433         assert( str1 == str );
434 
435         string url1 = NStr::URLEncode(str1, NStr::eUrlEnc_ProcessMarkChars);
436         assert( url1 == url );
437     }}
438 
439     const string bad_url("%ax");
440     try {
441         NStr::URLDecode(bad_url);
442     } STD_CATCH("%ax");
443 }
444 
445 
TestCgi(const CNcbiArguments & args)446 static void TestCgi(const CNcbiArguments& args)
447 {
448     // this is to get rid of warnings on some strict compilers (like SUN Forte)
449     #define X_PUTENV(s)  NcbiSysChar_putenv((char*) s)
450 
451     TestCgi_Cookies();
452     TestCgi_Request_Static();
453 
454     assert( !X_PUTENV("CONTENT_TYPE=application/x-www-form-urlencoded") );
455 
456     try { // POST only
457         char inp_str[] = "post11=val11&post12void=&post13=val13";
458         CNcbiIstrstream istr(inp_str);
459         char len[64];
460         assert(::sprintf(len, "CONTENT_LENGTH=%ld", (long) ::strlen(inp_str)));
461         assert( !NcbiSysChar_putenv(len) );
462 
463         assert( !X_PUTENV("SERVER_PORT=") );
464         assert( !X_PUTENV("REMOTE_ADDRESS=") );
465         assert( !X_PUTENV("REQUEST_METHOD=POST") );
466         assert( !X_PUTENV("QUERY_STRING=") );
467         assert( !X_PUTENV("HTTP_COOKIE=") );
468         TestCgi_Request_Full(&istr);
469     } STD_CATCH("TestCgi(POST only)");
470 
471     try { // POST, fDoNotParseContent
472         char inp_str[] = "post11=val11&post12void=&post13=val13";
473         CNcbiIstrstream istr(inp_str);
474         char len[64];
475         assert(::sprintf(len, "CONTENT_LENGTH=%ld", (long) ::strlen(inp_str)));
476         assert( !NcbiSysChar_putenv(len) );
477 
478         assert( !X_PUTENV("SERVER_PORT=") );
479         assert( !X_PUTENV("REMOTE_ADDRESS=") );
480         assert( !X_PUTENV("REQUEST_METHOD=POST") );
481         assert( !X_PUTENV("QUERY_STRING=") );
482         assert( !X_PUTENV("HTTP_COOKIE=") );
483         TestCgi_Request_Full(&istr, 0, CCgiRequest::fDoNotParseContent);
484     } STD_CATCH("TestCgi(POST only)");
485 
486     try { // POST + aux. functions
487         char inp_str[] = "post22void=&post23void=";
488         CNcbiIstrstream istr(inp_str);
489         char len[64];
490         assert(::sprintf(len, "CONTENT_LENGTH=%ld", (long) ::strlen(inp_str)));
491         assert( !NcbiSysChar_putenv(len) );
492 
493         assert( !X_PUTENV("SERVER_PORT=9999") );
494         assert( !X_PUTENV("HTTP_USER_AGENT=MyUserAgent") );
495         assert( !X_PUTENV("HTTP_MY_RANDOM_PROP=MyRandomPropValue") );
496         assert( !X_PUTENV("REMOTE_ADDRESS=130.14.25.129") );
497         TestCgi_Request_Full(&istr);
498     } STD_CATCH("TestCgi(POST + aux. functions)");
499 
500     // this is for all following tests...
501     char inp_str[] = "postXXX=valXXX";
502     char len[64];
503     assert( ::sprintf(len, "CONTENT_LENGTH=%ld", (long) ::strlen(inp_str)) );
504     assert( !NcbiSysChar_putenv(len) );
505 
506     try { // POST + ISINDEX(action)
507         CNcbiIstrstream istr(inp_str);
508         assert( !X_PUTENV("QUERY_STRING=isidx1+isidx2+isidx3") );
509         TestCgi_Request_Full(&istr);
510     } STD_CATCH("TestCgi(POST + ISINDEX(action))");
511 
512     try { // POST + QUERY(action)
513         CNcbiIstrstream istr(inp_str);
514         assert( !X_PUTENV("QUERY_STRING=query1=vv1&query2=") );
515         TestCgi_Request_Full(&istr);
516     } STD_CATCH("TestCgi(POST + QUERY(action))");
517 
518     try { // GET ISINDEX + COOKIES
519         CNcbiIstrstream istr(inp_str);
520         assert( !X_PUTENV("QUERY_STRING=get_isidx1+get_isidx2+get_isidx3") );
521         assert( !X_PUTENV("HTTP_COOKIE=cook1=val1; cook2=val2;") );
522         TestCgi_Request_Full(&istr, 0, CCgiRequest::fIndexesNotEntries);
523     } STD_CATCH("TestCgi(GET ISINDEX + COOKIES)");
524 
525     try { // GET REGULAR, NO '='
526         CNcbiIstrstream istr(inp_str);
527         assert( !X_PUTENV("QUERY_STRING=get_query1_empty&get_query2_empty") );
528         TestCgi_Request_Full(&istr);
529     } STD_CATCH("TestCgi(GET REGULAR, NO '=' )");
530 
531     try { // GET REGULAR + COOKIES
532         CNcbiIstrstream istr(inp_str);
533         assert( !X_PUTENV("QUERY_STRING=get_query1=gq1&get_query2=") );
534         assert( !X_PUTENV("HTTP_COOKIE=_cook1=_val1;_cook2=_val2") );
535         TestCgi_Request_Full(&istr);
536     } STD_CATCH("TestCgi(GET REGULAR + COOKIES)");
537 
538     try { // ERRONEOUS STDIN
539         CNcbiIstrstream istr("123");
540         assert( !X_PUTENV("QUERY_STRING=get_query1=gq1&get_query2=") );
541         assert( !X_PUTENV("HTTP_COOKIE=_cook1=_val1;_cook2=_val2") );
542         TestCgi_Request_Full(&istr);
543     } STD_CATCH("TestCgi(ERRONEOUS STDIN)");
544 
545     try { // USER INPUT(real STDIN)
546         assert( !X_PUTENV("QUERY_STRING=u_query1=uq1") );
547         assert( !X_PUTENV("HTTP_COOKIE=u_cook1=u_val1; u_cook2=u_val2") );
548         assert( !X_PUTENV("REQUEST_METHOD=POST") );
549         NcbiCout << "Enter the length of CGI posted data now: " << NcbiFlush;
550         long l = 0;
551         if (!(NcbiCin >> l)  ||  l < 0) {
552             NcbiCin.clear();
553             throw runtime_error("Invalid length of CGI posted data");
554         }
555         char cs[64];
556         assert( ::sprintf(cs, "CONTENT_LENGTH=%ld", (long) l) );
557         assert( !X_PUTENV(cs) );
558         NcbiCout << "Enter the CGI posted data now(no spaces): " << NcbiFlush;
559         NcbiCin >> NcbiWs;
560         TestCgi_Request_Full(0);
561         NcbiCin.clear();
562     } STD_CATCH("TestCgi(USER STDIN)");
563 
564     try { // CMD.-LINE ARGS
565         assert( !X_PUTENV("REQUEST_METHOD=") );
566         assert( !X_PUTENV("QUERY_STRING=MUST NOT BE USED HERE!!!") );
567         TestCgi_Request_Full(&NcbiCin/* dummy */, &args);
568     } STD_CATCH("TestCgi(CMD.-LINE ARGS)");
569 
570     TestCgiMisc();
571 }
572 
573 
TestCgiResponse(const CNcbiArguments & args)574 static void TestCgiResponse(const CNcbiArguments& args)
575 {
576     NcbiCout << "Starting CCgiResponse test" << NcbiEndl;
577 
578     CCgiResponse response1;
579     // Optional (since it's the default)
580     response1.SetOutput(&NcbiCout);
581 
582     if (args.Size() > 2) {
583         CCgiCookies cookies(args[2]);
584         response1.Cookies().Add(cookies);
585     }
586     response1.Cookies().Remove(response1.Cookies().Find("to-Remove"));
587     NcbiCout << "Cookies: " << response1.Cookies() << NcbiEndl;
588 
589     CCgiResponse response2;
590 
591     NcbiCout << "Generated simple HTTP response:" << NcbiEndl;
592     response2.WriteHeader() << "Data1" << NcbiEndl << NcbiFlush;
593     response2.out() << "Data2" << NcbiEndl << NcbiFlush;
594     NcbiCout << "End of simple HTTP response" << NcbiEndl << NcbiEndl;
595 
596     CCgiResponse response3;
597 
598     response3.SetHeaderValue("Some-Header-Name", "Some Header Value");
599     response3.SetHeaderValue("status", "399 Something is BAAAAD!!!!!");
600     response3.SetStatus(301, "Moved");
601 
602     NcbiCout << "Generated HTTP response:" << NcbiEndl;
603     response3.WriteHeader() << "Data1" << NcbiEndl << NcbiFlush;
604     response3.out() << "Data2" << NcbiEndl << NcbiFlush;
605     NcbiCout << "End of HTTP response" << NcbiEndl << NcbiEndl;
606 
607     CCgiResponse response4;
608 
609     response4.SetRawCgi(true);
610     NcbiCout << "Generated HTTP \"raw CGI\" response:" << NcbiEndl;
611     response4.WriteHeader() << "Data1" << NcbiEndl << NcbiFlush;
612     response4.out() << "Data2" << NcbiEndl << NcbiFlush;
613     NcbiCout << "End of HTTP \"raw CGI\" response" << NcbiEndl << NcbiEndl;
614 
615     try {
616         NcbiCout << "Checking double headed HTTP response:" << NcbiEndl;
617         response4.WriteHeader() << "Data1" << NcbiEndl << NcbiFlush;
618     } catch (CCgiResponseException& e) {
619         _ASSERT(e.GetErrCode() == CCgiResponseException::eDoubleHeader);
620         NCBI_REPORT_EXCEPTION("Caught", e);
621     }
622     NcbiCout << "Checking double headed HTTP response into a different stream:"
623              << NcbiEndl;
624     response4.WriteHeader(NcbiCerr);
625 }
626 
627 
628 class CTestApplication : public CNcbiApplication
629 {
630 public:
631     virtual ~CTestApplication(void);
632     virtual int Run(void);
633 };
634 
635 
Run(void)636 int CTestApplication::Run(void)
637 {
638     TestCgi( GetArguments() );
639     TestCgiResponse( GetArguments() );
640     return 0;
641 }
642 
643 
~CTestApplication()644 CTestApplication::~CTestApplication()
645 {
646     SetDiagStream(0);
647 }
648 
649 
main(int argc,char ** argv)650 int main(int argc, char** argv)
651 {
652     return CTestApplication().AppMain(argc, argv);
653 }
654