blob: 85e9c87c17b490eb32c1522152be9d30fcf8d368 [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
joshualitt7f6a1e02016-01-22 11:21:43 -080021#include <sys/socket.h>
joshualitt7f6a1e02016-01-22 11:21:43 -080022#include <microhttpd.h>
23
24// To get image decoders linked in we have to do the below magic
25#include "SkForceLinking.h"
26#include "SkImageDecoder.h"
27__SK_FORCE_IMAGE_DECODER_LINKING;
28
jcgregorio21ab1202016-01-28 06:24:19 -080029DEFINE_string(source, "https://debugger.skia.org", "Where to load the web UI from.");
30DEFINE_int32(port, 8888, "The port to listen on.");
joshualitt7f6a1e02016-01-22 11:21:43 -080031
32// TODO probably want to make this configurable
33static const int kImageWidth = 1920;
34static const int kImageHeight = 1080;
35
joshualitt7f6a1e02016-01-22 11:21:43 -080036// TODO move to template file
37SkString generateTemplate(SkString source) {
38 SkString debuggerTemplate;
39 debuggerTemplate.appendf(
40 "<!DOCTYPE html>\n"
41 "<html>\n"
42 "<head>\n"
43 " <title>SkDebugger</title>\n"
44 " <meta charset=\"utf-8\" />\n"
45 " <meta http-equiv=\"X-UA-Compatible\" content=\"IE=egde,chrome=1\">\n"
46 " <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n"
47 " <script src=\"%s/res/js/core.js\" type=\"text/javascript\" charset=\"utf-8\"></script>\n"
48 " <link href=\"%s/res/vul/elements.html\" rel=\"import\" />\n"
49 "</head>\n"
50 "<body class=\"fullbleed layout vertical\">\n"
51 " <debugger-app-sk>This is the app."
52 " </debugger-app-sk>\n"
53 "</body>\n"
54 "</html>", source.c_str(), source.c_str());
55 return debuggerTemplate;
56
57}
58
joshualitt9a4e1882016-01-27 07:03:29 -080059struct UploadContext {
joshualitt609d9792016-01-27 11:07:23 -080060 SkDynamicMemoryWStream fStream;
61 MHD_PostProcessor* fPostProcessor;
joshualitt9a4e1882016-01-27 07:03:29 -080062 MHD_Connection* connection;
63};
64
65struct Request {
66 Request() : fUploadContext(nullptr) {}
67 UploadContext* fUploadContext;
joshualitt609d9792016-01-27 11:07:23 -080068 SkAutoTUnref<SkPicture> fPicture;
joshualitt136f5172016-02-02 11:07:39 -080069 SkAutoTUnref<SkDebugCanvas> fDebugCanvas;
joshualitt9a4e1882016-01-27 07:03:29 -080070};
71
joshualitt609d9792016-01-27 11:07:23 -080072// TODO factor this out into functions, also handle CPU path
joshualitt136f5172016-02-02 11:07:39 -080073SkSurface* setupSurface(GrContextFactory* factory) {
joshualitt609d9792016-01-27 11:07:23 -080074 GrContext* context = factory->get(GrContextFactory::kNative_GLContextType,
75 GrContextFactory::kNone_GLContextOptions);
76 int maxRTSize = context->caps()->maxRenderTargetSize();
77 SkImageInfo info = SkImageInfo::Make(SkTMin(kImageWidth, maxRTSize),
78 SkTMin(kImageHeight, maxRTSize),
79 kN32_SkColorType, kPremul_SkAlphaType);
80 uint32_t flags = 0;
81 SkSurfaceProps props(flags, SkSurfaceProps::kLegacyFontHost_InitType);
joshualitt136f5172016-02-02 11:07:39 -080082 SkSurface* surface = SkSurface::NewRenderTarget(context, SkSurface::kNo_Budgeted, info, 0,
83 &props);
84 SkASSERT(surface);
joshualitt609d9792016-01-27 11:07:23 -080085
86 SkGLContext* gl = factory->getContextInfo(GrContextFactory::kNative_GLContextType,
87 GrContextFactory::kNone_GLContextOptions).fGLContext;
88 gl->makeCurrent();
joshualitt136f5172016-02-02 11:07:39 -080089 return surface;
90}
joshualitt609d9792016-01-27 11:07:23 -080091
joshualitt136f5172016-02-02 11:07:39 -080092SkData* writeCanvasToPng(SkCanvas* canvas) {
joshualitt609d9792016-01-27 11:07:23 -080093 // capture pixels
94 SkBitmap bmp;
95 bmp.setInfo(canvas->imageInfo());
96 if (!canvas->readPixels(&bmp, 0, 0)) {
joshualitt136f5172016-02-02 11:07:39 -080097 fprintf(stderr, "Can't read pixels\n");
98 return nullptr;
joshualitt609d9792016-01-27 11:07:23 -080099 }
100
101 // write to png
joshualitt136f5172016-02-02 11:07:39 -0800102 SkData* png = SkImageEncoder::EncodeData(bmp, SkImageEncoder::kPNG_Type, 100);
103 if (!png) {
104 fprintf(stderr, "Can't encode to png\n");
105 return nullptr;
joshualitt609d9792016-01-27 11:07:23 -0800106 }
joshualitt136f5172016-02-02 11:07:39 -0800107 return png;
108}
109
110SkData* setupAndDrawToCanvasReturnPng(SkDebugCanvas* debugCanvas, int n) {
111 GrContextOptions grContextOpts;
112 SkAutoTDelete<GrContextFactory> factory(new GrContextFactory(grContextOpts));
113 SkAutoTUnref<SkSurface> surface(setupSurface(factory.get()));
114
115 SkASSERT(debugCanvas);
116 SkCanvas* canvas = surface->getCanvas();
117 debugCanvas->drawTo(canvas, n);
118 return writeCanvasToPng(canvas);
joshualitt609d9792016-01-27 11:07:23 -0800119}
120
joshualitt29e5a892016-02-04 06:08:33 -0800121SkSurface* setupCpuSurface() {
122 SkImageInfo info = SkImageInfo::Make(kImageWidth, kImageHeight, kN32_SkColorType,
123 kPremul_SkAlphaType);
124 return SkSurface::NewRaster(info);
125}
126
joshualitt9a4e1882016-01-27 07:03:29 -0800127static const size_t kBufferSize = 1024;
128
129static int process_upload_data(void* cls, enum MHD_ValueKind kind,
130 const char* key, const char* filename,
131 const char* content_type, const char* transfer_encoding,
132 const char* data, uint64_t off, size_t size) {
133 struct UploadContext* uc = reinterpret_cast<UploadContext*>(cls);
134
135 if (0 != size) {
joshualitt609d9792016-01-27 11:07:23 -0800136 uc->fStream.write(data, size);
joshualitt9a4e1882016-01-27 07:03:29 -0800137 }
138 return MHD_YES;
139}
140
joshualitt792345f2016-02-02 13:02:33 -0800141static int SendData(MHD_Connection* connection, const SkData* data, const char* type,
142 bool setContentDisposition = false, const char* dispositionString = nullptr) {
joshualittccfdaa52016-01-27 07:40:29 -0800143 MHD_Response* response = MHD_create_response_from_buffer(data->size(),
144 const_cast<void*>(data->data()),
145 MHD_RESPMEM_MUST_COPY);
joshualitt609d9792016-01-27 11:07:23 -0800146 MHD_add_response_header(response, "Content-Type", type);
joshualittccfdaa52016-01-27 07:40:29 -0800147
joshualitt792345f2016-02-02 13:02:33 -0800148 if (setContentDisposition) {
149 MHD_add_response_header(response, "Content-Disposition", dispositionString);
150 }
151
joshualittccfdaa52016-01-27 07:40:29 -0800152 int ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
153 MHD_destroy_response(response);
154 return ret;
155}
156
joshualitt136f5172016-02-02 11:07:39 -0800157static int SendJSON(MHD_Connection* connection, SkDebugCanvas* debugCanvas, int n) {
joshualitt609d9792016-01-27 11:07:23 -0800158 SkDynamicMemoryWStream stream;
159 SkAutoTUnref<SkJSONCanvas> jsonCanvas(new SkJSONCanvas(kImageWidth, kImageHeight, stream));
joshualitt136f5172016-02-02 11:07:39 -0800160 debugCanvas->drawTo(jsonCanvas, n);
joshualitt609d9792016-01-27 11:07:23 -0800161 jsonCanvas->finish();
162
163 SkAutoTUnref<SkData> data(stream.copyToData());
164 return SendData(connection, data, "application/json");
165}
166
167static int SendTemplate(MHD_Connection* connection, bool redirect = false,
168 const char* redirectUrl = nullptr) {
jcgregorio21ab1202016-01-28 06:24:19 -0800169 SkString debuggerTemplate = generateTemplate(SkString(FLAGS_source[0]));
joshualittccfdaa52016-01-27 07:40:29 -0800170
171 MHD_Response* response = MHD_create_response_from_buffer(
172 debuggerTemplate.size(),
173 (void*) const_cast<char*>(debuggerTemplate.c_str()),
174 MHD_RESPMEM_MUST_COPY);
jcgregorio6a2046e2016-01-28 05:31:31 -0800175 MHD_add_response_header (response, "Access-Control-Allow-Origin", "*");
joshualittccfdaa52016-01-27 07:40:29 -0800176
jcgregorio6f17bc52016-01-27 11:44:38 -0800177 int status = MHD_HTTP_OK;
178
joshualitt609d9792016-01-27 11:07:23 -0800179 if (redirect) {
180 MHD_add_response_header (response, "Location", redirectUrl);
jcgregorio6f17bc52016-01-27 11:44:38 -0800181 status = MHD_HTTP_SEE_OTHER;
joshualitt609d9792016-01-27 11:07:23 -0800182 }
183
jcgregorio6f17bc52016-01-27 11:44:38 -0800184 int ret = MHD_queue_response(connection, status, response);
joshualittccfdaa52016-01-27 07:40:29 -0800185 MHD_destroy_response(response);
186 return ret;
187}
188
joshualitt483b9012016-02-02 07:16:24 -0800189class UrlHandler {
190public:
191 virtual ~UrlHandler() {}
192 virtual bool canHandle(const char* method, const char* url) = 0;
193 virtual int handle(Request* request, MHD_Connection* connection,
joshualitt136f5172016-02-02 11:07:39 -0800194 const char* url, const char* method,
joshualitt483b9012016-02-02 07:16:24 -0800195 const char* upload_data, size_t* upload_data_size) = 0;
196};
joshualittccfdaa52016-01-27 07:40:29 -0800197
joshualitt29e5a892016-02-04 06:08:33 -0800198class CmdHandler : public UrlHandler {
joshualitt483b9012016-02-02 07:16:24 -0800199public:
200 bool canHandle(const char* method, const char* url) override {
joshualitt136f5172016-02-02 11:07:39 -0800201 const char* kBasePath = "/cmd";
202 return 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() > 3) {
212 return MHD_NO;
joshualitt483b9012016-02-02 07:16:24 -0800213 }
joshualitt136f5172016-02-02 11:07:39 -0800214
215 // /cmd or /cmd/N or /cmd/N/[0|1]
216 if (commands.count() == 1 && 0 == strcmp(method, MHD_HTTP_METHOD_GET)) {
217 int n = request->fDebugCanvas->getSize() - 1;
218 return SendJSON(connection, request->fDebugCanvas, n);
219 }
220
221 // /cmd/N, for now only delete supported
222 if (commands.count() == 2 && 0 == strcmp(method, MHD_HTTP_METHOD_DELETE)) {
223 int n;
224 sscanf(commands[1].c_str(), "%d", &n);
225 request->fDebugCanvas->deleteDrawCommandAt(n);
226 return MHD_YES;
227 }
228
229 // /cmd/N/[0|1]
230 if (commands.count() == 3 && 0 == strcmp(method, MHD_HTTP_METHOD_POST)) {
231 int n, toggle;
232 sscanf(commands[1].c_str(), "%d", &n);
233 sscanf(commands[2].c_str(), "%d", &toggle);
234 request->fDebugCanvas->toggleCommand(n, toggle);
235 return MHD_YES;
236 }
237
joshualitt483b9012016-02-02 07:16:24 -0800238 return MHD_NO;
239 }
240};
241
242class ImgHandler : public UrlHandler {
243public:
244 bool canHandle(const char* method, const char* url) override {
245 static const char* kBasePath = "/img";
246 return 0 == strcmp(method, MHD_HTTP_METHOD_GET) &&
247 0 == strncmp(url, kBasePath, strlen(kBasePath));
joshualittccfdaa52016-01-27 07:40:29 -0800248 }
249
joshualitt483b9012016-02-02 07:16:24 -0800250 int handle(Request* request, MHD_Connection* connection,
joshualitt136f5172016-02-02 11:07:39 -0800251 const char* url, const char* method,
joshualitt483b9012016-02-02 07:16:24 -0800252 const char* upload_data, size_t* upload_data_size) override {
joshualitt136f5172016-02-02 11:07:39 -0800253 SkTArray<SkString> commands;
254 SkStrSplit(url, "/", &commands);
255
256 if (!request->fPicture.get() || commands.count() > 2) {
257 return MHD_NO;
joshualitt483b9012016-02-02 07:16:24 -0800258 }
joshualitta341b902016-02-02 07:37:21 -0800259
joshualitt136f5172016-02-02 11:07:39 -0800260 int n;
261 // /img or /img/N
262 if (commands.count() == 1) {
263 n = request->fDebugCanvas->getSize() - 1;
264 } else {
265 sscanf(commands[1].c_str(), "%d", &n);
266 }
267
268 SkAutoTUnref<SkData> data(setupAndDrawToCanvasReturnPng(request->fDebugCanvas, n));
269 return SendData(connection, data, "image/png");
joshualitt483b9012016-02-02 07:16:24 -0800270 }
271};
joshualittccfdaa52016-01-27 07:40:29 -0800272
joshualitt483b9012016-02-02 07:16:24 -0800273class PostHandler : public UrlHandler {
274public:
275 bool canHandle(const char* method, const char* url) override {
276 return 0 == strcmp(method, MHD_HTTP_METHOD_POST) &&
277 0 == strcmp(url, "/new");
joshualittccfdaa52016-01-27 07:40:29 -0800278 }
279
joshualitt483b9012016-02-02 07:16:24 -0800280 int handle(Request* request, MHD_Connection* connection,
joshualitt136f5172016-02-02 11:07:39 -0800281 const char* url, const char* method,
joshualitt483b9012016-02-02 07:16:24 -0800282 const char* upload_data, size_t* upload_data_size) override {
283 UploadContext* uc = request->fUploadContext;
joshualittccfdaa52016-01-27 07:40:29 -0800284
joshualitt483b9012016-02-02 07:16:24 -0800285 // New connection
286 if (!uc) {
287 // TODO make this a method on request
288 uc = new UploadContext;
289 uc->connection = connection;
290 uc->fPostProcessor = MHD_create_post_processor(connection, kBufferSize,
291 &process_upload_data, uc);
292 SkASSERT(uc->fPostProcessor);
joshualitt609d9792016-01-27 11:07:23 -0800293
joshualitt483b9012016-02-02 07:16:24 -0800294 request->fUploadContext = uc;
295 return MHD_YES;
296 }
297
298 // in process upload
299 if (0 != *upload_data_size) {
300 SkASSERT(uc->fPostProcessor);
301 MHD_post_process(uc->fPostProcessor, upload_data, *upload_data_size);
302 *upload_data_size = 0;
303 return MHD_YES;
304 }
305
306 // end of upload
307 MHD_destroy_post_processor(uc->fPostProcessor);
308 uc->fPostProcessor = nullptr;
309
joshualitt136f5172016-02-02 11:07:39 -0800310 // parse picture from stream
311 request->fPicture.reset(
312 SkPicture::CreateFromStream(request->fUploadContext->fStream.detachAsStream()));
313 if (!request->fPicture.get()) {
314 fprintf(stderr, "Could not create picture from stream.\n");
315 return MHD_NO;
joshualitt483b9012016-02-02 07:16:24 -0800316 }
317
joshualitt136f5172016-02-02 11:07:39 -0800318 // pour picture into debug canvas
319 request->fDebugCanvas.reset(new SkDebugCanvas(kImageWidth, kImageHeight));
320 request->fDebugCanvas->drawPicture(request->fPicture);
321
joshualitta341b902016-02-02 07:37:21 -0800322 // clear upload context
323 delete request->fUploadContext;
324 request->fUploadContext = nullptr;
325
joshualitt483b9012016-02-02 07:16:24 -0800326 return SendTemplate(connection, true, "/");
joshualittccfdaa52016-01-27 07:40:29 -0800327 }
joshualitt483b9012016-02-02 07:16:24 -0800328};
329
joshualitt792345f2016-02-02 13:02:33 -0800330class DownloadHandler : public UrlHandler {
331public:
332 bool canHandle(const char* method, const char* url) override {
333 return 0 == strcmp(method, MHD_HTTP_METHOD_GET) &&
334 0 == strcmp(url, "/download");
335 }
336
337 int handle(Request* request, MHD_Connection* connection,
338 const char* url, const char* method,
339 const char* upload_data, size_t* upload_data_size) override {
340 if (!request->fPicture.get()) {
341 return MHD_NO;
342 }
343
344 // TODO move to a function
345 // Playback into picture recorder
346 SkPictureRecorder recorder;
347 SkCanvas* canvas = recorder.beginRecording(kImageWidth, kImageHeight);
348
349 request->fDebugCanvas->draw(canvas);
350
351 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
352
353 SkDynamicMemoryWStream outStream;
354
355 SkAutoTUnref<SkPixelSerializer> serializer(SkImageEncoder::CreatePixelSerializer());
356 picture->serialize(&outStream, serializer);
357
358 SkAutoTUnref<SkData> data(outStream.copyToData());
359
360 // TODO fancier name handling
361 return SendData(connection, data, "application/octet-stream", true,
362 "attachment; filename=something.skp;");
363 }
364};
365
joshualitt29e5a892016-02-04 06:08:33 -0800366class InfoHandler : public UrlHandler {
367public:
368 bool canHandle(const char* method, const char* url) override {
369 const char* kBaseName = "/info";
370 return 0 == strcmp(method, MHD_HTTP_METHOD_GET) &&
371 0 == strncmp(url, kBaseName, strlen(kBaseName));
372 }
373
374 int handle(Request* request, MHD_Connection* connection,
375 const char* url, const char* method,
376 const char* upload_data, size_t* upload_data_size) override {
377 SkTArray<SkString> commands;
378 SkStrSplit(url, "/", &commands);
379
380 if (!request->fPicture.get() || commands.count() > 2) {
381 return MHD_NO;
382 }
383
384 // drawTo
385 SkAutoTUnref<SkSurface> surface(setupCpuSurface());
386 SkCanvas* canvas = surface->getCanvas();
387
388 int n;
389 // /info or /info/N
390 if (commands.count() == 1) {
391 n = request->fDebugCanvas->getSize() - 1;
392 } else {
393 sscanf(commands[1].c_str(), "%d", &n);
394 }
395
396 // TODO this is really slow and we should cache the matrix and clip
397 request->fDebugCanvas->drawTo(canvas, n);
398
399 // make some json
400 SkMatrix vm = request->fDebugCanvas->getCurrentMatrix();
401 SkIRect clip = request->fDebugCanvas->getCurrentClip();
402 Json::Value info(Json::objectValue);
403 info["ViewMatrix"] = SkJSONCanvas::MakeMatrix(vm);
404 info["ClipRect"] = SkJSONCanvas::MakeIRect(clip);
405
406 std::string json = Json::FastWriter().write(info);
407
408 // We don't want the null terminator so strlen is correct
409 SkAutoTUnref<SkData> data(SkData::NewWithCopy(json.c_str(), strlen(json.c_str())));
410 return SendData(connection, data, "application/json");
411 }
412};
413
414
joshualitt483b9012016-02-02 07:16:24 -0800415class RootHandler : public UrlHandler {
416public:
417 bool canHandle(const char* method, const char* url) override {
418 return 0 == strcmp(method, MHD_HTTP_METHOD_GET) &&
419 0 == strcmp(url, "/");
420 }
421
422 int handle(Request* request, MHD_Connection* connection,
joshualitt136f5172016-02-02 11:07:39 -0800423 const char* url, const char* method,
joshualitt483b9012016-02-02 07:16:24 -0800424 const char* upload_data, size_t* upload_data_size) override {
425 return SendTemplate(connection);
426 }
427};
joshualittccfdaa52016-01-27 07:40:29 -0800428
429class UrlManager {
430public:
431 UrlManager() {
432 // Register handlers
joshualitt483b9012016-02-02 07:16:24 -0800433 fHandlers.push_back(new RootHandler);
434 fHandlers.push_back(new PostHandler);
435 fHandlers.push_back(new ImgHandler);
joshualitt29e5a892016-02-04 06:08:33 -0800436 fHandlers.push_back(new CmdHandler);
joshualitt483b9012016-02-02 07:16:24 -0800437 fHandlers.push_back(new InfoHandler);
joshualitt792345f2016-02-02 13:02:33 -0800438 fHandlers.push_back(new DownloadHandler);
joshualitt483b9012016-02-02 07:16:24 -0800439 }
440
441 ~UrlManager() {
442 for (int i = 0; i < fHandlers.count(); i++) { delete fHandlers[i]; }
joshualittccfdaa52016-01-27 07:40:29 -0800443 }
444
445 // This is clearly not efficient for a large number of urls and handlers
446 int invoke(Request* request, MHD_Connection* connection, const char* url, const char* method,
447 const char* upload_data, size_t* upload_data_size) const {
448 for (int i = 0; i < fHandlers.count(); i++) {
joshualitt483b9012016-02-02 07:16:24 -0800449 if (fHandlers[i]->canHandle(method, url)) {
joshualitt136f5172016-02-02 11:07:39 -0800450 return fHandlers[i]->handle(request, connection, url, method, upload_data,
451 upload_data_size);
joshualittccfdaa52016-01-27 07:40:29 -0800452 }
453 }
454 return MHD_NO;
455 }
456
457private:
joshualitt483b9012016-02-02 07:16:24 -0800458 SkTArray<UrlHandler*> fHandlers;
joshualittccfdaa52016-01-27 07:40:29 -0800459};
460
461const UrlManager kUrlManager;
462
joshualitt7f6a1e02016-01-22 11:21:43 -0800463int answer_to_connection(void* cls, struct MHD_Connection* connection,
464 const char* url, const char* method, const char* version,
465 const char* upload_data, size_t* upload_data_size,
466 void** con_cls) {
joshualitt9a4e1882016-01-27 07:03:29 -0800467 SkDebugf("New %s request for %s using version %s\n", method, url, version);
joshualitt7f6a1e02016-01-22 11:21:43 -0800468
joshualitt9a4e1882016-01-27 07:03:29 -0800469 Request* request = reinterpret_cast<Request*>(cls);
joshualitt483b9012016-02-02 07:16:24 -0800470 int result = kUrlManager.invoke(request, connection, url, method, upload_data,
471 upload_data_size);
472 if (MHD_NO == result) {
473 fprintf(stderr, "Invalid method and / or url: %s %s)\n", method, url);
474 }
475 return result;
joshualitt7f6a1e02016-01-22 11:21:43 -0800476}
477
478int skiaserve_main() {
joshualitt9a4e1882016-01-27 07:03:29 -0800479 Request request; // This simple server has one request
joshualitt7f6a1e02016-01-22 11:21:43 -0800480 struct MHD_Daemon* daemon;
jcgregorio21ab1202016-01-28 06:24:19 -0800481 // TODO Add option to bind this strictly to an address, e.g. localhost, for security.
482 daemon = MHD_start_daemon(MHD_USE_SELECT_INTERNALLY, FLAGS_port, nullptr, nullptr,
joshualitt9a4e1882016-01-27 07:03:29 -0800483 &answer_to_connection, &request,
484 MHD_OPTION_END);
joshualitt7f6a1e02016-01-22 11:21:43 -0800485 if (NULL == daemon) {
486 return 1;
487 }
488
489 getchar();
490 MHD_stop_daemon(daemon);
491 return 0;
492}
493
494#if !defined SK_BUILD_FOR_IOS
495int main(int argc, char** argv) {
496 SkCommandLineFlags::Parse(argc, argv);
497 return skiaserve_main();
498}
499#endif