-2

So I have a working FastCGI application written in C++ using Light HTTPd, however I'm unable to retrieve the querystring using getenv("QUERY_STRING"). Everything works fine if I take out the querystring request (or add a check for null), but with that in place it fails:

#include <stdlib.h>
#ifdef _WIN32
#include <process.h>
#else
#include <unistd.h>
extern char ** environ;
#endif

#include "fcgio.h"
#include "fcgi_config.h"  // HAVE_IOSTREAM_WITHASSIGN_STREAMBUF
#include "redisclient.h"

....

 while (FCGX_Accept_r(&request) == 0)
 {        
    fcgi_streambuf cin_fcgi_streambuf(request.in);
    fcgi_streambuf cout_fcgi_streambuf(request.out);
    fcgi_streambuf cerr_fcgi_streambuf(request.err);

...

    cout << "Content-type: text/html\r\n"
                "\r\n"
                "<TITLE>^_^</TITLE>\n"
                "<H1>echo-cfpp</H1>\n"
                "<H4>PID: " << pid << "</H4>\n"
                "<H4>Request Number: " << ++count << "</H4>\n";

    // If I make this conditional on getenv("QUERY_STRING") not returning null,
    // then the program behaves reliably.
    cout <<getenv("QUERY_STRING");
 }

I've verified that I'm passing a querystring in the request, so why then is getenv("QUERY_STRING") returning null? And what should I be doing to retrieve it?

Shog9
  • 156,901
  • 35
  • 231
  • 235
user1202909
  • 59
  • 1
  • 7

1 Answers1

7

I don't have extensive experience with the reference FastCGI library for C/C++, but I've implemented both CGI and FastCGI libraries for Windows in the past, so the following might help.

Basically, as per the FastCGI specification, the CGI environment variables are passed through the FCGI_PARAMS stream, which are normally decoded by the FastCGI library. Now, FastCGI doesn't specify much about what is required and what isn't, and it's assumed that the rules are basically the same as for CGI. The CGI specification section 4.1.7 says the following about the QUERY_STRING environment variable:

The server MUST set this variable; if the Script-URI does not include a query component, the QUERY_STRING MUST be defined as an empty string ("").

Now, this basically means that your FastCGI library is decoding a QUERY_STRING parameter in the FCGI_PARAMS stream (or else the gateway server is not following the specification).

Since the reference library tries to abstract both CGI and FastCGI library in the same program and supports multi-threading, I strongly doubt that you would find the result in an environment variable (or there would be a race condition).

Basically, this means getenv() always returns NULL and you are passing a null const char* to an std::ostream through operator<< is illegal. This probably crashes your application since NULL is not a special value designating the end of stream.


TL; DR: You cannot access the QUERY_STRING value through the process environment because you are using FastCGI, not CGI. You need to read the library's documentation for the standard way to access the request's query string.


Edit: I've got some more info on this situation.

The documentation for FCGX_Accept_r() in fcgiapp.h says`:

Creates a parameters data structure to be accessed via getenv(3) (if assigned to environ) or by FCGX_GetParam and assigns it to *envp.

Using the following after FCGX_Accept_r would fix the issue:

environ = request.envp;

However, this is not safe for multi-threaded applications (environ is not in thread-local storage), so I recommend that you use the other documented method instead:

const char * query_string = FCGX_GetParam("QUERY_STRING", request.envp);
André Caron
  • 44,541
  • 12
  • 67
  • 125
  • this is actually the libraries documented way of accessing query string, sadly. http://www.fastcgi.com/devkit/doc/fastcgi-prog-guide/ch2c.htm I rtfm'd a lot before i came here. Also I found out about null thanks for affirming that. – user1202909 Feb 28 '12 at 21:21
  • @user1202909: Updated my answer. Your code does not satisfy the pre-conditions stated in the documentation. – André Caron Feb 28 '12 at 21:34
  • Actually, FWIW, libstdc++ checks for streaming of null `char const*`. It's still UB, of course – Lightness Races in Orbit Feb 28 '12 at 21:35
  • The documentation is so sparse i'm not even sure where you found that but thanks for the link and info. You should really have concentrated on this instead of asking me for irrelevant stuff. The information i gave out was obviously sufficient if you were able to find the answer. – user1202909 Feb 28 '12 at 21:47
  • 1
    @user1202909: I found it by reading the documentation on the first Google hit. And what I asked is not irrelevant. My answer was a **guess** because I didn't know if **your** program used `environ=request.envp;` or not. If you had show me your code, I would have found your answer faster and could have given you a **reliable, straightforward, 2-line answer**. – André Caron Feb 28 '12 at 21:52
  • 2
    @user1202909: You know... you asked a question, we tried to help and asked for more information, and all you've done is *fight back* (first by resisting, then by insulting). And how that I've solved your problem, *you're telling me I should have done something differently*. Honestly, I really don't get it. In any case, don't expect help from me in the future. – André Caron Feb 28 '12 at 22:42
  • You attacked what i wrote as being wrong, and yet were able to simply get the answer with out more information. I put everything you needed up. You were just being a dick, honestly. Kudos for answering it. I awarded you the points. This is my real complaint with stackoverflow.. if a question isn't something they can google, most people just complain not enough information. You had no problem finding the answer with what i gave you but you said not enough info. You were wrong. – user1202909 Feb 28 '12 at 23:36
  • @user1202909: you still don't get it. When people ask for clarifications, they're trying to help you faster and more accurately. There are a million things that can go wrong. You could have been using an old version of the library with up-to-date documentation, in which case your code could have been fine and simple "upgrade to the next version" would have been the right answer. My point is that without clarifications, I had to guess. Remember that my initial answer was wrong and you asked me to check again based on new information (working samples using getenv()). – André Caron Feb 29 '12 at 04:33
  • @user1202909: Note that your first comment to this answer is one of the things that led me to find the real answer. The tutorial you referenced uses `FCGI_Accept`, not `FCGX_Accept_r`. I then checked the documentation of both functions to look at the differences, and the answer happened to be in the documentation for `FCGX_Accept_r`. I probably would not have noticed this otherwise. I really don't understand your reluctance to help people help you. Answering a question can't always be done with a single round-trip and might require some discussion. – André Caron Feb 29 '12 at 04:38
  • @user1202909: And please, point me out anything I've said that was rude or impolite, or otherwise could be interpreted as "being a dick". I insited on helping you even when you didn't want to help yourself. How is that being a dick? – André Caron Feb 29 '12 at 04:42
  • @user1202909: The `const` in there means you can't modify the string contents, not that you can't change the pointer. Also, you can declare this variable inside the loop like `for(...){const char* query_string=...;}`, in which case, you'll get a new "constant" variable at each iteration. – André Caron Feb 29 '12 at 18:38