blob: 09819cf22d0b3aad4fc292fd6e8ad12a9ef1b2ec [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;
ethannicholasde8e54c2016-02-12 10:09:34 -080073 SkAutoTDelete<GrContextFactory> fContextFactory;
74 SkAutoTUnref<SkSurface> fSurface;
joshualittcdad12f2016-02-08 07:08:21 -080075 UrlDataManager fUrlDataManager;
joshualitt9a4e1882016-01-27 07:03:29 -080076};
77
joshualitt609d9792016-01-27 11:07:23 -080078// TODO factor this out into functions, also handle CPU path
joshualitt136f5172016-02-02 11:07:39 -080079SkSurface* setupSurface(GrContextFactory* factory) {
joshualitt609d9792016-01-27 11:07:23 -080080 GrContext* context = factory->get(GrContextFactory::kNative_GLContextType,
81 GrContextFactory::kNone_GLContextOptions);
82 int maxRTSize = context->caps()->maxRenderTargetSize();
83 SkImageInfo info = SkImageInfo::Make(SkTMin(kImageWidth, maxRTSize),
84 SkTMin(kImageHeight, maxRTSize),
85 kN32_SkColorType, kPremul_SkAlphaType);
86 uint32_t flags = 0;
87 SkSurfaceProps props(flags, SkSurfaceProps::kLegacyFontHost_InitType);
joshualitt136f5172016-02-02 11:07:39 -080088 SkSurface* surface = SkSurface::NewRenderTarget(context, SkSurface::kNo_Budgeted, info, 0,
89 &props);
90 SkASSERT(surface);
joshualitt609d9792016-01-27 11:07:23 -080091
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
ethannicholasde8e54c2016-02-12 10:09:34 -0800114SkData* setupAndDrawToCanvasReturnPng(Request* request, int n) {
115 GrContextFactory* factory = request->fContextFactory;
116 SkGLContext* gl = factory->getContextInfo(GrContextFactory::kNative_GLContextType,
117 GrContextFactory::kNone_GLContextOptions).fGLContext;
118 gl->makeCurrent();
119 SkASSERT(request->fDebugCanvas);
120 SkCanvas* target = request->fSurface->getCanvas();
121 request->fDebugCanvas->drawTo(target, n);
122 return writeCanvasToPng(target);
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
jcgregorio12d47ce2016-02-10 14:10:37 -0800145// SendOK just sends an empty response with a 200 OK status code.
146static int SendOK(MHD_Connection* connection) {
147 const char* data = "";
148
149 MHD_Response* response = MHD_create_response_from_buffer(strlen(data),
150 (void*)data,
151 MHD_RESPMEM_PERSISTENT);
152 int ret = MHD_queue_response(connection, 200, response);
153 MHD_destroy_response(response);
154 return ret;
155}
156
joshualitt792345f2016-02-02 13:02:33 -0800157static int SendData(MHD_Connection* connection, const SkData* data, const char* type,
158 bool setContentDisposition = false, const char* dispositionString = nullptr) {
joshualittccfdaa52016-01-27 07:40:29 -0800159 MHD_Response* response = MHD_create_response_from_buffer(data->size(),
160 const_cast<void*>(data->data()),
161 MHD_RESPMEM_MUST_COPY);
joshualitt609d9792016-01-27 11:07:23 -0800162 MHD_add_response_header(response, "Content-Type", type);
joshualittccfdaa52016-01-27 07:40:29 -0800163
joshualitt792345f2016-02-02 13:02:33 -0800164 if (setContentDisposition) {
165 MHD_add_response_header(response, "Content-Disposition", dispositionString);
166 }
167
joshualittccfdaa52016-01-27 07:40:29 -0800168 int ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
169 MHD_destroy_response(response);
170 return ret;
171}
172
joshualittdb6a2542016-02-11 07:09:51 -0800173static int SendJSON(MHD_Connection* connection, SkDebugCanvas* debugCanvas,
174 UrlDataManager* urlDataManager, int n) {
175 Json::Value root = debugCanvas->toJSON(*urlDataManager, n);
joshualitt609d9792016-01-27 11:07:23 -0800176 SkDynamicMemoryWStream stream;
joshualittdb6a2542016-02-11 07:09:51 -0800177 stream.writeText(Json::FastWriter().write(root).c_str());
joshualitt609d9792016-01-27 11:07:23 -0800178
179 SkAutoTUnref<SkData> data(stream.copyToData());
180 return SendData(connection, data, "application/json");
181}
182
183static int SendTemplate(MHD_Connection* connection, bool redirect = false,
184 const char* redirectUrl = nullptr) {
jcgregorio21ab1202016-01-28 06:24:19 -0800185 SkString debuggerTemplate = generateTemplate(SkString(FLAGS_source[0]));
joshualittccfdaa52016-01-27 07:40:29 -0800186
187 MHD_Response* response = MHD_create_response_from_buffer(
188 debuggerTemplate.size(),
189 (void*) const_cast<char*>(debuggerTemplate.c_str()),
190 MHD_RESPMEM_MUST_COPY);
jcgregorio6a2046e2016-01-28 05:31:31 -0800191 MHD_add_response_header (response, "Access-Control-Allow-Origin", "*");
joshualittccfdaa52016-01-27 07:40:29 -0800192
jcgregorio6f17bc52016-01-27 11:44:38 -0800193 int status = MHD_HTTP_OK;
194
joshualitt609d9792016-01-27 11:07:23 -0800195 if (redirect) {
196 MHD_add_response_header (response, "Location", redirectUrl);
jcgregorio6f17bc52016-01-27 11:44:38 -0800197 status = MHD_HTTP_SEE_OTHER;
joshualitt609d9792016-01-27 11:07:23 -0800198 }
199
jcgregorio6f17bc52016-01-27 11:44:38 -0800200 int ret = MHD_queue_response(connection, status, response);
joshualittccfdaa52016-01-27 07:40:29 -0800201 MHD_destroy_response(response);
202 return ret;
203}
204
joshualitt483b9012016-02-02 07:16:24 -0800205class UrlHandler {
206public:
207 virtual ~UrlHandler() {}
208 virtual bool canHandle(const char* method, const char* url) = 0;
209 virtual 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) = 0;
212};
joshualittccfdaa52016-01-27 07:40:29 -0800213
joshualitt29e5a892016-02-04 06:08:33 -0800214class CmdHandler : public UrlHandler {
joshualitt483b9012016-02-02 07:16:24 -0800215public:
216 bool canHandle(const char* method, const char* url) override {
joshualitt136f5172016-02-02 11:07:39 -0800217 const char* kBasePath = "/cmd";
218 return 0 == strncmp(url, kBasePath, strlen(kBasePath));
joshualittccfdaa52016-01-27 07:40:29 -0800219 }
220
joshualitt483b9012016-02-02 07:16:24 -0800221 int handle(Request* request, MHD_Connection* connection,
joshualitt136f5172016-02-02 11:07:39 -0800222 const char* url, const char* method,
joshualitt483b9012016-02-02 07:16:24 -0800223 const char* upload_data, size_t* upload_data_size) override {
joshualitt136f5172016-02-02 11:07:39 -0800224 SkTArray<SkString> commands;
225 SkStrSplit(url, "/", &commands);
226
227 if (!request->fPicture.get() || commands.count() > 3) {
228 return MHD_NO;
joshualitt483b9012016-02-02 07:16:24 -0800229 }
joshualitt136f5172016-02-02 11:07:39 -0800230
joshualittdb6a2542016-02-11 07:09:51 -0800231 // /cmd or /cmd/N
232 if (0 == strcmp(method, MHD_HTTP_METHOD_GET)) {
233 int n;
234 if (commands.count() == 1) {
ethannicholas0a0520a2016-02-12 12:06:53 -0800235 n = request->fDebugCanvas->getSize() - 1;
joshualittdb6a2542016-02-11 07:09:51 -0800236 } else {
237 sscanf(commands[1].c_str(), "%d", &n);
238 }
239 return SendJSON(connection, request->fDebugCanvas, &request->fUrlDataManager, n);
joshualitt136f5172016-02-02 11:07:39 -0800240 }
241
242 // /cmd/N, for now only delete supported
243 if (commands.count() == 2 && 0 == strcmp(method, MHD_HTTP_METHOD_DELETE)) {
244 int n;
245 sscanf(commands[1].c_str(), "%d", &n);
246 request->fDebugCanvas->deleteDrawCommandAt(n);
jcgregorio12d47ce2016-02-10 14:10:37 -0800247 return SendOK(connection);
joshualitt136f5172016-02-02 11:07:39 -0800248 }
249
250 // /cmd/N/[0|1]
251 if (commands.count() == 3 && 0 == strcmp(method, MHD_HTTP_METHOD_POST)) {
252 int n, toggle;
253 sscanf(commands[1].c_str(), "%d", &n);
254 sscanf(commands[2].c_str(), "%d", &toggle);
255 request->fDebugCanvas->toggleCommand(n, toggle);
jcgregorio12d47ce2016-02-10 14:10:37 -0800256 return SendOK(connection);
joshualitt136f5172016-02-02 11:07:39 -0800257 }
258
joshualitt483b9012016-02-02 07:16:24 -0800259 return MHD_NO;
260 }
261};
262
263class ImgHandler : public UrlHandler {
264public:
265 bool canHandle(const char* method, const char* url) override {
266 static const char* kBasePath = "/img";
267 return 0 == strcmp(method, MHD_HTTP_METHOD_GET) &&
268 0 == strncmp(url, kBasePath, strlen(kBasePath));
joshualittccfdaa52016-01-27 07:40:29 -0800269 }
270
joshualitt483b9012016-02-02 07:16:24 -0800271 int handle(Request* request, MHD_Connection* connection,
joshualitt136f5172016-02-02 11:07:39 -0800272 const char* url, const char* method,
joshualitt483b9012016-02-02 07:16:24 -0800273 const char* upload_data, size_t* upload_data_size) override {
joshualitt136f5172016-02-02 11:07:39 -0800274 SkTArray<SkString> commands;
275 SkStrSplit(url, "/", &commands);
276
277 if (!request->fPicture.get() || commands.count() > 2) {
278 return MHD_NO;
joshualitt483b9012016-02-02 07:16:24 -0800279 }
joshualitta341b902016-02-02 07:37:21 -0800280
joshualitt136f5172016-02-02 11:07:39 -0800281 int n;
282 // /img or /img/N
283 if (commands.count() == 1) {
284 n = request->fDebugCanvas->getSize() - 1;
285 } else {
286 sscanf(commands[1].c_str(), "%d", &n);
287 }
288
ethannicholasde8e54c2016-02-12 10:09:34 -0800289 SkAutoTUnref<SkData> data(setupAndDrawToCanvasReturnPng(request, n));
joshualitt136f5172016-02-02 11:07:39 -0800290 return SendData(connection, data, "image/png");
joshualitt483b9012016-02-02 07:16:24 -0800291 }
292};
joshualittccfdaa52016-01-27 07:40:29 -0800293
ethannicholas0a0520a2016-02-12 12:06:53 -0800294/**
295 Updates the clip visualization alpha. On all subsequent /img requests, the clip will be drawn in
296 black with the specified alpha. 0 = no visible clip, 255 = fully opaque clip.
297 */
298class ClipAlphaHandler : public UrlHandler {
299public:
300 bool canHandle(const char* method, const char* url) override {
301 static const char* kBasePath = "/clipAlpha/";
jcgregorio3341d422016-02-16 10:31:07 -0800302 return 0 == strcmp(method, MHD_HTTP_METHOD_POST) &&
ethannicholas0a0520a2016-02-12 12:06:53 -0800303 0 == strncmp(url, kBasePath, strlen(kBasePath));
304 }
305
306 int handle(Request* request, MHD_Connection* connection,
307 const char* url, const char* method,
308 const char* upload_data, size_t* upload_data_size) override {
309 SkTArray<SkString> commands;
310 SkStrSplit(url, "/", &commands);
311
312 if (!request->fPicture.get() || commands.count() != 2) {
313 return MHD_NO;
314 }
315
316 int alpha;
317 sscanf(commands[1].c_str(), "%d", &alpha);
318
319 request->fDebugCanvas->setClipVizColor(SkColorSetARGB(alpha, 0, 0, 0));
320 return SendOK(connection);
321 }
322};
323
joshualitt483b9012016-02-02 07:16:24 -0800324class PostHandler : public UrlHandler {
325public:
326 bool canHandle(const char* method, const char* url) override {
327 return 0 == strcmp(method, MHD_HTTP_METHOD_POST) &&
328 0 == strcmp(url, "/new");
joshualittccfdaa52016-01-27 07:40:29 -0800329 }
330
joshualitt483b9012016-02-02 07:16:24 -0800331 int handle(Request* request, MHD_Connection* connection,
joshualitt136f5172016-02-02 11:07:39 -0800332 const char* url, const char* method,
joshualitt483b9012016-02-02 07:16:24 -0800333 const char* upload_data, size_t* upload_data_size) override {
334 UploadContext* uc = request->fUploadContext;
joshualittccfdaa52016-01-27 07:40:29 -0800335
joshualitt483b9012016-02-02 07:16:24 -0800336 // New connection
337 if (!uc) {
338 // TODO make this a method on request
339 uc = new UploadContext;
340 uc->connection = connection;
341 uc->fPostProcessor = MHD_create_post_processor(connection, kBufferSize,
342 &process_upload_data, uc);
343 SkASSERT(uc->fPostProcessor);
joshualitt609d9792016-01-27 11:07:23 -0800344
joshualitt483b9012016-02-02 07:16:24 -0800345 request->fUploadContext = uc;
346 return MHD_YES;
347 }
348
349 // in process upload
350 if (0 != *upload_data_size) {
351 SkASSERT(uc->fPostProcessor);
352 MHD_post_process(uc->fPostProcessor, upload_data, *upload_data_size);
353 *upload_data_size = 0;
354 return MHD_YES;
355 }
356
357 // end of upload
358 MHD_destroy_post_processor(uc->fPostProcessor);
359 uc->fPostProcessor = nullptr;
360
joshualitt136f5172016-02-02 11:07:39 -0800361 // parse picture from stream
362 request->fPicture.reset(
363 SkPicture::CreateFromStream(request->fUploadContext->fStream.detachAsStream()));
364 if (!request->fPicture.get()) {
365 fprintf(stderr, "Could not create picture from stream.\n");
366 return MHD_NO;
joshualitt483b9012016-02-02 07:16:24 -0800367 }
368
ethannicholasde8e54c2016-02-12 10:09:34 -0800369 // create surface
370 GrContextOptions grContextOpts;
371 request->fContextFactory.reset(new GrContextFactory(grContextOpts));
372 request->fSurface.reset(setupSurface(request->fContextFactory.get()));
373
joshualitt136f5172016-02-02 11:07:39 -0800374 // pour picture into debug canvas
375 request->fDebugCanvas.reset(new SkDebugCanvas(kImageWidth, kImageHeight));
376 request->fDebugCanvas->drawPicture(request->fPicture);
377
joshualitta341b902016-02-02 07:37:21 -0800378 // clear upload context
379 delete request->fUploadContext;
380 request->fUploadContext = nullptr;
381
joshualitt483b9012016-02-02 07:16:24 -0800382 return SendTemplate(connection, true, "/");
joshualittccfdaa52016-01-27 07:40:29 -0800383 }
joshualitt483b9012016-02-02 07:16:24 -0800384};
385
joshualitt792345f2016-02-02 13:02:33 -0800386class DownloadHandler : public UrlHandler {
387public:
388 bool canHandle(const char* method, const char* url) override {
389 return 0 == strcmp(method, MHD_HTTP_METHOD_GET) &&
390 0 == strcmp(url, "/download");
391 }
392
393 int handle(Request* request, MHD_Connection* connection,
394 const char* url, const char* method,
395 const char* upload_data, size_t* upload_data_size) override {
396 if (!request->fPicture.get()) {
397 return MHD_NO;
398 }
399
400 // TODO move to a function
401 // Playback into picture recorder
402 SkPictureRecorder recorder;
403 SkCanvas* canvas = recorder.beginRecording(kImageWidth, kImageHeight);
404
405 request->fDebugCanvas->draw(canvas);
406
407 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
408
409 SkDynamicMemoryWStream outStream;
410
411 SkAutoTUnref<SkPixelSerializer> serializer(SkImageEncoder::CreatePixelSerializer());
412 picture->serialize(&outStream, serializer);
413
414 SkAutoTUnref<SkData> data(outStream.copyToData());
415
416 // TODO fancier name handling
417 return SendData(connection, data, "application/octet-stream", true,
418 "attachment; filename=something.skp;");
419 }
420};
421
joshualitt29e5a892016-02-04 06:08:33 -0800422class InfoHandler : public UrlHandler {
423public:
424 bool canHandle(const char* method, const char* url) override {
425 const char* kBaseName = "/info";
426 return 0 == strcmp(method, MHD_HTTP_METHOD_GET) &&
427 0 == strncmp(url, kBaseName, strlen(kBaseName));
428 }
429
430 int handle(Request* request, MHD_Connection* connection,
431 const char* url, const char* method,
432 const char* upload_data, size_t* upload_data_size) override {
433 SkTArray<SkString> commands;
434 SkStrSplit(url, "/", &commands);
435
436 if (!request->fPicture.get() || commands.count() > 2) {
437 return MHD_NO;
438 }
439
440 // drawTo
441 SkAutoTUnref<SkSurface> surface(setupCpuSurface());
442 SkCanvas* canvas = surface->getCanvas();
443
444 int n;
445 // /info or /info/N
446 if (commands.count() == 1) {
447 n = request->fDebugCanvas->getSize() - 1;
448 } else {
449 sscanf(commands[1].c_str(), "%d", &n);
450 }
451
452 // TODO this is really slow and we should cache the matrix and clip
453 request->fDebugCanvas->drawTo(canvas, n);
454
455 // make some json
456 SkMatrix vm = request->fDebugCanvas->getCurrentMatrix();
457 SkIRect clip = request->fDebugCanvas->getCurrentClip();
458 Json::Value info(Json::objectValue);
459 info["ViewMatrix"] = SkJSONCanvas::MakeMatrix(vm);
460 info["ClipRect"] = SkJSONCanvas::MakeIRect(clip);
461
462 std::string json = Json::FastWriter().write(info);
463
464 // We don't want the null terminator so strlen is correct
465 SkAutoTUnref<SkData> data(SkData::NewWithCopy(json.c_str(), strlen(json.c_str())));
466 return SendData(connection, data, "application/json");
467 }
468};
469
joshualittcdad12f2016-02-08 07:08:21 -0800470class DataHandler : public UrlHandler {
471public:
472 bool canHandle(const char* method, const char* url) override {
473 static const char* kBaseUrl = "/data";
474 return 0 == strcmp(method, MHD_HTTP_METHOD_GET) &&
475 0 == strncmp(url, kBaseUrl, strlen(kBaseUrl));
476 }
477
478 int handle(Request* request, MHD_Connection* connection,
479 const char* url, const char* method,
480 const char* upload_data, size_t* upload_data_size) override {
481 SkTArray<SkString> commands;
482 SkStrSplit(url, "/", &commands);
483
484 if (!request->fPicture.get() || commands.count() != 2) {
485 return MHD_NO;
486 }
487
488 SkAutoTUnref<UrlDataManager::UrlData> urlData(
489 SkRef(request->fUrlDataManager.getDataFromUrl(SkString(url))));
490
491 if (urlData) {
492 return SendData(connection, urlData->fData.get(), urlData->fContentType.c_str());
493 }
494 return MHD_NO;
495 }
496};
joshualitt29e5a892016-02-04 06:08:33 -0800497
joshualitt873d6242016-02-08 13:57:44 -0800498class FaviconHandler : public UrlHandler {
499public:
500 bool canHandle(const char* method, const char* url) override {
501 return 0 == strcmp(method, MHD_HTTP_METHOD_GET) &&
502 0 == strcmp(url, "/favicon.ico");
503 }
504
505 int handle(Request* request, MHD_Connection* connection,
506 const char* url, const char* method,
507 const char* upload_data, size_t* upload_data_size) override {
508 SkString dir(FLAGS_faviconDir[0]);
509 dir.append("/favicon.ico");
510 FILE* ico = fopen(dir.c_str(), "r");
511
512 SkAutoTUnref<SkData> data(SkData::NewFromFILE(ico));
513 int ret = SendData(connection, data, "image/vnd.microsoft.icon");
514 fclose(ico);
515 return ret;
516 }
517};
518
519
joshualitt483b9012016-02-02 07:16:24 -0800520class RootHandler : public UrlHandler {
521public:
522 bool canHandle(const char* method, const char* url) override {
523 return 0 == strcmp(method, MHD_HTTP_METHOD_GET) &&
524 0 == strcmp(url, "/");
525 }
526
527 int handle(Request* request, MHD_Connection* connection,
joshualitt136f5172016-02-02 11:07:39 -0800528 const char* url, const char* method,
joshualitt483b9012016-02-02 07:16:24 -0800529 const char* upload_data, size_t* upload_data_size) override {
530 return SendTemplate(connection);
531 }
532};
joshualittccfdaa52016-01-27 07:40:29 -0800533
534class UrlManager {
535public:
536 UrlManager() {
537 // Register handlers
joshualitt483b9012016-02-02 07:16:24 -0800538 fHandlers.push_back(new RootHandler);
539 fHandlers.push_back(new PostHandler);
540 fHandlers.push_back(new ImgHandler);
ethannicholas0a0520a2016-02-12 12:06:53 -0800541 fHandlers.push_back(new ClipAlphaHandler);
joshualitt29e5a892016-02-04 06:08:33 -0800542 fHandlers.push_back(new CmdHandler);
joshualitt483b9012016-02-02 07:16:24 -0800543 fHandlers.push_back(new InfoHandler);
joshualitt792345f2016-02-02 13:02:33 -0800544 fHandlers.push_back(new DownloadHandler);
joshualittcdad12f2016-02-08 07:08:21 -0800545 fHandlers.push_back(new DataHandler);
joshualitt873d6242016-02-08 13:57:44 -0800546 fHandlers.push_back(new FaviconHandler);
joshualitt483b9012016-02-02 07:16:24 -0800547 }
548
549 ~UrlManager() {
550 for (int i = 0; i < fHandlers.count(); i++) { delete fHandlers[i]; }
joshualittccfdaa52016-01-27 07:40:29 -0800551 }
552
553 // This is clearly not efficient for a large number of urls and handlers
554 int invoke(Request* request, MHD_Connection* connection, const char* url, const char* method,
555 const char* upload_data, size_t* upload_data_size) const {
556 for (int i = 0; i < fHandlers.count(); i++) {
joshualitt483b9012016-02-02 07:16:24 -0800557 if (fHandlers[i]->canHandle(method, url)) {
joshualitt136f5172016-02-02 11:07:39 -0800558 return fHandlers[i]->handle(request, connection, url, method, upload_data,
559 upload_data_size);
joshualittccfdaa52016-01-27 07:40:29 -0800560 }
561 }
562 return MHD_NO;
563 }
564
565private:
joshualitt483b9012016-02-02 07:16:24 -0800566 SkTArray<UrlHandler*> fHandlers;
joshualittccfdaa52016-01-27 07:40:29 -0800567};
568
569const UrlManager kUrlManager;
570
joshualitt7f6a1e02016-01-22 11:21:43 -0800571int answer_to_connection(void* cls, struct MHD_Connection* connection,
572 const char* url, const char* method, const char* version,
573 const char* upload_data, size_t* upload_data_size,
574 void** con_cls) {
joshualitt9a4e1882016-01-27 07:03:29 -0800575 SkDebugf("New %s request for %s using version %s\n", method, url, version);
joshualitt7f6a1e02016-01-22 11:21:43 -0800576
joshualitt9a4e1882016-01-27 07:03:29 -0800577 Request* request = reinterpret_cast<Request*>(cls);
joshualitt483b9012016-02-02 07:16:24 -0800578 int result = kUrlManager.invoke(request, connection, url, method, upload_data,
579 upload_data_size);
580 if (MHD_NO == result) {
joshualitt873d6242016-02-08 13:57:44 -0800581 fprintf(stderr, "Invalid method and / or url: %s %s\n", method, url);
joshualitt483b9012016-02-02 07:16:24 -0800582 }
583 return result;
joshualitt7f6a1e02016-01-22 11:21:43 -0800584}
585
586int skiaserve_main() {
joshualittcdad12f2016-02-08 07:08:21 -0800587 Request request(SkString("/data")); // This simple server has one request
joshualitt7f6a1e02016-01-22 11:21:43 -0800588 struct MHD_Daemon* daemon;
jcgregorio21ab1202016-01-28 06:24:19 -0800589 // TODO Add option to bind this strictly to an address, e.g. localhost, for security.
590 daemon = MHD_start_daemon(MHD_USE_SELECT_INTERNALLY, FLAGS_port, nullptr, nullptr,
joshualitt9a4e1882016-01-27 07:03:29 -0800591 &answer_to_connection, &request,
592 MHD_OPTION_END);
joshualitt7f6a1e02016-01-22 11:21:43 -0800593 if (NULL == daemon) {
594 return 1;
595 }
596
597 getchar();
598 MHD_stop_daemon(daemon);
599 return 0;
600}
601
602#if !defined SK_BUILD_FOR_IOS
603int main(int argc, char** argv) {
604 SkCommandLineFlags::Parse(argc, argv);
605 return skiaserve_main();
606}
607#endif