blob: e2e5450fbdfd066ba7d502d94a17347c396a0180 [file] [log] [blame]
joshualitt7f6a1e02016-01-22 11:21:43 -08001/*
2 * Copyright 2016 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "GrCaps.h"
9#include "GrContextFactory.h"
10#include "SkCanvas.h"
11#include "SkCommandLineFlags.h"
joshualitt136f5172016-02-02 11:07:39 -080012#include "SkDebugCanvas.h"
joshualitt609d9792016-01-27 11:07:23 -080013#include "SkJSONCanvas.h"
joshualitt29e5a892016-02-04 06:08:33 -080014#include "SkJSONCPP.h"
joshualitt9a4e1882016-01-27 07:03:29 -080015#include "SkPicture.h"
joshualitt792345f2016-02-02 13:02:33 -080016#include "SkPictureRecorder.h"
17#include "SkPixelSerializer.h"
joshualitt7f6a1e02016-01-22 11:21:43 -080018#include "SkStream.h"
19#include "SkSurface.h"
20
joshualittcdad12f2016-02-08 07:08:21 -080021#include "UrlDataManager.h"
22
joshualitt7f6a1e02016-01-22 11:21:43 -080023#include <sys/socket.h>
joshualitt7f6a1e02016-01-22 11:21:43 -080024#include <microhttpd.h>
25
26// To get image decoders linked in we have to do the below magic
27#include "SkForceLinking.h"
28#include "SkImageDecoder.h"
29__SK_FORCE_IMAGE_DECODER_LINKING;
30
jcgregorio21ab1202016-01-28 06:24:19 -080031DEFINE_string(source, "https://debugger.skia.org", "Where to load the web UI from.");
joshualitt873d6242016-02-08 13:57:44 -080032DEFINE_string(faviconDir, "tools/skiaserve", "The directory of the favicon");
jcgregorio21ab1202016-01-28 06:24:19 -080033DEFINE_int32(port, 8888, "The port to listen on.");
joshualitt7f6a1e02016-01-22 11:21:43 -080034
35// TODO probably want to make this configurable
36static const int kImageWidth = 1920;
37static const int kImageHeight = 1080;
38
joshualitt7f6a1e02016-01-22 11:21:43 -080039// TODO move to template file
40SkString generateTemplate(SkString source) {
41 SkString debuggerTemplate;
42 debuggerTemplate.appendf(
43 "<!DOCTYPE html>\n"
44 "<html>\n"
45 "<head>\n"
46 " <title>SkDebugger</title>\n"
47 " <meta charset=\"utf-8\" />\n"
48 " <meta http-equiv=\"X-UA-Compatible\" content=\"IE=egde,chrome=1\">\n"
49 " <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n"
50 " <script src=\"%s/res/js/core.js\" type=\"text/javascript\" charset=\"utf-8\"></script>\n"
51 " <link href=\"%s/res/vul/elements.html\" rel=\"import\" />\n"
52 "</head>\n"
53 "<body class=\"fullbleed layout vertical\">\n"
54 " <debugger-app-sk>This is the app."
55 " </debugger-app-sk>\n"
56 "</body>\n"
57 "</html>", source.c_str(), source.c_str());
58 return debuggerTemplate;
59
60}
61
joshualitt9a4e1882016-01-27 07:03:29 -080062struct UploadContext {
joshualitt609d9792016-01-27 11:07:23 -080063 SkDynamicMemoryWStream fStream;
64 MHD_PostProcessor* fPostProcessor;
joshualitt9a4e1882016-01-27 07:03:29 -080065 MHD_Connection* connection;
66};
67
68struct Request {
joshualittcdad12f2016-02-08 07:08:21 -080069 Request(SkString rootUrl) : fUploadContext(nullptr), fUrlDataManager(rootUrl) {}
joshualitt9a4e1882016-01-27 07:03:29 -080070 UploadContext* fUploadContext;
joshualitt609d9792016-01-27 11:07:23 -080071 SkAutoTUnref<SkPicture> fPicture;
joshualitt136f5172016-02-02 11:07:39 -080072 SkAutoTUnref<SkDebugCanvas> fDebugCanvas;
joshualittcdad12f2016-02-08 07:08:21 -080073 UrlDataManager fUrlDataManager;
joshualitt9a4e1882016-01-27 07:03:29 -080074};
75
joshualitt609d9792016-01-27 11:07:23 -080076// TODO factor this out into functions, also handle CPU path
joshualitt136f5172016-02-02 11:07:39 -080077SkSurface* setupSurface(GrContextFactory* factory) {
joshualitt609d9792016-01-27 11:07:23 -080078 GrContext* context = factory->get(GrContextFactory::kNative_GLContextType,
79 GrContextFactory::kNone_GLContextOptions);
80 int maxRTSize = context->caps()->maxRenderTargetSize();
81 SkImageInfo info = SkImageInfo::Make(SkTMin(kImageWidth, maxRTSize),
82 SkTMin(kImageHeight, maxRTSize),
83 kN32_SkColorType, kPremul_SkAlphaType);
84 uint32_t flags = 0;
85 SkSurfaceProps props(flags, SkSurfaceProps::kLegacyFontHost_InitType);
joshualitt136f5172016-02-02 11:07:39 -080086 SkSurface* surface = SkSurface::NewRenderTarget(context, SkSurface::kNo_Budgeted, info, 0,
87 &props);
88 SkASSERT(surface);
joshualitt609d9792016-01-27 11:07:23 -080089
90 SkGLContext* gl = factory->getContextInfo(GrContextFactory::kNative_GLContextType,
91 GrContextFactory::kNone_GLContextOptions).fGLContext;
92 gl->makeCurrent();
joshualitt136f5172016-02-02 11:07:39 -080093 return surface;
94}
joshualitt609d9792016-01-27 11:07:23 -080095
joshualitt136f5172016-02-02 11:07:39 -080096SkData* writeCanvasToPng(SkCanvas* canvas) {
joshualitt609d9792016-01-27 11:07:23 -080097 // capture pixels
98 SkBitmap bmp;
99 bmp.setInfo(canvas->imageInfo());
100 if (!canvas->readPixels(&bmp, 0, 0)) {
joshualitt136f5172016-02-02 11:07:39 -0800101 fprintf(stderr, "Can't read pixels\n");
102 return nullptr;
joshualitt609d9792016-01-27 11:07:23 -0800103 }
104
105 // write to png
joshualittcdad12f2016-02-08 07:08:21 -0800106 // TODO encoding to png can be quite slow, we should investigate bmp
joshualitt136f5172016-02-02 11:07:39 -0800107 SkData* png = SkImageEncoder::EncodeData(bmp, SkImageEncoder::kPNG_Type, 100);
108 if (!png) {
109 fprintf(stderr, "Can't encode to png\n");
110 return nullptr;
joshualitt609d9792016-01-27 11:07:23 -0800111 }
joshualitt136f5172016-02-02 11:07:39 -0800112 return png;
113}
114
115SkData* setupAndDrawToCanvasReturnPng(SkDebugCanvas* debugCanvas, int n) {
116 GrContextOptions grContextOpts;
117 SkAutoTDelete<GrContextFactory> factory(new GrContextFactory(grContextOpts));
118 SkAutoTUnref<SkSurface> surface(setupSurface(factory.get()));
119
120 SkASSERT(debugCanvas);
121 SkCanvas* canvas = surface->getCanvas();
122 debugCanvas->drawTo(canvas, n);
123 return writeCanvasToPng(canvas);
joshualitt609d9792016-01-27 11:07:23 -0800124}
125
joshualitt29e5a892016-02-04 06:08:33 -0800126SkSurface* setupCpuSurface() {
127 SkImageInfo info = SkImageInfo::Make(kImageWidth, kImageHeight, kN32_SkColorType,
128 kPremul_SkAlphaType);
129 return SkSurface::NewRaster(info);
130}
131
joshualitt9a4e1882016-01-27 07:03:29 -0800132static const size_t kBufferSize = 1024;
133
134static int process_upload_data(void* cls, enum MHD_ValueKind kind,
135 const char* key, const char* filename,
136 const char* content_type, const char* transfer_encoding,
137 const char* data, uint64_t off, size_t size) {
138 struct UploadContext* uc = reinterpret_cast<UploadContext*>(cls);
139
140 if (0 != size) {
joshualitt609d9792016-01-27 11:07:23 -0800141 uc->fStream.write(data, size);
joshualitt9a4e1882016-01-27 07:03:29 -0800142 }
143 return MHD_YES;
144}
145
joshualitt792345f2016-02-02 13:02:33 -0800146static int SendData(MHD_Connection* connection, const SkData* data, const char* type,
147 bool setContentDisposition = false, const char* dispositionString = nullptr) {
joshualittccfdaa52016-01-27 07:40:29 -0800148 MHD_Response* response = MHD_create_response_from_buffer(data->size(),
149 const_cast<void*>(data->data()),
150 MHD_RESPMEM_MUST_COPY);
joshualitt609d9792016-01-27 11:07:23 -0800151 MHD_add_response_header(response, "Content-Type", type);
joshualittccfdaa52016-01-27 07:40:29 -0800152
joshualitt792345f2016-02-02 13:02:33 -0800153 if (setContentDisposition) {
154 MHD_add_response_header(response, "Content-Disposition", dispositionString);
155 }
156
joshualittccfdaa52016-01-27 07:40:29 -0800157 int ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
158 MHD_destroy_response(response);
159 return ret;
160}
161
joshualitt136f5172016-02-02 11:07:39 -0800162static int SendJSON(MHD_Connection* connection, SkDebugCanvas* debugCanvas, int n) {
joshualitt609d9792016-01-27 11:07:23 -0800163 SkDynamicMemoryWStream stream;
164 SkAutoTUnref<SkJSONCanvas> jsonCanvas(new SkJSONCanvas(kImageWidth, kImageHeight, stream));
joshualitt136f5172016-02-02 11:07:39 -0800165 debugCanvas->drawTo(jsonCanvas, n);
joshualitt609d9792016-01-27 11:07:23 -0800166 jsonCanvas->finish();
167
168 SkAutoTUnref<SkData> data(stream.copyToData());
169 return SendData(connection, data, "application/json");
170}
171
172static int SendTemplate(MHD_Connection* connection, bool redirect = false,
173 const char* redirectUrl = nullptr) {
jcgregorio21ab1202016-01-28 06:24:19 -0800174 SkString debuggerTemplate = generateTemplate(SkString(FLAGS_source[0]));
joshualittccfdaa52016-01-27 07:40:29 -0800175
176 MHD_Response* response = MHD_create_response_from_buffer(
177 debuggerTemplate.size(),
178 (void*) const_cast<char*>(debuggerTemplate.c_str()),
179 MHD_RESPMEM_MUST_COPY);
jcgregorio6a2046e2016-01-28 05:31:31 -0800180 MHD_add_response_header (response, "Access-Control-Allow-Origin", "*");
joshualittccfdaa52016-01-27 07:40:29 -0800181
jcgregorio6f17bc52016-01-27 11:44:38 -0800182 int status = MHD_HTTP_OK;
183
joshualitt609d9792016-01-27 11:07:23 -0800184 if (redirect) {
185 MHD_add_response_header (response, "Location", redirectUrl);
jcgregorio6f17bc52016-01-27 11:44:38 -0800186 status = MHD_HTTP_SEE_OTHER;
joshualitt609d9792016-01-27 11:07:23 -0800187 }
188
jcgregorio6f17bc52016-01-27 11:44:38 -0800189 int ret = MHD_queue_response(connection, status, response);
joshualittccfdaa52016-01-27 07:40:29 -0800190 MHD_destroy_response(response);
191 return ret;
192}
193
joshualitt483b9012016-02-02 07:16:24 -0800194class UrlHandler {
195public:
196 virtual ~UrlHandler() {}
197 virtual bool canHandle(const char* method, const char* url) = 0;
198 virtual int handle(Request* request, MHD_Connection* connection,
joshualitt136f5172016-02-02 11:07:39 -0800199 const char* url, const char* method,
joshualitt483b9012016-02-02 07:16:24 -0800200 const char* upload_data, size_t* upload_data_size) = 0;
201};
joshualittccfdaa52016-01-27 07:40:29 -0800202
joshualitt29e5a892016-02-04 06:08:33 -0800203class CmdHandler : public UrlHandler {
joshualitt483b9012016-02-02 07:16:24 -0800204public:
205 bool canHandle(const char* method, const char* url) override {
joshualitt136f5172016-02-02 11:07:39 -0800206 const char* kBasePath = "/cmd";
207 return 0 == strncmp(url, kBasePath, strlen(kBasePath));
joshualittccfdaa52016-01-27 07:40:29 -0800208 }
209
joshualitt483b9012016-02-02 07:16:24 -0800210 int handle(Request* request, MHD_Connection* connection,
joshualitt136f5172016-02-02 11:07:39 -0800211 const char* url, const char* method,
joshualitt483b9012016-02-02 07:16:24 -0800212 const char* upload_data, size_t* upload_data_size) override {
joshualitt136f5172016-02-02 11:07:39 -0800213 SkTArray<SkString> commands;
214 SkStrSplit(url, "/", &commands);
215
216 if (!request->fPicture.get() || commands.count() > 3) {
217 return MHD_NO;
joshualitt483b9012016-02-02 07:16:24 -0800218 }
joshualitt136f5172016-02-02 11:07:39 -0800219
220 // /cmd or /cmd/N or /cmd/N/[0|1]
221 if (commands.count() == 1 && 0 == strcmp(method, MHD_HTTP_METHOD_GET)) {
222 int n = request->fDebugCanvas->getSize() - 1;
223 return SendJSON(connection, request->fDebugCanvas, n);
224 }
225
226 // /cmd/N, for now only delete supported
227 if (commands.count() == 2 && 0 == strcmp(method, MHD_HTTP_METHOD_DELETE)) {
228 int n;
229 sscanf(commands[1].c_str(), "%d", &n);
230 request->fDebugCanvas->deleteDrawCommandAt(n);
231 return MHD_YES;
232 }
233
234 // /cmd/N/[0|1]
235 if (commands.count() == 3 && 0 == strcmp(method, MHD_HTTP_METHOD_POST)) {
236 int n, toggle;
237 sscanf(commands[1].c_str(), "%d", &n);
238 sscanf(commands[2].c_str(), "%d", &toggle);
239 request->fDebugCanvas->toggleCommand(n, toggle);
240 return MHD_YES;
241 }
242
joshualitt483b9012016-02-02 07:16:24 -0800243 return MHD_NO;
244 }
245};
246
247class ImgHandler : public UrlHandler {
248public:
249 bool canHandle(const char* method, const char* url) override {
250 static const char* kBasePath = "/img";
251 return 0 == strcmp(method, MHD_HTTP_METHOD_GET) &&
252 0 == strncmp(url, kBasePath, strlen(kBasePath));
joshualittccfdaa52016-01-27 07:40:29 -0800253 }
254
joshualitt483b9012016-02-02 07:16:24 -0800255 int handle(Request* request, MHD_Connection* connection,
joshualitt136f5172016-02-02 11:07:39 -0800256 const char* url, const char* method,
joshualitt483b9012016-02-02 07:16:24 -0800257 const char* upload_data, size_t* upload_data_size) override {
joshualitt136f5172016-02-02 11:07:39 -0800258 SkTArray<SkString> commands;
259 SkStrSplit(url, "/", &commands);
260
261 if (!request->fPicture.get() || commands.count() > 2) {
262 return MHD_NO;
joshualitt483b9012016-02-02 07:16:24 -0800263 }
joshualitta341b902016-02-02 07:37:21 -0800264
joshualitt136f5172016-02-02 11:07:39 -0800265 int n;
266 // /img or /img/N
267 if (commands.count() == 1) {
268 n = request->fDebugCanvas->getSize() - 1;
269 } else {
270 sscanf(commands[1].c_str(), "%d", &n);
271 }
272
273 SkAutoTUnref<SkData> data(setupAndDrawToCanvasReturnPng(request->fDebugCanvas, n));
274 return SendData(connection, data, "image/png");
joshualitt483b9012016-02-02 07:16:24 -0800275 }
276};
joshualittccfdaa52016-01-27 07:40:29 -0800277
joshualitt483b9012016-02-02 07:16:24 -0800278class PostHandler : public UrlHandler {
279public:
280 bool canHandle(const char* method, const char* url) override {
281 return 0 == strcmp(method, MHD_HTTP_METHOD_POST) &&
282 0 == strcmp(url, "/new");
joshualittccfdaa52016-01-27 07:40:29 -0800283 }
284
joshualitt483b9012016-02-02 07:16:24 -0800285 int handle(Request* request, MHD_Connection* connection,
joshualitt136f5172016-02-02 11:07:39 -0800286 const char* url, const char* method,
joshualitt483b9012016-02-02 07:16:24 -0800287 const char* upload_data, size_t* upload_data_size) override {
288 UploadContext* uc = request->fUploadContext;
joshualittccfdaa52016-01-27 07:40:29 -0800289
joshualitt483b9012016-02-02 07:16:24 -0800290 // New connection
291 if (!uc) {
292 // TODO make this a method on request
293 uc = new UploadContext;
294 uc->connection = connection;
295 uc->fPostProcessor = MHD_create_post_processor(connection, kBufferSize,
296 &process_upload_data, uc);
297 SkASSERT(uc->fPostProcessor);
joshualitt609d9792016-01-27 11:07:23 -0800298
joshualitt483b9012016-02-02 07:16:24 -0800299 request->fUploadContext = uc;
300 return MHD_YES;
301 }
302
303 // in process upload
304 if (0 != *upload_data_size) {
305 SkASSERT(uc->fPostProcessor);
306 MHD_post_process(uc->fPostProcessor, upload_data, *upload_data_size);
307 *upload_data_size = 0;
308 return MHD_YES;
309 }
310
311 // end of upload
312 MHD_destroy_post_processor(uc->fPostProcessor);
313 uc->fPostProcessor = nullptr;
314
joshualitt136f5172016-02-02 11:07:39 -0800315 // parse picture from stream
316 request->fPicture.reset(
317 SkPicture::CreateFromStream(request->fUploadContext->fStream.detachAsStream()));
318 if (!request->fPicture.get()) {
319 fprintf(stderr, "Could not create picture from stream.\n");
320 return MHD_NO;
joshualitt483b9012016-02-02 07:16:24 -0800321 }
322
joshualitt136f5172016-02-02 11:07:39 -0800323 // pour picture into debug canvas
324 request->fDebugCanvas.reset(new SkDebugCanvas(kImageWidth, kImageHeight));
325 request->fDebugCanvas->drawPicture(request->fPicture);
326
joshualitta341b902016-02-02 07:37:21 -0800327 // clear upload context
328 delete request->fUploadContext;
329 request->fUploadContext = nullptr;
330
joshualitt483b9012016-02-02 07:16:24 -0800331 return SendTemplate(connection, true, "/");
joshualittccfdaa52016-01-27 07:40:29 -0800332 }
joshualitt483b9012016-02-02 07:16:24 -0800333};
334
joshualitt792345f2016-02-02 13:02:33 -0800335class DownloadHandler : public UrlHandler {
336public:
337 bool canHandle(const char* method, const char* url) override {
338 return 0 == strcmp(method, MHD_HTTP_METHOD_GET) &&
339 0 == strcmp(url, "/download");
340 }
341
342 int handle(Request* request, MHD_Connection* connection,
343 const char* url, const char* method,
344 const char* upload_data, size_t* upload_data_size) override {
345 if (!request->fPicture.get()) {
346 return MHD_NO;
347 }
348
349 // TODO move to a function
350 // Playback into picture recorder
351 SkPictureRecorder recorder;
352 SkCanvas* canvas = recorder.beginRecording(kImageWidth, kImageHeight);
353
354 request->fDebugCanvas->draw(canvas);
355
356 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
357
358 SkDynamicMemoryWStream outStream;
359
360 SkAutoTUnref<SkPixelSerializer> serializer(SkImageEncoder::CreatePixelSerializer());
361 picture->serialize(&outStream, serializer);
362
363 SkAutoTUnref<SkData> data(outStream.copyToData());
364
365 // TODO fancier name handling
366 return SendData(connection, data, "application/octet-stream", true,
367 "attachment; filename=something.skp;");
368 }
369};
370
joshualitt29e5a892016-02-04 06:08:33 -0800371class InfoHandler : public UrlHandler {
372public:
373 bool canHandle(const char* method, const char* url) override {
374 const char* kBaseName = "/info";
375 return 0 == strcmp(method, MHD_HTTP_METHOD_GET) &&
376 0 == strncmp(url, kBaseName, strlen(kBaseName));
377 }
378
379 int handle(Request* request, MHD_Connection* connection,
380 const char* url, const char* method,
381 const char* upload_data, size_t* upload_data_size) override {
382 SkTArray<SkString> commands;
383 SkStrSplit(url, "/", &commands);
384
385 if (!request->fPicture.get() || commands.count() > 2) {
386 return MHD_NO;
387 }
388
389 // drawTo
390 SkAutoTUnref<SkSurface> surface(setupCpuSurface());
391 SkCanvas* canvas = surface->getCanvas();
392
393 int n;
394 // /info or /info/N
395 if (commands.count() == 1) {
396 n = request->fDebugCanvas->getSize() - 1;
397 } else {
398 sscanf(commands[1].c_str(), "%d", &n);
399 }
400
401 // TODO this is really slow and we should cache the matrix and clip
402 request->fDebugCanvas->drawTo(canvas, n);
403
404 // make some json
405 SkMatrix vm = request->fDebugCanvas->getCurrentMatrix();
406 SkIRect clip = request->fDebugCanvas->getCurrentClip();
407 Json::Value info(Json::objectValue);
408 info["ViewMatrix"] = SkJSONCanvas::MakeMatrix(vm);
409 info["ClipRect"] = SkJSONCanvas::MakeIRect(clip);
410
411 std::string json = Json::FastWriter().write(info);
412
413 // We don't want the null terminator so strlen is correct
414 SkAutoTUnref<SkData> data(SkData::NewWithCopy(json.c_str(), strlen(json.c_str())));
415 return SendData(connection, data, "application/json");
416 }
417};
418
joshualittcdad12f2016-02-08 07:08:21 -0800419class DataHandler : public UrlHandler {
420public:
421 bool canHandle(const char* method, const char* url) override {
422 static const char* kBaseUrl = "/data";
423 return 0 == strcmp(method, MHD_HTTP_METHOD_GET) &&
424 0 == strncmp(url, kBaseUrl, strlen(kBaseUrl));
425 }
426
427 int handle(Request* request, MHD_Connection* connection,
428 const char* url, const char* method,
429 const char* upload_data, size_t* upload_data_size) override {
430 SkTArray<SkString> commands;
431 SkStrSplit(url, "/", &commands);
432
433 if (!request->fPicture.get() || commands.count() != 2) {
434 return MHD_NO;
435 }
436
437 SkAutoTUnref<UrlDataManager::UrlData> urlData(
438 SkRef(request->fUrlDataManager.getDataFromUrl(SkString(url))));
439
440 if (urlData) {
441 return SendData(connection, urlData->fData.get(), urlData->fContentType.c_str());
442 }
443 return MHD_NO;
444 }
445};
joshualitt29e5a892016-02-04 06:08:33 -0800446
joshualitt873d6242016-02-08 13:57:44 -0800447class FaviconHandler : public UrlHandler {
448public:
449 bool canHandle(const char* method, const char* url) override {
450 return 0 == strcmp(method, MHD_HTTP_METHOD_GET) &&
451 0 == strcmp(url, "/favicon.ico");
452 }
453
454 int handle(Request* request, MHD_Connection* connection,
455 const char* url, const char* method,
456 const char* upload_data, size_t* upload_data_size) override {
457 SkString dir(FLAGS_faviconDir[0]);
458 dir.append("/favicon.ico");
459 FILE* ico = fopen(dir.c_str(), "r");
460
461 SkAutoTUnref<SkData> data(SkData::NewFromFILE(ico));
462 int ret = SendData(connection, data, "image/vnd.microsoft.icon");
463 fclose(ico);
464 return ret;
465 }
466};
467
468
joshualitt483b9012016-02-02 07:16:24 -0800469class RootHandler : public UrlHandler {
470public:
471 bool canHandle(const char* method, const char* url) override {
472 return 0 == strcmp(method, MHD_HTTP_METHOD_GET) &&
473 0 == strcmp(url, "/");
474 }
475
476 int handle(Request* request, MHD_Connection* connection,
joshualitt136f5172016-02-02 11:07:39 -0800477 const char* url, const char* method,
joshualitt483b9012016-02-02 07:16:24 -0800478 const char* upload_data, size_t* upload_data_size) override {
479 return SendTemplate(connection);
480 }
481};
joshualittccfdaa52016-01-27 07:40:29 -0800482
483class UrlManager {
484public:
485 UrlManager() {
486 // Register handlers
joshualitt483b9012016-02-02 07:16:24 -0800487 fHandlers.push_back(new RootHandler);
488 fHandlers.push_back(new PostHandler);
489 fHandlers.push_back(new ImgHandler);
joshualitt29e5a892016-02-04 06:08:33 -0800490 fHandlers.push_back(new CmdHandler);
joshualitt483b9012016-02-02 07:16:24 -0800491 fHandlers.push_back(new InfoHandler);
joshualitt792345f2016-02-02 13:02:33 -0800492 fHandlers.push_back(new DownloadHandler);
joshualittcdad12f2016-02-08 07:08:21 -0800493 fHandlers.push_back(new DataHandler);
joshualitt873d6242016-02-08 13:57:44 -0800494 fHandlers.push_back(new FaviconHandler);
joshualitt483b9012016-02-02 07:16:24 -0800495 }
496
497 ~UrlManager() {
498 for (int i = 0; i < fHandlers.count(); i++) { delete fHandlers[i]; }
joshualittccfdaa52016-01-27 07:40:29 -0800499 }
500
501 // This is clearly not efficient for a large number of urls and handlers
502 int invoke(Request* request, MHD_Connection* connection, const char* url, const char* method,
503 const char* upload_data, size_t* upload_data_size) const {
504 for (int i = 0; i < fHandlers.count(); i++) {
joshualitt483b9012016-02-02 07:16:24 -0800505 if (fHandlers[i]->canHandle(method, url)) {
joshualitt136f5172016-02-02 11:07:39 -0800506 return fHandlers[i]->handle(request, connection, url, method, upload_data,
507 upload_data_size);
joshualittccfdaa52016-01-27 07:40:29 -0800508 }
509 }
510 return MHD_NO;
511 }
512
513private:
joshualitt483b9012016-02-02 07:16:24 -0800514 SkTArray<UrlHandler*> fHandlers;
joshualittccfdaa52016-01-27 07:40:29 -0800515};
516
517const UrlManager kUrlManager;
518
joshualitt7f6a1e02016-01-22 11:21:43 -0800519int answer_to_connection(void* cls, struct MHD_Connection* connection,
520 const char* url, const char* method, const char* version,
521 const char* upload_data, size_t* upload_data_size,
522 void** con_cls) {
joshualitt9a4e1882016-01-27 07:03:29 -0800523 SkDebugf("New %s request for %s using version %s\n", method, url, version);
joshualitt7f6a1e02016-01-22 11:21:43 -0800524
joshualitt9a4e1882016-01-27 07:03:29 -0800525 Request* request = reinterpret_cast<Request*>(cls);
joshualitt483b9012016-02-02 07:16:24 -0800526 int result = kUrlManager.invoke(request, connection, url, method, upload_data,
527 upload_data_size);
528 if (MHD_NO == result) {
joshualitt873d6242016-02-08 13:57:44 -0800529 fprintf(stderr, "Invalid method and / or url: %s %s\n", method, url);
joshualitt483b9012016-02-02 07:16:24 -0800530 }
531 return result;
joshualitt7f6a1e02016-01-22 11:21:43 -0800532}
533
534int skiaserve_main() {
joshualittcdad12f2016-02-08 07:08:21 -0800535 Request request(SkString("/data")); // This simple server has one request
joshualitt7f6a1e02016-01-22 11:21:43 -0800536 struct MHD_Daemon* daemon;
jcgregorio21ab1202016-01-28 06:24:19 -0800537 // TODO Add option to bind this strictly to an address, e.g. localhost, for security.
538 daemon = MHD_start_daemon(MHD_USE_SELECT_INTERNALLY, FLAGS_port, nullptr, nullptr,
joshualitt9a4e1882016-01-27 07:03:29 -0800539 &answer_to_connection, &request,
540 MHD_OPTION_END);
joshualitt7f6a1e02016-01-22 11:21:43 -0800541 if (NULL == daemon) {
542 return 1;
543 }
544
545 getchar();
546 MHD_stop_daemon(daemon);
547 return 0;
548}
549
550#if !defined SK_BUILD_FOR_IOS
551int main(int argc, char** argv) {
552 SkCommandLineFlags::Parse(argc, argv);
553 return skiaserve_main();
554}
555#endif