server.cpp 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. #include "server.h"
  2. #include <library/cpp/json/json_writer.h>
  3. #include <util/generic/map.h>
  4. #include <util/datetime/base.h>
  5. #include <util/datetime/cputimer.h>
  6. #include <util/system/hostname.h>
  7. #include <util/stream/buffer.h>
  8. #include <util/generic/bt_exception.h>
  9. using namespace NYql;
  10. using namespace NHttp;
  11. namespace {
  12. TBlob ToErrorsJson(const TStringBuf& message)
  13. {
  14. TBufferOutput output;
  15. NJson::TJsonWriter writer(&output, false);
  16. writer.OpenMap();
  17. writer.Write(TStringBuf("errors"));
  18. {
  19. writer.OpenArray();
  20. writer.Write(message);
  21. writer.CloseArray();
  22. }
  23. writer.CloseMap();
  24. writer.Flush();
  25. return TBlob::FromBuffer(output.Buffer());
  26. }
  27. ///////////////////////////////////////////////////////////////////////////////
  28. // THttpReplier
  29. ///////////////////////////////////////////////////////////////////////////////
  30. class THttpReplier: public THttpClientRequestEx
  31. {
  32. public:
  33. THttpReplier(const TServer* server)
  34. : Server_(server)
  35. {
  36. }
  37. private:
  38. TString GetRemoteAddr() const {
  39. NAddr::TOpaqueAddr remoteAddr;
  40. int rc = getpeername(Socket(), remoteAddr.MutableAddr(), remoteAddr.LenPtr());
  41. Y_ENSURE(rc == 0, "Can't get remote address");
  42. return PrintHost(remoteAddr);
  43. }
  44. bool Reply(void*) final {
  45. TSimpleTimer timer;
  46. if (!ProcessHeaders()) {
  47. return true;
  48. }
  49. RD.Scan();
  50. TResponse resp;
  51. const IServlet* servlet = Server_->FindServlet(RD.ScriptName());
  52. if (servlet != nullptr) {
  53. try {
  54. TRequest req{Input(), RD, Buf};
  55. TStringBuf method = TStringBuf(Input().FirstLine()).Before(' ');
  56. if (method == TStringBuf("GET")) {
  57. servlet->DoGet(req, resp);
  58. } else if (method == TStringBuf("POST")) {
  59. servlet->DoPost(req, resp);
  60. } else {
  61. resp.Code = HTTP_METHOD_NOT_ALLOWED;
  62. }
  63. } catch (const THttpError& e) {
  64. resp.Code = e.GetCode();
  65. resp.ContentType = TStringBuf("application/json");
  66. if (resp.Code >= HTTP_BAD_REQUEST) {
  67. Cerr << e.what() << Endl;
  68. TStringBuf message = e.AsStrBuf().RNextTok(':');
  69. if (!message.empty()) {
  70. resp.Body = ToErrorsJson(message);
  71. }
  72. }
  73. } catch (const TWithBackTrace<yexception>& e) {
  74. Cerr << e.what() << Endl;
  75. const TBackTrace* bt = e.BackTrace();
  76. bt->PrintTo(Cerr);
  77. resp.Code = HTTP_INTERNAL_SERVER_ERROR;
  78. resp.ContentType = TStringBuf("application/json");
  79. TStringBuf message = e.AsStrBuf().RNextTok(':');
  80. if (!message.empty()) {
  81. resp.Body = ToErrorsJson(message);
  82. }
  83. } catch (const yexception& e) {
  84. Cerr << e.what() << Endl;
  85. resp.Code = HTTP_INTERNAL_SERVER_ERROR;
  86. resp.ContentType = TStringBuf("application/json");
  87. TStringBuf message = e.AsStrBuf().RNextTok(':');
  88. if (!message.empty()) {
  89. resp.Body = ToErrorsJson(message);
  90. }
  91. }
  92. } else {
  93. resp.Code = HTTP_NOT_FOUND;
  94. resp.Body = ToErrorsJson(
  95. TString("Unknown path ") + RD.ScriptName());
  96. resp.ContentType = TStringBuf("application/json");
  97. }
  98. resp.Headers.AddHeader(THttpInputHeader("Content-Type", resp.ContentType));
  99. resp.OutTo(Output());
  100. Cout << '[' << TInstant::Now().ToStringUpToSeconds() << "] "
  101. << GetRemoteAddr() << ' '
  102. << Input().FirstLine() << ' ' << static_cast<int>(resp.Code)
  103. << " took: " << timer.Get().MilliSeconds() << "ms"
  104. << Endl;
  105. return true;
  106. }
  107. private:
  108. const TServer* Server_;
  109. };
  110. THttpServerOptions CreateOptions(const TServerConfig& config)
  111. {
  112. THttpServerOptions opts;
  113. if (config.IsBind(TBind::OnLocal)) {
  114. opts.AddBindAddress("localhost", config.GetPort());
  115. }
  116. if (config.IsBind(TBind::OnRemote)) {
  117. opts.AddBindAddress(HostName(), config.GetPort());
  118. }
  119. opts.SetPort(config.GetPort());
  120. opts.SetThreads(4);
  121. opts.SetMaxQueueSize(40);
  122. return opts;
  123. }
  124. } // namspace
  125. namespace NYql {
  126. namespace NHttp {
  127. void TServerConfig::InitCliOptions(NLastGetopt::TOpts& opts)
  128. {
  129. opts.AddLongOption("port", "listening port")
  130. .StoreResult<ui16>(&Port_)
  131. .DefaultValue("3000");
  132. opts.AddLongOption("local", "bind on local interface").NoArgument();
  133. opts.AddLongOption("remote", "bind on remote interface").NoArgument();
  134. opts.AddLongOption("assets", "path to folder with web intrface files")
  135. .StoreResult<TString>(&AssetsPath_).DefaultValue(AssetsPath_);
  136. }
  137. void TServerConfig::ParseFromCli(NLastGetopt::TOptsParseResult& res)
  138. {
  139. if (res.Has("local")) {
  140. Bind(TBind::OnLocal);
  141. }
  142. if (res.Has("remote")) {
  143. Bind(TBind::OnRemote);
  144. }
  145. if (Bind_ == 0) {
  146. Bind(TBind::OnLocal);
  147. }
  148. }
  149. ///////////////////////////////////////////////////////////////////////////////
  150. // TServer
  151. ///////////////////////////////////////////////////////////////////////////////
  152. TServer::TServer(const TServerConfig& config)
  153. : THttpServer(this, CreateOptions(config))
  154. {
  155. Cout << "Start server listening on\n";
  156. if (config.IsBind(TBind::OnLocal)) {
  157. Cout << "http://localhost:" << config.GetPort() << "/\n";
  158. }
  159. if (config.IsBind(TBind::OnRemote)) {
  160. Cout << "http://" << HostName() << ':' << config.GetPort() << "/\n";
  161. }
  162. Cout << Endl;
  163. }
  164. TServer::~TServer()
  165. {
  166. for (auto& it: Servlets_) {
  167. delete it.second;
  168. }
  169. }
  170. void TServer::RegisterServlet(const TString& path, TAutoPtr<IServlet> sp)
  171. {
  172. Cout << "Registered servlet on " << path << Endl;
  173. IServlet* servlet = sp.Release();
  174. Servlets_.push_back(std::pair<TString, IServlet*>(path, servlet));
  175. }
  176. TClientRequest* TServer::CreateClient()
  177. {
  178. return new THttpReplier(this);
  179. }
  180. const IServlet* TServer::FindServlet(TStringBuf path) const
  181. {
  182. for (const auto& servlet: Servlets_) {
  183. const TString& urlPrefix = servlet.first;
  184. if (path.StartsWith(urlPrefix))
  185. return servlet.second;
  186. }
  187. return nullptr;
  188. }
  189. TVector<TString> TServer::GetUrlMappings() const
  190. {
  191. TVector<TString> urls;
  192. urls.reserve(Servlets_.size());
  193. for (const auto& s: Servlets_) {
  194. urls.push_back(s.first);
  195. }
  196. return urls;
  197. }
  198. } // namspace NWeb
  199. } // namspace NYql