blob: a76e2aa537ca33063423292b30164de288354836 [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"
joshualitt24dd6872016-02-25 08:37:54 -080010
11#include "Request.h"
12
joshualitt7f6a1e02016-01-22 11:21:43 -080013#include "SkCanvas.h"
14#include "SkCommandLineFlags.h"
joshualitt609d9792016-01-27 11:07:23 -080015#include "SkJSONCanvas.h"
joshualitt792345f2016-02-02 13:02:33 -080016#include "SkPictureRecorder.h"
17#include "SkPixelSerializer.h"
joshualittcdad12f2016-02-08 07:08:21 -080018
joshualitt7f6a1e02016-01-22 11:21:43 -080019#include <sys/socket.h>
joshualitt7f6a1e02016-01-22 11:21:43 -080020#include <microhttpd.h>
21
22// To get image decoders linked in we have to do the below magic
23#include "SkForceLinking.h"
24#include "SkImageDecoder.h"
25__SK_FORCE_IMAGE_DECODER_LINKING;
26
jcgregorio21ab1202016-01-28 06:24:19 -080027DEFINE_string(source, "https://debugger.skia.org", "Where to load the web UI from.");
28DEFINE_int32(port, 8888, "The port to listen on.");
joshualitt7f6a1e02016-01-22 11:21:43 -080029
joshualitt7f6a1e02016-01-22 11:21:43 -080030SkString generateTemplate(SkString source) {
31 SkString debuggerTemplate;
32 debuggerTemplate.appendf(
33 "<!DOCTYPE html>\n"
34 "<html>\n"
35 "<head>\n"
36 " <title>SkDebugger</title>\n"
37 " <meta charset=\"utf-8\" />\n"
38 " <meta http-equiv=\"X-UA-Compatible\" content=\"IE=egde,chrome=1\">\n"
39 " <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n"
40 " <script src=\"%s/res/js/core.js\" type=\"text/javascript\" charset=\"utf-8\"></script>\n"
jcgregorio50fe38e2016-02-25 07:02:13 -080041 " <link href=\"%s/res/vul/elements.html\" rel=\"import\" />\n"
42 " <link rel='shortcut icon' href='https://debugger.skia.org/res/img/favicon.ico' type='image/x-icon'/ >"
joshualitt7f6a1e02016-01-22 11:21:43 -080043 "</head>\n"
44 "<body class=\"fullbleed layout vertical\">\n"
45 " <debugger-app-sk>This is the app."
46 " </debugger-app-sk>\n"
47 "</body>\n"
48 "</html>", source.c_str(), source.c_str());
49 return debuggerTemplate;
50
51}
52
joshualitt9a4e1882016-01-27 07:03:29 -080053static const size_t kBufferSize = 1024;
54
55static int process_upload_data(void* cls, enum MHD_ValueKind kind,
56 const char* key, const char* filename,
57 const char* content_type, const char* transfer_encoding,
58 const char* data, uint64_t off, size_t size) {
59 struct UploadContext* uc = reinterpret_cast<UploadContext*>(cls);
60
61 if (0 != size) {
joshualitt609d9792016-01-27 11:07:23 -080062 uc->fStream.write(data, size);
joshualitt9a4e1882016-01-27 07:03:29 -080063 }
64 return MHD_YES;
65}
66
jcgregorio12d47ce2016-02-10 14:10:37 -080067// SendOK just sends an empty response with a 200 OK status code.
68static int SendOK(MHD_Connection* connection) {
69 const char* data = "";
70
71 MHD_Response* response = MHD_create_response_from_buffer(strlen(data),
72 (void*)data,
73 MHD_RESPMEM_PERSISTENT);
74 int ret = MHD_queue_response(connection, 200, response);
75 MHD_destroy_response(response);
76 return ret;
77}
78
ethannicholas85fca852016-02-19 08:40:59 -080079static int SendError(MHD_Connection* connection, const char* msg) {
80 MHD_Response* response = MHD_create_response_from_buffer(strlen(msg),
81 (void*) msg,
82 MHD_RESPMEM_PERSISTENT);
83 int ret = MHD_queue_response(connection, 500, response);
84 MHD_destroy_response(response);
85 return ret;
86}
87
joshualitt792345f2016-02-02 13:02:33 -080088static int SendData(MHD_Connection* connection, const SkData* data, const char* type,
89 bool setContentDisposition = false, const char* dispositionString = nullptr) {
joshualittccfdaa52016-01-27 07:40:29 -080090 MHD_Response* response = MHD_create_response_from_buffer(data->size(),
91 const_cast<void*>(data->data()),
92 MHD_RESPMEM_MUST_COPY);
joshualitt609d9792016-01-27 11:07:23 -080093 MHD_add_response_header(response, "Content-Type", type);
joshualittccfdaa52016-01-27 07:40:29 -080094
joshualitt792345f2016-02-02 13:02:33 -080095 if (setContentDisposition) {
96 MHD_add_response_header(response, "Content-Disposition", dispositionString);
97 }
98
joshualittccfdaa52016-01-27 07:40:29 -080099 int ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
100 MHD_destroy_response(response);
101 return ret;
102}
103
ethannicholas3ff5d8c2016-02-22 08:59:57 -0800104static int SendJSON(MHD_Connection* connection, Request* request, int n) {
joshualitt4dcbe432016-02-25 10:50:28 -0800105 SkCanvas* canvas = request->getCanvas();
ethannicholas3ff5d8c2016-02-22 08:59:57 -0800106 SkDebugCanvas* debugCanvas = request->fDebugCanvas;
107 UrlDataManager* urlDataManager = &request->fUrlDataManager;
joshualitt6b3cf732016-02-17 11:20:26 -0800108 Json::Value root = debugCanvas->toJSON(*urlDataManager, n, canvas);
ethannicholas3ff5d8c2016-02-22 08:59:57 -0800109 root["mode"] = Json::Value(request->fGPUEnabled ? "gpu" : "cpu");
joshualitt609d9792016-01-27 11:07:23 -0800110 SkDynamicMemoryWStream stream;
joshualittdb6a2542016-02-11 07:09:51 -0800111 stream.writeText(Json::FastWriter().write(root).c_str());
joshualitt609d9792016-01-27 11:07:23 -0800112
113 SkAutoTUnref<SkData> data(stream.copyToData());
114 return SendData(connection, data, "application/json");
115}
116
117static int SendTemplate(MHD_Connection* connection, bool redirect = false,
118 const char* redirectUrl = nullptr) {
jcgregorio21ab1202016-01-28 06:24:19 -0800119 SkString debuggerTemplate = generateTemplate(SkString(FLAGS_source[0]));
joshualittccfdaa52016-01-27 07:40:29 -0800120
121 MHD_Response* response = MHD_create_response_from_buffer(
122 debuggerTemplate.size(),
123 (void*) const_cast<char*>(debuggerTemplate.c_str()),
124 MHD_RESPMEM_MUST_COPY);
jcgregorio6a2046e2016-01-28 05:31:31 -0800125 MHD_add_response_header (response, "Access-Control-Allow-Origin", "*");
joshualittccfdaa52016-01-27 07:40:29 -0800126
jcgregorio6f17bc52016-01-27 11:44:38 -0800127 int status = MHD_HTTP_OK;
128
joshualitt609d9792016-01-27 11:07:23 -0800129 if (redirect) {
130 MHD_add_response_header (response, "Location", redirectUrl);
jcgregorio6f17bc52016-01-27 11:44:38 -0800131 status = MHD_HTTP_SEE_OTHER;
joshualitt609d9792016-01-27 11:07:23 -0800132 }
133
jcgregorio6f17bc52016-01-27 11:44:38 -0800134 int ret = MHD_queue_response(connection, status, response);
joshualittccfdaa52016-01-27 07:40:29 -0800135 MHD_destroy_response(response);
136 return ret;
137}
138
joshualitt483b9012016-02-02 07:16:24 -0800139class UrlHandler {
140public:
141 virtual ~UrlHandler() {}
142 virtual bool canHandle(const char* method, const char* url) = 0;
143 virtual int handle(Request* request, MHD_Connection* connection,
joshualitt136f5172016-02-02 11:07:39 -0800144 const char* url, const char* method,
joshualitt483b9012016-02-02 07:16:24 -0800145 const char* upload_data, size_t* upload_data_size) = 0;
146};
joshualittccfdaa52016-01-27 07:40:29 -0800147
joshualitt29e5a892016-02-04 06:08:33 -0800148class CmdHandler : public UrlHandler {
joshualitt483b9012016-02-02 07:16:24 -0800149public:
150 bool canHandle(const char* method, const char* url) override {
joshualitt136f5172016-02-02 11:07:39 -0800151 const char* kBasePath = "/cmd";
152 return 0 == strncmp(url, kBasePath, strlen(kBasePath));
joshualittccfdaa52016-01-27 07:40:29 -0800153 }
154
joshualitt483b9012016-02-02 07:16:24 -0800155 int handle(Request* request, MHD_Connection* connection,
joshualitt136f5172016-02-02 11:07:39 -0800156 const char* url, const char* method,
joshualitt483b9012016-02-02 07:16:24 -0800157 const char* upload_data, size_t* upload_data_size) override {
joshualitt136f5172016-02-02 11:07:39 -0800158 SkTArray<SkString> commands;
159 SkStrSplit(url, "/", &commands);
160
161 if (!request->fPicture.get() || commands.count() > 3) {
162 return MHD_NO;
joshualitt483b9012016-02-02 07:16:24 -0800163 }
joshualitt136f5172016-02-02 11:07:39 -0800164
joshualittdb6a2542016-02-11 07:09:51 -0800165 // /cmd or /cmd/N
166 if (0 == strcmp(method, MHD_HTTP_METHOD_GET)) {
167 int n;
168 if (commands.count() == 1) {
ethannicholas0a0520a2016-02-12 12:06:53 -0800169 n = request->fDebugCanvas->getSize() - 1;
joshualittdb6a2542016-02-11 07:09:51 -0800170 } else {
171 sscanf(commands[1].c_str(), "%d", &n);
172 }
ethannicholas3ff5d8c2016-02-22 08:59:57 -0800173 return SendJSON(connection, request, n);
joshualitt136f5172016-02-02 11:07:39 -0800174 }
175
176 // /cmd/N, for now only delete supported
177 if (commands.count() == 2 && 0 == strcmp(method, MHD_HTTP_METHOD_DELETE)) {
178 int n;
179 sscanf(commands[1].c_str(), "%d", &n);
180 request->fDebugCanvas->deleteDrawCommandAt(n);
jcgregorio12d47ce2016-02-10 14:10:37 -0800181 return SendOK(connection);
joshualitt136f5172016-02-02 11:07:39 -0800182 }
183
184 // /cmd/N/[0|1]
185 if (commands.count() == 3 && 0 == strcmp(method, MHD_HTTP_METHOD_POST)) {
186 int n, toggle;
187 sscanf(commands[1].c_str(), "%d", &n);
188 sscanf(commands[2].c_str(), "%d", &toggle);
189 request->fDebugCanvas->toggleCommand(n, toggle);
jcgregorio12d47ce2016-02-10 14:10:37 -0800190 return SendOK(connection);
joshualitt136f5172016-02-02 11:07:39 -0800191 }
192
joshualitt483b9012016-02-02 07:16:24 -0800193 return MHD_NO;
194 }
195};
196
197class ImgHandler : public UrlHandler {
198public:
199 bool canHandle(const char* method, const char* url) override {
200 static const char* kBasePath = "/img";
201 return 0 == strcmp(method, MHD_HTTP_METHOD_GET) &&
202 0 == strncmp(url, kBasePath, strlen(kBasePath));
joshualittccfdaa52016-01-27 07:40:29 -0800203 }
204
joshualitt483b9012016-02-02 07:16:24 -0800205 int handle(Request* request, MHD_Connection* connection,
joshualitt136f5172016-02-02 11:07:39 -0800206 const char* url, const char* method,
joshualitt483b9012016-02-02 07:16:24 -0800207 const char* upload_data, size_t* upload_data_size) override {
joshualitt136f5172016-02-02 11:07:39 -0800208 SkTArray<SkString> commands;
209 SkStrSplit(url, "/", &commands);
210
211 if (!request->fPicture.get() || commands.count() > 2) {
212 return MHD_NO;
joshualitt483b9012016-02-02 07:16:24 -0800213 }
joshualitta341b902016-02-02 07:37:21 -0800214
joshualitt136f5172016-02-02 11:07:39 -0800215 int n;
216 // /img or /img/N
217 if (commands.count() == 1) {
218 n = request->fDebugCanvas->getSize() - 1;
219 } else {
220 sscanf(commands[1].c_str(), "%d", &n);
221 }
222
joshualitt4dcbe432016-02-25 10:50:28 -0800223 SkAutoTUnref<SkData> data(request->drawToPng(n));
joshualitt136f5172016-02-02 11:07:39 -0800224 return SendData(connection, data, "image/png");
joshualitt483b9012016-02-02 07:16:24 -0800225 }
226};
joshualittccfdaa52016-01-27 07:40:29 -0800227
ethannicholas3ca1e1a2016-02-18 10:22:34 -0800228class BreakHandler : public UrlHandler {
229public:
230 bool canHandle(const char* method, const char* url) override {
231 static const char* kBasePath = "/break";
232 return 0 == strcmp(method, MHD_HTTP_METHOD_GET) &&
233 0 == strncmp(url, kBasePath, strlen(kBasePath));
234 }
235
236 static SkColor GetPixel(Request* request, int x, int y) {
joshualitt4dcbe432016-02-25 10:50:28 -0800237 SkCanvas* canvas = request->getCanvas();
ethannicholas3ca1e1a2016-02-18 10:22:34 -0800238 canvas->flush();
joshualitt4dcbe432016-02-25 10:50:28 -0800239 SkAutoTDelete<SkBitmap> bitmap(request->getBitmapFromCanvas(canvas));
ethannicholas3ca1e1a2016-02-18 10:22:34 -0800240 SkASSERT(bitmap);
241 bitmap->lockPixels();
joshualitt4dcbe432016-02-25 10:50:28 -0800242 uint8_t* start = ((uint8_t*) bitmap->getPixels()) + (y * Request::kImageWidth + x) * 4;
ethannicholas3ca1e1a2016-02-18 10:22:34 -0800243 SkColor result = SkColorSetARGB(start[3], start[0], start[1], start[2]);
244 bitmap->unlockPixels();
245 return result;
246 }
247
248 int handle(Request* request, MHD_Connection* connection,
249 const char* url, const char* method,
250 const char* upload_data, size_t* upload_data_size) override {
251 SkTArray<SkString> commands;
252 SkStrSplit(url, "/", &commands);
253
254 if (!request->fPicture.get() || commands.count() != 4) {
255 return MHD_NO;
256 }
257
258 // /break/<n>/<x>/<y>
259 int n;
260 sscanf(commands[1].c_str(), "%d", &n);
261 int x;
262 sscanf(commands[2].c_str(), "%d", &x);
263 int y;
264 sscanf(commands[3].c_str(), "%d", &y);
265
266 int count = request->fDebugCanvas->getSize();
267 SkASSERT(n < count);
268
joshualitt4dcbe432016-02-25 10:50:28 -0800269 SkCanvas* canvas = request->getCanvas();
ethannicholas3ca1e1a2016-02-18 10:22:34 -0800270 canvas->clear(SK_ColorWHITE);
271 int saveCount = canvas->save();
272 for (int i = 0; i <= n; ++i) {
273 request->fDebugCanvas->getDrawCommandAt(i)->execute(canvas);
274 }
275 SkColor target = GetPixel(request, x, y);
276 Json::Value response(Json::objectValue);
277 Json::Value startColor(Json::arrayValue);
278 startColor.append(Json::Value(SkColorGetR(target)));
279 startColor.append(Json::Value(SkColorGetG(target)));
280 startColor.append(Json::Value(SkColorGetB(target)));
281 startColor.append(Json::Value(SkColorGetA(target)));
282 response["startColor"] = startColor;
283 response["endColor"] = startColor;
284 response["endOp"] = Json::Value(n);
285 for (int i = n + 1; i < n + count; ++i) {
286 int index = i % count;
287 if (index == 0) {
288 // reset canvas for wraparound
289 canvas->restoreToCount(saveCount);
290 canvas->clear(SK_ColorWHITE);
291 saveCount = canvas->save();
292 }
293 request->fDebugCanvas->getDrawCommandAt(index)->execute(canvas);
294 SkColor current = GetPixel(request, x, y);
295 if (current != target) {
296 Json::Value endColor(Json::arrayValue);
297 endColor.append(Json::Value(SkColorGetR(current)));
298 endColor.append(Json::Value(SkColorGetG(current)));
299 endColor.append(Json::Value(SkColorGetB(current)));
300 endColor.append(Json::Value(SkColorGetA(current)));
301 response["endColor"] = endColor;
302 response["endOp"] = Json::Value(index);
303 break;
304 }
305 }
306 canvas->restoreToCount(saveCount);
307 SkDynamicMemoryWStream stream;
308 stream.writeText(Json::FastWriter().write(response).c_str());
309 SkAutoTUnref<SkData> data(stream.copyToData());
310 return SendData(connection, data, "application/json");
311 }
312};
313
ethannicholas0a0520a2016-02-12 12:06:53 -0800314/**
315 Updates the clip visualization alpha. On all subsequent /img requests, the clip will be drawn in
316 black with the specified alpha. 0 = no visible clip, 255 = fully opaque clip.
317 */
318class ClipAlphaHandler : public UrlHandler {
319public:
320 bool canHandle(const char* method, const char* url) override {
321 static const char* kBasePath = "/clipAlpha/";
jcgregorio3341d422016-02-16 10:31:07 -0800322 return 0 == strcmp(method, MHD_HTTP_METHOD_POST) &&
ethannicholas0a0520a2016-02-12 12:06:53 -0800323 0 == strncmp(url, kBasePath, strlen(kBasePath));
324 }
325
326 int handle(Request* request, MHD_Connection* connection,
327 const char* url, const char* method,
328 const char* upload_data, size_t* upload_data_size) override {
329 SkTArray<SkString> commands;
330 SkStrSplit(url, "/", &commands);
331
332 if (!request->fPicture.get() || commands.count() != 2) {
333 return MHD_NO;
334 }
335
336 int alpha;
337 sscanf(commands[1].c_str(), "%d", &alpha);
338
339 request->fDebugCanvas->setClipVizColor(SkColorSetARGB(alpha, 0, 0, 0));
340 return SendOK(connection);
341 }
342};
343
ethannicholas85fca852016-02-19 08:40:59 -0800344/**
345 Controls whether GPU rendering is enabled. Posting to /enableGPU/1 turns GPU on, /enableGPU/0
346 disables it.
347 */
348class EnableGPUHandler : public UrlHandler {
349public:
350 bool canHandle(const char* method, const char* url) override {
351 static const char* kBasePath = "/enableGPU/";
352 return 0 == strcmp(method, MHD_HTTP_METHOD_POST) &&
353 0 == strncmp(url, kBasePath, strlen(kBasePath));
354 }
355
356 int handle(Request* request, MHD_Connection* connection,
357 const char* url, const char* method,
358 const char* upload_data, size_t* upload_data_size) override {
359 SkTArray<SkString> commands;
360 SkStrSplit(url, "/", &commands);
361
362 if (commands.count() != 2) {
363 return MHD_NO;
364 }
365
366 int enable;
367 sscanf(commands[1].c_str(), "%d", &enable);
368
369 if (enable) {
joshualitt4dcbe432016-02-25 10:50:28 -0800370 SkSurface* surface = request->createGPUSurface();
ethannicholas85fca852016-02-19 08:40:59 -0800371 if (surface) {
372 request->fSurface.reset(surface);
ethannicholas3ff5d8c2016-02-22 08:59:57 -0800373 request->fGPUEnabled = true;
ethannicholas85fca852016-02-19 08:40:59 -0800374 return SendOK(connection);
375 }
376 return SendError(connection, "Unable to create GPU surface");
377 }
joshualitt4dcbe432016-02-25 10:50:28 -0800378 request->fSurface.reset(request->createCPUSurface());
ethannicholas3ff5d8c2016-02-22 08:59:57 -0800379 request->fGPUEnabled = false;
ethannicholas85fca852016-02-19 08:40:59 -0800380 return SendOK(connection);
381 }
382};
383
joshualitt483b9012016-02-02 07:16:24 -0800384class PostHandler : public UrlHandler {
385public:
386 bool canHandle(const char* method, const char* url) override {
387 return 0 == strcmp(method, MHD_HTTP_METHOD_POST) &&
388 0 == strcmp(url, "/new");
joshualittccfdaa52016-01-27 07:40:29 -0800389 }
390
joshualitt483b9012016-02-02 07:16:24 -0800391 int handle(Request* request, MHD_Connection* connection,
joshualitt136f5172016-02-02 11:07:39 -0800392 const char* url, const char* method,
joshualitt483b9012016-02-02 07:16:24 -0800393 const char* upload_data, size_t* upload_data_size) override {
394 UploadContext* uc = request->fUploadContext;
joshualittccfdaa52016-01-27 07:40:29 -0800395
joshualitt483b9012016-02-02 07:16:24 -0800396 // New connection
397 if (!uc) {
398 // TODO make this a method on request
399 uc = new UploadContext;
400 uc->connection = connection;
401 uc->fPostProcessor = MHD_create_post_processor(connection, kBufferSize,
402 &process_upload_data, uc);
403 SkASSERT(uc->fPostProcessor);
joshualitt609d9792016-01-27 11:07:23 -0800404
joshualitt483b9012016-02-02 07:16:24 -0800405 request->fUploadContext = uc;
406 return MHD_YES;
407 }
408
409 // in process upload
410 if (0 != *upload_data_size) {
411 SkASSERT(uc->fPostProcessor);
412 MHD_post_process(uc->fPostProcessor, upload_data, *upload_data_size);
413 *upload_data_size = 0;
414 return MHD_YES;
415 }
416
417 // end of upload
418 MHD_destroy_post_processor(uc->fPostProcessor);
419 uc->fPostProcessor = nullptr;
420
joshualitt136f5172016-02-02 11:07:39 -0800421 // parse picture from stream
422 request->fPicture.reset(
423 SkPicture::CreateFromStream(request->fUploadContext->fStream.detachAsStream()));
424 if (!request->fPicture.get()) {
425 fprintf(stderr, "Could not create picture from stream.\n");
426 return MHD_NO;
joshualitt483b9012016-02-02 07:16:24 -0800427 }
428
joshualitt136f5172016-02-02 11:07:39 -0800429 // pour picture into debug canvas
joshualitt4dcbe432016-02-25 10:50:28 -0800430 request->fDebugCanvas.reset(new SkDebugCanvas(Request::kImageWidth,
431 Request::kImageHeight));
joshualitt136f5172016-02-02 11:07:39 -0800432 request->fDebugCanvas->drawPicture(request->fPicture);
433
joshualitta341b902016-02-02 07:37:21 -0800434 // clear upload context
435 delete request->fUploadContext;
436 request->fUploadContext = nullptr;
437
joshualitt483b9012016-02-02 07:16:24 -0800438 return SendTemplate(connection, true, "/");
joshualittccfdaa52016-01-27 07:40:29 -0800439 }
joshualitt483b9012016-02-02 07:16:24 -0800440};
441
joshualitt792345f2016-02-02 13:02:33 -0800442class DownloadHandler : public UrlHandler {
443public:
444 bool canHandle(const char* method, const char* url) override {
445 return 0 == strcmp(method, MHD_HTTP_METHOD_GET) &&
446 0 == strcmp(url, "/download");
447 }
448
449 int handle(Request* request, MHD_Connection* connection,
450 const char* url, const char* method,
451 const char* upload_data, size_t* upload_data_size) override {
452 if (!request->fPicture.get()) {
453 return MHD_NO;
454 }
455
456 // TODO move to a function
457 // Playback into picture recorder
458 SkPictureRecorder recorder;
joshualitt4dcbe432016-02-25 10:50:28 -0800459 SkCanvas* canvas = recorder.beginRecording(Request::kImageWidth,
460 Request::kImageHeight);
joshualitt792345f2016-02-02 13:02:33 -0800461
462 request->fDebugCanvas->draw(canvas);
463
464 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
465
466 SkDynamicMemoryWStream outStream;
467
468 SkAutoTUnref<SkPixelSerializer> serializer(SkImageEncoder::CreatePixelSerializer());
469 picture->serialize(&outStream, serializer);
470
471 SkAutoTUnref<SkData> data(outStream.copyToData());
472
473 // TODO fancier name handling
474 return SendData(connection, data, "application/octet-stream", true,
475 "attachment; filename=something.skp;");
476 }
477};
478
joshualitt29e5a892016-02-04 06:08:33 -0800479class InfoHandler : public UrlHandler {
480public:
481 bool canHandle(const char* method, const char* url) override {
482 const char* kBaseName = "/info";
483 return 0 == strcmp(method, MHD_HTTP_METHOD_GET) &&
484 0 == strncmp(url, kBaseName, strlen(kBaseName));
485 }
486
487 int handle(Request* request, MHD_Connection* connection,
488 const char* url, const char* method,
489 const char* upload_data, size_t* upload_data_size) override {
490 SkTArray<SkString> commands;
491 SkStrSplit(url, "/", &commands);
492
493 if (!request->fPicture.get() || commands.count() > 2) {
494 return MHD_NO;
495 }
496
497 // drawTo
joshualitt4dcbe432016-02-25 10:50:28 -0800498 SkAutoTUnref<SkSurface> surface(request->createCPUSurface());
joshualitt29e5a892016-02-04 06:08:33 -0800499 SkCanvas* canvas = surface->getCanvas();
500
501 int n;
502 // /info or /info/N
503 if (commands.count() == 1) {
504 n = request->fDebugCanvas->getSize() - 1;
505 } else {
506 sscanf(commands[1].c_str(), "%d", &n);
507 }
508
509 // TODO this is really slow and we should cache the matrix and clip
510 request->fDebugCanvas->drawTo(canvas, n);
511
512 // make some json
513 SkMatrix vm = request->fDebugCanvas->getCurrentMatrix();
514 SkIRect clip = request->fDebugCanvas->getCurrentClip();
515 Json::Value info(Json::objectValue);
516 info["ViewMatrix"] = SkJSONCanvas::MakeMatrix(vm);
517 info["ClipRect"] = SkJSONCanvas::MakeIRect(clip);
518
519 std::string json = Json::FastWriter().write(info);
520
521 // We don't want the null terminator so strlen is correct
522 SkAutoTUnref<SkData> data(SkData::NewWithCopy(json.c_str(), strlen(json.c_str())));
523 return SendData(connection, data, "application/json");
524 }
525};
526
joshualittcdad12f2016-02-08 07:08:21 -0800527class DataHandler : public UrlHandler {
528public:
529 bool canHandle(const char* method, const char* url) override {
530 static const char* kBaseUrl = "/data";
531 return 0 == strcmp(method, MHD_HTTP_METHOD_GET) &&
532 0 == strncmp(url, kBaseUrl, strlen(kBaseUrl));
533 }
534
535 int handle(Request* request, MHD_Connection* connection,
536 const char* url, const char* method,
537 const char* upload_data, size_t* upload_data_size) override {
538 SkTArray<SkString> commands;
539 SkStrSplit(url, "/", &commands);
540
541 if (!request->fPicture.get() || commands.count() != 2) {
542 return MHD_NO;
543 }
544
545 SkAutoTUnref<UrlDataManager::UrlData> urlData(
546 SkRef(request->fUrlDataManager.getDataFromUrl(SkString(url))));
547
548 if (urlData) {
549 return SendData(connection, urlData->fData.get(), urlData->fContentType.c_str());
550 }
551 return MHD_NO;
552 }
553};
joshualitt29e5a892016-02-04 06:08:33 -0800554
joshualitt483b9012016-02-02 07:16:24 -0800555class RootHandler : public UrlHandler {
556public:
557 bool canHandle(const char* method, const char* url) override {
558 return 0 == strcmp(method, MHD_HTTP_METHOD_GET) &&
559 0 == strcmp(url, "/");
560 }
561
562 int handle(Request* request, MHD_Connection* connection,
joshualitt136f5172016-02-02 11:07:39 -0800563 const char* url, const char* method,
joshualitt483b9012016-02-02 07:16:24 -0800564 const char* upload_data, size_t* upload_data_size) override {
565 return SendTemplate(connection);
566 }
567};
joshualittccfdaa52016-01-27 07:40:29 -0800568
569class UrlManager {
570public:
571 UrlManager() {
572 // Register handlers
joshualitt483b9012016-02-02 07:16:24 -0800573 fHandlers.push_back(new RootHandler);
574 fHandlers.push_back(new PostHandler);
575 fHandlers.push_back(new ImgHandler);
ethannicholas0a0520a2016-02-12 12:06:53 -0800576 fHandlers.push_back(new ClipAlphaHandler);
ethannicholas85fca852016-02-19 08:40:59 -0800577 fHandlers.push_back(new EnableGPUHandler);
joshualitt29e5a892016-02-04 06:08:33 -0800578 fHandlers.push_back(new CmdHandler);
joshualitt483b9012016-02-02 07:16:24 -0800579 fHandlers.push_back(new InfoHandler);
joshualitt792345f2016-02-02 13:02:33 -0800580 fHandlers.push_back(new DownloadHandler);
joshualittcdad12f2016-02-08 07:08:21 -0800581 fHandlers.push_back(new DataHandler);
ethannicholas3ca1e1a2016-02-18 10:22:34 -0800582 fHandlers.push_back(new BreakHandler);
joshualitt483b9012016-02-02 07:16:24 -0800583 }
584
585 ~UrlManager() {
586 for (int i = 0; i < fHandlers.count(); i++) { delete fHandlers[i]; }
joshualittccfdaa52016-01-27 07:40:29 -0800587 }
588
589 // This is clearly not efficient for a large number of urls and handlers
590 int invoke(Request* request, MHD_Connection* connection, const char* url, const char* method,
591 const char* upload_data, size_t* upload_data_size) const {
592 for (int i = 0; i < fHandlers.count(); i++) {
joshualitt483b9012016-02-02 07:16:24 -0800593 if (fHandlers[i]->canHandle(method, url)) {
joshualitt136f5172016-02-02 11:07:39 -0800594 return fHandlers[i]->handle(request, connection, url, method, upload_data,
595 upload_data_size);
joshualittccfdaa52016-01-27 07:40:29 -0800596 }
597 }
598 return MHD_NO;
599 }
600
601private:
joshualitt483b9012016-02-02 07:16:24 -0800602 SkTArray<UrlHandler*> fHandlers;
joshualittccfdaa52016-01-27 07:40:29 -0800603};
604
605const UrlManager kUrlManager;
606
joshualitt7f6a1e02016-01-22 11:21:43 -0800607int answer_to_connection(void* cls, struct MHD_Connection* connection,
608 const char* url, const char* method, const char* version,
609 const char* upload_data, size_t* upload_data_size,
610 void** con_cls) {
joshualitt9a4e1882016-01-27 07:03:29 -0800611 SkDebugf("New %s request for %s using version %s\n", method, url, version);
joshualitt7f6a1e02016-01-22 11:21:43 -0800612
joshualitt9a4e1882016-01-27 07:03:29 -0800613 Request* request = reinterpret_cast<Request*>(cls);
joshualitt483b9012016-02-02 07:16:24 -0800614 int result = kUrlManager.invoke(request, connection, url, method, upload_data,
615 upload_data_size);
616 if (MHD_NO == result) {
joshualitt873d6242016-02-08 13:57:44 -0800617 fprintf(stderr, "Invalid method and / or url: %s %s\n", method, url);
joshualitt483b9012016-02-02 07:16:24 -0800618 }
619 return result;
joshualitt7f6a1e02016-01-22 11:21:43 -0800620}
621
622int skiaserve_main() {
joshualittcdad12f2016-02-08 07:08:21 -0800623 Request request(SkString("/data")); // This simple server has one request
ethannicholas85fca852016-02-19 08:40:59 -0800624
625 // create surface
626 GrContextOptions grContextOpts;
627 request.fContextFactory.reset(new GrContextFactory(grContextOpts));
joshualitt4dcbe432016-02-25 10:50:28 -0800628 request.fSurface.reset(request.createCPUSurface());
ethannicholas85fca852016-02-19 08:40:59 -0800629
joshualitt7f6a1e02016-01-22 11:21:43 -0800630 struct MHD_Daemon* daemon;
jcgregorio21ab1202016-01-28 06:24:19 -0800631 // TODO Add option to bind this strictly to an address, e.g. localhost, for security.
632 daemon = MHD_start_daemon(MHD_USE_SELECT_INTERNALLY, FLAGS_port, nullptr, nullptr,
joshualitt9a4e1882016-01-27 07:03:29 -0800633 &answer_to_connection, &request,
634 MHD_OPTION_END);
joshualitt7f6a1e02016-01-22 11:21:43 -0800635 if (NULL == daemon) {
636 return 1;
637 }
638
639 getchar();
640 MHD_stop_daemon(daemon);
641 return 0;
642}
643
644#if !defined SK_BUILD_FOR_IOS
645int main(int argc, char** argv) {
646 SkCommandLineFlags::Parse(argc, argv);
647 return skiaserve_main();
648}
649#endif