blob: a8ddcdf488f9d812e925ff8c2d8334908456450e [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.");
32DEFINE_int32(port, 8888, "The port to listen on.");
joshualitt7f6a1e02016-01-22 11:21:43 -080033
34// TODO probably want to make this configurable
35static const int kImageWidth = 1920;
36static const int kImageHeight = 1080;
37
joshualitt7f6a1e02016-01-22 11:21:43 -080038// TODO move to template file
39SkString generateTemplate(SkString source) {
40 SkString debuggerTemplate;
41 debuggerTemplate.appendf(
42 "<!DOCTYPE html>\n"
43 "<html>\n"
44 "<head>\n"
45 " <title>SkDebugger</title>\n"
46 " <meta charset=\"utf-8\" />\n"
47 " <meta http-equiv=\"X-UA-Compatible\" content=\"IE=egde,chrome=1\">\n"
48 " <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n"
49 " <script src=\"%s/res/js/core.js\" type=\"text/javascript\" charset=\"utf-8\"></script>\n"
50 " <link href=\"%s/res/vul/elements.html\" rel=\"import\" />\n"
51 "</head>\n"
52 "<body class=\"fullbleed layout vertical\">\n"
53 " <debugger-app-sk>This is the app."
54 " </debugger-app-sk>\n"
55 "</body>\n"
56 "</html>", source.c_str(), source.c_str());
57 return debuggerTemplate;
58
59}
60
joshualitt9a4e1882016-01-27 07:03:29 -080061struct UploadContext {
joshualitt609d9792016-01-27 11:07:23 -080062 SkDynamicMemoryWStream fStream;
63 MHD_PostProcessor* fPostProcessor;
joshualitt9a4e1882016-01-27 07:03:29 -080064 MHD_Connection* connection;
65};
66
67struct Request {
joshualittcdad12f2016-02-08 07:08:21 -080068 Request(SkString rootUrl) : fUploadContext(nullptr), fUrlDataManager(rootUrl) {}
joshualitt9a4e1882016-01-27 07:03:29 -080069 UploadContext* fUploadContext;
joshualitt609d9792016-01-27 11:07:23 -080070 SkAutoTUnref<SkPicture> fPicture;
joshualitt136f5172016-02-02 11:07:39 -080071 SkAutoTUnref<SkDebugCanvas> fDebugCanvas;
joshualittcdad12f2016-02-08 07:08:21 -080072 UrlDataManager fUrlDataManager;
joshualitt9a4e1882016-01-27 07:03:29 -080073};
74
joshualitt609d9792016-01-27 11:07:23 -080075// TODO factor this out into functions, also handle CPU path
joshualitt136f5172016-02-02 11:07:39 -080076SkSurface* setupSurface(GrContextFactory* factory) {
joshualitt609d9792016-01-27 11:07:23 -080077 GrContext* context = factory->get(GrContextFactory::kNative_GLContextType,
78 GrContextFactory::kNone_GLContextOptions);
79 int maxRTSize = context->caps()->maxRenderTargetSize();
80 SkImageInfo info = SkImageInfo::Make(SkTMin(kImageWidth, maxRTSize),
81 SkTMin(kImageHeight, maxRTSize),
82 kN32_SkColorType, kPremul_SkAlphaType);
83 uint32_t flags = 0;
84 SkSurfaceProps props(flags, SkSurfaceProps::kLegacyFontHost_InitType);
joshualitt136f5172016-02-02 11:07:39 -080085 SkSurface* surface = SkSurface::NewRenderTarget(context, SkSurface::kNo_Budgeted, info, 0,
86 &props);
87 SkASSERT(surface);
joshualitt609d9792016-01-27 11:07:23 -080088
89 SkGLContext* gl = factory->getContextInfo(GrContextFactory::kNative_GLContextType,
90 GrContextFactory::kNone_GLContextOptions).fGLContext;
91 gl->makeCurrent();
joshualitt136f5172016-02-02 11:07:39 -080092 return surface;
93}
joshualitt609d9792016-01-27 11:07:23 -080094
joshualitt136f5172016-02-02 11:07:39 -080095SkData* writeCanvasToPng(SkCanvas* canvas) {
joshualitt609d9792016-01-27 11:07:23 -080096 // capture pixels
97 SkBitmap bmp;
98 bmp.setInfo(canvas->imageInfo());
99 if (!canvas->readPixels(&bmp, 0, 0)) {
joshualitt136f5172016-02-02 11:07:39 -0800100 fprintf(stderr, "Can't read pixels\n");
101 return nullptr;
joshualitt609d9792016-01-27 11:07:23 -0800102 }
103
104 // write to png
joshualittcdad12f2016-02-08 07:08:21 -0800105 // TODO encoding to png can be quite slow, we should investigate bmp
joshualitt136f5172016-02-02 11:07:39 -0800106 SkData* png = SkImageEncoder::EncodeData(bmp, SkImageEncoder::kPNG_Type, 100);
107 if (!png) {
108 fprintf(stderr, "Can't encode to png\n");
109 return nullptr;
joshualitt609d9792016-01-27 11:07:23 -0800110 }
joshualitt136f5172016-02-02 11:07:39 -0800111 return png;
112}
113
114SkData* setupAndDrawToCanvasReturnPng(SkDebugCanvas* debugCanvas, int n) {
115 GrContextOptions grContextOpts;
116 SkAutoTDelete<GrContextFactory> factory(new GrContextFactory(grContextOpts));
117 SkAutoTUnref<SkSurface> surface(setupSurface(factory.get()));
118
119 SkASSERT(debugCanvas);
120 SkCanvas* canvas = surface->getCanvas();
121 debugCanvas->drawTo(canvas, n);
122 return writeCanvasToPng(canvas);
joshualitt609d9792016-01-27 11:07:23 -0800123}
124
joshualitt29e5a892016-02-04 06:08:33 -0800125SkSurface* setupCpuSurface() {
126 SkImageInfo info = SkImageInfo::Make(kImageWidth, kImageHeight, kN32_SkColorType,
127 kPremul_SkAlphaType);
128 return SkSurface::NewRaster(info);
129}
130
joshualitt9a4e1882016-01-27 07:03:29 -0800131static const size_t kBufferSize = 1024;
132
133static int process_upload_data(void* cls, enum MHD_ValueKind kind,
134 const char* key, const char* filename,
135 const char* content_type, const char* transfer_encoding,
136 const char* data, uint64_t off, size_t size) {
137 struct UploadContext* uc = reinterpret_cast<UploadContext*>(cls);
138
139 if (0 != size) {
joshualitt609d9792016-01-27 11:07:23 -0800140 uc->fStream.write(data, size);
joshualitt9a4e1882016-01-27 07:03:29 -0800141 }
142 return MHD_YES;
143}
144
joshualitt792345f2016-02-02 13:02:33 -0800145static int SendData(MHD_Connection* connection, const SkData* data, const char* type,
146 bool setContentDisposition = false, const char* dispositionString = nullptr) {
joshualittccfdaa52016-01-27 07:40:29 -0800147 MHD_Response* response = MHD_create_response_from_buffer(data->size(),
148 const_cast<void*>(data->data()),
149 MHD_RESPMEM_MUST_COPY);
joshualitt609d9792016-01-27 11:07:23 -0800150 MHD_add_response_header(response, "Content-Type", type);
joshualittccfdaa52016-01-27 07:40:29 -0800151
joshualitt792345f2016-02-02 13:02:33 -0800152 if (setContentDisposition) {
153 MHD_add_response_header(response, "Content-Disposition", dispositionString);
154 }
155
joshualittccfdaa52016-01-27 07:40:29 -0800156 int ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
157 MHD_destroy_response(response);
158 return ret;
159}
160
joshualitt136f5172016-02-02 11:07:39 -0800161static int SendJSON(MHD_Connection* connection, SkDebugCanvas* debugCanvas, int n) {
joshualitt609d9792016-01-27 11:07:23 -0800162 SkDynamicMemoryWStream stream;
163 SkAutoTUnref<SkJSONCanvas> jsonCanvas(new SkJSONCanvas(kImageWidth, kImageHeight, stream));
joshualitt136f5172016-02-02 11:07:39 -0800164 debugCanvas->drawTo(jsonCanvas, n);
joshualitt609d9792016-01-27 11:07:23 -0800165 jsonCanvas->finish();
166
167 SkAutoTUnref<SkData> data(stream.copyToData());
168 return SendData(connection, data, "application/json");
169}
170
171static int SendTemplate(MHD_Connection* connection, bool redirect = false,
172 const char* redirectUrl = nullptr) {
jcgregorio21ab1202016-01-28 06:24:19 -0800173 SkString debuggerTemplate = generateTemplate(SkString(FLAGS_source[0]));
joshualittccfdaa52016-01-27 07:40:29 -0800174
175 MHD_Response* response = MHD_create_response_from_buffer(
176 debuggerTemplate.size(),
177 (void*) const_cast<char*>(debuggerTemplate.c_str()),
178 MHD_RESPMEM_MUST_COPY);
jcgregorio6a2046e2016-01-28 05:31:31 -0800179 MHD_add_response_header (response, "Access-Control-Allow-Origin", "*");
joshualittccfdaa52016-01-27 07:40:29 -0800180
jcgregorio6f17bc52016-01-27 11:44:38 -0800181 int status = MHD_HTTP_OK;
182
joshualitt609d9792016-01-27 11:07:23 -0800183 if (redirect) {
184 MHD_add_response_header (response, "Location", redirectUrl);
jcgregorio6f17bc52016-01-27 11:44:38 -0800185 status = MHD_HTTP_SEE_OTHER;
joshualitt609d9792016-01-27 11:07:23 -0800186 }
187
jcgregorio6f17bc52016-01-27 11:44:38 -0800188 int ret = MHD_queue_response(connection, status, response);
joshualittccfdaa52016-01-27 07:40:29 -0800189 MHD_destroy_response(response);
190 return ret;
191}
192
joshualitt483b9012016-02-02 07:16:24 -0800193class UrlHandler {
194public:
195 virtual ~UrlHandler() {}
196 virtual bool canHandle(const char* method, const char* url) = 0;
197 virtual int handle(Request* request, MHD_Connection* connection,
joshualitt136f5172016-02-02 11:07:39 -0800198 const char* url, const char* method,
joshualitt483b9012016-02-02 07:16:24 -0800199 const char* upload_data, size_t* upload_data_size) = 0;
200};
joshualittccfdaa52016-01-27 07:40:29 -0800201
joshualitt29e5a892016-02-04 06:08:33 -0800202class CmdHandler : public UrlHandler {
joshualitt483b9012016-02-02 07:16:24 -0800203public:
204 bool canHandle(const char* method, const char* url) override {
joshualitt136f5172016-02-02 11:07:39 -0800205 const char* kBasePath = "/cmd";
206 return 0 == strncmp(url, kBasePath, strlen(kBasePath));
joshualittccfdaa52016-01-27 07:40:29 -0800207 }
208
joshualitt483b9012016-02-02 07:16:24 -0800209 int handle(Request* request, MHD_Connection* connection,
joshualitt136f5172016-02-02 11:07:39 -0800210 const char* url, const char* method,
joshualitt483b9012016-02-02 07:16:24 -0800211 const char* upload_data, size_t* upload_data_size) override {
joshualitt136f5172016-02-02 11:07:39 -0800212 SkTArray<SkString> commands;
213 SkStrSplit(url, "/", &commands);
214
215 if (!request->fPicture.get() || commands.count() > 3) {
216 return MHD_NO;
joshualitt483b9012016-02-02 07:16:24 -0800217 }
joshualitt136f5172016-02-02 11:07:39 -0800218
219 // /cmd or /cmd/N or /cmd/N/[0|1]
220 if (commands.count() == 1 && 0 == strcmp(method, MHD_HTTP_METHOD_GET)) {
221 int n = request->fDebugCanvas->getSize() - 1;
222 return SendJSON(connection, request->fDebugCanvas, n);
223 }
224
225 // /cmd/N, for now only delete supported
226 if (commands.count() == 2 && 0 == strcmp(method, MHD_HTTP_METHOD_DELETE)) {
227 int n;
228 sscanf(commands[1].c_str(), "%d", &n);
229 request->fDebugCanvas->deleteDrawCommandAt(n);
230 return MHD_YES;
231 }
232
233 // /cmd/N/[0|1]
234 if (commands.count() == 3 && 0 == strcmp(method, MHD_HTTP_METHOD_POST)) {
235 int n, toggle;
236 sscanf(commands[1].c_str(), "%d", &n);
237 sscanf(commands[2].c_str(), "%d", &toggle);
238 request->fDebugCanvas->toggleCommand(n, toggle);
239 return MHD_YES;
240 }
241
joshualitt483b9012016-02-02 07:16:24 -0800242 return MHD_NO;
243 }
244};
245
246class ImgHandler : public UrlHandler {
247public:
248 bool canHandle(const char* method, const char* url) override {
249 static const char* kBasePath = "/img";
250 return 0 == strcmp(method, MHD_HTTP_METHOD_GET) &&
251 0 == strncmp(url, kBasePath, strlen(kBasePath));
joshualittccfdaa52016-01-27 07:40:29 -0800252 }
253
joshualitt483b9012016-02-02 07:16:24 -0800254 int handle(Request* request, MHD_Connection* connection,
joshualitt136f5172016-02-02 11:07:39 -0800255 const char* url, const char* method,
joshualitt483b9012016-02-02 07:16:24 -0800256 const char* upload_data, size_t* upload_data_size) override {
joshualitt136f5172016-02-02 11:07:39 -0800257 SkTArray<SkString> commands;
258 SkStrSplit(url, "/", &commands);
259
260 if (!request->fPicture.get() || commands.count() > 2) {
261 return MHD_NO;
joshualitt483b9012016-02-02 07:16:24 -0800262 }
joshualitta341b902016-02-02 07:37:21 -0800263
joshualitt136f5172016-02-02 11:07:39 -0800264 int n;
265 // /img or /img/N
266 if (commands.count() == 1) {
267 n = request->fDebugCanvas->getSize() - 1;
268 } else {
269 sscanf(commands[1].c_str(), "%d", &n);
270 }
271
272 SkAutoTUnref<SkData> data(setupAndDrawToCanvasReturnPng(request->fDebugCanvas, n));
273 return SendData(connection, data, "image/png");
joshualitt483b9012016-02-02 07:16:24 -0800274 }
275};
joshualittccfdaa52016-01-27 07:40:29 -0800276
joshualitt483b9012016-02-02 07:16:24 -0800277class PostHandler : public UrlHandler {
278public:
279 bool canHandle(const char* method, const char* url) override {
280 return 0 == strcmp(method, MHD_HTTP_METHOD_POST) &&
281 0 == strcmp(url, "/new");
joshualittccfdaa52016-01-27 07:40:29 -0800282 }
283
joshualitt483b9012016-02-02 07:16:24 -0800284 int handle(Request* request, MHD_Connection* connection,
joshualitt136f5172016-02-02 11:07:39 -0800285 const char* url, const char* method,
joshualitt483b9012016-02-02 07:16:24 -0800286 const char* upload_data, size_t* upload_data_size) override {
287 UploadContext* uc = request->fUploadContext;
joshualittccfdaa52016-01-27 07:40:29 -0800288
joshualitt483b9012016-02-02 07:16:24 -0800289 // New connection
290 if (!uc) {
291 // TODO make this a method on request
292 uc = new UploadContext;
293 uc->connection = connection;
294 uc->fPostProcessor = MHD_create_post_processor(connection, kBufferSize,
295 &process_upload_data, uc);
296 SkASSERT(uc->fPostProcessor);
joshualitt609d9792016-01-27 11:07:23 -0800297
joshualitt483b9012016-02-02 07:16:24 -0800298 request->fUploadContext = uc;
299 return MHD_YES;
300 }
301
302 // in process upload
303 if (0 != *upload_data_size) {
304 SkASSERT(uc->fPostProcessor);
305 MHD_post_process(uc->fPostProcessor, upload_data, *upload_data_size);
306 *upload_data_size = 0;
307 return MHD_YES;
308 }
309
310 // end of upload
311 MHD_destroy_post_processor(uc->fPostProcessor);
312 uc->fPostProcessor = nullptr;
313
joshualitt136f5172016-02-02 11:07:39 -0800314 // parse picture from stream
315 request->fPicture.reset(
316 SkPicture::CreateFromStream(request->fUploadContext->fStream.detachAsStream()));
317 if (!request->fPicture.get()) {
318 fprintf(stderr, "Could not create picture from stream.\n");
319 return MHD_NO;
joshualitt483b9012016-02-02 07:16:24 -0800320 }
321
joshualitt136f5172016-02-02 11:07:39 -0800322 // pour picture into debug canvas
323 request->fDebugCanvas.reset(new SkDebugCanvas(kImageWidth, kImageHeight));
324 request->fDebugCanvas->drawPicture(request->fPicture);
325
joshualitta341b902016-02-02 07:37:21 -0800326 // clear upload context
327 delete request->fUploadContext;
328 request->fUploadContext = nullptr;
329
joshualitt483b9012016-02-02 07:16:24 -0800330 return SendTemplate(connection, true, "/");
joshualittccfdaa52016-01-27 07:40:29 -0800331 }
joshualitt483b9012016-02-02 07:16:24 -0800332};
333
joshualitt792345f2016-02-02 13:02:33 -0800334class DownloadHandler : public UrlHandler {
335public:
336 bool canHandle(const char* method, const char* url) override {
337 return 0 == strcmp(method, MHD_HTTP_METHOD_GET) &&
338 0 == strcmp(url, "/download");
339 }
340
341 int handle(Request* request, MHD_Connection* connection,
342 const char* url, const char* method,
343 const char* upload_data, size_t* upload_data_size) override {
344 if (!request->fPicture.get()) {
345 return MHD_NO;
346 }
347
348 // TODO move to a function
349 // Playback into picture recorder
350 SkPictureRecorder recorder;
351 SkCanvas* canvas = recorder.beginRecording(kImageWidth, kImageHeight);
352
353 request->fDebugCanvas->draw(canvas);
354
355 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
356
357 SkDynamicMemoryWStream outStream;
358
359 SkAutoTUnref<SkPixelSerializer> serializer(SkImageEncoder::CreatePixelSerializer());
360 picture->serialize(&outStream, serializer);
361
362 SkAutoTUnref<SkData> data(outStream.copyToData());
363
364 // TODO fancier name handling
365 return SendData(connection, data, "application/octet-stream", true,
366 "attachment; filename=something.skp;");
367 }
368};
369
joshualitt29e5a892016-02-04 06:08:33 -0800370class InfoHandler : public UrlHandler {
371public:
372 bool canHandle(const char* method, const char* url) override {
373 const char* kBaseName = "/info";
374 return 0 == strcmp(method, MHD_HTTP_METHOD_GET) &&
375 0 == strncmp(url, kBaseName, strlen(kBaseName));
376 }
377
378 int handle(Request* request, MHD_Connection* connection,
379 const char* url, const char* method,
380 const char* upload_data, size_t* upload_data_size) override {
381 SkTArray<SkString> commands;
382 SkStrSplit(url, "/", &commands);
383
384 if (!request->fPicture.get() || commands.count() > 2) {
385 return MHD_NO;
386 }
387
388 // drawTo
389 SkAutoTUnref<SkSurface> surface(setupCpuSurface());
390 SkCanvas* canvas = surface->getCanvas();
391
392 int n;
393 // /info or /info/N
394 if (commands.count() == 1) {
395 n = request->fDebugCanvas->getSize() - 1;
396 } else {
397 sscanf(commands[1].c_str(), "%d", &n);
398 }
399
400 // TODO this is really slow and we should cache the matrix and clip
401 request->fDebugCanvas->drawTo(canvas, n);
402
403 // make some json
404 SkMatrix vm = request->fDebugCanvas->getCurrentMatrix();
405 SkIRect clip = request->fDebugCanvas->getCurrentClip();
406 Json::Value info(Json::objectValue);
407 info["ViewMatrix"] = SkJSONCanvas::MakeMatrix(vm);
408 info["ClipRect"] = SkJSONCanvas::MakeIRect(clip);
409
410 std::string json = Json::FastWriter().write(info);
411
412 // We don't want the null terminator so strlen is correct
413 SkAutoTUnref<SkData> data(SkData::NewWithCopy(json.c_str(), strlen(json.c_str())));
414 return SendData(connection, data, "application/json");
415 }
416};
417
joshualittcdad12f2016-02-08 07:08:21 -0800418class DataHandler : public UrlHandler {
419public:
420 bool canHandle(const char* method, const char* url) override {
421 static const char* kBaseUrl = "/data";
422 return 0 == strcmp(method, MHD_HTTP_METHOD_GET) &&
423 0 == strncmp(url, kBaseUrl, strlen(kBaseUrl));
424 }
425
426 int handle(Request* request, MHD_Connection* connection,
427 const char* url, const char* method,
428 const char* upload_data, size_t* upload_data_size) override {
429 SkTArray<SkString> commands;
430 SkStrSplit(url, "/", &commands);
431
432 if (!request->fPicture.get() || commands.count() != 2) {
433 return MHD_NO;
434 }
435
436 SkAutoTUnref<UrlDataManager::UrlData> urlData(
437 SkRef(request->fUrlDataManager.getDataFromUrl(SkString(url))));
438
439 if (urlData) {
440 return SendData(connection, urlData->fData.get(), urlData->fContentType.c_str());
441 }
442 return MHD_NO;
443 }
444};
joshualitt29e5a892016-02-04 06:08:33 -0800445
joshualitt483b9012016-02-02 07:16:24 -0800446class RootHandler : public UrlHandler {
447public:
448 bool canHandle(const char* method, const char* url) override {
449 return 0 == strcmp(method, MHD_HTTP_METHOD_GET) &&
450 0 == strcmp(url, "/");
451 }
452
453 int handle(Request* request, MHD_Connection* connection,
joshualitt136f5172016-02-02 11:07:39 -0800454 const char* url, const char* method,
joshualitt483b9012016-02-02 07:16:24 -0800455 const char* upload_data, size_t* upload_data_size) override {
456 return SendTemplate(connection);
457 }
458};
joshualittccfdaa52016-01-27 07:40:29 -0800459
460class UrlManager {
461public:
462 UrlManager() {
463 // Register handlers
joshualitt483b9012016-02-02 07:16:24 -0800464 fHandlers.push_back(new RootHandler);
465 fHandlers.push_back(new PostHandler);
466 fHandlers.push_back(new ImgHandler);
joshualitt29e5a892016-02-04 06:08:33 -0800467 fHandlers.push_back(new CmdHandler);
joshualitt483b9012016-02-02 07:16:24 -0800468 fHandlers.push_back(new InfoHandler);
joshualitt792345f2016-02-02 13:02:33 -0800469 fHandlers.push_back(new DownloadHandler);
joshualittcdad12f2016-02-08 07:08:21 -0800470 fHandlers.push_back(new DataHandler);
joshualitt483b9012016-02-02 07:16:24 -0800471 }
472
473 ~UrlManager() {
474 for (int i = 0; i < fHandlers.count(); i++) { delete fHandlers[i]; }
joshualittccfdaa52016-01-27 07:40:29 -0800475 }
476
477 // This is clearly not efficient for a large number of urls and handlers
478 int invoke(Request* request, MHD_Connection* connection, const char* url, const char* method,
479 const char* upload_data, size_t* upload_data_size) const {
480 for (int i = 0; i < fHandlers.count(); i++) {
joshualitt483b9012016-02-02 07:16:24 -0800481 if (fHandlers[i]->canHandle(method, url)) {
joshualitt136f5172016-02-02 11:07:39 -0800482 return fHandlers[i]->handle(request, connection, url, method, upload_data,
483 upload_data_size);
joshualittccfdaa52016-01-27 07:40:29 -0800484 }
485 }
486 return MHD_NO;
487 }
488
489private:
joshualitt483b9012016-02-02 07:16:24 -0800490 SkTArray<UrlHandler*> fHandlers;
joshualittccfdaa52016-01-27 07:40:29 -0800491};
492
493const UrlManager kUrlManager;
494
joshualitt7f6a1e02016-01-22 11:21:43 -0800495int answer_to_connection(void* cls, struct MHD_Connection* connection,
496 const char* url, const char* method, const char* version,
497 const char* upload_data, size_t* upload_data_size,
498 void** con_cls) {
joshualitt9a4e1882016-01-27 07:03:29 -0800499 SkDebugf("New %s request for %s using version %s\n", method, url, version);
joshualitt7f6a1e02016-01-22 11:21:43 -0800500
joshualitt9a4e1882016-01-27 07:03:29 -0800501 Request* request = reinterpret_cast<Request*>(cls);
joshualitt483b9012016-02-02 07:16:24 -0800502 int result = kUrlManager.invoke(request, connection, url, method, upload_data,
503 upload_data_size);
504 if (MHD_NO == result) {
505 fprintf(stderr, "Invalid method and / or url: %s %s)\n", method, url);
506 }
507 return result;
joshualitt7f6a1e02016-01-22 11:21:43 -0800508}
509
510int skiaserve_main() {
joshualittcdad12f2016-02-08 07:08:21 -0800511 Request request(SkString("/data")); // This simple server has one request
joshualitt7f6a1e02016-01-22 11:21:43 -0800512 struct MHD_Daemon* daemon;
jcgregorio21ab1202016-01-28 06:24:19 -0800513 // TODO Add option to bind this strictly to an address, e.g. localhost, for security.
514 daemon = MHD_start_daemon(MHD_USE_SELECT_INTERNALLY, FLAGS_port, nullptr, nullptr,
joshualitt9a4e1882016-01-27 07:03:29 -0800515 &answer_to_connection, &request,
516 MHD_OPTION_END);
joshualitt7f6a1e02016-01-22 11:21:43 -0800517 if (NULL == daemon) {
518 return 1;
519 }
520
521 getchar();
522 MHD_stop_daemon(daemon);
523 return 0;
524}
525
526#if !defined SK_BUILD_FOR_IOS
527int main(int argc, char** argv) {
528 SkCommandLineFlags::Parse(argc, argv);
529 return skiaserve_main();
530}
531#endif