diff options
| author | Roland Reichwein <mail@reichwein.it> | 2020-05-02 18:12:37 +0200 | 
|---|---|---|
| committer | Roland Reichwein <mail@reichwein.it> | 2020-05-02 18:12:37 +0200 | 
| commit | 6ab2d11a72041e22eee42b0f582377b148e4c348 (patch) | |
| tree | 4fcffab342dfc5a60777ad45230fc22bb1c4ba4f /plugins/fcgi | |
| parent | 5dd4766490e4b29634ea0b3ff35e5d124f657f9c (diff) | |
Fixed FCGI
Diffstat (limited to 'plugins/fcgi')
| -rw-r--r-- | plugins/fcgi/fcgi.cpp | 211 | 
1 files changed, 143 insertions, 68 deletions
diff --git a/plugins/fcgi/fcgi.cpp b/plugins/fcgi/fcgi.cpp index eb9abe2..23fa91e 100644 --- a/plugins/fcgi/fcgi.cpp +++ b/plugins/fcgi/fcgi.cpp @@ -43,41 +43,6 @@ namespace {   const std::string gateway_interface{"CGI/1.1"}; - // Return a reasonable mime type based on the extension of a file. - std::string mime_type(fs::path path) - { -  using boost::algorithm::iequals; -  auto const ext = [&path] -  { -   size_t pos = path.string().rfind("."); -   if (pos == std::string::npos) -    return std::string{}; -   return path.string().substr(pos); -  }(); -  if(iequals(ext, ".htm"))  return "text/html"; // TODO: unordered_map -  if(iequals(ext, ".html")) return "text/html"; -  if(iequals(ext, ".php"))  return "text/html"; -  if(iequals(ext, ".css"))  return "text/css"; -  if(iequals(ext, ".txt"))  return "text/plain"; -  if(iequals(ext, ".js"))   return "application/javascript"; -  if(iequals(ext, ".json")) return "application/json"; -  if(iequals(ext, ".xml"))  return "application/xml"; -  if(iequals(ext, ".swf"))  return "application/x-shockwave-flash"; -  if(iequals(ext, ".flv"))  return "video/x-flv"; -  if(iequals(ext, ".png"))  return "image/png"; -  if(iequals(ext, ".jpe"))  return "image/jpeg"; -  if(iequals(ext, ".jpeg")) return "image/jpeg"; -  if(iequals(ext, ".jpg"))  return "image/jpeg"; -  if(iequals(ext, ".gif"))  return "image/gif"; -  if(iequals(ext, ".bmp"))  return "image/bmp"; -  if(iequals(ext, ".ico"))  return "image/vnd.microsoft.icon"; -  if(iequals(ext, ".tiff")) return "image/tiff"; -  if(iequals(ext, ".tif"))  return "image/tiff"; -  if(iequals(ext, ".svg"))  return "image/svg+xml"; -  if(iequals(ext, ".svgz")) return "image/svg+xml"; -  return "application/text"; - } -   typedef boost::coroutines2::coroutine<std::string> coro_t;   // returns true iff std::string is empty or contains newline @@ -180,14 +145,14 @@ namespace {    {     if (type == FCGI_BEGIN_REQUEST) {      size_t size {sizeof(FCGI_BeginRequestRecord)}; -    m_data.resize(size); +    m_data.resize(size, 0);      FCGI_BeginRequestRecord& r{*reinterpret_cast<FCGI_BeginRequestRecord*>(m_data.data())};      r.header.version = FCGI_VERSION_1;      r.header.type = type;      r.header.requestIdB1 = id >> 8;      r.header.requestIdB0 = id & 0xFF; -    r.header.contentLengthB1 = 0; -    r.header.contentLengthB0 = sizeof(r.body); +    r.header.contentLengthB1 = sizeof(r.body) >> 8; +    r.header.contentLengthB0 = sizeof(r.body) & 0xFF;      r.body.roleB1 = 0;      r.body.roleB0 = arg1;      r.body.flags = arg2; @@ -198,16 +163,16 @@ namespace {    // create record to send    FCGI_Record(unsigned char type, uint16_t id, const std::string& data)    { -   if (type == FCGI_PARAMS || type == FCGI_STDIN) { +   if (type == FCGI_PARAMS || type == FCGI_STDIN || type == FCGI_GET_VALUES || type == FCGI_DATA) {      size_t size {sizeof(FCGI_Header) + data.size()};      m_data.resize(size); -    FCGI_Header& r{*reinterpret_cast<FCGI_Header*>(m_data.data())}; -    r.version = FCGI_VERSION_1; -    r.type = type; -    r.requestIdB1 = id >> 8; -    r.requestIdB0 = id & 0xFF; -    r.contentLengthB1 = 0; -    r.contentLengthB0 = data.size(); +    FCGI_Header& h{*reinterpret_cast<FCGI_Header*>(m_data.data())}; +    h.version = FCGI_VERSION_1; +    h.type = type; +    h.requestIdB1 = id >> 8; +    h.requestIdB0 = id & 0xFF; +    h.contentLengthB1 = data.size() >> 8; +    h.contentLengthB0 = data.size() & 0xFF;      memcpy((void*)&m_data[sizeof(FCGI_Header)], (void*)data.data(), data.size());     } else      throw std::runtime_error("Bad FCGI type: "s + std::to_string(type)); @@ -222,11 +187,11 @@ namespace {     FCGI_Header& r{*reinterpret_cast<FCGI_Header*>(v.data())};     size_t content_length {((size_t)r.contentLengthB1) << 8 | r.contentLengthB0}; -   size_t record_size {sizeof(FCGI_Header) + content_length}; +   size_t record_size {sizeof(FCGI_Header) + content_length + r.paddingLength};     if (v.size() < record_size)      throw std::length_error("No full FCGI record available"); -   m_data = std::vector(v.begin(), v.begin() + record_size); +   m_data = std::vector(v.begin(), v.begin() + record_size - r.paddingLength);     v.erase(v.begin(), v.begin() + record_size);    } @@ -255,6 +220,7 @@ namespace {   std::string encode_u32(size_t v)   {    uint32_t x {static_cast<uint32_t>(v)}; +  x |= 0x80000000;    boost::endian::native_to_big_inplace(x);    return std::string(reinterpret_cast<char*>(&x), sizeof(x));   } @@ -279,6 +245,67 @@ namespace {    }   } + bool decode_size(const std::string& s, size_t& pos, size_t& size) + { +  uint8_t byte {static_cast<uint8_t>(s[pos])}; +  if (byte <= 127) { +   size = byte; +   pos += 1; + +  } else { +   if (s.size() < pos + 4) { +    std::cerr << "FCGI Error: Broken FCGI_GET_VALUES data (size)" << std::endl; +    return false; +   } + +   uint32_t v {*reinterpret_cast<const uint32_t*>(&s[pos])}; +   boost::endian::big_to_native_inplace(v); +   v &= ~0x80000000; +   size = static_cast<size_t>(v); + +   pos += 4; +  } + +  return true; + } + + void FCGI_DecodeEnv(const std::string& s, std::unordered_map<std::string, std::string>& map) + { +  map.clear(); + +  size_t pos{0}; + +  while (pos < s.size()) { +   size_t keysize{}; +   size_t valuesize{}; + +   if (!decode_size(s, pos, keysize)) +    return; +   if (!decode_size(s, pos, valuesize)) +    return; + +   if (s.size() < pos + keysize) { +     std::cerr << "FCGI Error: Broken FCGI_GET_VALUES data (key)" << std::endl; +     return; +   } +   std::string key{s.substr(pos, keysize)}; +   pos += keysize; +    +   if (s.size() < pos + valuesize) { +     std::cerr << "FCGI Error: Broken FCGI_GET_VALUES data (value) for key " << key << std::endl; +     return; +   } +   std::string value{s.substr(pos, valuesize)}; +   pos += valuesize; + +   if (map.find(key) != map.end()) { +    std::cerr << "FCGI Warning: Double key in FCGI_GET_VALUES: " << key << std::endl; +   } else { +    map[key] = value; +   } +  } + } +   // Used to return errors by generating response page and HTTP status code   std::string HttpStatus(std::string status, std::string message, std::function<plugin_interface_setter_type>& SetResponseHeader)   { @@ -287,6 +314,16 @@ namespace {    return status + " " + message;   } + void DumpAppValues(const std::unordered_map<std::string, std::string>& app_values) + { +  std::cout << "App properties:" << std::endl; +  if (app_values.size() == 0) +   std::cout << " (empty)" << std::endl; +  else for (auto&[key, value]: app_values) { +   std::cout << " " << key << "=" << value << std::endl; +  } + } +  } // anonymous namespace  std::string fcgi_plugin::fcgiQuery(FCGIContext& context) @@ -296,14 +333,21 @@ std::string fcgi_plugin::fcgiQuery(FCGIContext& context)   std::string output_data; + std::unordered_map<std::string, std::string> system_config{{"FCGI_MAX_CONNS", ""}, {"FCGI_MAX_REQS", ""}, {"FCGI_MPXS_CONNS", ""}}; + std::string system_config_bytes; + FCGI_EncodeEnv(system_config, system_config_bytes); +   std::unordered_map<std::string, std::string> env;   setFCGIEnvironment(env, context);   std::string env_bytes;   FCGI_EncodeEnv(env, env_bytes); + std::unordered_map<std::string, std::string> app_values; // will be read by FCGI_GET_VALUES +   size_t pos { app_addr.find(':') };   if (pos != app_addr.npos) { // host:port    boost::asio::io_context io_context; // TODO: member? +    tcp::resolver resolver(io_context);    auto endpoints{resolver.resolve(app_addr.substr(0, pos), app_addr.substr(pos + 1))};    tcp::socket socket(io_context); @@ -313,31 +357,60 @@ std::string fcgi_plugin::fcgiQuery(FCGIContext& context)     return HttpStatus("500", "FCGI connection", context.SetResponseHeader);    } +  FCGI_Record get_values{FCGI_GET_VALUES, 0, system_config_bytes}; +  if (socket.write_some(boost::asio::buffer(get_values.getBuffer())) != get_values.getBuffer().size()) +   std::cerr << "Warning: Not all bytes written 1" << std::endl; +    FCGI_ID_Guard id_guard(m_fcgi_id); -  uint16_t id{id_guard.getID()}; +  uint16_t id {id_guard.getID()}; -  FCGI_Record begin_request{FCGI_BEGIN_REQUEST, id, FCGI_RESPONDER, FCGI_KEEP_CONN}; -  socket.write_some(boost::asio::buffer(begin_request.getBuffer())); +  FCGI_Record begin_request{FCGI_BEGIN_REQUEST, id, FCGI_RESPONDER, 0/*FCGI_KEEP_CONN*/}; +  if (socket.write_some(boost::asio::buffer(begin_request.getBuffer())) != begin_request.getBuffer().size()) +   std::cerr << "Warning: Not all bytes written 3" << std::endl;    FCGI_Record params{FCGI_PARAMS, id, env_bytes}; -  socket.write_some(boost::asio::buffer(params.getBuffer())); +  if (socket.write_some(boost::asio::buffer(params.getBuffer())) != params.getBuffer().size()) +   std::cerr << "Warning: Not all bytes written 4" << std::endl; + +  if (env_bytes.size()) {  +   FCGI_Record params_end{FCGI_PARAMS, id, std::string{}}; +   if (socket.write_some(boost::asio::buffer(params_end.getBuffer())) != params_end.getBuffer().size()) +    std::cerr << "Warning: Not all bytes written 5" << std::endl; +  } + +  std::string body {context.GetRequestParam("body")}; +  FCGI_Record stdin_{FCGI_STDIN, id, body}; +  if (socket.write_some(boost::asio::buffer(stdin_.getBuffer())) != stdin_.getBuffer().size()) +   std::cerr << "Warning: Not all bytes written 6" << std::endl; -  FCGI_Record params_end{FCGI_PARAMS, id, std::string{}}; -  socket.write_some(boost::asio::buffer(params_end.getBuffer())); +  if (body.size()) { +   FCGI_Record stdin_end{FCGI_STDIN, id, std::string{}}; +   if (socket.write_some(boost::asio::buffer(stdin_end.getBuffer())) != stdin_end.getBuffer().size()) +    std::cerr << "Warning: Not all bytes written 7" << std::endl; +  } -  FCGI_Record stdin_{FCGI_PARAMS, id, context.GetRequestParam("body")}; -  socket.write_some(boost::asio::buffer(stdin_.getBuffer())); -   -  FCGI_Record stdin_end{FCGI_PARAMS, id, std::string{}}; -  socket.write_some(boost::asio::buffer(stdin_end.getBuffer())); +#if 0 +  FCGI_Record data{FCGI_DATA, id, std::string{}}; +  if (socket.write_some(boost::asio::buffer(data.getBuffer())) != data.getBuffer().size()) +   std::cerr << "Warning: Not all bytes written 8" << std::endl; +#endif    bool ended{false};    std::vector<char> inbuf; +  std::vector<char> inbuf_part(1024);    while (!ended) { -   std::vector<char> inbuf_part(1024); -   size_t got {socket.read_some(boost::asio::buffer(inbuf_part))}; -   inbuf.insert(inbuf.end(), inbuf_part.begin(), inbuf_part.begin() + got); - +   try { +    size_t got {socket.read_some(boost::asio::buffer(inbuf_part))}; +    inbuf.insert(inbuf.end(), inbuf_part.begin(), inbuf_part.begin() + got); +   } catch (const boost::system::system_error& ex) { +    if (ex.code() == boost::asio::error::eof) { +     //std::cerr << "FCGI Warning: Early EOF" << std::endl; // seems to be ok here +    } else { +     std::cerr << "FCGI Warning: Expected EOF, got " << ex.code() << ", " << ex.what() << std::endl; +     ended = true; +    } +   } +        try {      FCGI_Record r{inbuf};      if (r.getType() == FCGI_END_REQUEST) { @@ -345,7 +418,10 @@ std::string fcgi_plugin::fcgiQuery(FCGIContext& context)      } else if (r.getType() == FCGI_STDOUT) {       output_data += r.getContent();      } else if (r.getType() == FCGI_STDERR) { -     std::cerr << "FCGI Error: " << r.getContent(); +     std::cerr << "FCGI STDERR: " << r.getContent(); +    } else if (r.getType() == FCGI_GET_VALUES_RESULT) { +     FCGI_DecodeEnv(r.getContent(), app_values); +     DumpAppValues(app_values);      } else       throw std::runtime_error("Unhandled FCGI type: "s + std::to_string(r.getType()));     } catch (const std::length_error& ex) { @@ -386,9 +462,10 @@ std::string fcgi_plugin::fcgiQuery(FCGIContext& context)                                 throw std::runtime_error("Input missing on processing CGI body");   }); - while (std::getline(is_out, line)) { + do { +  std::getline(is_out, line);    processLine(line); - } + } while (!is_out.eof());   return output;  } @@ -432,8 +509,6 @@ std::string fcgi_plugin::generate_page(    try {     return fcgiQuery(context); -  } catch (const std::runtime_error& ex) { -   return HttpStatus("404", "Not found: "s + GetRequestParam("target"), SetResponseHeader);    } catch (const std::exception& ex) {     return HttpStatus("500", "Internal Server Error: "s + ex.what(), SetResponseHeader);    }  | 
