PageRenderTime 46ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 0ms

/src/ulib/net/server/plugin/mod_scgi.cpp

http://github.com/stefanocasazza/ULib
C++ | 235 lines | 135 code | 63 blank | 37 comment | 25 complexity | 836770d2e0dcc1cbcd67ccb7afa93bad MD5 | raw file
Possible License(s): GPL-2.0, LGPL-3.0, AGPL-1.0, Apache-2.0
  1. // ============================================================================
  2. //
  3. // = LIBRARY
  4. // ULib - c++ library
  5. //
  6. // = FILENAME
  7. // mod_scgi.cpp - Perform simple scgi request forwarding
  8. //
  9. // = AUTHOR
  10. // Stefano Casazza
  11. //
  12. // ============================================================================
  13. #include <ulib/file_config.h>
  14. #include <ulib/utility/uhttp.h>
  15. #include <ulib/net/tcpsocket.h>
  16. #include <ulib/utility/services.h>
  17. #include <ulib/net/client/client.h>
  18. #include <ulib/net/server/server.h>
  19. #include <ulib/net/server/plugin/mod_scgi.h>
  20. #ifndef _MSWINDOWS_
  21. # include <ulib/net/unixsocket.h>
  22. #endif
  23. U_CREAT_FUNC(server_plugin_scgi, USCGIPlugIn)
  24. bool USCGIPlugIn::scgi_keep_conn;
  25. UClient_Base* USCGIPlugIn::connection;
  26. USCGIPlugIn::USCGIPlugIn()
  27. {
  28. U_TRACE_CTOR(0, USCGIPlugIn, "")
  29. }
  30. USCGIPlugIn::~USCGIPlugIn()
  31. {
  32. U_TRACE_DTOR(0, USCGIPlugIn)
  33. if (connection) U_DELETE(connection)
  34. }
  35. // Server-wide hooks
  36. int USCGIPlugIn::handlerConfig(UFileConfig& cfg)
  37. {
  38. U_TRACE(0, "USCGIPlugIn::handlerConfig(%p)", &cfg)
  39. // ------------------------------------------------------------------------------------------
  40. // SCGI_URI_MASK mask (DOS regexp) of uri type that send request to SCGI (*.php)
  41. //
  42. // NAME_SOCKET file name for the scgi socket
  43. //
  44. // SERVER host name or ip address for the scgi host
  45. // PORT port number for the scgi host
  46. //
  47. // RES_TIMEOUT timeout for response from server SCGI
  48. // SCGI_KEEP_CONN If not zero, the server SCGI does not close the connection after
  49. // responding to request; the plugin retains responsibility for the connection.
  50. //
  51. // LOG_FILE location for file log (use server log if exist)
  52. // ------------------------------------------------------------------------------------------
  53. UClient_Base::pcfg = &cfg;
  54. U_NEW(UClient_Base, connection, UClient_Base(&cfg));
  55. UString x = cfg.at(U_CONSTANT_TO_PARAM("SCGI_URI_MASK"));
  56. U_INTERNAL_ASSERT_EQUALS(UHTTP::scgi_uri_mask, U_NULLPTR)
  57. if (x) U_NEW_STRING(UHTTP::scgi_uri_mask, UString(x))
  58. scgi_keep_conn = cfg.readBoolean(U_CONSTANT_TO_PARAM("SCGI_KEEP_CONN"));
  59. U_RETURN(U_PLUGIN_HANDLER_PROCESSED);
  60. }
  61. int USCGIPlugIn::handlerInit()
  62. {
  63. U_TRACE_NO_PARAM(1, "USCGIPlugIn::handlerInit()")
  64. if (connection &&
  65. UHTTP::scgi_uri_mask)
  66. {
  67. # ifdef _MSWINDOWS_
  68. U_INTERNAL_ASSERT_DIFFERS(connection->port, 0)
  69. U_NEW(UTCPSocket, connection->socket, UTCPSocket(connection->bIPv6));
  70. # else
  71. if (connection->port) U_NEW(UTCPSocket, connection->socket, UTCPSocket(connection->bIPv6))
  72. else U_NEW(UUnixSocket, connection->socket, UUnixSocket)
  73. # endif
  74. if (connection->connect())
  75. {
  76. U_SRV_LOG("connection to the scgi-backend %V accepted", connection->host_port.rep);
  77. # ifndef U_ALIAS
  78. U_ERROR("Sorry, I can't run scgi plugin because alias URI support is missing, please recompile ULib");
  79. # else
  80. // NB: SCGI is NOT a static page...
  81. if (UHTTP::valias == U_NULLPTR) U_NEW(UVector<UString>, UHTTP::valias, UVector<UString>(2U))
  82. UHTTP::valias->push_back(*UHTTP::scgi_uri_mask);
  83. UHTTP::valias->push_back(*UString::str_nostat);
  84. U_RETURN(U_PLUGIN_HANDLER_OK);
  85. # endif
  86. }
  87. U_DELETE(connection)
  88. connection = U_NULLPTR;
  89. }
  90. U_RETURN(U_PLUGIN_HANDLER_ERROR);
  91. }
  92. // Connection-wide hooks
  93. int USCGIPlugIn::handlerRequest()
  94. {
  95. U_TRACE_NO_PARAM(0, "USCGIPlugIn::handlerRequest()")
  96. if (connection &&
  97. UHTTP::isSCGIRequest())
  98. {
  99. // Set environment for the SCGI application server
  100. char* equalPtr;
  101. char* envp[128];
  102. UString environment(U_CAPACITY);
  103. if (UHTTP::getCGIEnvironment(environment, U_WSCGI) == false) U_RETURN(U_PLUGIN_HANDLER_ERROR);
  104. int n = u_split(U_STRING_TO_PARAM(environment), envp, U_NULLPTR);
  105. U_INTERNAL_ASSERT_MINOR(n, 128)
  106. # ifdef DEBUG
  107. uint32_t hlength = 0; // calculate the total length of the headers
  108. # endif
  109. for (int i = 0; i < n; ++i)
  110. {
  111. equalPtr = strchr(envp[i], '=');
  112. if (equalPtr)
  113. {
  114. U_INTERNAL_ASSERT_MAJOR(equalPtr-envp[i], 0)
  115. U_INTERNAL_ASSERT_MAJOR(u__strlen(equalPtr+1, __PRETTY_FUNCTION__), 0)
  116. # ifdef DEBUG
  117. hlength += (equalPtr - envp[i]) + u__strlen(equalPtr, __PRETTY_FUNCTION__) + 1;
  118. # endif
  119. *equalPtr = '\0';
  120. }
  121. }
  122. n = environment.size();
  123. U_INTERNAL_ASSERT_EQUALS((int)hlength, n)
  124. // send header data as netstring -> [len]":"[string]","
  125. UString request(10U + n);
  126. request.snprintf(U_CONSTANT_TO_PARAM("%u:%v,"), environment.size(), environment.rep);
  127. (void) request.append(*UHTTP::body);
  128. connection->prepareRequest(request);
  129. if (connection->sendRequestAndReadResponse() == false)
  130. {
  131. UHTTP::setInternalError();
  132. goto end;
  133. }
  134. if (scgi_keep_conn == false)
  135. {
  136. /**
  137. * The shutdown() tells the receiver the server is done sending data. No
  138. * more data is going to be send. More importantly, it doesn't close the
  139. * socket. At the socket layer, this sends a TCP/IP FIN packet to the receiver
  140. */
  141. if (connection->shutdown(SHUT_WR) == false)
  142. {
  143. UHTTP::setInternalError();
  144. goto end;
  145. }
  146. }
  147. *UClientImage_Base::wbuffer = connection->getResponse();
  148. if (UHTTP::processCGIOutput(false, false) == false) UHTTP::setInternalError();
  149. end: connection->clearData();
  150. if (scgi_keep_conn == false &&
  151. connection->isConnected())
  152. {
  153. connection->close();
  154. }
  155. U_RETURN(U_PLUGIN_HANDLER_PROCESSED);
  156. }
  157. U_RETURN(U_PLUGIN_HANDLER_OK);
  158. }
  159. // DEBUG
  160. #if defined(U_STDCPP_ENABLE) && defined(DEBUG)
  161. const char* USCGIPlugIn::dump(bool reset) const
  162. {
  163. *UObjectIO::os << "scgi_keep_conn " << scgi_keep_conn << '\n'
  164. << "connection (UClient_Base " << (void*)connection << ')';
  165. if (reset)
  166. {
  167. UObjectIO::output();
  168. return UObjectIO::buffer_output;
  169. }
  170. return U_NULLPTR;
  171. }
  172. #endif