1 #include <signal.h>
2 #include <cstdlib>
3 #include <cstring>
4 #include <string>
5 #include <vector>
6 #include <algorithm>
7
8 #include <Wt/WServer.h>
9 #include <Wt/WResource.h>
10 #include <Wt/Http/Request.h>
11 #include <Wt/Http/Response.h>
12 #include <Wt/WTemplate.h>
13 #include <Wt/Utils.h>
14
15 #include <Wt/Dbo/Dbo.h>
16 #include <Wt/Dbo/Json.h>
17 #ifndef BENCHMARK_USE_POSTGRES
18 #include <Wt/Dbo/backend/MySQL.h>
19 #else
20 #include <Wt/Dbo/backend/Postgres.h>
21 #endif
22
23 #include <random>
24
25 #ifndef WT_WIN32
26 extern char **environ;
27 #endif // WT_WIN32
28
29 class MyMessage {
30 public:
31 std::string message;
32
33 template<class Action>
persist(Action & a)34 void persist(Action& a) {
35 Wt::Dbo::field(a, message, "message");
36 }
37 };
38
39 class World {
40 public:
41 int randomNumber;
42
43 template<class Action>
persist(Action & a)44 void persist(Action& a) {
45 Wt::Dbo::field(a, randomNumber, "randomnumber");
46 }
47 };
48
49 class Fortune {
50 public:
51 std::string message;
52
53 template<class Action>
persist(Action & a)54 void persist(Action& a) {
55 Wt::Dbo::field(a, message, "message");
56 }
57 };
58
59 namespace Wt {
60 namespace Dbo {
61 template<>
62 struct dbo_traits<World> : public dbo_default_traits {
versionFieldWt::Dbo::dbo_traits63 static const char *versionField() {
64 return 0;
65 }
invalidIdWt::Dbo::dbo_traits66 static IdType invalidId() {
67 return 0;
68 }
69 };
70 template<>
71 struct dbo_traits<Fortune> : public dbo_default_traits {
versionFieldWt::Dbo::dbo_traits72 static const char *versionField() {
73 return 0;
74 }
invalidIdWt::Dbo::dbo_traits75 static IdType invalidId() {
76 return 0;
77 }
78 };
79 }
80 }
81
82 class JsonResource : public Wt::WResource {
83 public:
handleRequest(const Wt::Http::Request & request,Wt::Http::Response & response)84 virtual void handleRequest(const Wt::Http::Request &request, Wt::Http::Response &response) {
85 response.setMimeType("application/json");
86 response.addHeader("Server", "Wt");
87
88 MyMessage message;
89 message.message = "Hello, World!";
90
91 Wt::Dbo::JsonSerializer writer(response.out());
92 writer.serialize(message);
93 }
94 };
95
96 class MyConnection : public
97 #ifdef BENCHMARK_USE_POSTGRES
98 Wt::Dbo::backend::Postgres
99 #else
100 Wt::Dbo::backend::MySQL
101 #endif
102 {
103 public:
104 #ifdef BENCHMARK_USE_POSTGRES
MyConnection(const std::string & db)105 MyConnection(const std::string &db) :
106 Wt::Dbo::backend::Postgres(db) {}
107 #else
108 MyConnection(const std::string &db, const std::string &dbuser, const std::string &dbpasswd, const std::string &dbhost, unsigned int dbport) :
109 Wt::Dbo::backend::MySQL(db, dbuser, dbpasswd, dbhost, dbport) {}
110 #endif
111
startTransaction()112 virtual void startTransaction() { }
commitTransaction()113 virtual void commitTransaction() { }
rollbackTransaction()114 virtual void rollbackTransaction() { }
115 };
116
117 struct DbStruct {
118 MyConnection *connection;
119 Wt::Dbo::Session session;
120
121 std::default_random_engine rng;
122 std::uniform_int_distribution<int> distribution;
123
DbStructDbStruct124 DbStruct()
125 : connection(0),
126 rng(clock()),
127 distribution(1, 10000) {
128 std::string dbHostStr = "localhost";
129 char *dbHost = std::getenv("DBHOST");
130 if (dbHost)
131 dbHostStr = std::string(dbHost);
132 #ifndef BENCHMARK_USE_POSTGRES
133 auto c = std::make_unique<MyConnection>("hello_world", "benchmarkdbuser", "benchmarkdbpass", dbHostStr, 3306);
134 #else
135 auto connStr = std::string("host=") + dbHostStr + " port=5432 user=benchmarkdbuser password=benchmarkdbpass dbname=hello_world";
136 auto c = std::make_unique<MyConnection>(connStr);
137 #endif
138
139 connection = c.get();
140 session.setConnection(std::move(c));
141 session.mapClass<World>("world");
142 session.mapClass<Fortune>("fortune");
143 }
144
randDbStruct145 int rand() {
146 return distribution(rng);
147 }
148 };
149
150 namespace {
151 thread_local DbStruct *dbStruct_;
152 }
153
154 class DbResource : public Wt::WResource {
155 public:
handleRequest(const Wt::Http::Request & request,Wt::Http::Response & response)156 virtual void handleRequest(const Wt::Http::Request &request, Wt::Http::Response &response) {
157 response.setMimeType("application/json");
158 response.addHeader("Server", "Wt");
159
160 if (!dbStruct_) {
161 dbStruct_ = new DbStruct();
162 }
163
164 Wt::Dbo::Transaction transaction(dbStruct_->session);
165 Wt::Dbo::ptr<World> entry = dbStruct_->session.load<World>(dbStruct_->rand());
166
167 Wt::Dbo::JsonSerializer writer(response.out());
168 writer.serialize(entry);
169 }
170 };
171
172 class QueriesResource : public Wt::WResource {
173 public:
handleRequest(const Wt::Http::Request & request,Wt::Http::Response & response)174 virtual void handleRequest(const Wt::Http::Request &request, Wt::Http::Response &response) {
175 int n;
176 if (const std::string *queries = request.getParameter("queries")) {
177 n = atoi(queries->c_str());
178 if (n < 1)
179 n = 1;
180 else if (n > 500)
181 n = 500;
182 } else {
183 n = 1;
184 }
185
186 response.setMimeType("application/json");
187 response.addHeader("Server", "Wt");
188
189 if (!dbStruct_) {
190 dbStruct_ = new DbStruct();
191 }
192
193 Wt::Dbo::Transaction transaction(dbStruct_->session);
194 std::vector<Wt::Dbo::ptr<World> > results;
195 results.reserve(n);
196 for (int i = 0; i < n; ++i) {
197 results.push_back(dbStruct_->session.load<World>(dbStruct_->rand()));
198 }
199 Wt::Dbo::JsonSerializer writer(response.out());
200 writer.serialize(results);
201 }
202 };
203
204 typedef Wt::Dbo::collection< Wt::Dbo::ptr<Fortune> > Fortunes;
205 typedef std::vector<Wt::Dbo::ptr<Fortune> > VFortunes;
206
fortuneCmp(const Wt::Dbo::ptr<Fortune> & f1,const Wt::Dbo::ptr<Fortune> & f2)207 bool fortuneCmp(const Wt::Dbo::ptr<Fortune>& f1, const Wt::Dbo::ptr<Fortune>& f2) {
208 return strcmp(f1->message.c_str(), f2->message.c_str()) < 0;
209 }
210
211 class FortuneTemplate : public Wt::WTemplate {
212 private:
213 const VFortunes *fortunes_;
214 mutable std::vector<Wt::Dbo::ptr<Fortune> >::const_iterator it_;
215 public:
FortuneTemplate(const std::vector<Wt::Dbo::ptr<Fortune>> & fortunes)216 FortuneTemplate(const std::vector<Wt::Dbo::ptr<Fortune> >& fortunes)
217 : Wt::WTemplate(tr("fortunes")),
218 fortunes_(&fortunes),
219 it_(fortunes.end())
220 {
221 addFunction("while", &Wt::WTemplate::Functions::while_f);
222 }
223
conditionValue(const std::string & name) const224 virtual bool conditionValue(const std::string& name) const {
225 if (name == "next-fortune") {
226 if (it_ == fortunes_->end())
227 it_ = fortunes_->begin();
228 else
229 ++it_;
230
231 if (it_ == fortunes_->end())
232 return false;
233
234 return true;
235 } else
236 return Wt::WTemplate::conditionValue(name);
237 }
238
resolveString(const std::string & varName,const std::vector<Wt::WString> & vars,std::ostream & result)239 virtual void resolveString(const std::string& varName, const std::vector<Wt::WString>& vars, std::ostream& result) {
240 if (varName == "id")
241 result << it_->id();
242 else if (varName == "message")
243 format(result, Wt::WString((*it_)->message));
244 else
245 Wt::WTemplate::resolveString(varName, vars, result);
246 }
247 };
248
249 class FortuneResource : public Wt::WResource {
250 public:
handleRequest(const Wt::Http::Request & request,Wt::Http::Response & response)251 virtual void handleRequest(const Wt::Http::Request &request, Wt::Http::Response &response) {
252 response.setMimeType("text/html; charset=utf-8");
253 response.addHeader("Server", "Wt");
254
255 if (!dbStruct_) {
256 dbStruct_ = new DbStruct();
257 }
258
259 Wt::Dbo::Transaction transaction(dbStruct_->session);
260 Fortunes fortunes = dbStruct_->session.find<Fortune>();
261 VFortunes vFortunes;
262 for (Fortunes::const_iterator i = fortunes.begin(); i != fortunes.end(); ++i)
263 vFortunes.push_back(*i);
264 auto additionalFortune = std::make_unique<Fortune>();
265 additionalFortune->message = "Additional fortune added at request time.";
266 vFortunes.push_back(Wt::Dbo::ptr<Fortune>(std::move(additionalFortune)));
267
268 std::sort(vFortunes.begin(), vFortunes.end(), fortuneCmp);
269
270 FortuneTemplate tpl(vFortunes);
271
272 response.out() << "<!DOCTYPE html>";
273 tpl.renderTemplate(response.out());
274 }
275 };
276
277 class UpdateResource : public Wt::WResource {
278 public:
handleRequest(const Wt::Http::Request & request,Wt::Http::Response & response)279 virtual void handleRequest(const Wt::Http::Request &request, Wt::Http::Response &response) {
280 int n;
281 if (const std::string *queries = request.getParameter("queries")) {
282 n = atoi(queries->c_str());
283 if (n < 1)
284 n = 1;
285 else if (n > 500)
286 n = 500;
287 } else {
288 n = 1;
289 }
290
291 response.setMimeType("application/json");
292 response.addHeader("Server", "Wt");
293
294 if (!dbStruct_) {
295 dbStruct_ = new DbStruct();
296 }
297
298 std::vector<Wt::Dbo::ptr<World> > results;
299
300 for (int i = 0; i < n; ++i) {
301 bool success = false;
302 while (!success) {
303 try {
304 Wt::Dbo::Transaction transaction(dbStruct_->session);
305 Wt::Dbo::ptr<World> world = dbStruct_->session.load<World>(dbStruct_->rand());
306 world.modify()->randomNumber = dbStruct_->rand();
307 transaction.commit();
308 results.push_back(world);
309 success = true;
310 } catch (Wt::Dbo::Exception& e) {
311 // Retry
312 }
313 }
314 }
315
316 Wt::Dbo::JsonSerializer writer(response.out());
317 writer.serialize(results);
318 }
319 };
320
321 class PlaintextResource : public Wt::WResource {
handleRequest(const Wt::Http::Request & request,Wt::Http::Response & response)322 virtual void handleRequest(const Wt::Http::Request &request, Wt::Http::Response &response) {
323 response.setMimeType("text/plain");
324 response.addHeader("Server", "Wt");
325
326 response.out() << "Hello, World!";
327 }
328 };
329
main(int argc,char ** argv)330 int main(int argc, char** argv) {
331 try {
332 Wt::WServer server(argv[0]);
333
334 server.setServerConfiguration(argc, argv, WTHTTP_CONFIGURATION);
335
336 auto bundle = std::make_shared<Wt::WMessageResourceBundle>();
337 bundle->use(server.appRoot() + "fortunes");
338 server.setLocalizedStrings(bundle);
339
340 JsonResource jsonResource;
341 server.addResource(&jsonResource, "/json");
342
343 DbResource dbResource;
344 server.addResource(&dbResource, "/db");
345
346 QueriesResource queriesResource;
347 server.addResource(&queriesResource, "/queries");
348
349 FortuneResource fortuneResource;
350 server.addResource(&fortuneResource, "/fortune");
351
352 UpdateResource updateResource;
353 server.addResource(&updateResource, "/updates");
354
355 PlaintextResource plaintextResource;
356 server.addResource(&plaintextResource, "/plaintext");
357
358 if (server.start()) {
359 int sig = Wt::WServer::waitForShutdown();
360
361 std::cerr << "Shutdown (signal = " << sig << ")" << std::endl;
362 server.stop();
363
364 #ifndef WT_WIN32
365 if (sig == SIGHUP)
366 Wt::WServer::restart(argc, argv, environ);
367 #endif // WT_WIN32
368 }
369 } catch (Wt::WServer::Exception& e) {
370 std::cerr << e.what() << "\n";
371 return 1;
372 } catch (std::exception& e) {
373 std::cerr << "exception: " << e.what() << "\n";
374 return 1;
375 }
376 }
377