blob: 4b2d52b6124f392b038ad522a1ae8f3d6a511430 [file] [log] [blame]
joshualitt7f6a1e02016-01-22 11:21:43 -08001/*
2 * Copyright 2016 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "GrCaps.h"
9#include "GrContextFactory.h"
10#include "SkCanvas.h"
11#include "SkCommandLineFlags.h"
joshualitt136f5172016-02-02 11:07:39 -080012#include "SkDebugCanvas.h"
joshualitt609d9792016-01-27 11:07:23 -080013#include "SkJSONCanvas.h"
joshualitt29e5a892016-02-04 06:08:33 -080014#include "SkJSONCPP.h"
joshualitt9a4e1882016-01-27 07:03:29 -080015#include "SkPicture.h"
joshualitt792345f2016-02-02 13:02:33 -080016#include "SkPictureRecorder.h"
17#include "SkPixelSerializer.h"
joshualitt7f6a1e02016-01-22 11:21:43 -080018#include "SkStream.h"
19#include "SkSurface.h"
20
joshualittcdad12f2016-02-08 07:08:21 -080021#include "UrlDataManager.h"
22
joshualitt7f6a1e02016-01-22 11:21:43 -080023#include <sys/socket.h>
joshualitt7f6a1e02016-01-22 11:21:43 -080024#include <microhttpd.h>
25
26// To get image decoders linked in we have to do the below magic
27#include "SkForceLinking.h"
28#include "SkImageDecoder.h"
29__SK_FORCE_IMAGE_DECODER_LINKING;
30
jcgregorio21ab1202016-01-28 06:24:19 -080031DEFINE_string(source, "https://debugger.skia.org", "Where to load the web UI from.");
joshualitt873d6242016-02-08 13:57:44 -080032DEFINE_string(faviconDir, "tools/skiaserve", "The directory of the favicon");
jcgregorio21ab1202016-01-28 06:24:19 -080033DEFINE_int32(port, 8888, "The port to listen on.");
joshualitt7f6a1e02016-01-22 11:21:43 -080034
35// TODO probably want to make this configurable
36static const int kImageWidth = 1920;
37static const int kImageHeight = 1080;
38
joshualitt7f6a1e02016-01-22 11:21:43 -080039// TODO move to template file
40SkString generateTemplate(SkString source) {
41 SkString debuggerTemplate;
42 debuggerTemplate.appendf(
43 "<!DOCTYPE html>\n"
44 "<html>\n"
45 "<head>\n"
46 " <title>SkDebugger</title>\n"
47 " <meta charset=\"utf-8\" />\n"
48 " <meta http-equiv=\"X-UA-Compatible\" content=\"IE=egde,chrome=1\">\n"
49 " <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n"
50 " <script src=\"%s/res/js/core.js\" type=\"text/javascript\" charset=\"utf-8\"></script>\n"
51 " <link href=\"%s/res/vul/elements.html\" rel=\"import\" />\n"
52 "</head>\n"
53 "<body class=\"fullbleed layout vertical\">\n"
54 " <debugger-app-sk>This is the app."
55 " </debugger-app-sk>\n"
56 "</body>\n"
57 "</html>", source.c_str(), source.c_str());
58 return debuggerTemplate;
59
60}
61
joshualitt9a4e1882016-01-27 07:03:29 -080062struct UploadContext {
joshualitt609d9792016-01-27 11:07:23 -080063 SkDynamicMemoryWStream fStream;
64 MHD_PostProcessor* fPostProcessor;
joshualitt9a4e1882016-01-27 07:03:29 -080065 MHD_Connection* connection;
66};
67
68struct Request {
joshualittcdad12f2016-02-08 07:08:21 -080069 Request(SkString rootUrl) : fUploadContext(nullptr), fUrlDataManager(rootUrl) {}
joshualitt9a4e1882016-01-27 07:03:29 -080070 UploadContext* fUploadContext;
joshualitt609d9792016-01-27 11:07:23 -080071 SkAutoTUnref<SkPicture> fPicture;
joshualitt136f5172016-02-02 11:07:39 -080072 SkAutoTUnref<SkDebugCanvas> fDebugCanvas;
joshualittcdad12f2016-02-08 07:08:21 -080073 UrlDataManager fUrlDataManager;
joshualitt9a4e1882016-01-27 07:03:29 -080074};
75
joshualitt609d9792016-01-27 11:07:23 -080076// TODO factor this out into functions, also handle CPU path
joshualitt136f5172016-02-02 11:07:39 -080077SkSurface* setupSurface(GrContextFactory* factory) {
joshualitt609d9792016-01-27 11:07:23 -080078 GrContext* context = factory->get(GrContextFactory::kNative_GLContextType,
79 GrContextFactory::kNone_GLContextOptions);
80 int maxRTSize = context->caps()->maxRenderTargetSize();
81 SkImageInfo info = SkImageInfo::Make(SkTMin(kImageWidth, maxRTSize),
82 SkTMin(kImageHeight, maxRTSize),
83 kN32_SkColorType, kPremul_SkAlphaType);
84 uint32_t flags = 0;
85 SkSurfaceProps props(flags, SkSurfaceProps::kLegacyFontHost_InitType);
joshualitt136f5172016-02-02 11:07:39 -080086 SkSurface* surface = SkSurface::NewRenderTarget(context, SkSurface::kNo_Budgeted, info, 0,
87 &props);
88 SkASSERT(surface);
joshualitt609d9792016-01-27 11:07:23 -080089
90 SkGLContext* gl = factory->getContextInfo(GrContextFactory::kNative_GLContextType,
91 GrContextFactory::kNone_GLContextOptions).fGLContext;
92 gl->makeCurrent();
joshualitt136f5172016-02-02 11:07:39 -080093 return surface;
94}
joshualitt609d9792016-01-27 11:07:23 -080095
joshualitt136f5172016-02-02 11:07:39 -080096SkData* writeCanvasToPng(SkCanvas* canvas) {
joshualitt609d9792016-01-27 11:07:23 -080097 // capture pixels
98 SkBitmap bmp;
99 bmp.setInfo(canvas->imageInfo());
100 if (!canvas->readPixels(&bmp, 0, 0)) {
joshualitt136f5172016-02-02 11:07:39 -0800101 fprintf(stderr, "Can't read pixels\n");
102 return nullptr;
joshualitt609d9792016-01-27 11:07:23 -0800103 }
104
105 // write to png
joshualittcdad12f2016-02-08 07:08:21 -0800106 // TODO encoding to png can be quite slow, we should investigate bmp
joshualitt136f5172016-02-02 11:07:39 -0800107 SkData* png = SkImageEncoder::EncodeData(bmp, SkImageEncoder::kPNG_Type, 100);
108 if (!png) {
109 fprintf(stderr, "Can't encode to png\n");
110 return nullptr;
joshualitt609d9792016-01-27 11:07:23 -0800111 }
joshualitt136f5172016-02-02 11:07:39 -0800112 return png;
113}
114
115SkData* setupAndDrawToCanvasReturnPng(SkDebugCanvas* debugCanvas, int n) {
116 GrContextOptions grContextOpts;
117 SkAutoTDelete<GrContextFactory> factory(new GrContextFactory(grContextOpts));
118 SkAutoTUnref<SkSurface> surface(setupSurface(factory.get()));
119
120 SkASSERT(debugCanvas);
121 SkCanvas* canvas = surface->getCanvas();
122 debugCanvas->drawTo(canvas, n);
123 return writeCanvasToPng(canvas);
joshualitt609d9792016-01-27 11:07:23 -0800124}
125
joshualitt29e5a892016-02-04 06:08:33 -0800126SkSurface* setupCpuSurface() {
127 SkImageInfo info = SkImageInfo::Make(kImageWidth, kImageHeight, kN32_SkColorType,
128 kPremul_SkAlphaType);
129 return SkSurface::NewRaster(info);
130}
131
joshualitt9a4e1882016-01-27 07:03:29 -0800132static const size_t kBufferSize = 1024;
133
134static int process_upload_data(void* cls, enum MHD_ValueKind kind,
135 const char* key, const char* filename,
136 const char* content_type, const char* transfer_encoding,
137 const char* data, uint64_t off, size_t size) {
138 struct UploadContext* uc = reinterpret_cast<UploadContext*>(cls);
139
140 if (0 != size) {
joshualitt609d9792016-01-27 11:07:23 -0800141 uc->fStream.write(data, size);
joshualitt9a4e1882016-01-27 07:03:29 -0800142 }
143 return MHD_YES;
144}
145
jcgregorio12d47ce2016-02-10 14:10:37 -0800146// SendOK just sends an empty response with a 200 OK status code.
147static int SendOK(MHD_Connection* connection) {
148 const char* data = "";
149
150 MHD_Response* response = MHD_create_response_from_buffer(strlen(data),
151 (void*)data,
152 MHD_RESPMEM_PERSISTENT);
153 int ret = MHD_queue_response(connection, 200, response);
154 MHD_destroy_response(response);
155 return ret;
156}
157
joshualitt792345f2016-02-02 13:02:33 -0800158static int SendData(MHD_Connection* connection, const SkData* data, const char* type,
159 bool setContentDisposition = false, const char* dispositionString = nullptr) {
joshualittccfdaa52016-01-27 07:40:29 -0800160 MHD_Response* response = MHD_create_response_from_buffer(data->size(),
161 const_cast<void*>(data->data()),
162 MHD_RESPMEM_MUST_COPY);
joshualitt609d9792016-01-27 11:07:23 -0800163 MHD_add_response_header(response, "Content-Type", type);
joshualittccfdaa52016-01-27 07:40:29 -0800164
joshualitt792345f2016-02-02 13:02:33 -0800165 if (setContentDisposition) {
166 MHD_add_response_header(response, "Content-Disposition", dispositionString);
167 }
168
joshualittccfdaa52016-01-27 07:40:29 -0800169 int ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
170 MHD_destroy_response(response);
171 return ret;
172}
173
joshualittdb6a2542016-02-11 07:09:51 -0800174static int SendJSON(MHD_Connection* connection, SkDebugCanvas* debugCanvas,
175 UrlDataManager* urlDataManager, int n) {
176 Json::Value root = debugCanvas->toJSON(*urlDataManager, n);
joshualitt609d9792016-01-27 11:07:23 -0800177 SkDynamicMemoryWStream stream;
joshualittdb6a2542016-02-11 07:09:51 -0800178 stream.writeText(Json::FastWriter().write(root).c_str());
joshualitt609d9792016-01-27 11:07:23 -0800179
180 SkAutoTUnref<SkData> data(stream.copyToData());
181 return SendData(connection, data, "application/json");
182}
183
184static int SendTemplate(MHD_Connection* connection, bool redirect = false,
185 const char* redirectUrl = nullptr) {
jcgregorio21ab1202016-01-28 06:24:19 -0800186 SkString debuggerTemplate = generateTemplate(SkString(FLAGS_source[0]));
joshualittccfdaa52016-01-27 07:40:29 -0800187
188 MHD_Response* response = MHD_create_response_from_buffer(
189 debuggerTemplate.size(),
190 (void*) const_cast<char*>(debuggerTemplate.c_str()),
191 MHD_RESPMEM_MUST_COPY);
jcgregorio6a2046e2016-01-28 05:31:31 -0800192 MHD_add_response_header (response, "Access-Control-Allow-Origin", "*");
joshualittccfdaa52016-01-27 07:40:29 -0800193
jcgregorio6f17bc52016-01-27 11:44:38 -0800194 int status = MHD_HTTP_OK;
195
joshualitt609d9792016-01-27 11:07:23 -0800196 if (redirect) {
197 MHD_add_response_header (response, "Location", redirectUrl);
jcgregorio6f17bc52016-01-27 11:44:38 -0800198 status = MHD_HTTP_SEE_OTHER;
joshualitt609d9792016-01-27 11:07:23 -0800199 }
200
jcgregorio6f17bc52016-01-27 11:44:38 -0800201 int ret = MHD_queue_response(connection, status, response);
joshualittccfdaa52016-01-27 07:40:29 -0800202 MHD_destroy_response(response);
203 return ret;
204}
205
joshualitt483b9012016-02-02 07:16:24 -0800206class UrlHandler {
207public:
208 virtual ~UrlHandler() {}
209 virtual bool canHandle(const char* method, const char* url) = 0;
210 virtual int handle(Request* request, MHD_Connection* connection,
joshualitt136f5172016-02-02 11:07:39 -0800211 const char* url, const char* method,
joshualitt483b9012016-02-02 07:16:24 -0800212 const char* upload_data, size_t* upload_data_size) = 0;
213};
joshualittccfdaa52016-01-27 07:40:29 -0800214
joshualitt29e5a892016-02-04 06:08:33 -0800215class CmdHandler : public UrlHandler {
joshualitt483b9012016-02-02 07:16:24 -0800216public:
217 bool canHandle(const char* method, const char* url) override {
joshualitt136f5172016-02-02 11:07:39 -0800218 const char* kBasePath = "/cmd";
219 return 0 == strncmp(url, kBasePath, strlen(kBasePath));
joshualittccfdaa52016-01-27 07:40:29 -0800220 }
221
joshualitt483b9012016-02-02 07:16:24 -0800222 int handle(Request* request, MHD_Connection* connection,
joshualitt136f5172016-02-02 11:07:39 -0800223 const char* url, const char* method,
joshualitt483b9012016-02-02 07:16:24 -0800224 const char* upload_data, size_t* upload_data_size) override {
joshualitt136f5172016-02-02 11:07:39 -0800225 SkTArray<SkString> commands;
226 SkStrSplit(url, "/", &commands);
227
228 if (!request->fPicture.get() || commands.count() > 3) {
229 return MHD_NO;
joshualitt483b9012016-02-02 07:16:24 -0800230 }
joshualitt136f5172016-02-02 11:07:39 -0800231
joshualittdb6a2542016-02-11 07:09:51 -0800232 // /cmd or /cmd/N
233 if (0 == strcmp(method, MHD_HTTP_METHOD_GET)) {
234 int n;
235 if (commands.count() == 1) {
236 n = request->fDebugCanvas->getSize() - 1;
237 } else {
238 sscanf(commands[1].c_str(), "%d", &n);
239 }
240 return SendJSON(connection, request->fDebugCanvas, &request->fUrlDataManager, n);
joshualitt136f5172016-02-02 11:07:39 -0800241 }
242
243 // /cmd/N, for now only delete supported
244 if (commands.count() == 2 && 0 == strcmp(method, MHD_HTTP_METHOD_DELETE)) {
245 int n;
246 sscanf(commands[1].c_str(), "%d", &n);
247 request->fDebugCanvas->deleteDrawCommandAt(n);
jcgregorio12d47ce2016-02-10 14:10:37 -0800248 return SendOK(connection);
joshualitt136f5172016-02-02 11:07:39 -0800249 }
250
251 // /cmd/N/[0|1]
252 if (commands.count() == 3 && 0 == strcmp(method, MHD_HTTP_METHOD_POST)) {
253 int n, toggle;
254 sscanf(commands[1].c_str(), "%d", &n);
255 sscanf(commands[2].c_str(), "%d", &toggle);
256 request->fDebugCanvas->toggleCommand(n, toggle);
jcgregorio12d47ce2016-02-10 14:10:37 -0800257 return SendOK(connection);
joshualitt136f5172016-02-02 11:07:39 -0800258 }
259
joshualitt483b9012016-02-02 07:16:24 -0800260 return MHD_NO;
261 }
262};
263
264class ImgHandler : public UrlHandler {
265public:
266 bool canHandle(const char* method, const char* url) override {
267 static const char* kBasePath = "/img";
268 return 0 == strcmp(method, MHD_HTTP_METHOD_GET) &&
269 0 == strncmp(url, kBasePath, strlen(kBasePath));
joshualittccfdaa52016-01-27 07:40:29 -0800270 }
271
joshualitt483b9012016-02-02 07:16:24 -0800272 int handle(Request* request, MHD_Connection* connection,
joshualitt136f5172016-02-02 11:07:39 -0800273 const char* url, const char* method,
joshualitt483b9012016-02-02 07:16:24 -0800274 const char* upload_data, size_t* upload_data_size) override {
joshualitt136f5172016-02-02 11:07:39 -0800275 SkTArray<SkString> commands;
276 SkStrSplit(url, "/", &commands);
277
278 if (!request->fPicture.get() || commands.count() > 2) {
279 return MHD_NO;
joshualitt483b9012016-02-02 07:16:24 -0800280 }
joshualitta341b902016-02-02 07:37:21 -0800281
joshualitt136f5172016-02-02 11:07:39 -0800282 int n;
283 // /img or /img/N
284 if (commands.count() == 1) {
285 n = request->fDebugCanvas->getSize() - 1;
286 } else {
287 sscanf(commands[1].c_str(), "%d", &n);
288 }
289
290 SkAutoTUnref<SkData> data(setupAndDrawToCanvasReturnPng(request->fDebugCanvas, n));
291 return SendData(connection, data, "image/png");
joshualitt483b9012016-02-02 07:16:24 -0800292 }
293};
joshualittccfdaa52016-01-27 07:40:29 -0800294
joshualitt483b9012016-02-02 07:16:24 -0800295class PostHandler : public UrlHandler {
296public:
297 bool canHandle(const char* method, const char* url) override {
298 return 0 == strcmp(method, MHD_HTTP_METHOD_POST) &&
299 0 == strcmp(url, "/new");
joshualittccfdaa52016-01-27 07:40:29 -0800300 }
301
joshualitt483b9012016-02-02 07:16:24 -0800302 int handle(Request* request, MHD_Connection* connection,
joshualitt136f5172016-02-02 11:07:39 -0800303 const char* url, const char* method,
joshualitt483b9012016-02-02 07:16:24 -0800304 const char* upload_data, size_t* upload_data_size) override {
305 UploadContext* uc = request->fUploadContext;
joshualittccfdaa52016-01-27 07:40:29 -0800306
joshualitt483b9012016-02-02 07:16:24 -0800307 // New connection
308 if (!uc) {
309 // TODO make this a method on request
310 uc = new UploadContext;
311 uc->connection = connection;
312 uc->fPostProcessor = MHD_create_post_processor(connection, kBufferSize,
313 &process_upload_data, uc);
314 SkASSERT(uc->fPostProcessor);
joshualitt609d9792016-01-27 11:07:23 -0800315
joshualitt483b9012016-02-02 07:16:24 -0800316 request->fUploadContext = uc;
317 return MHD_YES;
318 }
319
320 // in process upload
321 if (0 != *upload_data_size) {
322 SkASSERT(uc->fPostProcessor);
323 MHD_post_process(uc->fPostProcessor, upload_data, *upload_data_size);
324 *upload_data_size = 0;
325 return MHD_YES;
326 }
327
328 // end of upload
329 MHD_destroy_post_processor(uc->fPostProcessor);
330 uc->fPostProcessor = nullptr;
331
joshualitt136f5172016-02-02 11:07:39 -0800332 // parse picture from stream
333 request->fPicture.reset(
334 SkPicture::CreateFromStream(request->fUploadContext->fStream.detachAsStream()));
335 if (!request->fPicture.get()) {
336 fprintf(stderr, "Could not create picture from stream.\n");
337 return MHD_NO;
joshualitt483b9012016-02-02 07:16:24 -0800338 }
339
joshualitt136f5172016-02-02 11:07:39 -0800340 // pour picture into debug canvas
341 request->fDebugCanvas.reset(new SkDebugCanvas(kImageWidth, kImageHeight));
342 request->fDebugCanvas->drawPicture(request->fPicture);
343
joshualitta341b902016-02-02 07:37:21 -0800344 // clear upload context
345 delete request->fUploadContext;
346 request->fUploadContext = nullptr;
347
joshualitt483b9012016-02-02 07:16:24 -0800348 return SendTemplate(connection, true, "/");
joshualittccfdaa52016-01-27 07:40:29 -0800349 }
joshualitt483b9012016-02-02 07:16:24 -0800350};
351
joshualitt792345f2016-02-02 13:02:33 -0800352class DownloadHandler : public UrlHandler {
353public:
354 bool canHandle(const char* method, const char* url) override {
355 return 0 == strcmp(method, MHD_HTTP_METHOD_GET) &&
356 0 == strcmp(url, "/download");
357 }
358
359 int handle(Request* request, MHD_Connection* connection,
360 const char* url, const char* method,
361 const char* upload_data, size_t* upload_data_size) override {
362 if (!request->fPicture.get()) {
363 return MHD_NO;
364 }
365
366 // TODO move to a function
367 // Playback into picture recorder
368 SkPictureRecorder recorder;
369 SkCanvas* canvas = recorder.beginRecording(kImageWidth, kImageHeight);
370
371 request->fDebugCanvas->draw(canvas);
372
373 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
374
375 SkDynamicMemoryWStream outStream;
376
377 SkAutoTUnref<SkPixelSerializer> serializer(SkImageEncoder::CreatePixelSerializer());
378 picture->serialize(&outStream, serializer);
379
380 SkAutoTUnref<SkData> data(outStream.copyToData());
381
382 // TODO fancier name handling
383 return SendData(connection, data, "application/octet-stream", true,
384 "attachment; filename=something.skp;");
385 }
386};
387
joshualitt29e5a892016-02-04 06:08:33 -0800388class InfoHandler : public UrlHandler {
389public:
390 bool canHandle(const char* method, const char* url) override {
391 const char* kBaseName = "/info";
392 return 0 == strcmp(method, MHD_HTTP_METHOD_GET) &&
393 0 == strncmp(url, kBaseName, strlen(kBaseName));
394 }
395
396 int handle(Request* request, MHD_Connection* connection,
397 const char* url, const char* method,
398 const char* upload_data, size_t* upload_data_size) override {
399 SkTArray<SkString> commands;
400 SkStrSplit(url, "/", &commands);
401
402 if (!request->fPicture.get() || commands.count() > 2) {
403 return MHD_NO;
404 }
405
406 // drawTo
407 SkAutoTUnref<SkSurface> surface(setupCpuSurface());
408 SkCanvas* canvas = surface->getCanvas();
409
410 int n;
411 // /info or /info/N
412 if (commands.count() == 1) {
413 n = request->fDebugCanvas->getSize() - 1;
414 } else {
415 sscanf(commands[1].c_str(), "%d", &n);
416 }
417
418 // TODO this is really slow and we should cache the matrix and clip
419 request->fDebugCanvas->drawTo(canvas, n);
420
421 // make some json
422 SkMatrix vm = request->fDebugCanvas->getCurrentMatrix();
423 SkIRect clip = request->fDebugCanvas->getCurrentClip();
424 Json::Value info(Json::objectValue);
425 info["ViewMatrix"] = SkJSONCanvas::MakeMatrix(vm);
426 info["ClipRect"] = SkJSONCanvas::MakeIRect(clip);
427
428 std::string json = Json::FastWriter().write(info);
429
430 // We don't want the null terminator so strlen is correct
431 SkAutoTUnref<SkData> data(SkData::NewWithCopy(json.c_str(), strlen(json.c_str())));
432 return SendData(connection, data, "application/json");
433 }
434};
435
joshualittcdad12f2016-02-08 07:08:21 -0800436class DataHandler : public UrlHandler {
437public:
438 bool canHandle(const char* method, const char* url) override {
439 static const char* kBaseUrl = "/data";
440 return 0 == strcmp(method, MHD_HTTP_METHOD_GET) &&
441 0 == strncmp(url, kBaseUrl, strlen(kBaseUrl));
442 }
443
444 int handle(Request* request, MHD_Connection* connection,
445 const char* url, const char* method,
446 const char* upload_data, size_t* upload_data_size) override {
447 SkTArray<SkString> commands;
448 SkStrSplit(url, "/", &commands);
449
450 if (!request->fPicture.get() || commands.count() != 2) {
451 return MHD_NO;
452 }
453
454 SkAutoTUnref<UrlDataManager::UrlData> urlData(
455 SkRef(request->fUrlDataManager.getDataFromUrl(SkString(url))));
456
457 if (urlData) {
458 return SendData(connection, urlData->fData.get(), urlData->fContentType.c_str());
459 }
460 return MHD_NO;
461 }
462};
joshualitt29e5a892016-02-04 06:08:33 -0800463
joshualitt873d6242016-02-08 13:57:44 -0800464class FaviconHandler : public UrlHandler {
465public:
466 bool canHandle(const char* method, const char* url) override {
467 return 0 == strcmp(method, MHD_HTTP_METHOD_GET) &&
468 0 == strcmp(url, "/favicon.ico");
469 }
470
471 int handle(Request* request, MHD_Connection* connection,
472 const char* url, const char* method,
473 const char* upload_data, size_t* upload_data_size) override {
474 SkString dir(FLAGS_faviconDir[0]);
475 dir.append("/favicon.ico");
476 FILE* ico = fopen(dir.c_str(), "r");
477
478 SkAutoTUnref<SkData> data(SkData::NewFromFILE(ico));
479 int ret = SendData(connection, data, "image/vnd.microsoft.icon");
480 fclose(ico);
481 return ret;
482 }
483};
484
485
joshualitt483b9012016-02-02 07:16:24 -0800486class RootHandler : public UrlHandler {
487public:
488 bool canHandle(const char* method, const char* url) override {
489 return 0 == strcmp(method, MHD_HTTP_METHOD_GET) &&
490 0 == strcmp(url, "/");
491 }
492
493 int handle(Request* request, MHD_Connection* connection,
joshualitt136f5172016-02-02 11:07:39 -0800494 const char* url, const char* method,
joshualitt483b9012016-02-02 07:16:24 -0800495 const char* upload_data, size_t* upload_data_size) override {
496 return SendTemplate(connection);
497 }
498};
joshualittccfdaa52016-01-27 07:40:29 -0800499
500class UrlManager {
501public:
502 UrlManager() {
503 // Register handlers
joshualitt483b9012016-02-02 07:16:24 -0800504 fHandlers.push_back(new RootHandler);
505 fHandlers.push_back(new PostHandler);
506 fHandlers.push_back(new ImgHandler);
joshualitt29e5a892016-02-04 06:08:33 -0800507 fHandlers.push_back(new CmdHandler);
joshualitt483b9012016-02-02 07:16:24 -0800508 fHandlers.push_back(new InfoHandler);
joshualitt792345f2016-02-02 13:02:33 -0800509 fHandlers.push_back(new DownloadHandler);
joshualittcdad12f2016-02-08 07:08:21 -0800510 fHandlers.push_back(new DataHandler);
joshualitt873d6242016-02-08 13:57:44 -0800511 fHandlers.push_back(new FaviconHandler);
joshualitt483b9012016-02-02 07:16:24 -0800512 }
513
514 ~UrlManager() {
515 for (int i = 0; i < fHandlers.count(); i++) { delete fHandlers[i]; }
joshualittccfdaa52016-01-27 07:40:29 -0800516 }
517
518 // This is clearly not efficient for a large number of urls and handlers
519 int invoke(Request* request, MHD_Connection* connection, const char* url, const char* method,
520 const char* upload_data, size_t* upload_data_size) const {
521 for (int i = 0; i < fHandlers.count(); i++) {
joshualitt483b9012016-02-02 07:16:24 -0800522 if (fHandlers[i]->canHandle(method, url)) {
joshualitt136f5172016-02-02 11:07:39 -0800523 return fHandlers[i]->handle(request, connection, url, method, upload_data,
524 upload_data_size);
joshualittccfdaa52016-01-27 07:40:29 -0800525 }
526 }
527 return MHD_NO;
528 }
529
530private:
joshualitt483b9012016-02-02 07:16:24 -0800531 SkTArray<UrlHandler*> fHandlers;
joshualittccfdaa52016-01-27 07:40:29 -0800532};
533
534const UrlManager kUrlManager;
535
joshualitt7f6a1e02016-01-22 11:21:43 -0800536int answer_to_connection(void* cls, struct MHD_Connection* connection,
537 const char* url, const char* method, const char* version,
538 const char* upload_data, size_t* upload_data_size,
539 void** con_cls) {
joshualitt9a4e1882016-01-27 07:03:29 -0800540 SkDebugf("New %s request for %s using version %s\n", method, url, version);
joshualitt7f6a1e02016-01-22 11:21:43 -0800541
joshualitt9a4e1882016-01-27 07:03:29 -0800542 Request* request = reinterpret_cast<Request*>(cls);
joshualitt483b9012016-02-02 07:16:24 -0800543 int result = kUrlManager.invoke(request, connection, url, method, upload_data,
544 upload_data_size);
545 if (MHD_NO == result) {
joshualitt873d6242016-02-08 13:57:44 -0800546 fprintf(stderr, "Invalid method and / or url: %s %s\n", method, url);
joshualitt483b9012016-02-02 07:16:24 -0800547 }
548 return result;
joshualitt7f6a1e02016-01-22 11:21:43 -0800549}
550
551int skiaserve_main() {
joshualittcdad12f2016-02-08 07:08:21 -0800552 Request request(SkString("/data")); // This simple server has one request
joshualitt7f6a1e02016-01-22 11:21:43 -0800553 struct MHD_Daemon* daemon;
jcgregorio21ab1202016-01-28 06:24:19 -0800554 // TODO Add option to bind this strictly to an address, e.g. localhost, for security.
555 daemon = MHD_start_daemon(MHD_USE_SELECT_INTERNALLY, FLAGS_port, nullptr, nullptr,
joshualitt9a4e1882016-01-27 07:03:29 -0800556 &answer_to_connection, &request,
557 MHD_OPTION_END);
joshualitt7f6a1e02016-01-22 11:21:43 -0800558 if (NULL == daemon) {
559 return 1;
560 }
561
562 getchar();
563 MHD_stop_daemon(daemon);
564 return 0;
565}
566
567#if !defined SK_BUILD_FOR_IOS
568int main(int argc, char** argv) {
569 SkCommandLineFlags::Parse(argc, argv);
570 return skiaserve_main();
571}
572#endif