FreeWRL/FreeX3D  3.0.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 
75 Proposed SSR API JSON
76 Get: (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 
164 x Set: (POST) - there's no such thing because we are (currently) sessionless.
165  - resend any client-specific data with any needy request
166 
167 July 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"
207 int run_fw = 1;
208 void *fwctx = NULL;
209 int 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 }
220 int 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);
230 struct keyval {
231  char *key;
232  cson_value *cv;
233 };
234 typedef 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*
242 } walk_cbdata;
243 int walk_array_cson(cson_array *arr, void *cbdata);
244 
245 
246 int 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 }
268 int 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 }
281 int 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 }
289 int 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 }
356 int 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 }
367 int 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 }
375 int 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 }
402 int 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 
415 int 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 }
451 int 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 }
485 int 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 }
494 int 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==>>>>>>>>>>>>
519 static int running_as_zonebalancer = 0;
520 #ifdef _MSC_VER
521 #include <direct.h>
522 #define chdir _chdir
523 #define strcasecmp _stricmp
524 WSADATA wsaData;
525 void 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
534 void initialize_sockets(){}
535 #endif
536 
537 //POINT IN POLY
538 typedef struct Point {
539  double x;
540  double y;
541 } Point;
542 
543 typedef 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;
552 typedef 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;
560 static zone *zones = NULL;
561 static ssr *ssrs = NULL;
562 void 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 }
574 void 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>
590 static 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 
608 static 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 }
681 zone *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 }
690 int cn_PnPoly( Point P, Point *V, int n );
691 zone *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 }
703 void 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 }
721 void 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 }
738 static int testing_pointinpoly = 1;
739 void 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 */
767 typedef struct polygon {
768  int n;
769  double *pts; //xyz, so polygon can have varying z
770 } polygon;
771 typedef struct extrusion {
772  polygon poly;
773  double below;
774  double above;
775 } extrusion;
776 static 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 
819 const 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 
826 const char *greetingpage =
827  "<html><body><h1>Welcome, %s!</center></h1></body></html>";
828 
829 const char *errorpage =
830  "<html><body>This doesn't seem to be right.</body></html>";
831 
832 
833 static int
834 send_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
850 static 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 }
855 static 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 }
860 static 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 
927 static 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
978 int sockwrite(SOCKET s, const char *buf, int len){
979  return send(s,buf,len,0);
980 }
981 int 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
990 int sockwrite(SOCKET s, const char *buf, int len){
991  return write(s,buf,len);
992 }
993 int sockread(SOCKET s, const char *buf, int len){
994  return recv(s,buf,len,0);
995 }
996 #endif
997 
998 int 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]";
1049 int 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";
1068 int 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 }
1174 int 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
1201 1. sniffs request for geographic coords
1202 2. looks up SSR in zone table
1203 3. acts as reverse proxy and forwards request, waits for response
1204 4 copies response to zonebalancer response
1205 */
1206 static 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
1261 static 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 }
1365 void 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 
1382 int 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 
1406 static 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
1515 static 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
1609 int main0(int argc, char ** argv) {
1610 /*
1611 How to run SSR Server
1612 SSRServer.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 
1619 How to run ZoneBalancer:
1620 SSRServer.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 
1726 int main(int argc, char ** argv) {
1727  int iret = main0(argc, argv);
1728  if(iret){
1729  printf("Press Enter to exit:");
1730  getchar();
1731  }
1732 }
unsigned char * mem
The memory allocated for and owned by this buffer.
The core value type of this API.
cson_array is an opaque handle to an Array value.
Definition: SSRServer.c:552
A class for holding JSON parser information.
cson_size_t used
The number of bytes "used" by this object.
A key/value pair collection.
An iterator type for traversing object properties.
Strings are allocated as an instances of this class with N+1 trailing bytes, where N is the length of...
Definition: common.c:52
cson_object is an opaque handle to an Object value.
A generic buffer class.
Definition: Viewer.h:174