rpc_view.cpp 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. // Licensed to the Apache Software Foundation (ASF) under one
  2. // or more contributor license agreements. See the NOTICE file
  3. // distributed with this work for additional information
  4. // regarding copyright ownership. The ASF licenses this file
  5. // to you under the Apache License, Version 2.0 (the
  6. // "License"); you may not use this file except in compliance
  7. // with the License. You may obtain a copy of the License at
  8. //
  9. // http://www.apache.org/licenses/LICENSE-2.0
  10. //
  11. // Unless required by applicable law or agreed to in writing,
  12. // software distributed under the License is distributed on an
  13. // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  14. // KIND, either express or implied. See the License for the
  15. // specific language governing permissions and limitations
  16. // under the License.
  17. #include <gflags/gflags.h>
  18. #include <butil/logging.h>
  19. #include <brpc/server.h>
  20. #include <brpc/channel.h>
  21. #include "view.pb.h"
  22. DEFINE_int32(port, 8888, "TCP Port of this server");
  23. DEFINE_string(target, "", "The server to view");
  24. DEFINE_int32(timeout_ms, 5000, "Timeout for calling the server to view");
  25. // handle HTTP response of accessing builtin services of the target server.
  26. static void handle_response(brpc::Controller* client_cntl,
  27. std::string target,
  28. brpc::Controller* server_cntl,
  29. google::protobuf::Closure* server_done) {
  30. // Copy all headers. The "Content-Length" will be overwriteen.
  31. server_cntl->http_response() = client_cntl->http_response();
  32. // Copy content.
  33. server_cntl->response_attachment() = client_cntl->response_attachment();
  34. // Insert "rpc_view: <target>" before </body> so that users are always
  35. // visually notified with target server w/o confusions.
  36. butil::IOBuf& content = server_cntl->response_attachment();
  37. butil::IOBuf before_body;
  38. if (content.cut_until(&before_body, "</body>") == 0) {
  39. before_body.append(
  40. "<style type=\"text/css\">\n"
  41. ".rpcviewlogo {position: fixed; bottom: 0px; right: 0px;"
  42. " color: #ffffff; background-color: #000000; }\n"
  43. " </style>\n"
  44. "<span class='rpcviewlogo'>&nbsp;rpc_view: ");
  45. before_body.append(target);
  46. before_body.append("&nbsp;</span></body>");
  47. before_body.append(content);
  48. content = before_body;
  49. }
  50. // Notice that we don't set RPC to failed on http errors because we
  51. // want to pass unchanged content to the users otherwise RPC replaces
  52. // the content with ErrorText.
  53. if (client_cntl->Failed() &&
  54. client_cntl->ErrorCode() != brpc::EHTTP) {
  55. server_cntl->SetFailed(client_cntl->ErrorCode(),
  56. "%s", client_cntl->ErrorText().c_str());
  57. }
  58. delete client_cntl;
  59. server_done->Run();
  60. }
  61. // A http_master_service.
  62. class ViewServiceImpl : public ViewService {
  63. public:
  64. ViewServiceImpl() {}
  65. virtual ~ViewServiceImpl() {};
  66. virtual void default_method(google::protobuf::RpcController* cntl_base,
  67. const HttpRequest*,
  68. HttpResponse*,
  69. google::protobuf::Closure* done) {
  70. brpc::ClosureGuard done_guard(done);
  71. brpc::Controller* server_cntl =
  72. static_cast<brpc::Controller*>(cntl_base);
  73. // Get or set target. Notice that we don't access FLAGS_target directly
  74. // which is thread-unsafe (for string flags).
  75. std::string target;
  76. const std::string* newtarget =
  77. server_cntl->http_request().uri().GetQuery("changetarget");
  78. if (newtarget) {
  79. if (GFLAGS_NS::SetCommandLineOption("target", newtarget->c_str()).empty()) {
  80. server_cntl->SetFailed("Fail to change value of -target");
  81. return;
  82. }
  83. target = *newtarget;
  84. } else {
  85. if (!GFLAGS_NS::GetCommandLineOption("target", &target)) {
  86. server_cntl->SetFailed("Fail to get value of -target");
  87. return;
  88. }
  89. }
  90. // Create the http channel on-the-fly. Notice that we've set
  91. // `defer_close_second' in main() so that dtor of channels do not
  92. // close connections immediately and ad-hoc creation of channels
  93. // often reuses the not-yet-closed connections.
  94. brpc::Channel http_chan;
  95. brpc::ChannelOptions http_chan_opt;
  96. http_chan_opt.protocol = brpc::PROTOCOL_HTTP;
  97. if (http_chan.Init(target.c_str(), &http_chan_opt) != 0) {
  98. server_cntl->SetFailed(brpc::EINTERNAL,
  99. "Fail to connect to %s", target.c_str());
  100. return;
  101. }
  102. // Remove "Accept-Encoding". We need to insert additional texts
  103. // before </body>, preventing the server from compressing the content
  104. // simplifies our code. The additional bandwidth consumption shouldn't
  105. // be an issue for infrequent checking out of builtin services pages.
  106. server_cntl->http_request().RemoveHeader("Accept-Encoding");
  107. brpc::Controller* client_cntl = new brpc::Controller;
  108. client_cntl->http_request() = server_cntl->http_request();
  109. // Remove "Host" so that RPC will laterly serialize the (correct)
  110. // target server in.
  111. client_cntl->http_request().RemoveHeader("host");
  112. // Setup the URI.
  113. const brpc::URI& server_uri = server_cntl->http_request().uri();
  114. std::string uri = server_uri.path();
  115. if (!server_uri.query().empty()) {
  116. uri.push_back('?');
  117. uri.append(server_uri.query());
  118. }
  119. if (!server_uri.fragment().empty()) {
  120. uri.push_back('#');
  121. uri.append(server_uri.fragment());
  122. }
  123. client_cntl->http_request().uri() = uri;
  124. // /hotspots pages may take a long time to finish, since they all have
  125. // query "seconds", we set the timeout to be longer than "seconds".
  126. const std::string* seconds =
  127. server_cntl->http_request().uri().GetQuery("seconds");
  128. int64_t timeout_ms = FLAGS_timeout_ms;
  129. if (seconds) {
  130. timeout_ms += atoll(seconds->c_str()) * 1000;
  131. }
  132. client_cntl->set_timeout_ms(timeout_ms);
  133. // Keep content as it is.
  134. client_cntl->request_attachment() = server_cntl->request_attachment();
  135. http_chan.CallMethod(NULL, client_cntl, NULL, NULL,
  136. brpc::NewCallback(
  137. handle_response, client_cntl, target,
  138. server_cntl, done_guard.release()));
  139. }
  140. };
  141. int main(int argc, char* argv[]) {
  142. GFLAGS_NS::ParseCommandLineFlags(&argc, &argv, true);
  143. if (FLAGS_target.empty() &&
  144. (argc != 2 ||
  145. GFLAGS_NS::SetCommandLineOption("target", argv[1]).empty())) {
  146. LOG(ERROR) << "Usage: ./rpc_view <ip>:<port>";
  147. return -1;
  148. }
  149. // This keeps ad-hoc creation of channels reuse previous connections.
  150. GFLAGS_NS::SetCommandLineOption("defer_close_seconds", "10");
  151. brpc::Server server;
  152. server.set_version("rpc_view_server");
  153. brpc::ServerOptions server_opt;
  154. server_opt.http_master_service = new ViewServiceImpl;
  155. if (server.Start(FLAGS_port, &server_opt) != 0) {
  156. LOG(ERROR) << "Fail to start ViewServer";
  157. return -1;
  158. }
  159. server.RunUntilAskedToQuit();
  160. return 0;
  161. }