blob: 119f065ac071d10632fbaf463b3dea404ba78d57 [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>
ethannicholas546d6652016-02-16 11:03:04 -080025#include "png.h"
joshualitt7f6a1e02016-01-22 11:21:43 -080026
27// To get image decoders linked in we have to do the below magic
28#include "SkForceLinking.h"
29#include "SkImageDecoder.h"
30__SK_FORCE_IMAGE_DECODER_LINKING;
31
jcgregorio21ab1202016-01-28 06:24:19 -080032DEFINE_string(source, "https://debugger.skia.org", "Where to load the web UI from.");
joshualitt873d6242016-02-08 13:57:44 -080033DEFINE_string(faviconDir, "tools/skiaserve", "The directory of the favicon");
jcgregorio21ab1202016-01-28 06:24:19 -080034DEFINE_int32(port, 8888, "The port to listen on.");
joshualitt7f6a1e02016-01-22 11:21:43 -080035
36// TODO probably want to make this configurable
37static const int kImageWidth = 1920;
38static const int kImageHeight = 1080;
39
joshualitt7f6a1e02016-01-22 11:21:43 -080040// TODO move to template file
41SkString generateTemplate(SkString source) {
42 SkString debuggerTemplate;
43 debuggerTemplate.appendf(
44 "<!DOCTYPE html>\n"
45 "<html>\n"
46 "<head>\n"
47 " <title>SkDebugger</title>\n"
48 " <meta charset=\"utf-8\" />\n"
49 " <meta http-equiv=\"X-UA-Compatible\" content=\"IE=egde,chrome=1\">\n"
50 " <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n"
51 " <script src=\"%s/res/js/core.js\" type=\"text/javascript\" charset=\"utf-8\"></script>\n"
52 " <link href=\"%s/res/vul/elements.html\" rel=\"import\" />\n"
53 "</head>\n"
54 "<body class=\"fullbleed layout vertical\">\n"
55 " <debugger-app-sk>This is the app."
56 " </debugger-app-sk>\n"
57 "</body>\n"
58 "</html>", source.c_str(), source.c_str());
59 return debuggerTemplate;
60
61}
62
joshualitt9a4e1882016-01-27 07:03:29 -080063struct UploadContext {
joshualitt609d9792016-01-27 11:07:23 -080064 SkDynamicMemoryWStream fStream;
65 MHD_PostProcessor* fPostProcessor;
joshualitt9a4e1882016-01-27 07:03:29 -080066 MHD_Connection* connection;
67};
68
69struct Request {
joshualittcdad12f2016-02-08 07:08:21 -080070 Request(SkString rootUrl) : fUploadContext(nullptr), fUrlDataManager(rootUrl) {}
joshualitt9a4e1882016-01-27 07:03:29 -080071 UploadContext* fUploadContext;
joshualitt609d9792016-01-27 11:07:23 -080072 SkAutoTUnref<SkPicture> fPicture;
joshualitt136f5172016-02-02 11:07:39 -080073 SkAutoTUnref<SkDebugCanvas> fDebugCanvas;
ethannicholasde8e54c2016-02-12 10:09:34 -080074 SkAutoTDelete<GrContextFactory> fContextFactory;
75 SkAutoTUnref<SkSurface> fSurface;
joshualittcdad12f2016-02-08 07:08:21 -080076 UrlDataManager fUrlDataManager;
joshualitt9a4e1882016-01-27 07:03:29 -080077};
78
joshualitt609d9792016-01-27 11:07:23 -080079// TODO factor this out into functions, also handle CPU path
joshualitt136f5172016-02-02 11:07:39 -080080SkSurface* setupSurface(GrContextFactory* factory) {
joshualitt609d9792016-01-27 11:07:23 -080081 GrContext* context = factory->get(GrContextFactory::kNative_GLContextType,
82 GrContextFactory::kNone_GLContextOptions);
83 int maxRTSize = context->caps()->maxRenderTargetSize();
84 SkImageInfo info = SkImageInfo::Make(SkTMin(kImageWidth, maxRTSize),
85 SkTMin(kImageHeight, maxRTSize),
86 kN32_SkColorType, kPremul_SkAlphaType);
87 uint32_t flags = 0;
88 SkSurfaceProps props(flags, SkSurfaceProps::kLegacyFontHost_InitType);
joshualitt136f5172016-02-02 11:07:39 -080089 SkSurface* surface = SkSurface::NewRenderTarget(context, SkSurface::kNo_Budgeted, info, 0,
90 &props);
91 SkASSERT(surface);
joshualitt609d9792016-01-27 11:07:23 -080092
joshualitt136f5172016-02-02 11:07:39 -080093 return surface;
94}
joshualitt609d9792016-01-27 11:07:23 -080095
ethannicholas546d6652016-02-16 11:03:04 -080096static void write_png_callback(png_structp png_ptr, png_bytep data, png_size_t length) {
97 SkWStream* out = (SkWStream*) png_get_io_ptr(png_ptr);
98 out->write(data, length);
99}
100
101static void write_png(const png_bytep rgba, png_uint_32 width, png_uint_32 height, SkWStream& out) {
102 png_structp png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
103 SkASSERT(png != nullptr);
104 png_infop info_ptr = png_create_info_struct(png);
105 SkASSERT(info_ptr != nullptr);
106 if (setjmp(png_jmpbuf(png))) {
107 SkFAIL("png encode error");
108 }
109 png_set_IHDR(png, info_ptr, width, height, 8, PNG_COLOR_TYPE_RGBA, PNG_INTERLACE_NONE,
110 PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
111 png_set_compression_level(png, 1);
112 png_bytepp rows = (png_bytepp) sk_malloc_throw(height * sizeof(png_byte*));
113 for (png_size_t y = 0; y < height; ++y) {
114 rows[y] = (png_bytep) rgba + y * width * 4;
115 }
116 png_set_filter(png, 0, PNG_NO_FILTERS);
117 png_set_rows(png, info_ptr, &rows[0]);
118 png_set_write_fn(png, &out, write_png_callback, NULL);
119 png_write_png(png, info_ptr, PNG_TRANSFORM_IDENTITY, NULL);
120 png_destroy_write_struct(&png, NULL);
121 sk_free(rows);
122}
123
joshualitt136f5172016-02-02 11:07:39 -0800124SkData* writeCanvasToPng(SkCanvas* canvas) {
joshualitt609d9792016-01-27 11:07:23 -0800125 // capture pixels
126 SkBitmap bmp;
127 bmp.setInfo(canvas->imageInfo());
128 if (!canvas->readPixels(&bmp, 0, 0)) {
joshualitt136f5172016-02-02 11:07:39 -0800129 fprintf(stderr, "Can't read pixels\n");
130 return nullptr;
joshualitt609d9792016-01-27 11:07:23 -0800131 }
132
133 // write to png
ethannicholas546d6652016-02-16 11:03:04 -0800134 SkDynamicMemoryWStream buffer;
135 write_png((const png_bytep) bmp.getPixels(), bmp.width(), bmp.height(), buffer);
136 return buffer.copyToData();
joshualitt136f5172016-02-02 11:07:39 -0800137}
138
ethannicholasde8e54c2016-02-12 10:09:34 -0800139SkData* setupAndDrawToCanvasReturnPng(Request* request, int n) {
140 GrContextFactory* factory = request->fContextFactory;
141 SkGLContext* gl = factory->getContextInfo(GrContextFactory::kNative_GLContextType,
142 GrContextFactory::kNone_GLContextOptions).fGLContext;
143 gl->makeCurrent();
144 SkASSERT(request->fDebugCanvas);
145 SkCanvas* target = request->fSurface->getCanvas();
146 request->fDebugCanvas->drawTo(target, n);
147 return writeCanvasToPng(target);
joshualitt609d9792016-01-27 11:07:23 -0800148}
149
joshualitt29e5a892016-02-04 06:08:33 -0800150SkSurface* setupCpuSurface() {
151 SkImageInfo info = SkImageInfo::Make(kImageWidth, kImageHeight, kN32_SkColorType,
152 kPremul_SkAlphaType);
153 return SkSurface::NewRaster(info);
154}
155
joshualitt9a4e1882016-01-27 07:03:29 -0800156static const size_t kBufferSize = 1024;
157
158static int process_upload_data(void* cls, enum MHD_ValueKind kind,
159 const char* key, const char* filename,
160 const char* content_type, const char* transfer_encoding,
161 const char* data, uint64_t off, size_t size) {
162 struct UploadContext* uc = reinterpret_cast<UploadContext*>(cls);
163
164 if (0 != size) {
joshualitt609d9792016-01-27 11:07:23 -0800165 uc->fStream.write(data, size);
joshualitt9a4e1882016-01-27 07:03:29 -0800166 }
167 return MHD_YES;
168}
169
jcgregorio12d47ce2016-02-10 14:10:37 -0800170// SendOK just sends an empty response with a 200 OK status code.
171static int SendOK(MHD_Connection* connection) {
172 const char* data = "";
173
174 MHD_Response* response = MHD_create_response_from_buffer(strlen(data),
175 (void*)data,
176 MHD_RESPMEM_PERSISTENT);
177 int ret = MHD_queue_response(connection, 200, response);
178 MHD_destroy_response(response);
179 return ret;
180}
181
joshualitt792345f2016-02-02 13:02:33 -0800182static int SendData(MHD_Connection* connection, const SkData* data, const char* type,
183 bool setContentDisposition = false, const char* dispositionString = nullptr) {
joshualittccfdaa52016-01-27 07:40:29 -0800184 MHD_Response* response = MHD_create_response_from_buffer(data->size(),
185 const_cast<void*>(data->data()),
186 MHD_RESPMEM_MUST_COPY);
joshualitt609d9792016-01-27 11:07:23 -0800187 MHD_add_response_header(response, "Content-Type", type);
joshualittccfdaa52016-01-27 07:40:29 -0800188
joshualitt792345f2016-02-02 13:02:33 -0800189 if (setContentDisposition) {
190 MHD_add_response_header(response, "Content-Disposition", dispositionString);
191 }
192
joshualittccfdaa52016-01-27 07:40:29 -0800193 int ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
194 MHD_destroy_response(response);
195 return ret;
196}
197
joshualittdb6a2542016-02-11 07:09:51 -0800198static int SendJSON(MHD_Connection* connection, SkDebugCanvas* debugCanvas,
199 UrlDataManager* urlDataManager, int n) {
200 Json::Value root = debugCanvas->toJSON(*urlDataManager, n);
joshualitt609d9792016-01-27 11:07:23 -0800201 SkDynamicMemoryWStream stream;
joshualittdb6a2542016-02-11 07:09:51 -0800202 stream.writeText(Json::FastWriter().write(root).c_str());
joshualitt609d9792016-01-27 11:07:23 -0800203
204 SkAutoTUnref<SkData> data(stream.copyToData());
205 return SendData(connection, data, "application/json");
206}
207
208static int SendTemplate(MHD_Connection* connection, bool redirect = false,
209 const char* redirectUrl = nullptr) {
jcgregorio21ab1202016-01-28 06:24:19 -0800210 SkString debuggerTemplate = generateTemplate(SkString(FLAGS_source[0]));
joshualittccfdaa52016-01-27 07:40:29 -0800211
212 MHD_Response* response = MHD_create_response_from_buffer(
213 debuggerTemplate.size(),
214 (void*) const_cast<char*>(debuggerTemplate.c_str()),
215 MHD_RESPMEM_MUST_COPY);
jcgregorio6a2046e2016-01-28 05:31:31 -0800216 MHD_add_response_header (response, "Access-Control-Allow-Origin", "*");
joshualittccfdaa52016-01-27 07:40:29 -0800217
jcgregorio6f17bc52016-01-27 11:44:38 -0800218 int status = MHD_HTTP_OK;
219
joshualitt609d9792016-01-27 11:07:23 -0800220 if (redirect) {
221 MHD_add_response_header (response, "Location", redirectUrl);
jcgregorio6f17bc52016-01-27 11:44:38 -0800222 status = MHD_HTTP_SEE_OTHER;
joshualitt609d9792016-01-27 11:07:23 -0800223 }
224
jcgregorio6f17bc52016-01-27 11:44:38 -0800225 int ret = MHD_queue_response(connection, status, response);
joshualittccfdaa52016-01-27 07:40:29 -0800226 MHD_destroy_response(response);
227 return ret;
228}
229
joshualitt483b9012016-02-02 07:16:24 -0800230class UrlHandler {
231public:
232 virtual ~UrlHandler() {}
233 virtual bool canHandle(const char* method, const char* url) = 0;
234 virtual int handle(Request* request, MHD_Connection* connection,
joshualitt136f5172016-02-02 11:07:39 -0800235 const char* url, const char* method,
joshualitt483b9012016-02-02 07:16:24 -0800236 const char* upload_data, size_t* upload_data_size) = 0;
237};
joshualittccfdaa52016-01-27 07:40:29 -0800238
joshualitt29e5a892016-02-04 06:08:33 -0800239class CmdHandler : public UrlHandler {
joshualitt483b9012016-02-02 07:16:24 -0800240public:
241 bool canHandle(const char* method, const char* url) override {
joshualitt136f5172016-02-02 11:07:39 -0800242 const char* kBasePath = "/cmd";
243 return 0 == strncmp(url, kBasePath, strlen(kBasePath));
joshualittccfdaa52016-01-27 07:40:29 -0800244 }
245
joshualitt483b9012016-02-02 07:16:24 -0800246 int handle(Request* request, MHD_Connection* connection,
joshualitt136f5172016-02-02 11:07:39 -0800247 const char* url, const char* method,
joshualitt483b9012016-02-02 07:16:24 -0800248 const char* upload_data, size_t* upload_data_size) override {
joshualitt136f5172016-02-02 11:07:39 -0800249 SkTArray<SkString> commands;
250 SkStrSplit(url, "/", &commands);
251
252 if (!request->fPicture.get() || commands.count() > 3) {
253 return MHD_NO;
joshualitt483b9012016-02-02 07:16:24 -0800254 }
joshualitt136f5172016-02-02 11:07:39 -0800255
joshualittdb6a2542016-02-11 07:09:51 -0800256 // /cmd or /cmd/N
257 if (0 == strcmp(method, MHD_HTTP_METHOD_GET)) {
258 int n;
259 if (commands.count() == 1) {
ethannicholas0a0520a2016-02-12 12:06:53 -0800260 n = request->fDebugCanvas->getSize() - 1;
joshualittdb6a2542016-02-11 07:09:51 -0800261 } else {
262 sscanf(commands[1].c_str(), "%d", &n);
263 }
264 return SendJSON(connection, request->fDebugCanvas, &request->fUrlDataManager, n);
joshualitt136f5172016-02-02 11:07:39 -0800265 }
266
267 // /cmd/N, for now only delete supported
268 if (commands.count() == 2 && 0 == strcmp(method, MHD_HTTP_METHOD_DELETE)) {
269 int n;
270 sscanf(commands[1].c_str(), "%d", &n);
271 request->fDebugCanvas->deleteDrawCommandAt(n);
jcgregorio12d47ce2016-02-10 14:10:37 -0800272 return SendOK(connection);
joshualitt136f5172016-02-02 11:07:39 -0800273 }
274
275 // /cmd/N/[0|1]
276 if (commands.count() == 3 && 0 == strcmp(method, MHD_HTTP_METHOD_POST)) {
277 int n, toggle;
278 sscanf(commands[1].c_str(), "%d", &n);
279 sscanf(commands[2].c_str(), "%d", &toggle);
280 request->fDebugCanvas->toggleCommand(n, toggle);
jcgregorio12d47ce2016-02-10 14:10:37 -0800281 return SendOK(connection);
joshualitt136f5172016-02-02 11:07:39 -0800282 }
283
joshualitt483b9012016-02-02 07:16:24 -0800284 return MHD_NO;
285 }
286};
287
288class ImgHandler : public UrlHandler {
289public:
290 bool canHandle(const char* method, const char* url) override {
291 static const char* kBasePath = "/img";
292 return 0 == strcmp(method, MHD_HTTP_METHOD_GET) &&
293 0 == strncmp(url, kBasePath, strlen(kBasePath));
joshualittccfdaa52016-01-27 07:40:29 -0800294 }
295
joshualitt483b9012016-02-02 07:16:24 -0800296 int handle(Request* request, MHD_Connection* connection,
joshualitt136f5172016-02-02 11:07:39 -0800297 const char* url, const char* method,
joshualitt483b9012016-02-02 07:16:24 -0800298 const char* upload_data, size_t* upload_data_size) override {
joshualitt136f5172016-02-02 11:07:39 -0800299 SkTArray<SkString> commands;
300 SkStrSplit(url, "/", &commands);
301
302 if (!request->fPicture.get() || commands.count() > 2) {
303 return MHD_NO;
joshualitt483b9012016-02-02 07:16:24 -0800304 }
joshualitta341b902016-02-02 07:37:21 -0800305
joshualitt136f5172016-02-02 11:07:39 -0800306 int n;
307 // /img or /img/N
308 if (commands.count() == 1) {
309 n = request->fDebugCanvas->getSize() - 1;
310 } else {
311 sscanf(commands[1].c_str(), "%d", &n);
312 }
313
ethannicholasde8e54c2016-02-12 10:09:34 -0800314 SkAutoTUnref<SkData> data(setupAndDrawToCanvasReturnPng(request, n));
joshualitt136f5172016-02-02 11:07:39 -0800315 return SendData(connection, data, "image/png");
joshualitt483b9012016-02-02 07:16:24 -0800316 }
317};
joshualittccfdaa52016-01-27 07:40:29 -0800318
ethannicholas0a0520a2016-02-12 12:06:53 -0800319/**
320 Updates the clip visualization alpha. On all subsequent /img requests, the clip will be drawn in
321 black with the specified alpha. 0 = no visible clip, 255 = fully opaque clip.
322 */
323class ClipAlphaHandler : public UrlHandler {
324public:
325 bool canHandle(const char* method, const char* url) override {
326 static const char* kBasePath = "/clipAlpha/";
jcgregorio3341d422016-02-16 10:31:07 -0800327 return 0 == strcmp(method, MHD_HTTP_METHOD_POST) &&
ethannicholas0a0520a2016-02-12 12:06:53 -0800328 0 == strncmp(url, kBasePath, strlen(kBasePath));
329 }
330
331 int handle(Request* request, MHD_Connection* connection,
332 const char* url, const char* method,
333 const char* upload_data, size_t* upload_data_size) override {
334 SkTArray<SkString> commands;
335 SkStrSplit(url, "/", &commands);
336
337 if (!request->fPicture.get() || commands.count() != 2) {
338 return MHD_NO;
339 }
340
341 int alpha;
342 sscanf(commands[1].c_str(), "%d", &alpha);
343
344 request->fDebugCanvas->setClipVizColor(SkColorSetARGB(alpha, 0, 0, 0));
345 return SendOK(connection);
346 }
347};
348
joshualitt483b9012016-02-02 07:16:24 -0800349class PostHandler : public UrlHandler {
350public:
351 bool canHandle(const char* method, const char* url) override {
352 return 0 == strcmp(method, MHD_HTTP_METHOD_POST) &&
353 0 == strcmp(url, "/new");
joshualittccfdaa52016-01-27 07:40:29 -0800354 }
355
joshualitt483b9012016-02-02 07:16:24 -0800356 int handle(Request* request, MHD_Connection* connection,
joshualitt136f5172016-02-02 11:07:39 -0800357 const char* url, const char* method,
joshualitt483b9012016-02-02 07:16:24 -0800358 const char* upload_data, size_t* upload_data_size) override {
359 UploadContext* uc = request->fUploadContext;
joshualittccfdaa52016-01-27 07:40:29 -0800360
joshualitt483b9012016-02-02 07:16:24 -0800361 // New connection
362 if (!uc) {
363 // TODO make this a method on request
364 uc = new UploadContext;
365 uc->connection = connection;
366 uc->fPostProcessor = MHD_create_post_processor(connection, kBufferSize,
367 &process_upload_data, uc);
368 SkASSERT(uc->fPostProcessor);
joshualitt609d9792016-01-27 11:07:23 -0800369
joshualitt483b9012016-02-02 07:16:24 -0800370 request->fUploadContext = uc;
371 return MHD_YES;
372 }
373
374 // in process upload
375 if (0 != *upload_data_size) {
376 SkASSERT(uc->fPostProcessor);
377 MHD_post_process(uc->fPostProcessor, upload_data, *upload_data_size);
378 *upload_data_size = 0;
379 return MHD_YES;
380 }
381
382 // end of upload
383 MHD_destroy_post_processor(uc->fPostProcessor);
384 uc->fPostProcessor = nullptr;
385
joshualitt136f5172016-02-02 11:07:39 -0800386 // parse picture from stream
387 request->fPicture.reset(
388 SkPicture::CreateFromStream(request->fUploadContext->fStream.detachAsStream()));
389 if (!request->fPicture.get()) {
390 fprintf(stderr, "Could not create picture from stream.\n");
391 return MHD_NO;
joshualitt483b9012016-02-02 07:16:24 -0800392 }
393
ethannicholasde8e54c2016-02-12 10:09:34 -0800394 // create surface
395 GrContextOptions grContextOpts;
396 request->fContextFactory.reset(new GrContextFactory(grContextOpts));
397 request->fSurface.reset(setupSurface(request->fContextFactory.get()));
398
joshualitt136f5172016-02-02 11:07:39 -0800399 // pour picture into debug canvas
400 request->fDebugCanvas.reset(new SkDebugCanvas(kImageWidth, kImageHeight));
401 request->fDebugCanvas->drawPicture(request->fPicture);
402
joshualitta341b902016-02-02 07:37:21 -0800403 // clear upload context
404 delete request->fUploadContext;
405 request->fUploadContext = nullptr;
406
joshualitt483b9012016-02-02 07:16:24 -0800407 return SendTemplate(connection, true, "/");
joshualittccfdaa52016-01-27 07:40:29 -0800408 }
joshualitt483b9012016-02-02 07:16:24 -0800409};
410
joshualitt792345f2016-02-02 13:02:33 -0800411class DownloadHandler : public UrlHandler {
412public:
413 bool canHandle(const char* method, const char* url) override {
414 return 0 == strcmp(method, MHD_HTTP_METHOD_GET) &&
415 0 == strcmp(url, "/download");
416 }
417
418 int handle(Request* request, MHD_Connection* connection,
419 const char* url, const char* method,
420 const char* upload_data, size_t* upload_data_size) override {
421 if (!request->fPicture.get()) {
422 return MHD_NO;
423 }
424
425 // TODO move to a function
426 // Playback into picture recorder
427 SkPictureRecorder recorder;
428 SkCanvas* canvas = recorder.beginRecording(kImageWidth, kImageHeight);
429
430 request->fDebugCanvas->draw(canvas);
431
432 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
433
434 SkDynamicMemoryWStream outStream;
435
436 SkAutoTUnref<SkPixelSerializer> serializer(SkImageEncoder::CreatePixelSerializer());
437 picture->serialize(&outStream, serializer);
438
439 SkAutoTUnref<SkData> data(outStream.copyToData());
440
441 // TODO fancier name handling
442 return SendData(connection, data, "application/octet-stream", true,
443 "attachment; filename=something.skp;");
444 }
445};
446
joshualitt29e5a892016-02-04 06:08:33 -0800447class InfoHandler : public UrlHandler {
448public:
449 bool canHandle(const char* method, const char* url) override {
450 const char* kBaseName = "/info";
451 return 0 == strcmp(method, MHD_HTTP_METHOD_GET) &&
452 0 == strncmp(url, kBaseName, strlen(kBaseName));
453 }
454
455 int handle(Request* request, MHD_Connection* connection,
456 const char* url, const char* method,
457 const char* upload_data, size_t* upload_data_size) override {
458 SkTArray<SkString> commands;
459 SkStrSplit(url, "/", &commands);
460
461 if (!request->fPicture.get() || commands.count() > 2) {
462 return MHD_NO;
463 }
464
465 // drawTo
466 SkAutoTUnref<SkSurface> surface(setupCpuSurface());
467 SkCanvas* canvas = surface->getCanvas();
468
469 int n;
470 // /info or /info/N
471 if (commands.count() == 1) {
472 n = request->fDebugCanvas->getSize() - 1;
473 } else {
474 sscanf(commands[1].c_str(), "%d", &n);
475 }
476
477 // TODO this is really slow and we should cache the matrix and clip
478 request->fDebugCanvas->drawTo(canvas, n);
479
480 // make some json
481 SkMatrix vm = request->fDebugCanvas->getCurrentMatrix();
482 SkIRect clip = request->fDebugCanvas->getCurrentClip();
483 Json::Value info(Json::objectValue);
484 info["ViewMatrix"] = SkJSONCanvas::MakeMatrix(vm);
485 info["ClipRect"] = SkJSONCanvas::MakeIRect(clip);
486
487 std::string json = Json::FastWriter().write(info);
488
489 // We don't want the null terminator so strlen is correct
490 SkAutoTUnref<SkData> data(SkData::NewWithCopy(json.c_str(), strlen(json.c_str())));
491 return SendData(connection, data, "application/json");
492 }
493};
494
joshualittcdad12f2016-02-08 07:08:21 -0800495class DataHandler : public UrlHandler {
496public:
497 bool canHandle(const char* method, const char* url) override {
498 static const char* kBaseUrl = "/data";
499 return 0 == strcmp(method, MHD_HTTP_METHOD_GET) &&
500 0 == strncmp(url, kBaseUrl, strlen(kBaseUrl));
501 }
502
503 int handle(Request* request, MHD_Connection* connection,
504 const char* url, const char* method,
505 const char* upload_data, size_t* upload_data_size) override {
506 SkTArray<SkString> commands;
507 SkStrSplit(url, "/", &commands);
508
509 if (!request->fPicture.get() || commands.count() != 2) {
510 return MHD_NO;
511 }
512
513 SkAutoTUnref<UrlDataManager::UrlData> urlData(
514 SkRef(request->fUrlDataManager.getDataFromUrl(SkString(url))));
515
516 if (urlData) {
517 return SendData(connection, urlData->fData.get(), urlData->fContentType.c_str());
518 }
519 return MHD_NO;
520 }
521};
joshualitt29e5a892016-02-04 06:08:33 -0800522
joshualitt873d6242016-02-08 13:57:44 -0800523class FaviconHandler : public UrlHandler {
524public:
525 bool canHandle(const char* method, const char* url) override {
526 return 0 == strcmp(method, MHD_HTTP_METHOD_GET) &&
527 0 == strcmp(url, "/favicon.ico");
528 }
529
530 int handle(Request* request, MHD_Connection* connection,
531 const char* url, const char* method,
532 const char* upload_data, size_t* upload_data_size) override {
533 SkString dir(FLAGS_faviconDir[0]);
534 dir.append("/favicon.ico");
535 FILE* ico = fopen(dir.c_str(), "r");
536
537 SkAutoTUnref<SkData> data(SkData::NewFromFILE(ico));
538 int ret = SendData(connection, data, "image/vnd.microsoft.icon");
539 fclose(ico);
540 return ret;
541 }
542};
543
544
joshualitt483b9012016-02-02 07:16:24 -0800545class RootHandler : public UrlHandler {
546public:
547 bool canHandle(const char* method, const char* url) override {
548 return 0 == strcmp(method, MHD_HTTP_METHOD_GET) &&
549 0 == strcmp(url, "/");
550 }
551
552 int handle(Request* request, MHD_Connection* connection,
joshualitt136f5172016-02-02 11:07:39 -0800553 const char* url, const char* method,
joshualitt483b9012016-02-02 07:16:24 -0800554 const char* upload_data, size_t* upload_data_size) override {
555 return SendTemplate(connection);
556 }
557};
joshualittccfdaa52016-01-27 07:40:29 -0800558
559class UrlManager {
560public:
561 UrlManager() {
562 // Register handlers
joshualitt483b9012016-02-02 07:16:24 -0800563 fHandlers.push_back(new RootHandler);
564 fHandlers.push_back(new PostHandler);
565 fHandlers.push_back(new ImgHandler);
ethannicholas0a0520a2016-02-12 12:06:53 -0800566 fHandlers.push_back(new ClipAlphaHandler);
joshualitt29e5a892016-02-04 06:08:33 -0800567 fHandlers.push_back(new CmdHandler);
joshualitt483b9012016-02-02 07:16:24 -0800568 fHandlers.push_back(new InfoHandler);
joshualitt792345f2016-02-02 13:02:33 -0800569 fHandlers.push_back(new DownloadHandler);
joshualittcdad12f2016-02-08 07:08:21 -0800570 fHandlers.push_back(new DataHandler);
joshualitt873d6242016-02-08 13:57:44 -0800571 fHandlers.push_back(new FaviconHandler);
joshualitt483b9012016-02-02 07:16:24 -0800572 }
573
574 ~UrlManager() {
575 for (int i = 0; i < fHandlers.count(); i++) { delete fHandlers[i]; }
joshualittccfdaa52016-01-27 07:40:29 -0800576 }
577
578 // This is clearly not efficient for a large number of urls and handlers
579 int invoke(Request* request, MHD_Connection* connection, const char* url, const char* method,
580 const char* upload_data, size_t* upload_data_size) const {
581 for (int i = 0; i < fHandlers.count(); i++) {
joshualitt483b9012016-02-02 07:16:24 -0800582 if (fHandlers[i]->canHandle(method, url)) {
joshualitt136f5172016-02-02 11:07:39 -0800583 return fHandlers[i]->handle(request, connection, url, method, upload_data,
584 upload_data_size);
joshualittccfdaa52016-01-27 07:40:29 -0800585 }
586 }
587 return MHD_NO;
588 }
589
590private:
joshualitt483b9012016-02-02 07:16:24 -0800591 SkTArray<UrlHandler*> fHandlers;
joshualittccfdaa52016-01-27 07:40:29 -0800592};
593
594const UrlManager kUrlManager;
595
joshualitt7f6a1e02016-01-22 11:21:43 -0800596int answer_to_connection(void* cls, struct MHD_Connection* connection,
597 const char* url, const char* method, const char* version,
598 const char* upload_data, size_t* upload_data_size,
599 void** con_cls) {
joshualitt9a4e1882016-01-27 07:03:29 -0800600 SkDebugf("New %s request for %s using version %s\n", method, url, version);
joshualitt7f6a1e02016-01-22 11:21:43 -0800601
joshualitt9a4e1882016-01-27 07:03:29 -0800602 Request* request = reinterpret_cast<Request*>(cls);
joshualitt483b9012016-02-02 07:16:24 -0800603 int result = kUrlManager.invoke(request, connection, url, method, upload_data,
604 upload_data_size);
605 if (MHD_NO == result) {
joshualitt873d6242016-02-08 13:57:44 -0800606 fprintf(stderr, "Invalid method and / or url: %s %s\n", method, url);
joshualitt483b9012016-02-02 07:16:24 -0800607 }
608 return result;
joshualitt7f6a1e02016-01-22 11:21:43 -0800609}
610
611int skiaserve_main() {
joshualittcdad12f2016-02-08 07:08:21 -0800612 Request request(SkString("/data")); // This simple server has one request
joshualitt7f6a1e02016-01-22 11:21:43 -0800613 struct MHD_Daemon* daemon;
jcgregorio21ab1202016-01-28 06:24:19 -0800614 // TODO Add option to bind this strictly to an address, e.g. localhost, for security.
615 daemon = MHD_start_daemon(MHD_USE_SELECT_INTERNALLY, FLAGS_port, nullptr, nullptr,
joshualitt9a4e1882016-01-27 07:03:29 -0800616 &answer_to_connection, &request,
617 MHD_OPTION_END);
joshualitt7f6a1e02016-01-22 11:21:43 -0800618 if (NULL == daemon) {
619 return 1;
620 }
621
622 getchar();
623 MHD_stop_daemon(daemon);
624 return 0;
625}
626
627#if !defined SK_BUILD_FOR_IOS
628int main(int argc, char** argv) {
629 SkCommandLineFlags::Parse(argc, argv);
630 return skiaserve_main();
631}
632#endif