FreeWRL / FreeX3D 4.3.0
SSRServer.c
1
2/* win32: include the headers of your operating system that define the size_t, fd_set, socklen_t and struct sockaddr data types and define MHD_PLATFORM_H
3 linux: don't define MHD_PLATFORM_H and it will figure it out
4 license MIT or similar - hack away, have fun
5
6 SSR - server-side rendering - the user drags on a webgl canvas featuring a simple grid, and when
7 they lift their mousebutton/touchfinger the client code ajaxes (asynchronous javascript and xml, via xmlhttprequest)
8 the viewpoint pose to the server
9 the server reponds by correcting the pose for gravity/collision and sends back
10 then the client sends the corrected pose and asks via for a snapshot
11 the server takes a screen snapshot and sends back to clien
12 the client shows the snapshot over the grid and waits for the next mousedown/touch to show the grid again
13 SSRServer.exe this commandline program that takes Port (ie 8080 or your choice) on the command line,
14 - runs linked-in libmicrohttpd C web server to listen and call functions below
15 - you need to run this program in src/SSR/public to server SSRClient.html
16 - serves SSRClient.html when in a browser you go: http://localhost:8080/SSRClient.html or -via your noip/dyndns hostname http://myhostname:8080/SSRClient.html
17 Current state Feb 25, 2015:
18 * works a bit on win32 and in up-to-date desktop web browsers IE, Chrome, FF
19 x doesn't work in iPhone, BBPlaybook webbrowsers
20 - needs shims/polyfills in SSRClient.html for a few modern things window.requestNextAnimationFrame and xhr2 xmlhttprequest JSON/blob payloads
21 * tested with free noip.com account, which re-directed to home server computer behind DSL modem
22 - DSL modem had settings Advanced > DDNS (dynamic dns) > no-ip auto-update feature,
23 and PortForwarding (set to our chosen PORT 8080 on WAN and LAN)
24 Apr 10, 2015:
25 * ZoneBalancer:
26 - will be adding a geographic zoneserver/loadbalancer/reverseproxy/gateway
27 - ZoneBalancer acts as a gateway between LAN and internet
28 - gets request from an internet client
29 - sniffs the request content for geographic coords
30 - looks up in a table of {polygon,IP:PORT} using point-in-polygon algorithm
31 to determine which zone polygon the client's viewer is in, then uses
32 the associated IP and port number to call regular SSRserver,
33 wait for response, then relay response back to internet client
34 - benefit: more CPUs, GPUs and memory slots can be used for very large 3D datasets
35 allowing the solution to scale up using ordinary desktop computers which these days
36 have 32GB RAM limit (4x8GB). So if your data has a 96GB footprint in memory, you could use
37 3 SSRservers and 1 ZoneBalancer to distribute the load
38 - for programmer convenience the ZoneBalancer will be just a runtime configuration of SSRserver
39 In SSRserver.c response handler pseudocode:
40 if( static html) {
41 serve directly (ie SSRClient.html)
42 } else if working as SSRserver {
43 render snapshot and return response - the original SSRserver configuration
44 } else if working as zone balancer {
45 1. sniff request for geographic coords of client's ViewPose/avatar
46 2. do point-in-polygon on a list of polygons to determine which zone the avatar is in
47 and which IP:Port / SSRServer instance to relay to
48 3. open a TCP connection using sockets, and forward the request
49 4. wait for response from SSR on the ZoneBalancer's request handler thread
50 5. when response arrives, copy it to the zoneBalancer's response and return response to client
51 }
52 May 26, 2015:
53 revamping SSR_API using json (via cson lib/functions)
54 making SSRservers into a tree-like cascade, with nodes and leafs of overviews and LOD (level of detail)
55 SSRClient.html -- zoneserver -- SSRserver - leaf scene
56 -- SSRserver - leaf
57 -- SSRserver - leaf
58 -- SSRserver - leaf
59 -- SSRserver - leaf
60 -- SSRserver - leaf
61 each SSRserver instance will be responsible for knowing its:
62 service volume -extent and/or polygon extrusion (polgon2D, -height, +height) and/or shape
63 placemark tree (placemark is "name",x,y or more generally "name",level, pose {.position, .orientation))
64 including aggregating child SSR placemarks
65 so placemarks show up as clickable links or pulldown list in html client
66 transform, if any
67 - usually just optional x,y offset of scene origin,
68 so geonodes aren't needed in the scene to get double SFVec3d,
69 floats / SFVec3f will be sufficient in the scene, and doubles added/subtracted by the SSR code
70 so html client uses absolute coordinates (ie GIS/geographic/mapping-plane coords) in doubles
71 each SSR html client being sessionless, will transmit its desired LOD level and its position so ZoneServer
72 will know which child SSR to relay it to, and avatarSize.height so libfreewrl will know how to
73 apply gravity to the avatar before rendering the snapshot
74
75Proposed SSR API JSON
76Get: (POST)
77- initial_pose()
78 //generally, its the 'most absolute' pose of the (currently bound) viewpoint as positioned in the scene
79 // Client -- ZoneServer -- SSR +shifts -- libfreewrl_scene geoViewpoint ? geoCoords : (ViewMatrix x viewpoint.position)
80 // where ViewMatrix is the sceene-root to Viewpoint part of modelView matrix
81 //another client may have moved the view, so this would make sure its the .pos, .quat = 0 or inital
82 //or if no viewpoint in the scene freewrl and vivaty make viewpoint.pos.z = 10, then at least the client will know z=10 and can sync
83 request: {"command":"initial_pose", "level":0}
84 response: {"command":"initial_pose", "status":"OK", "pose": { "position":[x,y,z], "orientation": [q0,q1,q2,q3]}
85- levels available
86 //a fuzzy concept, of whether an overview or more detailed scene is desired at a given position
87 request: {"command":"levels_available"}
88 response: {"command":"levels_available", "status":"OK", "value":1}
89- placemark tree
90 //html client can request placemarks and get a tree list, showing overviews and more detailed LOD sub-scenes
91 // and can click on a placemark like a link, or choose from a pulldown list, to navigate to the placemark's pose
92 request: {"command":"placemark_tree"}
93 response: {"command":" placemark_tree", "status":"OK",
94 "placemarks": [
95 {..tree..},
96 {"placemark": {
97 "name": "overzicht",
98 "pose":{ "position":[x,y,z], "orientation": [q0,q1,q2,q3]},
99
100 "children":[
101 {"placemark": {
102 "name": "Afrikaanderwuk",
103 "pose":{ "position":[x,y,z], "orientation": [q0,q1,q2,q3]}
104 }
105 ]
106 }
107 },
108 {..tree..}.
109 ]}
110- adjusted pose
111 - needs to deliver previous pose, so wall and ground penetration can be done, and an adjusted pose returned to client
112 July 3, 2015 update: forget wall penetration for now, and forget previous pose. Problem if previous and current pose
113 are in different leaf scenes, freewrl wall penetration calculation can't happen
114 {"command":"posepose", "level":0, "height":1234.567,
115 "pose":{ "position":[x,y,z], "orientation": [q0,q1,q2,q3]}}
116 {"command":"posepose", "status":"OK",
117 "pose":{ "position":[x,y,z], "orientation": [q0,q1,q2,q3]}}
118- snapshot
119 - assumes the pose has been adjusted already, because the return is not in json. Its a blob representing the image.
120 {"command":"posesnapshot", "level":0, "height":1234.567,
121 "pose":{ "position":[x,y,z], "orientation": [q0,q1,q2,q3]}}
122 blob = .response; //not json
123
124- service volume
125 //the client talks to a single ZoneServer, and the Zoneserver sniffs the coordinates
126 //of the client's request to decide which SSRserver instance (host:port) to forward the request to
127 //zoneserver decides by testing the client's position against each SSR's service volume
128 // - a kind of advanced point-in-polygon test
129 // EITHER during startup, ZoneServer will ask each SSR once for its service volume
130 // OR asks just for the UNION of extents of its leaf and sub-SSRs on startup,
131 // and thereafter when in doubt asks each SSR to give detailed check if
132 // a given position is in its (more detailed) service volume
133 //html client won't ask or perform tests. It assumes its inside the service volume of
134 // the zoneserver (or directly SSR) it's talking to
135 //so the default action by html client and ssr server ie when no intermediaries, is to
136 //assume the client is within the extent of the SSRserver leaf scene
137 {"command":"service_volume", "level":0, "position":[x,y,z] }
138 {"command":"service_volume", "status":"OK", "volumes": [
139 "extent":[x,y,z,x,y,z],
140 "extrusion":{ "polygon":[x,y,z,x,y,z...x,y,z], "bottom":-z, "top":+z},
141 "shape": {"indexes":[0 1 2 -1 3 4 5 -1], "vertices":[x,y,z,x,y,z...]}
142 ]
143 }
144- inside
145 //if the service volume responses above are limited to Extents and extent testing, then
146 // we would need another command to ask each extent-success SSR to check
147 // again against finer granularity service volume definition,
148 // such as polygon extrusion or general surface shape
149 {"command":"inside", "level":0, "position":[x,y,z] }
150 {"command":"inside", "isInside":true }
151
152- maybe fov (and aspect)?
153 //if the scene has a navigationInfo it can adjust the fov
154 // so to synchronize the client will need to know fov to set it in webgl
155 // in theory if there's a bunch of SSRs running with different scenes, the fov could be different in each
156 {"command":"fov", "level":0, "position":[x,y,z] }
157 {"command":"fov", "status":"OK", "fov":123.456}
158- quit
159 //disabled for internet-facing outer SSR/Zoneserver, just for use by internal
160 //option: put on another port, contrlled by a per-server-node launcher utility
161 {"command":"quit"}
162 {"command":"quit", "status":"OK"}
163
164x Set: (POST) - there's no such thing because we are (currently) sessionless.
165 - resend any client-specific data with any needy request
166
167July 3, 2015 update
168 SSRClient and SSRServer can do yaw, pitch, height/world-z, vp and world xy.
169 Pitch = 0 is looking Nadir/down.
170 Scene can have a Transform with rotations, translations, and Viewpoint can have .postion, .orientation.
171 Scale in the transform is untested, and client can't/doesn't do scale, just translation and orientation of viewpoint.
172*/
173
174
175#ifdef _MSC_VER
176//WIN32
177#define MHD_PLATFORM_H WIN32
178#include <sys/types.h>
179#include <stddef.h>
180#include <stdarg.h>
181#include <stdint.h>
182#include <ws2tcpip.h>
183#define ssize_t size_t
184#define off_t ptrdiff_t
185#include <sys/stat.h>
186#include <limits.h>
187#include <io.h>
188#include <fcntl.h>
189#include <stdio.h>
190#define R_OK 4
191#define X_OK 4
192#define O_NONBLOCK 0
193#define O_BLOCK 0
194#define SSIZE_MAX 100000000L
195#define close _close
196#define open _open
197#define read _read
198#define write _write
199//END WIN32
200#endif
201// in theory scene_path could/should be a command line arg, or something the server helps the user upload or select on another page
202#define scene_path "http://dug9.users.sourceforge.net/web3d/townsite_2014/townsite.x3d" //"C:/Users/Public/dev/source2/placeholders/townsite_2014/townsite.x3d"
203#include <cdllFreeWRL.h>
204#define SSR_SERVER 1
205#include "../lib/SSRhelper.h"
206#include "cson/fw_cson.h"
207int run_fw = 1;
208void *fwctx = NULL;
209int runFW(char *url){
210 if(run_fw){
211 fwctx = dllFreeWRL_dllFreeWRL();
212 dllFreeWRL_commandline(fwctx,"set_keyval,SSR,true");
213 //dllFreeWRL_onInit(fwctx,400,300,NULL,FALSE,FALSE);
214 dllFreeWRL_onInit(fwctx,640,480,NULL,FALSE,FALSE);
215 dllFreeWRL_onLoad(fwctx,url);
216 dllFreeWRL_commandline(fwctx,"pin,FF");
217 }
218 return 0;
219}
220int stopFW(){
221 if(run_fw)
222 dllFreeWRL_onClose(fwctx);
223 return 0;
224}
225
226
227//===========CSON JSON>>>>>>>>>>>>>>>>>>>>
228//typedef int (*cbkey_cson)(const char *key, int index, cson_value *val, void *cbdata);
229//typedef int (*cbval_cson)(cson_value *val, int index, void *cbdata);
230struct keyval {
231 char *key;
232 cson_value *cv;
233};
234typedef struct walk_cbdata {
235 int (*fkey)(const char *key, int index, cson_value *val, void *cbdata);
236 int (*fval)(cson_value *val, int index, void *cbdata);
237 void *data;
238 //int level; //could increment before descending, decrement after ascending, in case a cb wants it
239 //cson_object *parent; //could set before descending in case a cb wants it
240 void *arr; //points to array
241 int arrtype; //0 double, 1 int, 2 char*
243int walk_array_cson(cson_array *arr, void *cbdata);
244
245
246int walk_obj_cson(cson_object *obj, void *cbdata){
247 int i;
249 walk_cbdata *wcbd = (walk_cbdata*)cbdata;
250 int rc = cson_object_iter_init( obj, &iter );
251 if( 0 != rc ) {
252 printf("error, but can only fail if obj is NULL\n");
253 }
254 cson_kvp * kvp; // key/value pair
255 i = 0;
256 while( (kvp = cson_object_iter_next(&iter)) )
257 {
258 cson_string const * ckey = cson_kvp_key(kvp);
259 cson_value * v = cson_kvp_value(kvp);
260 rc = wcbd->fkey(cson_string_cstr(ckey),i,v,cbdata);
261 if(!rc)
262 rc = wcbd->fval(v,0,cbdata);
263 if(rc) break;
264 i++;
265 }
266 return rc;
267}
268int walk_array_cson(cson_array *arr, void *cbdata){
269 int len, i, rc;
270 walk_cbdata *wcbd = (walk_cbdata*)cbdata;
271 len = cson_array_length_get(arr);
272 rc = 0;
273 for( i = 0; i < len; ++i ) {
274 cson_value *vi;
275 vi = cson_array_get( arr, i );
276 rc = wcbd->fval(vi,i,cbdata);
277 if(rc) break;
278 }
279 return rc;
280}
281int cb_print_key(const char *key, int index, cson_value *val, void *cbdata){
282 int indent;
283 walk_cbdata *wcbd = (walk_cbdata*)cbdata;
284 indent = *((int*)(wcbd->data));
285 if(index) printf(",");
286 printf("\"%s\":", key );
287 return 0;
288}
289int cb_print_val(cson_value *val, int index, void *cbdata){
290 int rc;
291 int indent;
292 walk_cbdata *wcbd = (walk_cbdata*)cbdata;
293
294 indent = *((int*)wcbd->data);
295 rc = 0;
296 if(index) printf(",");
297 switch(cson_value_type_id(val))
298 {
299 case CSON_TYPE_UNDEF:
300 case CSON_TYPE_NULL:
301 printf("null");
302 break;
303 case CSON_TYPE_BOOL:
304 {
305 if(cson_value_get_bool(val))
306 printf("true");
307 else
308 printf("false");
309 }
310 break;
311 case CSON_TYPE_INTEGER:
312 {
313 cson_int_t ii;
314 rc = cson_value_fetch_integer(val, &ii );
315 printf("%lld",ii);
316 }
317 break;
318 case CSON_TYPE_DOUBLE:
319 {
320 cson_double_t dd;
321 rc = cson_value_fetch_double(val, &dd );
322 printf("%lf",dd);
323 }
324 break;
325 case CSON_TYPE_STRING:
326 {
327 cson_string *str;
328 rc = cson_value_fetch_string(val, &str );
329 printf("\"%s\"",cson_string_cstr(str));
330 }
331 break;
332 case CSON_TYPE_OBJECT:
333 {
334 cson_object * obji = cson_value_get_object(val);
335 printf("{");
336 rc = walk_obj_cson(obji,cbdata);
337 printf("}");
338 }
339 break;
340 case CSON_TYPE_ARRAY:
341 {
342 cson_array *ar;
343 rc = cson_value_fetch_array(val,&ar);
344 if(!rc){
345 printf("[");
346 rc = walk_array_cson(ar,cbdata);
347 printf("]");
348 }
349 }
350 break;
351 default:
352 break;
353 }
354 return rc;
355}
356int print_json_tree(cson_object *obj){
357 walk_cbdata cbdata;
358 int indent = 0;
359 cbdata.data = &indent;
360 cbdata.fkey = cb_print_key;
361 cbdata.fval = cb_print_val;
362 printf("{");
363 walk_obj_cson(obj,&cbdata);
364 printf("}");
365 return 0;
366}
367int cb_sniff_key(const char *key, int index, cson_value *val, void *cbdata){
368 struct keyval *kv;
369 walk_cbdata *wcbd = (walk_cbdata*)cbdata;
370 kv = (struct keyval *)(wcbd->data);
371 if(!strcmp(kv->key,key))
372 kv->cv = val;
373 return 0;
374}
375int cb_sniff_val(cson_value *val, int index, void *cbdata){
376 int rc;
377 walk_cbdata *wcbd = (walk_cbdata*)cbdata;
378
379 rc = 0;
380 switch(cson_value_type_id(val))
381 {
382 case CSON_TYPE_OBJECT:
383 {
384 cson_object * obji = cson_value_get_object(val);
385 rc = walk_obj_cson(obji,cbdata);
386 }
387 break;
388 case CSON_TYPE_ARRAY:
389 {
390 cson_array *ar;
391 rc = cson_value_fetch_array(val,&ar);
392 if(!rc){
393 rc = walk_array_cson(ar,cbdata);
394 }
395 }
396 break;
397 default:
398 break;
399 }
400 return rc;
401}
402int sniff_json_tree(char *key, cson_object *obj){
403 walk_cbdata cbdata;
404 struct keyval kv;
405 kv.key = key;
406 kv.cv = NULL;
407 cbdata.data = &kv;
408 cbdata.fkey = cb_sniff_key;
409 cbdata.fval = cb_sniff_val;
410 walk_obj_cson(obj,&cbdata);
411 if(kv.cv) printf("sniffed key: %s\n",key);
412 return 0;
413}
414
415int cb_gather_key(const char *key, int index, cson_value *val, void *cbdata){
416 int rc;
417 SSR_request *req;
418 walk_cbdata *wcbd = (walk_cbdata*)cbdata;
419 req = (SSR_request *)(wcbd->data);
420 if(!strcmp(key,"command")){
421
422 if(!strcmp(key,"init_pose")){
423 req->type = SSR_INITPOSE;
424 }else if(!strcmp(key,"posepose")){
425 req->type = SSR_POSEPOSE;
426 }else if(!strcmp(key,"posesnapshot")){
427 req->type = SSR_POSESNAPSHOT;
428 }else if(!strcmp(key,"")){
429 }else if(!strcmp(key,"")){
430 }else if(!strcmp(key,"")){
431 }else if(!strcmp(key,"")){
432 }else if(!strcmp(key,"")){
433 }else if(!strcmp(key,"")){
434 }
435
436 }else if(!strcmp(key,"level")){
437 cson_int_t ii;
438 rc = cson_value_fetch_integer(val, &ii );
439 req->LOD = (int)ii;
440 }else if(!strcmp(key,"position")){
441 wcbd->arr = req->vec3;
442 wcbd->arrtype = 0; //double
443 }else if(!strcmp(key,"orientation")){
444 wcbd->arr = req->quat4;
445 wcbd->arrtype = 0; //double
446 }else if(!strcmp(key,"")){
447 }else if(!strcmp(key,"")){
448 }
449 return 0;
450}
451int cb_gather_val(cson_value *val, int index, void *cbdata){
452 int rc;
453 walk_cbdata *wcbd = (walk_cbdata*)cbdata;
454
455 rc = 0;
456 switch(cson_value_type_id(val))
457 {
458 case CSON_TYPE_OBJECT:
459 {
460 cson_object * obji = cson_value_get_object(val);
461 rc = walk_obj_cson(obji,cbdata);
462 }
463 break;
464 case CSON_TYPE_ARRAY:
465 {
466 cson_array *ar;
467 rc = cson_value_fetch_array(val,&ar);
468 if(!rc){
469 rc = walk_array_cson(ar,cbdata);
470 }
471 }
472 break;
473 case CSON_TYPE_DOUBLE:
474 {
475 cson_double_t dd;
476 rc = cson_value_fetch_double(val, &dd );
477 ((double *)wcbd->arr)[index] = dd;
478 }
479 break;
480 default:
481 break;
482 }
483 return rc;
484}
485int gather_ssr_req(cson_object *obj, SSR_request *ssr_req ){
486 walk_cbdata cbdata;
487 cbdata.data = ssr_req;
488 cbdata.fkey = cb_gather_key;
489 cbdata.fval = cb_gather_val;
490 walk_obj_cson(obj,&cbdata);
491
492 return 0;
493}
494int parse_json_and_gather_ssr_req(char *strdata, SSR_request *ssr_req){
495 int rc;
496 cson_value *root;
497 cson_parse_info info;
498 memset(&info,0,sizeof(cson_parse_info));
499 rc = cson_parse_string(&root,strdata,strlen(strdata),NULL,&info); //opt,info);
500 if(!rc){
501 if( cson_value_is_object(root) ) {
502 cson_object * obj = cson_value_get_object(root);
503 //print_json_obj_cson(obj);
504 //print_json_tree(obj);
505 //find_my_key("coords2",obj);
506 //sniff_json_tree("coords2",obj);
507 gather_ssr_req(root,ssr_req);
508 }
509 cson_value_free(root);
510 }
511 return rc;
512}
513//<<<<<<<<<<<CSON JSON======================
514
515
516
517
518//=========ZONE BALANCER==>>>>>>>>>>>>
519static int running_as_zonebalancer = 0;
520#ifdef _MSC_VER
521#include <direct.h>
522#define chdir _chdir
523#define strcasecmp _stricmp
524WSADATA wsaData;
525void initialize_sockets(){
526 int iResult;
527 // Initialize Winsock
528 iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
529 if (iResult != 0) {
530 printf("WSAStartup failed: %d\n", iResult);
531 }
532}
533#else
534void initialize_sockets(){}
535#endif
536
537//POINT IN POLY
538typedef struct Point {
539 double x;
540 double y;
541} Point;
542
543typedef struct zone {
544 char *name;
545 Point center;
546 Point *poly;
547 int n;
548 char * ssrname;
549 void * ssr;
550 void * next;
551} zone;
552typedef struct ssr {
553 char *name;
554 char *ip;
555 char *port;
556 void *next;
557 double extent[6]; //added for ssr2
558 int levels_available;
559} ssr;
560static zone *zones = NULL;
561static ssr *ssrs = NULL;
562void ssr_list_add(ssr *s){
563 if(!ssrs){
564 ssrs = s;
565 s->next = NULL;
566 }else{
567 ssr *cur = ssrs;
568 while(cur->next)
569 cur = cur->next;
570 cur->next = s;
571 s->next = NULL;
572 }
573}
574void zone_list_add(zone *z){
575 if(!zones){
576 zones = z;
577 z->next = NULL;
578 }else{
579 zone *cur = zones;
580 while(cur->next)
581 cur = cur->next;
582 cur->next = z;
583 z->next = NULL;
584 }
585}
586
587
588#include <libxml/parser.h>
589#include <libxml/tree.h>
590static void print_element_names(xmlNode * a_node)
591{
592 xmlNode *cur_node = NULL;
593
594 for (cur_node = a_node; cur_node; cur_node = cur_node->next) {
595 if (cur_node->type == XML_ELEMENT_NODE) {
596 struct _xmlAttr *cp;
597 printf("node type: Element, name: %s\n", cur_node->name);
598 for(cp = cur_node->properties; cp; cp = cp->next) {
599 // http://www.xmlsoft.org/html/libxml-tree.html
600 printf("\tname=%s value=%s\n",cp->name,xmlGetNoNsProp(cur_node,cp->name));
601 }
602 }
603
604 print_element_names(cur_node->children);
605 }
606}
607
608static void load_zone_elements(xmlNode * a_node)
609{
610 xmlNode *cur_node = NULL;
611
612 for (cur_node = a_node; cur_node; cur_node = cur_node->next) {
613 if (cur_node->type == XML_ELEMENT_NODE) {
614 struct _xmlAttr *cp;
615 char *value;
616 printf("node type: Element, name: %s\n", cur_node->name);
617 if(!strcasecmp(cur_node->name,"zone")){
618 //add to zone list
619 zone *z = malloc(sizeof(zone));
620 for(cp = cur_node->properties; cp; cp = cp->next) {
621 // http://www.xmlsoft.org/html/libxml-tree.html
622 value = xmlGetNoNsProp(cur_node,cp->name);
623 printf("\tname=%s value=%s\n",cp->name,value);
624 if(!strcasecmp(cp->name,"ssr")){
625 z->ssrname = strdup(value);
626 }else if(!strcmp(cp->name,"name")){
627 z->name = strdup(value);
628 }else if(!strcasecmp(cp->name,"center")){
629 double x,y,zz;
630 sscanf(value,"%lf %lf %lf",&x,&y,&zz);
631 z->center.x = x;
632 z->center.y = y;
633 }else if(!strcasecmp(cp->name,"polygon")){
634 double x,y;
635 Point points[1000];
636 int n, m;
637 char *p, *q;
638 q = value;
639 n = 0;
640 while(q){
641 m = sscanf(q,"%lf %lf",&x,&y);
642 if(m==2){
643 points[n].x = x;
644 points[n].y = y;
645 n++;
646 }else{
647 break;
648 }
649 q = strchr(q,',');
650 if(!q) break;
651 q++;
652 }
653 z->n = n;
654 z->poly = malloc(n*sizeof(Point));
655 memcpy(z->poly,points,n*sizeof(Point));
656 }
657 }
658
659 zone_list_add(z);
660 }else if(!strcasecmp(cur_node->name,"ssrserver")){
661 //add to ssr list
662 ssr *s = malloc(sizeof(ssr));
663 for(cp = cur_node->properties; cp; cp = cp->next) {
664 value = xmlGetNoNsProp(cur_node,cp->name);
665 printf("\tname=%s value=%s\n",cp->name,value);
666 if(!strcasecmp(cp->name,"name")){
667 s->name = strdup(value);
668 }else if(!strcasecmp(cp->name,"ip")){
669 s->ip = strdup(value);
670 }else if(!strcasecmp(cp->name,"port")){
671 s->port = strdup(value);
672 }
673 }
674 ssr_list_add(s);
675 }
676 }
677 //recurse
678 load_zone_elements(cur_node->children);
679 }
680}
681zone *find_zone_by_name(char *name){
682 zone *z = zones;
683 while(z){
684 if(!strcmp(z->name,name))
685 break;
686 z=z->next;
687 }
688 return z;
689}
690int cn_PnPoly( Point P, Point *V, int n );
691zone *find_zone_by_point(Point P){
692 int inside;
693 zone *z;
694 z = zones;
695 while(z){
696 inside = cn_PnPoly( P, z->poly, z->n -1 );
697 if(inside)
698 break;
699 z = z->next;
700 }
701 return z;
702}
703void test_pointinpoly(){
704 zone *z, *zn;
705 int inside;
706 Point p;
707 zn = find_zone_by_name("StrandEnDuin"); //("Afrikaanderwuk");
708 z = NULL;
709 if(zn){
710 p.x = zn->center.x;
711 p.y = zn->center.y;
712 z = find_zone_by_point(p);
713 if(z){
714 printf("inside polygon %s\n",z->name);
715 }else{
716 printf("polygon not found\n");
717 }
718 }
719
720}
721void assign_ssr_by_name(){
722 zone *z;
723 ssr *s;
724 z = zones;
725 while(z){
726 z->ssr = NULL;
727 s = ssrs;
728 while(s){
729 if(!strcasecmp(z->ssrname,s->name)){
730 z->ssr = s;
731 break;
732 }
733 s = s->next;
734 }
735 z = z->next;
736 }
737}
738static int testing_pointinpoly = 1;
739void load_polys(char *filename){
740 xmlDocPtr doc; /* the resulting document tree */
741 xmlNodePtr root_node = NULL, node = NULL, node1 = NULL;/* node pointers */
742 doc = xmlReadFile(filename, NULL, 0);
743 if (doc == NULL) {
744 fprintf(stderr, "Failed to parse %s\n", filename);
745 return;
746 }
747 root_node = xmlDocGetRootElement(doc);
748 //print_element_names(root_node);
749 load_zone_elements(root_node);
750 assign_ssr_by_name();
751 xmlFreeDoc(doc);
752 if(testing_pointinpoly)
753 test_pointinpoly();
754 printf("done test_pointinpoly\n");
755}
756//<<<<<<<<===ZONE BALANCER===========
757
758
759
760//===========LEAF SSR >>>>>>>>>>>>>>>>>>>>>>>
761/* currently each SSR instance can have 1 leaf scene (when acting as SSR)
762 and many children scenes (when acting as zoneserver)
763 ssr_leaf 1:1 ssr process 1:1 static
764 we could leave the leaf scene items as scattered statics, but
765 we gather them here in ssr_leaf{} for conceptual clarity, and maintenance convenience
766*/
767typedef struct polygon {
768 int n;
769 double *pts; //xyz, so polygon can have varying z
770} polygon;
771typedef struct extrusion {
772 polygon poly;
773 double below;
774 double above;
775} extrusion;
776static struct ssr_leaf {
777 //double transform[16]; //each SSR can have an xy offset, or more generally a transform,
778 //so a scene with no geo nodes can stay float/SFVec3f, and the offset here will expand to double absolute coords
779 double xoff, yoff,zoff;
780 double inverse[16]; //and its inverse when going the other way, prepared on init
781 double extent[6]; //of leaf scene
782 extrusion volume; //more detailed than extent, used for 3D version of point-in-polygon test
783 int volume_type;
784 double extents[6]; //union of extents of leaf and children scenes
785} ssrleaf;
786
787//<<<<<<<<<<<<<<<LEAF SSR ==================
788
789
790
791
792#include <microhttpd.h>
793#include <stdlib.h>
794#include <string.h>
795#include <stdio.h>
796
797#define PAGE "<html><head><title>libmicrohttpd demo</title>"\
798 "</head><body>libmicrohttpd demo</body></html>"
799
800//the iterate_post, request_completed functions and connection_info_struct
801// were copied from the POST tutorial at:
802// http://www.gnu.org/software/libmicrohttpd/tutorial.html
803#define PORT 8888
804#define POSTBUFFERSIZE 512
805#define MAXNAMESIZE 20
806#define MAXANSWERSIZE 512
807
808#define GET 0
809#define POST 1
810
812{
813 int connectiontype;
814 char *answerstring;
815 int len;
816 struct MHD_PostProcessor *postprocessor;
817};
818
819const char *askpage = "<html><body>\
820 What's your name, Sir?<br>\
821 <form action=\"/namepost\" method=\"post\">\
822 <input name=\"name\" type=\"text\"\
823 <input type=\"submit\" value=\" Send \"></form>\
824 </body></html>";
825
826const char *greetingpage =
827 "<html><body><h1>Welcome, %s!</center></h1></body></html>";
828
829const char *errorpage =
830 "<html><body>This doesn't seem to be right.</body></html>";
831
832
833static int
834send_page (struct MHD_Connection *connection, const char *page, int len)
835{
836 int ret;
837 struct MHD_Response *response;
838
839
840 response = MHD_create_response_from_buffer (len, (void *) page, MHD_RESPMEM_PERSISTENT);
841 if (!response)
842 return MHD_NO;
843
844 ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
845 MHD_destroy_response (response);
846
847 return ret;
848}
849#define MAXPOSESIZE 255
850static void jsonPose2double(double *quat4, double *vec3, char *data){
851 sscanf(data,"[%lf, %lf, %lf, %lf, %lf, %lf, %lf, %lf, %lf, %lf, %lf, %lf, %lf, %lf, %lf, %lf]",
852 &quat4[0],&quat4[1],&quat4[2],&quat4[3],
853 &vec3[0],&vec3[1],&vec3[2]);
854}
855static void doublePose2json(double *quat4, double *vec3, char *data, int MAX){
856 sprintf(data,"[%lf, %lf, %lf, %lf, %lf, %lf, %lf]",
857 quat4[0],quat4[1],quat4[2],quat4[3],
858 vec3[0],vec3[1],vec3[2]);
859}
860static void doublePose2jsonB(double *quat4, double *vec3, char *data, int MAX){
861 //a gruelling workout with cson to generate and stringify some json
862 //-compare with simpler sprintf above. Should get something like:
863 // {"position":[2500333.123456,510444.123456,1022.9876],"orientation":[0.0,0.001,-0.005,0.99998]}
864 //-goal is to generalize, and provide sample code for more complex API functions, more complex projects
865 int i, rc;
866 // Create a root object:
867 cson_value * objroot = cson_value_new_object();
868 // Objects, Arrays, and Strings are represented by higher-level
869 // objects owned by their containing cson_value, so we fetch
870 // that Object:
871 cson_object * obj = cson_value_get_object(objroot);
872 // Achuntg: destroying objV will also invalidate/destroy obj.
873
875 //cson_object_set( obj, "myInt", cson_value_new_integer(42) );
876 //cson_object_set( obj, "myDouble", cson_value_new_double(42.24) );
877
878 {
879 //1. create object tree
880 // Add an array:
881 cson_array *ar;
882 cson_value *arori, *arpos;
883 arpos = cson_value_new_array();
884 cson_object_set( obj, "position", arpos ); // transfers ownership of arpos to obj
885 ar = cson_value_get_array(arpos);
886 for(i=0;i<3;i++)
887 cson_array_set( ar, i, cson_value_new_double(vec3[i]) );
888 arori = cson_value_new_array();
889 cson_object_set( obj, "orientation", arori ); // transfers ownership of arori to obj
890 ar = cson_value_get_array(arori);
891 for(i=0;i<4;i++)
892 cson_array_set( ar, i, cson_value_new_double(quat4[i]) );
893 }
894 {
895 //2. stringify
896 cson_buffer buf = cson_buffer_empty;
897 rc = cson_output_buffer( objroot, &buf, NULL );
898 if( 0 != rc ) {
899 //... error ...
900 } else {
901 //JSON data is the first (buf.used) bytes of (buf.mem).
902 }
903 // Regardless of success or failure, make sure to either
904 // clean up the buffer:
905 //3. copy string to our buf
906 if(buf.used < MAX){
907 memcpy(data,buf.mem,buf.used);
908 data[buf.used] = '\0';
909 }
910 //4. free cson stringify buf
911 cson_buffer_reserve( &buf, 0 );
912 }
913 // or take over ownership of its bytes:
914 //{
915 // char * mem = (char *)buf.mem;
916 // // mem is (buf.capacity) bytes long, of which (buf.used)
917 // // are "used" (they contain the JSON data in this case).
918 // buf = cson_buffer_empty;
919 // //... you now own the buffer's memory and must eventually free() it ...
920 //}
921
922 //5. Free cson obj tree: root and all child values it owns:
923 cson_value_free( objroot );
924}
925
926
927static char* getSnapshot(int *len){
928 struct stat ss;
929 int fd;
930 ssize_t blocksz; //, left2read;
931 char *filename = "2.jpg";
932 if (stat(filename, &ss) < 0) {
933 printf("load_file_read: could not stat: %s\n", filename);
934 return MHD_NO;
935 }
936 if (!ss.st_size) {
937 printf("load_file_read: file is empty %s\n", filename);
938 return MHD_NO;
939 }
940 if (ss.st_size > SSIZE_MAX) {
941 /* file is greater that read's max block size: we must make a loop */
942 blocksz = SSIZE_MAX;
943 } else {
944 blocksz = ss.st_size;
945 }
946 fd = open(filename, O_RDONLY | O_BINARY | O_BLOCK);
947 if (fd < 0) {
948 printf("load_file_read: could not open: %s\n", filename);
949 return MHD_NO;
950 }
951 //response = MHD_create_response_from_fd(ss.st_size,fd); //didn't work in win32, complained ERR_CONTENT_LENGTH_MISMATCH in client
952 // will be closed when response is destroyed
953 //close(fd);
954
955 char *blob = malloc(blocksz);
956 read(fd,blob,blocksz);
957 //on win32, it works most reliably -without crashing- if I say for it to deep copy the blob, and free its own copy
958 //then free my copy here
959 //response = MHD_create_response_from_data(blocksz,(void*) blob, MHD_NO, MHD_YES);
960 //free(blob);
961 close(fd);
962 *len = blocksz;
963 return blob;
964
965}
966#ifdef WIN32
967 #ifndef WIN32_LEAN_AND_MEAN
968 #define WIN32_LEAN_AND_MEAN
969 #endif
970 #define strdup _strdup
971 #include <winsock2.h>
972 #include <ws2tcpip.h> /* for TCPIP - are we using tcp? */
973 #define SHUT_RDWR SD_BOTH
974 #include <windows.h>
975 #define snprintf _snprintf
976 //#define sscanf sscanf_s
977 #define STRTOK_S strtok_s
978int sockwrite(SOCKET s, const char *buf, int len){
979 return send(s,buf,len,0);
980}
981int sockread(SOCKET s, const char *buf, int len){
982 return recv(s,buf,len,0);
983}
984
985#else
986 #include <sys/socket.h>
987 #include <netinet/in.h>
988 #include <netdb.h>
989 #define STRTOK_S strtok_r
990int sockwrite(SOCKET s, const char *buf, int len){
991 return write(s,buf,len);
992}
993int sockread(SOCKET s, const char *buf, int len){
994 return recv(s,buf,len,0);
995}
996#endif
997
998int socket_connect(char *host, int port){
999 struct hostent *hp;
1000 struct sockaddr_in addr;
1001 int on = 1, sock;
1002 unsigned short port16;
1003 struct addrinfo *result = NULL,
1004 *ptr = NULL,
1005 hints;
1006 int iResult;
1007
1008 ZeroMemory( &hints, sizeof(hints) );
1009 hints.ai_family = AF_UNSPEC;
1010 hints.ai_socktype = SOCK_STREAM;
1011 hints.ai_protocol = IPPROTO_TCP;
1012
1013 iResult = getaddrinfo("localhost","8081", &hints, &result);
1014 if (iResult != 0) {
1015 printf("getaddrinfo failed: %d\n", iResult);
1016 //WSACleanup();
1017 return 0;
1018 }
1019 //if((hp = gethostbyname(host)) == NULL){
1020 if((hp = gethostbyname("localhost")) == NULL){
1021 perror("gethostbyname");
1022 return 0;
1023 }
1024 //memcpy(hp->h_addr, &addr.sin_addr, hp->h_length);
1025 memcpy(&addr.sin_addr,hp->h_addr, hp->h_length);
1026 port16 = port; //long to unsigned short
1027 addr.sin_port = htons(port); //unsigned short to network byte order
1028 addr.sin_family = AF_INET;
1029 sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
1030 setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (const char *)&on, sizeof(int));
1031
1032 if(sock == -1){
1033 perror("setsockopt");
1034 return 0;
1035 }
1036
1037 if(connect(sock, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)) == -1){
1038 perror("connect");
1039 return 0;
1040
1041 }
1042 //write(sock,"hi",2); // write(fd, char[]*, len);
1043 //sockwrite(sock,"hi",2);
1044 return sock;
1045}
1046
1047//static char * fakepostpose = "POST /pose HTTP/1.1\r\nHost: localhost:8080\r\nConnection: keep-alive\r\nContent-Length: 100\r\nOrigin: http://localhost:8080\r\nUser-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.118 Safari/537.36\r\nContent-type: application/x-www-form-urlencoded\r\nAccept: */*\r\nReferer: http://localhost:8080/SSRClient.html\r\nAccept-Encoding: gzip, deflate\r\nAccept-Language: en-US,en;q=0.8\r\n\r\nposepose=[0, -0.9977955222129822, 0, -0.06639080494642258, -161.7969512939453, 0,284.27691650390625]";
1048//static char * fakepostsnap = "POST /pose HTTP/1.1\r\nHost: localhost:8080\r\nConnection: keep-alive\r\nContent-Length: 100\r\nOrigin: http://localhost:8080\r\nUser-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.118 Safari/537.36\r\nContent-type: application/x-www-form-urlencoded\r\nAccept: */*\r\nReferer: http://localhost:8080/SSRClient.html\r\nAccept-Encoding: gzip, deflate\r\nAccept-Language: en-US,en;q=0.8\r\n\r\nposesnapshot=[0, -0.9977955222129822, 0, -0.06639080494642258, -161.7969512939453, 0,284.27691650390625]";
1049int build_http_post(char *post, char *host, int port, int lencontent, char *useragent, char *origin, char *referer)
1050{
1051 int len;
1052 char *post_fmt;
1053 post_fmt = "POST /pose HTTP/1.1\r\nHost: %s:%d\r\nConnection: keep-alive\r\nContent-Length: %d\r\nContent-type: application/x-www-form-urlencoded\r\nAccept: */*\r\nAccept-Encoding: gzip, deflate\r\nAccept-Language: en-US,en;q=0.8\r\n\r\n";
1054 sprintf(post, post_fmt, host, port,lencontent);
1055 len = strlen(post);
1056 return len;
1057}
1058
1059
1060#define BUFFER_SIZE 32768
1061
1062//static char *request_line_format = "POST /%s HTTP/1.1\n";
1063//static char *request_header_format = "Host: %s\nConnection: Keep-Alive\nAccept: */*\nAccept-Language: us-en\nContent-Length: %d\n\n";
1064//static char *request_line = NULL;
1065//static char *request_header = NULL;
1066//char * content_type = "Content_type: application/x-www-form-urlencoded\n";
1067//char * content_length_format = "Content-Length: %d\n";
1068int reverse_proxy(char *host, char *port, char *ssr_command, char *key, char *request, int size, char **response)
1069{
1070 int fd, li,lr;
1071 char *r;
1072 char buffer[BUFFER_SIZE];
1073 char content_length[100];
1074 //if(!request_line)
1075 // request_line = malloc(500);
1076 //sprintf(request_line,request_line_format,ssr_command); //port);
1077
1078 //if(!request_header)
1079 // request_header = malloc(500);
1080 //sprintf(request_header,request_header_format,"localhost",strlen(request));
1081 *response = NULL;
1082 fd = socket_connect(host, atoi(port));
1083 if (fd == INVALID_SOCKET) {
1084 printf("Error at socket(): %ld\n", WSAGetLastError());
1085 //freeaddrinfo(result);
1086 // WSACleanup();
1087 //return 1;
1088 return 0;
1089 }
1090
1091 if(fd){
1092 int lh;
1093 char *answer, *temp;
1094 int answermax = BUFFER_SIZE;
1095 answer = malloc(answermax);
1096
1097 //POST request-URI HTTP-version
1098 //Content-Type: mime-type
1099 //Content-Length: number-of-bytes
1100 //(other optional request headers)
1101 //
1102 //(URL-encoded query string)
1103
1104 if(1){
1105 char post[8192];
1106 int lenpost;
1107 int lencontent = size + strlen(key) + 1;
1108 lenpost = build_http_post(post, host, atoi(port), lencontent, NULL, NULL, NULL);
1109 sprintf(&post[lenpost],"%s=",key);
1110 lenpost += strlen(key)+1;
1111 memcpy(&post[lenpost],request,size);
1112 lenpost += size;
1113 memcpy(&post[lenpost],"\r\n",3);
1114 //printf("%s",post);
1115 sockwrite(fd,post,lenpost);
1116 }
1117 //I learned http uses content-length rather than shutdown signals to end recv loop
1118 //shutdown(fd, SD_SEND); //shutdown the client's side of the connection (SSR server side can still send)
1119 memset(buffer,0, BUFFER_SIZE); //we need \0 on the end of each read, so we can safely do strstr below
1120 r = answer;
1121 lr = 0;
1122 int icount = 0;
1123 //char *content = NULL;
1124 int lencontent = 0;
1125 int offcontent = 0;
1126 while( li = sockread(fd, buffer, BUFFER_SIZE - 1) ){ //subtract one because we need \0 on the end for strstr below
1127 if(lr + li > answermax ){
1128 answermax *= 2;
1129 answer = realloc(answer,answermax);
1130 r = &answer[lr];
1131 }
1132 memcpy(r,buffer,li);
1133 lr += li;
1134 r += li;
1135 memset(buffer,0, BUFFER_SIZE);
1136 if(!offcontent){
1137 //first time reading, lets get the Content-Length and \n\n linebreak pointer to data
1138 if(!lencontent){
1139 char *cl = strstr(answer,"Content-Length:");
1140 if(cl){
1141 cl += strlen("Content-Length:");
1142 int lc = atoi(cl);
1143 lencontent = lc;
1144 }
1145 }
1146 if(lencontent && !offcontent){
1147 char *cd = strstr(answer,"\r\n\r\n"); //linebreak
1148 if(cd) cd +=4;
1149 if(!cd){
1150 cd = strstr(answer,"\n\n");
1151 if(cd) cd +=2;
1152 }
1153 if(cd) offcontent = cd - answer;
1154 }
1155 }
1156 if(lencontent && offcontent)
1157 if(lr >= offcontent + lencontent) break;
1158 icount++;
1159 }
1160 shutdown(fd, SHUT_RDWR);
1161 //closesocket(fd); //might need this
1162 if(lencontent && offcontent){
1163 char *finalanswer = malloc(lencontent+1);
1164 memcpy(finalanswer,&answer[offcontent],lencontent);
1165 finalanswer[lencontent] = '\0';
1166 lr = lencontent;
1167 free(answer);
1168 answer = finalanswer;
1169 }
1170 *response = answer;
1171 }
1172 return lr;
1173}
1174int sniff_and_foreward(char *ssr_command, char *key, char *data, int size, char **answerstring)
1175{
1176 int len;
1177 double quat4[4];
1178 double vec3[3];
1179 Point posexy;
1180 zone *z;
1181
1182 //sniff request for map coordinates
1183 jsonPose2double(quat4,vec3,data);
1184 posexy.x = vec3[0];
1185 posexy.y = vec3[1];
1186 //lookup zone
1187 z = find_zone_by_point(posexy);
1188 if(!z) z = zones;
1189 if(z){
1190 ssr *ssri;
1191 //get the host:port address of the running ssr server to forward to
1192 ssri = (ssr*)z->ssr;
1193 //forward request, wait for response
1194 len = reverse_proxy(ssri->ip, ssri->port, ssr_command, key, data, size, answerstring);
1195 //if we got a response, return it
1196 }
1197 return len;
1198}
1199
1200/*zonebalancer version of iterate_post, acts like a load balancer and gateway
12011. sniffs request for geographic coords
12022. looks up SSR in zone table
12033. acts as reverse proxy and forwards request, waits for response
12044 copies response to zonebalancer response
1205*/
1206static int iterate_post_zb (void *coninfo_cls, enum MHD_ValueKind kind, const char *key,
1207 const char *filename, const char *content_type,
1208 const char *transfer_encoding, const char *data,
1209 uint64_t off, size_t size)
1210{
1211 struct connection_info_struct *con_info = coninfo_cls;
1212 if(1){
1213 printf("Key=%s\n",key);
1214 printf("filename=%s\n",filename);
1215 printf("content_type=%s\n",content_type);
1216 printf("transfer_encoding=%s\n",transfer_encoding);
1217 printf("data=%s\n",data);
1218 }
1219 if (0 == strcmp (key, "posepose"))
1220 {
1221 if ((size > 0) && (size <= MAXPOSESIZE)) //MAXNAMESIZE
1222 {
1223 char *answerstring;
1224 int len;
1225 SSR_request ssr_req;
1226
1227 //answerstring = malloc (MAXANSWERSIZE);
1228 answerstring = NULL;
1229 ssr_req.type = SSR_POSEPOSE;
1230 len = sniff_and_foreward("pose",key,data,size,&answerstring);
1231 con_info->answerstring = answerstring;
1232 con_info->len = len;
1233 }
1234 else con_info->answerstring = NULL;
1235
1236 return MHD_NO;
1237 }
1238 if (0 == strcmp (key, "posesnapshot"))
1239 {
1240 if ((size > 0) && (size <= MAXPOSESIZE)) //MAXNAMESIZE
1241 {
1242 char *answerstring;
1243 int len;
1244 SSR_request ssr_req;
1245 ssr_req.type = SSR_POSESNAPSHOT;
1246 answerstring = NULL;
1247 len = sniff_and_foreward("pose",key,data,size,&answerstring);
1248 con_info->answerstring = answerstring;
1249 con_info->len = len;
1250 }
1251 else con_info->answerstring = NULL;
1252
1253 return MHD_NO;
1254 }
1255
1256 return MHD_YES;
1257}
1258
1259
1260// this is the non-zonebalancer , regular SSR response
1261static int iterate_post (void *coninfo_cls, enum MHD_ValueKind kind, const char *key,
1262 const char *filename, const char *content_type,
1263 const char *transfer_encoding, const char *data,
1264 uint64_t off, size_t size)
1265{
1266 struct connection_info_struct *con_info = coninfo_cls;
1267 if(1){
1268 fprintf(stdout,"Key=%s\n",key);
1269 fprintf(stdout,"filename=%s\n",filename);
1270 fprintf(stdout,"content_type=%s\n",content_type);
1271 fprintf(stdout,"transfer_encoding=%s\n",transfer_encoding);
1272 fprintf(stdout,"data=%s\n",data);
1273 }
1274
1275 if (0 == strcmp (key, "init_pose"))
1276 {
1277 fprintf(stdout,"A");
1278 if ((size > 0) && (size <= MAXPOSESIZE)) //MAXNAMESIZE
1279 {
1280 char *answerstring;
1281 //double quat4[4], vec3[3];
1282 SSR_request ssr_req;
1283 answerstring = malloc (MAXANSWERSIZE);
1284 fprintf(stdout,"B");
1285 if (!answerstring)
1286 return MHD_NO;
1287 ssr_req.type = SSR_INITPOSE;
1288 //in here we should use cson to parse, and our walk_ to gather into ssr_req
1289 //parse_json_and_gather_ssr_req(data,&ssr_req);
1290 ssr_req.LOD = 0;
1291 //jsonPose2double(ssr_req.quat4,ssr_req.vec3,data);
1292 fprintf(stdout,"C");
1293 dllFreeWRL_SSRserver_enqueue_request_and_wait(fwctx, &ssr_req);
1294 fprintf(stdout,"D");
1295 ssr_req.vec3[0] += ssrleaf.xoff;
1296 ssr_req.vec3[1] += ssrleaf.yoff;
1297 ssr_req.vec3[2] += ssrleaf.zoff;
1298 doublePose2json(ssr_req.quat4,ssr_req.vec3, answerstring, MAXANSWERSIZE);
1299 fprintf(stdout,"E");
1300 //_snprintf (answerstring, MAXANSWERSIZE, greetingpage, data);
1301 con_info->answerstring = answerstring;
1302 con_info->len = strlen(answerstring);
1303 for(int k=0;k<con_info->len;k++)
1304 fprintf(stdout,"%c",con_info->answerstring[k]);
1305 fprintf(stdout," len=%d\n",con_info->len);
1306 }
1307 else con_info->answerstring = NULL;
1308
1309 return MHD_NO;
1310 }
1311
1312 if (0 == strcmp (key, "posepose"))
1313 {
1314 if ((size > 0) && (size <= MAXPOSESIZE)) //MAXNAMESIZE
1315 {
1316 char *answerstring;
1317 //double quat4[4], vec3[3];
1318 SSR_request ssr_req;
1319 answerstring = malloc (MAXANSWERSIZE);
1320 if (!answerstring) return MHD_NO;
1321 ssr_req.type = SSR_POSEPOSE;
1322 jsonPose2double(ssr_req.quat4,ssr_req.vec3,data);
1323 ssr_req.vec3[0] -= ssrleaf.xoff;
1324 ssr_req.vec3[1] -= ssrleaf.yoff;
1325 ssr_req.vec3[2] -= ssrleaf.zoff;
1326 dllFreeWRL_SSRserver_enqueue_request_and_wait(fwctx, &ssr_req);
1327 ssr_req.vec3[0] += ssrleaf.xoff;
1328 ssr_req.vec3[1] += ssrleaf.yoff;
1329 ssr_req.vec3[2] += ssrleaf.zoff;
1330 doublePose2json(ssr_req.quat4,ssr_req.vec3, answerstring, MAXANSWERSIZE);
1331 //_snprintf (answerstring, MAXANSWERSIZE, greetingpage, data);
1332 con_info->answerstring = answerstring;
1333 con_info->len = strlen(answerstring);
1334
1335 }
1336 else con_info->answerstring = NULL;
1337
1338 return MHD_NO;
1339 }
1340 if (0 == strcmp (key, "posesnapshot"))
1341 {
1342 if ((size > 0) && (size <= MAXPOSESIZE)) //MAXNAMESIZE
1343 {
1344 char *answerstring;
1345 SSR_request ssr_req;
1346 ssr_req.type = SSR_POSESNAPSHOT;
1347 ssr_req.blob = NULL;
1348 ssr_req.len = 0;
1349 jsonPose2double(ssr_req.quat4,ssr_req.vec3,data);
1350 ssr_req.vec3[0] -= ssrleaf.xoff;
1351 ssr_req.vec3[1] -= ssrleaf.yoff;
1352 ssr_req.vec3[2] -= ssrleaf.zoff;
1353 dllFreeWRL_SSRserver_enqueue_request_and_wait(fwctx, &ssr_req);
1354 con_info->answerstring = ssr_req.blob;
1355 con_info->len = ssr_req.len;
1356 }
1357 else con_info->answerstring = NULL;
1358
1359 return MHD_NO;
1360 }
1361
1362
1363 return MHD_YES;
1364}
1365void request_completed (void *cls, struct MHD_Connection *connection,
1366 void **con_cls,
1367 enum MHD_RequestTerminationCode toe)
1368{
1369 struct connection_info_struct *con_info = *con_cls;
1370
1371 if (NULL == con_info) return;
1372 if (con_info->connectiontype == POST)
1373 {
1374 MHD_destroy_post_processor (con_info->postprocessor);
1375 if (con_info->answerstring) free (con_info->answerstring);
1376 }
1377
1378 free (con_info);
1379 *con_cls = NULL;
1380}
1381
1382int haveNasties(char *url){
1383 // check for nasties
1384 //- I don't seem to need any nasty checking:
1385 //don't want them to fetch ./../../../passowrds.txt or anything above our public folder,
1386 // but the .. get cleaned out on the client end, if you trust the clients,
1387 // and %20 or %2E (.) get converted to ascii '.', and cleaned out, before this callback
1388 //on windows, don't want them to fetch C:/passwords.txt,
1389 // but it seems impossible anyway, because it comes in as /C:/passwords.txt which is nonsense so file not found
1390 //putting %08 (backspace, \b) in the url gets in here, and /%08C:/tmp/passwords.html printfs as C:/tmp/passwords.html,
1391 // but file functions don't back up, so \b stays in filepath and it can't find it
1392 int urllen, nasties = 0;
1393 urllen = strlen(url);
1394 for(int i=0;i<urllen;i++){
1395 if(i<urllen-1)
1396 if(url[i]=='.' && url[i+1] == '.') nasties++;
1397 if(url[i]==':') nasties++;
1398 if(url[i]=='\b') nasties++; //backspace
1399 //Q. are there other nasties we should check for?
1400 //Q. have incoming urls converted %20 and %specials to ascii
1401 }
1402 //end check for nasties
1403 return nasties;
1404}
1405
1406static int answer_to_connection (void *cls, struct MHD_Connection *connection,
1407 const char *url,
1408 const char *method, const char *version,
1409 const char *upload_data,
1410 size_t *upload_data_size, void **con_cls)
1411{
1412 if(1){
1413 printf("\nurl=%s\n",url);
1414 printf("method=%s\n",method);
1415 printf("version=%s\n\n",version);
1416 printf("upload_data=%s\n",upload_data);
1417 }
1418 if(NULL == *con_cls)
1419 {
1420 struct connection_info_struct *con_info;
1421
1422 con_info = malloc (sizeof (struct connection_info_struct));
1423 if (NULL == con_info)
1424 return MHD_NO;
1425 con_info->answerstring = NULL;
1426 //If the new request is a POST, the postprocessor must be created now. In addition, the type of the request is stored for convenience.
1427
1428 if (0 == strcmp (method, "POST"))
1429 {
1430 if(running_as_zonebalancer)
1431 con_info->postprocessor = MHD_create_post_processor (connection, POSTBUFFERSIZE,
1432 iterate_post_zb, (void*) con_info);
1433 else
1434 con_info->postprocessor = MHD_create_post_processor (connection, POSTBUFFERSIZE,
1435 iterate_post, (void*) con_info);
1436 if (NULL == con_info->postprocessor)
1437 {
1438 free (con_info);
1439 return MHD_NO;
1440 }
1441 con_info->connectiontype = POST;
1442 }
1443 else
1444 con_info->connectiontype = GET;
1445 //The address of our structure will both serve as the indicator for successive iterations and to remember the particular details about the connection.
1446
1447 *con_cls = (void*) con_info;
1448 return MHD_YES;
1449 }
1450 //The rest of the function will not be executed on the first iteration. A GET request is easily satisfied by sending the question form.
1451 if (0 == strcmp (method, "GET"))
1452 {
1453 //return send_page (connection, askpage);
1454 // maybe valid GET request
1455 struct MHD_Response * response;
1456 struct stat ss;
1457 int fd, ret;
1458 ssize_t blocksz; //, left2read;
1459 char *filename = &url[1];
1460 if (stat(filename, &ss) < 0) {
1461 printf("load_file_read: could not stat: %s\n", filename);
1462 return MHD_NO;
1463 }
1464 if (!ss.st_size) {
1465 printf("load_file_read: file is empty %s\n", filename);
1466 return MHD_NO;
1467 }
1468 if (ss.st_size > SSIZE_MAX) {
1469 /* file is greater that read's max block size: we must make a loop */
1470 blocksz = SSIZE_MAX;
1471 } else {
1472 blocksz = ss.st_size;
1473 }
1474 fd = open(filename, O_RDONLY | O_BINARY | O_BLOCK);
1475 if (fd < 0) {
1476 printf("load_file_read: could not open: %s\n", filename);
1477 return MHD_NO;
1478 }
1479 //response = MHD_create_response_from_fd(ss.st_size,fd); //didn't work in win32, complained ERR_CONTENT_LENGTH_MISMATCH in client
1480 // will be closed when response is destroyed
1481 //close(fd);
1482
1483 char *blob = malloc(blocksz);
1484 read(fd,blob,blocksz);
1485 //on win32, it works most reliably -without crashing- if I say for it to deep copy the blob, and free its own copy
1486 //then free my copy here
1487 response = MHD_create_response_from_data(blocksz,(void*) blob, MHD_NO, MHD_YES);
1488 free(blob);
1489 close(fd);
1490 ret = MHD_queue_response(connection,MHD_HTTP_OK,response);
1491 MHD_destroy_response(response);
1492 return ret;
1493
1494 }
1495 //In case of POST, we invoke the post processor for as long as data keeps incoming, setting *upload_data_size to zero in order to indicate that we have processed—or at least have considered—all of it.
1496 if (0 == strcmp (method, "POST"))
1497 {
1498 struct connection_info_struct *con_info = *con_cls;
1499 if (*upload_data_size != 0)
1500 {
1501 MHD_post_process (con_info->postprocessor, upload_data,
1502 *upload_data_size);
1503 *upload_data_size = 0;
1504 return MHD_YES;
1505 }
1506 else
1507 if (NULL != con_info->answerstring)
1508 return send_page (connection, con_info->answerstring, con_info->len);
1509 }
1510 //Finally, if they are neither GET nor POST requests, the error page is returned.
1511 return send_page(connection, errorpage, strlen(errorpage));
1512}
1513
1514// simple handler - no post guts
1515static int ahc_echo(void * cls,
1516 struct MHD_Connection * connection,
1517 const char * url,
1518 const char * method,
1519 const char * version,
1520 const char * upload_data,
1521 size_t * upload_data_size,
1522 void ** ptr)
1523{
1524 static int dummy;
1525 const char * page = cls;
1526 struct MHD_Response * response;
1527 int ret;
1528
1529 if (!strcmp(method, "POST"))
1530 {
1531 //POST handler - we'll come in here for our special goodies:
1532 // come in with viewpoint pose matrix, leave with screen snapshot and updated pose matrix
1533 printf("url=%s\n",url);
1534 printf("method=%s\n",method);
1535 printf("version=%s\n",version);
1536 printf("upload_data=%s\n",upload_data);
1537 }
1538
1539 if(!strcmp(method, "GET"))
1540 {
1541 //GET handler - we'll come in here for our ordinary static pages, glMatrix.js etc
1542
1543 if (&dummy != *ptr)
1544 {
1545 /* The first time only the headers are valid,
1546 do not respond in the first round... */
1547 *ptr = &dummy;
1548 return MHD_YES;
1549 }
1550 if (0 != *upload_data_size)
1551 return MHD_NO; /* upload data in a GET!? */
1552 *ptr = NULL; /* clear context pointer */
1553 printf(" %p ",connection);
1554 if(!strcmp(url,"/") || haveNasties(url)){
1555 // invalid GET request
1556 response = MHD_create_response_from_data(strlen(page),
1557 (void*) page,
1558 MHD_NO,
1559 MHD_NO);
1560 }else{
1561 // maybe valid GET request
1562 struct stat ss;
1563 int fd;
1564 ssize_t blocksz; //, left2read;
1565 char *filename = &url[1];
1566 if (stat(filename, &ss) < 0) {
1567 printf("load_file_read: could not stat: %s\n", filename);
1568 return MHD_NO;
1569 }
1570 if (!ss.st_size) {
1571 printf("load_file_read: file is empty %s\n", filename);
1572 return MHD_NO;
1573 }
1574 if (ss.st_size > SSIZE_MAX) {
1575 /* file is greater that read's max block size: we must make a loop */
1576 blocksz = SSIZE_MAX;
1577 } else {
1578 blocksz = ss.st_size;
1579 }
1580 fd = open(filename, O_RDONLY | O_BINARY | O_BLOCK);
1581 if (fd < 0) {
1582 printf("load_file_read: could not open: %s\n", filename);
1583 return MHD_NO;
1584 }
1585 //response = MHD_create_response_from_fd(ss.st_size,fd); //didn't work in win32, complained ERR_CONTENT_LENGTH_MISMATCH in client
1586 // will be closed when response is destroyed
1587 //close(fd);
1588
1589 char *blob = malloc(blocksz);
1590 read(fd,blob,blocksz);
1591 //on win32, it works most reliably -without crashing- if I say for it to deep copy the blob, and free its own copy
1592 //then free my copy here
1593 response = MHD_create_response_from_data(blocksz,(void*) blob, MHD_NO, MHD_YES);
1594 free(blob);
1595 close(fd);
1596 }
1597 ret = MHD_queue_response(connection,MHD_HTTP_OK,response);
1598 MHD_destroy_response(response);
1599
1600 }else{
1601 ret = MHD_NO; /* unexpected method, not GET or POST */
1602 }
1603 return ret;
1604}
1605
1606// see libmicrohttpd docs on this site:
1607// http://www.gnu.org/software/libmicrohttpd/
1608// tutorials: http://www.gnu.org/software/libmicrohttpd/tutorial.html
1609int main0(int argc, char ** argv) {
1610/*
1611How to run SSR Server
1612SSRServer.exe 8081 "C:/myscenes/sceneA.x3d" --xoff 123.45 --yoff 345.67 --zoff 789.01 --publicpath "C:\abc\def"
1613- use a different port for each SSR
1614- xoff,yoff,zoff are applied to world coordinates going one way, and stripped off going the other
1615 - so you can get the effect of double precision coordinates
1616--publicpath - /public folder that contains SSRClient.html, DragHere.jpg, favicon.ico, gl-matrix.js
1617 (if not given, ssrserver has a guess for win32 developer running from projectfiles_*)
1618
1619How to run ZoneBalancer:
1620SSRServer.exe 8080 --zonebalancer
1621 - it attempts to read zonebalancer.xml from the folder above the current working directory
1622 - in xml, it says how many SSRs, and what geographic zones each SSR covers
1623 - a zone is demarcated with a polygon
1624 - then your html client will talk to zonebalancer, and zonebalancer will talk to the SSRs
1625 - currently zonebalancer doesn't launch -or kill- SSRs, you need to do each of those some other way, such as shell script
1626*/
1627 struct MHD_Daemon * d;
1628 char *portstr, *url, *publicpath;
1629 int iaction;
1630 if(strstr(argv[0],"projectfiles")){
1631 //developer folders, use src/SSR/public for Client.html
1632 char *pf;
1633 publicpath = strdup(argv[0]);
1634 pf = strstr(publicpath,"projectfiles");
1635 strcpy(pf,"src/SSR/public");
1636 }else{
1637 //installed, use folder beside ssrserver.exe (PS installer please install public folder with binary distro)
1638 char *pf;
1639 publicpath = strdup(argv[0]);
1640 pf = strstr(publicpath,"SSRserver.exe");
1641 strcpy(pf,"public");
1642 }
1643
1644
1645 if (argc < 2) {
1646 portstr = "8080";
1647 printf("%s PORT\n",portstr);
1648 url = scene_path;
1649 //return 1;
1650 }else{
1651 portstr = argv[1];
1652 if(argc < 3)
1653 url = scene_path;
1654 else{
1655 url = argv[2];
1656 if(!strcmp(url,"--zonebalancer")){
1657 load_polys("..\\zonebalancer.xml");
1658 running_as_zonebalancer = 1;
1659 initialize_sockets();
1660 }
1661 for(int k=3;k<argc;k++)
1662 {
1663 char *arg = argv[k];
1664 if(!strncmp(arg,"--",2))
1665 if(!strncmp(&arg[3],"off=",4)){
1666 if(!strncmp(&arg[2],"x",1)){
1667 sscanf(&arg[7],"%lf",&ssrleaf.xoff);
1668 }else if(!strncmp(&arg[2],"y",1)){
1669 sscanf(&arg[7],"%lf",&ssrleaf.yoff);
1670 }else if(!strncmp(&arg[2],"z",1)){
1671 sscanf(&arg[7],"%lf",&ssrleaf.zoff);
1672 }
1673 }
1674 if(!strcmp(arg,"--publicpath")){
1675 if(argc > k){
1676 publicpath = strdup(argv[k+1]);
1677 k++;
1678 }
1679 }
1680 }
1681 }
1682 }
1683 iaction = 2;
1684 if(running_as_zonebalancer)
1685 iaction = 2;
1686 if(iaction == 1) {
1687
1688 //simple echo of incoming request
1689 d = MHD_start_daemon(MHD_USE_THREAD_PER_CONNECTION,
1690 atoi(portstr),
1691 NULL,
1692 NULL,
1693 &ahc_echo,
1694 PAGE,
1695 MHD_OPTION_END);
1696 }
1697 if(iaction == 2){
1698 printf("public path: %s\n",publicpath);
1699 chdir(publicpath);
1700
1701 //our special SSRServer request handler
1702 d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION, //MHD_USE_SELECT_INTERNALLY
1703 atoi(portstr), //PORT,
1704 NULL,
1705 NULL,
1706 &answer_to_connection,
1707 PAGE,
1708 MHD_OPTION_NOTIFY_COMPLETED,
1709 &request_completed,
1710 NULL,
1711 MHD_OPTION_END);
1712 }
1713 if (d == NULL)
1714 return 1;
1715 if(!running_as_zonebalancer)
1716 runFW(url);
1717 printf("Press Enter to stop libmicrohttp deamon and exit:");
1718 getchar();
1719 if(!running_as_zonebalancer)
1720 stopFW();
1721 MHD_stop_daemon(d);
1722 return 0;
1723}
1724
1725
1726int main(int argc, char ** argv) {
1727 int iret = main0(argc, argv);
1728 if(iret){
1729 printf("Press Enter to exit:");
1730 getchar();
1731 }
1732}
Definition DIS.h:642
float x
x
Definition DIS.h:644
float y
y
Definition DIS.h:646
cson_array is an opaque handle to an Array value.
A generic buffer class.
A key/value pair collection.
An iterator type for traversing object properties.
cson_object is an opaque handle to an Object value.
A class for holding JSON parser information.
Strings are allocated as an instances of this class with N+1 trailing bytes, where N is the length of...
The core value type of this API.
Definition Viewer.h:139