PageRenderTime 22ms CodeModel.GetById 0ms RepoModel.GetById 0ms app.codeStats 0ms

/mordor/http/servlets/config.cpp

http://github.com/mozy/mordor
C++ | 147 lines | 134 code | 10 blank | 3 comment | 32 complexity | 3c9139f991cabf8b120eb4d217c2cd49 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. // Copyright (c) 2010 - Mozy, Inc.
  2. #include "mordor/http/servlets/config.h"
  3. #include "mordor/config.h"
  4. #include "mordor/http/server.h"
  5. #include "mordor/json.h"
  6. #include "mordor/streams/buffered.h"
  7. #include "mordor/streams/limited.h"
  8. #include "mordor/streams/memory.h"
  9. #include "mordor/streams/transfer.h"
  10. namespace Mordor {
  11. namespace HTTP {
  12. namespace Servlets {
  13. static void eachConfigVarHTMLWrite(ConfigVarBase::ptr var, Stream::ptr stream)
  14. {
  15. std::string name = var->name();
  16. stream->write("<tr><td align=\"right\">", 22);
  17. stream->write(name.c_str(), name.size());
  18. stream->write("=</td><td><form name=\"", 22);
  19. stream->write(name.c_str(), name.size());
  20. stream->write("\" method=\"post\"><input type=\"text\" name=\"", 41);
  21. stream->write(name.c_str(), name.size());
  22. stream->write("\" value=\"", 9);
  23. std::string value = var->toString();
  24. stream->write(value.c_str(), value.size());
  25. stream->write("\" /><input type=\"submit\" value=\"Change\" /></form></td></tr>\n", 60);
  26. }
  27. static void eachConfigVarHTML(ConfigVarBase::ptr var, Stream::ptr stream)
  28. {
  29. std::string name = var->name();
  30. stream->write("<tr><td align=\"right\">", 22);
  31. stream->write(name.c_str(), name.size());
  32. stream->write("=</td><td>", 10);
  33. std::string value = var->toString();
  34. stream->write(value.c_str(), value.size());
  35. stream->write("</td></tr>\n", 11);
  36. }
  37. static void eachConfigVarJSON(ConfigVarBase::ptr var, JSON::Object &object)
  38. {
  39. object.insert(std::make_pair(var->name(), var->toString()));
  40. }
  41. namespace {
  42. enum Format {
  43. HTML,
  44. JSON
  45. };
  46. }
  47. void Config::request(ServerRequest::ptr request, Access access)
  48. {
  49. const std::string &method = request->request().requestLine.method;
  50. if (method == POST) {
  51. if (access != READWRITE) {
  52. respondError(request, FORBIDDEN);
  53. return;
  54. }
  55. if (request->request().entity.contentType.type != "application" ||
  56. request->request().entity.contentType.subtype != "x-www-form-urlencoded") {
  57. respondError(request, UNSUPPORTED_MEDIA_TYPE);
  58. return;
  59. }
  60. Stream::ptr requestStream = request->requestStream();
  61. requestStream.reset(new LimitedStream(requestStream, 65536));
  62. MemoryStream requestBody;
  63. transferStream(requestStream, requestBody);
  64. std::string queryString;
  65. queryString.resize(requestBody.buffer().readAvailable());
  66. requestBody.buffer().copyOut(&queryString[0], requestBody.buffer().readAvailable());
  67. bool failed = false;
  68. URI::QueryString qs(queryString);
  69. for (URI::QueryString::const_iterator it = qs.begin();
  70. it != qs.end();
  71. ++it) {
  72. ConfigVarBase::ptr var = Mordor::Config::lookup(it->first);
  73. if (var && !var->fromString(it->second))
  74. failed = true;
  75. }
  76. if (failed) {
  77. respondError(request, HTTP::FORBIDDEN,
  78. "One or more new values were not accepted");
  79. return;
  80. }
  81. // Fall through
  82. }
  83. if (method == GET || method == HEAD || method == POST) {
  84. Format format = HTML;
  85. URI::QueryString qs;
  86. if (request->request().requestLine.uri.queryDefined())
  87. qs = request->request().requestLine.uri.queryString();
  88. URI::QueryString::const_iterator it = qs.find("alt");
  89. if (it != qs.end() && it->second == "json")
  90. format = JSON;
  91. // TODO: use Accept to indicate JSON
  92. switch (format) {
  93. case HTML:
  94. {
  95. request->response().status.status = OK;
  96. request->response().entity.contentType = MediaType("text", "html");
  97. if (method == HEAD) {
  98. if (request->request().requestLine.ver == Version(1, 1) &&
  99. isAcceptable(request->request().request.te, "chunked", true)) {
  100. request->response().general.transferEncoding.push_back("chunked");
  101. }
  102. return;
  103. }
  104. Stream::ptr response = request->responseStream();
  105. response.reset(new BufferedStream(response));
  106. response->write("<html><body><table>\n", 20);
  107. Mordor::Config::visit(boost::bind(access == READWRITE ?
  108. &eachConfigVarHTMLWrite : &eachConfigVarHTML, _1,
  109. response));
  110. response->write("</table></body></html>", 22);
  111. response->close();
  112. break;
  113. }
  114. case JSON:
  115. {
  116. JSON::Object root;
  117. Mordor::Config::visit(boost::bind(&eachConfigVarJSON, _1, boost::ref(root)));
  118. std::ostringstream os;
  119. os << root;
  120. std::string str = os.str();
  121. request->response().status.status = OK;
  122. request->response().entity.contentType = MediaType("application", "json");
  123. request->response().entity.contentLength = str.size();
  124. if (method != HEAD) {
  125. request->responseStream()->write(str.c_str(), str.size());
  126. request->responseStream()->close();
  127. }
  128. break;
  129. }
  130. default:
  131. MORDOR_NOTREACHED();
  132. }
  133. } else {
  134. respondError(request, METHOD_NOT_ALLOWED);
  135. }
  136. }
  137. }}}