[
  {
    "path": ".gitignore",
    "content": "*.a\n*.o\n*.lo\n*.so*\nfqd.h\nfqd\nfqc\nfqs\nfqtool\nfq_bench\nfq_dtrace.h\nfq_rcvr\nfq_sndr\nlua/fqclient.lua\nMakefile.build\nMakefile.depend\n*.dSYM/\n.*.swp\nck-*/Makefile\nck-*/build/ck.spec\nck-*/doc/Makefile\njava/classes/\njava/*.class\njava/fqclient.jar\n*~\n.autotools\n.cproject\n.project\n.settings/\ntest/fqd.sqlite\ntest/out.log\ntest/test_detail.xml\n"
  },
  {
    "path": "ChangeLog.md",
    "content": "# ChangeLog\n\n### v0.13.11\n\n* Fix test issue where libfq could not be found.\n* Minor change to compiler optimization flag. Level `O5` does not exist and is\n  effectively `O3`.\n* Update systemd install path. The new path is equivalent to the old, but\n  avoids potential issues with `/lib` being a symbolic link on popular Linux\n  distros.\n* Fix potential race in host resolution.\n\n### v0.13.10\n\n* Correct a build issue when libbcd support is disabled.\n* Fix race condition in client connection status.\n\n### v0.13.9\n\n* Fix file descriptor leak on connection error.\n* Correct usage of `pthread_*` return values. Remove invalid use of `volatile`.\n* Add missing `volatile` in Java client, and replace `LinkedList` with\n  `ArrayDeque`.\n\n### v0.13.8\n\n* Upgrade jquery to 3.5.1 for FQ user interface\n\n### v0.13.7\n\n * Queue drops are tracked as `dropped_to`.\n * Web UI updated to display queue drops/rate.\n * -b deprecated, -B added, and BCD is disabled by default.\n\n### v0.13.6\n\n * Track drops to queues as `dropped_in` in status.\n\n### v0.13.5\n\n * Force disconnect on message read/write error.\n * Reuse listener threads.\n\n### v0.13.4\n\n * Various code cleanups.\n * Better bounds checking on auth handshake (allow full size).\n * Fix BCD integration.\n\n### v0.13.3\n\n * Set `SO_REUSEPORT = 1` for listener.\n * Add `-b` to disable BCD/backtrace integration.\n\n### v0.13.2\n\n * Name threads on Linux to aid debugging.\n * Prevent abort when queue removal fails.\n\n### v0.13.1\n\n * Add libbcd support for catching faults with backtrace.\n\n### v0.13.0\n\n * Place fq modules in $(LIBEXECDIR)/fq\n * Automatically load all available modules\n\n### v0.12.1\n\n * Move the `valnode_t` definition into fq.h.\n * Fix hex construction macro.\n * Support var-args in loadable program functions.\n * Fix multi-argument parsing in routing grammar.\n\n### v0.12.0\n\n * Omit unneeded library dependencies on Illumos.\n * Make poll() calls resume after signal interruption.\n\n### v0.11.0\n\n * Use socket keep-alives for client/server connections.\n * Fix use-after-free bug in lua ffi client bindings.\n * Fix test suite.\n * Explicit registration of local function to better navigate\n   changing dlsym \"self\" personalities.\n * ENABLE_DTRACE=1 Linux build flag.\n\n### v0.10.14\n\n * Fixes to fq-client.lua on OmniOS\n\n### v0.10.13\n\n * Add `fqs` tool for sending messages from stdin\n * Test suite utilizing `mtevbusted` from\n   [libmtev](https://github.com/circonus-labs/libmtev/) (PR #37)\n\n### v0.10.12\n\n * Fix misuse of stack for freeing messages (0.10.11 fix was bad).\n * Add Linux futex support for lower-latency idle network wake-up.\n * Ensure message ordering on per-client data connections.\n\n### v0.10.11\n\n * Fix crash when shutting down client that has never seen a message.\n\n### v0.10.10\n\n * Fix source management issue. 0.10.9 tag exluded commits.\n * Change message free-lists to prevent use-after-free on thread exit.\n\n### v0.10.9\n\n * Fix builds on newer Mac OS X\n * Change message free-lists to prevent use-after-free on thread exit.\n * Fix bug in server->client heartbeats not beating.\n\n### v0.10.8\n\n * Fix querystring parsing crash when parameters are not k=v form.\n * Resume on-disk queues at the right checkpoint location.\n\n### v0.10.7\n\n * Fix bug in route binding prefix matching causing misdirected messages\n   (clients could get more than they asked for).\n * Fix bug on some Linux systems regarding exposed symbols.\n\n### v0.10.6\n\n * Fix crashing issue enqueueing messages due to unsafe use of spsc fifos.\n * Add dynamic loading of routing program extensions.\n * Move the \"sample\" function to a dynamic extension for example purposes.\n"
  },
  {
    "path": "LICENSE",
    "content": "Copyright (c) 2013 OmniTI Computer Consulting, Inc.\nAll rights reserved.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to\ndeal in the Software without restriction, including without limitation the\nrights to use, copy, modify, merge, publish, distribute, sublicense, and/or\nsell copies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\nFROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\nIN THE SOFTWARE.\n"
  },
  {
    "path": "Makefile",
    "content": "# If you want a verbose make (visible commands) add V=1 to you invocation\n\n.SUFFIXES: .lo\n\nCC=gcc\nLD=gcc\nLN_S=ln -s\nCOPT=-O3\nTAR=tar\nSED=sed\nPREFIX=/usr/local\nINCLUDEDIR=$(PREFIX)/include\nLIBDIR=$(PREFIX)/lib\nLIBEXECDIR=$(PREFIX)/libexec\nBINDIR=$(PREFIX)/bin\nSBINDIR=$(PREFIX)/sbin\nVARLIBFQ=$(PREFIX)/var/lib/fq\nLUADIR=$(PREFIX)/share/lua/5.1\nINSTALL=install\nSHLD=$(LD) -shared\nMODULELD=$(LD) -shared\nLIBEXT=so\nSHCFLAGS=-fPIC\nDTRACE=/usr/sbin/dtrace\nOS=$(shell uname)\n\nFQ_MAJOR=0\nFQ_MINOR=13\nFQ_MICRO=12\n\nQ=\nifeq ($(V),)\n\tQ=@\nendif\n\nVENDOR_CFLAGS=\nVENDOR_LDFLAGS=\nDTRACEFLAGS=\nEXTRA_CFLAGS=$(VENDOR_CFLAGS) -g -D_REENTRANT -std=gnu99 -pedantic -Wall\nEXTRA_CFLAGS+=-DVARLIBFQDIR=\\\"$(VARLIBFQ)\\\"\nEXTRA_CFLAGS+=-DLIBEXECDIR=\\\"$(LIBEXECDIR)/fq\\\"\n#EXTRA_CFLAGS+=-DDEBUG\n\nCLIENT_OBJ=fq_client.o fq_msg.o fq_utils.o\nCLIENT_OBJ_LO=$(CLIENT_OBJ:%.o=%.lo)\nFQD_OBJ=fqd.o fqd_listener.o fqd_ccs.o fqd_dss.o fqd_config.o \\\n\tfqd_queue.o fqd_routemgr.o fqd_queue_mem.o fqd_queue_jlog.o \\\n\tfqd_http.o fqd_prog.o fqd_peer.o http_parser.o \\\n\t$(CLIENT_OBJ)\nFQC_OBJ=fqc.o $(CLIENT_OBJ)\nFQD_SAMPLE_OBJ=fqd_dyn_sample.lo\nFQD_DTRACE_OBJ=\n\nskip_bcd=$(NO_BCD)\nifdef skip_bcd\nFQDLIBS=-ljlog -lsqlite3\nEXTRA_CFLAGS+=-DNO_BCD\nelse\nFQDLIBS=-ljlog -lsqlite3 -lbcd\nendif\nLIBS+=-lck\n\nSHLDFLAGS=\nifeq ($(OS),SunOS)\nSHLDFLAGS+=-R$(LIBDIR)\nLIBS+=-lcrypto -lsocket -lnsl -lumem -luuid\nLIBLIBS+=-luuid -lsocket -lnsl\nEXTRA_CFLAGS+=-D_XOPEN_SOURCE=600 \nEXTRA_CFLAGS+=-D_BSD_SOURCE\nEXTRA_CFLAGS+=-D__EXTENSIONS__ -DHAVE_UINTXX_T -DSIZEOF_LONG_LONG_INT=8 -m64 -D_REENTRANT -DHAVE_GETHOSTBYNAME_R\nEXTRA_SHLDFLAGS=-m64\nFQD_DTRACE_OBJ=fq_dtrace.o\nDTRACEFLAGS=-xnolibs\nelse\nifeq ($(OS),Darwin)\nMODULELD=ld -bundle\nLOADER=-bundle_loader fqd -lc\nEXTRA_CFLAGS+=-D_DARWIN_C_SOURCE -DHAVE_U_INTXX_T -DHAVE_INTXX_T -DHAVE_U_INT64_T -DHAVE_INT64_T \\\n\t-Wno-dollar-in-identifier-extension -Wno-gnu-statement-expression -Wno-deprecated-declarations\n#EXTRA_CFLAGS+=-Weverything\nLIBEXT=dylib\nelse\nifeq ($(OS),Linux)\nEXTRA_CFLAGS+=-D_XOPEN_SOURCE=600 \nEXTRA_CFLAGS+=-D_DEFAULT_SOURCE -DBYTE_ORDER=__BYTE_ORDER -DBIG_ENDIAN=__BIG_ENDIAN\nSHLDFLAGS+=-Wl,-rpath=$(LIBDIR)\nLDFLAGS+=-rdynamic -export-dynamic\nLIBS+=-lcrypto -lpthread -ldl -luuid -lrt \nLIBLIBS+=-lpthread -luuid -lrt\nDTRACE=\nifeq ($(ENABLE_DTRACE),1)\nDTRACE=/bin/dtrace\nFQD_DTRACE_OBJ=fq_dtrace.o\nendif\nelse\nifeq ($(OS),FreeBSD)\nSHLDFLAGS+=-Wl,-rpath=$(LIBDIR)\nLDFLAGS+=-rdynamic\nLIBS+=-lcrypto -lpthread -luuid -lexecinfo\nLIBLIBS+=-lpthread -luuid -lexecinfo\nFQD_DTRACE_OBJ=fq_dtrace.o\nendif\nendif\nendif\nendif\n\nall:\tlibfq.$(LIBEXT) libfq.a fqd fqc fqs fqtool fq_sndr fq_rcvr fq_bench \\\n\tfq-sample.so lua/fqclient.lua\n\ninclude Makefile.depend\n\nSHLDFLAGS+=$(VENDOR_LDFLAGS) -L$(LIBDIR)\nifeq ($(OS),Darwin)\nSHLDFLAGS+=-current_version $(FQ_MAJOR).$(FQ_MINOR).$(FQ_MICRO) -install_name $(LIBDIR)/libfq.$(FQ_MAJOR).dylib\nSOLONG=libfq.$(FQ_MAJOR).$(FQ_MINOR).$(FQ_MICRO).dylib\nSOSHORT=libfq.$(FQ_MAJOR).dylib\nLIBNAME=libfq.dylib\nelse\nSHLDFLAGS+=-Wl,-soname,libfq.so.$(FQ_MAJOR)\nSOLONG=libfq.so.$(FQ_MAJOR).$(FQ_MINOR).$(FQ_MICRO)\nSOSHORT=libfq.so.$(FQ_MAJOR)\nLIBNAME=libfq.so\nendif\n\nCFLAGS+=$(EXTRA_CFLAGS)\nSHCFLAGS+=$(EXTRA_CFLAGS)\nLDFLAGS+=$(VENDOR_LDFLAGS)\n\nfqd.h:\tfqd.h.in\n\tsed -e 's/@@FQ_MAJOR@@/'$(FQ_MAJOR)'/g;' \\\n\t\t-e 's/@@FQ_MINOR@@/'$(FQ_MINOR)'/g;' \\\n\t\t-e 's/@@FQ_MICRO@@/'$(FQ_MICRO)'/g;' < fqd.h.in > fqd.h\n\nfq_dtrace.h:\tfq_dtrace.d\n\t-$(DTRACE) $(DTRACEFLAGS) -h -o $@ -s $<\n\tif [ ! -f $@ ]; then cp fq_dtrace.blank.h $@; fi\n\nfq_dtrace.o: $(FQD_OBJ)\n\t$(DTRACE) $(DTRACEFLAGS) -64 -G -s fq_dtrace.d -o $@ $(FQD_OBJ)\n\nfq_dtrace.blank.h:\tfq_dtrace.h\n\tawk 'BEGIN{print \"#if 0\"} /#else/,/#endif/{print}' $< > $@\n\nfqd:\t$(FQD_OBJ) $(FQD_DTRACE_OBJ)\n\t@echo \" - linking $@\"\n\t$(Q)$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(FQD_OBJ) $(FQD_DTRACE_OBJ) $(LIBS) $(FQDLIBS)\n\nfqc:\t$(FQC_OBJ)\n\t@echo \" - linking $@\"\n\t$(Q)$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(FQC_OBJ) $(LIBS)\n\nfq-sample.so:\tfqd $(FQD_SAMPLE_OBJ)\n\t$(Q)$(MODULELD) $(LOADER) $(EXTRA_SHLDFLAGS) -o $@ $(FQD_SAMPLE_OBJ)\n\nfq_sndr:\tfq_sndr.o libfq.a\n\t@echo \" - linking $@\"\n\t$(Q)$(CC) $(CFLAGS) $(LDFLAGS) -L. -lfq -o $@ $^ $(LIBS)\n\nfqs:\tfqs.o libfq.a\n\t@echo \" - linking $@\"\n\t$(Q)$(CC) $(CFLAGS) $(LDFLAGS) -L. -lfq -o $@ $^ $(LIBS)\n\nfq_rcvr:\tfq_rcvr.o libfq.a\n\t@echo \" - linking $@\"\n\t$(Q)$(CC) $(CFLAGS) $(LDFLAGS) -L. -lfq -o $@ $^ $(LIBS)\n\nfqtool:\tfqtool.o libfq.a\n\t@echo \" - linking $@\"\n\t$(Q)$(CC) $(CFLAGS) $(LDFLAGS) -L. -lfq -o $@ $^ $(LIBS)\n\nfq_bench:\tfq_bench.o libfq.a\n\t@echo \" - linking $@\"\n\t$(Q)$(CC) $(CFLAGS) $(LDFLAGS) -L. -lfq -o $@ $^ $(LIBS)\n\nlibfq.$(LIBEXT):\t$(CLIENT_OBJ_LO)\n\t@echo \" - creating $@\"\n\t$(Q)$(SHLD) $(EXTRA_SHLDFLAGS) $(SHLDFLAGS) -o $@ $(CLIENT_OBJ_LO) $(LIBLIBS)\n\t$(LN_S) -f $@ $(SOSHORT)\n\nlibfq.a:\t$(CLIENT_OBJ)\n\t@echo \" - creating $@\"\n\t$(Q)ar cr $@ $(CLIENT_OBJ)\n\n.c.o:\t$<\n\t@echo \" - compiling $<\"\n\t$(Q)$(CC) $(CPPFLAGS) $(CFLAGS) $(COPT) -o $@ -c $<\n\n.c.lo:\t$<\n\t@echo \" - compiling $<\"\n\t$(Q)$(CC) $(CPPFLAGS) $(SHCFLAGS) -o $@ -c $<\n\nMakefile.depend:\tfq_dtrace.h fqd.h\n\t@echo \" - make depend\"\n\t$(Q)$(CC) $(CPPFLAGS) $(CFLAGS) -MM *.c > Makefile.depend\n\njava/fqclient.jar:\n\t(cd java && $(MAKE) fqclient.jar)\n\nlua/fqclient.lua:\n\t(cd lua; ./generatelua.sh)\n\ninstall:\tall\n\t$(INSTALL) -d $(DESTDIR)$(INCLUDEDIR)\n\t$(INSTALL) -m 0444 fq.h $(DESTDIR)$(INCLUDEDIR)/fq.h\n\t$(INSTALL) -d $(DESTDIR)$(LIBDIR)\n\t$(INSTALL) -m 0444 libfq.a $(DESTDIR)$(LIBDIR)/libfq.a\n\t$(INSTALL) -m 0555 libfq.$(LIBEXT) $(DESTDIR)$(LIBDIR)/$(SOLONG)\n\t$(LN_S) -f $(SOLONG) $(DESTDIR)$(LIBDIR)/$(SOSHORT)\n\t$(LN_S) -f $(SOLONG) $(DESTDIR)$(LIBDIR)/$(LIBNAME)\n\t$(INSTALL) -d $(DESTDIR)$(LIBEXECDIR)/fq\n\t$(INSTALL) -m 0555 fq-sample.so $(DESTDIR)$(LIBEXECDIR)/fq/fq-sample.so\n\t$(INSTALL) -d $(DESTDIR)$(BINDIR)\n\t$(INSTALL) -m 0555 fqtool $(DESTDIR)$(BINDIR)/fqtool\n\t$(INSTALL) -m 0555 fqs $(DESTDIR)$(BINDIR)/fqs\n\t$(INSTALL) -d $(DESTDIR)$(SBINDIR)\n\t$(INSTALL) -m 0555 fqd $(DESTDIR)$(SBINDIR)/fqd\n\t$(INSTALL) -d $(DESTDIR)$(VARLIBFQ)\n\t$(TAR) cf - web | (cd $(DESTDIR)$(VARLIBFQ) && $(TAR) xf -)\n\t$(INSTALL) -d $(DESTDIR)/usr/lib/dtrace\n\t$(INSTALL) -m 0444 fq.d $(DESTDIR)/usr/lib/dtrace/fq.d\n\t$(INSTALL) -d $(DESTDIR)$(LUADIR)\n\t$(INSTALL) -m 0644 lua/fqclient.lua $(DESTDIR)$(LUADIR)\n\t$(INSTALL) -m 0555 lua/fq-sender lua/fq-receiver lua/fq-proxy $(DESTDIR)$(BINDIR)\n\ninstall-systemd:\tinstall\n\t$(INSTALL) -d $(DESTDIR)/usr/lib/systemd/system\n\t$(INSTALL) -d $(DESTDIR)/usr/lib/systemd/system-preset\n\t$(INSTALL) -m 0644 service-configs/circonus-fq.service $(DESTDIR)/usr/lib/systemd/system/circonus-fq.service\n\t$(INSTALL) -m 0644 service-configs/50-circonus-fq.preset $(DESTDIR)/usr/lib/systemd/system-preset/50-circonus-fq.preset\n\t$(INSTALL) -m 0644 service-configs/daemon_options $(DESTDIR)$(VARLIBFQ)/daemon_options\n\nclean:\n\trm -f *.o *.a fqc fqd fqs *.$(LIBEXT) $(SOSHORT) fq_dtrace.h lua/fqclient.lua\n\n.PHONY: test\ntest: lua/fqclient.lua\n\t./test/run-tests.sh\n"
  },
  {
    "path": "README.md",
    "content": "# fq.\n\n<a href=\"https://scan.coverity.com/projects/circonus-labs-fq\">\n  <img alt=\"Coverity Scan Build Status\"\n       src=\"https://scan.coverity.com/projects/13357/badge.svg\"/>\n</a>\n\nfq is a *brokered* message queue using a publish subscribe model.  It is architected for performance and isn't (today) designed for large numbers of connected clients.\n\n\n    +------------+                        +-----------+\n    |- exchange -|<-- (msg publication) --|- client0 -|\n    +------------+                        +-----------+\n    |- routemap -|\n    +------------+\n        |      |              +---------+\n        |      +--------------|- queue -|\n        |                     +---------+\n    +---------+                  |\n    |- queue -|                  |   +-----------+\n    +---------+                  +---|- client1 -|\n            |                        +-----------+\n            |  +-----------+\n            +--|- client2 -|\n            |  +-----------+\n            |\n       +-----------+\n       |- client3 -|\n       +-----------+\n\n## Terminology\n\n### Broker\n\nThe `fqd` process. The daemon through which all knowledge passes.\n\n### Peers\n\nPeers are connected `fqd` processes.  It is important to note that peers are unidirectional.  If A peers with B, then A will act as a client to B. If you want bidirectional peering, you must specify that A peers with B and B peers with A.  The system aims to prevent cyclic delivery of messages efficiently.\n\nAdding peers is done directly via fqd's sqlite DB store:\n\n```\n; sqlite3 /var/lib/fq/fqd.sqlite\nsqlite> INSERT INTO \"upstream\"\n              (host, port, source, password, exchange, program, permanent_binding)\n        VALUES('peerB',8765,'fqd-peera//mem:drop,private,backlog=4096','none','logging','prefix:\"http.access.json.\"','false');\n```\n\n### Client\n\n * [C client - libfq](https://github.com/postwait/fq/blob/master/fq.h#L164-L205)\n * [Java client - fq.jar](https://github.com/postwait/fq/blob/master/java/src/com/omniti/labs/FqClientImplInterface.java)\n * [Node.js client - fq](https://www.npmjs.com/package/fq)\n * [Go client - fq](https://godoc.org/github.com/postwait/gofq)\n * submission-only /submit API (see below)\n\nA client is an applications connection to fq over TCP/IP to send or receive messages. A client makes two TCP/IP connections to fq.  An application can present itself to fq as multiple clients at one time (by opening new pairs of connections). See Queues for reasons why.\n\n### Exchanges\n\nExchanges are like buses on which messages may be sent.  You cannot send a message without doing so on an exchange.  Exchanges are created within fq on-demand.\n\n### Queues\n\nQueues are queues. If you stick something in one end, you should expect it to come out the other.  A single queue may have multiple clients subscribed.  When a client connects, it is attached to one and only one queue.  If an application wishes to attach to more than one queue, it should present as multiple clients.  Queues use a competitive consumption model meaning that if multiple clients are attached to a single queue, the messages sent to that queue will be distributed over the clients such that no two clients will see the same message.\n\n#### Queue Types\n\nQueues can be of type `mem` or `disk`.  The contents of memory queues will not survive restarts.\n\nVarious parameters can be set on a queue using the syntax `type:param1,param2`.\n\n#### Sharing\n\nQueues with the `public` parameter can have multiple clients connected to them (in which case they compete for messages).  If you want a private queue you can specify the `private` parameter.\n\n#### Policy\n\nQueues can either have a `block` or `drop` policy.  The drop policy means that messages that would be routed to a queue that is full will be dropped and never delivered.  The block policy will cause the publisher to wait until there is room in the queue.  The block policy makes no sense on a disk queue.\n\n#### Backlog\n\nThe `backlog=<number>` parameter will specify how many messages may be held in the queue before the block or drop policies are applied.\n\n#### Permanence\n\nIf you want a queue to be remembered by fqd, you can specify `permanent` as a flag.  If you'd like for fqd to forget the queue after all clients have disconnected, you can specify the `transient` flag.  If neither flag is specified, then an existing queue will retain its previous permanence setting or a new transient queue will be created.\n\n#### Examples:\n\nA queue called `bob` will be in memory, allowed to have multiple clients connected to it, with a drop policy and an allowable message backlog of 100000 messages: `bob/mem:public,drop,backlog=100000`\n\nA connection client will specify username/queue.  A user \"USER\" connecting to the aforementioned queue would connect as `USER/bob/mem:public,drop,backlog=100000`\n\n### Messages\n\nMessages are, of course, a payload and metadata.\n\n#### Message metadata\n\nSome are set by the broker.\n \n * sender [set by the broker]\n * hops (a list of fqd via which the message passed)\n\nOthers are set by the sender. \n\n * exchange (up to 127 bytes)\n * route (up to 127 bytes)\n * id (128 bits). The first 64 bits the sender shall control, the latter 64bits the broker *might* control.\n\n### Routes and Programs\n\nRoutes and programs define how messages sent on exchanges are placed in queues:\n\n- A receiver that connects to an fq-broker specifies a program that filters the messages on the exchange.\n- A sender specifies a route for every message as part of the metadata\n\nPrograms follow the following syntax (cf. `fqd.h`):\n\n```\nPROGRAM: <prefix|exact>:string RULES*\nRULE: (RULE)\nRULE: (RULE && RULE)\nRULE: (RULE || RULE)\nRULE: EXPR\nEXPR: function(args)\nargs: arg\nargs: arg, args\narg: \"string\"\narg: true|false\narg: [0-9][0-9]*(?:.[0-9]*)\n\nfunctions are dynamically loadable with type signature\nstrings: s, booleans: b, integers: d\nfunction: substr_eq(9.3,10,\"tailorings\",true)\nC symbol: fqd_route_prog__substr_eq__ddsb(int nargs, valnode_t *args);\n ```\n\nIn particular:\n\n- Every program starts with either `prefix:` or `exact:`\n- The program `prefix:` matches all rules\n- The program string is matched against the message route\n\nThe following rule functions are defined in `fq_prog.c`:\n\n- `fqd_route_prog__sample__d()` -- subsample the stream\n- `fqd_route_prog__route_contains__s()` -- check if route contains a string\n- `fqd_route_prog__payload_prefix__s()` -- check if payload starts with prefix\n- `fqd_route_prog__payload_contains__s()` -- check if payload contains a string\n- `fqd_route_prog__true__()` -- always true\n\nExamples:\n\n- `prefix:` -- matches all messages\n- `prefix:bla` or `prefix:\"bla\"` -- matches all messages with rules starting with the sting 'bla'\n- `prefix: payload_prefix(\"M\")` -- matches messages where the payload starts with 'M'\n- `prefix:foo (payload_prefix(\"M\") && route_contains(\"bar\"))` -- matches messages where the payload starts with 'M' and route starts with \"foo\" and moreover contains \"bar\"\n\n## Protocol\n\nInformation on command and message protocol is found in `docs/fq_protocol.md`\n\n### HTTP superposition\n\nThe Fq protocol also acts as a non-compliant HTTP server (though compliant enough of most clients and browsers).  Fq ships with a web UI that allows inspecting real-time state and performance.\n\n#### GET /stats.json\n\nexposes current exchange, queue, and client information.\n\n#### POST /submit\n\nAn endpoint allowing message submission without a full and stateful Fq connection.  It expects the following headers:\n\n * ```X-Fq-User```,\n * ```X-Fq-Route```, and\n * ```X-Fq-Exchange```.\n \n The HTTP client *MUST* provide a Content-Length header corresponding to the payload content (no chunked submission).  The payload is treated as the raw message box without any special encoding.\n\nExample:\n\n```\ncurl -X POST -H \"X-Fq-User: user\" -H 'X-Fq-Route: bla' -H 'X-Fq-Exchange: test' localhost:8765/submit --data \"TEST\"\n```\n\n## Building\n\nRequirements:\n* C compiler\n* GNU make\n* libuuid\n* sqlite3\n* [jlog](https://github.com/omniti-labs/jlog)\n* [libbcd](https://github.com/backtrace-labs/bcd) (optional, for crash tracing)\n\nGenerally:\n```\nmake\nmake install\n```\n\nTo build without libbcd support:\n```\nNO_BCD=1 make\n```\n\n## Debugging\n\nFQ can be run in debug mode from the command line.\n\nTo run FQ in debug mode, kill any and all existing FQ processes, then enter the\nfollowing command:\n```\nfq -g fq FQ_DEBUG=<flag values> <path to fqd>/fqd -D -c <path to fqd.sqlite>/fqd.sqlite -p <port number>\n```\n\nFlag values determine debug output type and can have the following values:\n```\nFQ_DEBUG_MEM =     0x00000001,\nFQ_DEBUG_MSG =     0x00000002,\nFQ_DEBUG_ROUTE =   0x00000004,\nFQ_DEBUG_IO =      0x00000008,\nFQ_DEBUG_CONN =    0x00000010,\nFQ_DEBUG_CONFIG =  0x00000020,\nFQ_DEBUG        =  0x00000040,\nFQ_DEBUG_PEER =    0x00000080,\nFQ_DEBUG_HTTP =    0x00000100,\nFQ_DEBUG_PANIC =   0x40000000\n```\n\nTo debug more than one flag, simply OR the flag values. For example, to output\nconnection, configuration, and route information, set `FQ_DEBUG` equal to\n`0x00000034 (FQ_DEBUG_CONFIG|FQ_DEBUG_CONN|FQ_DEBUG_ROUTE)`.\n\nFor example, you can run FQ in debug mode with the variables shown below to\noutput configuration, connection, and route information to the console:\n```\nfq -g fq FQ_DEBUG=0x00000034  /opt/circonus/sbin/fqd -D -c /opt/circonus/var/lib/fq/fqd.sqlite -p 8765\n```\n"
  },
  {
    "path": "coverity_model.c",
    "content": "typedef unsigned int uint32_t;\ntypedef _Bool bool;\n\nvoid\nck_pr_dec_uint_zero(uint32_t *v, bool *zero) {\n  *v = (*v) - 1;\n  if(zero) *zero = ((*v) == 0);\n}\n\nvoid\nck_pr_inc_uint(uint32_t *v) {\n  *v = (*v) + 1;\n}\n\nuint32_t\nck_pr_load_uint(uint32_t *v) {\n  return *v;\n}\n\n"
  },
  {
    "path": "docs/fq_protocol.md",
    "content": "# fq Protocol\n\n## Client\n\nClients maintain two paired tcp connections to fq. After the connections are made, some preliminary data is sent over the command socket. A \"plain auth\" command is then issued, return a client key. The client key is then used in some preliminary data sent over the data socket, pairing the two sockets for the session.\n\n### Prefixes\n\n* Cmd Mode: `0xcc50cafe`\n* Data Mode: `0xcc50face`\n* Peer Mode: `0xcc50fade`\n\n### Command Socket\n\n    Length   | Description\n    ---------+-----------------------------\n    4 bytes  | Cmd Mode\n\n### Data Socket\n\n    Length   | Description\n    ---------+-----------------------------\n    4 bytes  | Data Mode\n    2 bytes  | Client Key Length\n    variable | Client Key\n\n## Commands\n\nGeneral form `(command prefix)(command)`, big endian. All non-heartbeat related commands have in-band responses corresponding to the order in which requests they were sent to fq. Heartbeat requests are used simply to tell fq to look for and to send heartbeats at a specific interval. Heartbeats should be checked for during normal command processing and not as a response to a specific request.\n\n### Prefixes\n\nCommad prefixes are two bytes at the beginning of the command\n\n* Error: `0xeeee`\n* Heartbeat: `0xbea7`\n* Auth CMD: `0xaaaa`\n* Auth Plain: `0x0000`\n* Auth Response: `0xaa00`\n* Heartbeat Request: `0x4848`\n* Bind: `0xb171`\n* Bind Request: `0xb170`\n* Unbind: `0x171b`\n* Unbind Request: `0x071b`\n* Status: `0x57a7`\n* Status Request: `0xc7a7`\n\n#### Plain Auth \n\nPlain Auth is a subset of Auth and will have both prefixs\n\n##### Request\n\n    Length   | Description\n    ---------+-----------------------------\n    2 bytes  | Auth CMD Prefix\n    2 bytes  | Auth Plain Prefix\n    2 bytes  | User Length\n    variable | User\n    2 bytes  | Queue Length (Queue + 1 byte + Queue type)\n    variable | Queue\n    1 byte   | 0\n    variable | Queue type (\"mem\" or \"disk\") + \":param,param\"\n    2 bytes  | Password Length (16 bit)\n    variable | Password\n\n\n##### Response\n\n    Length   | Description\n    ---------+-----------------------------\n    2 bytes  | Auth Response Prefix\n    2 bytes  | Client Key Length Length\n    variable | Client Key (0 < length < 127 bytes)\n\n#### Bind\n\n##### Request\n    Length   | Description\n    ---------+-----------------------------\n    2 bytes  | Bind Request Prefix\n    2 bytes  | Flags: Peer Mode (0 or 1) | perm(0110)/trans(0100)\n    2 bytes  | Exchange Length\n    2 bytes  | Exchange\n    2 bytes  | Program Length\n    variable | Program\n\n##### Response\n\n    Length   | Description\n    ---------+-----------------------------\n    2 bytes  | Bind Prefix\n    4 bytes  | Binding ID\n\n#### Unbind\n\n##### Request\n\n    Length   | Description\n    ---------+-----------------------------\n    2 bytes  | Unbind Request Prefix\n    4 bytes  | Binding ID\n    2 bytes  | Exchange Length\n    variable | Exchange\n\n##### Response\n\n    Length   | Description\n    ---------+-----------------------------\n    2 bytes  | Unbind Prefix\n    4 bytes  | Binding ID\n\nOn success, the response binding id will be the same as the one sent in the request.\n\n#### Status\n\n##### Request\n\n    Length   | Description\n    ---------+-----------------------------\n    2 bytes  | Status Request Prefix\n\n##### Response\n\n    Length   | Description\n    ---------+-----------------------------\n    2 bytes  | Status Prefix\n    2 bytes  | Key Length\n    variable | Key\n    4 bytes  | Value\n    … (repeat key length, key, value sets)\n    2 bytes  | Key Length 0\n\nThe response contains serveral key-value pairs. Each key is prefixed by a length and parsing of kv pairs should continue until a key length of 0 is read.\n\n#### Heartbeat Request\n\n##### Request\n\n    Length   | Description\n    ---------+-----------------------------\n    2 bytes  | Heartbeat Request Prefix\n    2 bytes  | Heartbeat Interval (milliseconds)\n\n##### Response\n\nNone\n\n#### Heartbeat\n\n##### Request\n\n    Length   | Description\n    ---------+-----------------------------\n    2 bytes  | Heartbeat Prefix\n    \n##### Response\n\n    Length   | Description\n    ---------+-----------------------------\n    2 bytes  | Heartbeat Prefix\n\n## Messages\n\n    Length   | Description                       | Note\n    ---------+-----------------------------------+------------------------\n    1 byte   | Exchange Length                   |\n    variable | Exchange                          |\n    1 byte   | Route Length                      |\n    16 bytes | Message ID                        |\n    1 byte   | Sender Length                     | Send for Peer Mode Only\n    variable | Sender                            | Send for Peer Mode Only\n    1 byte   | Number of Hops                    | Send for Peer Mode Only\n    variable | Hops (numHops sets of 4-byte IPs) | Send for Peer Mode Only\n    4 bytes  | Payload Length                    |\n    variable | Payload (<= 128kb)                |\n\nAll properties will be present when receiving a message, while some propertiers are only sent when in peer mode.\n"
  },
  {
    "path": "dtest.d",
    "content": "/*\n * Copyright (c) 2013 OmniTI Computer Consulting, Inc.\n * All rights reserved.\n * \n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n * \n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\nfq*:::message-receive{\n  printf(\"sender: %s\\n\", args[2]->sender);\n  printf(\"exchange: %s\\n\", args[2]->exchange);\n  printf(\"route: %s\\n\", args[2]->route);\n  printf(\"message len: %d\\n\", args[2]->payload_len);\n  printf(\"message: %.*s\\n\", args[2]->payload_len, args[2]->payload);\n\n  printf(\"client: %s\\n\", args[0]->pretty); \n  printf(\"client: %s\\n\", args[1]->pretty); \n\n  printf(\"latency: %d\\n\", args[2]->latency);\n}\n\nfq*:::queue-drop{\n  q = ((fq_queue_t *)arg0);\n  printf(\"dropped message on queue %s\\n\", q->name);\n}\n\nfq*:::queue-block{\n  printf(\"blocking queue %s\\n\", ((fq_queue_t *)arg0)->name);\n}\n"
  },
  {
    "path": "fq.d",
    "content": "/*\n * Copyright (c) 2013 OmniTI Computer Consulting, Inc.\n * All rights reserved.\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\ntypedef struct {\n  uintptr_t  route;\n  uintptr_t  sender;\n  uintptr_t  exchange;\n  uintptr_t  payload;\n  uint32_t   payload_len;\n  uint64_t   latency;\n} fq_dtrace_msg_t;\n\ntypedef struct {\n  string    route;\n  string    exchange;\n  string    sender;\n  string    payload;\n  uint32_t  payload_len;\n  uint64_t  latency;\n} fq_msg_t;\n\ntranslator fq_msg_t <fq_dtrace_msg_t *m> {\n  route = copyinstr(*(uintptr_t *)copyin((uintptr_t)&m->route, sizeof(uintptr_t)));\n  exchange = copyinstr(*(uintptr_t *)copyin((uintptr_t)&m->exchange, sizeof(uintptr_t)));\n  sender = copyinstr(*(uintptr_t *)copyin((uintptr_t)&m->sender, sizeof(uintptr_t)));\n  payload_len = *(uint32_t *)copyin((uintptr_t)&m->payload_len, sizeof(uint32_t));\n  payload = copyinstr(*(uintptr_t *)copyin((uintptr_t)&m->payload, sizeof(uintptr_t)), *(uint32_t *)copyin((uintptr_t)&m->payload_len, sizeof(uint32_t)));\n  latency = *(uint64_t *)copyin((uintptr_t)&m->latency, sizeof(uint64_t));\n};\n\ntypedef struct {\n  uintptr_t  name;\n  int32_t    isprivate;\n  int32_t    policy;\n  uintptr_t  type;\n} fq_dtrace_queue_t;\n\ntypedef struct {\n  string     name;\n  int32_t    isprivate;\n  int32_t    policy;\n  string     type;\n} fq_queue_t;\n\ntranslator fq_queue_t <fq_dtrace_queue_t *m> {\n  name = copyinstr(*(uintptr_t *)copyin((uintptr_t)&m->name, sizeof(uintptr_t)));\n  type = copyinstr(*(uintptr_t *)copyin((uintptr_t)&m->type, sizeof(uintptr_t)));\n  isprivate = *(uint32_t *)copyin((uintptr_t)&m->isprivate, sizeof(int32_t));\n  policy = *(uint32_t *)copyin((uintptr_t)&m->policy, sizeof(int32_t));\n};\n\ntypedef struct {\n  int32_t   fd;\n  uintptr_t pretty;\n} fq_dtrace_remote_anon_client_t;\n\ntypedef struct {\n  int32_t  fd;\n  string pretty;\n} fq_remote_anon_client_t;\n\ntypedef struct {\n  int32_t   fd;\n  uintptr_t pretty;\n} fq_dtrace_remote_client_t;\n\ntypedef struct {\n  int32_t  fd;\n  string pretty;\n} fq_remote_client_t;\n\ntypedef struct {\n  int32_t   fd;\n  uintptr_t pretty;\n} fq_dtrace_remote_data_client_t;\n\ntypedef struct {\n  int32_t  fd;\n  string pretty;\n} fq_remote_data_client_t;\n\ntranslator fq_remote_anon_client_t <fq_dtrace_remote_anon_client_t *c> {\n  fd = *(uint32_t *)copyin((uintptr_t)&c->fd, sizeof(int32_t));\n  pretty = copyinstr(*(uintptr_t *)copyin((uintptr_t)&c->pretty, sizeof(uintptr_t)));\n};\n\n"
  },
  {
    "path": "fq.h",
    "content": "/*\n * Copyright (c) 2013 OmniTI Computer Consulting, Inc.\n * All rights reserved.\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\n#ifndef FQ_H\n#define FQ_H\n\n#ifndef _REENTRANT\n#error \"You must compile with -D_REENTRANT\"\n#endif\n\n#include <string.h>\n#include <stdio.h>\n#include <sys/types.h>\n#include <sys/time.h>\n#include <stdint.h>\n#include <stdbool.h>\n#include <stdlib.h>\n#include <ck_fifo.h>\n#include <ck_stack.h>\n\n#define FQ_PROTO_CMD_MODE  0xcc50cafe\n#define FQ_PROTO_DATA_MODE 0xcc50face\n#define FQ_PROTO_PEER_MODE 0xcc50feed\n#define FQ_PROTO_OLD_PEER_MODE 0xcc50fade\n#define FQ_PROTO_READ_STAT 0x47455420 /* \"GET \" */\n#define FQ_PROTO_HTTP_GET  0x47455420 /* \"GET \" */\n#define FQ_PROTO_HTTP_PUT  0x50555420 /* \"PUT \" */\n#define FQ_PROTO_HTTP_POST 0x504f5354 /* \"POST\" */\n#define FQ_PROTO_HTTP_HEAD 0x48454144 /* \"HEAD\" */\n\n#define FQ_BIND_PEER       0x00000001\n#define FQ_BIND_PERM       0x00000110\n#define FQ_BIND_TRANS      0x00000100\n\n#define FQ_PROTO_ERROR     0xeeee\n#define FQ_PROTO_AUTH_CMD  0xaaaa\n#define FQ_PROTO_AUTH_PLAIN 0\n#define FQ_PROTO_AUTH_RESP 0xaa00\n#define FQ_PROTO_HBREQ     0x4848\n#define FQ_PROTO_HB        0xbea7\n#define FQ_PROTO_BINDREQ   0xb170\n#define FQ_PROTO_BIND      0xb171\n#define FQ_PROTO_UNBINDREQ 0x071b\n#define FQ_PROTO_UNBIND    0x171b\n#define FQ_PROTO_STATUS    0x57a7\n#define FQ_PROTO_STATUSREQ 0xc7a7\n\n#define FQ_DEFAULT_QUEUE_TYPE \"mem\"\n\n#ifndef min\n#define min(a,b) ((a) < (b) ? (a) : (b))\n#endif\n\n#define MAX_RK_LEN 127\n\n/* !lua start */\n\ntypedef struct fq_rk {\n  unsigned char  name[MAX_RK_LEN];\n  uint8_t        len;\n} fq_rk;\n\nstatic inline void\nfq_rk_from_str(fq_rk *rk, const char *str) {\n  size_t len = strlen(str);\n  memset(rk->name, 0, MAX_RK_LEN);\n  rk->len = min(len, MAX_RK_LEN - 1);\n  memcpy(rk->name, str, rk->len);\n}\n\nstatic inline int\nfq_rk_cmp(const fq_rk * const a, const fq_rk * const b) {\n  if(a->len < b->len) return -1;\n  if(a->len > b->len) return 1;\n  return memcmp(a->name, b->name, a->len);\n}\n\n#define FQ_BIND_ILLEGAL (uint32_t)0xffffffff\n\ntypedef struct {\n  fq_rk exchange;\n  uint32_t flags;\n  char *program;\n\n  uint32_t out__route_id;\n} fq_bind_req;\n\ntypedef struct {\n  fq_rk exchange;\n  uint32_t route_id;\n\n  uint32_t out__success;\n} fq_unbind_req;\n\ntypedef struct fq_msgid {\n  union {\n    struct {\n      uint32_t p1; /* user(sender) */\n      uint32_t p2; /* user(sender) */\n      uint32_t p3; /* reserved */\n      uint32_t p4; /* reserved */\n    } u32;\n    unsigned char d[16];\n  } id;\n} fq_msgid;\n\ntypedef struct msg_free_stacks_handle_t msg_free_stacks_handle_t;\n\n#define MAX_HOPS 32\ntypedef struct fq_msg {\n  uint32_t       hops[MAX_HOPS];\n  fq_rk          route;\n  fq_rk          sender;\n  fq_rk          exchange;\n  fq_msgid       sender_msgid;\n  uint32_t       refcnt;\n  uint32_t       payload_len;\n  uint64_t       arrival_time;\n\n  ck_stack_entry_t cleanup_stack_entry;\n  msg_free_stacks_handle_t *cleanup_handle;\n\n  /* define a free function as an alternative to `free()` */\n  void           (*free_fn)(struct fq_msg *m);\n  unsigned char  payload[];  /* over allocated */\n} fq_msg;\n\nextern void fq_clear_message_cleanup_stack(void);\n\nextern fq_msg *fq_msg_alloc(const void *payload,\n                            size_t payload_size);\nextern fq_msg *fq_msg_alloc_BLANK(size_t payload_size);\nextern void    fq_msg_ref(fq_msg *);\nextern void    fq_msg_deref(fq_msg *);\n#define fq_msg_free(a) fq_msg_deref(a)\nextern void    fq_msg_exchange(fq_msg *, const void *key, int klen);\nextern void    fq_msg_route(fq_msg *, const void *key, int klen);\nextern void    fq_msg_id(fq_msg *, fq_msgid *id);\nextern int     fq_find_in_hops(uint32_t, fq_msg *);\n\ntypedef struct buffered_msg_reader buffered_msg_reader;\n\nextern buffered_msg_reader *\n  fq_buffered_msg_reader_alloc(int fd, uint32_t peermode);\nextern void fq_buffered_msg_reader_free(buffered_msg_reader *f);\nextern int\n  fq_buffered_msg_read(buffered_msg_reader *f,\n                       void (*f_msg_handler)(void *, fq_msg *),\n                       void *);\n\n/* frame */\n/*\n *    1 x uint8_t<net>   hops\n * hops x uint32_t<net>  node\n *    1 x <nstring>      exchange\n *    1 x fq_rk<nstring> sender\n *    1 x fq_rk<nstring> route\n *    1 x uint32_t<net>  payload_len\n *    1 x data\n */\n\n\ntypedef struct fq_conn_s *fq_client;\n\n#define FQ_HOOKS_V1 1\n#define FQ_HOOKS_V2 2\n#define FQ_HOOKS_V3 3\n#define FQ_HOOKS_V4 4\ntypedef struct fq_hooks {\n  int version;\n  /* V1 */\n  void (*auth)(fq_client, int);\n  void (*bind)(fq_client, fq_bind_req *);\n  /* V2 */\n  void (*unbind)(fq_client, fq_unbind_req *);\n  /* V3 */\n  int sync;\n  /* V4 */\n  bool (*message)(fq_client, fq_msg *m);\n  void (*cleanup)(fq_client);\n  void (*disconnect)(fq_client);\n} fq_hooks;\n\nextern int\n  fq_client_hooks(fq_client conn, fq_hooks *hooks);\n\nextern void\n  fq_client_set_userdata(fq_client, void *);\n\nextern void *\n  fq_client_get_userdata(fq_client);\n\nextern int\n  fq_client_init(fq_client *, uint32_t peermode,\n                 void (*)(fq_client, const char *));\n\nextern int\n  fq_client_creds(fq_client,\n                  const char *host, unsigned short port,\n                  const char *source, const char *pass);\n\nextern void\n  fq_client_status(fq_client conn,\n                   void (*f)(char *, uint32_t, void *), void *c);\n\nextern void\n  fq_client_heartbeat(fq_client conn, unsigned short ms);\n\nextern void\n  fq_client_bind(fq_client conn, fq_bind_req *req);\n\nextern void\n  fq_client_unbind(fq_client conn, fq_unbind_req *req);\n\nextern void\n  fq_client_set_backlog(fq_client conn, uint32_t len, uint32_t stall);\n\nextern void\n  fq_client_set_nonblock(fq_client conn, bool nonblock);\n\nextern int\n  fq_client_connect(fq_client conn);\n\nextern int\n  fq_client_publish(fq_client, fq_msg *msg);\n\nextern fq_msg *\n  fq_client_receive(fq_client conn);\n\nextern void\n  fq_client_destroy(fq_client conn);\n\nextern int\n  fq_client_data_backlog(fq_client conn);\n\nextern int\n  fq_rk_to_hex(char *buf, int len, fq_rk *k);\n\nextern int\n  fq_read_status(int fd, void (*f)(char *, uint32_t, void *), void *);\n\nextern int\n  fq_read_uint16(int fd, unsigned short *v);\n\nextern int\n  fq_write_uint16(int fd, unsigned short hs);\n\nextern int\n  fq_read_uint32(int fd, uint32_t *v);\n\nextern int\n  fq_write_uint32(int fd, uint32_t hs);\n\nextern int\n  fq_read_short_cmd(int fd, unsigned short buflen, void *buf);\n\nextern int\n  fq_write_short_cmd(int fd, unsigned short buflen, const void *buf);\n\nextern int\n  fq_read_long_cmd(int fd, int *len, void **buf);\n\n/* This function returns 0 on success, -1 on failure or a positive\n * integer indicating that a partial write as happened.\n * The initial call should be made with off = 0, if a positive\n * value is returned, a subsequent call should be made with\n * off = (off + return value).\n * The caller must be able to keep track of an accumulated offset\n * in the event that several invocations are required to send the\n * message.\n */\nextern int\n  fq_client_write_msg(int fd, uint32_t peermode, fq_msg *m,\n                      size_t off, size_t *written);\n\ntypedef enum {\n  FQ_POLICY_DROP = 0,\n  FQ_POLICY_BLOCK = 1,\n} queue_policy_t;\n\ntypedef enum {\n  FQ_DEBUG_MEM =     0x00000001,\n  FQ_DEBUG_MSG =     0x00000002,\n  FQ_DEBUG_ROUTE =   0x00000004,\n  FQ_DEBUG_IO =      0x00000008,\n  FQ_DEBUG_CONN =    0x00000010,\n  FQ_DEBUG_CONFIG =  0x00000020,\n  FQ_DEBUG        =  0x00000040,\n  FQ_DEBUG_PEER =    0x00000080,\n  FQ_DEBUG_HTTP =    0x00000100,\n  FQ_DEBUG_PANIC =   0x40000000\n} fq_debug_bits_t;\n\nvoid fq_keepalive_fd(int fd, int cnt, int idle, int invtl);\n\nextern uint32_t fq_debug_bits;\n\nvoid fq_debug_set_bits(uint32_t bits);\n/* string can be integer, hex or comma separated string (e.g. \"mem,io\") */\nvoid fq_debug_set_string(const char *s);\n\nextern int\n  fq_debug_fl(const char *file, int line, fq_debug_bits_t, const char *fmt, ...)\n  __attribute__((format(printf, 4, 5)));\n\n#define fq_debug(type, ...) do { \\\n  if(0 != (type & fq_debug_bits)) { \\\n    fq_debug_fl(__FILE__, __LINE__, type, __VA_ARGS__); \\\n  } \\\n} while(0)\n\n#define fq_stacktrace(b,t,s,e) do { \\\n  if(0 != (b & fq_debug_bits)) { \\\n    fq_debug_stacktrace(b,t,s,e); \\\n  } \\\n} while(0)\n\n/* programming:\n *\n *  PROGRAM: <prefix|exact>:string RULES*\n *  RULE: (RULE)\n *  RULE: (RULE && RULE)\n *  RULE: (RULE || RULE)\n *  RULE: EXPR\n *  EXPR: function(args)\n *  args: arg\n *  args: arg, args\n *  arg: \"string\"\n *  arg: true|false\n *  arg: [0-9][0-9]*(?:.[0-9]*)\n *\n *  functions are dynamically loadable with type signature\n *  strings: s, booleans: b, integers: d\n *  function: substr_eq(9.3,10,\"tailorings\",true)\n *  C symbol: fqd_route_prog__substr_eq__ddsb(int nargs, valnode_t *args);\n *  fallback symbol: fqd_route_prog_substr_eq__VA(int nargs, valnode_t *args);\n */\n\ntypedef struct valnode {\n  enum {\n    RP_VALUE_STRING = 1,\n    RP_VALUE_BOOLEAN = 2,\n    RP_VALUE_DOUBLE = 3\n  } value_type;\n  union {\n    char  *s;\n    bool   b;\n    double d;\n  } value;\n} valnode_t;\n\nextern void fq_debug_stacktrace(fq_debug_bits_t b, const char *tag, int start, int end);\n\n#if defined(__MACH__)\ntypedef uint64_t hrtime_t;\n#elif defined(BSD) || defined(__FreeBSD__)\ntypedef uint64_t hrtime_t;\n#elif defined(linux) || defined(__linux) || defined(__linux__)\ntypedef uint64_t hrtime_t;\n#endif\nextern hrtime_t fq_gethrtime(void);\n\n/* !lua stop */\n\n/* DTrace helpers */\ntypedef struct {\n  char *route;\n  char *sender;\n  char *exchange;\n  char *payload;\n  uint32_t payload_len;\n  uint64_t latency;\n} fq_dtrace_msg_t;\n\n#define DTRACE_PACK_MSG(dmsg, msg) do { \\\n    (dmsg)->route = (char *)(msg)->route.name; \\\n    (dmsg)->sender = (char *)(msg)->sender.name; \\\n    (dmsg)->exchange = (char *)(msg)->exchange.name; \\\n    (dmsg)->payload_len = (uint32_t)(msg)->payload_len; \\\n    (dmsg)->payload = (char *)(msg)->payload; \\\n    (dmsg)->latency = fq_gethrtime() - (msg)->arrival_time; \\\n} while(0)\n\n#define fq_assert(A) do { \\\n    if(!(A)) { \\\n        fq_debug_stacktrace(FQ_DEBUG_PANIC, \"assert\", 1, 1000); \\\n        (void)fprintf (stderr, \"%s:%s:%u: failed assertion `%s'\\n\", __func__, __FILE__, __LINE__, #A); \\\n        abort(); \\\n    } \\\n} while(0)\n\n#endif\n"
  },
  {
    "path": "fq_bench.c",
    "content": "/*\n * Copyright (c) 2016 Circonus\n * All rights reserved.\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\n#include <stdlib.h>\n#include <stdio.h>\n#include <unistd.h>\n#include <signal.h>\n#include <string.h>\n#include \"fq.h\"\n\nvoid logger(fq_client c, const char *s) {\n  (void)c;\n  fprintf(stderr, \"fq_logger: %s\\n\", s);\n}\n\nstatic void\ndebug_status(char *key, uint32_t value, void *unused) {\n  (void)unused;\n  fq_debug(FQ_DEBUG_CONN, \" ---> %s : %u\\n\", key, value);\n}\n\nstatic void\nprint_rate(fq_client c, hrtime_t s, hrtime_t f, uint64_t cnt, uint64_t icnt) {\n  double d;\n  fq_client_status(c, debug_status, NULL);\n  if(cnt) {\n    d = (double)cnt * 1000000000;\n    d /= (double)(f-s);\n    printf(\"[%d backlog] output %0.2f msg/sec\\n\",\n           fq_client_data_backlog(c), d);\n  }\n  if(icnt) {\n    d = (double)icnt * 1000000000;\n    d /= (double)(f-s);\n    printf(\"[%d backlog]  input %0.2f msg/sec\\n\",\n           fq_client_data_backlog(c), d);\n  }\n}\n\nstatic void usage(const char *prog) {\n  printf(\"%s:\\n\", prog);\n  printf(\"\\t-H\\t\\tthis help message\\n\");\n  printf(\"\\t-h\\t\\tthe fq host to connect to\\n\");\n  printf(\"\\t-p <port>\\tspecify connecting port (default: 8765)\\n\");\n  printf(\"\\t-u <user>\\tuser name\\n\");\n  printf(\"\\t-P <password>\\tpassword\\n\");\n  printf(\"\\t-t <type>\\t'disk' or 'mem' for type of queue to test\\n\");\n  printf(\"\\t-q <queue>\\tname of queue to test (default: benchmark_queue)\\n\");\n  printf(\"\\t-c <count>\\tnumber of messages to bench with\\n\");\n  printf(\"\\t-s <size>\\tsize of each message\\n\");\n}\n\nstatic char *host;\nstatic int port = 8765;\nstatic char *user;\nstatic char *exchange;\nstatic char *pass;\nstatic char *type;\nstatic char *queue_name;\nstatic int count = 100000;\nstatic int size = 100;\n\nstatic void parse_cli(int argc, char **argv) {\n  int c;\n  const char *debug = getenv(\"FQ_DEBUG\");\n  while((c = getopt(argc, argv, \"Hh:p:u:P:t:q:e:c:s:\")) != EOF) {\n    switch(c) {\n      case 'H':\n        usage(argv[0]);\n        exit(0);\n      case 'h':\n        free(host);\n        host = strdup(optarg);\n        break;\n      case 'p':\n        port = atoi(optarg);\n        break;\n      case 'u':\n        free(user);\n        user = strdup(optarg);\n        break;\n      case 'P':\n        free(pass);\n        pass = strdup(optarg);\n        break;\n      case 't':\n        free(type);\n        type = strdup(optarg);\n        break;\n      case 'q':\n        free(queue_name);\n        queue_name = strdup(optarg);\n        break;\n      case 'e':\n        free(exchange);\n        exchange = strdup(optarg);\n        break;\n      case 'c':\n        count = atoi(optarg);\n        break;\n      case 's':\n        size = atoi(optarg);\n        break;\n      default:\n        usage(argv[0]);\n        exit(-1);\n    }\n  }\n  if(debug) fq_debug_set_string(debug);\n  if(!host) host = strdup(\"localhost\");\n  if(!user) user = strdup(\"user\");\n  if(!pass) pass = strdup(\"pass\");\n  if(!queue_name) queue_name = strdup(\"benchmark_queue\");\n  if(!exchange) exchange = strdup(\"maryland\");\n  if(!type) type = strdup(\"mem\");\n}\n\n\n/**\n * Benchmark a single thread against a type of queue at some host.\n */\nint main(int argc, char **argv) {\n  char queue[256] = {0};\n  hrtime_t s0, s, f, f0;\n  uint64_t cnt = 0, icnt = 0;\n  int i = 0;\n  fq_client c;\n  fq_msg *m;\n  char *fq_debug = getenv(\"FQ_DEBUG\");\n  if(fq_debug) fq_debug_set_bits(atoi(fq_debug));\n  signal(SIGPIPE, SIG_IGN);\n  fq_client_init(&c, 0, logger);\n\n  parse_cli(argc, argv);\n\n  sprintf(queue, \"%s/%s/%s:public,drop,backlog=10000,permanent\", user, queue_name, type);\n\n  printf(\"using queue -> %s\\n\", queue); \n\n  fq_client_creds(c, host, port, queue, pass);\n  fq_client_heartbeat(c, 1000);\n  fq_client_set_backlog(c, 10000, 100);\n  fq_client_connect(c);\n\n  printf(\"payload size -> %d\\n\", size);\n  printf(\"message count -> %d\\n\", count);\n\n  s0 = s = fq_gethrtime();\n  f = s;\n\n  m = fq_msg_alloc_BLANK(size);\n  memset(m->payload, 'X', size);\n  fq_msg_exchange(m, exchange, strlen(exchange));\n  fq_msg_route(m, \"test.bench.foo\", 14);\n\n  while(i < count || fq_client_data_backlog(c) > 0) {\n    if(i < count) {\n      m->arrival_time = fq_gethrtime();\n      fq_msg_id(m, NULL);\n      fq_client_publish(c, m);\n      cnt++;\n      i++;\n    }\n    else usleep(10);\n\n    if (i % 1000 == 0) {\n      f = fq_gethrtime();\n    }\n\n    if(f-s > 1000000000) {\n      print_rate(c, s, f, cnt, icnt);\n      icnt = 0;\n      cnt = 0;\n      s = f;\n    }\n  }\n  f0 = fq_gethrtime();\n  print_rate(c, s0, f0, i, 0);\n\n  free(host);\n  free(user);\n  free(pass);\n  free(exchange);\n  free(queue_name);\n  free(type);\n  return 0;\n}\n"
  },
  {
    "path": "fq_client.c",
    "content": "/*\n * Copyright (c) 2013 OmniTI Computer Consulting, Inc.\n * All rights reserved.\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <stdarg.h>\n#include <ctype.h>\n#include <string.h>\n#include <unistd.h>\n#include <sys/uio.h>\n#include <fcntl.h>\n#include <errno.h>\n#include <pthread.h>\n#include <sys/socket.h>\n#include <arpa/inet.h>\n#include <netinet/in.h>\n#include <netinet/tcp.h>\n#include <netdb.h>\n#include <poll.h>\n#include <ck_fifo.h>\n#include <uuid/uuid.h>\n\n#include \"fq.h\"\n#include \"fqd.h\"\n#include \"fqd_private.h\"\n\nstatic const long MAX_RESOLVE_CACHE = 1000000000; /* ns */\n\nstatic __thread char thread_local_name[16];\nvoid\nfq_thread_setname(const char *format, ...) {\n  va_list arg;\n  va_start(arg, format);\n  char thrname[16] = \"\\0\";\n  if(!format) format = \"terminated\";\n  vsnprintf(thrname, sizeof(thrname), format, arg);\n\n#if defined(linux) || defined(__linux) || defined(__linux__)\n  pthread_setname_np(pthread_self(), thrname);\n#endif\n  strlcpy(thread_local_name, thrname, sizeof(thread_local_name));\n  va_end(arg);\n}\n\n#define likely(x)      __builtin_expect(!!(x), 1)\n#define unlikely(x)    __builtin_expect(!!(x), 0)\n\n#define CONNERR_S(c) do { \\\n  if(c->errorlog) c->errorlog(c, c->error); \\\n} while(0)\n\n#define CONNERR(c, s) do { \\\n  strlcpy(c->error, s, sizeof(c->error)); \\\n  if(c->errorlog) c->errorlog(c, c->error); \\\n} while(0)\n\nstatic inline int\nfq_client_wfrw_internal(int fd, int needs_read, int needs_write,\n                        uint64_t ms, int *mask) {\n  int rv;\n  struct pollfd pfd;\n  pfd.fd = fd;\n  pfd.events = 0;\n  if(needs_read) pfd.events |= POLLIN | POLLERR | POLLHUP;\n  if(needs_write) pfd.events |= POLLOUT | POLLERR | POLLHUP;\n  while(-1 == (rv = poll(&pfd, 1, ms)) && errno == EINTR);\n  if(mask) *mask = pfd.revents;\n  return rv;\n}\n\nstruct fq_conn_s {\n  struct         sockaddr_in remote;\n  char          *host;\n  short          port;\n  hrtime_t       last_resolve;\n  char           error[128];\n  char          *user;\n  char          *pass;\n  char          *queue;\n  char          *queue_type;\n  fq_rk          key;\n  int            cmd_fd;\n  int            cmd_hb_needed;\n  unsigned short cmd_hb_ms;\n  hrtime_t       cmd_hb_last;\n  uint32_t       peermode;\n  int            data_fd;\n  pthread_t      worker;\n  pthread_t      data_worker;\n  int            stop;\n  ck_fifo_spsc_t cmdq;\n  ck_fifo_spsc_t q;\n  ck_fifo_spsc_t backq;\n  uint32_t       qlen;\n  uint32_t       qmaxlen;\n  uint32_t       q_stall_time;\n  bool           non_blocking;\n  int            connected;\n  int            data_ready;\n  fq_msg        *tosend;\n  int            tosend_offset;\n  int            sync_hooks; /* should they run in the calling thread */\n  void         (*auth_hook)(fq_client, int);\n  void         (*bind_hook)(fq_client, fq_bind_req *);\n  void         (*unbind_hook)(fq_client, fq_unbind_req *);\n  bool         (*message_hook)(fq_client, fq_msg *);\n  void         (*cleanup_hook)(fq_client);\n  void         (*disconnect_hook)(fq_client);\n\n  void         (*errorlog)(fq_client, const char *);\n  ck_fifo_spsc_entry_t *cmdqhead;\n  ck_fifo_spsc_entry_t *qhead;\n  ck_fifo_spsc_entry_t *backqhead;\n  uint32_t       thrcnt;\n  void          *userdata;\n};\ntypedef struct fq_conn_s fq_conn_s;\n\ntypedef struct {\n  unsigned short cmd;\n  union {\n    struct {\n      uint16_t       ms;\n    } heartbeat;\n    struct {\n      void (*callback)(char *, uint32_t, void *);\n      void *closure;\n    } status;\n    fq_bind_req *bind;\n    fq_unbind_req *unbind;\n    int return_value;\n  } data;\n} cmd_instr;\n\nstatic void mark_safe_free(void *pptr);\nstatic void\nfq_conn_free(fq_conn_s *conn_s) {\n  if(conn_s->user) free(conn_s->user);\n  if(conn_s->pass) free(conn_s->pass);\n  if(conn_s->queue) free(conn_s->queue);\n  if(conn_s->queue_type) free(conn_s->queue_type);\n  if(conn_s->tosend) fq_msg_free(conn_s->tosend);\n\n#define SWEEP_CONN_Q(TYPE, FREE, QNAME, ENTRYNAME) do { \\\n  TYPE tofree; \\\n  ck_fifo_spsc_dequeue_lock(&conn_s->QNAME); \\\n  while(ck_fifo_spsc_dequeue(&conn_s->QNAME, &tofree) == true) { \\\n    FREE(tofree); \\\n  } \\\n  ck_fifo_spsc_dequeue_unlock(&conn_s->QNAME); \\\n  ck_fifo_spsc_entry_t *garbage = NULL; \\\n  ck_fifo_spsc_deinit(&conn_s->QNAME, &garbage); \\\n  while (garbage != NULL) { \\\n    ck_fifo_spsc_entry_t *gn = garbage->next; \\\n    free(garbage); \\\n    garbage = gn; \\\n  } \\\n} while(0)\n  SWEEP_CONN_Q(fq_msg *, fq_msg_free, q, qhead);\n  SWEEP_CONN_Q(void *, mark_safe_free, backq, backqhead);\n  SWEEP_CONN_Q(cmd_instr *, free, cmdq, cmdqhead);\n#undef SWEEP_CONN_Q\n\n  if(conn_s->cleanup_hook) conn_s->cleanup_hook(conn_s);\n\n  free(conn_s);\n}\n\ntypedef enum {\n  AUTH_HOOK_TYPE,\n  CMD_HOOK_TYPE\n} hook_req_type_t;\n\ntypedef struct {\n  hook_req_type_t type;\n  cmd_instr *entry;\n} hook_req_t;\n\nstatic void\nfq_client_signal(fq_client conn, cmd_instr *e) \n{\n  fq_conn_s *conn_s = conn;\n  ck_fifo_spsc_enqueue_lock(&conn_s->cmdq);\n  ck_fifo_spsc_entry_t *fifo_entry = ck_fifo_spsc_recycle(&conn_s->cmdq);\n  if (fifo_entry == NULL) {\n    fifo_entry = malloc(sizeof(ck_fifo_spsc_entry_t));\n  }\n  ck_fifo_spsc_enqueue(&conn_s->cmdq, fifo_entry, e);\n  ck_fifo_spsc_enqueue_unlock(&conn_s->cmdq);\n}\n\nstatic int\nfq_resolve_endpoint(fq_conn_s *conn_s) {\n  conn_s->remote.sin_family = AF_INET;\n  conn_s->remote.sin_port = htons(conn_s->port);\n  if(inet_pton(AF_INET, conn_s->host, &conn_s->remote.sin_addr) != 1) {\n#ifdef HAVE_GETHOSTBYNAME_R\n    struct hostent hostbuf, *hp;\n    struct in_addr **addr_list;\n    int buflen = 1024, herr;\n    char *buf;\n    if((buf = malloc(buflen)) == NULL) {\n      CONNERR(conn_s, \"out of memory\");\n      return -1;\n    }\n    while((hp = gethostbyname_r(conn_s->host, &hostbuf, buf, buflen, &herr)) == NULL &&\n          errno == ERANGE) {\n      buflen *= 2;\n      if((buf = realloc(buf, buflen)) == NULL) {\n        CONNERR(conn_s, \"out of memory\");\n        free(buf);\n        return -1;\n      }\n    }\n    if(!hp) {\n      CONNERR(conn_s, \"host lookup failed\");\n      free(buf);\n      return -1;\n    }\n    addr_list = (struct in_addr **)hp->h_addr_list;\n    if(*addr_list == 0) {\n      CONNERR(conn_s, \"no address for host\");\n      free(buf);\n      return -1;\n    }\n    memcpy(&conn_s->remote.sin_addr, *addr_list, sizeof(struct in_addr));\n    free(buf);\n#elif defined _POSIX_C_SOURCE\n    struct addrinfo *result;\n\n    if (getaddrinfo(conn_s->host, NULL, NULL, &result)) {\n      CONNERR(conn_s, \"host lookup failed\");\n      return -1;\n    }\n\n    if (result) {\n      for (struct addrinfo *rp = result; rp; rp = rp->ai_next) {\n        if (rp->ai_addr) {\n          conn_s->remote.sin_addr = ((struct sockaddr_in*)rp->ai_addr)->sin_addr;\n          break;\n        }\n      }\n\n      freeaddrinfo(result);\n    } else {\n      CONNERR(conn_s, \"no address for host\");\n      return -1;\n    }\n#else\n    static pthread_mutex_t guard = PTHREAD_MUTEX_INITIALIZER;\n    struct hostent *hp;\n    struct in_addr **addr_list;\n    pthread_mutex_lock(&guard);\n    hp = gethostbyname(conn_s->host);\n    if(!hp) {\n      CONNERR(conn_s, \"host lookup failed\");\n      pthread_mutex_unlock(&guard);\n      return -1;\n    }\n    addr_list = (struct in_addr **)hp->h_addr_list;\n    if(*addr_list == 0) {\n      CONNERR(conn_s, \"no address for host\");\n      pthread_mutex_unlock(&guard);\n      return -1;\n    }\n    memcpy(&conn_s->remote.sin_addr, *addr_list, sizeof(struct in_addr));\n    pthread_mutex_unlock(&guard);\n#endif\n  }\n  conn_s->last_resolve = fq_gethrtime();\n  return 0;\n}\n\nstatic int\nfq_socket_connect(fq_conn_s *conn_s) {\n  int fd, rv, on = 1;\n  /* re-resolve if we've not done so quite recently */\n  if(conn_s->last_resolve == 0 ||\n     (fq_gethrtime() - conn_s->last_resolve) > MAX_RESOLVE_CACHE) {\n    if(fq_resolve_endpoint(conn_s) < 0) {\n      return -1;\n    }\n  }\n  fd = socket(AF_INET, SOCK_STREAM, 0);\n  if(fd == -1) return -1;\n  rv = connect(fd, (struct sockaddr *)&conn_s->remote,\n               sizeof(conn_s->remote));\n  if(rv == -1) {\n    snprintf(conn_s->error, sizeof(conn_s->error),\n             \"socket: %s\", strerror(errno));\n    CONNERR_S(conn_s);\n    close(fd);\n    return -1;\n  }\n  /* If this fails, we ignore it */\n  (void)setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on));\n  fq_keepalive_fd(fd, 10, 5, 2);\n  return fd;\n}\n\nstatic void\nfq_client_disconnect_internal(fq_conn_s *conn_s) {\n  if(conn_s->cmd_fd >= 0) {\n    int toclose = conn_s->cmd_fd;\n    conn_s->cmd_fd = -1;\n    fq_stacktrace(FQ_DEBUG_CONN, \"close(cmd_fd)\\n\",1,2);\n    close(toclose);\n    if(conn_s->disconnect_hook) conn_s->disconnect_hook(conn_s);\n  }\n  if(conn_s->data_fd >= 0) {\n    int toclose = conn_s->data_fd;\n    conn_s->data_fd = -1;\n    fq_debug(FQ_DEBUG_CONN, \"close(data_fd)\\n\");\n    close(toclose);\n  }\n  conn_s->data_ready = 0;\n  if(conn_s->cmd_hb_ms) {\n    unsigned short hb;\n    hb = conn_s->cmd_hb_ms;\n    conn_s->cmd_hb_ms = 0;\n    fq_client_heartbeat(conn_s, hb);\n    conn_s->cmd_hb_last = 0;\n  }\n}\n\nstatic int\nfq_client_do_auth(fq_conn_s *conn_s) {\n  int len, qlen, qtlen;\n  uint16_t cmd;\n  char error[1024];\n  char queue_composed[MAX_RK_LEN*2 + 1];\n  if(fq_write_uint16(conn_s->cmd_fd, FQ_PROTO_AUTH_CMD)) return -1;\n  if(fq_write_uint16(conn_s->cmd_fd, FQ_PROTO_AUTH_PLAIN)) return -2;\n  if(fq_write_short_cmd(conn_s->cmd_fd, strlen(conn_s->user), conn_s->user) < 0)\n    return -3;\n\n  qlen = strlen(conn_s->queue);\n  if(qlen > MAX_RK_LEN) qlen = MAX_RK_LEN;\n  qtlen = strlen(conn_s->queue_type);\n  if(qtlen > MAX_RK_LEN) qtlen = MAX_RK_LEN;\n  len = qlen + qtlen + 1;\n  memcpy(queue_composed, conn_s->queue, qlen);\n  queue_composed[qlen] = '\\0';\n  memcpy(queue_composed + qlen + 1, conn_s->queue_type, qtlen);\n  if(fq_write_short_cmd(conn_s->cmd_fd, len, queue_composed) < 0)\n    return -4;\n  if(fq_write_short_cmd(conn_s->cmd_fd, strlen(conn_s->pass), conn_s->pass) < 0)\n    return -5;\n  if(fq_read_uint16(conn_s->cmd_fd, &cmd)) return -6;\n  switch(cmd) {\n    case FQ_PROTO_ERROR:\n      len = fq_read_short_cmd(conn_s->cmd_fd, sizeof(error)-1, error);\n      if(conn_s->errorlog) {\n        if(len > (int)sizeof(error)-1) len = sizeof(error)-1;\n        if(len < 0) conn_s->errorlog(conn_s, \"error reading error\");\n        else conn_s->errorlog(conn_s,error);\n      }\n      return -7;\n    case FQ_PROTO_AUTH_RESP:\n      len = fq_read_short_cmd(conn_s->cmd_fd,\n                              sizeof(conn_s->key.name), conn_s->key.name);\n      if(len < 0 || len > (int)sizeof(conn_s->key.name)) return -8;\n      conn_s->key.len = len;\n#ifdef DEBUG\n      {\n        char hex[260];\n        if(fq_rk_to_hex(hex, sizeof(hex), &conn_s->key) >= 0)\n          fq_debug(FQ_DEBUG_CONN, \"client keyed:\\n%s\\n\", hex);\n      }\n#endif\n      conn_s->data_ready = 1;\n      break;\n    default:\n      if(conn_s->errorlog) {\n        snprintf(error, sizeof(error),\n                 \"server auth response 0x%04x unknown\\n\", cmd);\n        conn_s->errorlog(conn_s, error);\n      }\n      return -9;\n  }\n  return 0;\n}\n\nstatic int\nfq_client_data_connect_internal(fq_conn_s *conn_s) {\n  int flags;\n  uint32_t cmd = htonl(conn_s->peermode ? FQ_PROTO_PEER_MODE\n                                        : FQ_PROTO_DATA_MODE);\n  /* We don't support data connections when the cmd connection is down */\n  if(conn_s->cmd_fd < 0) return -1;\n\n  if(conn_s->data_fd >= 0) {\n    int toclose = conn_s->data_fd;\n    conn_s->data_fd = -1;\n    fq_debug(FQ_DEBUG_CONN, \"close(data_fd)\\n\");\n    close(toclose);\n  }\n  conn_s->data_fd = fq_socket_connect(conn_s);\n  if(conn_s->data_fd < 0) goto shutdown;\n  fq_debug(FQ_DEBUG_CONN, \"connect(data_fd) -> %d\\n\", conn_s->data_fd);\n  if(write(conn_s->data_fd, &cmd, sizeof(cmd)) != sizeof(cmd))\n    goto shutdown;\n  if(conn_s->peermode) {\n    if(write(conn_s->data_fd, &conn_s->peermode,\n             sizeof(conn_s->peermode)) != sizeof(conn_s->peermode))\n      goto shutdown;\n  }\n#ifdef DEBUG\n      {\n        char hex[260];\n        if(fq_rk_to_hex(hex, sizeof(hex), &conn_s->key) >= 0)\n          fq_debug(FQ_DEBUG_CONN, \"client keying:\\n%s\\n\", hex);\n      }\n#endif\n  if(fq_write_short_cmd(conn_s->data_fd,\n                        conn_s->key.len, conn_s->key.name) < 0) {\n    goto shutdown;\n  }\n  conn_s->tosend_offset = 0;\n  if(((flags = fcntl(conn_s->data_fd, F_GETFL, 0)) == -1) ||\n     (fcntl(conn_s->data_fd, F_SETFL, flags | O_NONBLOCK) == -1))\n    goto shutdown;\n\n  return 0;\n\n shutdown:\n  if(conn_s->data_fd >= 0) {\n    int toclose = conn_s->data_fd;\n    conn_s->data_fd = -1;\n    fq_debug(FQ_DEBUG_CONN, \"close(data_fd)\\n\");\n    close(toclose);\n  }\n  return -1;\n}\n\n/* This is dastardly... we know the ptr has to be aligned,\n * when we see that it isn't on dequeue, we know it is a hook_req_t\n */\n\n#define MARKED_HOOK_REQ_PTR(p) ((void *)(((uintptr_t)p)|1))\n#define CHECK_HOOK_REQ_PTR(p) (((uintptr_t)p)&1)\n#define UNMARKED_HOOK_REQ_PTR(p) ((void *)(((uintptr_t)p)&~1))\n\nstatic void\nmark_safe_free(void *pptr) {\n  if(CHECK_HOOK_REQ_PTR(pptr)) {\n    void *ptr = UNMARKED_HOOK_REQ_PTR(pptr);\n    free(ptr);\n  }\n  else fq_msg_free(pptr);\n}\nstatic void\nenqueue_auth_hook_req(fq_conn_s *conn_s, int rv) \n{\n  ck_fifo_spsc_enqueue_lock(&conn_s->backq);\n  ck_fifo_spsc_entry_t *fifo_entry = ck_fifo_spsc_recycle(&conn_s->backq);\n  if (unlikely(fifo_entry == NULL)) {\n    fifo_entry = malloc(sizeof(ck_fifo_spsc_entry_t));\n  }\n  hook_req_t *hreq = calloc(1,sizeof(*hreq));\n  hreq->type = AUTH_HOOK_TYPE;\n  hreq->entry = calloc(1, sizeof(*hreq->entry));\n  hreq->entry->data.return_value = 0;\n  ck_fifo_spsc_enqueue(&conn_s->backq, fifo_entry, MARKED_HOOK_REQ_PTR(hreq));\n  ck_fifo_spsc_enqueue_unlock(&conn_s->backq);\n}\n\nstatic void\nenqueue_cmd_hook_req(fq_conn_s *conn_s, cmd_instr *e) {\n  ck_fifo_spsc_enqueue_lock(&conn_s->backq);\n  ck_fifo_spsc_entry_t *fifo_entry = ck_fifo_spsc_recycle(&conn_s->backq);\n  if (unlikely(fifo_entry == NULL)) {\n    fifo_entry = malloc(sizeof(ck_fifo_spsc_entry_t));\n  }\n  hook_req_t *hreq = calloc(1,sizeof(*hreq));\n  hreq->type = CMD_HOOK_TYPE;\n  hreq->entry = e;\n  ck_fifo_spsc_enqueue(&conn_s->backq, fifo_entry, MARKED_HOOK_REQ_PTR(hreq));\n  ck_fifo_spsc_enqueue_unlock(&conn_s->backq);\n}\n\nstatic int\nfq_client_connect_internal(fq_conn_s *conn_s) {\n  int rv = -1;\n  uint32_t cmd = htonl(FQ_PROTO_CMD_MODE);\n  fq_client_disconnect_internal(conn_s);\n  conn_s->cmd_fd = fq_socket_connect(conn_s);\n  if(conn_s->cmd_fd < 0) goto shutdown;\n  fq_debug(FQ_DEBUG_CONN, \"connect(cmd_fd) -> %d\\n\", conn_s->cmd_fd);\n  if(write(conn_s->cmd_fd, &cmd, sizeof(cmd)) != sizeof(cmd))\n    goto shutdown;\n  if((rv = fq_client_do_auth(conn_s)) < 0) {\n    fq_debug(FQ_DEBUG_CONN, \"fq_client_do_auth -> %d\\n\", rv);\n    goto shutdown;\n  }\n  if(conn_s->auth_hook) {\n    if(conn_s->sync_hooks) enqueue_auth_hook_req(conn_s, 0);\n    else conn_s->auth_hook((fq_client)conn_s, 0);\n  }\n  return 0;\n\n shutdown:\n  if(conn_s->cmd_fd >= 0) {\n    int toclose = conn_s->cmd_fd;\n    conn_s->cmd_fd = -1;\n    fq_debug(FQ_DEBUG_CONN, \"close(cmd_fd) (in auth)\\n\");\n    close(toclose);\n    if(conn_s->disconnect_hook) conn_s->disconnect_hook(conn_s);\n  }\n  if(conn_s->auth_hook) {\n    if(conn_s->sync_hooks) enqueue_auth_hook_req(conn_s, rv);\n    else conn_s->auth_hook((fq_client)conn_s, rv);\n  }\n  return -1;\n}\n\nstatic void\nfq_client_read_complete(void *closure, fq_msg *msg) {\n  fq_conn_s *conn_s = (fq_conn_s *)closure;\n\n  if(conn_s->message_hook && conn_s->message_hook(conn_s, msg)) {\n    fq_msg_deref(msg);\n  }\n  else {\n\n    ck_fifo_spsc_enqueue_lock(&conn_s->backq);\n    ck_fifo_spsc_entry_t *fifo_entry = ck_fifo_spsc_recycle(&conn_s->backq);\n    if (unlikely(fifo_entry == NULL)) {\n      fifo_entry = malloc(sizeof(ck_fifo_spsc_entry_t));\n    }\n    ck_fifo_spsc_enqueue(&conn_s->backq, fifo_entry, msg);\n    ck_fifo_spsc_enqueue_unlock(&conn_s->backq);\n  }\n}\n\nstatic void\nfq_data_worker_loop(fq_conn_s *conn_s) {\n  buffered_msg_reader *ctx = NULL;\n  ctx = fq_buffered_msg_reader_alloc(conn_s->data_fd, 1);\n  fq_thread_setname(\"fq:w:%d/%d\", conn_s->cmd_fd, conn_s->data_fd);\n  while(conn_s->cmd_fd >= 0 && conn_s->data_fd >= 0 && conn_s->stop == 0) {\n    int rv;\n    int wait_ms = 500, needs_write = 0, mask, write_rv;\n    if(conn_s->tosend) goto the_thick_of_it;\n    ck_fifo_spsc_dequeue_lock(&conn_s->q);\n    while(ck_fifo_spsc_dequeue(&conn_s->q, &conn_s->tosend) == true) {\n      conn_s->tosend_offset = 0;\n      ck_pr_dec_uint(&conn_s->qlen);\n     the_thick_of_it:\n#ifdef DEBUG\n      fq_debug(FQ_DEBUG_MSG, \"dequeue message to submit to server\\n\");\n#endif\n      write_rv = fq_client_write_msg(conn_s->data_fd, conn_s->peermode,\n                                     conn_s->tosend, conn_s->tosend_offset, NULL);\n      if(write_rv > 0) {\n        conn_s->tosend_offset += write_rv;\n        wait_ms = 0;\n        break;\n      }\n      if(write_rv < 0) {\n        if(errno == EAGAIN) {\n          needs_write = 1;\n          break;\n        }\n        if(conn_s->errorlog) {\n          char errbuf[128];\n          snprintf(errbuf, sizeof(errbuf), \"data write error: %s\\n\", strerror(errno));\n          conn_s->errorlog(conn_s, errbuf);\n        }\n        ck_fifo_spsc_dequeue_unlock(&conn_s->q);\n        goto finish;\n      }\n      fq_msg_deref(conn_s->tosend);\n      conn_s->tosend = NULL;\n      conn_s->tosend_offset = 0;\n      wait_ms = 0;\n    }\n    ck_fifo_spsc_dequeue_unlock(&conn_s->q);\n\n    rv = fq_client_wfrw_internal(conn_s->data_fd, 1, needs_write, wait_ms, &mask);\n    fq_debug(FQ_DEBUG_CONN, \"fq_client_wfrw_internal(data:%d) -> %d\\n\", conn_s->data_fd, rv);\n    if(rv < 0) {\n      if(conn_s->errorlog) {\n        char errbuf[128];\n        snprintf(errbuf, sizeof(errbuf), \"data read error: %s\\n\", strerror(errno));\n        conn_s->errorlog(conn_s, errbuf);\n      }\n      goto finish;\n    }\n    if(rv > 0 && (mask & POLLIN)) {\n      if(fq_buffered_msg_read(ctx, fq_client_read_complete, conn_s) < 0) {\n        if(conn_s->errorlog) conn_s->errorlog(conn_s, \"data read: end-of-line\\n\");\n        goto finish;\n      }\n    }\n  }\nfinish:\n  conn_s->data_ready = 0;\n  fq_clear_message_cleanup_stack();\n  if(ctx) fq_buffered_msg_reader_free(ctx);\n#ifdef DEBUG\n  fq_debug(FQ_DEBUG_CONN, \"cmd_fd -> %d, stop -> %d\\n\", conn_s->cmd_fd, conn_s->stop);\n#endif\n}\nstatic void *\nfq_data_worker(void *u) {\n  int backoff = 0;\n  bool zero;\n  fq_conn_s *conn_s = (fq_conn_s *)u;\n\n  ck_pr_inc_uint(&conn_s->thrcnt);\n\n  while(conn_s->stop == 0) {\n    if(conn_s->data_ready) {\n      if(fq_client_data_connect_internal(conn_s) == 0) {\n        backoff = 0; /* we're good, restart our backoff */\n      }\n      fq_debug(FQ_DEBUG_IO, \"[data] connected\\n\");\n      fq_data_worker_loop(conn_s);\n      fq_debug(FQ_DEBUG_IO, \"[data] connection failed: %s\\n\", conn_s->error);\n    }\n    if(backoff) usleep(backoff + (4096 - (lrand48()%8192)));  /* +/- 4ms */\n    else backoff = 16384;\n    if(backoff < 1000000) backoff += (backoff >> 4);\n  }\n  if(conn_s->data_fd >= 0) {\n    int toclose = conn_s->data_fd;\n    conn_s->data_fd = -1;\n    fq_debug(FQ_DEBUG_CONN, \"close(data_fd)\\n\");\n    close(toclose);\n  }\n\n  ck_pr_dec_uint_zero(&conn_s->thrcnt, &zero);\n  if(zero) fq_conn_free(conn_s);\n  return (void *)NULL;\n}\n#define MAX_PENDING 128\nstatic void *\nfq_conn_worker(void *u) {\n  int backoff = 0, i;\n  bool zero;\n  fq_conn_s *conn_s = (fq_conn_s *)u;\n  cmd_instr *last_entries[MAX_PENDING] = { NULL };\n  int last_entry_idx = 0;\n  int next_entry_idx = 0;\n\n#define SAVE_ENTRY(e) do { \\\n  if(last_entries[next_entry_idx] != NULL) { \\\n    CONNERR(conn_s, \"exceed max cmd pipeline\"); \\\n    goto restart; \\\n  } \\\n  last_entries[next_entry_idx++] = e; \\\n  next_entry_idx = next_entry_idx % MAX_PENDING; \\\n} while(0)\n#define last_entry last_entries[last_entry_idx]\n#define PROCESS_ENTRY(e, should_free) do { \\\n  fq_assert(last_entries[last_entry_idx] == e); \\\n  if(should_free) free(last_entries[last_entry_idx]); \\\n  last_entries[last_entry_idx++] = NULL; \\\n  last_entry_idx = last_entry_idx % MAX_PENDING; \\\n} while(0)\n\n  cmd_instr *entry;\n\n  fq_thread_setname(\"fq:c\");\n  ck_pr_inc_uint(&conn_s->thrcnt);\n\n\n  while(conn_s->stop == 0) {\n    int wait_ms = 50;\n    if(fq_client_connect_internal(conn_s) == 0) {\n      fq_thread_setname(\"fq:c:%d\", conn_s->cmd_fd);\n      backoff = 0; /* we're good, restart our backoff */\n    } else {\n      fq_thread_setname(\"fq:c\");\n    }\n    while(conn_s->data_ready && conn_s->stop == 0) {\n      hrtime_t t;\n      unsigned long long hb_us;\n      struct timeval tv;\n      int rv;\n\n      ck_fifo_spsc_dequeue_lock(&conn_s->cmdq);\n      while(ck_fifo_spsc_dequeue(&conn_s->cmdq, &entry) == true) {\n#ifdef DEBUG\n        fq_debug(FQ_DEBUG_CONN, \"client acting on user req 0x%04x\\n\", entry->cmd);\n#endif\n        switch(entry->cmd) {\n          case FQ_PROTO_STATUSREQ:\n            fq_debug(FQ_DEBUG_CONN, \"sending status request\\n\");\n            if(fq_write_uint16(conn_s->cmd_fd, entry->cmd)) {\n              free(entry);\n              CONNERR(conn_s, \"write failed (statusreq)\");\n              goto restart;\n            }\n            SAVE_ENTRY(entry);\n            break;\n          case FQ_PROTO_HBREQ:\n            fq_debug(FQ_DEBUG_CONN, \"sending heartbeat request\\n\");\n            if(fq_write_uint16(conn_s->cmd_fd, entry->cmd) ||\n               fq_write_uint16(conn_s->cmd_fd, entry->data.heartbeat.ms)) {\n              free(entry);\n              CONNERR(conn_s, \"write failed (hbreq)\");\n              goto restart;\n            }\n            conn_s->cmd_hb_ms = entry->data.heartbeat.ms;\n            tv.tv_sec = (unsigned long)entry->data.heartbeat.ms / 1000;\n            tv.tv_usec = 1000UL * (entry->data.heartbeat.ms % 1000);\n            if(setsockopt(conn_s->cmd_fd, SOL_SOCKET, SO_RCVTIMEO,\n                          &tv, sizeof(tv)))\n              CONNERR(conn_s, strerror(errno));\n            tv.tv_sec = (unsigned long)entry->data.heartbeat.ms / 1000;\n            tv.tv_usec = 1000UL * (entry->data.heartbeat.ms % 1000);\n            if(setsockopt(conn_s->cmd_fd, SOL_SOCKET, SO_SNDTIMEO,\n                          &tv, sizeof(tv)))\n              CONNERR(conn_s, strerror(errno));\n            conn_s->cmd_hb_last = fq_gethrtime();\n            free(entry);\n            break;\n          case FQ_PROTO_BINDREQ:\n            {\n              unsigned short flags = entry->data.bind->flags;\n              if(fq_write_uint16(conn_s->cmd_fd, entry->cmd) ||\n                 fq_write_uint16(conn_s->cmd_fd, flags) ||\n                 fq_write_short_cmd(conn_s->cmd_fd,\n                                    entry->data.bind->exchange.len,\n                                    entry->data.bind->exchange.name) < 0 ||\n                 fq_write_short_cmd(conn_s->cmd_fd,\n                                    strlen(entry->data.bind->program),\n                                    entry->data.bind->program) < 0) {\n\tCONNERR(conn_s, \"write failed (bindreq)\");\n                goto restart;\n              }\n              SAVE_ENTRY(entry);\n            }\n            break;\n          case FQ_PROTO_UNBINDREQ:\n            {\n              if(fq_write_uint16(conn_s->cmd_fd, entry->cmd) ||\n                 fq_write_uint32(conn_s->cmd_fd, entry->data.unbind->route_id) ||\n                 fq_write_short_cmd(conn_s->cmd_fd,\n                                    entry->data.unbind->exchange.len,\n                                    entry->data.unbind->exchange.name) < 0) {\n\tCONNERR(conn_s, \"write failed (unbindreq)\");\n                goto restart;\n              }\n              SAVE_ENTRY(entry);\n            }\n            break;\n          default:\n            CONNERR(conn_s, \"unknown user-side cmd\");\n            free(entry);\n        }\n      }\n      ck_fifo_spsc_dequeue_unlock(&conn_s->cmdq);\n\n      if(conn_s->cmd_hb_needed) {\n#ifdef DEBUG\n          fq_debug(FQ_DEBUG_CONN, \"-> heartbeat\\n\");\n#endif\n        if(fq_write_uint16(conn_s->cmd_fd, FQ_PROTO_HB)) {\n          CONNERR(conn_s, \"write failed (hb)\");\n          break;\n        }\n        conn_s->cmd_hb_needed = 0;\n      }\n\n      rv = fq_client_wfrw_internal(conn_s->cmd_fd, 1, 0, wait_ms, NULL);\n      if(rv == 0) {\n        wait_ms = (wait_ms >> 2) + wait_ms;\n        if(conn_s->cmd_hb_ms && wait_ms > conn_s->cmd_hb_ms)\n          wait_ms = conn_s->cmd_hb_ms;\n        if(wait_ms > 1000) wait_ms = 1000;\n      }\n      else wait_ms = 50;\n      fq_debug(FQ_DEBUG_CONN, \"fq_client_wfrw_internal(cmd:%d) -> %d\\n\", conn_s->cmd_fd, rv);\n      t = fq_gethrtime();\n      hb_us = (unsigned long long)conn_s->cmd_hb_ms * 3 * 1000000ULL;\n      if(conn_s->cmd_hb_last && hb_us &&\n         conn_s->cmd_hb_last < (unsigned int) (t - hb_us)) {\n        char errbuf[256];\n        snprintf(errbuf, sizeof(errbuf), \"heartbeat failed [%llu - %llu = %llu]\",\n                 (unsigned long long)t, (unsigned long long)conn_s->cmd_hb_last,\n                 (unsigned long long)(t - conn_s->cmd_hb_last));\n        CONNERR(conn_s, errbuf);\n        break;\n      }\n      if(rv < 0) {\n        CONNERR(conn_s, strerror(errno));\n        break;\n      }\n      if(rv > 0) {\n        bool handled = false;\n        uint16_t hb;\n        if(fq_read_uint16(conn_s->cmd_fd, &hb)) break;\n        switch(hb) {\n          case FQ_PROTO_HB:\n#ifdef DEBUG\n            fq_debug(FQ_DEBUG_CONN, \"<- heartbeat\\n\");\n#endif\n            conn_s->cmd_hb_last = fq_gethrtime();\n            conn_s->cmd_hb_needed = 1;\n            break;\n          case FQ_PROTO_STATUS:\n            if(!last_entry || last_entry->cmd != FQ_PROTO_STATUSREQ) {\n              CONNERR(conn_s, \"protocol violation (status unexpected)\");\n              goto restart;\n            }\n            if(fq_read_status(conn_s->cmd_fd,\n                              last_entry->data.status.callback,\n                              last_entry->data.status.closure))\n              goto restart;\n            PROCESS_ENTRY(last_entry, 1);\n            break;\n          case FQ_PROTO_BIND:\n            if(!last_entry || last_entry->cmd != FQ_PROTO_BINDREQ) {\n              CONNERR(conn_s, \"protocol violation (bind unexpected)\");\n              goto restart;\n            }\n            if(fq_read_uint32(conn_s->cmd_fd,\n                              &last_entry->data.bind->out__route_id)) {\n              if(conn_s->bind_hook) {\n                if(conn_s->sync_hooks) {\n                  enqueue_cmd_hook_req(conn_s, last_entry);\n\t  PROCESS_ENTRY(last_entry, 0);\n\t  handled = true;\n                }\n                else conn_s->bind_hook((fq_client)conn_s, last_entry->data.bind);\n              }\n              CONNERR(conn_s, \"read failed (bind)\");\n              goto restart;\n            }\n            if(conn_s->bind_hook) {\n              if(conn_s->sync_hooks) {\n                enqueue_cmd_hook_req(conn_s, last_entry);\n\tPROCESS_ENTRY(last_entry, 0);\n\thandled = true;\n              }\n              else conn_s->bind_hook((fq_client)conn_s, last_entry->data.bind);\n            }\n            if(!handled) PROCESS_ENTRY(last_entry, 1);\n            break;\n          case FQ_PROTO_UNBIND:\n            if(!last_entry || last_entry->cmd != FQ_PROTO_UNBINDREQ) {\n              CONNERR(conn_s, \"protocol violation (unbind unexpected)\");\n              goto restart;\n            }\n            if(fq_read_uint32(conn_s->cmd_fd,\n                              &last_entry->data.unbind->out__success)) {\n              if(conn_s->unbind_hook) {\n                if(conn_s->sync_hooks) {\n                  enqueue_cmd_hook_req(conn_s, last_entry);\n                  PROCESS_ENTRY(last_entry, 0);\n\t  handled = true;\n                }\n                conn_s->unbind_hook((fq_client)conn_s, last_entry->data.unbind);\n              }\n              CONNERR(conn_s, \"read failed (unbind)\");\n              goto restart;\n            }\n            if(conn_s->unbind_hook) {\n              if(conn_s->sync_hooks) {\n                enqueue_cmd_hook_req(conn_s, last_entry);\n                PROCESS_ENTRY(last_entry, 0);\n\thandled = true;\n                last_entry = NULL;\n              }\n              conn_s->unbind_hook((fq_client)conn_s, last_entry->data.unbind);\n            }\n            if(!handled) PROCESS_ENTRY(last_entry,1);\n            break;\n          default:\n            CONNERR(conn_s, \"protocol violation\");\n            goto restart;\n            break;\n        }\n      }\n    }\n\n    fq_debug(FQ_DEBUG_CONN, \"[cmd] connection failed: %s\\n\", conn_s->error);\n    if(backoff) usleep(backoff + (4096 - (lrand48()%8192)));  /* +/- 4ms */\n    else backoff = 16384;\n    if(backoff < 1000000) backoff += (backoff >> 4);\n   restart:\n    /* drain the queue.. we're going to make a new connection */\n#ifdef DEBUG\n    fq_debug(FQ_DEBUG_CONN, \"[cmd] draining cmds\\n\");\n#endif\n    last_entry = NULL;\n    for(i=0;i<MAX_PENDING;i++) {\n      if(last_entries[i]) {\n        free(last_entries[i]);\n        last_entries[i] = NULL;\n      }\n    }\n\n    while(ck_fifo_spsc_dequeue(&conn_s->cmdq, &entry) == true) {\n      free(entry);\n    }\n  }\n  fq_client_disconnect_internal(conn_s);\n\n  ck_pr_dec_uint_zero(&conn_s->thrcnt, &zero);\n  if(zero) fq_conn_free(conn_s);\n  return (void *)NULL;\n}\n\nint\nfq_client_init(fq_client *conn_ptr, uint32_t peermode,\n               void (*logger)(fq_client, const char *)) {\n  fq_conn_s *conn_s;\n  conn_s = *conn_ptr = calloc(1, sizeof(*conn_s));\n  if(!conn_s) return -1;\n  /* make the sockets as disconnected */\n  conn_s->cmd_fd = conn_s->data_fd = -1;\n  conn_s->peermode = peermode;\n  conn_s->errorlog = logger;\n  conn_s->thrcnt = 1;\n  return 0;\n}\n\nvoid\nfq_client_set_userdata(fq_client conn, void *d) {\n  fq_conn_s *conn_s = (fq_conn_s *)conn;\n  conn_s->userdata = d;\n}\n\nvoid *\nfq_client_get_userdata(fq_client conn) {\n  fq_conn_s *conn_s = (fq_conn_s *)conn;\n  return conn_s->userdata;\n}\n\nint\nfq_client_hooks(fq_client conn, fq_hooks *hooks) {\n  fq_conn_s *conn_s = (fq_conn_s *)conn;\n  switch(hooks->version) {\n    case FQ_HOOKS_V4:\n      conn_s->message_hook = hooks->message;\n      conn_s->cleanup_hook = hooks->cleanup;\n      conn_s->disconnect_hook = hooks->disconnect;\n    case FQ_HOOKS_V3:\n      conn_s->sync_hooks = hooks->sync;\n    case FQ_HOOKS_V2:\n      conn_s->unbind_hook = hooks->unbind;\n    case FQ_HOOKS_V1:\n      conn_s->auth_hook = hooks->auth;\n      conn_s->bind_hook = hooks->bind;\n      break;\n    default:\n      return -1;\n  }\n  return 0;\n}\n\nint\nfq_client_creds(fq_client conn, const char *host, unsigned short port,\n                const char *sender, const char *pass) {\n  char qname[39];\n  fq_conn_s *conn_s;\n  conn_s = conn;\n\n  if(conn_s->user) {\n    CONNERR(conn_s, \"fq_client_creds already called\");\n    return -1;\n  }\n  /* mark the sockets as disconnected */\n  conn_s->cmd_fd = conn_s->data_fd = -1;\n\n  /* parse the user info */\n  conn_s->user = strdup(sender);\n  conn_s->queue = strchr(conn_s->user, '/');\n  if(conn_s->queue) {\n    *conn_s->queue++ = '\\0';\n    conn_s->queue_type = strchr(conn_s->queue, '/');\n    if(conn_s->queue_type) {\n      *conn_s->queue_type++ = '\\0';\n    }\n  }\n  if(!conn_s->queue || conn_s->queue[0] == '\\0') {\n    char *cp;\n    uuid_t out;\n    uuid_generate(out);\n    qname[0] = 'q'; qname[1] = '-';\n    uuid_unparse(out, qname+2);\n    for(cp=qname;*cp;cp++) *cp = tolower(*cp);\n    conn_s->queue = qname;\n  }\n  conn_s->queue_type = strdup(conn_s->queue_type ?\n                                conn_s->queue_type :\n                                FQ_DEFAULT_QUEUE_TYPE);\n  conn_s->queue = strdup(conn_s->queue);\n  conn_s->pass = strdup(pass);\n\n  conn_s->cmdqhead = malloc(sizeof(ck_fifo_spsc_entry_t));\n  ck_fifo_spsc_init(&conn_s->cmdq, conn_s->cmdqhead);\n\n  conn_s->qhead = malloc(sizeof(ck_fifo_spsc_entry_t));\n  ck_fifo_spsc_init(&conn_s->q, conn_s->qhead);\n\n  conn_s->backqhead = malloc(sizeof(ck_fifo_spsc_entry_t));\n  ck_fifo_spsc_init(&conn_s->backq, conn_s->backqhead);\n\n  conn_s->host = strdup(host);\n  conn_s->port = port;\n\n  return 0;\n}\n\nvoid\nfq_client_status(fq_client conn,\n                 void (*f)(char *, uint32_t, void *), void *c) {\n  fq_conn_s *conn_s = conn;\n  cmd_instr *e;\n  if(conn_s == 0 || conn_s->cmd_fd < 0) return;\n  e = malloc(sizeof(*e));\n  e->cmd = FQ_PROTO_STATUSREQ;\n  e->data.status.callback = f;\n  e->data.status.closure = c;\n  fq_client_signal(conn, e);\n}\nvoid\nfq_client_heartbeat(fq_client conn, unsigned short heartbeat_ms) {\n  fq_conn_s *conn_s = conn;\n  cmd_instr *e;\n  if(conn_s->cmd_fd < 0) return;\n  e = malloc(sizeof(*e));\n  e->cmd = FQ_PROTO_HBREQ;\n  e->data.heartbeat.ms = heartbeat_ms;\n  fq_client_signal(conn, e);\n}\nvoid\nfq_client_bind(fq_client conn, fq_bind_req *req) {\n  fq_conn_s *conn_s = conn;\n  cmd_instr *e;\n  if(conn_s->cmd_fd < 0) return;\n  e = malloc(sizeof(*e));\n  e->cmd = FQ_PROTO_BINDREQ;\n  e->data.bind = req;\n  fq_client_signal(conn, e);\n}\nvoid\nfq_client_unbind(fq_client conn, fq_unbind_req *req) {\n  fq_conn_s *conn_s = conn;\n  cmd_instr *e;\n  if(conn_s->cmd_fd < 0) return;\n  e = malloc(sizeof(*e));\n  e->cmd = FQ_PROTO_UNBINDREQ;\n  e->data.unbind = req;\n  fq_client_signal(conn, e);\n}\n\nvoid\nfq_client_set_backlog(fq_client conn, uint32_t len, uint32_t stall) {\n  fq_conn_s *conn_s = conn;\n  conn_s->qmaxlen = len;\n  conn_s->q_stall_time = stall;\n}\n\nvoid\nfq_client_set_nonblock(fq_client conn, bool nonblock) {\n  fq_conn_s *conn_s = conn;\n  conn_s->non_blocking = nonblock;\n}\n\nint\nfq_client_connect(fq_client conn) {\n  pthread_attr_t attr;\n  fq_conn_s *conn_s = conn;\n  if(conn_s->connected != 0) return -1;\n\n  conn_s->connected = 1;\n  if(pthread_attr_init(&attr) != 0) {\n    CONNERR(conn_s, \"pthread_attr_init failed\");\n    return -1;\n  }\n  if(pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED) != 0) {\n    CONNERR(conn_s, \"pthread_attr_setdetachstate failed\");\n    return -1;\n  }\n  if(pthread_create(&conn_s->worker, NULL, fq_conn_worker, conn_s) != 0) {\n    CONNERR(conn_s, \"could not start command thread\");\n    conn_s->stop = 1;\n    return -1;\n  }\n  if(pthread_create(&conn_s->data_worker, NULL, fq_data_worker, conn_s) != 0) {\n    CONNERR(conn_s, \"could not start data thread\");\n    conn_s->stop = 1;\n    return -1;\n  }\n  return 0;\n}\n\nvoid\nfq_client_destroy(fq_client conn) {\n  bool zero;\n  fq_conn_s *conn_s = conn;\n  conn_s->stop = 1;\n  ck_pr_dec_uint_zero(&conn_s->thrcnt, &zero);\n  if(zero) fq_conn_free(conn_s);\n}\n\nint\nfq_client_data_backlog(fq_client conn) {\n  fq_conn_s *conn_s = conn;\n  return ck_pr_load_uint(&conn_s->qlen);\n}\nint\nfq_client_publish(fq_client conn, fq_msg *msg) {\n  fq_conn_s *conn_s = conn;\n  ck_fifo_spsc_enqueue_lock(&conn_s->q);\n  if(conn_s->non_blocking && conn_s->qlen >= conn_s->qmaxlen) {\n    ck_fifo_spsc_enqueue_unlock(&conn_s->q);\n    return -1;\n  }\n  ck_fifo_spsc_entry_t *fifo_entry = ck_fifo_spsc_recycle(&conn_s->q);\n  if (unlikely(fifo_entry == NULL)) {\n    fifo_entry = malloc(sizeof(ck_fifo_spsc_entry_t));\n  }\n  while(conn_s->qlen >= conn_s->qmaxlen) {\n    if(conn_s->q_stall_time > 0) usleep(conn_s->q_stall_time);\n    else ck_pr_stall();\n  }\n  fq_msg_ref(msg);\n  ck_fifo_spsc_enqueue(&conn_s->q, fifo_entry, msg);\n  ck_fifo_spsc_enqueue_unlock(&conn_s->q);\n  ck_pr_inc_uint(&conn_s->qlen);\n  return 1;\n}\nfq_msg *fq_client_receive(fq_client conn) {\n  bool success;\n  fq_conn_s *conn_s = conn;\n  fq_msg *m = NULL;\n\n  ck_fifo_spsc_dequeue_lock(&conn_s->backq);\n  success = ck_fifo_spsc_dequeue(&conn_s->backq, &m);\n  ck_fifo_spsc_dequeue_unlock(&conn_s->backq);\n  if(success && m && CHECK_HOOK_REQ_PTR(m)) {\n    hook_req_t *hreq = UNMARKED_HOOK_REQ_PTR(m);\n    m = NULL;\n    cmd_instr *entry = hreq->entry;\n    switch(hreq->type) {\n      case AUTH_HOOK_TYPE:\n        if(conn_s->sync_hooks && conn_s->auth_hook)\n          conn_s->auth_hook(conn_s, entry->data.return_value);\n        break;\n      case CMD_HOOK_TYPE:\n        switch(entry->cmd) {\n          case FQ_PROTO_BINDREQ:\n            if(conn_s->sync_hooks && conn_s->bind_hook)\n              conn_s->bind_hook(conn_s, entry->data.bind);\n            break;\n          case FQ_PROTO_UNBINDREQ:\n            if(conn_s->sync_hooks && conn_s->unbind_hook)\n              conn_s->unbind_hook(conn_s, entry->data.unbind);\n            break;\n          default:\n            snprintf(conn_s->error, sizeof(conn_s->error),\n                     \"sync cmd feedback unknown: %x\\n\", entry->cmd);\n            if(conn_s->errorlog) conn_s->errorlog(conn_s, conn_s->error);\n        }\n        break;\n    }\n    free(entry);\n    free(hreq);\n  }\n  return m;\n}\n\n"
  },
  {
    "path": "fq_dtrace.blank.h",
    "content": "/*\n * Copyright (c) 2013 OmniTI Computer Consulting, Inc.\n * All rights reserved.\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\n#if 0\n#else\n\n#define FQ_QUEUE_ENQUEUE(arg0, arg1) \\\ndo { \\\n\t} while (0)\n#define FQ_QUEUE_ENQUEUE_ENABLED() (0)\n#define\tFQ_CLIENT_AUTH(arg0) \\\ndo { \\\n\t} while (0)\n#define\tFQ_CLIENT_AUTH_ENABLED() (0)\n#define\tFQ_CLIENT_AUTH_DATA(arg0) \\\ndo { \\\n\t} while (0)\n#define\tFQ_CLIENT_AUTH_DATA_ENABLED() (0)\n#define\tFQ_CLIENT_CONNECT(arg0, arg1) \\\ndo { \\\n\t} while (0)\n#define\tFQ_CLIENT_CONNECT_ENABLED() (0)\n#define\tFQ_CLIENT_DISCONNECT(arg0, arg1) \\\ndo { \\\n\t} while (0)\n#define\tFQ_CLIENT_DISCONNECT_ENABLED() (0)\n#define\tFQ_CONFIG_ROTATE(arg0) \\\ndo { \\\n\t} while (0)\n#define\tFQ_CONFIG_ROTATE_ENABLED() (0)\n#define\tFQ_MESSAGE_DELIVER(arg0, arg1, arg2) \\\ndo { \\\n\t} while (0)\n#define\tFQ_MESSAGE_DELIVER_ENABLED() (0)\n#define\tFQ_MESSAGE_RECEIVE(arg0, arg1, arg2) \\\ndo { \\\n\t} while (0)\n#define\tFQ_MESSAGE_RECEIVE_ENABLED() (0)\n#define\tFQ_QUEUE_BLOCK(arg0, arg1) \\\ndo { \\\n\t} while (0)\n#define\tFQ_QUEUE_BLOCK_ENABLED() (0)\n#define\tFQ_QUEUE_CREATE_FAILURE(arg0, arg1, arg2) \\\ndo { \\\n\t} while (0)\n#define\tFQ_QUEUE_CREATE_FAILURE_ENABLED() (0)\n#define\tFQ_QUEUE_CREATE_SUCCESS(arg0, arg1, arg2, arg3, arg4, arg5) \\\ndo { \\\n\t} while (0)\n#define\tFQ_QUEUE_CREATE_SUCCESS_ENABLED() (0)\n#define\tFQ_QUEUE_DESTROY(arg0, arg1) \\\ndo { \\\n\t} while (0)\n#define\tFQ_QUEUE_DESTROY_ENABLED() (0)\n#define\tFQ_QUEUE_DROP(arg0, arg1) \\\ndo { \\\n\t} while (0)\n#define\tFQ_QUEUE_DROP_ENABLED() (0)\n#define\tFQ_ROUTE_PROGRAM_ENTRY(arg0, arg1) \\\ndo { \\\n\t} while (0)\n#define\tFQ_ROUTE_PROGRAM_ENTRY_ENABLED() (0)\n#define\tFQ_ROUTE_PROGRAM_RETURN(arg0, arg1, arg2) \\\ndo { \\\n\t} while (0)\n#define\tFQ_ROUTE_PROGRAM_RETURN_ENABLED() (0)\n\n#endif /* !defined(DTRACE_PROBES_DISABLED) || !DTRACE_PROBES_DISABLED */\n"
  },
  {
    "path": "fq_dtrace.d",
    "content": "/*\n * Copyright (c) 2013 OmniTI Computer Consulting, Inc.\n * All rights reserved.\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\ntypedef struct {\n    int dummy;\n} fq_dtrace_msg_t;\n\ntypedef struct {\n    int dummy;\n} fq_msg_t;\n\ntypedef struct {\n    int dummy;\n} fq_dtrace_queue_t;\n\ntypedef struct {\n    int dummy;\n} fq_queue_t;\n\ntypedef struct {\n    int dummy;\n} fq_dtrace_remote_client_t;\n\ntypedef struct {\n    int dummy;\n} fq_remote_client_t;\n\ntypedef struct {\n    int dummy;\n} fq_dtrace_remote_anon_client_t;\n\ntypedef struct {\n    int dummy;\n} fq_remote_anon_client_t;\n\ntypedef struct {\n    int dummy;\n} fq_dtrace_remote_data_client_t;\n\ntypedef struct {\n    int dummy;\n} fq_remote_data_client_t;\n\nprovider fq {\n    probe client__connect(fq_dtrace_remote_anon_client_t *c, int m) :\n      (fq_remote_anon_client_t *c, int m);\n    probe client__disconnect(fq_dtrace_remote_anon_client_t *c, int m) :\n      (fq_remote_anon_client_t *c, int m);\n    probe client__auth(fq_dtrace_remote_client_t *c) :\n      (fq_remote_client_t *c);\n    probe client__auth__data(fq_dtrace_remote_data_client_t *c) :\n      (fq_remote_data_client_t *c);\n    probe queue__create__success(int, char *, int, char *, int, int);\n    probe queue__create__failure(int, char *, char *);\n    probe queue__destroy(int, char *);\n    probe queue__drop(fq_dtrace_queue_t *q, fq_dtrace_msg_t *m) :\n      (fq_queue_t *q, fq_msg_t *m);\n    probe queue__block(fq_dtrace_queue_t *q, fq_dtrace_msg_t *m) :\n      (fq_queue_t *q, fq_msg_t *m);\n    probe queue__enqueue(fq_dtrace_queue_t *q, fq_dtrace_msg_t *m) :\n      (fq_queue_t *q, fq_msg_t *m);\n    probe config__rotate(int);\n    probe message__receive(fq_dtrace_remote_client_t *c,\n                           fq_dtrace_remote_data_client_t *d,\n                           fq_dtrace_msg_t *m) :\n      (fq_remote_client_t *c,\n       fq_remote_data_client_t *d,\n       fq_msg_t *m);\n    probe message__deliver(fq_dtrace_remote_client_t *c,\n                           fq_dtrace_remote_data_client_t *d,\n                           fq_dtrace_msg_t *m) :\n      (fq_remote_client_t *c,\n       fq_remote_data_client_t *d,\n       fq_msg_t *m);\n    probe route__program__entry(char *p, fq_dtrace_msg_t *m) :\n      (char *p, fq_msg_t *m);\n    probe route__program__return(char *p, fq_dtrace_msg_t *m, int32_t u) :\n      (char *p, fq_msg_t *m, int32_t u);\n};\n\n#pragma D attributes Evolving/Evolving/ISA provider fq provider\n#pragma D attributes Private/Private/Unknown provider fq module\n#pragma D attributes Private/Private/Unknown provider fq function\n#pragma D attributes Private/Private/ISA provider fq name\n#pragma D attributes Evolving/Evolving/ISA provider fq args\n"
  },
  {
    "path": "fq_msg.c",
    "content": "/*\n * Copyright (c) 2013 OmniTI Computer Consulting, Inc.\n * All rights reserved.\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\n#include <pthread.h>\n#include <stdio.h>\n#include <string.h>\n#include <stddef.h>\n#include <stdlib.h>\n#include \"fq.h\"\n#include \"ck_pr.h\"\n#include \"ck_malloc.h\"\n#include \"ck_hs.h\"\n#include \"ck_fifo.h\"\n\n#define MSG_ALIGN sizeof(void *)\n#define MAX_FREE_LIST_SIZE 1000000\n\n#define unlikely(x)    __builtin_expect(!!(x), 0)\n\nstatic fq_msgid local_msgid = {\n  .id = {\n    .u32 = {\n      .p1 = 0,\n      .p2 = 0,\n      .p3 = 0,\n      .p4 = 0\n    }\n  }\n};\n\nstatic void\npull_next_local_msgid(fq_msgid *msgid) {\n  uint32_t last;\n  fq_msgid g;\nagain:\n  memcpy(&g, &local_msgid, sizeof(fq_msgid));\n  memcpy(msgid, &g, sizeof(fq_msgid));\n  last = ck_pr_faa_32(&local_msgid.id.u32.p1, 1);\n  msgid->id.u32.p1 = last + 1;\n  if(last == 0xffffffffUL) {\n    last = ck_pr_faa_32(&local_msgid.id.u32.p2, 1);\n    msgid->id.u32.p2 = last + 1;\n    if(last == 0xffffffffUL) {\n      last = ck_pr_faa_32(&local_msgid.id.u32.p3, 1);\n      msgid->id.u32.p3 = last + 1;\n      if(last == 0xffffffffUL) {\n        last = ck_pr_faa_32(&local_msgid.id.u32.p4, 1);\n        msgid->id.u32.p4 = last + 1;\n      }\n    }\n  }\n  if(msgid->id.u32.p4 < g.id.u32.p4) goto again;\n  if(msgid->id.u32.p4 > g.id.u32.p4) return;\n  if(msgid->id.u32.p3 < g.id.u32.p3) goto again;\n  if(msgid->id.u32.p3 > g.id.u32.p3) return;\n  if(msgid->id.u32.p2 < g.id.u32.p2) goto again;\n  if(msgid->id.u32.p2 > g.id.u32.p2) return;\n  if(msgid->id.u32.p1 > g.id.u32.p1) return;\n  goto again;\n}\n\nstatic inline fq_msg*\nmsg_allocate(const size_t s) \n{\n  fq_msg *m = calloc(1, sizeof(fq_msg) + s);\n  if(!m) return NULL;\n  m->payload_len = s;\n  return m;\n}\n\nstatic inline void\nmsg_free(fq_msg *m)\n{\n  if (m->free_fn != NULL) {\n    m->free_fn(m);\n  } else {\n    free(m);\n  }\n}\n\nfq_msg *\nfq_msg_alloc(const void *data, size_t s) {\n  fq_msg *m = msg_allocate(s);\n  if (unlikely(m == NULL)) {\n    return NULL;\n  }\n  if(s) memcpy(m->payload, data, s);\n#ifdef DEBUG\n  fq_debug(FQ_DEBUG_MSG, \"msg(%p) -> alloc\\n\", (void *)m);\n#endif\n  m->arrival_time = fq_gethrtime();\n  m->refcnt = 1;\n  return m;\n}\n\nfq_msg *\nfq_msg_alloc_BLANK(size_t s) {\n  fq_msg *m = msg_allocate(s);\n  if (unlikely(m == NULL)) {\n    return NULL;\n  }\n#ifdef DEBUG\n  fq_debug(FQ_DEBUG_MSG, \"msg(%p) -> alloc\\n\", (void *)m);\n#endif\n  m->arrival_time = fq_gethrtime();\n  m->refcnt = 1;\n  return m;\n}\n\nvoid\nfq_msg_ref(fq_msg *msg) {\n  ck_pr_inc_uint(&msg->refcnt);\n#ifdef DEBUG\n  fq_debug(FQ_DEBUG_MSG, \"msg(%p) -> ref: %d\\n\", (void *)msg, msg->refcnt);\n#endif\n}\nvoid\nfq_msg_deref(fq_msg *msg) {\n  bool zero;\n\n  ck_pr_dec_uint_zero(&msg->refcnt, &zero);\n  if(zero) {\n#ifdef DEBUG\n    fq_debug(FQ_DEBUG_MSG, \"msg(%p) -> free\\n\", (void *)msg);\n#endif\n    msg_free(msg);\n  }\n#ifdef DEBUG\n  else {\n    fq_debug(FQ_DEBUG_MSG, \"msg(%p) -> deref: %d\\n\", (void *)msg, msg->refcnt);\n  }\n#endif\n}\nvoid\nfq_msg_exchange(fq_msg *msg, const void *exchange, int rlen) {\n  if(rlen <= 0) {\n    msg->exchange.len = 0;\n    return;\n  }\n  if(rlen > MAX_RK_LEN) rlen = MAX_RK_LEN;\n  msg->exchange.len = rlen;\n  memcpy(msg->exchange.name, exchange, rlen);\n}\nvoid\nfq_msg_route(fq_msg *msg, const void *route, int rlen) {\n  if(rlen <= 0) {\n    msg->route.len = 0;\n    return;\n  }\n  if(rlen > MAX_RK_LEN) rlen = MAX_RK_LEN;\n  msg->route.len = rlen;\n  memcpy(msg->route.name, route, rlen);\n}\nvoid\nfq_msg_id(fq_msg *msg, fq_msgid *id) {\n  if(!id) pull_next_local_msgid(&msg->sender_msgid);\n  else memcpy(&msg->sender_msgid, id, sizeof(*id));\n}\n"
  },
  {
    "path": "fq_rcvr.c",
    "content": "/*\n * Copyright (c) 2013 OmniTI Computer Consulting, Inc.\n * All rights reserved.\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <unistd.h>\n#include <signal.h>\n#include <string.h>\n#include \"fq.h\"\n\nchar *exchange = \"maryland\";\nchar *program = \"prefix:\\\"\\\"\";\nint output = 1;\n\nvoid logger(fq_client, const char *);\n\nvoid logger(fq_client c, const char *s) {\n  (void)c;\n  fprintf(stderr, \"fq_logger: %s\\n\", s);\n}\nstatic void\nprint_rate(fq_client c, hrtime_t s, hrtime_t f, uint64_t cnt, uint64_t icnt) {\n  double d;\n  if(cnt) {\n    d = (double)cnt * 1000000000;\n    d /= (double)(f-s);\n    printf(\"[%d backlog] output %0.2f msg/sec\\n\",\n           fq_client_data_backlog(c), d);\n  }\n  if(icnt) {\n    d = (double)icnt * 1000000000;\n    d /= (double)(f-s);\n    printf(\"[%d backlog]  input %0.2f msg/sec\\n\",\n           fq_client_data_backlog(c), d);\n  }\n}\n\n\nstatic void\nmy_auth_handler(fq_client c, int error) {\n  fq_bind_req *breq;\n \n  if(error) return;\n\n  printf(\"attempting bind\\n\"); \n  breq = malloc(sizeof(*breq));\n  memset(breq, 0, sizeof(*breq));\n  int exchange_len = strlen(exchange);\n  memcpy(breq->exchange.name, exchange, exchange_len);\n  breq->exchange.len = exchange_len;\n  breq->flags = FQ_BIND_TRANS;\n  breq->program = program;\n  fq_client_bind(c, breq);\n}\n\nstatic void\nmy_bind_handler(fq_client c, fq_bind_req *breq) {\n  (void)c;\n  printf(\"route set -> %u\\n\", breq->out__route_id);\n  if(breq->out__route_id == FQ_BIND_ILLEGAL) {\n    fprintf(stderr, \"Failure to bind...\\n\");\n    exit(-1);\n  }\n}\n\nfq_hooks hooks = {\n .version = FQ_HOOKS_V1,\n .auth = my_auth_handler,\n .bind = my_bind_handler\n};\n\nint main(int argc, char **argv) {\n  hrtime_t s, f;\n  uint64_t cnt = 0, icnt = 0, icnt_total = 0;\n  int rcvd = 0;\n  fq_client c;\n  fq_msg *m;\n\n  char *fq_debug = getenv(\"FQ_DEBUG\");\n  if(fq_debug) fq_debug_set_bits(atoi(fq_debug));\n  signal(SIGPIPE, SIG_IGN);\n  fq_client_init(&c, 0, logger);\n  if(fq_client_hooks(c, &hooks)) {\n    fprintf(stderr, \"Can't register hooks\\n\");\n    exit(-1);\n  }\n\n  char *host = \"localhost\";\n  int port = 8765;\n  char *user = \"guest\";\n  char *pass = \"guest\";\n  int o;\n  while(-1 != (o = getopt(argc, argv, \"h:p:u:P:e:b:s\"))) {\n    switch(o) {\n    case 'h': host = strdup(optarg); break;\n    case 'p': port = atoi(optarg); break;\n    case 'u': user = strdup(optarg); break;\n    case 'P': pass = strdup(optarg); break;\n    case 'e': exchange = strdup(optarg); break;\n    case 'b': program = strdup(optarg); break;\n    case 's': output = 2; break;\n    default:\n     fprintf(stderr, \"%s [-h host] [-p port] [-u user] [-P pass] [-e exchange] [-b program] [-s]\\n\",\n            argv[0]);\n     exit(-1);\n     break;\n    }\n  }\n  fq_client_hooks(c, &hooks);\n  fq_client_creds(c, host, port, user, pass);\n  fq_client_heartbeat(c, 1000);\n  fq_client_set_backlog(c, 10000, 100);\n  fq_client_connect(c);\n\n  s = fq_gethrtime();\n  while(1) {\n    f = fq_gethrtime();\n    while(NULL != (m = fq_client_receive(c))) {\n      icnt++;\n      icnt_total++;\n      rcvd++;\n      if(output == 1) {\n        int ending = m->payload[m->payload_len-1] == '\\n' ? 1 : 0;\n        printf(\"[%.*s] %.*s\\n\", m->route.len, m->route.name, m->payload_len - ending, m->payload);\n      }\n      fq_msg_deref(m);\n    }\n    usleep(1000);\n    if(f-s > 1000000000) {\n      if(output == 2) {\n        print_rate(c, s, f, cnt, icnt);\n        printf(\"total: %llu\\n\", (unsigned long long)icnt_total);\n      }\n      icnt = 0;\n      cnt = 0;\n      s = f;\n    }\n  }\n  (void) argc;\n  return 0;\n}\n"
  },
  {
    "path": "fq_sndr.c",
    "content": "/*\n * Copyright (c) 2013 OmniTI Computer Consulting, Inc.\n * All rights reserved.\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <unistd.h>\n#include <signal.h>\n#include <string.h>\n#include \"fq.h\"\n\n#define SEND_COUNT 1000\nint send_count = SEND_COUNT;\nvoid logger(fq_client c, const char *);\n\nvoid logger(fq_client c, const char *s) {\n  (void)c;\n  fprintf(stderr, \"fq_logger: %s\\n\", s);\n}\nstatic void\ndebug_status(char *key, uint32_t value, void *unused) {\n  (void)unused;\n  fq_debug(FQ_DEBUG_CONN, \" ---> %s : %u\\n\", key, value);\n}\nstatic void\nprint_rate(fq_client c, hrtime_t s, hrtime_t f, uint64_t cnt, uint64_t icnt) {\n  double d;\n  fq_client_status(c, debug_status, NULL);\n  if(cnt) {\n    d = (double)cnt * 1000000000;\n    d /= (double)(f-s);\n    printf(\"[%d backlog] output %0.2f msg/sec\\n\",\n           fq_client_data_backlog(c), d);\n  }\n  if(icnt) {\n    d = (double)icnt * 1000000000;\n    d /= (double)(f-s);\n    printf(\"[%d backlog]  input %0.2f msg/sec\\n\",\n           fq_client_data_backlog(c), d);\n  }\n}\nint main(int argc, char **argv) {\n  hrtime_t s0, s, f, f0;\n  uint64_t cnt = 0, icnt = 0;\n  int psize = 0, i = 0;\n  fq_client c;\n  fq_msg *m;\n  char *fq_debug = getenv(\"FQ_DEBUG\");\n  if(fq_debug) fq_debug_set_bits(atoi(fq_debug));\n  signal(SIGPIPE, SIG_IGN);\n  fq_client_init(&c, 0, logger);\n  if(argc < 5) {\n    fprintf(stderr, \"%s <host> <port> <user> <pass> [size [count]]\\n\",\n            argv[0]);\n    exit(-1);\n  }\n  fq_client_creds(c, argv[1], atoi(argv[2]), argv[3], argv[4]);\n  fq_client_heartbeat(c, 1000);\n  fq_client_set_backlog(c, 10000, 100);\n  fq_client_connect(c);\n\n  if(argc > 5) {\n     psize = atoi(argv[5]);\n  }\n  printf(\"payload size -> %d\\n\", psize);\n  if(argc > 6) {\n    send_count = atoi(argv[6]);\n  }\n  printf(\"message count -> %d\\n\", send_count);\n\n  s0 = s = fq_gethrtime();\n  while(i < send_count || fq_client_data_backlog(c) > 0) {\n    if(i < send_count) {\n      m = fq_msg_alloc_BLANK(psize);\n      memset(m->payload, 0, psize);\n      fq_msg_exchange(m, \"maryland\", 8);\n      fq_msg_route(m, \"test.prefix.boo\", 15);\n      fq_msg_id(m, NULL);\n      fq_client_publish(c, m);\n      cnt++;\n      i++;\n      fq_msg_free(m);\n    }\n    else usleep(100);\n\n\n    f = fq_gethrtime();\n    if(f-s > 1000000000) {\n      print_rate(c, s, f, cnt, icnt);\n      icnt = 0;\n      cnt = 0;\n      s = f;\n    }\n  }\n  f0 = fq_gethrtime();\n  print_rate(c, s0, f0, i, 0);\n  (void) argc;\n  return 0;\n}\n"
  },
  {
    "path": "fq_utils.c",
    "content": "/*\n * Copyright (c) 2013 OmniTI Computer Consulting, Inc.\n * All rights reserved.\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\n#include \"fq.h\"\n#include \"fqd.h\"\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <strings.h>\n#include <ctype.h>\n#include <unistd.h>\n#include <sys/uio.h>\n#include <sys/socket.h>\n#include <errno.h>\n#include <pthread.h>\n#include <stdarg.h>\n#include <execinfo.h>\n#include <sys/types.h>\n#include <netinet/in.h>\n#include <inttypes.h>\n#include <assert.h>\n\nstatic inline int msg_free_stack_select(ssize_t in);\n\ntypedef struct free_message_stack {\n   ck_stack_t stack;\n   uint32_t size;\n   uint32_t max_size;\n   size_t alloc_size;\n} free_message_stack;\n\n/* We support separate stacks for separate msg sizes...\n * containers are from 2^10 (1k) to 2^16 (65k).\n * Messages are allocated from the smallest stack that can contain them.\n * Otherwise, they are traditionally allocated.\n */\n#define MSG_FREE_BASE 10\n#define MSG_FREE_CEILING 16\n\n#define MSG_FREE_STACKS (MSG_FREE_CEILING-MSG_FREE_BASE+1)\n\n/* the handles are TLS, they on heap and allocated TLS, and freed to a pool.\n * once a handle is allocated it never actually freed. */\n\nstruct msg_free_stacks_handle_t {\n  free_message_stack *stacks[MSG_FREE_STACKS];\n  bool valid;\n};\n\nstruct handle_free_list {\n  msg_free_stacks_handle_t *handle;\n  struct handle_free_list *next;\n};\n\n/* this is actually in <sys/sysmacros.h> on illumos but flagged off for some reason */\n#ifndef container_of\n#define container_of(m, s, name)                        \\\n  (void *)((uintptr_t)(m) - (uintptr_t)offsetof(s, name))\n#endif\n\nuint32_t fq_debug_bits = FQ_DEBUG_PANIC;\n\nvoid fq_debug_set_bits(uint32_t bits) {\n  fq_debug_bits = bits | FQ_DEBUG_PANIC;\n}\n\nstatic void\nfq_init_free_message_stack(free_message_stack *stack, const size_t max_free_count,\n                           const size_t alloc_size)\n{\n  ck_stack_init(&stack->stack);\n  stack->size = 0;\n  stack->max_size = max_free_count;\n  stack->alloc_size = alloc_size;\n}\n\nstatic inline fq_msg *\nfq_pop_free_message_stack(struct free_message_stack *stack)\n{\n  fq_msg *rv = NULL;\n  if (stack == NULL) {\n    return rv;\n  }\n\n  ck_stack_entry_t *ce = ck_stack_pop_mpmc(&stack->stack);\n  if (ce != NULL) {\n    ck_pr_dec_32(&stack->size);\n    rv = container_of(ce, fq_msg, cleanup_stack_entry);\n  }\n  return rv;\n}\n\nstatic inline void \nfq_push_free_message_stack(struct free_message_stack *stack, fq_msg *m) \n{\n  if (stack == NULL) {\n    return;\n  }\n\n  while(ck_pr_load_32(&stack->size) > stack->max_size) {\n    ck_stack_entry_t *ce = ck_stack_pop_mpmc(&stack->stack);\n    if (ce != NULL) {\n      fq_msg *m = container_of(ce, fq_msg, cleanup_stack_entry);\n      free(m);\n      ck_pr_dec_32(&stack->size);\n    }\n    else break;\n  }\n  uint32_t c = ck_pr_load_32(&stack->size);\n  if (c >= stack->max_size) {\n    free(m);\n    return;\n  }\n\n  ck_pr_inc_32(&stack->size);\n  ck_stack_push_mpmc(&stack->stack, &m->cleanup_stack_entry);\n}\n\nstatic void\nfq_free_msg_fn(fq_msg *m) \n{\n  if (m->cleanup_handle && m->cleanup_handle->valid) {\n    int idx = msg_free_stack_select(m->payload_len);\n    if(idx >= 0) {\n      fq_push_free_message_stack(m->cleanup_handle->stacks[idx], m);\n      return;\n    }\n  }\n  free(m);\n}\n\nvoid fq_debug_set_string(const char *s) {\n  char *lastsep, *tok = NULL;\n  char copy[128];\n  unsigned long nv;\n  int slen;\n\n  if(!s) return;\n  /* then comma separated named */\n  slen = strlen(s);\n  if(slen < 0 || slen > sizeof(copy) - 1) return;\n  /* copy including null terminator */\n  memcpy(copy,s,slen+1);\n\n  /* First try decimal */\n  nv = strtoul(copy,&lastsep,10);\n  if(*lastsep == '\\0') {\n    fq_debug_set_bits(nv);\n    return;\n  }\n\n  /* Then try hex */\n  nv = strtoul(copy,&lastsep,16);\n  if(*lastsep == '\\0') {\n    fq_debug_set_bits(nv);\n    return;\n  }\n\n  for (tok = strtok_r(copy, \",\", &lastsep);\n       tok;\n       tok = strtok_r(NULL, \",\", &lastsep)) {\n#define SETBIT(tok, A) do { \\\n  if(!strcasecmp(tok, #A + 9)) fq_debug_bits |= A; \\\n} while(0)\n    SETBIT(tok, FQ_DEBUG_MEM);\n    SETBIT(tok, FQ_DEBUG_MSG);\n    SETBIT(tok, FQ_DEBUG_ROUTE);\n    SETBIT(tok, FQ_DEBUG_IO);\n    SETBIT(tok, FQ_DEBUG_CONN);\n    SETBIT(tok, FQ_DEBUG_CONFIG);\n    SETBIT(tok, FQ_DEBUG_PEER);\n    SETBIT(tok, FQ_DEBUG_HTTP);\n    if(lastsep == NULL) break;\n  }\n}\n\n#define IN_READ_BUFFER_SIZE 1024*128\n#define FREE_MSG_LIST_SIZE 100000000 /* in bytes */\n#define CAPPED(x) (((x)<(MAX_MESSAGE_SIZE))?(x):(MAX_MESSAGE_SIZE))\nstruct buffered_msg_reader {\n  unsigned char scratch[IN_READ_BUFFER_SIZE];\n  int fd;\n  int off;\n  uint32_t peermode;\n  ssize_t nread;\n  ssize_t into_body;\n  fq_msg *copy;\n  fq_msg *msg;\n};\n\n\nstatic struct handle_free_list *free_message_handle_list = NULL;\nstatic pthread_mutex_t free_message_handle_list_lock = PTHREAD_MUTEX_INITIALIZER;\n\nstatic inline msg_free_stacks_handle_t *free_message_handle_acquire(void) {\n  msg_free_stacks_handle_t *a = NULL;\n  pthread_mutex_lock(&free_message_handle_list_lock);\n  if(free_message_handle_list) {\n    struct handle_free_list *tofree = free_message_handle_list;\n    free_message_handle_list = tofree->next;\n    a = tofree->handle;\n    free(tofree);\n  }\n  pthread_mutex_unlock(&free_message_handle_list_lock);\n  if(!a) a = calloc(1, sizeof(*a));\n  a->valid = true;\n  return a;\n}\n\nstatic inline int msg_free_stack_select(ssize_t in) {\n  int i;\n  if(in <= (1 << MSG_FREE_BASE)) return 0;\n  in--;\n  in >>= MSG_FREE_BASE+1;\n  for(i = 1; i < MSG_FREE_STACKS && in; i++, in >>= 1);\n  if(i < MSG_FREE_STACKS) return i;\n  return -1;\n}\n\n\nstatic __thread msg_free_stacks_handle_t *tls_free_message_handle = NULL;\n\nbuffered_msg_reader *fq_buffered_msg_reader_alloc(int fd, uint32_t peermode) {\n  buffered_msg_reader *br;\n  br = calloc(1, sizeof(*br));\n  br->fd = fd;\n  br->peermode = peermode;\n  br->msg = fq_msg_alloc_BLANK(0);\n  return br;\n}\nvoid fq_buffered_msg_reader_free(buffered_msg_reader *f) {\n  assert(f->msg->refcnt == 1);\n  fq_msg_deref(f->msg);\n  if(f->copy) fq_msg_deref(f->copy);\n  free(f);\n}\nstatic int\nparse_message_headers(int peermode, unsigned char *d, int dlen,\n                      fq_msg *msg) {\n  int ioff = 0;\n  unsigned char exchange_len, route_len, sender_len, nhops;\n#define BAIL_UNLESS_LEFT(d) do { \\\n  if((dlen-ioff) < (int)(d)) return 0; \\\n} while(0)\n\n  BAIL_UNLESS_LEFT(sizeof(exchange_len));\n  memcpy(&exchange_len, d+ioff, sizeof(exchange_len));\n  ioff += sizeof(exchange_len);\n  if(exchange_len > sizeof(msg->exchange.name)) return -1;\n  msg->exchange.len = exchange_len;\n\n  BAIL_UNLESS_LEFT(exchange_len);\n  memcpy(msg->exchange.name, d+ioff, exchange_len);\n  ioff += exchange_len;\n\n  BAIL_UNLESS_LEFT(sizeof(route_len));\n  memcpy(&route_len, d+ioff, sizeof(route_len));\n  ioff += sizeof(route_len);\n  if(route_len > sizeof(msg->route.name)) return -2;\n  msg->route.len = route_len;\n\n  BAIL_UNLESS_LEFT(route_len);\n  memcpy(msg->route.name, d+ioff, route_len);\n  ioff += route_len;\n\n  BAIL_UNLESS_LEFT(sizeof(msg->sender_msgid));\n  memcpy(&msg->sender_msgid, d+ioff, sizeof(msg->sender_msgid));\n  ioff += sizeof(msg->sender_msgid);\n\n  if(peermode) {\n    /* Peer mode includes the sender and the hops */\n    BAIL_UNLESS_LEFT(sizeof(sender_len));\n    memcpy(&sender_len, d+ioff, sizeof(sender_len));\n    ioff += sizeof(sender_len);\n    if(sender_len > sizeof(msg->sender.name)) return -3;\n    msg->sender.len = sender_len;\n\n    BAIL_UNLESS_LEFT(sender_len);\n    memcpy(msg->sender.name, d+ioff, sender_len);\n    ioff += sender_len;\n\n    BAIL_UNLESS_LEFT(sizeof(nhops));\n    memcpy(&nhops, d+ioff, sizeof(nhops));\n    ioff += sizeof(nhops);\n    if(nhops > MAX_HOPS) return -4;\n\n    if(nhops > 0) {\n      BAIL_UNLESS_LEFT(sizeof(uint32_t) * nhops);\n      memcpy(msg->hops, d+ioff, sizeof(uint32_t) * nhops);\n      ioff += sizeof(uint32_t) * nhops;\n    }\n  }\n\n  BAIL_UNLESS_LEFT(sizeof(msg->payload_len));\n  memcpy(&msg->payload_len, d+ioff, sizeof(msg->payload_len));\n  msg->payload_len = ntohl(msg->payload_len);\n  ioff += sizeof(msg->payload_len);\n\n  return ioff;\n}\n\nvoid \nfq_clear_message_cleanup_stack()\n{\n  int i;\n  if(tls_free_message_handle == NULL) return;\n  tls_free_message_handle->valid = false;\n  for(i=0; i<MSG_FREE_STACKS; i++) {\n    if (tls_free_message_handle->stacks[i]) {\n      tls_free_message_handle->stacks[i]->size = 0;\n      ck_stack_entry_t *ce = ck_stack_batch_pop_mpmc(&tls_free_message_handle->stacks[i]->stack);\n      while (ce != NULL) {\n        fq_msg *m = container_of(ce, fq_msg, cleanup_stack_entry);\n        ce = ce->next;\n        free(m);\n      }\n    }\n  }\n  struct handle_free_list *node = calloc(1, sizeof(*node));\n  pthread_mutex_lock(&free_message_handle_list_lock);\n  node->handle = tls_free_message_handle;\n  node->next = free_message_handle_list;\n  free_message_handle_list = node;\n  pthread_mutex_unlock(&free_message_handle_list_lock);\n}\n\n/*\n * return 0: keep going (to write path)\n * return -1: busted\n * \n * Read into one of N buffers so the processing thread \n * can do the work separate from the read\n */\nint\nfq_buffered_msg_read(buffered_msg_reader *f,\n                     void (*f_msg_handler)(void *, fq_msg *),\n                     void *closure) {\n  int rv;\n  static char scratch_buf[IN_READ_BUFFER_SIZE];\n  while(f->into_body < f->msg->payload_len) {\n    fq_assert(f->copy);\n    /* we need to be reading a largish payload */\n    if(f->into_body >= MAX_MESSAGE_SIZE) {\n      /* read into a scratch buffer */\n      size_t readsize = f->copy->payload_len - f->into_body;\n      if(readsize > sizeof(scratch_buf)) readsize = sizeof(scratch_buf);\n      while((rv = read(f->fd, scratch_buf, readsize)) == -1 && errno == EINTR);\n    }\n    else {\n      while((rv = read(f->fd, f->copy->payload + f->into_body,\n                       CAPPED(f->copy->payload_len) - f->into_body)) == -1 && errno == EINTR);\n    }\n    if(rv < 0 && errno == EAGAIN) return 0;\n    if(rv <= 0) {\n      fq_debug(FQ_DEBUG_IO, \"read error: %s\\n\", rv < 0 ? strerror(errno) : \"end-of-line\");\n      return -1;\n    }\n    fq_debug(FQ_DEBUG_MSG, \"%p <-- %d bytes for payload\\n\", (void *)f, rv);\n    f->into_body += rv;\n    if(f->into_body == f->copy->payload_len) {\n      f->into_body = 0;\n      goto message_done;\n    }\n  }\n  while((rv = read(f->fd, f->scratch+f->nread, sizeof(f->scratch)-f->nread)) == -1 &&\n        errno == EINTR);\n  fq_debug(FQ_DEBUG_IO, \"%p <-- %d bytes @ %d (%d)\\n\", (void *)f, rv, (int)f->nread,\n          (int)f->nread + ((rv > 0) ? rv : 0));\n  if(rv == -1 && errno == EAGAIN) return 0;\n  if(rv <= 0) return -1;\n  f->nread += rv;\n\n  while(f->nread>0) {\n    uint32_t body_available;\n    int body_start;\n    body_start = parse_message_headers(f->peermode,\n                                       f->scratch+f->off, f->nread-f->off,\n                                       f->msg);\n    f->into_body = 0;\n    fq_debug(FQ_DEBUG_MSG, \"%d = parse(+%d, %d) -> %d\\n\",\n            body_start, f->off, (int)f->nread-f->off,\n            body_start ? (int)f->msg->payload_len : 0);\n    if(body_start < 0) return -1;\n    if(!body_start) {\n      fq_debug(FQ_DEBUG_MSG, \"incomplete message header...\\n\");\n      memmove(f->scratch, f->scratch + f->off, f->nread - f->off);\n      f->nread -= f->off;\n      f->off = 0;\n      return 0;\n    }\n\n    free_message_stack *tls_free_message_stack = NULL;\n    int msg_stack_idx = msg_free_stack_select(f->msg->payload_len);\n    if(msg_stack_idx >= 0) {\n      if(tls_free_message_handle == NULL)\n        tls_free_message_handle = free_message_handle_acquire();\n      if(tls_free_message_handle->stacks[msg_stack_idx] == NULL) {\n        /* lazy create/init the cleanup stack */\n        tls_free_message_handle->stacks[msg_stack_idx] = malloc(sizeof(free_message_stack));\n        fq_init_free_message_stack(tls_free_message_handle->stacks[msg_stack_idx],\n                                   FREE_MSG_LIST_SIZE/(1 << (msg_stack_idx + MSG_FREE_BASE)),\n                                   (1 << (msg_stack_idx + MSG_FREE_BASE)));\n      }\n      tls_free_message_stack = tls_free_message_handle->stacks[msg_stack_idx];\n    }\n\n    if(tls_free_message_stack) {\n      /* We have a message... or the formal beginnings of one */\n      f->copy = fq_pop_free_message_stack(tls_free_message_stack);\n      if (f->copy == NULL) {\n        /* ran out of entries in free list */\n        f->copy = fq_msg_alloc_BLANK(tls_free_message_stack->alloc_size);\n        if (f->copy == NULL) {\n          /* this is bad, we can't alloc */\n          fq_debug(FQ_DEBUG_MSG, \"unable to malloc, OOM?\\n\");\n          return -1;\n        }\n      }\n\n      /* always 1 as this msg only lives until it's copied by a worker thread */\n      memcpy(f->copy, f->msg, sizeof(fq_msg));\n      f->copy->refcnt = 1;\n      f->copy->free_fn = fq_free_msg_fn;\n\n    } else {\n      f->copy = fq_msg_alloc_BLANK(CAPPED(f->msg->payload_len));\n      if (f->copy == NULL) {\n        /* this is bad, we can't alloc */\n        fq_debug(FQ_DEBUG_MSG, \"unable to malloc, OOM?\\n\");\n        return -1;\n      }\n\n      memcpy(f->copy, f->msg, sizeof(fq_msg));\n      f->copy->refcnt = 1;\n      f->copy->free_fn = NULL;\n    }\n\n    /* assign the cleanup stack for this message */\n    f->copy->cleanup_handle = tls_free_message_stack ? tls_free_message_handle : NULL;\n    memset(&f->copy->cleanup_stack_entry, 0, sizeof(ck_stack_entry_t));\n\n    f->off += body_start;\n    body_available = f->nread - f->off;\n    if(f->copy->payload_len < body_available) body_available = f->copy->payload_len;\n    memcpy(f->copy->payload, f->scratch+f->off, CAPPED(body_available));\n    if(body_available == f->copy->payload_len) {\n      f->off += body_available;\n     message_done:\n      f->copy->refcnt = 1;\n      f->copy->payload_len = CAPPED(f->copy->payload_len);\n      fq_debug(FQ_DEBUG_MSG, \"message read... injecting\\n\");\n      f->copy->arrival_time = fq_gethrtime();\n      f_msg_handler(closure, f->copy);\n      f->copy = NULL;\n      memset(f->msg, 0, sizeof(fq_msg));\n      /* It is still allocated and we are the sole owner, refcnt must be 1 */\n      f->msg->refcnt = 1;\n    }\n    else {\n      f->nread = 0;\n      f->off = 0;\n      f->into_body = body_available;\n      fq_debug(FQ_DEBUG_MSG, \"incomplete message... (%d needed)\\n\",\n             (int)f->msg->payload_len - (int)f->into_body);\n      return 0;\n    }\n  }\n  return 0;\n}\n\n#if defined(BSD) || defined(__FreeBSD__)\n#include <time.h>\n#define NANOSEC\t1000000000\n\nhrtime_t fq_gethrtime() {\n  struct timespec ts;\n  clock_gettime(CLOCK_UPTIME,&ts);\n  return (((u_int64_t) ts.tv_sec) * NANOSEC + ts.tv_nsec);\n}\n#elif defined(linux) || defined(__linux) || defined(__linux__)\n#include <time.h>\nhrtime_t fq_gethrtime() {\n  struct timespec ts;\n  clock_gettime(CLOCK_MONOTONIC_RAW, &ts);\n  return ((ts.tv_sec * 1000000000) + ts.tv_nsec);\n}\n#elif defined(__MACH__)\n#include <mach/mach.h>\n#include <mach/mach_time.h>\n\nstatic int initialized = 0;\nstatic mach_timebase_info_data_t    sTimebaseInfo;\nhrtime_t fq_gethrtime() {\n  uint64_t t;\n  if(!initialized) {\n    if(sTimebaseInfo.denom == 0)\n      (void) mach_timebase_info(&sTimebaseInfo);\n  }\n  t = mach_absolute_time();\n  return t * sTimebaseInfo.numer / sTimebaseInfo.denom;\n}\n#elif defined(__sun)\ninline hrtime_t fq_gethrtime() {\n  return gethrtime();\n}\n#else\n#error \"Unknown platform for clock implementation\"\n#endif\n\nint fq_rk_to_hex(char *buf, int len, fq_rk *k) {\n  int i;\n  unsigned char *bout = (unsigned char *)buf;\n  if(k->len * 2 + 4 > len) return -1;\n  *bout++ = '0';\n  *bout++ = 'x';\n  for (i=0; i<k->len; i++) {\n    snprintf((char *)bout, 3, \"%02x\", k->name[i]);\n    bout+=2;\n  }\n  *bout = '\\0';\n  return (bout - (unsigned char *)buf);\n}\nint\nfq_read_uint16(int fd, unsigned short *v) {\n  unsigned short nlen;\n  int rv;\n  while((rv = read(fd, &nlen, sizeof(nlen))) == -1 && errno == EINTR);\n  if(rv != sizeof(nlen)) return -1;\n  *v = ntohs(nlen);\n  return 0;\n}\nint\nfq_write_uint16(int fd, unsigned short v) {\n  uint16_t nv;\n  int rv;\n  nv = htons(v);\n  while((rv = write(fd, &nv, sizeof(nv))) == -1 && errno == EINTR);\n  return (rv == sizeof(nv)) ? 0 : -1;\n}\nint\nfq_read_uint32(int fd, uint32_t *v) {\n  uint32_t nlen;\n  int rv;\n  while((rv = read(fd, &nlen, sizeof(nlen))) == -1 && errno == EINTR);\n  if(rv != sizeof(nlen)) return -1;\n  *v = ntohl(nlen);\n  return 0;\n}\nint\nfq_write_uint32(int fd, uint32_t v) {\n  uint32_t nv;\n  int rv;\n  nv = htonl(v);\n  while((rv = write(fd, &nv, sizeof(nv))) == -1 && errno == EINTR);\n  return (rv == sizeof(nv)) ? 0 : -1;\n}\nint\nfq_read_short_cmd(int fd, unsigned short buflen, void *buf) {\n  void *tgt = buf;\n  unsigned char  scratch[0xffff];\n  unsigned short nlen, len;\n  int rv;\n  while((rv = read(fd, &nlen, sizeof(nlen))) == -1 && errno == EINTR);\n  if(rv < 0 || rv != sizeof(nlen)) return -1;\n  len = ntohs(nlen);\n  if(len == 0) return 0;\n  if(len > buflen)\n    tgt = scratch;\n  while((rv = read(fd, tgt, len)) == -1 && errno == EINTR);\n  if(rv != len) {\n    return -1;\n  }\n  if(tgt != buf) memcpy(buf, tgt, buflen); /* truncated */\n  return rv;\n}\nint\nfq_read_status(int fd, void (*f)(char *, uint32_t, void *), void *closure) {\n  while(1) {\n    char key[0x10000];\n    int len;\n    uint32_t value;\n\n    len = fq_read_short_cmd(fd, 0xffff, key);\n    if(len < 0) return -1;\n    if(len == 0) break;\n    key[len] = '\\0';\n    if(fq_read_uint32(fd, &value) < 0) return -1;\n    f(key, value, closure);\n  }\n  return 0;\n}\nint\nfq_write_short_cmd(int fd, unsigned short buflen, const void *buf) {\n  unsigned short nlen;\n  int rv;\n  nlen = htons(buflen);\n  while((rv = write(fd, &nlen, sizeof(nlen))) == -1 && errno == EINTR);\n  if(rv != sizeof(nlen)) return -1;\n  if(buflen == 0) return 0;\n  while((rv = write(fd, buf, buflen)) == -1 && errno == EINTR);\n  if(rv != buflen) return -1;\n  return rv;\n}\n\nint\nfq_read_long_cmd(int fd, int *rlen, void **rbuf) {\n  unsigned int nlen;\n  int rv, len;\n  while((rv = read(fd, &nlen, sizeof(nlen))) == -1 && errno == EINTR);\n  if(rv < 0 || rv != sizeof(nlen)) return -1;\n  len = ntohl(nlen);\n  *rlen = 0;\n  *rbuf = NULL;\n  if(len < 0) {\n    return -1;\n  }\n  else if(len > 0) {\n    *rbuf = malloc(len);\n    while((rv = read(fd, *rbuf, len)) == -1 && errno == EINTR);\n    if(rv != len) {\n      free(*rbuf);\n      *rlen = 0;\n      *rbuf = NULL;\n      return -1;\n    }\n    *rlen = rv;\n  }\n  return *rlen;\n}\n\nint\nfq_debug_fl(const char *file, int line, fq_debug_bits_t b, const char *fmt, ...) {\n  int rv;\n  va_list argp;\n  static hrtime_t epoch = 0;\n  hrtime_t now;\n  char fmtstring[1024];\n  uint64_t p = (uint64_t)pthread_self();\n  uint32_t ps = p & 0xffffffff;\n\n  (void)b;\n  now = fq_gethrtime();\n  if(!epoch) epoch = now;\n\n  snprintf(fmtstring, sizeof(fmtstring), \"[%\" PRIu64 \"] [%08x] %s\",\n           (uint64_t)((now-epoch)/1000), ps, fmt);\n  va_start(argp, fmt);\n  rv = vfprintf(stderr, fmtstring, argp);\n  va_end(argp);\n  (void)file;\n  (void)line;\n  return rv;\n}\n\nvoid\nfq_debug_stacktrace(fq_debug_bits_t b, const char *tag, int start, int end) {\n#define STACK_DEPTH 16\n  int i, cnt;\n  void *bti[STACK_DEPTH + 1], **bt = bti+1;\n  char **btname;\n  cnt = backtrace(bti, STACK_DEPTH + 1);\n  if(cnt < 1) {\n    fq_debug(b, \"track trace failed\\n\");\n    return;\n  }\n  btname = backtrace_symbols(bt, cnt);\n  if(start > cnt) start = cnt;\n  if(end > cnt) end = cnt;\n  for(i=start;i!=end;i += (start > end) ? -1 : 1) {\n    if(btname && btname[i])\n      fq_debug(b, \"[%2d] %s %s\\n\", i, tag, btname[i]);\n    else\n      fq_debug(b, \"[%2d] %s %p\\n\", i, tag, bt[i]);\n  }\n  if(btname) free(btname);\n}\n\nint fq_serialize(struct iovec **vecs, int *vec_count, uint32_t peermode, size_t off, fq_msg *m) \n{\n  int      i, writev_start = 0, idx = 0;\n  size_t   expect = 0;\n  uint32_t data_len = htonl(m->payload_len);\n  uint8_t  nhops = 0;\n  uint8_t  sender_len = m->sender.len;\n  uint8_t  exchange_len = m->exchange.len;\n  uint8_t  route_len = m->route.len;\n\n  if (vecs == NULL) {\n    return -1;\n  }\n\n  *vec_count = 7 + (peermode ? 4 : 0);\n  /* 7 for normal + 4 for peer */\n  *vecs = calloc(*vec_count, sizeof(struct iovec));\n\n  struct iovec *pv = *vecs;\n\n  expect = 1 + m->exchange.len + 1 + m->route.len +\n           sizeof(m->sender_msgid) +\n           sizeof(data_len) + m->payload_len;\n\n  if(peermode) {\n    for(i = 0; i < MAX_HOPS; i++) {\n      if(m->hops[i] == 0) break;\n      nhops++;\n    }\n    expect += 1 + m->sender.len + 1 + (nhops * sizeof(uint32_t));\n  }\n  fq_assert(off < expect);\n  expect -= off;\n  pv[idx  ].iov_len = 1;\n  pv[idx++].iov_base = &exchange_len;\n  pv[idx  ].iov_len = m->exchange.len;\n  pv[idx++].iov_base = m->exchange.name;\n  pv[idx  ].iov_len = 1;\n  pv[idx++].iov_base = &route_len;\n  pv[idx  ].iov_len = m->route.len;\n  pv[idx++].iov_base = m->route.name;\n  pv[idx  ].iov_len = sizeof(m->sender_msgid);\n  pv[idx++].iov_base = &m->sender_msgid;\n  if(peermode) {\n    pv[idx  ].iov_len = 1;\n    pv[idx++].iov_base = &sender_len;\n    pv[idx  ].iov_len = m->sender.len;\n    pv[idx++].iov_base = m->sender.name;\n    pv[idx  ].iov_len = 1;\n    pv[idx++].iov_base = &nhops;\n    pv[idx  ].iov_len = nhops * sizeof(uint32_t);\n    pv[idx++].iov_base = m->hops;\n  }\n  pv[idx  ].iov_len = sizeof(data_len);\n  pv[idx++].iov_base = &data_len;\n  pv[idx  ].iov_len = m->payload_len;\n  pv[idx++].iov_base = m->payload;\n  if(off > 0) {\n    for(i = 0; i < idx; i++) {\n      if(off >= pv[i].iov_len) {\n        off -= pv[i].iov_len;\n        writev_start++;\n      }\n      else {\n        pv[i].iov_len -= off;\n        pv[i].iov_base = ((unsigned char *)pv[i].iov_base) + off;\n        off = 0;\n        break;\n      }\n    }\n  }\n  return 0;\n}\n\nint\nfq_client_write_msg(int fd, uint32_t peermode, fq_msg *m, size_t off, size_t *written) {\n  struct iovec pv[11]; /* 7 for normal + 4 for peer */\n  int rv, i, writev_start = 0, idx = 0;\n  size_t expect;\n  unsigned char nhops = 0;\n  unsigned char sender_len = m->sender.len;\n  unsigned char exchange_len = m->exchange.len;\n  unsigned char route_len = m->route.len;\n  uint32_t      data_len = htonl(m->payload_len);\n\n  expect = 1 + m->exchange.len + 1 + m->route.len +\n           sizeof(m->sender_msgid) +\n           sizeof(data_len) + m->payload_len;\n\n  if(peermode) {\n    for(i=0;i<MAX_HOPS;i++) {\n      if(m->hops[i] == 0) break;\n      nhops++;\n    }\n    expect += 1 + m->sender.len + 1 + (nhops * sizeof(uint32_t));\n  }\n  fq_assert(off < expect);\n  expect -= off;\n  pv[idx  ].iov_len = 1;\n  pv[idx++].iov_base = &exchange_len;\n  pv[idx  ].iov_len = m->exchange.len;\n  pv[idx++].iov_base = m->exchange.name;\n  pv[idx  ].iov_len = 1;\n  pv[idx++].iov_base = &route_len;\n  pv[idx  ].iov_len = m->route.len;\n  pv[idx++].iov_base = m->route.name;\n  pv[idx  ].iov_len = sizeof(m->sender_msgid);\n  pv[idx++].iov_base = &m->sender_msgid;\n  if(peermode) {\n    pv[idx  ].iov_len = 1;\n    pv[idx++].iov_base = &sender_len;\n    pv[idx  ].iov_len = m->sender.len;\n    pv[idx++].iov_base = m->sender.name;\n    pv[idx  ].iov_len = 1;\n    pv[idx++].iov_base = &nhops;\n    pv[idx  ].iov_len = nhops * sizeof(uint32_t);\n    pv[idx++].iov_base = m->hops;\n  }\n  pv[idx  ].iov_len = sizeof(data_len);\n  pv[idx++].iov_base = &data_len;\n  pv[idx  ].iov_len = m->payload_len;\n  pv[idx++].iov_base = m->payload;\n  if(off > 0) {\n    for(i=0;i<idx;i++) {\n      if(off >= pv[i].iov_len) {\n        off -= pv[i].iov_len;\n        writev_start++;\n      }\n      else {\n        pv[i].iov_len -= off;\n        pv[i].iov_base = ((unsigned char *)pv[i].iov_base) + off;\n        off = 0;\n        break;\n      }\n    }\n  }\n  rv = writev(fd, pv+writev_start, idx-writev_start);\n  fq_debug(FQ_DEBUG_IO, \"writev(%d bytes [%d data]) -> %d\\n\",\n           (int)expect, (int)m->payload_len, rv);\n  if(rv > 0 && written) *written = rv;\n  if(rv != (int)expect) {\n    return rv;\n  }\n  if(rv == 0) return -1;\n  return 0;\n}\n\nint\nfq_find_in_hops(uint32_t needle, fq_msg *m) {\n  int i;\n  for(i=0; i<MAX_HOPS; i++) {\n    if(m->hops[i] == needle) return i;\n  }\n  return -1;\n}\n\nvoid\nfq_keepalive_fd(int fd, int cnt, int idle, int invtl) {\n  int optval = 1;\n  socklen_t optlen = sizeof(optval);\n  if(setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &optval, optlen) < 0) {\n    fq_debug(FQ_DEBUG_CONN, \"client(%d) keepalive failed: %s\\n\", fd, strerror(errno));\n  }\n#if defined(SOL_TCP)\n#if defined(TCP_KEEPCNT)\n  optval = cnt;\n  if(setsockopt(fd, SOL_TCP, TCP_KEEPCNT, &optval, optlen) < 0) {\n    fq_debug(FQ_DEBUG_CONN, \"client(%d) keepcnt failed : %s\\n\", fd, strerror(errno));\n  }\n#endif\n#if defined(TCP_KEEPIDLE)\n  optval = idle;\n  if(setsockopt(fd, SOL_TCP, TCP_KEEPIDLE, &optval, optlen) < 0) {\n    fq_debug(FQ_DEBUG_CONN, \"client(%d) keepidle failed : %s\\n\", fd, strerror(errno));\n  }\n#endif\n#if defined(TCP_KEEPINTVL)\n  optval = intvl;\n  if(setsockopt(fd, SOL_TCP, TCP_KEEPINTVL, &optval, optlen) < 0) {\n    fq_debug(FQ_DEBUG_CONN, \"client(%d) keepidle failed : %s\\n\", fd, strerror(errno));\n  }\n#endif\n#endif\n}\n\n"
  },
  {
    "path": "fqc.c",
    "content": "/*\n * Copyright (c) 2013 OmniTI Computer Consulting, Inc.\n * All rights reserved.\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <unistd.h>\n#include <signal.h>\n#include <string.h>\n#include \"fq.h\"\n\n#define SEND_COUNT 1000\nint send_count = SEND_COUNT;\nvoid logger(fq_client, const char *);\n\nvoid logger(fq_client c, const char *s) {\n  (void)c;\n  fprintf(stderr, \"fq_logger: %s\\n\", s);\n}\nstatic void\nprint_rate(fq_client c, hrtime_t s, hrtime_t f, uint64_t cnt, uint64_t icnt) {\n  double d;\n  if(cnt) {\n    d = (double)cnt * 1000000000;\n    d /= (double)(f-s);\n    printf(\"[%d backlog] output %0.2f msg/sec\\n\",\n           fq_client_data_backlog(c), d);\n  }\n  if(icnt) {\n    d = (double)icnt * 1000000000;\n    d /= (double)(f-s);\n    printf(\"[%d backlog]  input %0.2f msg/sec\\n\",\n           fq_client_data_backlog(c), d);\n  }\n}\nint main(int argc, char **argv) {\n  hrtime_t s0, s, f, f0;\n  uint64_t cnt = 0, icnt = 0;\n  int psize = 0, i = 0, rcvd = 0;\n  fq_client c;\n  fq_bind_req breq;\n  fq_msg *m;\n  signal(SIGPIPE, SIG_IGN);\n  fq_client_init(&c, 0, logger);\n  if(argc < 5) {\n    fprintf(stderr, \"%s <host> <port> <user> <pass> [size [count]]\\n\",\n            argv[0]);\n    exit(-1);\n  }\n  fq_client_creds(c, argv[1], atoi(argv[2]), argv[3], argv[4]);\n  fq_client_heartbeat(c, 1000);\n  fq_client_set_backlog(c, 10000, 100);\n  fq_client_connect(c);\n\n  memset(&breq, 0, sizeof(breq));\n  memcpy(breq.exchange.name, \"maryland\", 8);\n  breq.exchange.len = 8;\n  breq.flags = 0;\n  breq.program = (char *)\"prefix:\\\"test.prefix.\\\"\";\n\n  fq_client_bind(c, &breq);\n  while(ck_pr_load_32(&breq.out__route_id) == 0) usleep(100);\n  printf(\"route set -> %u\\n\", breq.out__route_id);\n  if(breq.out__route_id == FQ_BIND_ILLEGAL) {\n    fprintf(stderr, \"Failure to bind...\\n\");\n    exit(-1);\n  }\n\n  if(argc > 5) {\n    psize = atoi(argv[5]);\n    if(psize <= 0 || psize > 100000000) {\n      fprintf(stderr, \"invalid size must be > 0  and < 100000000\\n\");\n      exit(-1);\n    }\n  }\n  printf(\"payload size -> %d\\n\", psize);\n  if(argc > 6) {\n    send_count = atoi(argv[6]);\n    if(send_count <= 0 || send_count > 10000000) {\n      fprintf(stderr, \"invalid send count must be > 0  and < 10000000\\n\");\n      exit(-1);\n    }\n  }\n  printf(\"message count -> %d\\n\", send_count);\n\n  s0 = s = fq_gethrtime();\n  while(i < send_count || fq_client_data_backlog(c) > 0) {\n    if(i < send_count) {\n      m = fq_msg_alloc_BLANK(psize);\n      memset(m->payload, 0, psize);\n      fq_msg_exchange(m, \"maryland\", 8);\n      fq_msg_route(m, \"test.prefix.foo\", 15);\n      fq_msg_id(m, NULL);\n      fq_client_publish(c, m);\n      cnt++;\n      i++;\n      fq_msg_free(m);\n    }\n    else usleep(100);\n\n\n    f = fq_gethrtime();\n    while(NULL != (m = fq_client_receive(c))) {\n      icnt++;\n      rcvd++;\n      fq_msg_deref(m);\n    }\n    if(f-s > 1000000000) {\n      print_rate(c, s, f, cnt, icnt);\n      icnt = 0;\n      cnt = 0;\n      s = f;\n    }\n  }\n  f0 = fq_gethrtime();\n  print_rate(c, s0, f0, i, 0);\n  do {\n    icnt=0;\n    while(NULL != (m = fq_client_receive(c))) {\n      icnt++;\n      rcvd++;\n      fq_msg_deref(m);\n    }\n  } while(rcvd < send_count);\n  f0 = fq_gethrtime();\n  print_rate(c, s0, f0, 0, rcvd);\n  printf(\"Total received during test: %d\\n\", rcvd);\n\n  (void) argc;\n  return 0;\n}\n"
  },
  {
    "path": "fqd.c",
    "content": "/*\n * Copyright (c) 2013 OmniTI Computer Consulting, Inc.\n * All rights reserved.\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <signal.h>\n#include <unistd.h>\n#include <errno.h>\n#include <sys/types.h>\n#include <fcntl.h>\n#include <pthread.h>\n#include <netdb.h>\n#include <sys/socket.h>\n#include <arpa/inet.h>\n#include <dlfcn.h>\n#include \"getopt.h\"\n#include \"fqd.h\"\n#include \"fqd_private.h\"\n#ifndef NO_BCD\n#include <bcd.h>\n\nstatic void bcd_signal_handler(int s, siginfo_t *si, void *unused) {\n  (void)si;\n  (void)unused;\n  bcd_fatal(\"This is a fatal crash\");\n  signal(s, SIG_DFL);\n  return;\n}\n\nstatic void\nbcd_setup_sigaction(void)\n{\n  struct sigaction sa;\n  int signals[] = {\n    SIGSEGV,\n    SIGFPE,\n    SIGABRT,\n    SIGBUS,\n    SIGILL,\n    SIGFPE\n  };\n\n  sa.sa_sigaction = bcd_signal_handler;\n  sa.sa_flags = SA_SIGINFO | SA_ONSTACK;\n\n  for (size_t i = 0; i < sizeof(signals) / sizeof(*signals); i++) {\n    if (sigaction(signals[i], &sa, NULL) == -1) {\n      fprintf(stderr, \"warning: failed to set signal \"\n          \"handler %d\\n\", signals[i]);\n    }\n  }\n\n  return;\n}\n#else\ntypedef void *bcd_t;\n#endif\n\nstatic uint32_t nodeid = 0;\nstatic unsigned short port = 8765;\nstatic int foreground = 0;\nstatic bool usebcd = false;\nstatic int worker_threads = 1;\nstatic char *config_path = NULL;\nstatic char *queue_path = NULL;\nstatic char *libexecdir = NULL;\nstatic bcd_t global_bcd = { 0 };\n\n#define die(str) do { \\\n  fprintf(stderr, \"%s: %s\\n\", str, strerror(errno)); \\\n  exit(-1); \\\n} while(0)\n\nvoid fqd_bcd_attach(void) {\n  if(!usebcd) return;\n#ifndef NO_BCD\n  bcd_error_t error;\n  if (bcd_attach(&global_bcd, &error) == -1) {\n    fprintf(stderr, \"error: %s\\n\",\n            bcd_error_message(&error));\n     exit(-1);\n  }\n#endif\n}\n\nstatic void *listener_thread(void *unused) {\n  (void)unused;\n  fqd_start_worker_threads(worker_threads);\n  fprintf(stderr, \"Listening on port: %d\\n\", port);\n  fqd_listener(NULL, port);\n  fqd_stop_worker_threads();\n  return NULL;\n}\nstatic void usage(const char *prog) {\n  printf(\"%s:\\n\", prog);\n  printf(\"\\t-h\\t\\tthis help message\\n\");\n  printf(\"\\t-D\\t\\trun in the foreground\\n\");\n  printf(\"\\t-B\\t\\tenable BCD backtrace reporting\\n\");\n  printf(\"\\t-t <count>\\tnumber of worker threads to use (default 1)\\n\");\n  printf(\"\\t-n <ip>\\t\\tnode self identifier (IPv4)\\n\");\n  printf(\"\\t-p <port>\\tspecify listening port (default: 8765)\\n\");\n  printf(\"\\t-c <file>\\tlocation of the configdb\\n\");\n  printf(\"\\t-q <dir>\\twhere persistent queues are stored\\n\");\n  printf(\"\\t-w <dir>\\twhere files for web services are available\\n\");\n  printf(\"\\t-v <flags>\\tprint additional debugging information, by overriding FQ_DEBUG (cf. fq.h)\\n\");\n  printf(\"\\t-l <dir>\\tuse this dir for relative module loads\\n\");\n  printf(\"\\t-m <module>\\tmodule to load\\n\");\n}\nstatic void parse_cli(int argc, char **argv) {\n  int c;\n  char *debug = NULL;\n  if(getenv(\"FQ_DEBUG\")) {\n    debug = strdup(getenv(\"FQ_DEBUG\"));\n  }\n  libexecdir = strdup(LIBEXECDIR);\n  while((c = getopt(argc, argv, \"Bbl:m:hDt:n:p:q:c:w:v:\")) != EOF) {\n    switch(c) {\n      case 'B':\n        usebcd = true;\n        break;\n      case 'b':\n        usebcd = false;\n        break;\n      case 'l':\n        free(libexecdir);\n        libexecdir = strdup(optarg);\n        break;\n      case 'm':\n        fqd_route_load_module(libexecdir, optarg, \".so\");\n        break;\n      case 'q':\n        free(queue_path);\n        queue_path = strdup(optarg);\n        break;\n      case 'w':\n        fqd_http_set_root(optarg);\n        break;\n      case 'c':\n        free(config_path);\n        config_path = strdup(optarg);\n        break;\n      case 'D':\n        foreground = 1;\n        break;\n      case 't':\n        worker_threads = atoi(optarg);\n        break;\n      case 'h':\n        usage(argv[0]);\n        exit(0);\n      case 'n':\n        if(inet_pton(AF_INET, optarg, &nodeid) != 1) {\n          fprintf(stderr, \"Bad argument to -n, must be an IPv4 address.\\n\");\n          exit(-1);\n        }\n        if(nodeid == 0 || nodeid == htonl(0x7f000001)) {\n          fprintf(stderr, \"nodeid cannot be INADDR_ANY or loopback\\n\");\n          exit(-1);\n        }\n        break;\n      case 'p':\n        port = atoi(optarg);\n        break;\n      case 'v':\n        free(debug);\n        debug = strdup(optarg);\n        break;\n      default:\n        usage(argv[0]);\n        exit(-1);\n    }\n  }\n  if(debug) fq_debug_set_string(debug);\n  free(debug);\n}\nstatic uint32_t get_my_ip(void) {\n  uint32_t ip;\n  struct hostent *h;\n  char buff[128];\n  gethostname(buff, sizeof(buff));\n  h = gethostbyname(buff);\n  if(h && h->h_addrtype == AF_INET && h->h_length == 4) {\n    memcpy(&ip, h->h_addr_list[0], h->h_length);\n    if(ip == htonl(0x7f000001)) return 0;\n    return ip;\n  }\n  return 0;\n}\nint main(int argc, char **argv) {\n  nodeid = get_my_ip();\n  parse_cli(argc,argv);\n  global_functions_init(libexecdir);\n  if(nodeid == 0) {\n    fprintf(stderr, \"Could not determine host address, use -n <ip>\\n\");\n    exit(-1);\n  }\n  signal(SIGPIPE, SIG_IGN);\n  if(!foreground) {\n    int pid, fd;\n\n    /* Handle stdin/stdout/stderr */\n    fd = open(\"/dev/null\", O_RDONLY);\n    if(fd < 0 || dup2(fd, STDIN_FILENO) < 0) die(\"Failed to setup stdin\");\n    close(fd);\n    fd = open(\"/dev/null\", O_WRONLY);\n    if(fd < 0 || dup2(fd, STDOUT_FILENO) < 0 || dup2(fd, STDERR_FILENO) < 0)\n      die(\"Failed to setup std{out,err}\");\n    close(fd);\n\n    /* daemonize */\n    pid = fork();\n    if(pid < 0) die(\"Failed to fork\");\n    if(pid > 0) exit(0);\n    setsid();\n    pid = fork();\n    if(pid < 0) die(\"Failed to fork\");\n    if(pid > 0) exit(0);\n\n    /* run */\n  }\n#ifndef NO_BCD\n  bcd_error_t error;\n  if(usebcd) {\n    struct bcd_config config;\n\n    /* Initialize BCD configuration. See bcd.h for options */\n    if (bcd_config_init(&config, &error) == -1)\n      goto fatal;\n\n    /* Initialize the library. */\n    if (bcd_init(&config, &error) == -1)\n      goto fatal;\n\n    /* Initialize a handle to BCD. This should be called by every thread interacting with BCD. */\n    if (bcd_attach(&global_bcd, &error) == -1)\n      goto fatal;\n\n    if (bcd_kv(&global_bcd, \"application\", \"fqd\", &error) == -1)\n      goto fatal;\n\n    if (bcd_kv(&global_bcd, \"version\", FQ_VERSION, &error) == -1)\n      goto fatal;\n\n    bcd_setup_sigaction();\n  }\n#endif\n  fqd_config_init(nodeid, config_path, queue_path);\n  listener_thread(NULL);\n  fprintf(stderr, \"Listener thread could not start. Exiting.\\n\");\n  exit(0);\n  return 0;\n\n#ifndef NO_BCD\nfatal:\n  fprintf(stderr, \"error: %s\\n\",\n          bcd_error_message(&error));\n  exit(-1);\n#endif\n}\n"
  },
  {
    "path": "fqd.h.in",
    "content": "/*\n * Copyright (c) 2013 OmniTI Computer Consulting, Inc.\n * All rights reserved.\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\n#ifndef FQD_H\n#define FQD_H\n\n#ifndef _REENTRANT\n#error \"You must compile with -D_REENTRANT\"\n#endif\n\n#include <stdint.h>\n#include <stdbool.h>\n#include <stdlib.h>\n#include <sys/time.h>\n#include <netinet/in.h>\n\n#define FQ_VERSION_MAJOR @@FQ_MAJOR@@\n#define FQ_VERSION_MINOR @@FQ_MINOR@@\n#define FQ_VERSION_PATCH @@FQ_MICRO@@\n#define FQ_VERSION \"@@FQ_MAJOR@@.@@FQ_MINOR@@.@@FQ_MICRO@@\"\n\n#include \"fq.h\"\n\n#ifndef VARLIBFQDIR\n#define VARLIBFQDIR \"/var/lib/fq\"\n#endif\n\n#define MAX_MESSAGE_SIZE (1 << 28) /* 256MB */\n\ntypedef void * fqd_queue_impl_data;\n\ntypedef struct fqd_queue_impl {\n  const char *name;\n  fqd_queue_impl_data (*setup)(fq_rk *, uint32_t *count);\n  void (*enqueue)(fqd_queue_impl_data, fq_msg *);\n  fq_msg *(*dequeue)(fqd_queue_impl_data);\n  void (*dispose)(fq_rk *, fqd_queue_impl_data);\n  int (*add_checkpoint)(fqd_queue_impl_data, const char *name, const fq_msgid *id);\n  int (*remove_checkpoint)(fqd_queue_impl_data, const char *name);\n  int (*reset_checkpoint)(fqd_queue_impl_data, const char *name);\n} fqd_queue_impl;\n\n/* implememted in fqd_queue_mem.c */\nextern fqd_queue_impl fqd_queue_mem_impl;\n/* implememted in fqd_queue_jlog.c */\nextern fqd_queue_impl fqd_queue_jlog_impl;\n\ntypedef struct fqd_queue fqd_queue;\ntypedef struct fqd_route_rules fqd_route_rules;\ntypedef struct fqd_route_rule fqd_route_rule;\ntypedef struct fqd_config fqd_config;\n\ntypedef struct fqd_exchange_stats {\n  uint64_t n_messages;\n  uint64_t n_bytes;\n  uint64_t n_routed;\n  uint64_t n_no_route;\n  uint64_t n_dropped;\n  uint64_t n_size_dropped;\n  uint64_t n_no_exchange;\n  uint64_t n_loops;\n} fqd_exchange_stats_t;\n\ntypedef struct fqd_exchange {\n  fq_rk exchange;\n  fqd_exchange_stats_t *stats;\n  fqd_route_rules *set;\n} fqd_exchange;\n\nextern int fqd_queue_write_json(int fd, fqd_queue *q);\nextern int fqd_queue_sprint(char *buf, int len, fqd_queue *q);\nextern void fqd_queue_ref(fqd_queue *);\nextern bool fqd_queue_deref(fqd_queue *);\nextern int fqd_queue_cmp(const fqd_queue *, const fqd_queue *);\nextern int fqd_config_make_perm_queue(fqd_queue *q);\nextern int fqd_config_make_trans_queue(fqd_queue *q);\nextern int fqd_config_make_perm_binding(fq_rk *exchange, fqd_queue *q,\n                                        int peermode, const char *program);\nextern int fqd_config_make_trans_binding(fq_rk *exchange, fqd_queue *q,\n                                         int peermode, const char *program);\n\n#define CLIENT_SHARED \\\n  uint32_t refcnt; \\\n  int fd; \\\n  struct timeval connect_time; \\\n  struct sockaddr_in remote; \\\n  hrtime_t last_activity; \\\n  hrtime_t last_heartbeat; \\\n  char  pretty[80];\n\ntypedef struct {\n  CLIENT_SHARED\n} remote_anon_client;\n\ntypedef struct {\n  CLIENT_SHARED\n  uint32_t mode;\n  uint32_t peer_id;\n  uint32_t no_exchange;\n  uint32_t no_route;\n  uint32_t routed;\n  uint32_t dropped;\n  uint32_t size_dropped;\n  uint32_t msgs_in;\n  uint32_t msgs_out;\n  uint32_t octets_in;\n  uint32_t octets_out;\n} remote_data_client;\n\ntypedef struct remote_client {\n  CLIENT_SHARED\n\n  fq_rk user;\n  fq_rk key;\n  fqd_queue *queue;\n  remote_data_client *data;\n  unsigned short heartbeat_ms;\n} remote_client;\n\n/* You can read around in this... but can't modify it */\nextern void fqd_config_init(uint32_t, const char *config_path,\n                            const char *queue_path);\nextern int fqd_config_construct_queue_path(char *, size_t, fq_rk *);\nextern uint32_t fqd_config_get_nodeid(void);\nextern fqd_config *fqd_config_get(void);\nextern void fqd_config_release(fqd_config *);\nextern int fqd_config_register_client(remote_client *, uint64_t *gen);\nextern int fqd_config_deregister_client(remote_client *, uint64_t *gen);\nextern fqd_queue *fqd_config_register_queue(fqd_queue *, uint64_t *gen);\nextern int fqd_config_deregister_queue(fqd_queue *, uint64_t *gen);\nextern fqd_queue *fqd_config_get_registered_queue(fqd_config *, fq_rk *);\nextern remote_client *fqd_config_get_registered_client(fqd_config *, fq_rk *key);\nextern fqd_exchange *fqd_config_get_exchange(fqd_config *c, fq_rk *exchange);\n\nextern void fqd_size_dropped(uint64_t);\nextern void fqd_exchange_messages(fqd_exchange *, uint64_t);\nextern void fqd_exchange_message_octets(fqd_exchange *, uint64_t);\nextern void fqd_exchange_no_route(fqd_exchange *, uint64_t);\nextern void fqd_exchange_routed(fqd_exchange *, uint64_t);\nextern void fqd_exchange_dropped(fqd_exchange *, uint64_t);\nextern void fqd_exchange_no_exchange(fqd_exchange *, uint64_t);\n\nextern uint32_t fqd_config_bind(fq_rk *exchange, uint16_t flags,\n                                const char *program,\n                                fqd_queue *q, uint64_t *gen);\nextern int fqd_config_unbind(fq_rk *exchange, uint32_t route_id,\n                             fqd_queue *q, uint64_t *gen);\nextern void fqd_config_wait(uint64_t gen, int us);\nextern void fqd_config_http_stats(remote_client *client);\n\nextern void fqd_command_and_control_server(remote_client *);\nextern void fqd_data_subscription_server(remote_data_client *);\n\nextern int fqd_listener(const char *ip, unsigned short port);\nextern void fqd_remote_client_ref(remote_client *);\nextern bool fqd_remote_client_deref(remote_client *);\n\nextern fq_rk *fqd_queue_name(fqd_queue *q);\nextern fqd_queue *fqd_queue_get(fq_rk *, const char *, const char *,\n                                int, char *);\nextern uint32_t fqd_queue_get_backlog_limit(fqd_queue *);\nextern void fqd_queue_set_backlog_limit(fqd_queue *, uint32_t);\nextern queue_policy_t fqd_queue_get_policy(fqd_queue *);\nextern void fqd_queue_set_policy(fqd_queue *, queue_policy_t);\nextern void fqd_queue_enqueue(fqd_queue *q, fq_msg *m, int *dropped);\nextern fq_msg *fqd_queue_dequeue(fqd_queue *q);\nextern int fqd_queue_register_client(fqd_queue *q, remote_client *c);\nextern bool fqd_queue_deregister_client(fqd_queue *q, remote_client *c);\n\nextern void fqd_inject_message(remote_data_client *c, fq_msg *m);\nextern struct fqd_route_rule *\n  fqd_routemgr_compile(const char *program, int peermode, fqd_queue *q);\nextern void fqd_routemgr_rule_free(fqd_route_rule *rule);\nextern fqd_route_rules *fqd_routemgr_ruleset_alloc(void);\nextern uint32_t fqd_routemgr_ruleset_add_rule(fqd_route_rules *set,\n                                          fqd_route_rule *r, int *isnew);\nextern int\n  fqd_routemgr_drop_rules_by_route_id(fqd_route_rules *set, fqd_queue *q,\n                                      uint32_t route_id);\nextern int\n  fqd_routemgr_perm_route_id(fqd_route_rules *set, uint32_t route_id);\nextern int\n  fqd_routemgr_trans_route_id(fqd_route_rules *set, uint32_t route_id);\nextern void\n  fqd_routemgr_drop_rules_by_queue(fqd_route_rules *set, fqd_queue *q);\nextern fqd_route_rules *fqd_routemgr_ruleset_copy(fqd_route_rules *set);\nextern void fqd_routemgr_ruleset_free(fqd_route_rules *set);\n\nextern int\n  fqd_add_peer(uint64_t gen,\n               const char *host, int port,\n               const char *user, const char *pass,\n               fq_rk *exchange, const char *prog,\n               bool perm);\n\nextern int\n  fqd_remove_peers(uint64_t current_gen);\n\nextern int\n  fqd_remove_peer(const char *host, int port,\n                  const char *user, const char *pass,\n                  fq_rk *exchange, const char *prog);\n\n#define ERRTOFD(fd, error) do { \\\n  (void)fq_write_uint16(fd, htons(FQ_PROTO_ERROR)); \\\n  (void)fq_write_short_cmd(fd, strlen(error), error); \\\n} while(0)\n\n/* programming:\n *\n *  PROGRAM: <prefix|exact>:string RULES*\n *  RULE: (RULE)\n *  RULE: (RULE && RULE)\n *  RULE: (RULE || RULE)\n *  RULE: EXPR\n *  EXPR: function(args)\n *  args: arg\n *  args: arg, args\n *  arg: \"string\"\n *  arg: true|false\n *  arg: [0-9][0-9]*(?:.[0-9]*)\n *\n *  functions are dynamically loadable with type signature\n *  strings: s, booleans: b, integers: d\n *  function: substr_eq(9.3,10,\"tailorings\",true)\n *  C symbol: fqd_route_prog__substr_eq__ddsb(int nargs, valnode_t *args);\n *  fallback symbol: fqd_route_prog_substr_eq__VA(int nargs, valnode_t *args);\n */\n\n#define MAX_VALNODE_ARGS 16\n\ntypedef struct exprnode {\n  bool     (*match)(fq_msg *m, int nargs, valnode_t *args);\n  int        nargs;\n  valnode_t *args;\n} exprnode_t;\n\ntypedef struct rulenode {\n  uint32_t refcnt;\n  char   oper;\n  struct rulenode *left;\n  struct rulenode *right;\n  struct exprnode *expr;\n} rulenode_t;\n\n/* DTrace helpers */\ntypedef struct {\n  int fd;\n  char *pretty;\n} fq_dtrace_remote_anon_client_t;\n\ntypedef struct {\n  int fd;\n  char *pretty;\n  char *user;\n} fq_dtrace_remote_client_t;\n\ntypedef struct {\n  int fd;\n  char *pretty;\n  uint32_t mode;\n  uint32_t no_exchange;\n  uint32_t no_route;\n  uint32_t routed;\n  uint32_t dropped;\n  uint32_t size_dropped;\n  uint32_t msgs_in;\n  uint32_t msgs_out;\n} fq_dtrace_remote_data_client_t;\n\n#define DTRACE_PACK_ANON_CLIENT(dc, c) do { \\\n  (dc)->fd = (int32_t)(c)->fd; \\\n  (dc)->pretty = (c)->pretty; \\\n} while(0)\n\n#define DTRACE_PACK_CLIENT(dc, c) do { \\\n  (dc)->fd = (int32_t)(c)->fd; \\\n  (dc)->pretty = (c)->pretty; \\\n  (dc)->user = (char *)(c)->user.name; \\\n} while(0)\n\n#define DTRACE_PACK_DATA_CLIENT(dc, c) do { \\\n  (dc)->fd = (int32_t)(c)->fd; \\\n  (dc)->pretty = (c)->pretty; \\\n  (dc)->mode = (c)->mode; \\\n  (dc)->no_exchange = (c)->no_exchange; \\\n  (dc)->no_route = (c)->no_route; \\\n  (dc)->routed = (c)->routed; \\\n  (dc)->dropped = (c)->dropped; \\\n  (dc)->size_dropped = (c)->size_dropped; \\\n  (dc)->msgs_in = (c)->msgs_in; \\\n  (dc)->msgs_out = (c)->msgs_out; \\\n} while(0)\n\ntypedef struct {\n  char   *name;\n  int32_t private;\n  int32_t policy;\n  char   *type;\n} fq_dtrace_queue_t;\n\nvoid fqd_queue_dtrace_pack(fq_dtrace_queue_t *, fqd_queue *);\n\nvoid fqd_http_loop(remote_client *c, uint32_t bytes_four);\n\nvoid fqd_http_set_root(const char *newpath);\n\n#define DTRACE_PACK_QUEUE(dq, c) fqd_queue_dtrace_pack(dq, c)\n\n#endif\n"
  },
  {
    "path": "fqd_ccs.c",
    "content": "/*\n * Copyright (c) 2013 OmniTI Computer Consulting, Inc.\n * All rights reserved.\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\n#include \"fqd.h\"\n#include \"fqd_private.h\"\n#include \"fq_dtrace.h\"\n\n#include <stdio.h>\n#include <unistd.h>\n#include <stdlib.h>\n#include <arpa/inet.h>\n#include <sys/socket.h>\n#include <netinet/in.h>\n#include <poll.h>\n#include <errno.h>\n#include <uuid/uuid.h>\n\n#ifdef __MACH__\nstatic int mkkey(void *ptr, int len) {\n  static FILE *_random;\n  if(_random == NULL) _random = fopen(\"/dev/random\", \"r\");\n  if(_random == NULL) return -1;\n  unsigned char *ucp = ptr;\n  for(int i=0; i<len; i++) ucp[i] = fgetc(_random);\n  return 0;\n}\n#else\n#include <openssl/rand.h>\nstatic int mkkey(void *ptr, int len) {\n  if(RAND_bytes(ptr, len) != 1) {\n#if OPENSSL_VERSION_NUMBER < 0x1010100fL\n    if(RAND_pseudo_bytes(ptr, len) != 1) {\n      return -1;\n    }\n#else\n    return -1;\n#endif\n  }\n  return 0;\n}\n#endif\n\nstatic int\nfqd_ccs_auth(remote_client *client) {\n  uint16_t cmd, method;\n  fq_rk queue_name;\n\n  if(fq_read_uint16(client->fd, &cmd) ||\n     ntohs(cmd) != FQ_PROTO_AUTH_CMD) {\n    ERRTOFD(client->fd, \"auth command expected\");\n    return -1;\n  }\n  if(fq_read_uint16(client->fd, &method)) {\n    ERRTOFD(client->fd, \"auth method read failed\");\n    return -2;\n  }\n  method = ntohs(method);\n  if(method == 0) {\n    char buf[128];\n    unsigned char pass[10240];\n    char queue_detail[1024], *end_of_qd;\n    char *qtype = NULL, *qparams = NULL;\n    char *replace_params = NULL;\n    int len;\n    len = fq_read_short_cmd(client->fd, sizeof(client->user.name),\n                            client->user.name);\n    if(len < 0 || len > (int)sizeof(client->user.name)) {\n      ERRTOFD(client->fd, \"user name is too long\");\n      return -3;\n    }\n    client->user.len = len & 0xff;\n    len = fq_read_short_cmd(client->fd, sizeof(queue_detail)-1,\n                            queue_detail);\n    if(len < 0) return -4;\n    if(len >= sizeof(queue_detail)) {\n      ERRTOFD(client->fd, \"queue detail is too long\");\n      return -4;\n    }\n    queue_detail[len] = '\\0';\n    end_of_qd = memchr(queue_detail, '\\0', len);\n    if(!end_of_qd) {\n      if(len < 0 || len > (int)sizeof(queue_name.name)) {\n        ERRTOFD(client->fd, \"queue name is too long\");\n        return -4;\n      }\n      queue_name.len = len & 0xff;\n      memcpy(queue_name.name, queue_detail, queue_name.len);\n      if(queue_name.len < sizeof(queue_name.name))\n       memset(queue_name.name + queue_name.len, 0,\n              sizeof(queue_name.name) - queue_name.len);\n    }\n    else if(end_of_qd - queue_detail <= 0xff) {\n      queue_name.len = end_of_qd - queue_detail;\n      memcpy(queue_name.name, queue_detail, queue_name.len);\n      if(queue_name.len < sizeof(queue_name.name))\n       memset(queue_name.name + queue_name.len, 0,\n              sizeof(queue_name.name) - queue_name.len);\n      qtype = end_of_qd + 1;\n      if(*qtype) qparams = strchr(qtype, ':');\n      else qtype = NULL;\n      if(qparams) *qparams++ = '\\0';\n    }\n    else {\n      ERRTOFD(client->fd, \"pass field is too long\");\n      return -4;\n    }\n    if(queue_name.len == 0) {\n      uuid_t autogen;\n      static const char *DYNAMIC_QUEUE_FORCE_OPTIONS = \"transient,private\";\n      int rlen = strlen(DYNAMIC_QUEUE_FORCE_OPTIONS)+1;\n      if(!qparams || *qparams == '\\0') {\n        replace_params = malloc(rlen);\n        memcpy(replace_params, DYNAMIC_QUEUE_FORCE_OPTIONS, rlen);\n      }\n      else {\n        rlen += strlen(qparams)+1;\n        replace_params = malloc(rlen);\n        snprintf(replace_params, rlen, \"%s,%s\",\n                 qparams, DYNAMIC_QUEUE_FORCE_OPTIONS);\n      }\n      qparams = replace_params;\n      uuid_generate(autogen);\n      memcpy(queue_name.name, \"auto-\", 5);\n      uuid_unparse_lower(autogen, (void *)(queue_name.name+5));\n      queue_name.len = 5 + 36; /* 5 + 36 uuid, no trailing \\0 */\n    }\n    len = fq_read_short_cmd(client->fd, sizeof(pass), pass);\n    if(len < 0 || len > (int)sizeof(pass)) {\n      ERRTOFD(client->fd, \"queue name is too long\");\n      free(replace_params);\n      return -4;\n    }\n\n    client->queue = fqd_queue_get(&queue_name, qtype, qparams,\n                                  sizeof(buf), buf);\n    if(client->queue == NULL) {\n      ERRTOFD(client->fd, buf);\n      free(replace_params);\n      return -6;\n    }\n\n    /* do AUTH */\n    buf[0] = '\\0';\n    inet_ntop(AF_INET, &client->remote.sin_addr, buf, sizeof(buf));\n    snprintf(client->pretty, sizeof(client->pretty), \"%.*s/%.*s@%s:%d\",\n             client->user.len, client->user.name,\n             queue_name.len, queue_name.name,\n             buf, ntohs(client->remote.sin_port));\n    if(FQ_CLIENT_AUTH_ENABLED()) {\n      fq_dtrace_remote_client_t dclient;\n      DTRACE_PACK_CLIENT(&dclient, client);\n      FQ_CLIENT_AUTH(&dclient);\n    }\n    free(replace_params);\n    return 0;\n  }\n  ERRTOFD(client->fd, \"unsupported auth method\");\n  return -1;\n}\n\nstatic int\nfqd_ccs_key_client(remote_client *client) {\n  int fd = client->fd;\n\n  client->key.len = sizeof(client->key.name);\n  if(mkkey(client->key.name, client->key.len) != 0) {\n    ERRTOFD(fd, \"can't generate random key\");\n    return -1;\n  }\n\n  if(fqd_queue_register_client(client->queue, client)) {\n    ERRTOFD(fd, \"can't add you to queue\");\n    return -1;\n  }\n\n  if(fq_write_uint16(client->fd, FQ_PROTO_AUTH_RESP) ||\n     fq_write_short_cmd(client->fd,\n                        client->key.len, client->key.name) < 0) {\n    return -2;\n  }\n#ifdef DEBUG\n    {\n      char hex[260];\n      if(fq_rk_to_hex(hex, sizeof(hex), &client->key) >= 0)\n        fq_debug(FQ_DEBUG_CONN, \"client keyed:\\n%s\\n\", hex);\n    }\n#endif\n\n  return 0;\n}\n\nstatic int\nfqd_ccs_heartbeat(remote_client *client) {\n#ifdef DEBUG\n  fq_debug(FQ_DEBUG_CONN, \"heartbeat -> %s\\n\", client->pretty);\n#endif\n  return fq_write_uint16(client->fd, FQ_PROTO_HB);\n}\n\nstatic int\nfqd_css_status(remote_client *client) {\n  remote_data_client *data = client->data;\n#ifdef DEBUG\n  fq_debug(FQ_DEBUG_CONN, \"status -> %s\\n\", client->pretty);\n#endif\n  if(fq_write_uint16(client->fd, FQ_PROTO_STATUS) < 0) return -1;\n#define write_uintkey(name, v) do { \\\n  if(fq_write_short_cmd(client->fd, strlen(name), name) < 0) return -1; \\\n  if(fq_write_uint32(client->fd, v) < 0) return -1; \\\n} while(0)\n  if(client->queue) write_uintkey(\"dropped_in\", client->queue->dropped_to);\n  if(data) {\n    write_uintkey(\"no_exchange\", data->no_exchange);\n    write_uintkey(\"no_route\", data->no_route);\n    write_uintkey(\"routed\", data->routed);\n    write_uintkey(\"dropped\", data->dropped);\n    write_uintkey(\"size_dropped\", data->size_dropped);\n    write_uintkey(\"msgs_in\", data->msgs_in);\n    write_uintkey(\"msgs_out\", data->msgs_out);\n    write_uintkey(\"octets_in\", data->octets_in);\n    write_uintkey(\"octets_out\", data->octets_out);\n  }\n  if(fq_write_uint16(client->fd, 0) < 0) return -1;\n  return 0;\n}\n\nstatic int\nfqd_ccs_loop(remote_client *client) {\n  int poll_timeout = 10;\n  while(1) {\n    int rv;\n    struct pollfd pfd;\n    uint16_t cmd;\n    hrtime_t t;\n    pfd.fd = client->fd;\n    pfd.events = POLLIN|POLLHUP;\n    pfd.revents = 0;\n    rv = poll(&pfd, 1, poll_timeout);\n    if(rv < 0) {\n#ifdef DEBUG\n      fq_debug(FQ_DEBUG_CONN, \"poll() failed on %s: %s\\n\", client->pretty,\n               strerror(errno));\n#endif\n      break;\n    }\n    if(rv > 0) poll_timeout = 10;\n    else poll_timeout *= 2;\n    if(poll_timeout > 4000) poll_timeout = 4000;\n    /* we must wake up often enough to emit our heartbeat */\n    if(client->heartbeat_ms && poll_timeout > client->heartbeat_ms) poll_timeout = client->heartbeat_ms;\n    t = fq_gethrtime();\n    unsigned long long hb_ns = ((unsigned long long)client->heartbeat_ms) * 1000000ULL;\n    long long hb_age_ns = t - client->last_heartbeat;\n    if(client->heartbeat_ms && hb_age_ns > hb_ns) {\n      if(fqd_ccs_heartbeat(client)) break;\n      client->last_heartbeat = t;\n    }\n    if(hb_ns && client->last_activity < (t - hb_ns * 3)) {\n      ERRTOFD(client->fd, \"client heartbeat failed\");\n#ifdef DEBUG\n      fq_debug(FQ_DEBUG_CONN, \"heartbeat [%dms] failed from %s [%lld]\\n\", client->heartbeat_ms,\n               client->pretty, hb_age_ns);\n#endif\n      break;\n    }\n    if(rv > 0) {\n      if(fq_read_uint16(client->fd, &cmd) != 0) break;\n      client->last_activity = fq_gethrtime();\n      switch(cmd) {\n        case FQ_PROTO_HB:\n#ifdef DEBUG\n          fq_debug(FQ_DEBUG_CONN, \"heartbeat <- %s\\n\", client->pretty);\n#endif\n          break;\n        case FQ_PROTO_HBREQ:\n        {\n          uint16_t ms;\n          if(fq_read_uint16(client->fd, &ms) < 0) return -1;\n#ifdef DEBUG\n          fq_debug(FQ_DEBUG_CONN, \"setting client(%p) heartbeat to %d\\n\",\n                  (void *)client, ms);\n#endif\n          client->heartbeat_ms = ms;\n          break;\n        }\n        case FQ_PROTO_STATUSREQ:\n          if(fqd_css_status(client)) return -1;\n          break;\n        case FQ_PROTO_BINDREQ:\n        {\n          int len;\n          uint16_t flags;\n          uint32_t route_id;\n          uint64_t cgen;\n          char program[0xffff];\n          fq_rk exchange;\n          if(fq_read_uint16(client->fd, &flags)) return -1;\n          len = fq_read_short_cmd(client->fd, sizeof(exchange.name),\n                                  exchange.name);\n          if(len < 0 || len > (int)sizeof(exchange.name)) return -3;\n          exchange.len = len & 0xff;\n          len = fq_read_short_cmd(client->fd, sizeof(program)-1, program);\n          if(len < 0 || len > (int)sizeof(program)-1) return -1;\n          program[len] = '\\0';\n          route_id = fqd_config_bind(&exchange, flags, program,\n                                     client->queue, &cgen);\n          if(route_id != FQ_BIND_ILLEGAL)\n            fqd_config_wait(cgen, 100);\n          if(fq_write_uint16(client->fd, FQ_PROTO_BIND) != 0) return -1;\n          if(fq_write_uint32(client->fd, route_id) != 0) return -1;\n          break;\n        }\n        case FQ_PROTO_UNBINDREQ:\n        {\n          uint32_t route_id;\n          fq_rk exchange;\n          int success, len;\n          if(fq_read_uint32(client->fd, &route_id)) return -1;\n          len = fq_read_short_cmd(client->fd, sizeof(exchange.name),\n                                  exchange.name);\n          if(len < 0 || len > (int)sizeof(exchange.name)) return -1;\n          exchange.len = len & 0xff;\n          success = fqd_config_unbind(&exchange, route_id, client->queue, NULL);\n          if(fq_write_uint16(client->fd, FQ_PROTO_UNBIND) != 0) return -1;\n          if(fq_write_uint32(client->fd, success ? route_id : FQ_BIND_ILLEGAL))\n            return -1;\n          break;\n        }\n        default:\n          return -1;\n      }\n    }\n  }\n  return -1;\n}\n\nextern void\nfqd_command_and_control_server(remote_client *client) {\n  /* auth */\n  int rv, registered = 0;\n  uint64_t cgen;\n  fq_debug(FQ_DEBUG_CONN, \"--> ccs thread\\n\");\n  if((rv = fqd_ccs_auth(client)) != 0) {\n    fq_debug(FQ_DEBUG_CONN, \"client auth failed: %d\\n\", rv);\n    (void)rv;\n    goto out;\n  }\n  if(fqd_config_register_client(client, &cgen)) {\n    fq_debug(FQ_DEBUG_CONN, \"client registration failed\\n\");\n    goto out;\n  }\n  fq_thread_setname(\"fqd:ccs:%s\", client->pretty);\n  registered = 1;\n  fqd_config_wait(cgen, 100);\n  if(fqd_ccs_key_client(client) != 0) {\n    fq_debug(FQ_DEBUG_CONN, \"client keying failed: %d\\n\", rv);\n    goto out;\n  }\n  fqd_ccs_heartbeat(client);\n  fqd_ccs_loop(client);\nout:\n  if(registered) fqd_config_deregister_client(client, NULL);\n  fq_debug(FQ_DEBUG_CONN, \"<-- ccs thread\\n\");\n}\n"
  },
  {
    "path": "fqd_config.c",
    "content": "/*\n * Copyright (c) 2013 OmniTI Computer Consulting, Inc.\n * All rights reserved.\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\n#include <stdio.h>\n#include <string.h>\n#include <stdint.h>\n#include <stdlib.h>\n#include <unistd.h>\n#include <pthread.h>\n#include <errno.h>\n#include <ck_pr.h>\n\n#include <sqlite3.h>\n\n#include \"fq.h\"\n#include \"fqd.h\"\n#include \"fqd_private.h\"\n#include \"fq_dtrace.h\"\n\n#define CONFIG_RING_SIZE 3\n#define CONFIG_ROTATE_NS (100*1000*1000) /*100ms*/\n#define DEFAULT_CLIENT_CNT 128\n\nconst char *fq_version_string = FQ_VERSION;\nconst char *fqd_config_path = VARLIBFQDIR \"/fqd.sqlite\";\nconst char *fqd_queue_path = VARLIBFQDIR \"/queues\";\n\n/* A ring of three configs\n *\n * [cycleout] [currentread] [currentwrite]\n *\n */\n\nfqd_exchange_stats_t global_counters = { 0, 0, 0, 0, 0, 0 };\n\nstruct fqd_config {\n  uint64_t gen;\n  int n_clients;\n  remote_client **clients;\n  int n_queues;\n  fqd_queue **queues;\n  int n_exchanges;\n  fqd_exchange **exchanges;\n};\n\nstatic sqlite3 *configdb = NULL;\nstatic uint64_t global_gen = 0;\nstatic uint32_t global_nodeid = 0;\nuint32_t fqd_config_get_nodeid() { return global_nodeid; }\n\ntypedef struct fqd_config_ref {\n  fqd_config        config;\n  uint32_t          readers;\n  uint32_t          dirty;\n} fqd_config_ref;\n\nstatic struct {\n  fqd_config_ref    configs[CONFIG_RING_SIZE];\n\n  /* protected by writelock */\n  pthread_mutex_t   writelock;\n  uint32_t          current_config;\n  /* end writelock protected things */\n} global_config;\n\n#define FQGC(i) global_config.configs[i]\n\nstatic void *config_rotation(void *);\nstatic void setup_config(void);\nstatic void setup_initial_config(void);\n\nvoid\nfqd_config_init(uint32_t nodeid, const char *config_path, const char *qpath) {\n  int i;\n  pthread_t t;\n  pthread_attr_t attr;\n\n  global_nodeid = nodeid;\n  if(config_path) fqd_config_path = config_path;\n  if(qpath) fqd_queue_path = qpath;\n  memset(&global_config, 0, sizeof(global_config));\n\n  pthread_mutex_init(&global_config.writelock, NULL);\n\n  for(i=0;i<CONFIG_RING_SIZE;i++)\n    global_config.configs[i].config.gen = ++global_gen;\n\n  pthread_attr_init(&attr);\n  pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);\n  pthread_create(&t, &attr, config_rotation, NULL);\n\n  setup_config();\n}\n\nextern fqd_config *\nfqd_config_get() {\n  int lcc = global_config.current_config;\n  ck_pr_inc_32(&global_config.configs[lcc].readers);\n  return (fqd_config *)&global_config.configs[lcc];\n}\n\nextern void\nfqd_config_release(fqd_config *fake) {\n  fqd_config_ref *real = (fqd_config_ref *)fake;\n  ck_pr_dec_32(&real->readers);\n}\n\nint\nfqd_config_construct_queue_path(char *path, size_t pathlen,\n                                fq_rk *qname) {\n  int i;\n  char *qout, qhex[MAX_RK_LEN * 2 + 1];\n  qout = qhex;\n  for(i=0; i<qname->len; i++) {\n    snprintf(qout, 3, \"%02x\", (int)qname->name[i]);\n    qout += 2;\n  }\n  *qout = '\\0';\n  return snprintf(path, pathlen, \"%s/%s\", fqd_queue_path, qhex);\n}\n\nfqd_queue *\nfqd_config_get_registered_queue(fqd_config *c, fq_rk *qname) {\n  int i;\n  fqd_queue *q = NULL;\n  for(i=0;i<c->n_queues;i++) {\n    if(c->queues[i] && fq_rk_cmp(qname, fqd_queue_name(c->queues[i])) == 0) {\n      q = c->queues[i];\n      break;\n    }\n  }\n  fq_debug(FQ_DEBUG_CONFIG, \"referencing queue -> (%p)\\n\", (void *)q);\n  return q;\n}\n\nremote_client *\nfqd_config_get_registered_client(fqd_config *c, fq_rk *key) {\n  int i;\n  remote_client *client = NULL;\n  for(i=0;i<c->n_clients;i++) {\n    if(c->clients[i] && fq_rk_cmp(key, &c->clients[i]->key) == 0) {\n      client = c->clients[i];\n      break;\n    }\n  }\n  return client;\n}\n\nfqd_exchange *\nfqd_config_get_exchange(fqd_config *c, fq_rk *exchange) {\n  int i;\n  for(i=0;i<c->n_exchanges;i++)\n    if(c->exchanges[i] &&\n       fq_rk_cmp(exchange, &c->exchanges[i]->exchange) == 0)\n      return c->exchanges[i];\n  return NULL;\n}\n\n/* This is static b/c no one but us should be calling it\n * we we need to hold a lock whilst calling it.\n */\nstatic fqd_exchange *\nfqd_config_add_exchange(fqd_config *c, fq_rk *exchange) {\n  int i;\n  for(i=0;i<c->n_exchanges;i++) {\n    if(c->exchanges[i] == NULL) break;\n    if(fq_rk_cmp(exchange, &c->exchanges[i]->exchange) == 0)\n      return c->exchanges[i];\n  }\n  if(i == c->n_exchanges) {\n    fqd_exchange **nlist;\n    int ncnt = c->n_exchanges * 2;\n    if(ncnt == 0) ncnt = 16;\n    nlist = calloc(ncnt, sizeof(*c->exchanges));\n    if(c->n_exchanges) {\n      memcpy(nlist, c->exchanges, c->n_exchanges * sizeof(*c->exchanges));\n      free(c->exchanges);\n    }\n    c->n_exchanges = ncnt;\n    c->exchanges = nlist;\n  }\n  c->exchanges[i] = calloc(1, sizeof(*c->exchanges[i]));\n  memcpy(&c->exchanges[i]->exchange, exchange, sizeof(*exchange));\n  c->exchanges[i]->stats = calloc(1, sizeof(*c->exchanges[i]->stats));\n  c->exchanges[i]->set = fqd_routemgr_ruleset_alloc();\n  fq_debug(FQ_DEBUG_CONFIG, \"Adding new exchange[%.*s] -> %d\\n\",\n           exchange->len, exchange->name, i);\n  return c->exchanges[i];\n}\n\nvoid fqd_config_wait(uint64_t gen, int us) {\n  while(1) {\n    int which;\n    which = ck_pr_load_uint(&global_config.current_config);\n    if(FQGC(which).config.gen >= gen) return;\n    if(us>0) usleep(us);\n  }\n}\n/* config modification */\n#define BEGIN_CONFIG_MODIFY(conf) \\\n  fqd_config_ref *conf ## _ref; \\\n  fqd_config *conf; \\\n  pthread_mutex_lock(&global_config.writelock); \\\n  conf ## _ref = &FQGC((global_config.current_config + 1) % CONFIG_RING_SIZE); \\\n  conf = &conf ## _ref->config\n#define MARK_CONFIG(conf) do { conf ## _ref->dirty = 1; } while(0)\n#define END_CONFIG_MODIFY() pthread_mutex_unlock(&global_config.writelock)\n\nextern uint32_t\nfqd_config_bind(fq_rk *exchange, uint16_t flags, const char *program,\n                fqd_queue *q, uint64_t *gen) {\n  uint32_t route_id;\n  fqd_exchange *x;\n  fqd_route_rule *rule;\n  int peermode = ((flags & FQ_BIND_PEER) == FQ_BIND_PEER);\n  int isnew = 0;\n  rule = fqd_routemgr_compile(program, peermode, q);\n  if(!rule) return FQ_BIND_ILLEGAL;\n  BEGIN_CONFIG_MODIFY(config);\n  x = fqd_config_get_exchange(config, exchange);\n  if(!x) x = fqd_config_add_exchange(config, exchange);\n  route_id = fqd_routemgr_ruleset_add_rule(x->set, rule, &isnew);\n  if(flags & FQ_BIND_PERM) {\n    if((flags & FQ_BIND_PERM) == FQ_BIND_PERM) {\n      fqd_routemgr_perm_route_id(x->set, route_id);\n    }\n    else if((flags & FQ_BIND_PERM) == FQ_BIND_TRANS) {\n      fqd_routemgr_trans_route_id(x->set, route_id);\n    }\n  }\n  fq_debug(FQ_DEBUG_CONFIG,\n           \"rule %u \\\"%s\\\" for exchange \\\"%.*s\\\" -> Q[%p]\\n\", route_id,\n           program, exchange->len, exchange->name, (void *)q);\n  if(gen) *gen = config->gen;\n  MARK_CONFIG(config);\n  END_CONFIG_MODIFY();\n\n  /* if these bits are set, we have configdb work to do */\n  if(flags & FQ_BIND_PERM) {\n    if((flags & FQ_BIND_PERM) == FQ_BIND_PERM) {\n      fqd_config_make_perm_binding(exchange, q, peermode, program);\n    }\n    else if((flags & FQ_BIND_PERM) == FQ_BIND_TRANS) {\n      fqd_config_make_trans_binding(exchange, q, peermode, program);\n    }\n  }\n  return route_id;\n}\n\nextern int\nfqd_config_unbind(fq_rk *exchange, uint32_t route_id,\n                  fqd_queue *c, uint64_t *gen) {\n  int i, dropped = 0;\n\tBEGIN_CONFIG_MODIFY(config);\n  for(i=0;i<config->n_exchanges;i++) {\n    if(config->exchanges[i] != NULL &&\n       fq_rk_cmp(exchange, &config->exchanges[i]->exchange) == 0) {\n      dropped = fqd_routemgr_drop_rules_by_route_id(config->exchanges[i]->set,\n                                                    c, route_id);\n      if(gen) *gen = config->gen;\n      break;\n    }\n  }\n  if(dropped) MARK_CONFIG(config);\n\tEND_CONFIG_MODIFY();\n  fq_debug(FQ_DEBUG_CONFIG,\n           \"unbind rule %u %s for exchange \\\"%.*s\\\" -> Q[%p]\\n\", route_id,\n           dropped ? \"successful\" : \"failed\", exchange->len, exchange->name,\n           (void *)c);\n  return dropped;\n}\n\nextern int\nfqd_config_register_client(remote_client *c, uint64_t *gen) {\n  int i, rv = 0, available_slot = -1;\n  BEGIN_CONFIG_MODIFY(config);\n  for(i=0; i<config->n_clients; i++) {\n    fq_assert(c != config->clients[i]);\n    if(available_slot == -1 && config->clients[i] == NULL)\n      available_slot = i;\n  }\n  if(available_slot < 0) {\n    remote_client **f;\n    f = calloc(sizeof(*f), config->n_clients + 128);\n    if(f == NULL) goto oom;\n    if(config->n_clients)\n      memcpy(f, config->clients, sizeof(*f) * config->n_clients);\n    available_slot = config->n_clients;\n    config->n_clients += 128;\n    free(config->clients);\n    config->clients = f;\n  }\n  config->clients[available_slot] = c;\n  fq_debug(FQ_DEBUG_CONFIG, \"registering client -> (%p:%s)\\n\", (void *)c, c->pretty);\n  fqd_remote_client_ref(c);\n  if(gen) *gen = config->gen;\n  MARK_CONFIG(config);\n  rv = 0;\n oom:\n  END_CONFIG_MODIFY();\n  return rv;\n}\n\nextern int\nfqd_config_deregister_client(remote_client *c, uint64_t *gen) {\n  int i;\n  remote_client *toderef = NULL;\n  BEGIN_CONFIG_MODIFY(config);\n  for(i=0; i<config->n_clients; i++) {\n    if(c == config->clients[i]) {\n      config->clients[i] = NULL;\n      toderef = c;\n      fq_debug(FQ_DEBUG_CONFIG, \"deregistering client -> (%p:%s)\\n\", (void *)c, c->pretty);\n      break;\n    }\n  }\n  if(i == config->n_clients)\n    fq_debug(FQ_DEBUG_CONFIG,\n             \"FAILED deregistering client -> (%p:%s)\\n\", (void *)c, c->pretty);\n  fq_assert(i != config->n_clients);\n  MARK_CONFIG(config);\n  if(gen) *gen = config->gen;\n  END_CONFIG_MODIFY();\n\n  if(toderef) {\n    /* Do this work without holding the lock */\n    if(toderef->queue) {\n      if(fqd_queue_deregister_client(toderef->queue, c)) {\n        fqd_config_deregister_queue(toderef->queue, NULL);\n      }\n    }\n    toderef->queue = NULL;\n    fqd_remote_client_deref(toderef);\n  }\n  return 0;\n}\n\nextern fqd_queue *\nfqd_config_register_queue(fqd_queue *c, uint64_t *gen) {\n  int i, available_slot = -1;\n  BEGIN_CONFIG_MODIFY(config);\n  for(i=0; i<config->n_queues; i++) {\n    if(config->queues[i] && fqd_queue_cmp(c, config->queues[i]) == 0) {\n      if(gen) *gen = config->gen;\n      c = config->queues[i];\n      goto out;\n    }\n    if(available_slot == -1 && config->queues[i] == NULL)\n      available_slot = i;\n  }\n  if(available_slot < 0) {\n    fqd_queue **f;\n    f = calloc(sizeof(*f), config->n_queues + 128);\n    if(f == NULL) goto out;\n    if(config->n_queues)\n      memcpy(f, config->queues, sizeof(*f) * config->n_queues);\n    available_slot = config->n_queues;\n    config->n_queues += 128;\n    free(config->queues);\n    config->queues = f;\n  }\n  config->queues[available_slot] = c;\n  fqd_queue_ref(c);\n  if(gen) *gen = config->gen;\n  MARK_CONFIG(config);\n out:\n  END_CONFIG_MODIFY();\n  fq_debug(FQ_DEBUG_CONFIG, \"registering queue (%s) -> (%p:%.*s)\\n\",\n           (available_slot == -1) ? \"old\" : \"new\", (void *)c,\n            c->name.len, c->name.name);\n  return c;\n}\n\nextern int\nfqd_config_deregister_queue(fqd_queue *c, uint64_t *gen) {\n  int i;\n  fqd_queue *toderef = NULL;\n  BEGIN_CONFIG_MODIFY(config);\n  for(i=0; i<config->n_queues; i++) {\n    if(config->queues[i] && fqd_queue_cmp(c, config->queues[i]) == 0) {\n      config->queues[i] = NULL;\n      toderef = c;\n      fq_debug(FQ_DEBUG_CONFIG, \"deregistering queue -> (%p:%.*s)\\n\", (void *)c, c->name.len, c->name.name);\n      break;\n    }\n  }\n  if(toderef) {\n    for(i=0;i<config->n_exchanges;i++) {\n      if(config->exchanges[i] != NULL) {\n        fqd_routemgr_drop_rules_by_queue(config->exchanges[i]->set, toderef);\n      }\n    }\n    MARK_CONFIG(config);\n  }\n  else {\n    fq_debug(FQ_DEBUG_CONFIG, \"FAILED deregistering queue -> (%p:%.*s)\\n\", (void *)c, c->name.len, c->name.name);\n  }\n  if(gen) *gen = config->gen;\n  END_CONFIG_MODIFY();\n  if(toderef)\n    fqd_queue_deref(toderef);\n  return 0;\n}\n\n/* This section deals with managing the rings */\nstatic void\nfqd_internal_copy_config(fqd_config_ref *src, fqd_config_ref *tgt) {\n  int i;\n  /* First clients */\n  if(tgt->config.clients) {\n    for(i=0;i<tgt->config.n_clients;i++)\n      if(tgt->config.clients[i])\n        fqd_remote_client_deref(tgt->config.clients[i]);\n    free(tgt->config.clients);\n    tgt->config.clients = NULL;\n  }\n  if(src->config.clients) {\n    tgt->config.n_clients = src->config.n_clients;\n    tgt->config.clients =\n      malloc(sizeof(*tgt->config.clients) * tgt->config.n_clients);\n    fq_assert(tgt->config.clients);\n    memcpy(tgt->config.clients, src->config.clients,\n           sizeof(*tgt->config.clients) * tgt->config.n_clients);\n    for(i=0;i<tgt->config.n_clients;i++)\n      if(tgt->config.clients[i])\n        fqd_remote_client_ref(tgt->config.clients[i]);\n  }\n\n  /* Now the same thing of queues */\n  if(tgt->config.queues) {\n    for(i=0;i<tgt->config.n_queues;i++)\n      if(tgt->config.queues[i])\n        fqd_queue_deref(tgt->config.queues[i]);\n    free(tgt->config.queues);\n    tgt->config.queues = NULL;\n  }\n  if(src->config.queues) {\n    tgt->config.n_queues = src->config.n_queues;\n    tgt->config.queues =\n      malloc(sizeof(*tgt->config.queues) * tgt->config.n_queues);\n    fq_assert(tgt->config.queues);\n    memcpy(tgt->config.queues, src->config.queues,\n           sizeof(*tgt->config.queues) * tgt->config.n_queues);\n    for(i=0;i<tgt->config.n_queues;i++)\n      if(tgt->config.queues[i])\n        fqd_queue_ref(tgt->config.queues[i]);\n  }\n\n  /* next the exchang/routemaps */\n  if(tgt->config.exchanges) {\n    for(i=0;i<tgt->config.n_exchanges;i++) {\n      if(tgt->config.exchanges[i] && tgt->config.exchanges[i]->set) {\n        fqd_routemgr_ruleset_free(tgt->config.exchanges[i]->set);\n        free(tgt->config.exchanges[i]);\n      }\n    }\n    free(tgt->config.exchanges);\n    tgt->config.exchanges = NULL;\n  }\n  if(src->config.exchanges) {\n    tgt->config.n_exchanges = src->config.n_exchanges;\n    tgt->config.exchanges =\n      malloc(sizeof(*tgt->config.exchanges) * tgt->config.n_exchanges);\n    fq_assert(tgt->config.exchanges);\n    for(i=0;i<tgt->config.n_exchanges;i++) {\n      if(src->config.exchanges[i]) {\n        tgt->config.exchanges[i] = malloc(sizeof(*tgt->config.exchanges[i]));\n        memcpy(tgt->config.exchanges[i], src->config.exchanges[i],\n               sizeof(*tgt->config.exchanges[i]));\n        tgt->config.exchanges[i]->set =\n          fqd_routemgr_ruleset_copy(src->config.exchanges[i]->set);\n      }\n      else tgt->config.exchanges[i] = NULL;\n    }\n  }\n}\n\nstatic void\nfixup_config_write_context(void) {\n  uint32_t current, next, nextnext;\n\n  current = global_config.current_config;\n  next = (current + 1) % CONFIG_RING_SIZE;\n  nextnext = (current + 2) % CONFIG_RING_SIZE;\n\n  FQ_CONFIG_ROTATE(FQGC(next).dirty);\n  if(!FQGC(next).dirty) return;\n\n  fq_debug(FQ_DEBUG_CONFIG, \"Swapping to next running config\\n\");\n  pthread_mutex_lock(&global_config.writelock);\n\n  /* We've locked writing... let the world use the new config */\n  global_config.current_config = next;\n\n  /* Wait until the next(next) has no readers so we can copy into it */\n  while(ck_pr_load_uint(&FQGC(nextnext).readers) != 0)\n    ck_pr_stall();\n\n  /* Safe to do the copy */\n  fqd_internal_copy_config(&FQGC(next), &FQGC(nextnext));\n  /* Mark that new write target as clean */\n  FQGC(nextnext).config.gen = ++global_gen;\n  FQGC(nextnext).dirty = 0;\n\n  pthread_mutex_unlock(&global_config.writelock);\n  fq_debug(FQ_DEBUG_CONFIG, \"Swapped to next running config\\n\");\n}\n\nstatic void *config_rotation(void *unused) {\n  fq_thread_setname(\"fqd:config\");\n  fqd_bcd_attach();\n  while(1) {\n    fixup_config_write_context();\n    usleep(CONFIG_ROTATE_NS / 1000);\n  }\n  (void)unused;\n  return NULL;\n}\n\n#define cprintf(client, fmt, ...) do { \\\n  char scratch[1024]; \\\n  int len; \\\n  len = snprintf(scratch, sizeof(scratch), fmt, __VA_ARGS__); \\\n  while(write(client->fd, scratch, len) == -1 && errno == EINTR); \\\n} while(0)\n#define cwrite(client, str) write(client->fd, str, strlen(str))\n\nint fqd_config_http_routes(struct fqd_route_rule *r, int rv, void *closure) {\n  remote_client *client = closure;\n  char *program_encoded, *cp, *tcp;\n  int len;\n\n  len = strlen(r->program)*2+1;\n  program_encoded = malloc(len);\n  for(cp = r->program, tcp = program_encoded; *cp; cp++) {\n    switch(*cp) {\n      case '\\n': *tcp++ = '\\\\'; *tcp++ = 'n'; break;\n      case '\\r': *tcp++ = '\\\\'; *tcp++ = 'r'; break;\n      case '\\t': *tcp++ = '\\\\'; *tcp++ = 't'; break;\n\n      case '\\\\':\n      case '\\\"':\n        *tcp++ = '\\\\'; *tcp++ = *cp; break;\n      default: *tcp++ = *cp; break;\n    }\n  }\n  *tcp = '\\0';\n\n  cprintf(client, \"   %s\\\"%u\\\": {\\n\", rv ? \",\" : \" \", r->route_id);\n  cprintf(client, \"     \\\"route_id\\\": %u,\\n\", r->route_id);\n  cprintf(client, \"     \\\"prefix\\\": \\\"%.*s\\\",\\n\", r->prefix.len, r->prefix.name);\n  cprintf(client, \"     \\\"queue\\\": \\\"%.*s\\\",\\n\", r->queue->name.len, r->queue->name.name);\n  cprintf(client, \"     \\\"permanent\\\": %s,\\n\", r->permanent ? \"true\" : \"false\");\n  cprintf(client, \"     \\\"invocations\\\": %llu,\\n\", (unsigned long long)r->stats->invocations);\n  cprintf(client, \"     \\\"avg_ns\\\": %u,\\n\", r->stats->avg_ns);\n  cprintf(client, \"     \\\"program\\\": \\\"%s\\\"\\n\", program_encoded);\n  cwrite(client, \"    }\\n\");\n  free(program_encoded);\n  return 1;\n}\nvoid fqd_config_http_stats(remote_client *client) {\n  int i;\n  const char *headers = \"HTTP/1.0 200 OK\\r\\nConnection: close\\r\\nContent-Type: application/json\\r\\n\\r\\n\";\n  fqd_config *config;\n  while(write(client->fd, headers, strlen(headers)) == -1 && errno == EINTR);\n  config = fqd_config_get();\n  cwrite(client, \"{\\n\");\n  cprintf(client, \" \\\"version\\\": \\\"%s\\\",\\n\", fq_version_string);\n  cwrite(client, \" \\\"exchanges\\\": {\\n\");\n  for(i=0;i<config->n_exchanges;i++) {\n    if(config->exchanges[i]) {\n      fqd_exchange *e = config->exchanges[i];\n      cprintf(client, \"  \\\"%.*s\\\": {\\n\", e->exchange.len, e->exchange.name);\n      cprintf(client, \"   \\\"messages\\\": %llu,\\n\", (long long unsigned int) e->stats->n_messages);\n      cprintf(client, \"   \\\"octets\\\": %llu,\\n\", (long long unsigned int) e->stats->n_bytes);\n      cprintf(client, \"   \\\"no_route\\\": %llu,\\n\", (long long unsigned int) e->stats->n_no_route);\n      cprintf(client, \"   \\\"routed\\\": %llu,\\n\", (long long unsigned int) e->stats->n_routed);\n      cprintf(client, \"   \\\"dropped\\\": %llu,\\n\", (long long unsigned int) e->stats->n_dropped);\n      cwrite(client, \"   \\\"routes\\\": {\\n\");\n      for_each_route_rule_do(e->set, fqd_config_http_routes, client);\n      cwrite(client, \"   }\\n\");\n      cwrite(client, \"  },\\n\");\n    }\n  }\n  cwrite(client, \"  \\\"_aggregate\\\": {\\n\");\n  cprintf(client, \"   \\\"no_exchange\\\": %llu,\\n\", (long long unsigned int) global_counters.n_no_exchange);\n  cprintf(client, \"   \\\"messages\\\": %llu,\\n\", (long long unsigned int) global_counters.n_messages);\n  cprintf(client, \"   \\\"octets\\\": %llu,\\n\", (long long unsigned int) global_counters.n_bytes);\n  cprintf(client, \"   \\\"no_route\\\": %llu,\\n\", (long long unsigned int) global_counters.n_no_route);\n  cprintf(client, \"   \\\"routed\\\": %llu,\\n\", (long long unsigned int) global_counters.n_routed);\n  cprintf(client, \"   \\\"dropped\\\": %llu,\\n\", (long long unsigned int) global_counters.n_dropped);\n  cprintf(client, \"   \\\"size_dropped\\\": %llu\\n\", (long long unsigned int) global_counters.n_size_dropped);\n  cwrite(client, \"  }\\n\");\n  cwrite(client, \" },\\n\");\n  cwrite(client, \" \\\"queues\\\": {\\n\");\n  int seen = 0;\n  for(i=0;i<config->n_queues;i++) {\n    if(config->queues[i]) {\n      fqd_queue *q = config->queues[i];\n      fq_rk *qname = fqd_queue_name(q);\n      if(seen++) cwrite(client, \",\\n\");\n      cprintf(client, \"  \\\"%.*s\\\": \\n\", qname->len, qname->name);\n      fqd_queue_write_json(client->fd, q);\n    }\n  }\n  cwrite(client, \" }\\n\");\n  cwrite(client, \"}\\n\");\n  fqd_config_release(config);\n}\n\nvoid fqd_size_dropped(uint64_t n) {\n  ck_pr_add_64(&global_counters.n_size_dropped, n);\n}\nvoid fqd_exchange_messages(fqd_exchange *e, uint64_t n) {\n  if(e) ck_pr_add_64(&e->stats->n_messages, n);\n  ck_pr_add_64(&global_counters.n_messages, n);\n}\nvoid fqd_exchange_message_octets(fqd_exchange *e, uint64_t n) {\n  if(e) ck_pr_add_64(&e->stats->n_bytes, n);\n  ck_pr_add_64(&global_counters.n_bytes, n);\n}\nvoid fqd_exchange_no_route(fqd_exchange *e, uint64_t n) {\n  if(e) ck_pr_add_64(&e->stats->n_no_route, n);\n  ck_pr_add_64(&global_counters.n_no_route, n);\n}\nvoid fqd_exchange_routed(fqd_exchange *e, uint64_t n) {\n  fq_assert(e);\n  ck_pr_add_64(&e->stats->n_routed, n);\n  ck_pr_add_64(&global_counters.n_routed, n);\n}\nvoid fqd_exchange_dropped(fqd_exchange *e, uint64_t n) {\n  if(e) ck_pr_add_64(&e->stats->n_dropped, n);\n  ck_pr_add_64(&global_counters.n_dropped, n);\n}\nvoid fqd_exchange_no_exchange(fqd_exchange *e, uint64_t n) {\n  fq_assert(!e);\n  ck_pr_add_64(&global_counters.n_no_exchange, n);\n}\n\n#define bail(...) do {fprintf(stderr, __VA_ARGS__); exit(-2);} while(0)\n\nstatic void setup_initial_config() {\n  char *SQL, *errmsg = NULL;\n  int rv;\n  int flags = SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE|SQLITE_OPEN_EXCLUSIVE;\n  if((rv = sqlite3_open_v2(fqd_config_path, &configdb, flags, NULL)) != 0)\n    bail(\"... failed to open %s: %s\\n\", fqd_config_path,\n         sqlite3_errmsg(configdb));\n\n  sqlite3_exec(configdb, \"PRAGMA foreign_keys = ON\", 0, 0, &errmsg);\n  if(errmsg) bail(\"sqlite error: %s\\n\", sqlite3_errmsg(configdb));\n\n  SQL = sqlite3_mprintf(\n    \"CREATE TABLE queue (name TEXT NOT NULL PRIMARY KEY,\"\n    \" type TEXT NOT NULL DEFAULT \\\"mem\\\", attributes TEXT)\"\n  );\n  sqlite3_exec(configdb, SQL, 0, 0, &errmsg);\n  sqlite3_free(SQL);\n  if(errmsg && strcmp(errmsg, \"table queue already exists\"))\n    bail(\"sqlite error: %s\\n\", sqlite3_errmsg(configdb));\n  if(errmsg) sqlite3_free(errmsg);\n\n  SQL = sqlite3_mprintf(\n    \"CREATE TABLE binding ( \"\n    \" exchange TEXT NOT NULL, \"\n    \" queue TEXT NOT NULL, \"\n    \" peermode BOOLEAN NOT NULL DEFAULT FALSE, program TEXT, \"\n    \" UNIQUE(exchange, queue, peermode, program), \"\n    \" FOREIGN KEY(queue) REFERENCES queue(name) \"\n    \")\"\n  );\n  sqlite3_exec(configdb, SQL, 0, 0, &errmsg);\n  sqlite3_free(SQL);\n  if(errmsg && strcmp(errmsg, \"table binding already exists\"))\n    bail(\"sqlite error: %s\\n\", sqlite3_errmsg(configdb));\n  if(errmsg) sqlite3_free(errmsg);\n\n  SQL = sqlite3_mprintf(\n    \"CREATE TABLE upstream ( \"\n    \" host TEXT NOT NULL, \"\n    \" port INTEGER NOT NULL DEFAULT 8765, \"\n    \" source TEXT NOT NULL, \"\n    \" password TEXT NOT NULL, \"\n    \" exchange TEXT NOT NULL, \"\n    \" program TEXT NOT NULL DEFAULT '', \"\n    \" permanent_binding BOOLEAN NOT NULL DEFAULT FALSE, \"\n    \" UNIQUE(host, port, source, password, exchange, program, permanent_binding) \"\n    \")\"\n  );\n  sqlite3_exec(configdb, SQL, 0, 0, &errmsg);\n  sqlite3_free(SQL);\n  if(errmsg && strcmp(errmsg, \"table upstream already exists\"))\n    bail(\"sqlite error: %s\\n\", sqlite3_errmsg(configdb));\n  if(errmsg) sqlite3_free(errmsg);\n}\n\nint fqd_config_make_perm_queue(fqd_queue *q) {\n  sqlite3_stmt *stmt;\n  fq_rk *qname;\n  const char *insertSQL;\n  char qtype[1024], *attrs;\n  fqd_queue_sprint(qtype, sizeof(qtype), q);\n  attrs = strchr(qtype, ':');\n  if(attrs == NULL) return -1;\n  *attrs++ = '\\0';\n  insertSQL = \"INSERT INTO queue VALUES(?,?,?)\";\n  qname = fqd_queue_name(q);\n  sqlite3_prepare_v2(configdb, insertSQL, strlen(insertSQL), &stmt, NULL);\n  sqlite3_bind_text(stmt, 1, (char *)qname->name, qname->len, NULL);\n  sqlite3_bind_text(stmt, 2, qtype, strlen(qtype), NULL);\n  sqlite3_bind_text(stmt, 3, attrs, strlen(attrs), NULL);\n  switch(sqlite3_step(stmt)) {\n    case SQLITE_DONE:\n      if(sqlite3_changes(configdb) > 0) {\n        fq_debug(FQ_DEBUG_CONFIG, \"Queue %.*s made permanent\\n\",\n                 qname->len, qname->name);\n        fqd_queue_ref(q);\n      }\n      break;\n    default:\n      fq_debug(FQ_DEBUG_CONFIG, \"Queue %.*s not made permanent: %s\\n\",\n               qname->len, qname->name, sqlite3_errmsg(configdb));\n      break;\n  }\n  sqlite3_finalize(stmt);\n  return 0;\n}\n\nint fqd_config_make_trans_queue(fqd_queue *q) {\n  sqlite3_stmt *stmt;\n  fq_rk *qname;\n  const char *insertSQL;\n  char qtype[1024], *attrs;\n  fqd_queue_sprint(qtype, sizeof(qtype), q);\n  attrs = strchr(qtype, ':');\n  if(attrs == NULL) return -1;\n  *attrs++ = '\\0';\n  insertSQL = \"DELETE FROM queue WHERE name = ?\";\n  qname = fqd_queue_name(q);\n  sqlite3_prepare_v2(configdb, insertSQL, strlen(insertSQL), &stmt, NULL);\n  sqlite3_bind_text(stmt, 1, (char *)qname->name, qname->len, NULL);\n  switch(sqlite3_step(stmt)) {\n    case SQLITE_DONE:\n      if(sqlite3_changes(configdb) > 0) {\n        fq_debug(FQ_DEBUG_CONFIG, \"Queue %.*s made transient\\n\",\n                 qname->len, qname->name);\n        fqd_queue_deref(q);\n        break;\n      }\n      fq_debug(FQ_DEBUG_CONFIG, \"Queue %.*s not made transient: not found\\n\",\n               qname->len, qname->name);\n      break;\n    default:\n      fq_debug(FQ_DEBUG_CONFIG, \"Queue %.*s not made transient: %s\\n\",\n               qname->len, qname->name, sqlite3_errmsg(configdb));\n      break;\n  }\n  sqlite3_finalize(stmt);\n  return 0;\n}\nstatic int sql_make_queues(void *c, int n, char **row, char **col) {\n  fqd_queue *queue;\n  char err[1024];\n  fq_rk q;\n  fq_assert(n == 3);\n  (void)c;\n  (void)col;\n  q.len = strlen(row[0]);\n  if(q.len != strlen(row[0])) return 0;\n  memcpy(q.name, row[0], q.len);\n \n  queue = fqd_queue_get(&q, row[1], row[2],  sizeof(err), err);\n  if(!queue) {\n    fprintf(stderr, \"queue(%s) -> %s\\n\", row[0], err);\n    return 0;\n  }\n  fqd_queue_ref(queue);\n  return 0;\n}\n\nstatic uint64_t peer_generation = 0;\nstatic int sql_make_peers(void *c, int n, char **row, char **col) {\n  fq_rk exchange;\n  char *host = row[0], *source = row[2], *pass = row[3], *prog = row[5];\n  int port = atoi(row[1]);\n  bool perm = !strcmp(row[6],\"true\");\n\n  exchange.len = strlen(row[4]);\n  if(exchange.len != strlen(row[4])) return 0;\n  memcpy(exchange.name, row[4], exchange.len);\n\n  fqd_add_peer(peer_generation, host, port, source, pass, &exchange, prog, perm);\n  return 0;\n}\nint fqd_config_make_perm_binding(fq_rk *exchange, fqd_queue *q,\n                                 int peermode, const char *program) {\n  sqlite3_stmt *stmt;\n  fq_rk *qname;\n  const char *insertSQL;\n  const char *pmstr = peermode ? \"true\" : \"false\";\n  char qtype[1024], *attrs;\n  fqd_queue_sprint(qtype, sizeof(qtype), q);\n  attrs = strchr(qtype, ':');\n  if(attrs == NULL) return -1;\n  *attrs++ = '\\0';\n  insertSQL = \"INSERT INTO binding (exchange,queue,peermode,program) \"\n              \"VALUES(?,?,?,?)\";\n  qname = fqd_queue_name(q);\n  sqlite3_prepare_v2(configdb, insertSQL, strlen(insertSQL), &stmt, NULL);\n  sqlite3_bind_text(stmt, 1, (char *)exchange->name, exchange->len, NULL);\n  sqlite3_bind_text(stmt, 2, (char *)qname->name, qname->len, NULL);\n  sqlite3_bind_text(stmt, 3, pmstr, strlen(pmstr), NULL);\n  sqlite3_bind_text(stmt, 4, program, strlen(program), NULL);\n  switch(sqlite3_step(stmt)) {\n    case SQLITE_DONE:\n      if(sqlite3_changes(configdb) > 0) {\n        fq_debug(FQ_DEBUG_CONFIG, \"Binding %.*s made permanent\\n\",\n                 qname->len, qname->name);\n        fqd_queue_ref(q);\n      }\n      break;\n    default:\n      fq_debug(FQ_DEBUG_CONFIG, \"Binding %.*s not made permanent: %s\\n\",\n               qname->len, qname->name, sqlite3_errmsg(configdb));\n      break;\n  }\n  sqlite3_finalize(stmt);\n  return 0;\n}\nint fqd_config_make_trans_binding(fq_rk *exchange, fqd_queue *q,\n                                  int peermode, const char *program) {\n  sqlite3_stmt *stmt;\n  fq_rk *qname;\n  const char *delSQL;\n  const char *pmstr = peermode ? \"true\" : \"false\";\n  char qtype[1024], *attrs;\n  fqd_queue_sprint(qtype, sizeof(qtype), q);\n  attrs = strchr(qtype, ':');\n  if(attrs != NULL) *attrs++ = '\\0';\n  delSQL = \"DELETE FROM binding WHERE exchange=? AND queue=? \"\n              \"  AND peermode=? AND program=?\";\n  qname = fqd_queue_name(q);\n  sqlite3_prepare_v2(configdb, delSQL, strlen(delSQL), &stmt, NULL);\n  sqlite3_bind_text(stmt, 1, (char *)exchange->name, exchange->len, NULL);\n  sqlite3_bind_text(stmt, 2, (char *)qname->name, qname->len, NULL);\n  sqlite3_bind_text(stmt, 3, pmstr, strlen(pmstr), NULL);\n  sqlite3_bind_text(stmt, 4, program, strlen(program), NULL);\n  switch(sqlite3_step(stmt)) {\n    case SQLITE_DONE:\n      if(sqlite3_changes(configdb) > 0) {\n        fq_debug(FQ_DEBUG_CONFIG, \"Binding %.*s made transient\\n\",\n                 qname->len, qname->name);\n        fqd_queue_ref(q);\n        break;\n      }\n      fq_debug(FQ_DEBUG_CONFIG, \"Binding %.*s not made transient: not found\\n\",\n               qname->len, qname->name);\n      break;\n    default:\n      fq_debug(FQ_DEBUG_CONFIG, \"Binding %.*s not made transient: %s\\n\",\n               qname->len, qname->name, sqlite3_errmsg(configdb));\n      break;\n  }\n  sqlite3_finalize(stmt);\n  return 0;\n}\n\nstatic int sql_make_bindings(void *c, int n, char **row, char **col) {\n  int *nbindings = (int *)c;\n  fqd_queue *queue;\n  fq_rk q, x;\n  uint16_t flags;\n\n  fq_assert(n == 4);\n  (void)c;\n  (void)col;\n\n  x.len = strlen(row[0]);\n  if(x.len != strlen(row[0])) return 0;\n  memcpy(x.name, row[0], x.len);\n\n  q.len = strlen(row[1]);\n  if(q.len != strlen(row[1])) return 0;\n  memcpy(q.name, row[1], q.len);\n\n  BEGIN_CONFIG_MODIFY(config);\n  queue = fqd_config_get_registered_queue(config, &q);\n  MARK_CONFIG(config);\n  END_CONFIG_MODIFY();\n\n  if(queue == NULL) return 1;\n  flags = !strcmp(row[2],\"true\") ? FQ_BIND_PEER : 0;\n  flags |= FQ_BIND_PERM;\n  fqd_config_bind(&x, flags, row[3], queue, NULL);\n  (*nbindings)++;\n  return 0;\n}\nstatic void\nfqd_refresh_peers(bool fatal) {\n  char *errmsg = NULL;\n  sqlite3_exec(configdb,\n    \"SELECT host, port, source, password, exchange, program, permanent_binding FROM upstream\",\n    sql_make_peers, NULL, &errmsg\n  );\n  if(errmsg) {\n    if(fatal) bail(\"sqlite error: %s\\n\", sqlite3_errmsg(configdb));\n    fq_debug(FQ_DEBUG_PEER, \"sqlite error: %s\\n\", sqlite3_errmsg(configdb));\n  }\n}\nstatic void *\nfqd_peer_config_maintenance(void *c) {\n  fq_thread_setname(\"fqd:peer_config\");\n  fqd_bcd_attach();\n  while(1) {\n    peer_generation++;\n    fqd_refresh_peers(0);\n    fqd_remove_peers(peer_generation);\n    sleep(1);\n  }\n  return NULL;\n}\n\nstatic void setup_config() {\n  pthread_t tid;\n  pthread_attr_t attr;\n  fqd_config *config;\n  int i, nexchanges = 0, nqueues = 0, nbindings = 0;\n  char *errmsg = NULL;\n  int flags = SQLITE_OPEN_READWRITE|SQLITE_OPEN_EXCLUSIVE;\n\n  if(sqlite3_config(SQLITE_CONFIG_SERIALIZED) != SQLITE_OK) {\n    bail(\"... failed to set sqlite3 threadsafety\\n\");\n  }\n\n  fprintf(stderr, \"Opening configdb %s\\n\", fqd_config_path);\n  if(sqlite3_open_v2(fqd_config_path, &configdb, flags, NULL)) {\n    flags = SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE|SQLITE_OPEN_EXCLUSIVE;\n    if(sqlite3_open_v2(fqd_config_path, &configdb, flags, NULL))\n      bail(\"... failed to open %s: %s\\n\", fqd_config_path,\n           sqlite3_errmsg(configdb));\n  }\n  setup_initial_config();\n\n  sqlite3_exec(configdb,\n    \"SELECT name, type, attributes FROM queue\",\n    sql_make_queues, NULL, &errmsg\n  );\n  if(errmsg) bail(\"sqlite error: %s\\n\", sqlite3_errmsg(configdb));\n\n  sqlite3_exec(configdb,\n    \"SELECT exchange, queue, peermode, program FROM binding\",\n    sql_make_bindings, &nbindings, &errmsg\n  );\n  if(errmsg) bail(\"sqlite error: %s\\n\", sqlite3_errmsg(configdb));\n\n  /* Summarize */\n  {\n  BEGIN_CONFIG_MODIFY(tc);\n  (void)tc;\n  MARK_CONFIG(tc);\n  END_CONFIG_MODIFY();\n  }\n  fqd_config_wait(global_gen-1, 1000);\n  config = fqd_config_get();\n  for(i=0;i<config->n_exchanges;i++) if(config->exchanges[i]) nexchanges++;\n  for(i=0;i<config->n_queues;i++) if(config->queues[i]) nqueues++;\n  fprintf(stderr, \"Established %d exchanges, %d queues, %d bindings\\n\",\n          nexchanges, nqueues, nbindings);\n  fqd_config_release(config);\n\n  pthread_attr_init(&attr);\n  pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);\n  pthread_create(&tid, &attr, fqd_peer_config_maintenance, NULL);\n}\n"
  },
  {
    "path": "fqd_dss.c",
    "content": "/*\n * Copyright (c) 2013 OmniTI Computer Consulting, Inc.\n * All rights reserved.\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\n#include \"fq.h\"\n#include \"fqd.h\"\n#include \"fqd_private.h\"\n#include \"fq_dtrace.h\"\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <unistd.h>\n#include <time.h>\n#include <errno.h>\n#include <fcntl.h>\n#include <signal.h>\n#include <poll.h>\n#include <pthread.h>\n#include <inttypes.h>\n\n#include <ck_fifo.h>\n#include <ck_pr.h>\n\n#ifdef linux\n#include <linux/futex.h>\n#include <sys/time.h>\n#include <sys/syscall.h>\n\nstatic int\nfutex(int *uaddr, int futex_op, int val, const struct timespec *timeout,\n      int *uaddr2, int val3)\n{\n  return (int)syscall(SYS_futex, uaddr, futex_op, val, timeout,\n          uaddr2, val3);\n}\n#endif\n\n#define IN_READ_BUFFER_SIZE 1024*256\n#define MAX_STALL_ATTEMPTS 10000\n\nstatic ck_fifo_spsc_t *work_queues;\nstatic uint32_t *work_queue_backlogs;\n#ifdef linux\nstatic int *work_queue_waiting;\nstatic int *work_queue_futexes;\n#endif\nstatic int *worker_thread_ids;\nstatic int worker_thread_count = 0;\nstatic pthread_t *worker_threads;\nstatic bool worker_thread_shutdown = false;\n\nstruct incoming_message\n{\n  remote_data_client *client;\n  fq_msg *msg;\n};\n\nvoid *\nfqd_worker_thread(void *arg)\n{\n  struct incoming_message *m;\n  int tindex = *(int*) arg;\n  uint64_t empty_retries = 0;\n\n  ck_fifo_spsc_t *q = &work_queues[tindex];\n  uint32_t *backlog = &work_queue_backlogs[tindex];\n\n  fq_thread_setname(\"fqd:worker/%d\", tindex);\n\n  fqd_bcd_attach();\n\n  while (!worker_thread_shutdown) {\n    if (!CK_FIFO_SPSC_ISEMPTY(q)) {\n      bool success;\n      ck_fifo_spsc_dequeue_lock(q);\n      success = ck_fifo_spsc_dequeue(q, &m);\n      ck_fifo_spsc_dequeue_unlock(q);\n      if (success) {\n        empty_retries = 0;\n        if (m != NULL) {\n          ck_pr_dec_32(backlog);\n          fq_msg *copy = fq_msg_alloc_BLANK(m->msg->payload_len);\n          if (copy == NULL) {\n            continue;\n          }\n          memcpy(copy, m->msg, sizeof(fq_msg) + m->msg->payload_len);\n          /* reset the refcnt on the copy since the memcpy above will have overwritten it */\n          copy->refcnt = 1;\n          /* the copy will be freed as normal so eliminate cleanup_stack pointer */\n          copy->free_fn = NULL;\n          copy->cleanup_handle = NULL;\n\n          /* we are done with the incoming message, deref */\n          fq_msg_deref(m->msg);\n\n          fqd_inject_message(m->client, copy);\n\t  fqd_remote_client_deref((remote_client *)m->client);\n          free(m);\n        }\n      }\n    } else {\n      empty_retries++;\n      if(empty_retries > MAX_STALL_ATTEMPTS) {\n#ifdef linux\n        ck_pr_store_int(&work_queue_waiting[tindex], 1);\n        int cv = ck_pr_load_int(&work_queue_futexes[tindex]);\n        struct timespec timeout = { .tv_sec = 1, .tv_nsec = 0 };\n        int rv = futex(&work_queue_futexes[tindex], FUTEX_WAIT_PRIVATE, cv, &timeout, NULL, 0);\n        fq_debug(FQ_DEBUG_MSG, \"retries: %\" PRId64 \", futex -> %d/%s\\n\", empty_retries, rv, strerror(errno));\n#else\n        /* slow-stall 100ms */\n        const struct timespec timeout = { .tv_sec = 0, .tv_nsec = 100 * 1000 * 1000 };\n        nanosleep(&timeout, NULL);\n#endif\n      }\n      else {\n        /* fast-stall 1us */\n        const struct timespec timeout = { .tv_sec = 0, .tv_nsec = 1000 };\n        nanosleep(&timeout, NULL);\n      }\n    }\n  }\n\n  /* drain the queue before exiting */\n  if (!CK_FIFO_SPSC_ISEMPTY(q)) {\n    bool success;\n    ck_fifo_spsc_dequeue_lock(q);\n    success = ck_fifo_spsc_dequeue(q, &m);\n    ck_fifo_spsc_dequeue_unlock(q);\n    if (success) {\n      if (m != NULL) {\n        ck_pr_dec_32(backlog);\n        fq_msg *copy = fq_msg_alloc_BLANK(m->msg->payload_len);\n        memcpy(copy, m->msg, sizeof(fq_msg) + m->msg->payload_len);\n        /* the copy will be freed as normal so eliminate cleanup_stack pointer */\n        copy->cleanup_handle = NULL;\n        /* we are done with the incoming message, drop it on it's cleanup stack */\n        fqd_inject_message(m->client, copy);\n        fq_msg_deref(m->msg);\n        free(m);\n      }\n    }\n  }\n  usleep(10);\n  return NULL;\n}\n\nvoid \nfqd_start_worker_threads(int thread_count) \n{\n  int i = 0;\n  worker_thread_count = thread_count;\n  work_queues = calloc(thread_count, sizeof(ck_fifo_spsc_t));\n#ifdef linux\n  work_queue_futexes = calloc(thread_count, sizeof(int));\n  work_queue_waiting = calloc(thread_count, sizeof(int));\n#endif\n  work_queue_backlogs = calloc(thread_count, sizeof(uint32_t));\n  worker_threads = calloc(thread_count, sizeof(pthread_t));\n  worker_thread_ids = calloc(thread_count, sizeof(int));\n  \n  for ( ; i < thread_count; i++) {\n    worker_thread_ids[i] = i;\n    ck_fifo_spsc_init(&work_queues[i], malloc(sizeof(ck_fifo_spsc_entry_t)));\n    pthread_create(&worker_threads[i], NULL, fqd_worker_thread, &worker_thread_ids[i]);\n  }\n}\n\nvoid \nfqd_stop_worker_threads() \n{\n  int i = 0;\n  worker_thread_shutdown = true;\n\n  for ( ; i < worker_thread_count; i++) {\n    pthread_join(worker_threads[i], NULL);\n  }\n}\n\nstatic void \nfqd_queue_message_process(remote_data_client *me, fq_msg *msg)\n{\n  ck_fifo_spsc_t *work_queue = NULL;\n  ck_fifo_spsc_entry_t *entry = NULL, *tofree = NULL;\n  int tindex = 0;\n  struct incoming_message *m = malloc(sizeof(struct incoming_message));\n  \n  m->client = me;\n  m->msg = msg;\n\n  /* while we live in this queue, we ref the client so it can't be destroyed until the queue is cleared of it */\n  fqd_remote_client_ref((remote_client *) m->client);\n\n  tindex = me->fd % worker_thread_count;\n\n  ck_pr_inc_32(&work_queue_backlogs[tindex]);\n  work_queue = &work_queues[tindex];\n  ck_fifo_spsc_enqueue_lock(work_queue);\n  entry = ck_fifo_spsc_recycle(work_queue);\n  if (entry == NULL) {\n    ck_fifo_spsc_enqueue_unlock(work_queue);\n    tofree = malloc(sizeof(ck_fifo_spsc_entry_t));\n    ck_fifo_spsc_enqueue_lock(work_queue);\n    entry = ck_fifo_spsc_recycle(work_queue);\n    if(entry == NULL) {\n      entry = tofree;\n      tofree = NULL;\n    }\n  }\n  ck_fifo_spsc_enqueue(work_queue, entry, m);\n  ck_fifo_spsc_enqueue_unlock(work_queue);\n#ifdef linux\n  ck_pr_inc_int(&work_queue_futexes[tindex]);\n  if(ck_pr_load_int(&work_queue_waiting[tindex]) == 1) {\n    ck_pr_store_int(&work_queue_waiting[tindex], 0);\n    futex(&work_queue_futexes[tindex], FUTEX_WAKE_PRIVATE, INT_MAX,\n          NULL, NULL, 0);\n  }\n#endif\n  free(tofree);\n}\n\nstatic void\nfqd_dss_read_complete(void *closure, fq_msg *msg) {\n  int i;\n  remote_client *parent = closure;\n  remote_data_client *me = parent->data;\n  msg->hops[0] = fqd_config_get_nodeid();\n  if(me->mode == FQ_PROTO_DATA_MODE) {\n    memcpy(&msg->sender, &parent->user, sizeof(parent->user));\n    memcpy(&msg->hops[1], &me->remote.sin_addr, sizeof(uint32_t));\n  }\n  if(msg->payload_len > MAX_MESSAGE_SIZE) {\n    me->size_dropped++;\n    fqd_size_dropped(1);\n    fq_msg_deref(msg);\n    return;\n  }\n  me->msgs_in++;\n  me->octets_in += (1 + msg->exchange.len) +\n                   (1 + msg->route.len) +\n                   sizeof(fq_msgid) +\n                   (4 + msg->payload_len);\n  if(me->mode == FQ_PROTO_PEER_MODE) {\n    me->octets_in += 1 + msg->sender.len;\n    me->octets_in += 1; /* nhops */\n    for(i=0;i<MAX_HOPS;i++) {\n      if(msg->hops[i] != 0) {\n        me->octets_in += 4;\n      }\n    }\n  }\n  if(FQ_MESSAGE_RECEIVE_ENABLED()) {\n    fq_dtrace_msg_t dmsg;\n    fq_dtrace_remote_client_t dpc;\n    fq_dtrace_remote_data_client_t dme;\n    DTRACE_PACK_MSG(&dmsg, msg);\n    DTRACE_PACK_CLIENT(&dpc, parent);\n    DTRACE_PACK_DATA_CLIENT(&dme, me);\n    FQ_MESSAGE_RECEIVE(&dpc, &dme, &dmsg);\n  }\n\n  /* don't do any work here, just drop the message on one of N processing queues */\n  fqd_queue_message_process(me, msg);\n\n}\n\nstatic void\nfqd_data_driver(remote_client *parent) {\n  remote_data_client *me = parent->data;\n  fq_msg *inflight = NULL;\n  size_t inflight_sofar = 0;\n  buffered_msg_reader *ctx = NULL;\n  int had_msgs = 1, flags, needs_write = 1;\n\n  if(((flags = fcntl(me->fd, F_GETFL, 0)) == -1) ||\n     (fcntl(me->fd, F_SETFL, flags | O_NONBLOCK) == -1))\n    return;\n\n  ctx = fq_buffered_msg_reader_alloc(me->fd, (me->mode == FQ_PROTO_PEER_MODE));\n  while(1) {\n    uint32_t msgs_in = me->msgs_in, msgs_out = me->msgs_out;\n    int rv, timeout_ms = 1000;\n    struct pollfd pfd;\n    pfd.fd = me->fd;\n    pfd.events = POLLIN|POLLHUP;\n    if(needs_write) pfd.events |= POLLOUT;\n    pfd.revents = 0;\n    if(parent->heartbeat_ms && parent->heartbeat_ms < timeout_ms)\n      timeout_ms = parent->heartbeat_ms;\n    /* if we had msgs, but aren't waiting for write,\n     * then we set a very short timeout\n     */\n    if(had_msgs && !needs_write) timeout_ms = 1;\n\n    rv = poll(&pfd, 1, timeout_ms);\n    if(rv < 0) break;\n\n    had_msgs = 0;\n    if(rv > 0 && (pfd.revents & POLLIN)) {\n      me->last_heartbeat = me->last_activity = fq_gethrtime();\n      if(fq_buffered_msg_read(ctx, fqd_dss_read_complete, parent) < 0) {\n        fq_debug(FQ_DEBUG_IO, \"client read error\\n\");\n        break;\n      }\n      had_msgs = 1;\n    }\n\n    if(!needs_write || (rv > 0 && (pfd.revents & POLLOUT))) {\n      fq_msg *m;\n      needs_write = 0;\n     haveanother:\n      m = inflight ? inflight\n                   : parent->queue ? fqd_queue_dequeue(parent->queue)\n                                   : NULL;\n\n      /* If we're in peer mode, we can just toss messages the remote has seen */\n      if(m && me->mode == FQ_PROTO_PEER_MODE &&\n         fq_find_in_hops(me->peer_id, m) >= 0) {\n        fq_msg_deref(m);\n        goto haveanother;\n      }\n\n      inflight = NULL;\n      while(m) {\n        int written;\n        size_t octets_out = 0;\n        had_msgs = 1;\n        written = fq_client_write_msg(me->fd, 1, m, inflight_sofar, &octets_out);\n        if(written > 0) inflight_sofar += written;\n        if(octets_out) me->octets_out += octets_out;\n\n        if(written > 0 || (written < 0 && errno == EAGAIN)) {\n          inflight = m;\n          needs_write = 1;\n          break;\n        }\n        else if(written < 0) {\n          fq_debug(FQ_DEBUG_IO, \"client write error\\n\");\n          goto broken;\n        }\n\n        if(FQ_MESSAGE_DELIVER_ENABLED()) {\n          fq_dtrace_msg_t dmsg;\n          fq_dtrace_remote_client_t dpc;\n          fq_dtrace_remote_data_client_t dme;\n          DTRACE_PACK_MSG(&dmsg, m);\n          DTRACE_PACK_CLIENT(&dpc, parent);\n          DTRACE_PACK_DATA_CLIENT(&dme, me);\n          FQ_MESSAGE_DELIVER(&dpc, &dme, &dmsg);\n        }\n        fq_msg_deref(m);\n        me->msgs_out++;\n        inflight_sofar = 0;\n        m = parent->queue ? fqd_queue_dequeue(parent->queue) : NULL;\n      }\n    }\n\n    if(me->msgs_in != msgs_in || me->msgs_out != msgs_out)\n      fq_debug(FQ_DEBUG_MSG, \"Round.... %d in, %d out\\n\", me->msgs_in, me->msgs_out);\n  }\nbroken:\n  if(inflight) {\n    /* We're screwed here... we might have delivered it. so just toss it */\n    fq_msg_deref(inflight);\n  }\n  if(ctx) fq_buffered_msg_reader_free(ctx);\n  parent->data = NULL;\n  fq_debug(FQ_DEBUG_IO, \"data path from client ended: %s\\n\", parent->pretty);\n}\n\nvoid\nfqd_data_subscription_server(remote_data_client *client) {\n  int len;\n  char buf[260];\n  fqd_config *config;\n  remote_client *parent;\n  fq_rk key;\n  fq_debug(FQ_DEBUG_CONN, \"--> dss thread [%s]\\n\",\n           client->mode == FQ_PROTO_DATA_MODE ? \"client\" : \"peer\");\n  if((len = fq_read_short_cmd(client->fd, sizeof(key.name), key.name)) < 0)\n    return;\n  if(len > (int)sizeof(key.name)) return;\n  key.len = len;\n\n  fq_rk_to_hex(buf, sizeof(buf), &key);\n  fq_debug(FQ_DEBUG_CONN, \"data conn w/ key:\\n%s\\n\", buf);\n\n  config = fqd_config_get();\n  parent = fqd_config_get_registered_client(config, &key);\n  fqd_config_release(config);\n  if(!parent) return;\n  if(parent->data) return;\n  fq_thread_setname(\"fqd:dss:%s\", parent->pretty);\n  ck_pr_cas_ptr(&parent->data, NULL, client);\n  if(parent->data != client) {\n    fq_debug(FQ_DEBUG_CONN, \"%s dss double gang rejected\\n\", parent->pretty);\n    return;\n  }\n  if(FQ_CLIENT_AUTH_DATA_ENABLED()) {\n    fq_dtrace_remote_data_client_t dclient;\n    DTRACE_PACK_DATA_CLIENT(&dclient, client);\n    FQ_CLIENT_AUTH_DATA(&dclient);\n  }\n  fqd_remote_client_ref(parent);\n  fqd_data_driver(parent);\n  fq_clear_message_cleanup_stack();\n  fqd_remote_client_deref(parent);\n  fq_debug(FQ_DEBUG_CONN, \"<-- dss thread\\n\");\n}\n"
  },
  {
    "path": "fqd_dyn_sample.c",
    "content": "/*\n * Copyright (c) 2014 Circonus, Inc.\n * All rights reserved.\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\n#include \"fqd.h\"\n#include <assert.h>\n\nbool fqd_route_prog__sample__d(fq_msg *, int, valnode_t *);\nbool\nfqd_route_prog__sample__d(fq_msg *m, int nargs, valnode_t *args) {\n  (void)m;\n  assert(nargs == 1);\n  assert(args[0].value_type == RP_VALUE_DOUBLE);\n  if(drand48() < args[0].value.d) return true;\n  return false;\n}\n\n"
  },
  {
    "path": "fqd_http.c",
    "content": "/*\n * Copyright (c) 2013 OmniTI Computer Consulting, Inc.\n * All rights reserved.\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\n#include \"fqd.h\"\n#include \"fqd_private.h\"\n#include \"http_parser.h\"\n#include <stdio.h>\n#include <stdarg.h>\n#include <string.h>\n#include <strings.h>\n#include <unistd.h>\n#include <fcntl.h>\n#include <sys/mman.h>\n#include <sys/stat.h>\n#include <sys/socket.h>\n#include <errno.h>\n#include <poll.h>\n#include <ctype.h>\n#include <ck_ht.h>\n\nconst char *fqd_web_path = VARLIBFQDIR \"/web\";\nconst char *index_file = \"index.html\";\n\nstatic remote_data_client web_data_client = {\n  .refcnt = 1,\n  .fd = -1,\n  .pretty = \"_web_data\",\n  .mode = FQ_PROTO_DATA_MODE\n};\n\nvoid fqd_http_set_root(const char *newpath) {\n  char path[PATH_MAX];\n  if(realpath(newpath, path) != NULL)\n    fqd_web_path = strdup(path);\n}\n\nstatic inline int ends_with(const char *str, const char *end) {\n  int elen = strlen(end);\n  int slen = strlen(str);\n  if(slen < elen) return 0;\n  return !strcasecmp(str + slen - elen, end);\n}\nstatic const char *\nfqd_http_mime_type(const char *url) {\n  if(ends_with(url, \".js\")) return \"text/javascript\";\n  if(ends_with(url, \".json\")) return \"application/json\";\n  if(ends_with(url, \".css\")) return \"text/css\";\n  if(ends_with(url, \".jpg\") || ends_with(url, \".jpeg\")) return \"image/jpeg\";\n  if(ends_with(url, \".gif\")) return \"image/gif\";\n  if(ends_with(url, \".png\")) return \"image/png\";\n  if(ends_with(url, \"/\") || ends_with(url, \".html\") || ends_with(url, \".htm\"))\n    return \"text/html\";\n  return \"application/octet-stream\";\n}\n\nstruct http_req {\n  remote_client *client;\n  enum http_method method;\n  char *url;\n  char *qs;\n  char *status;\n  char *fldname;\n  char *error;\n  ck_ht_t headers;\n  ck_ht_t query_params;\n  size_t body_len;\n  size_t body_read;\n  fq_msg *msg;\n  enum {\n    HTTP_EXPECT_NONE = 0,\n    HTTP_EXPECT_CONTINUE,\n    HTTP_EXPECT_SENT\n  } expect_continue;\n  int close;\n};\n\nstatic int fqd_http_submit_msg(struct http_req *req);\nstatic int fqd_http_add_checkpoint(struct http_req *req);\nstatic int fqd_http_remove_checkpoint(struct http_req *req);\nstatic int fqd_http_reset_to_checkpoint(struct http_req *req);\n\nstatic void *\nht_malloc(size_t r)\n{ return malloc(r); }\n\nstatic void\nht_free(void *p, size_t b, bool r)\n{ (void)b; (void)r; free(p); return; }\n\nstatic struct ck_malloc my_alloc = {\n  .malloc = ht_malloc,\n  .free = ht_free\n};\n\nstatic void\nhttp_req_clean(struct http_req *req) {\n  ck_ht_entry_t *cursor;\n  ck_ht_iterator_t iterator = CK_HT_ITERATOR_INITIALIZER;\n\n  while(ck_ht_next(&req->headers, &iterator, &cursor)) {\n    ck_ht_hash_t hv;\n    char *key = ck_ht_entry_key(cursor);\n    char *value = ck_ht_entry_value(cursor);\n    ck_ht_hash(&hv, &req->headers, key, strlen(key));\n    ck_ht_remove_spmc(&req->headers, hv, cursor);\n    free(key);\n    free(value);\n  }\n\n  ck_ht_iterator_init(&iterator);\n\n  while(ck_ht_next(&req->query_params, &iterator, &cursor)) {\n    ck_ht_hash_t hv;\n    char *key = ck_ht_entry_key(cursor);\n    char *value = ck_ht_entry_value(cursor);\n    ck_ht_hash(&hv, &req->headers, key, strlen(key));\n    ck_ht_remove_spmc(&req->headers, hv, cursor);\n    free(key);\n    free(value);\n  }\n\n  if(req->url) free(req->url);\n  /* req->qs isn't allocated */\n  if(req->status) free(req->status);\n  if(req->fldname) free(req->fldname);\n  if(req->error) free(req->error);\n  if(req->msg) fq_msg_deref(req->msg);\n\n  req->url = NULL;\n  req->qs = NULL;\n  req->status = NULL;\n  req->fldname = NULL;\n  req->error = NULL;\n  req->body_len = 0;\n  req->body_read = 0;\n  req->msg = NULL;\n  req->expect_continue = HTTP_EXPECT_NONE;\n}\n\n/* split incoming string by '=' and store the left as key and right as value in the table */\nstatic void\nstore_kv(ck_ht_t *table, char *kv_string) {\n  ck_ht_entry_t entry;\n  ck_ht_hash_t hv;\n\n  const char *key = kv_string;\n  char *eq = strchr(kv_string, '=');\n  if(eq) eq[0] = '\\0';\n  const char *val = eq ? eq + 1 : \"\";\n\n  ck_ht_hash(&hv, table, key, strlen(key));\n  ck_ht_entry_set(&entry, hv, strdup(key), strlen(key), strdup(val));\n\n  if(ck_ht_set_spmc(table, hv, &entry)) {\n    fq_debug(FQ_DEBUG_HTTP, \".store_kv -> added (%s, %s)\\n\", key, val);\n  }\n\n  /* be non-destructive */\n  if(eq) eq[0] = '=';\n}\n\nstatic const char *\nget_ht_value(ck_ht_t *table, const char *key) {\n  ck_ht_entry_t entry;\n  ck_ht_hash_t hv;\n\n  ck_ht_hash(&hv, table, key, strlen(key));\n  ck_ht_entry_key_set(&entry, key, strlen(key));\n\n  if (ck_ht_get_spmc(table, hv, &entry)) {\n    return ck_ht_entry_value(&entry);\n  }\n  return NULL;\n}\n\nstatic int\nfqd_http_message_url(http_parser *p, const char *at, size_t len) {\n  struct http_req *req = p->data;\n  req->url = malloc(len+1);\n  strlcpy(req->url, at, len+1);\n  req->qs = strchr(req->url, '?');\n  if(req->qs) *(req->qs++) = '\\0';\n\n  if (req->qs != NULL) {\n    char *trailing = req->qs;\n    for (uint32_t i = 0; i < strlen(req->qs); i++) {\n      if (req->qs[i] == '&') {\n        req->qs[i] = '\\0';\n        store_kv(&req->query_params, trailing);\n        req->qs[i] = '&';\n        trailing = &req->qs[i+1];\n      }\n    }\n    store_kv(&req->query_params, trailing);\n  }\n\n  fq_debug(FQ_DEBUG_HTTP, \".on_url -> '%s'\\n\", req->url);\n  fq_debug(FQ_DEBUG_HTTP, \".on_url query_string -> '%s'\\n\", req->qs);\n  return 0;\n}\nstatic int\nfqd_http_message_status(http_parser *p, const char *at, size_t len) {\n  struct http_req *req = p->data;\n  req->status = malloc(len+1);\n  strlcpy(req->status, at, len+1);\n  fq_debug(FQ_DEBUG_HTTP, \".on_status -> '%s'\\n\", req->status);\n  return 0;\n}\nstatic int\nfqd_http_message_body(http_parser *p, const char *at, size_t len) {\n  struct http_req *req = p->data;\n  fq_debug(FQ_DEBUG_HTTP, \".on_data -> %zu\\n\", len);\n  if(req->msg) {\n    if(req->body_read + len > req->body_len) {\n      req->error = strdup(\"excessive data received\");\n      return 1;\n    }\n    memcpy(req->msg->payload + req->body_read, at, len);\n    req->body_read += len;\n  }\n  return 0;\n}\nstatic int\nfqd_http_message_header_field(http_parser *p, const char *at, size_t len) {\n  char *cp;\n  struct http_req *req = p->data;\n  req->fldname = malloc(len+1);\n  strlcpy(req->fldname, at, len+1);\n  for(cp=req->fldname;*cp;cp++) *cp = tolower(*cp);\n  fq_debug(FQ_DEBUG_HTTP, \".on_header_field -> '%s'\\n\", req->fldname);\n  return 0;\n}\nstatic const char *\nfqd_http_header(struct http_req *req, const char *hdr) {\n  ck_ht_entry_t entry;\n  ck_ht_hash_t hv;\n  int hdrlen = strlen(hdr);\n\n  ck_ht_hash(&hv, &req->headers, hdr, hdrlen);\n  ck_ht_entry_set(&entry, hv, hdr, hdrlen, NULL);\n  if(ck_ht_get_spmc(&req->headers, hv, &entry)) {\n    return ck_ht_entry_value(&entry);\n  }\n  return NULL;\n}\nstatic int\nfqd_http_message_header_value(http_parser *p, const char *at, size_t len) {\n  struct http_req *req = p->data;\n  ck_ht_entry_t entry;\n  ck_ht_hash_t hv;\n  char *val;\n  if(!req->fldname) return -1;\n  val = malloc(len+1);\n  strlcpy(val, at, len+1);\n  fq_debug(FQ_DEBUG_HTTP, \".on_header_value -> '%s'\\n\", val);\n\n  ck_ht_hash(&hv, &req->headers, req->fldname, strlen(req->fldname));\n  ck_ht_entry_set(&entry, hv, req->fldname, strlen(req->fldname), val);\n\n  if(ck_ht_set_spmc(&req->headers, hv, &entry) && !ck_ht_entry_empty(&entry)) {\n    char *key = ck_ht_entry_key(&entry);\n    char *value = ck_ht_entry_value(&entry);\n    if(key && key != req->fldname) free(key);\n    if(value && value != val) free(value);\n  }\n\n  if(!strcmp(req->fldname, \"expect\") && !strcasecmp(val,\"100-continue\"))\n    req->expect_continue = HTTP_EXPECT_CONTINUE;\n  req->fldname = NULL;\n  return 0;\n}\nstatic int\nfqd_http_message_headers_complete(http_parser *p) {\n#define EXPECT_CONTINUE \"HTTP/1.1 100 Continue\\r\\n\\r\\n\"\n  const char *clen;\n  static char *expect_continue = EXPECT_CONTINUE;\n  static int expect_continue_len = sizeof(EXPECT_CONTINUE)-1;\n  struct http_req *req = p->data;\n  if(req->expect_continue == HTTP_EXPECT_CONTINUE) {\n    while(write(req->client->fd, expect_continue, expect_continue_len) == -1 && errno == EINTR);\n    req->expect_continue = HTTP_EXPECT_SENT;\n  }\n  clen = fqd_http_header(req, \"content-length\");\n  if(!strcmp(req->url, \"/submit\") && clen) {\n    req->body_len = atoi(clen);\n    req->msg = fq_msg_alloc_BLANK(req->body_len);\n  }\n  return 0;\n}\n\n#define cwrite(client, str) write(client->fd, str, strlen(str))\n\nstatic void\nfqd_http_jsend(remote_client *client, const char *status, const char *fmt, ...)\n{\n  char error[1024];\n  char scratch[1124];\n  const char *headers = \"HTTP/1.0 200 OK\\r\\nConnection: close\\r\\nContent-Type: application/json\\r\\n\\r\\n\";\n  va_list argp;\n\n  error[0] = 0;\n  scratch[0] = 0;\n\n  va_start(argp, fmt);\n  vsnprintf(error, sizeof(error), fmt, argp);\n  va_end(argp);\n\n  while(write(client->fd, headers, strlen(headers)) == -1 && errno == EINTR);\n  cwrite(client, \"{\\n\");\n  cwrite(client,  \" \\\"status\\\": \\\"\");\n  cwrite(client, status);\n  cwrite(client,  \"\\\",\\n\");\n  snprintf(scratch, sizeof(scratch), \" \\\"message\\\": \\\"%s\\\"\\n\", error);\n  cwrite(client, scratch);\n  cwrite(client, \"}\\n\");\n}\n\n#define fqd_http_error_json_f(client, fmt, ...) fqd_http_jsend(client, \"error\", fmt, __VA_ARGS__)\n#define fqd_http_success_json_f(client, fmt, ...) fqd_http_jsend(client, \"success\", fmt, __VA_ARGS__)\n#define fqd_http_error_json(client, fmt) fqd_http_error_json_f(client, \"%s\", fmt)\n#define fqd_http_success_json(client, fmt) fqd_http_success_json_f(client, \"%s\", fmt)\n\n\nstatic int\nfqd_http_message_complete(http_parser *p) {\n  char file[PATH_MAX], rfile[PATH_MAX];\n  struct http_req *req = p->data;\n\n  fq_debug(FQ_DEBUG_HTTP, \".on_complete ->\\n\");\n\n  /* programmatic endpoints */\n  if(!strcmp(req->url, \"/stats.json\")) {\n    fqd_config_http_stats(req->client);\n    req->close = 1;\n    return 0;\n  }\n\n  if(!strcmp(req->url, \"/submit\")) {\n    fqd_http_submit_msg(req);\n    return 0;\n  }\n\n  if(!strcmp(req->url, \"/shutdown\")) {\n    const char *allowed = getenv(\"HTTP_SHUTDOWN\");\n    if(allowed && !strcmp(allowed, \"1\")) exit(0);\n  }\n\n  if (!strcmp(req->url, \"/add_checkpoint\")) {\n    fqd_http_add_checkpoint(req);\n    return 0;\n  }\n\n  if (!strcmp(req->url, \"/remove_checkpoint\")) {\n    fqd_http_remove_checkpoint(req);\n    return 0;\n  }\n\n  if (!strcmp(req->url, \"/reset_to_checkpoint\")) {\n    fqd_http_reset_to_checkpoint(req);\n    return 0;\n  }\n\n  /* Files */\n  if(strlen(fqd_web_path)) {\n    int fd, rv;\n    int drlen = strlen(fqd_web_path);\n    char http_header[1024];\n    void *contents;\n    struct stat st;\n    strlcpy(file, fqd_web_path, sizeof(file));\n    if(file[drlen-1] != '/') strlcat(file, \"/\", sizeof(file));\n    strlcat(file, req->url, sizeof(file));\n    if(file[strlen(file) - 1] == '/') strlcat(file, index_file, sizeof(file));\n\n    if(realpath(file, rfile) == NULL) goto not_found;\n    if(strncmp(rfile, fqd_web_path, drlen)) goto not_found;\n    if(rfile[drlen] != '/' && rfile[drlen + 1] != '/') goto not_found;\n\n    fd = open(rfile, O_RDONLY);\n    if(fd < 0) goto not_found;\n    while((rv = fstat(fd, &st)) < 0 && errno == EINTR);\n    if(rv < 0) {\n      close(fd);\n      goto not_found;\n    }\n\n    contents = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0);\n    close(fd);\n    snprintf(http_header, sizeof(http_header), \"HTTP/1.0 200 OK\\r\\nContent-Length: %lu\\r\\n\"\n             \"Content-Type: %s\\r\\n\\r\\n\", (long int)st.st_size,\n             fqd_http_mime_type(req->url));\n    drlen = strlen(http_header);\n    while(write(req->client->fd, http_header, drlen) == -1 && errno == EINTR);\n    while(write(req->client->fd, contents, st.st_size) == -1 && errno == EINTR);\n    munmap(contents, st.st_size);\n    return 0;\n  }\n  /* 404 */\n not_found:\n  {\n    const char *headers = \"HTTP/1.0 404 OK\\r\\nConnection: close\\r\\nContent-Type: text/html\\r\\n\\r\\n\";\n    while(write(req->client->fd, headers, strlen(headers)) == -1 && errno == EINTR);\n    req->close = 1;\n  }\n\n  return 0;\n}\n\nstatic int\nfqd_http_add_checkpoint(struct http_req *req) {\n\n  fqd_config *config = fqd_config_get();\n\n  const char *cpname = get_ht_value(&req->query_params, \"cpname\");\n  const char *qname = get_ht_value(&req->query_params, \"qname\");\n  const char *chkptid = get_ht_value(&req->query_params, \"chkptid\");\n\n  fq_rk qn;\n  fq_rk_from_str(&qn, qname);\n\n  fqd_queue *queue = fqd_config_get_registered_queue(config, &qn);\n\n  if (strcmp(cpname, \"fq\") == 0) {\n    fqd_http_error_json(req->client, \"'fq' is a reserved name, cannot be used for a checkpoint name\");\n    req->close = 1;\n    return 0;\n  }\n\n  if (queue == NULL) {\n    fqd_http_error_json_f(req->client, \"Cannot find registered queue '%s'\", qname);\n    req->close = 1;\n    return 0;\n  }\n\n  /* do we really want this restriction? */\n  if (queue->permanent == false) {\n    fqd_http_error_json(req->client, \"Checkpoints on ephemeral queues not supported\");\n    req->close = 1;\n    return 0;\n  }\n\n  /* check points only supported on disk queue */\n  if (strcmp(queue->impl->name, \"disk\") != 0) {\n    fqd_http_error_json(req->client, \"Checkpoints on memory queues not supported\");\n    req->close = 1;\n    return 0;\n  }\n\n  /* validate chkptid format */\n  char ckid[48] = {0};\n  strncpy(ckid, chkptid, sizeof(ckid)-1);\n  const char *log_string = ckid;\n  char *sep = strchr(ckid, ':');\n  if (sep == NULL) {\n    fqd_http_error_json(req->client, \"'chkptid' must be of format: [0-9]*:[0-9]*\");\n    req->close = 1;\n    return 0;\n  }\n  sep[0] = '\\0';\n  const char *marker_string = sep + 1;\n\n  uint32_t log = atoi(log_string);\n  uint32_t marker = atoi(marker_string);\n\n  fq_msgid id = {\n    .id.u32.p1 = log,\n    .id.u32.p2 = marker\n  };\n\n  int rv = queue->impl->add_checkpoint(queue->impl_data, cpname, &id);\n  if (rv == -1) {\n    fqd_http_error_json(req->client, \"'chkptid' is out of range of the queue\");\n    req->close = 1;\n    return 0;\n  }\n\n  if (rv == -2) {\n    fqd_http_error_json(req->client, \"Failed to set checkpoint\");\n    req->close = 1;\n    return 0;\n  }\n\n  if (rv < 0) {\n    fqd_http_error_json(req->client, \"Unknown error\");\n    req->close = 1;\n    return 0;\n  }\n\n  fqd_http_success_json(req->client, \"Checkpoint added\");\n  req->close = 1;\n  fq_debug(FQ_DEBUG_HTTP, \".on_complete -> add_checkpoint on [%s] for queue [%s] and id [%s]\\n\", cpname, qname, chkptid);\n  return 0;\n}\n\nstatic int\nfqd_http_remove_checkpoint(struct http_req *req)\n{\n  fqd_config *config = fqd_config_get();\n\n  const char *cpname = get_ht_value(&req->query_params, \"cpname\");\n  const char *qname = get_ht_value(&req->query_params, \"qname\");\n\n  fq_rk qn;\n  fq_rk_from_str(&qn, qname);\n\n  fqd_queue *queue = fqd_config_get_registered_queue(config, &qn);\n\n  if (strcmp(cpname, \"fq\") == 0) {\n    fqd_http_error_json(req->client, \"'fq' is a reserved name, cannot be used for a checkpoint name\");\n    req->close = 1;\n    return 0;\n  }\n\n  if (queue == NULL) {\n    fqd_http_error_json_f(req->client, \"Cannot find registered queue '%s'\", qname);\n    req->close = 1;\n    return 0;\n  }\n\n  int rv = queue->impl->remove_checkpoint(queue->impl_data, cpname);\n  if (rv == -1) {\n    fqd_http_error_json(req->client, \"Checkpoint does not exist\");\n    req->close = 1;\n    return 0;\n  }\n\n  if (rv < 0) {\n    fqd_http_error_json(req->client, \"Unknown error\");\n    req->close = 1;\n    return 0;\n  }\n\n  fqd_http_success_json(req->client, \"Checkpoint removed\");\n  req->close = 1;\n  fq_debug(FQ_DEBUG_HTTP, \".on_complete -> remove_checkpoint on [%s] for queue [%s]\\n\", cpname, qname);\n  return 0;\n}\n\nstatic int\nfqd_http_reset_to_checkpoint(struct http_req *req)\n{\n  fqd_config *config = fqd_config_get();\n  const char *cpname = get_ht_value(&req->query_params, \"cpname\");\n  const char *qname = get_ht_value(&req->query_params, \"qname\");\n\n  fq_rk qn;\n  fq_rk_from_str(&qn, qname);\n\n  fqd_queue *queue = fqd_config_get_registered_queue(config, &qn);\n\n  if (strcmp(cpname, \"fq\") == 0) {\n    fqd_http_error_json(req->client, \"'fq' is a reserved name, cannot be used for a checkpoint name\");\n    req->close = 1;\n    return 0;\n  }\n\n  if (queue == NULL) {\n    fqd_http_error_json_f(req->client, \"Cannot find registered queue '%s'\", qname);\n    req->close = 1;\n    return 0;\n  }\n\n  int rv = queue->impl->reset_checkpoint(queue->impl_data, cpname);\n  if (rv == -1) {\n    fqd_http_error_json(req->client, \"Checkpoint does not exist\");\n    req->close = 1;\n    return 0;\n  }\n\n  if (rv < 0) {\n    fqd_http_error_json(req->client, \"Unknown error\");\n    req->close = 1;\n    return 0;\n  }\n\n  fqd_http_success_json_f(req->client, \"'%s' reset to checkpoint '%s'\", qname, cpname);\n  req->close = 1;\n  fq_debug(FQ_DEBUG_HTTP, \".on_complete -> reset_to_checkpoint on [%s] for queue [%s]\\n\", cpname, qname);\n  return 0;\n}\n\nstatic int\nfqd_http_submit_msg(struct http_req *req) {\n  remote_data_client tmp_data_client = {\n    .refcnt = 1,\n    .fd = -1,\n    .pretty = \"_web_data\",\n    .mode = FQ_PROTO_DATA_MODE\n  };\n  const char *hdrval;\n  int len, slen;\n  char http_header[1024];\n  char scratch[1024];\n  const char *status = \"200 OK\";\n#define SUBERR(a) do { req->error = strdup(a); goto error; } while(0)\n\n  if(!req->msg) SUBERR(\"no message\");\n  if(req->msg->payload_len != req->body_len) SUBERR(\"short message\");\n\n  hdrval = fqd_http_header(req, \"x-fq-sender\");\n  if(!hdrval) hdrval = \"_web\";\n  if(strlen(hdrval) > MAX_RK_LEN) SUBERR(\"sender too long\");\n  req->msg->sender.len = strlen(hdrval);\n  memcpy(req->msg->sender.name, hdrval, req->msg->sender.len);\n\n  hdrval = fqd_http_header(req, \"x-fq-route\");\n  if(!hdrval) SUBERR(\"missing route\");\n  if(strlen(hdrval) > MAX_RK_LEN) SUBERR(\"route too long\");\n  req->msg->route.len = strlen(hdrval);\n  memcpy(req->msg->route.name, hdrval, req->msg->route.len);\n\n  hdrval = fqd_http_header(req, \"x-fq-exchange\");\n  if(!hdrval) SUBERR(\"missing exchange\");\n  if(strlen(hdrval) > MAX_RK_LEN) SUBERR(\"exchange too long\");\n  req->msg->exchange.len = strlen(hdrval);\n  memcpy(req->msg->exchange.name, hdrval, req->msg->exchange.len);\n\n  if(req->error) goto error;\n  fq_msg_id(req->msg, NULL);\n\n  fqd_inject_message(&tmp_data_client, req->msg);\n  req->msg = NULL; /* not my problem anymore */\n\n  snprintf(scratch, sizeof(scratch),\n           \"{\\\"routed\\\":%u,\\\"dropped\\\":%u,\"\n           \"\\\"no_route\\\":%u,\\\"no_exchange\\\":%u}\\n\",\n           tmp_data_client.routed, tmp_data_client.dropped,\n           tmp_data_client.no_route, tmp_data_client.no_exchange);\n#define BUMP(a) ck_pr_add_32(&web_data_client.a, tmp_data_client.a)\n  BUMP(msgs_in);\n  BUMP(octets_in);\n  BUMP(msgs_out);\n  BUMP(octets_out);\n  BUMP(routed);\n  BUMP(dropped);\n  BUMP(no_route);\n  BUMP(no_exchange);\n  goto out;\n\n error:\n  status = \"500 ERROR\";\n  snprintf(scratch, sizeof(scratch), \"{ \\\"error\\\": \\\"%s\\\" }\\n\", req->error);\n\n out:\n  slen = strlen(scratch);\n  snprintf(http_header, sizeof(http_header), \"HTTP/1.0 %s\\r\\nContent-Length: %lu\\r\\n\"\n           \"Content-Type: application/json\\r\\n\\r\\n\", status, (long int)slen);\n  len = strlen(http_header);\n  while(write(req->client->fd, http_header, len) == -1 && errno == EINTR);\n  while(write(req->client->fd, scratch, slen) == -1 && errno == EINTR);\n  return 0;\n}\n\nvoid\nfqd_http_loop(remote_client *client, uint32_t bytes) {\n  ssize_t rv, len = 4;\n  char inbuff[4096 * 16];\n  struct http_req req = { .client = client };\n  http_parser parser;\n  http_parser_settings settings;\n\n  fq_thread_setname(\"fqd:http:%s\", client->pretty);\n\n  memset(&parser, 0, sizeof(parser));\n  memset(&settings, 0, sizeof(settings));\n  fq_assert(ck_ht_init(&req.headers, CK_HT_MODE_BYTESTRING, NULL, &my_alloc, 8, lrand48()));\n  fq_assert(ck_ht_init(&req.query_params, CK_HT_MODE_BYTESTRING, NULL, &my_alloc, 8, lrand48()));\n  http_parser_init(&parser, HTTP_REQUEST);\n  http_parser_settings_init(&settings);\n\n  parser.data = &req;\n  settings.on_url = fqd_http_message_url;\n  settings.on_status = fqd_http_message_status;\n  settings.on_body = fqd_http_message_body;\n  settings.on_header_field = fqd_http_message_header_field;\n  settings.on_header_value = fqd_http_message_header_value;\n  settings.on_headers_complete = fqd_http_message_headers_complete;\n  settings.on_message_complete = fqd_http_message_complete;\n\n  memcpy(inbuff, &bytes, 4);\n  while((rv = http_parser_execute(&parser, &settings, inbuff, len)) == len && !req.close) {\n    struct pollfd pfd;\n    pfd.fd = client->fd;\n    pfd.events = POLLIN|POLLHUP;\n    /* We actually don't care why we woke up, so ignore return */\n    (void)poll(&pfd, 1, 0);\n    len = recv(client->fd, inbuff, sizeof(inbuff), 0);\n    fq_debug(FQ_DEBUG_HTTP, \"recv() -> %d\\n\", (int)len);\n    if(len <= 0) break;\n  }\n\n  http_req_clean(&req);\n  ck_ht_destroy(&req.headers);\n  ck_ht_destroy(&req.query_params);\n  (void)bytes;\n}\n"
  },
  {
    "path": "fqd_listener.c",
    "content": "/*\n * Copyright (c) 2013 OmniTI Computer Consulting, Inc.\n * All rights reserved.\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <unistd.h>\n#include <assert.h>\n#include <sys/time.h>\n#include <sys/uio.h>\n#include <errno.h>\n#include <pthread.h>\n#include <sys/socket.h>\n#include <arpa/inet.h>\n#include <netinet/in.h>\n#include <netinet/tcp.h>\n#include <netdb.h>\n#include <ck_pr.h>\n\n#include \"fq.h\"\n#include \"fqd.h\"\n#include \"fqd_private.h\"\n#include \"fq_dtrace.h\"\n\nvoid\nfqd_remote_client_ref(remote_client *r) {\n  ck_pr_inc_uint(&r->refcnt);\n}\nbool\nfqd_remote_client_deref(remote_client *r) {\n  bool zero;\n  ck_pr_dec_uint_zero(&r->refcnt, &zero);\n  fq_debug(FQ_DEBUG_CONN, \"deref client -> %u%s\\n\",\n           r->refcnt, zero ? \" dropping\" : \"\");\n  if(zero) {\n    close(r->fd);\n    free(r);\n    return true;\n  }\n  return false;\n}\n\nstatic void\nservice_connection(remote_anon_client *client) {\n  uint32_t cmd;\n  uint32_t peer_id = 0;\n  int rv, on = 1;\n  char buf[40];\n  buf[0] = '\\0';\n  inet_ntop(AF_INET, &client->remote.sin_addr, buf, sizeof(buf));\n  fq_thread_setname(\"fqd:c:%s\", client->pretty);\n  snprintf(client->pretty, sizeof(client->pretty),\n           \"(pre-auth)@%s:%d\", buf, ntohs(client->remote.sin_port));\n  gettimeofday(&client->connect_time, NULL);\n  fq_debug(FQ_DEBUG_CONN, \"client(%s) connected\\n\", client->pretty);\n\n  /* We do nothing if this fails. */\n  (void)setsockopt(client->fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on));\n\n  while((rv = read(client->fd, &cmd, sizeof(cmd))) == -1 && errno == EINTR);\n  if(rv != 4) goto disconnect;\n  if(FQ_CLIENT_DISCONNECT_ENABLED()) {\n    fq_dtrace_remote_anon_client_t dc;\n    DTRACE_PACK_ANON_CLIENT(&dc, client);\n    FQ_CLIENT_CONNECT(&dc, ntohl(cmd));\n  }\n  fq_debug(FQ_DEBUG_CONN, \"read(%d) cmd -> %08x\\n\", client->fd, ntohl(cmd));\n  switch(ntohl(cmd)) {\n    case FQ_PROTO_CMD_MODE:\n    {\n      remote_client *newc = calloc(1, sizeof(*newc));\n      memcpy(newc, client, sizeof(*client));\n      newc->refcnt = 1;\n      client->fd=-1;\n      fqd_command_and_control_server(newc);\n      (void)fqd_remote_client_deref((remote_client *)newc);\n    }\n    break;\n\n    case FQ_PROTO_PEER_MODE:\n      while((rv = read(client->fd, &peer_id, sizeof(peer_id))) == -1 && errno == EINTR);\n      if(rv != 4) goto disconnect;\n      /* FALLTHROUGH */\n    case FQ_PROTO_OLD_PEER_MODE:\n      cmd = FQ_PROTO_PEER_MODE;\n      /* FALLTHROUGH */\n    case FQ_PROTO_DATA_MODE:\n    {\n      remote_data_client *newc = calloc(1, sizeof(*newc));\n      memcpy(newc, client, sizeof(*client));\n      newc->mode = ntohl(cmd);\n      newc->peer_id = peer_id;\n      newc->refcnt=1;\n      client->fd=-1;\n      fqd_data_subscription_server(newc);\n      (void)fqd_remote_client_deref((remote_client *)newc);\n    }\n    break;\n\n    case FQ_PROTO_HTTP_GET:\n    case FQ_PROTO_HTTP_HEAD:\n    case FQ_PROTO_HTTP_POST:\n    case FQ_PROTO_HTTP_PUT:\n    {\n      remote_client *newc = calloc(1, sizeof(*newc));\n      memcpy(newc, client, sizeof(*client));\n      newc->refcnt = 1;\n      client->fd=-1;\n      fqd_http_loop(newc, cmd);\n      (void)fqd_remote_client_deref((remote_client *)newc);\n    }\n    break;\n\n    default:\n      fq_debug(FQ_DEBUG_CONN, \"client protocol violation in initial cmd\\n\");\n      close(client->fd);\n      client->fd = -1;\n      break;\n  }\n\n disconnect:\n  if(FQ_CLIENT_DISCONNECT_ENABLED()) {\n    fq_dtrace_remote_anon_client_t dc;\n    DTRACE_PACK_ANON_CLIENT(&dc, client);\n    FQ_CLIENT_DISCONNECT(&dc, ntohl(cmd));\n  }\n\n  close(client->fd);\n  free(client);\n}\n\nstatic void *\nconn_handler(void *vc) {\n  fqd_bcd_attach();\n  while(1) {\n    fq_thread_setname(\"fqd:c:idle\");\n    remote_anon_client *client = fqd_ccs_dequeue_work();\n    service_connection(client);\n  }\n  return NULL;\n}\n\ntypedef struct fqd_ccs_work_queue {\n  remote_anon_client *client;\n  struct fqd_ccs_work_queue *next;\n} fqd_ccs_work_queue_t;\n\nstatic pthread_mutex_t fqd_ccs_work_queue_lock;\nstatic pthread_cond_t fqd_ccs_work_queue_cv;\nstatic fqd_ccs_work_queue_t *fqd_ccs_work_queue;\nstatic int fqd_ccs_idle_threads = 0;\n\nstatic void\nfqd_ccs_enqueue_work(remote_anon_client *client) {\n  fqd_ccs_work_queue_t *node = calloc(sizeof(*client), 1);\n  int err;\n  node->client = client;\n  err = pthread_mutex_lock(&fqd_ccs_work_queue_lock);\n  if(err != 0) {\n    fprintf(stderr, \"pthread_mutex_lock: %s\\n\", strerror(err));\n    exit(2);\n  }\n  if(fqd_ccs_idle_threads < 1) {\n    pthread_t client_task;\n    assert(pthread_create(&client_task, NULL, conn_handler, NULL) == 0);\n  }\n  node->next = (fqd_ccs_work_queue_t *)fqd_ccs_work_queue;\n  fqd_ccs_work_queue = node;\n  err = pthread_mutex_unlock(&fqd_ccs_work_queue_lock);\n  if(err != 0) {\n    fprintf(stderr, \"pthread_mutex_unlock: %s\\n\", strerror(err));\n    exit(2);\n  }\n  err = pthread_cond_signal(&fqd_ccs_work_queue_cv);\n  if(err != 0) {\n    fprintf(stderr, \"pthread_cond_signal: %s\\n\", strerror(err));\n    exit(2);\n  }\n}\n\nremote_anon_client *\nfqd_ccs_dequeue_work(void) {\n  remote_anon_client *client = NULL;\n  int err;\n  err = pthread_mutex_lock(&fqd_ccs_work_queue_lock);\n  if(err != 0) {\n    fprintf(stderr, \"pthread_mutex_lock: %s\\n\", strerror(err));\n    exit(2);\n  }\n  fqd_ccs_idle_threads++;\n  while(fqd_ccs_work_queue == NULL) {\n\terr = pthread_cond_wait(&fqd_ccs_work_queue_cv, &fqd_ccs_work_queue_lock);\n    if(err != 0) {\n      fprintf(stderr, \"pthread_cond_wait: %s\\n\", strerror(err));\n      exit(2);\n    }\n  }\n  client = fqd_ccs_work_queue->client;\n  fqd_ccs_work_queue_t *tofree = (fqd_ccs_work_queue_t *)fqd_ccs_work_queue;\n  fqd_ccs_work_queue = fqd_ccs_work_queue->next;\n  fqd_ccs_idle_threads--;\n  err = pthread_mutex_unlock(&fqd_ccs_work_queue_lock);\n  if(err != 0) {\n    fprintf(stderr, \"pthread_mutex_unlock: %s\\n\", strerror(err));\n    exit(2);\n  }\n  free(tofree);\n  return client;\n}\n\nint\nfqd_listener(const char *host, unsigned short port) {\n  int fd;\n  remote_anon_client *client = NULL;\n  unsigned int on = 1;\n  struct sockaddr_in laddr;\n\n  pthread_mutex_init(&fqd_ccs_work_queue_lock, NULL);\n  pthread_cond_init(&fqd_ccs_work_queue_cv, NULL);\n\n  memset(&laddr, 0, sizeof(laddr));\n  laddr.sin_family = AF_INET;\n  laddr.sin_addr.s_addr = INADDR_ANY;\n  if(host && inet_pton(AF_INET, host, &laddr.sin_addr) != 0) {\n    return -1;\n  }\n  laddr.sin_port = htons(port);\n\n  fd = socket(AF_INET, SOCK_STREAM\n#ifdef SOCK_CLOEXEC\n      |SOCK_CLOEXEC\n#endif\n      , 0);\n  if(fd < 0) return -1;\n\n  if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) != 0 ||\n#ifdef SO_REUSEPORT\n     setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on)) != 0 ||\n#endif\n     bind(fd, (struct sockaddr *)&laddr, sizeof(laddr)) < 0 ||\n     listen(fd, 16) < 0) {\n    close(fd);\n    return -1;\n  }\n\n  while(1) {\n    struct sockaddr_in raddr;\n    socklen_t raddr_len;\n\n    if(client == NULL) client = calloc(1, sizeof(*client));\n    raddr_len = sizeof(raddr);\n    client->fd = accept(fd, (struct sockaddr *)&client->remote, &raddr_len);\n    if(client->fd < 0) continue;\n    fq_keepalive_fd(client->fd, 10, 5, 2);\n    client->refcnt = 1;\n    fqd_ccs_enqueue_work(client);\n    client = NULL;\n  }\n  return -1;\n}\n"
  },
  {
    "path": "fqd_peer.c",
    "content": "/*\n * Copyright (c) 2013 OmniTI Computer Consulting, Inc.\n * All rights reserved.\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\n#include <stdio.h>\n#include <string.h>\n#include <stdint.h>\n#include <stdlib.h>\n#include <unistd.h>\n#include <pthread.h>\n#include <errno.h>\n#include <ck_pr.h>\n\n#include <sqlite3.h>\n\n#include \"fq.h\"\n#include \"fqd.h\"\n\n/* Peer connections are currently only permanent, in memory\n * bounded, public, drop-semantic queues.\n */\n\nstatic pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;\n\n/* These are infrequent calls, big ol' lock is fine */\n\ntypedef struct {\n  uint64_t gen;\n  uint32_t route_id;\n  fq_rk exchange;\n  char *prog;\n  bool  perm;\n  bool  disabled;\n  bool  disable_requested;\n} peer_binding_info;\n\ntypedef struct peer_connection {\n  fq_client client;\n  char *host;\n  int port;\n  char *user;\n  char *pass;\n  bool online_and_bound;\n  int n_bindings;\n  peer_binding_info **bindings;\n  remote_data_client *stats_holder;\n  struct peer_connection *next;\n} fqd_peer_connection;\n\nstatic fqd_peer_connection list_head;\n\nstatic int\npeercmp(fqd_peer_connection *a, fqd_peer_connection *b) {\n  int rv;\n  if(a->host == NULL || b->host == NULL) return -1;\n  if(a->user == NULL || b->user == NULL) return -1;\n  if(a->pass == NULL || b->pass == NULL) return -1;\n  if(0 != (rv = strcmp(a->host, b->host))) return rv;\n  if(0 != (rv = strcmp(a->user, b->user))) return rv;\n  if(0 != (rv = strcmp(a->pass, b->pass))) return rv;\n  if(a->port == b->port) return 0;\n  if(a->port <  b->port) return -1;\n  return 1;\n}\n\nstatic void\nfqd_peer_auth_hook(fq_client conn, int authed) {\n  int i;\n  fqd_peer_connection *peer;\n  peer = fq_client_get_userdata(conn);\n  fq_debug(FQ_DEBUG_PEER, \"authed(%s:%d) -> %d\\n\",\n           peer->host, peer->port, authed);\n  if(authed || !peer) return;\n\n  peer->online_and_bound = true;\n\n  pthread_mutex_lock(&lock);\n  for(i=0;i<peer->n_bindings;i++) {\n    peer_binding_info *bi = peer->bindings[i];\n    fq_bind_req *breq;\n    breq = calloc(1, sizeof(*breq));\n    memcpy(&breq->exchange, &bi->exchange, sizeof(bi->exchange));\n    breq->flags = FQ_BIND_PEER | (bi->perm ? FQ_BIND_PERM : 0);\n    breq->program = strdup(bi->prog);\n    fq_client_bind(conn, breq);\n    fq_debug(FQ_DEBUG_PEER, \"bindreq(%s:%d) %.*s/%s\\n\",\n             peer->host, peer->port, bi->exchange.len, bi->exchange.name,\n             bi->prog);\n  }\n  pthread_mutex_unlock(&lock);\n}\nstatic void\nfqd_peer_bind_hook(fq_client conn, fq_bind_req *breq) {\n  int i;\n  fqd_peer_connection *peer;\n  peer = fq_client_get_userdata(conn);\n  if(!peer) return;\n  fq_debug(FQ_DEBUG_PEER, \"bindresp(%u) %.*s/%s\\n\",\n           breq->out__route_id, breq->exchange.len, breq->exchange.name,\n           breq->program);\n  if(breq->out__route_id == FQ_BIND_ILLEGAL) {\n    return;\n  }\n  pthread_mutex_lock(&lock);\n  for(i=0;i<peer->n_bindings;i++) {\n    peer_binding_info *bi = peer->bindings[i];\n    if(!fq_rk_cmp(&bi->exchange, &breq->exchange) &&\n       !strcmp(bi->prog, breq->program)) {\n      bi->route_id = breq->out__route_id;\n      break;\n    }\n  }\n  pthread_mutex_unlock(&lock);\n}\nstatic void\nfqd_peer_unbind_hook(fq_client conn, fq_unbind_req *breq) {\n  /* We *could* find the route and unset the route_id, but\n   * I see no point in doing that work.\n   */\n}\nstatic void\nfqd_peer_cleanup_hook(fq_client conn) {\n  int i;\n  fqd_peer_connection *peer;\n  peer = fq_client_get_userdata(conn);\n  for(i=0;i<peer->n_bindings;i++) {\n    peer_binding_info *bi = peer->bindings[i];\n    free(bi->prog);\n  }\n  if(peer->bindings) free(peer->bindings);\n  if(peer->host) free(peer->host);\n  if(peer->user) free(peer->user);\n  if(peer->pass) free(peer->pass);\n  if(peer->stats_holder) free(peer->stats_holder);\n  free(peer);\n}\nstatic void\nfqd_peer_disconnect_hook(fq_client conn) {\n  fqd_peer_connection *peer;\n  peer = fq_client_get_userdata(conn);\n  if(peer) {\n    fq_debug(FQ_DEBUG_PEER, \"disconnect from peer(%s:%d)\\n\",\n             peer->host, peer->port);\n    peer->online_and_bound = false;\n  }\n}\nstatic bool\nfqd_peer_message_hook(fq_client conn, fq_msg *m) {\n  int i;\n  uint32_t me = fqd_config_get_nodeid();\n  fqd_peer_connection *peer;\n  peer = fq_client_get_userdata(conn);\n  for(i=MAX_HOPS-1;i>0;i--) {\n    if(m->hops[i] == me) {\n      fq_debug(FQ_DEBUG_PEER, \"recieved looped message\");\n      return true;\n    }\n    m->hops[i] = m->hops[i-1];\n  }\n  m->hops[0] = fqd_config_get_nodeid();\n\n  /* route this */\n  fq_msg_ref(m);\n  fqd_inject_message(peer ? peer->stats_holder : NULL, m);\n  return true;\n}\nstatic fq_hooks fqd_peer_hooks = {\n  .version = FQ_HOOKS_V4,\n  .auth = fqd_peer_auth_hook,\n  .bind = fqd_peer_bind_hook,\n  .unbind = fqd_peer_unbind_hook,\n  .sync = 0,\n  .message = fqd_peer_message_hook,\n  .cleanup = fqd_peer_cleanup_hook,\n  .disconnect = fqd_peer_disconnect_hook\n};\n\nstatic void\npeerlog(fq_client conn, const char *str) {\n  fqd_peer_connection *peer;\n  peer = fq_client_get_userdata(conn);\n  fq_debug(FQ_DEBUG_PEER, \"error %s:%d -> %s\\n\", peer->host, peer->port, str);\n}\nstatic void\nfqd_peer_start(fqd_peer_connection *peer) {\n  fq_debug(FQ_DEBUG_PEER, \"starting peer(%s:%d)\\n\", peer->host, peer->port);\n  fq_client_init(&peer->client, fqd_config_get_nodeid(), peerlog);\n  fq_client_set_userdata(peer->client, peer);\n  fq_client_creds(peer->client, peer->host, peer->port,\n                  peer->user, peer->pass);\n  fq_client_hooks(peer->client, &fqd_peer_hooks);\n  fq_client_connect(peer->client);\n}\nstatic void\nfqd_peer_stop(fqd_peer_connection *peer) {\n  fq_debug(FQ_DEBUG_PEER, \"stopping peer(%s:%d)\\n\", peer->host, peer->port);\n  fq_client_destroy(peer->client);\n}\nstatic void\nfqd_peer_online_bind(fqd_peer_connection *peer, peer_binding_info *bi) {\n  if(peer->online_and_bound == true) {\n    fq_bind_req *breq;\n    fq_debug(FQ_DEBUG_PEER, \"binding peer(%s:%d) exchange:\\\"%.*s\\\"\\n\",\n             peer->host, peer->port, bi->exchange.len, bi->exchange.name);\n    breq = calloc(1, sizeof(*breq));\n    memcpy(&breq->exchange, &bi->exchange, sizeof(bi->exchange));\n    breq->flags = FQ_BIND_PEER | FQ_BIND_PERM;\n    breq->program = strdup(bi->prog);\n    fq_client_bind(peer->client, breq);\n  }\n}\nstatic void\nfqd_peer_online_unbind(fqd_peer_connection *peer, peer_binding_info *bi) {\n  fq_unbind_req *ureq;\n  if(!bi->disabled || bi->disable_requested ||\n     !peer->online_and_bound || bi->route_id == FQ_BIND_ILLEGAL) return;\n\n  fq_debug(FQ_DEBUG_PEER, \"unbinding peer(%s:%d) exchange:\\\"%.*s\\\"\\n\",\n           peer->host, peer->port, bi->exchange.len, bi->exchange.name);\n  ureq = calloc(1, sizeof(*ureq));\n  memcpy(&ureq->exchange, &bi->exchange, sizeof(bi->exchange));\n  ureq->route_id = bi->route_id;\n  fq_client_unbind(peer->client, ureq);\n}\n\nint\nfqd_add_peer(uint64_t gen,\n             const char *host, int port,\n             const char *user, const char *pass,\n             fq_rk *exchange, const char *prog,\n             bool perm) {\n  bool added_peer = false, added_binding = false;\n  fqd_peer_connection *peer, speer;\n  peer_binding_info *bi = NULL;\n  int i;\n\n  memset(&speer, 0, sizeof(speer));\n  speer.host = (char *)host;\n  speer.port = port;\n  speer.user = (char *)user;\n  speer.pass = (char *)pass;\n\n  pthread_mutex_lock(&lock);\n\n  /* Get a peer */\n  for(peer = list_head.next; peer; peer = peer->next) {\n    if(!peercmp(peer, &speer)) break;\n  }\n  if(!peer) {\n    peer = calloc(1, sizeof(*peer));\n    peer->host = strdup(speer.host);\n    peer->port = speer.port;\n    peer->user = strdup(speer.user);\n    peer->pass = strdup(speer.pass);\n    peer->next = list_head.next;\n    list_head.next = peer;\n    added_peer = true;\n  }\n\n  /* Get a binding */\n\n  for(i=0; i<peer->n_bindings; i++) {\n    bi = peer->bindings[i];\n    if(!fq_rk_cmp(exchange, &bi->exchange) && !strcmp(prog, bi->prog)) break;\n  }\n  if(i == peer->n_bindings) {\n    bi = calloc(1, sizeof(*bi));\n    memcpy(&bi->exchange, exchange, sizeof(*exchange));\n    bi->route_id = FQ_BIND_ILLEGAL;\n    bi->prog = strdup(prog);\n    peer->n_bindings++;\n    peer->bindings = realloc(peer->bindings, peer->n_bindings * sizeof(bi));\n    peer->bindings[peer->n_bindings - 1] = bi;\n    added_binding = true;\n  }\n\n  /* This will force a rebind */\n  if(perm != bi->perm) added_binding = true;\n  bi->perm = perm;\n  bi->gen = gen;\n\n  if(added_peer) fqd_peer_start(peer);\n  else if(added_binding) fqd_peer_online_bind(peer, bi);\n\n  pthread_mutex_unlock(&lock);\n\n  return added_binding ? 0 : -1;\n}\n\n/* Remove all peers older than the specified generation */\nint\nfqd_remove_peers(uint64_t current_gen) {\n  fqd_peer_connection *prev;\n  int turndown = 0;\n\n  pthread_mutex_lock(&lock);\n\n  for(prev = &list_head; prev && prev->next; prev = prev->next) {\n    fqd_peer_connection *peer = prev->next;\n    bool useful = false;\n    int i;\n\n    for(i=0; i<peer->n_bindings; i++) {\n      if(peer->bindings[i]->gen >= current_gen) useful = true;\n      else if(!peer->bindings[i]->disabled) {\n        peer->bindings[i]->disabled = true;\n        turndown++;\n      }\n    }\n    if(!useful) {\n      prev->next = peer->next;\n      peer->next = NULL;\n      fqd_peer_stop(peer);\n    }\n    else {\n      for(i=0; i<peer->n_bindings; i++) {\n        fqd_peer_online_unbind(peer, peer->bindings[i]);\n      }\n    }\n  }\n\n  pthread_mutex_unlock(&lock);\n  return turndown;\n}\nint\nfqd_remove_peer(const char *host, int port,\n                const char *user, const char *pass,\n                fq_rk *exchange, const char *prog) {\n  fqd_peer_connection *peer, speer;\n  peer_binding_info *bi = NULL;\n  int i;\n\n  memset(&speer, 0, sizeof(speer));\n  speer.host = (char *)host;\n  speer.port = port;\n  speer.user = (char *)user;\n  speer.pass = (char *)pass;\n  pthread_mutex_lock(&lock);\n\n  /* Get a peer */\n  for(peer = list_head.next; peer; peer = peer->next) {\n    if(!peercmp(peer, &speer)) {\n      for(i=0; i<peer->n_bindings; i++) {\n        bi = peer->bindings[i];\n        if(bi->disabled == false &&\n           fq_rk_cmp(exchange, &bi->exchange) &&\n           !strcmp(prog, bi->prog)) {\n          bi->disabled = true;\n          break;\n        }\n      }\n      break;\n    }\n  }\n  if(bi && peer->n_bindings == 1) {\n    /* Special case, we must remove this so no one else can see it */\n    fqd_peer_connection *prev = &list_head;\n    for(prev = &list_head; prev->next; prev = prev->next) {\n      if(!peercmp(prev->next, peer)) {\n        prev->next = peer->next;\n        peer->next = NULL;\n        break;\n      }\n    }\n  }\n\n  if(bi) {\n    /* We found something and it is our job to turn it down */\n    if(peer->n_bindings == 1) fqd_peer_stop(peer);\n    else fqd_peer_online_unbind(peer, bi);\n  }\n\n  pthread_mutex_unlock(&lock);\n  return bi ? 0 : -1;\n}\n"
  },
  {
    "path": "fqd_private.h",
    "content": "/*\n * Copyright (c) 2013 Circonus, Inc.\n * All rights reserved.\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\n#ifndef FQD_PRIVATE_H\n#define FQD_PRIVATE_H\n\n#include \"fq.h\"\n\n#define MAX_QUEUE_CLIENTS 16\n\nstruct fqd_route_stats {\n  /* These are just estimates */\n  uint64_t invocations;\n  uint32_t avg_ns;\n  uint32_t refcnt;\n};\nstruct fqd_route_rule {\n  fq_rk prefix;\n  int match_maxlen;\n  char *program;\n  rulenode_t *compiled_program;\n  uint32_t route_id;\n  bool permanent;\n  int peermode;\n  fqd_queue *queue;\n  struct fqd_route_stats *stats;\n  struct fqd_route_rule *next;\n};\n\nstruct prefix_jumptable {\n  enum { JUMPTABLE, RULETABLE } tabletype;\n  struct fqd_route_rule *rules;\n  struct {\n    uint64_t pattern;\n    uint64_t checkbits;\n    struct prefix_jumptable *jt;\n  } *pats;\n  int pat_len;\n};\n\nstruct fqd_route_rules {\n  struct prefix_jumptable master;\n};\n\nstruct fqd_queue {\n  fq_rk               name;\n  bool                permanent;\n  bool                private;\n  remote_client      *downstream[MAX_QUEUE_CLIENTS];\n  /* referenced by: routes and connections */\n  queue_policy_t      policy;\n  uint32_t            backlog_limit;\n  uint32_t            backlog;\n  uint32_t            dropped_to;\n\n  /* These are only use for FQ_POLICY_BLOCK */\n  pthread_cond_t      cv;\n  pthread_mutex_t     lock;\n\n  uint32_t            refcnt;\n  fqd_queue_impl      *impl;\n  fqd_queue_impl_data *impl_data;\n};\n\nextern int\n  for_each_route_rule_do(struct fqd_route_rules *set,\n                         int (*f)(struct fqd_route_rule *, int, void *),\n                         void *closure);\n\nvoid fqd_bcd_attach(void);\nvoid fqd_start_worker_threads(int thread_count);\nvoid fqd_stop_worker_threads(void);\nvoid fqd_routemgr_add_handle(void *);\nvoid global_function_register(const char *name, void (*f)(void));\nvoid global_functions_init(const char *dir);\nvoid fqd_route_load_module(const char *libexecdir, const char *file, const char *ext);\nvoid fq_thread_setname(const char *format, ...);\nremote_anon_client *fqd_ccs_dequeue_work(void);\n\n#if defined(linux) || defined(__linux) || defined(__linux__)\n#if ((__GLIBC__ == 2) && (__GLIBC_MINOR__ < 38))\nstatic inline size_t strlcpy(char *dst, const char *src, size_t size)\n{\n  if(size > 0) {\n    strncpy(dst, src, size-1);\n    dst[size-1] = '\\0';\n    return size;\n  }\n\n  dst[0] = '\\0';\n  return 0;\n}\nstatic inline size_t strlcat(char *dst, const char *src, size_t size)\n{\n  int dl = strlen(dst);\n  int sz = size-dl-1;\n\n  if(sz >= 0) {\n    strncat(dst, src, sz);\n    dst[dl+sz] = '\\0';\n  }\n\n  return dl+strlen(src);\n}\n#endif /* glibc */\n#endif /* linux */\n\n#endif\n"
  },
  {
    "path": "fqd_prog.c",
    "content": "/*\n * Copyright (c) 2013 OmniTI Computer Consulting, Inc.\n * All rights reserved.\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\n#define _GNU_SOURCE\n#include <string.h>\n#include <sys/types.h>\n#include <sys/stat.h>\n#include <unistd.h>\n#include <dlfcn.h>\n#include <dirent.h>\n#include \"fqd.h\"\n#include \"fqd_private.h\"\n\nbool fqd_route_prog__true__(fq_msg *, int, valnode_t *);\nbool fqd_route_prog__route_contains__s(fq_msg *, int, valnode_t *);\nbool fqd_route_prog__payload_prefix__s(fq_msg *, int, valnode_t *);\n\nvoid fqd_route_load_module(const char *libexecdir, const char *file, const char *ext) {\n  char path[PATH_MAX];\n  if(*file != '/') {\n    snprintf(path, sizeof(path), \"%s/%s%s\", libexecdir, file, ext ? ext : \"\");\n    file = path;\n  }\n  void *handle = dlopen(file, RTLD_NOW|RTLD_GLOBAL);\n  if(handle == NULL) {\n    fprintf(stderr, \"Failed to load %s: %s\\n\", file, dlerror());\n  }\n  fqd_routemgr_add_handle(handle);\n}\n\nvoid global_functions_init(const char *libexecdir) {\n#define GFR(a) global_function_register(#a, (void (*)(void))a)\n  GFR(fqd_route_prog__true__);\n  GFR(fqd_route_prog__route_contains__s);\n  GFR(fqd_route_prog__payload_prefix__s);\n#undef GFR\n\n  DIR *dir = opendir(libexecdir);\n  if(!dir) return;\n  struct dirent *de;\n  while(NULL != (de = readdir(dir))) {\n    char path[PATH_MAX];\n    struct stat sb;\n    int namelen = strlen(de->d_name);\n    if(namelen < 4 || memcmp(de->d_name + namelen - 3, \".so\", 3)) continue;\n    snprintf(path, sizeof(path), \"%s/%s\", libexecdir, de->d_name);\n    if(stat(path, &sb) == -1) continue;\n    if((sb.st_mode & S_IFMT) == S_IFREG || (sb.st_mode & S_IFMT) == S_IFLNK) {\n      fqd_route_load_module(libexecdir, de->d_name, NULL);\n    }\n  }\n  closedir(dir);\n}\n\nbool fqd_route_prog__true__(fq_msg *m, int nargs, valnode_t *args) {\n  fq_assert(nargs == 0);\n  (void)m;\n  (void)nargs;\n  (void)args;\n  return true;\n}\n\nbool\nfqd_route_prog__route_contains__s(fq_msg *m, int nargs, valnode_t *args) {\n  int flen;\n  fq_assert(nargs == 1);\n  fq_assert(args[0].value_type == RP_VALUE_STRING);\n  flen = strlen(args[0].value.s);\n  if(flen > m->route.len) return false;\n  return memmem(m->route.name, m->route.len, args[0].value.s, flen) != NULL;\n}\n\nbool\nfqd_route_prog__payload_prefix__s(fq_msg *m, int nargs, valnode_t *args) {\n  uint32_t flen;\n  fq_assert(nargs == 1);\n  fq_assert(args[0].value_type == RP_VALUE_STRING);\n  flen = strlen(args[0].value.s);\n  if(flen > m->payload_len) return false;\n  if(memcmp(args[0].value.s, m->payload, flen) == 0)\n    return true;\n  return false;\n}\n\nbool\nfqd_route_prog__payload_contains__s(fq_msg *m, int nargs, valnode_t *args) {\n    int flen;\n    fq_assert(nargs == 1);\n    fq_assert(args[0].value_type == RP_VALUE_STRING);\n    flen = strlen(args[0].value.s);\n    if(flen > m->payload_len) return false;\n    return memmem(m->payload, m->payload_len, args[0].value.s, flen) != NULL;\n}\n"
  },
  {
    "path": "fqd_queue.c",
    "content": "/*\n * Copyright (c) 2013 OmniTI Computer Consulting, Inc.\n * All rights reserved.\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\n#include <stdio.h>\n#include <string.h>\n#include <stdint.h>\n#include <stddef.h>\n#include <stdlib.h>\n#include <unistd.h>\n#include <sys/socket.h>\n#include <netinet/in.h>\n#include <arpa/inet.h>\n#include <pthread.h>\n\n#include \"fqd.h\"\n#include \"fqd_private.h\"\n#include \"ck_pr.h\"\n#include \"fq_dtrace.h\"\n\n#define DEFAULT_QUEUE_LIMIT 16384\n\n#define cprintf(fd, fmt, ...) do { \\\n  char scratch[1024]; \\\n  int len; \\\n  len = snprintf(scratch, sizeof(scratch), fmt, __VA_ARGS__); \\\n  write(fd, scratch, len); \\\n} while(0)\n#define cwrite(fd, str) write(fd, str, strlen(str))\nint fqd_queue_write_json(int fd, fqd_queue *q) {\n  int i, seen = 0;\n  cwrite(fd, \"{\\n\");\n  cprintf(fd, \"  \\\"private\\\": %s,\\n\", q->private ? \"true\" : \"false\");\n  cprintf(fd, \"  \\\"type\\\": \\\"%s\\\",\\n\", q->impl->name);\n  cprintf(fd, \"  \\\"policy\\\": \\\"%s\\\",\\n\", (q->policy == FQ_POLICY_DROP) ? \"drop\" : \"block\");\n  cprintf(fd, \"  \\\"backlog_limit\\\": %d,\\n\", q->backlog_limit);\n  cprintf(fd, \"  \\\"backlog\\\": %d,\\n\", q->backlog);\n  cprintf(fd, \"  \\\"dropped_to\\\": %d,\\n\", q->dropped_to);\n  cprintf(fd, \"  \\\"refcnt\\\": %d,\\n\", q->refcnt);\n  cwrite(fd, \"  \\\"clients\\\": [\");\n  for(i=0;i<MAX_QUEUE_CLIENTS;i++) {\n    remote_client *c = q->downstream[i];\n    if(c) {\n      char buf[INET6_ADDRSTRLEN+1];\n      buf[0] = '\\0';\n      inet_ntop(AF_INET, &c->remote.sin_addr, buf, sizeof(buf));\n      if(seen++) cwrite(fd, \"    ,{\\n\");\n      else       cwrite(fd, \"    {\\n\");\n      cprintf(fd, \"    \\\"user\\\": \\\"%.*s\\\"\\n\", c->user.len, c->user.name);\n      cprintf(fd, \"   ,\\\"remote_addr\\\": \\\"%s\\\"\\n\", buf);\n      cprintf(fd, \"   ,\\\"remote_port\\\": \\\"%d\\\"\\n\", ntohs(c->remote.sin_port));\n      if(c->data) {\n        cprintf(fd, \"   ,\\\"mode\\\": \\\"%s\\\"\\n\", (c->data->mode == FQ_PROTO_DATA_MODE) ? \"client\" : \"peer\");\n        cprintf(fd, \"   ,\\\"no_exchange\\\": \\\"%u\\\"\\n\", c->data->no_exchange);\n        cprintf(fd, \"   ,\\\"no_route\\\": \\\"%u\\\"\\n\", c->data->no_route);\n        cprintf(fd, \"   ,\\\"routed\\\": \\\"%u\\\"\\n\", c->data->routed);\n        cprintf(fd, \"   ,\\\"dropped\\\": \\\"%u\\\"\\n\", c->data->dropped);\n        cprintf(fd, \"   ,\\\"size_dropped\\\": \\\"%u\\\"\\n\", c->data->size_dropped);\n        cprintf(fd, \"   ,\\\"msgs_in\\\": \\\"%u\\\"\\n\", c->data->msgs_in);\n        cprintf(fd, \"   ,\\\"msgs_out\\\": \\\"%u\\\"\\n\", c->data->msgs_out);\n        cprintf(fd, \"   ,\\\"octets_in\\\": \\\"%u\\\"\\n\", c->data->octets_in);\n        cprintf(fd, \"   ,\\\"octets_out\\\": \\\"%u\\\"\\n\", c->data->octets_out);\n      }\n      cwrite(fd, \"    }\\n\");\n    }\n  }\n  cwrite(fd, \"]\\n\");\n  cwrite(fd, \"}\");\n  return 0;\n}\n\nint fqd_queue_sprint(char *buf, int len, fqd_queue *q) {\n  return\n    snprintf(buf, len, \"%s:%s,%s,backlog=%d\",\n             q->impl->name, q->private ? \"private\" : \"public\",\n             (q->policy == FQ_POLICY_DROP) ? \"drop\" : \"block\",\n             q->backlog_limit);\n}\n\nvoid fqd_queue_dtrace_pack(fq_dtrace_queue_t *d, fqd_queue *s) {\n  d->name = (char *)s->name.name;\n  d->private = s->private;\n  d->policy = s->policy;\n  d->type = (char *)s->impl->name;\n}\n\nstatic void fqd_queue_free(fqd_queue *q);\n\nfq_rk *\nfqd_queue_name(fqd_queue *q) {\n  return &q->name;\n}\n\nvoid\nfqd_queue_enqueue(fqd_queue *q, fq_msg *m, int *dropped) {\n  while(1) {\n    uint32_t backlog;\n    if(q->backlog_limit) {\n      backlog = ck_pr_load_uint(&q->backlog);\n      if(backlog < q->backlog_limit) break;\n    }\n    if(q->policy == FQ_POLICY_DROP) {\n      if(dropped) (*dropped)++;\n      ck_pr_inc_32(&q->dropped_to);\n      if(FQ_QUEUE_DROP_ENABLED()) {\n        fq_dtrace_msg_t dm;\n        fq_dtrace_queue_t dq;\n        DTRACE_PACK_MSG(&dm, m);\n        DTRACE_PACK_QUEUE(&dq, q);\n        FQ_QUEUE_DROP(&dq, &dm);\n      }\n      return;\n    }\n    else {\n      pthread_mutex_lock(&q->lock);\n    again:\n      if(q->backlog_limit) {\n        backlog = ck_pr_load_uint(&q->backlog);\n        if(backlog < q->backlog_limit) {\n          pthread_mutex_unlock(&q->lock);\n          break;\n        }\n      }\n      if(FQ_QUEUE_BLOCK_ENABLED()) {\n        fq_dtrace_msg_t dm;\n        fq_dtrace_queue_t dq;\n        DTRACE_PACK_MSG(&dm, m);\n        DTRACE_PACK_QUEUE(&dq, q);\n        FQ_QUEUE_BLOCK(&dq, &dm);\n      }\n      pthread_cond_wait(&q->cv, &q->lock);\n      goto again;\n    }\n  }\n  ck_pr_inc_32(&q->backlog);\n  if(FQ_QUEUE_ENQUEUE_ENABLED()) {\n    fq_dtrace_msg_t dm;\n    fq_dtrace_queue_t dq;\n    DTRACE_PACK_MSG(&dm, m);\n    DTRACE_PACK_QUEUE(&dq, q);\n    FQ_QUEUE_ENQUEUE(&dq, &dm);\n  }\n  q->impl->enqueue(q->impl_data, m);\n}\n\nfq_msg *\nfqd_queue_dequeue(fqd_queue *q) {\n  fq_msg *msg = q->impl->dequeue(q->impl_data);\n  if(msg) {\n    ck_pr_dec_32(&q->backlog);\n    if(q->policy == FQ_POLICY_BLOCK) pthread_cond_signal(&q->cv);\n  }\n  return msg;\n}\n\nint\nfqd_queue_register_client(fqd_queue *q, remote_client *c) {\n  int i;\n  int max_clients = q->private ? 1 : MAX_QUEUE_CLIENTS;\n  fqd_queue_ref(q);\n  fqd_remote_client_ref(c);\n  for(i=0;i<max_clients;i++) {\n    if(q->downstream[i] == NULL) {\n      if(ck_pr_cas_ptr(&q->downstream[i], NULL, c) == true) {\n#ifdef DEBUG\n        fq_debug(FQ_DEBUG_CONFIG, \"%.*s adding %s\\n\",\n                 q->name.len, q->name.name, c->pretty);\n#endif\n        return 0;\n      }\n    }\n  }\n  if(fqd_remote_client_deref(c)) abort();\n  if(fqd_queue_deref(q)) abort();\n  return -1;\n}\nbool\nfqd_queue_deregister_client(fqd_queue *q, remote_client *c) {\n  int i;\n  bool found = false;\n  int max_clients = q->private ? 1 : MAX_QUEUE_CLIENTS;\n  for(i=0;i<max_clients;i++) {\n    if(q->downstream[i] == c) {\n      q->downstream[i] = NULL;\n      fq_debug(FQ_DEBUG_CONFIG, \"%.*s dropping %s\\n\",\n              q->name.len, q->name.name, c->pretty);\n      if(fqd_remote_client_deref(c)) abort();\n      if(fqd_queue_deref(q)) abort();\n      if(found) abort();\n      found = true;\n    }\n  }\n  if(q->permanent) return false;\n  for(i=0;i<max_clients;i++) if(q->downstream[i]) return false;\n  return true;\n}\nint\nfqd_queue_cmp(const fqd_queue *a, const fqd_queue *b) {\n  return fq_rk_cmp(&a->name, &b->name);\n}\n\nvoid\nfqd_queue_ref(fqd_queue *q) {\n  fq_stacktrace(FQ_DEBUG_MEM,\"fqd_queue_ref\",1,2);\n  ck_pr_inc_uint(&q->refcnt);\n  fq_debug(FQ_DEBUG_MEM, \"Q[%.*s] -> refcnt:%u\\n\", q->name.len, q->name.name, q->refcnt);\n}\nbool\nfqd_queue_deref(fqd_queue *q) {\n  bool zero;\n  fq_stacktrace(FQ_DEBUG_MEM,\"fqd_queue_deref\",1,2);\n  ck_pr_dec_uint_zero(&q->refcnt, &zero);\n  fq_debug(FQ_DEBUG_MEM, \"Q[%.*s] -> refcnt:%u\\n\", q->name.len, q->name.name, q->refcnt);\n  if(zero) {\n    FQ_QUEUE_DESTROY(q->name.len, (char *)q->name.name);\n    fq_debug(FQ_DEBUG_CONFIG, \"dropping queue(%p) %.*s\\n\",\n            (void *)q, q->name.len, q->name.name);\n    fqd_queue_free(q);\n    return true;\n  }\n  return false;\n}\nuint32_t\nfqd_queue_get_backlog_limit(fqd_queue *q) {\n  return q->backlog_limit;\n}\nvoid\nfqd_queue_set_backlog_limit(fqd_queue *q, uint32_t l) {\n  q->backlog_limit = l;\n}\nqueue_policy_t\nfqd_queue_get_policy(fqd_queue *q) {\n  return q->policy;\n}\nvoid\nfqd_queue_set_policy(fqd_queue *q, queue_policy_t p) {\n  q->policy = p;\n}\nstatic void\nfqd_queue_free(fqd_queue *q) {\n  pthread_mutex_destroy(&q->lock);\n  pthread_cond_destroy(&q->cv);\n  q->impl->dispose(&q->name, q->impl_data);\n  free(q);\n}\nfqd_queue *\nfqd_queue_get(fq_rk *qname, const char *type, const char *params,\n              int errlen, char *err) {\n  bool error = false, created = false;\n  fqd_queue *q = NULL;\n  fqd_queue *nq = NULL;\n  fqd_config *config;\n  char *params_copy, *lastsep = NULL, *tok;\n  int permanent = -1; /* unset */\n  bool private = true;\n  queue_policy_t policy = FQ_POLICY_DROP;\n  uint32_t backlog_limit = DEFAULT_QUEUE_LIMIT;\n  fqd_queue_impl *queue_impl = &fqd_queue_mem_impl;\n\n  if(!type) type = FQ_DEFAULT_QUEUE_TYPE;\n  if(!strcmp(type, \"disk\")) {\n    queue_impl = &fqd_queue_jlog_impl;\n  }\n  else if(strcmp(type, \"mem\")) {\n    snprintf(err, errlen, \"invalid queue type: %s\", type);\n    return NULL;\n  }\n  params_copy = strdup(params ? params : \"\");\n  if(!params_copy) {\n    snprintf(err, errlen, \"memory exhaustion\");\n    return NULL;\n  }\n  for (tok = strtok_r(params_copy, \",\", &lastsep);\n       tok;\n       tok = strtok_r(NULL, \",\", &lastsep)) {\n    if(!strcmp(tok, \"private\")) private = true;\n    else if(!strcmp(tok, \"public\")) private = false;\n    else if(!strcmp(tok, \"drop\")) policy = FQ_POLICY_DROP;\n    else if(!strcmp(tok, \"block\")) policy = FQ_POLICY_BLOCK;\n    else if(!strncmp(tok, \"backlog=\", 8)) {\n      backlog_limit = atoi(tok + 8);\n    }\n    else if(!strcmp(tok, \"permanent\")) permanent = 1;\n    else if(!strcmp(tok, \"transient\")) permanent = 0;\n    else {\n      error = true;\n      snprintf(err, errlen, \"invalid queue param: %s\", tok);\n      break;\n    }\n    if(lastsep == NULL) break;\n  }\n  free(params_copy);\n  if(error) return NULL;\n\n  config = fqd_config_get();\n  nq = q = fqd_config_get_registered_queue(config, qname);\n  if(q) {\n    if(q->private) {\n      int i;\n      for(i=0; i<MAX_QUEUE_CLIENTS; i++) {\n        if(q->downstream[i]) {\n          snprintf(err, errlen, \"requested queue is private and in use\\n\");\n          fqd_config_release(config);\n          return NULL;\n        }\n      }\n    }\n  }\n  else {\n    nq = calloc(1, sizeof(*nq));\n    nq->refcnt = 0;\n    nq->private = private;\n    nq->policy = policy;\n    nq->backlog_limit = backlog_limit;\n    if(permanent == 1) nq->permanent = true;\n    pthread_mutex_init(&nq->lock, NULL);\n    pthread_cond_init(&nq->cv, NULL);\n    memcpy(&nq->name, qname, sizeof(*qname));\n    nq->impl = queue_impl;\n    nq->impl_data = nq->impl->setup(qname, &nq->backlog);\n    if(nq->impl_data == NULL) {\n      snprintf(err, errlen, \"initialization of %s queue failed\",\n               nq->impl->name);\n      fqd_queue_free(nq);\n      nq = q = NULL;\n    }\n  }\n  fqd_config_release(config);\n\n  if(nq != NULL) {\n    q = fqd_config_register_queue(nq, NULL);\n    if(nq != q) {\n      fqd_queue_free(nq);\n    }\n    else {\n      created = true;\n    }\n  }\n  if(q && q->impl != queue_impl) {\n    snprintf(err, errlen, \"requested type %s, queue is %s\",\n             type, q->impl->name);\n    q = NULL;\n  }\n  else if(q && q->private != private) {\n    snprintf(err, errlen, \"requested %s, queue is %s\",\n             private ? \"private\" : \"public\",\n             q->private ? \"private\" : \"public\");\n    q = NULL;\n  }\n  else if(q && q->policy != policy) {\n    snprintf(err, errlen, \"request %s, queue is %s\",\n             (policy == FQ_POLICY_DROP) ? \"drop\" : \"block\",\n             (q->policy == FQ_POLICY_DROP) ? \"drop\" : \"block\");\n    q = NULL;\n  }\n  /* We don't actually enforce a backlog difference */\n\n  if(q && permanent >= 0) {\n    if(!permanent) {\n      fqd_config_make_trans_queue(q);\n      q->permanent = false;\n    }\n    else {\n      fqd_config_make_perm_queue(q);\n      q->permanent = true;\n    }\n  }\n  if(q) {\n    (void)created;\n    FQ_QUEUE_CREATE_SUCCESS(qname->len, (char *)qname->name, created,\n                            (char *)q->impl->name, q->private, q->policy);\n  }\n  else {\n    (void)err;\n    FQ_QUEUE_CREATE_FAILURE(qname->len, (char *)qname->name, err);\n  }\n  return q;\n}\n\n"
  },
  {
    "path": "fqd_queue_jlog.c",
    "content": "/*\n * Copyright (c) 2013 OmniTI Computer Consulting, Inc.\n * All rights reserved.\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\n#include \"fqd.h\"\n#include <stdio.h>\n#include <stdlib.h>\n#include <stddef.h>\n#include <unistd.h>\n#include <fcntl.h>\n#include <uuid/uuid.h>\n#include <ftw.h>\n#include <jlog.h>\n#include <jlog_private.h>\n#include \"ck_pr.h\"\n\nstruct queue_jlog {\n  bool      auto_chkpt;\n  uint32_t  nenqueued;\n  uint32_t  last_seen_nenqueued;\n  char     *qpath;\n  jlog_ctx *writer;\n  jlog_ctx *reader;\n  jlog_id   start;\n  jlog_id   finish;\n  jlog_id   last_dequeued;\n  int       count;\n\n  uuid_t    uuid;\n  /*\n   * If we create a transient queue, diconnect and reconnect with the same\n   * transient queue then we expose a condition where the original queue\n   * has not been reaped (as it exists in older config version, but the\n   * new queue has the same name.\n   *\n   * For in-memory queues, this is no issue.  Because the jlog implementation\n   * stores the queue on disk (and must find it later on restart) there is a\n   * chance that the disposal of an old queue would wipe the new queue's\n   * on-disk structure rendering it completely busted.\n   *\n   * When a jlog queue is initially setup here, we generate a uuid and store\n   * that in the path/.sig file.  If we have a race such as above, then\n   * the disposal will come along and notice that the .sig does not match\n   * its uuid.  This indicates to the disposal that another queue owns the\n   * on-disk structure and it should skip the unlink/rmdir removal.\n   */\n  uint32_t  errors;\n};\n\nstatic void queue_jlog_enqueue(fqd_queue_impl_data f, fq_msg *m) {\n  struct queue_jlog *d = (struct queue_jlog *)f;\n  size_t wlen;\n  wlen = offsetof(fq_msg, payload) + m->payload_len;\n  if(jlog_ctx_write(d->writer, m, wlen) != 0) {\n    ck_pr_inc_uint(&d->errors);\n  }\n  ck_pr_inc_uint(&d->nenqueued);\n}\nstatic fq_msg *queue_jlog_dequeue(fqd_queue_impl_data f) {\n  struct queue_jlog *d = (struct queue_jlog *)f;\n  jlog_message msg;\n  fq_msg *m;\n  if(d->count == 0 && d->last_seen_nenqueued == d->nenqueued) return NULL;\n retry:\n  if(d->count <= 0) {\n    d->count = jlog_ctx_read_interval(d->reader, &d->start, &d->finish);\n    fq_debug(FQ_DEBUG_IO, \"jlog read batch count -> %d\\n\", d->count);\n    if(d->count < 0) {\n      char idxfile[PATH_MAX];\n      fq_debug(FQ_DEBUG_IO, \"jlog_ctx_read_interval: %s\\n\",\n               jlog_ctx_err_string(d->reader));\n      switch (jlog_ctx_err(d->reader)) {\n        case JLOG_ERR_FILE_CORRUPT:\n        case JLOG_ERR_IDX_CORRUPT:\n          jlog_repair_datafile(d->reader, d->start.log);\n          jlog_repair_datafile(d->reader, d->start.log + 1);\n          fq_debug(FQ_DEBUG_IO,\n                \"jlog reconstructed, deleting corresponding index.\\n\");\n          STRSETDATAFILE(d->reader, idxfile, d->start.log);\n          strncpy(idxfile + strlen(idxfile), INDEX_EXT, sizeof(idxfile) - strlen(idxfile));\n          unlink(idxfile);\n          STRSETDATAFILE(d->reader, idxfile, d->start.log + 1);\n          strncpy(idxfile + strlen(idxfile), INDEX_EXT, sizeof(idxfile) - strlen(idxfile));\n          unlink(idxfile);\n          break;\n        default:\n          break;\n      }\n    }\n    if(d->count <= 0) return NULL;\n  }\n  if(jlog_ctx_read_message(d->reader, &d->start, &msg) == -1) {\n    d->count = 0;\n    return NULL;\n  }\n  if(d->last_dequeued.log > d->start.log ||\n     (d->last_dequeued.log == d->start.log &&\n      d->last_dequeued.marker > d->start.marker)) {\n    d->count--;\n    JLOG_ID_ADVANCE(&d->start);\n    goto retry;\n  }\n  if(msg.mess_len < sizeof(fq_msg)-1)\n    m = NULL;\n  else {\n    off_t expected_len;\n    uint32_t payload_len;\n    m = (fq_msg *)msg.mess;\n    memcpy(&payload_len, &m->payload_len, sizeof(m->payload_len));\n    expected_len = offsetof(fq_msg, payload) + payload_len;\n    if(expected_len != msg.mess_len) m = NULL;\n    else {\n      m = malloc(expected_len);\n      memcpy(m, msg.mess, expected_len);\n      m->sender_msgid.id.u32.p3 = d->start.log;\n      m->sender_msgid.id.u32.p4 = d->start.marker;\n    }\n  }\n  d->count--;\n  fq_debug(FQ_DEBUG_IO, \"jlog batch count -> %d\\n\", d->count);\n  if(d->count == 0) {\n    if(d->auto_chkpt) {\n      jlog_ctx_read_checkpoint(d->reader, &d->start);\n    }\n  }\n  d->last_dequeued = d->start;\n  JLOG_ID_ADVANCE(&d->start);\n  ck_pr_inc_uint(&d->last_seen_nenqueued);\n  return m;\n}\n\n/*\n * returns -1 when the incoming checkpoint is out of range of the log\n * returns -2 if there was an error actually setting the checkpoint\n */\nstatic int queue_log_add_checkpoint(fqd_queue_impl_data data, const char *name, const fq_msgid *id)\n{\n  struct queue_jlog *d = (struct queue_jlog *)data;\n\n  jlog_id jid = {\n    .log = id->id.u32.p1,\n    .marker = id->id.u32.p2\n  };\n\n  /* ensure the checkpoint makes sense */\n  jlog_id first = {\n    .log = 0,\n    .marker = 0\n  };\n  jlog_id last = {\n    .log = 0,\n    .marker = 0\n  };\n  jlog_ctx_first_log_id(d->reader, &first);\n  jlog_ctx_last_log_id(d->reader, &last);\n\n  if (! (jid.log >= first.log && jid.log <= last.log &&\n         jid.marker >= first.marker && jid.marker <= last.marker)) {\n    return -1;\n  }\n\n  char **subs;\n  int sub_count = jlog_ctx_list_subscribers(d->reader, &subs);\n\n  int have_it = 0;\n  for (int i = 0; i < sub_count; i++) {\n    have_it += strcmp(subs[i], name) == 0 ? 1 : 0;\n  }\n\n  if (have_it == 0) {\n    jlog_ctx_add_subscriber(d->reader, name, JLOG_BEGIN);\n  }\n\n  if (jlog_ctx_read_checkpoint(d->reader, &jid) == -1) {\n    /*\n       If we failed to checkpoint we are in a situation where the 'add_subscriber' call above put\n       them at the beginning of the log so we have to remove the subscriber if we just added them\n\n       However, if they already existed and had a previous good checkpoint, leave it alone\n    */\n    if (have_it == 0) {\n      jlog_ctx_remove_subscriber(d->reader, name);\n    }\n    return -2;\n  }\n  return 0;\n}\n\n/*\n * return -1 if the subscriber doesn't exist\n * return 0 on success\n */\nstatic int queue_log_remove_checkpoint(fqd_queue_impl_data data, const char *name)\n{\n  struct queue_jlog *d = (struct queue_jlog *)data;\n\n  if (jlog_ctx_remove_subscriber(d->reader, name) == 0) {\n    return -1;\n  }\n  return 0;\n}\n\n/*\n * return -1 if the subscriber doesn't exist\n * return -2 if we can't reset the checkpoint\n * return 0 on success\n */\nstatic int queue_log_reset_to_checkpoint(fqd_queue_impl_data data, const char *name)\n{\n  struct queue_jlog *d = (struct queue_jlog *)data;\n\n  char **subs;\n  int sub_count = jlog_ctx_list_subscribers(d->reader, &subs);\n\n  int have_it = 0;\n  for (int i = 0; i < sub_count; i++) {\n    have_it += strcmp(subs[i], name) == 0 ? 1 : 0;\n  }\n\n  if (have_it == 0) {\n    return -1;\n  }\n\n  jlog_id checkpoint;\n  if (jlog_get_checkpoint(d->reader, name, &checkpoint) == -1) {\n    return -2;\n  }\n\n  if (jlog_ctx_read_checkpoint(d->reader, &checkpoint) == -1) {\n    return -2;\n  }\n  return 0;\n}\n\n\nstatic int write_sig(struct queue_jlog *d) {\n  char sigfile[PATH_MAX];\n  int fd;\n  snprintf(sigfile, sizeof(sigfile), \"%s/.sig\", d->qpath);\n  fd = open(sigfile, O_CREAT|O_TRUNC|O_WRONLY, 0640);\n  if(fd < 0) return -1;\n  write(fd, d->uuid, 16);\n  close(fd);\n  return 0;\n}\nstatic int read_sig(struct queue_jlog *d, uuid_t out) {\n  char sigfile[PATH_MAX];\n  int fd, rv;\n  snprintf(sigfile, sizeof(sigfile), \"%s/.sig\", d->qpath);\n  fd = open(sigfile, O_RDONLY);\n  if(fd < 0) return -1;\n  rv = read(fd, out, 16);\n  close(fd);\n  return (rv == 16) ? 0 : -1;\n}\nstatic fqd_queue_impl_data queue_jlog_setup(fq_rk *qname, uint32_t *count) {\n  char qpath[PATH_MAX];\n  jlog_id chkpt;\n  struct queue_jlog *d;\n\n  d = calloc(1, sizeof(*d));\n  d->auto_chkpt = true;\n  fqd_config_construct_queue_path(qpath, sizeof(qpath), qname);\n  d->qpath = strdup(qpath);\n  d->writer = jlog_new(d->qpath);\n\n  jlog_ctx_set_pre_commit_buffer_size(d->writer, 1024 * 1024);\n  jlog_ctx_set_multi_process(d->writer, 0);\n  jlog_ctx_set_use_compression(d->writer, 1);\n\n  if(jlog_ctx_open_writer(d->writer) != 0) {\n    jlog_ctx_close(d->writer);\n    d->writer = jlog_new(d->qpath);\n    jlog_ctx_set_pre_commit_buffer_size(d->writer, 1024 * 1024);\n    jlog_ctx_set_multi_process(d->writer, 0);\n    jlog_ctx_set_use_compression(d->writer, 1);\n    if(jlog_ctx_init(d->writer) != 0) {\n      fq_debug(FQ_DEBUG_IO, \"jlog init: %s\\n\", jlog_ctx_err_string(d->writer));\n      goto bail;\n    }\n    jlog_ctx_close(d->writer);\n    d->writer = jlog_new(d->qpath);\n    jlog_ctx_set_pre_commit_buffer_size(d->writer, 1024 * 1024);\n    jlog_ctx_set_multi_process(d->writer, 0);\n    jlog_ctx_set_use_compression(d->writer, 1);\n    if(jlog_ctx_open_writer(d->writer) != 0) {\n      fq_debug(FQ_DEBUG_IO, \"jlog writer: %s\\n\", jlog_ctx_err_string(d->writer));\n      goto bail;\n    }\n  }\n\n  /* 128MB journal chunks */\n  jlog_ctx_alter_journal_size(d->writer, 128 * 1024 * 1024);\n\n  d->reader = jlog_new(d->qpath);\n  if(jlog_get_checkpoint(d->reader, \"fq\", &chkpt) != 0) {\n    if(jlog_ctx_add_subscriber(d->reader, \"fq\", JLOG_BEGIN) != 0) {\n      fq_debug(FQ_DEBUG_IO, \"jlog add sub: %s\\n\", jlog_ctx_err_string(d->reader));\n      goto bail;\n    }\n  }\n  if(jlog_ctx_open_reader(d->reader, \"fq\") != 0) {\n    fq_debug(FQ_DEBUG_IO, \"jlog: %s\\n\", jlog_ctx_err_string(d->reader));\n    goto bail;\n  }\n  uuid_generate(d->uuid);\n  write_sig(d);\n  *count = d->count = jlog_ctx_read_interval(d->reader, &d->start, &d->finish);\n  (void)qname;\n  return d;\n\n bail:\n  if(d->writer) jlog_ctx_close(d->writer);\n  if(d->reader) jlog_ctx_close(d->reader);\n  free(d->qpath);\n  free(d);\n  return NULL;\n}\n\nstatic int\nmulti_unlink(const char *path, const struct stat *sb, int d, struct FTW *f) {\n  (void)sb;\n  (void)f;\n  if(d == FTW_D) rmdir(path);\n  else unlink(path);\n  return 0;\n}\n\nstatic void queue_jlog_dispose(fq_rk *qname, fqd_queue_impl_data f)\n{\n  struct queue_jlog *d = (struct queue_jlog *)f;\n  uuid_t exist;\n  (void)qname;\n\n  if (d == NULL) {\n    /* there was likely a total failure to init this queue type\n       due to file system permissions, bail\n    */\n    return;\n  }\n\n  uuid_clear(exist);\n  read_sig(d, exist);\n  if(uuid_compare(d->uuid, exist) == 0) {\n    /* This is my jlog queue ... I can delete it */\n    fq_debug(FQ_DEBUG_IO, \"jlog: removing %s\\n\", d->qpath);\n    nftw(d->qpath, multi_unlink, 2, FTW_DEPTH);\n    rmdir(d->qpath);\n  }\n  free(d);\n}\n\nfqd_queue_impl fqd_queue_jlog_impl = {\n  .name = \"disk\",\n  .setup = queue_jlog_setup,\n  .enqueue = queue_jlog_enqueue,\n  .dequeue = queue_jlog_dequeue,\n  .dispose = queue_jlog_dispose,\n  .add_checkpoint = queue_log_add_checkpoint,\n  .remove_checkpoint = queue_log_remove_checkpoint,\n  .reset_checkpoint = queue_log_reset_to_checkpoint\n};\n"
  },
  {
    "path": "fqd_queue_mem.c",
    "content": "/*\n * Copyright (c) 2013 OmniTI Computer Consulting, Inc.\n * All rights reserved.\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\n#include \"fqd.h\"\n#include <stdlib.h>\n#include <ck_fifo.h>\n\nstruct queue_mem {\n  uint32_t              qlen;\n  ck_fifo_spsc_t        q;\n  ck_fifo_spsc_entry_t *qhead;\n};\n\nstatic void queue_mem_enqueue(fqd_queue_impl_data f, fq_msg *m) {\n  struct queue_mem *d = (struct queue_mem *)f;\n  ck_fifo_spsc_enqueue_lock(&d->q);\n  ck_fifo_spsc_entry_t *fifo_entry = ck_fifo_spsc_recycle(&d->q);\n  if (fifo_entry == NULL) {\n    fifo_entry = malloc(sizeof(ck_fifo_spsc_entry_t));\n  }\n  fq_msg_ref(m);\n  ck_fifo_spsc_enqueue(&d->q, fifo_entry, m);\n  ck_fifo_spsc_enqueue_unlock(&d->q);\n  ck_pr_inc_uint(&d->qlen);\n}\nstatic fq_msg *queue_mem_dequeue(fqd_queue_impl_data f) {\n  struct queue_mem *d = (struct queue_mem *)f;\n  fq_msg *m = NULL;\n  ck_fifo_spsc_dequeue_lock(&d->q);\n  if(ck_fifo_spsc_dequeue(&d->q, &m) == true) {\n    ck_fifo_spsc_dequeue_unlock(&d->q);\n    ck_pr_dec_uint(&d->qlen);\n    return m;\n  }\n  ck_fifo_spsc_dequeue_unlock(&d->q);\n  return NULL;\n}\nstatic fqd_queue_impl_data queue_mem_setup(fq_rk *qname, uint32_t *count) {\n  struct queue_mem *d;\n  d = calloc(1, sizeof(*d));\n  d->qhead = malloc(sizeof(ck_fifo_spsc_entry_t));\n  *count = 0;\n  ck_fifo_spsc_init(&d->q, d->qhead);\n  (void)qname;\n  return d;\n}\nstatic void queue_mem_dispose(fq_rk *qname, fqd_queue_impl_data f) {\n  struct queue_mem *d = (struct queue_mem *)f;\n  fq_msg *m;\n  (void)qname;\n  while(NULL != (m = queue_mem_dequeue(d))) {\n    fq_msg_deref(m);\n  }\n  ck_fifo_spsc_entry_t *garbage = NULL;\n  ck_fifo_spsc_deinit(&d->q, &garbage);\n  while (garbage != NULL) {\n    ck_fifo_spsc_entry_t *n = garbage->next;\n    free(garbage);\n    garbage = n;\n  }\n  free(d);\n}\n\n/* not supported for now */\nstatic int queue_mem_add_checkpoint(fqd_queue_impl_data data, const char *name, const fq_msgid *id) {\n  return -1;\n}\n\n/* not supported for now */\nstatic int queue_mem_remove_checkpoint(fqd_queue_impl_data data, const char *name) {\n  return -1;\n}\n\n/* not supported for now */\nstatic int queue_mem_reset_to_checkpoint(fqd_queue_impl_data data, const char *name) {\n  return -1;\n}\n\nfqd_queue_impl fqd_queue_mem_impl = {\n  .name = \"mem\",\n  .setup = queue_mem_setup,\n  .enqueue = queue_mem_enqueue,\n  .dequeue = queue_mem_dequeue,\n  .dispose = queue_mem_dispose,\n  .add_checkpoint = queue_mem_add_checkpoint,\n  .remove_checkpoint = queue_mem_remove_checkpoint,\n  .reset_checkpoint = queue_mem_reset_to_checkpoint\n};\n"
  },
  {
    "path": "fqd_routemgr.c",
    "content": "/*\n * Copyright (c) 2013 OmniTI Computer Consulting, Inc.\n * All rights reserved.\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\n#include <stdio.h>\n#include <unistd.h>\n#include <string.h>\n#include <ck_pr.h>\n#include <ck_hs.h>\n#include <ctype.h>\n#include <dlfcn.h>\n#include \"fqd.h\"\n#include \"fqd_private.h\"\n#include \"fq_dtrace.h\"\n#include <arpa/inet.h>\n\nuint32_t global_route_id = 1;\n#define RR_SET_SIZE 32\n#define MAX_QUEUE_TARGETS 30\n\nstruct dl_handles {\n  void *handle;\n  struct dl_handles *next;\n} *global_handles;\n\nvoid fqd_routemgr_add_handle(void *handle) {\n  struct dl_handles *nh = calloc(1, sizeof(*nh));\n  nh->handle = handle;\n  nh->next = global_handles;\n  global_handles = nh;\n}\n\nstruct function_registry_entry {\n  char *name;\n  void (*handle)(void);\n};\n\nstatic void *gen_malloc(size_t r) { return malloc(r); }\nstatic void gen_free(void *p, size_t b, bool r) { (void)b; (void)r; free(p); }\nstatic struct ck_malloc malloc_ck_hs = { .malloc = gen_malloc, .free = gen_free };\nstatic unsigned long __ck_hash_name(const void *v, unsigned long seed) {\n  unsigned long hash = seed;\n  const struct function_registry_entry *e = v;\n  int len = strlen(e->name);\n  for(int i=0; i<len; i++)\n    hash = hash ^ (hash << 8 | e->name[i]);\n  return hash;\n}\nstatic bool __ck_compare_name(const void *a, const void *b) {\n  const struct function_registry_entry *ae = a, *be = b;\n  return 0 == strcmp(ae->name, be->name);\n}\nstatic ck_hs_t *global_functions = NULL;\n\nvoid global_function_register(const char *name, void (*f)(void)) {\n  if(!global_functions) {\n    ck_hs_t *map = calloc(1, sizeof(*map));\n    ck_hs_init(map, CK_HS_MODE_OBJECT | CK_HS_MODE_SPMC,\n               __ck_hash_name, __ck_compare_name, &malloc_ck_hs, 100, 0);\n    global_functions = map;\n  }\n  struct function_registry_entry *old = NULL, *entry = calloc(1, sizeof(*entry));\n  entry->name = strdup(name);\n  entry->handle = f;\n  unsigned long hash = CK_HS_HASH(global_functions, __ck_hash_name, entry);\n  ck_hs_set(global_functions, hash, (void *)entry, (void **)&old);\n  if(old) {\n    free(old->name);\n    free(old);\n  }\n}\n\nstatic void (*global_function_lookup(const char *name))(void) {\n  struct function_registry_entry *entry = NULL, stub = { .name = (char *)name };\n  unsigned long hash = CK_HS_HASH(global_functions, __ck_hash_name, &stub);\n  entry = ck_hs_get(global_functions, hash, &stub);\n  if(!entry) return NULL;\n  return entry->handle;\n}\n\nstatic void prog_free(rulenode_t *);\nstatic void expr_free(exprnode_t *);\nstatic rulenode_t *prog_compile(const char *program, int errlen, char *err);\n\nstatic bool apply_compiled_program_node(rulenode_t *, fq_msg *);\nstatic bool\napply_compiled_program_node(rulenode_t *p, fq_msg *m) {\n  bool lval = false, rval = false;\n  if(p->left)  lval = apply_compiled_program_node(p->left, m);\n  if(p->right) rval = apply_compiled_program_node(p->right, m);\n  if(p->oper == '|') return lval || rval;\n  if(p->oper == '&') return lval && rval;\n  if(p->expr) {\n    return p->expr->match(m, p->expr->nargs, p->expr->args);\n  }\n  fq_assert(\"Bad program\" == NULL);\n  return false;\n}\nstatic bool\napply_compiled_program(struct fqd_route_rule *r, fq_msg *m) {\n  bool rv;\n  hrtime_t start, delta, rpl;\n  ck_pr_add_64(&r->stats->invocations, 1);\n  start = fq_gethrtime();\n  rv = apply_compiled_program_node(r->compiled_program, m);\n  delta = fq_gethrtime() - start;\n  /* simple exp smoothing (alpha = 127/128) */\n  rpl = ((r->stats->avg_ns * 127)>>7) + (delta>>7);\n  r->stats->avg_ns = rpl;\n  return rv;\n}\nstruct queue_target {\n  fqd_queue *tgts[MAX_QUEUE_TARGETS];\n  int cnt;\n  int allocd;\n  struct queue_target *next;\n};\n\nstatic bool\nalready_queue_target(struct queue_target **d, fqd_queue *q) {\n  int i;\n  struct queue_target *nd;\n  /* Simple O(n) scan of target queues to avoid dup delivery */\n  for(nd = *d; nd; nd = nd->next)\n    for(i=0;i<nd->cnt;i++)\n      if(q == nd->tgts[i])\n        return true;\n  return false;\n}\n\nstatic void\nadd_queue_target(struct queue_target **d, fqd_queue *q) {\n  struct queue_target *nd;\n  if(!(*d) || (*d)->cnt >= MAX_QUEUE_TARGETS) {\n    nd = malloc(sizeof(*nd));\n    nd->next = *d;\n    nd->cnt = 1;\n    nd->allocd = 1;\n    fqd_queue_ref(q);\n    nd->tgts[0] = q;\n    *d = nd;\n  }\n  else {\n    fqd_queue_ref(q);\n    (*d)->tgts[(*d)->cnt++] = q;\n  }\n}\nstatic int\ninternal_jt_do(struct prefix_jumptable *jt, int rv,\n               int (*f)(struct fqd_route_rule *, int, void *), void *closure) {\n  int i;\n\n  if(jt->tabletype == RULETABLE) {\n    struct fqd_route_rule *r;\n    for(r=jt->rules;r;r=r->next) rv += f(r, rv, closure);\n  }\n  else if(jt->tabletype == JUMPTABLE) {\n    for(i=0;i<jt->pat_len;i++) {\n      rv += internal_jt_do(jt->pats[i].jt, rv, f, closure);\n    }\n  }\n  return rv;\n}\nint\nfor_each_route_rule_do(struct fqd_route_rules *set,\n                       int (*f)(struct fqd_route_rule *, int, void *), void *closure) {\n  return internal_jt_do(&set->master, 0, f, closure);\n}\nstatic void\nwalk_jump_table(struct prefix_jumptable *jt, fq_msg *m, int offset, struct queue_target **d) {\n  if(jt->tabletype == RULETABLE) {\n    struct fqd_route_rule *r;\n    for(r=jt->rules;r;r=r->next) {\n      if(m->route.len >= r->prefix.len &&\n         m->route.len <= r->match_maxlen &&\n         !already_queue_target(d, r->queue)) {\n        bool matched = false;\n        if(FQ_ROUTE_PROGRAM_ENTRY_ENABLED()) {\n          fq_dtrace_msg_t dmsg;\n          DTRACE_PACK_MSG(&dmsg, m);\n          FQ_ROUTE_PROGRAM_ENTRY(r->program, &dmsg);\n        }\n        if(apply_compiled_program(r, m)) {\n          fq_rk *rk = (fq_rk *)r->queue;\n          fq_debug(FQ_DEBUG_ROUTE, \"M[%p] -> Q[%.*s]\\n\", (void *)m, rk->len, rk->name);\n          add_queue_target(d, r->queue);\n          matched = true;\n        }\n        if(FQ_ROUTE_PROGRAM_RETURN_ENABLED()) {\n          fq_dtrace_msg_t dmsg;\n          DTRACE_PACK_MSG(&dmsg, m);\n          (void)matched;\n          FQ_ROUTE_PROGRAM_RETURN(r->program, &dmsg, matched);\n        }\n      }\n    }\n  }\n  else if(jt->tabletype == JUMPTABLE) {\n    int i;\n    uint64_t inbits;\n    uint8_t *in = m->route.name + offset;\n    memcpy(&inbits, in, sizeof(inbits));\n    for(i=0;i<jt->pat_len;i++) {\n      if(jt->pats[i].pattern == (jt->pats[i].checkbits & inbits)) {\n        walk_jump_table(jt->pats[i].jt, m, offset + sizeof(inbits), d);\n      }\n    }\n  }\n}\nvoid\nfqd_inject_message(remote_data_client *c, fq_msg *m) {\n  fqd_exchange *e;\n  fqd_config *config;\n  struct queue_target stub, *headptr = &stub;\n  stub.next = NULL;\n  stub.cnt = 0;\n  stub.allocd = 0;\n  config = fqd_config_get();\n  e = fqd_config_get_exchange(config, &m->exchange);\n  fqd_exchange_messages(e, 1);\n  fqd_exchange_message_octets(e, m->payload_len);\n  if(e) {\n    walk_jump_table(&e->set->master, m, 0, &headptr);\n  }\n  else {\n    fq_debug(FQ_DEBUG_ROUTE, \"No exchange \\\"%.*s\\\"\\n\", m->exchange.len, m->exchange.name);\n    fqd_exchange_no_exchange(NULL, 1);\n    if(c) c->no_exchange++;\n  }\n  fqd_config_release(config);\n\n  if(headptr->cnt == 0) {\n    fqd_exchange_no_route(e, 1);\n    if(c) c->no_route++;\n  }\n  while(headptr) {\n    int i;\n    struct queue_target *tofree = headptr;\n\n    for(i=0; i<headptr->cnt; i++) {\n      int dropped = 0;\n      fqd_queue *q = headptr->tgts[i];\n\n      fqd_queue_enqueue(q, m, &dropped);\n      fqd_queue_deref(q);\n\n      if(dropped) {\n        fqd_exchange_dropped(e, dropped);\n        if(c) c->dropped += dropped;\n      }\n\n      fqd_exchange_routed(e, 1);\n      if(c) c->routed += 1;\n    }\n    headptr = headptr->next;\n    if(tofree->allocd) free(tofree);\n  }\n\n  fq_msg_deref(m);\n}\n#define is_hex(a) (((a) > '0' || (a) < '9') || \\\n                   ((a) > 'a' || (a) < 'f') || \\\n                   ((a) > 'A' || (a) < 'F'))\n#define to_hex_c(a) (((a) > '0' || (a) < 9) ? ((a) - '0') : \\\n                    ((a) > 'a' || (a) < 'f') ? ((a) - 'a' + 10) : \\\n                    ((a) > 'A' || (a) < 'F') ? ((a) - 'A' + 10) : 0)\n#define to_hex(cp) ((to_hex_c(cp[0]) << 4) | to_hex_c(cp[1]))\n\nstatic inline int is_term_char(char a, const char *ts, int tslen) {\n  int i;\n  for(i=0;i<tslen;i++) if(a == ts[i]) return 1;\n  return 0;\n}\nstatic const char *\nparse_prog_string(const char *input, char *tgt, int *tgt_len) {\n  const char *term = \" \";\n  int term_len = 2; /* includes the \\0 */\n  int max_len = *tgt_len, i = 0;\n  if(*input == '\\\"') {\n    input++;\n    term = \"\\\"\";\n    term_len = 1;\n  }\n\n  while(*input && i<max_len) {\n    if(input[0] == '\\\\' && input[1] != '\\0') {\n      input++;\n      if(input[0] == 'x' && is_hex(input[1]) && is_hex(input[2])) {\n        input++;\n        tgt[i++] = to_hex(input);\n        input += 2;\n      }\n      else {\n        tgt[i++] = input[1];\n        input += 2;\n      }\n    }\n    else {\n      if(is_term_char(*input, term, term_len)) {\n        if(*input) input++;\n        goto out_clean;\n      }\n      tgt[i++] = *input++;\n    }\n  }\n  if(is_term_char(*input, term, term_len)) goto out_clean;\n\n  /* we failed to find a suitable terminator */\n  input = NULL;\n\n  out_clean:\n  *tgt_len = i;\n  return input;\n}\nstruct fqd_route_rule *\nfqd_routemgr_compile(const char *program, int peermode, fqd_queue *q) {\n  int len, alen;\n  const char *cp;\n  char err[128];\n  struct fqd_route_rule *r;\n\n  fq_assert(q);\n  len = strlen(program);\n  if(len > (int)sizeof(r->prefix.name)) return NULL;\n  if(strncmp(program, \"prefix:\", 7) && strncmp(program, \"exact:\", 6)) {\n    return NULL;\n  }\n  cp = strchr(program, ':');\n  if(!cp) return NULL;\n  cp++;\n  r = calloc(1, sizeof(*r));\n  alen = sizeof(r->prefix.name);\n  cp = parse_prog_string(cp, (char *)r->prefix.name, &alen);\n  r->prefix.len = (uint8_t)alen;\n  if(cp == NULL) {\n    fq_debug(FQ_DEBUG_ROUTE, \"Failed to parse: %s\\n\", r->prefix.name);\n    free(r);\n    return NULL;\n  }\n  r->compiled_program = prog_compile(cp, sizeof(err), err);\n  if(r->compiled_program == NULL) {\n    fq_debug(FQ_DEBUG_ROUTE, \"Failed to compile[%s]: %s\\n\", cp, err);\n    free(r);\n    return NULL;\n  }\n  r->match_maxlen = sizeof(r->prefix.name);\n  if(!strncmp(program, \"exact:\", 6)) r->match_maxlen = r->prefix.len;\n  r->program = strdup(program);\n  r->queue = q;\n  fqd_queue_ref(r->queue);\n  r->peermode = peermode;\n  r->stats = calloc(1, sizeof(*r->stats));\n  r->stats->refcnt = 1;\n  fq_debug(FQ_DEBUG_ROUTE, \"creating rule \\\"%s\\\"\\n\", r->program);\n  fq_debug(FQ_DEBUG_MEM, \"alloc rule [%p/%p] -> Q[%p]\\n\", (void *)r, (void *)r->compiled_program, (void *)r->queue);\n  return r;\n}\nvoid\nfqd_routemgr_rule_free(struct fqd_route_rule *rule) {\n  fq_debug(FQ_DEBUG_ROUTE, \"dropping rule \\\"%s\\\"\\n\", rule->program);\n  fq_debug(FQ_DEBUG_MEM, \"free rule  [%p] -> Q[%p]\\n\", (void *)rule, (void *)rule->queue);\n  free(rule->program);\n  prog_free(rule->compiled_program);\n  if(rule->queue) fqd_queue_deref(rule->queue);\n  if(rule->stats) {\n    bool zero;\n    ck_pr_dec_uint_zero(&rule->stats->refcnt, &zero);\n    if(zero) {\n      free(rule->stats);\n    }\n  }\n  free(rule);\n}\nstruct fqd_route_rules *\nfqd_routemgr_ruleset_alloc() {\n  return calloc(1, sizeof(struct fqd_route_rules));\n}\nstatic int\nwalk_jump_table_setp_by_route_id(struct prefix_jumptable *jt,\n                                 uint32_t route_id, bool nv) {\n  if(jt->tabletype == RULETABLE) {\n    struct fqd_route_rule *r = jt->rules;\n    while(r) {\n      if(r->route_id == route_id) {\n        r->permanent = nv;\n        return 1;\n      }\n      else r = r->next;\n    }\n  }\n  else if(jt->tabletype == JUMPTABLE) {\n    int i;\n    for(i=0;i<jt->pat_len;i++) {\n      if(walk_jump_table_setp_by_route_id(jt->pats[i].jt, route_id, nv)) {\n        return 1;\n      }\n    }\n  }\n  return 0;\n}\nstatic int\nwalk_jump_table_drop_rules_by_route_id(struct prefix_jumptable *jt,\n                                       fqd_queue *q,\n                                       uint32_t route_id) {\n  if(jt->tabletype == RULETABLE) {\n    struct fqd_route_rule *prev = NULL, *r = jt->rules;\n    while(r) {\n      if(r->route_id == route_id &&\n         (q == NULL || r->queue == q)) {\n        struct fqd_route_rule *tofree = r;\n        if(prev) r = prev->next = r->next;\n        else r = jt->rules= r->next;\n        fqd_routemgr_rule_free(tofree);\n        return 1;\n      }\n      else {\n        prev = r;\n        r = r->next;\n      }\n    }\n  }\n  else if(jt->tabletype == JUMPTABLE) {\n    int i;\n    for(i=0;i<jt->pat_len;i++) {\n      if(walk_jump_table_drop_rules_by_route_id(jt->pats[i].jt, q, route_id)) {\n        return 1;\n      }\n    }\n  }\n  return 0;\n}\nstatic void\nwalk_jump_table_drop_rules_by_queue(struct prefix_jumptable *jt,\n                                    fqd_queue *q) {\n  if(jt->tabletype == RULETABLE) {\n    struct fqd_route_rule *prev = NULL, *r = jt->rules;\n    while(r) {\n      if(r->queue == q) {\n        struct fqd_route_rule *tofree = r;\n        if(prev) r = prev->next = r->next;\n        else r = jt->rules= r->next;\n        fqd_routemgr_rule_free(tofree);\n      }\n      else {\n        prev = r;\n        r = r->next;\n      }\n    }\n  }\n  else if(jt->tabletype == JUMPTABLE) {\n    int i;\n    for(i=0;i<jt->pat_len;i++)\n      walk_jump_table_drop_rules_by_queue(jt->pats[i].jt, q);\n  }\n}\nstatic int\nfqd_routemgr_set_permanence_by_route_id(fqd_route_rules *set,\n                                        uint32_t route_id, bool nv) {\n  return walk_jump_table_setp_by_route_id(&set->master, route_id, nv);\n}\nint\nfqd_routemgr_perm_route_id(fqd_route_rules *set, uint32_t route_id) {\n  return fqd_routemgr_set_permanence_by_route_id(set, route_id, true);\n}\nint\nfqd_routemgr_trans_route_id(fqd_route_rules *set, uint32_t route_id) {\n  return fqd_routemgr_set_permanence_by_route_id(set, route_id, false);\n}\nint\nfqd_routemgr_drop_rules_by_route_id(fqd_route_rules *set, fqd_queue *q,\n                                    uint32_t route_id) {\n  fq_debug(FQ_DEBUG_ROUTE, \"fqd_routemgr_drop_rules_by_route_id(%p, %p, %u)\\n\",\n           (void *)set, (void *)q, route_id);\n  return walk_jump_table_drop_rules_by_route_id(&set->master, q, route_id);\n}\nvoid\nfqd_routemgr_drop_rules_by_queue(fqd_route_rules *set, fqd_queue *q) {\n  fq_debug(FQ_DEBUG_ROUTE, \"fqd_routemgr_drop_rules_by_queue(%p, %p)\\n\",\n           (void *)set, (void *)q);\n  walk_jump_table_drop_rules_by_queue(&set->master, q);\n}\nstatic struct prefix_jumptable *\nget_ruletable(struct prefix_jumptable *parent, fqd_route_rule *newrule,\n              int offset) {\n  uint64_t inbits;\n  uint64_t incb = 0;\n  int i;\n  struct prefix_jumptable *child;\n\n  if(newrule->prefix.len >= (offset+sizeof(inbits))) /* use all the bits */\n    incb = ~incb;\n  else if(newrule->prefix.len % sizeof(inbits) != 0) /* use partial bits */\n    incb = ~incb << ((sizeof(inbits) -\n                     (newrule->prefix.len % sizeof(inbits))) *\n                    sizeof(inbits));\n#if !defined(BYTE_ORDER) || !defined(BIG_ENDIAN)\n#error \"BYTE_ORDER and BIG_ENDIAN must be defined!\"\n#endif\n#if BYTE_ORDER != BIG_ENDIAN\n  incb = (((uint64_t)ntohl(incb & 0xffffffffLLU)) << 32) |\n         (ntohl((incb &0xffffffff00000000LLU) >> 32));\n#endif\n  memcpy(&inbits, newrule->prefix.name + offset, sizeof(inbits));\n\n    /* We need to (possibly) insert a rule table */\n  for(i=0;i<parent->pat_len;i++) {\n    if(parent->pats[i].checkbits == incb &&\n       parent->pats[i].pattern == (parent->pats[i].checkbits & inbits)) {\n      if(parent->pats[i].jt->tabletype == RULETABLE) {\n        return parent->pats[i].jt;\n      }\n      else {\n        return get_ruletable(parent->pats[i].jt, newrule, offset + sizeof(inbits));\n      }\n    }\n  }\n  child = calloc(1, sizeof(*child));\n  if(!child) return NULL;\n  child->tabletype = (~incb == 0) ? JUMPTABLE : RULETABLE;\n\n  /* Here we use child->pats as a tmp variable to grow parent->pats */\n  child->pats = malloc(sizeof(*child->pats) * (parent->pat_len + 1));\n  if(!child->pats) {\n    free(child);\n    return NULL;\n  }\n  if(parent->pat_len) {\n    memcpy(child->pats, parent->pats, sizeof(*child->pats) * parent->pat_len);\n    free(parent->pats);\n  }\n  parent->pats = child->pats;\n  child->pats = NULL;\n  parent->pat_len++;\n  parent->pats[i].pattern = (inbits & incb);\n  parent->pats[i].checkbits = incb;\n  parent->pats[i].jt = child;\n  if(child->tabletype == RULETABLE) {\n    return child;\n  }\n  return get_ruletable(child, newrule, offset + sizeof(inbits));\n}\nuint32_t\nfqd_routemgr_ruleset_add_rule(fqd_route_rules *set, fqd_route_rule *newrule,\n                              int *isnew) {\n  fqd_route_rule *r;\n  struct prefix_jumptable *jt;\n  jt = get_ruletable(&set->master, newrule, 0);\n\n  for(r=jt->rules;r;r=r->next) {\n    if(r->queue == newrule->queue &&\n       !strcmp(r->program, newrule->program)) {\n      fqd_routemgr_rule_free(newrule);\n      if(isnew) *isnew = 0;\n      return r->route_id;\n    }\n  }\n  do {\n    newrule->route_id = ck_pr_faa_32(&global_route_id, 1);\n  } while(newrule->route_id == FQ_BIND_ILLEGAL);\n  newrule->next = jt->rules;\n  jt->rules = newrule;\n  fq_debug(FQ_DEBUG_ROUTE, \"rule[%u] -> %p\\n\", newrule->route_id, (void *)newrule);\n  if(isnew) *isnew = 1;\n  return newrule->route_id;\n}\nstatic rulenode_t *\ncopy_compiled_program(rulenode_t *in) {\n  fq_debug(FQ_DEBUG_MEM, \"copy compiled program: %p\\n\", (void *)in);\n  ck_pr_inc_uint(&in->refcnt);\n  return in;\n}\nstatic fqd_route_rule *\ncopy_rule(fqd_route_rule *in) {\n  fqd_route_rule *out;\n  fq_debug(FQ_DEBUG_MEM, \"copy from [%p] -> Q[%p]\\n\", (void *)in, (void *)in->queue);\n  out = calloc(1, sizeof(*out));\n  memcpy(out, in, sizeof(*out));\n  fq_assert(out->queue);\n  out->program = strdup(in->program);\n  out->compiled_program = copy_compiled_program(in->compiled_program);\n  fqd_queue_ref(out->queue);\n  ck_pr_inc_uint(&out->stats->refcnt);\n  out->next = NULL;\n  fq_debug(FQ_DEBUG_MEM, \"copy to   [%p] -> Q[%p]\\n\", (void *)out, (void *)out->queue);\n  return out;\n}\nstatic void\ncopy_jt(struct prefix_jumptable *tgt, struct prefix_jumptable *src) {\n  memcpy(tgt, src, sizeof(*src));\n  if(src->tabletype == RULETABLE) {\n    fqd_route_rule *r, *nhead, *nr, *tmp;\n    nhead = nr = NULL;\n    for(r=src->rules;r;r=r->next) {\n      tmp = copy_rule(r);\n      if(!nhead) nhead = tmp;\n      if(nr) {\n        nr->next = tmp;\n      }\n      nr = tmp;\n    }\n    tgt->rules = nhead;\n  }\n  else if(src->tabletype == JUMPTABLE) {\n    int i;\n    tgt->pats = malloc(src->pat_len * sizeof(*tgt->pats));\n    if(src->pat_len) {\n      memcpy(tgt->pats, src->pats, src->pat_len * sizeof(*tgt->pats));\n      for(i=0; i<src->pat_len; i++) {\n        tgt->pats[i].jt = malloc(sizeof(*tgt->pats[i].jt));\n        if(tgt->pats[i].jt) copy_jt(tgt->pats[i].jt, src->pats[i].jt);\n      }\n    }\n  }\n}\nfqd_route_rules *\nfqd_routemgr_ruleset_copy(fqd_route_rules *set) {\n  fqd_route_rules *nset;\n  nset = fqd_routemgr_ruleset_alloc();\n  copy_jt(&nset->master, &set->master);\n  /* TODO: compress set */\n  return nset;\n}\nstatic void\nfree_jt(struct prefix_jumptable *jt) {\n  if(jt->tabletype == RULETABLE) {\n    while(jt->rules) {\n      fqd_route_rule *r = jt->rules->next;\n      fqd_routemgr_rule_free(jt->rules);\n      jt->rules = r;\n    }\n  }\n  else if(jt->tabletype == JUMPTABLE) {\n    int i;\n    for(i=0;i<jt->pat_len;i++) {\n      if(jt->pats[i].jt) {\n        free_jt(jt->pats[i].jt);\n        free(jt->pats[i].jt);\n      }\n    }\n    free(jt->pats);\n  }\n}\nvoid\nfqd_routemgr_ruleset_free(fqd_route_rules *set) {\n  free_jt(&set->master);\n  free(set);\n}\n\nstatic void\nprog_free(rulenode_t *p) {\n  bool zero;\n  if(!p) return;\n  ck_pr_dec_uint_zero(&p->refcnt, &zero);\n  if(!zero) return;\n  if(p->left) prog_free(p->left);\n  if(p->right) prog_free(p->right);\n  if(p->expr) expr_free(p->expr);\n  free(p);\n}\nstatic void\nexpr_free(exprnode_t *e) {\n  if(!e) return;\n  if(e->args) {\n    int i;\n    for(i=0;i<e->nargs;i++) {\n      if(e->args[i].value_type == RP_VALUE_STRING) free(e->args[i].value.s);\n    }\n    free(e->args);\n  }\n  free(e);\n}\n\n#define EAT_SPACE(p) while(*p != '\\0' && isspace(*p)) (p)++\nstatic int is_valid_term_char(char ch, bool first) {\n  if((ch >= 'a' && ch <= 'z') ||\n     (ch >= 'A' && ch <= 'Z') ||\n     (ch == '_')) return 1;\n  if(first) return 0;\n  if(ch >= '0' && ch <= '9') return 1;\n  return 0;\n}\nstatic int rule_getterm(const char **cp, char *term, int len) {\n  int idx = 0;\n  while(idx < (len-1) && is_valid_term_char((*cp)[idx], idx == 0)) {\n    term[idx] = (*cp)[idx];\n    idx++;\n  }\n  term[idx] = '\\0';\n  (*cp) += idx;\n  fq_debug(FQ_DEBUG_ROUTE, \"term[%s]\\n\", term);\n  if(idx > 0) return 0;\n  return 1;\n}\nstatic int rule_getstring(const char **cp, valnode_t *arg) {\n  const char *begin;\n  if(**cp != '\\\"') return 0;\n  (*cp)++;\n  begin = *cp;\n  while(**cp != '\\0') {\n    if(**cp == '\\\\' && (*cp)[1] != '\\0') (*cp)++;\n    (*cp)++;\n    if(**cp == '\\\"') {\n      char *dst;\n      int i, len = (*cp) - begin;\n      (*cp)++;\n      arg->value_type = RP_VALUE_STRING;\n      dst = arg->value.s = malloc(len + 1);\n      for(i=0;i<len;i++) {\n        *dst = begin[i];\n        if(begin[i] == '\\\\') {\n          switch(begin[i+1]) {\n            case '\\\\': i++; break;\n            case '0': *dst = '\\0'; i++; break;\n            case 'n': *dst = '\\n'; i++; break;\n            case 'r': *dst = '\\r'; i++; break;\n            case 't': *dst = '\\t'; i++; break;\n            default: break;\n          }\n        }\n        dst++;\n      }\n      *dst = '\\0';\n      return 0;\n    }\n  }\n  return 1;\n}\nstatic exprnode_t *\nrule_compose_expression(const char *fname, int nargs, valnode_t *args,\n                        int errlen, char *err) {\n  int i;\n  exprnode_t *expr = NULL;\n  char symbol_name[256];\n  char argsig[MAX_VALNODE_ARGS];\n  union {\n    void *symbol;\n    bool (*match)(fq_msg *m, int nargs, valnode_t *args);\n    void (*dummy)(void);\n  } u;\n\n  for(i=0;i<nargs;i++) {\n    switch(args[i].value_type) {\n      case RP_VALUE_STRING:  argsig[i] = 's'; break;\n      case RP_VALUE_BOOLEAN: argsig[i] = 'b'; break;\n      case RP_VALUE_DOUBLE:  argsig[i] = 'd'; break;\n    }\n  }\n  argsig[i] = '\\0';\n  snprintf(symbol_name, sizeof(symbol_name), \"fqd_route_prog__%s__%s\",\n           fname, argsig);\n#ifdef RTLD_SELF\n  u.symbol = dlsym(RTLD_SELF, symbol_name);\n#else\n  u.symbol = dlsym(RTLD_LOCAL, symbol_name);\n#endif\n  if(!u.symbol) {\n    for(struct dl_handles *node = global_handles; node; node = node->next) {\n      u.symbol = dlsym(node->handle, symbol_name);\n      if(u.symbol != NULL) break;\n    }\n  }\n  if(!u.symbol) {\n    u.dummy = global_function_lookup(symbol_name);\n  }\n  if(!u.symbol) {\n    snprintf(err, errlen, \"cannot find symbol: %s\\n\", symbol_name);\n    fq_debug(FQ_DEBUG_ROUTE, \"cannot find symbol: %s\\n\", symbol_name);\n    snprintf(symbol_name, sizeof(symbol_name), \"fqd_route_prog__%s__VA\", fname);\n    if(!u.symbol) {\n      for(struct dl_handles *node = global_handles; node; node = node->next) {\n        u.symbol = dlsym(node->handle, symbol_name);\n        if(u.symbol != NULL) break;\n      }\n    }\n    if(!u.symbol) {\n      u.dummy = global_function_lookup(symbol_name);\n    }\n    if(!u.symbol) {\n      fq_debug(FQ_DEBUG_ROUTE, \"cannot find symbol: %s\\n\", symbol_name);\n      return NULL;\n    }\n  }\n  expr = calloc(1, sizeof(*expr));\n  expr->match = (bool (*)(fq_msg *, int, valnode_t *)) u.match;\n  if(nargs > 0) {\n    expr->nargs = nargs;\n    expr->args = calloc(nargs, sizeof(*expr->args));\n    for(i=0; i<nargs; i++) {\n      memcpy(&expr->args[i], &args[i], sizeof(*args));\n      /* don't need to double the alloc */\n    }\n  }\n  return expr;\n}\nrulenode_t *rule_parse(const char **cp, int errlen, char *err);\n#define rule_parse_busted(fmt, ...) do { \\\n  snprintf(err, errlen, fmt, __VAR_ARGS__); \\\n  goto busted; \\\n} while(0)\nrulenode_t *\nrule_parse(const char **cp, int errlen, char *err) {\n  rulenode_t *nr = NULL;\n  EAT_SPACE(*cp); if(**cp == '\\0') goto busted;\n  fq_debug(FQ_DEBUG_ROUTE, \"parse->(%s)\\n\", *cp);\n  if(**cp == '(') {\n    (*cp)++;\n    EAT_SPACE(*cp); if(**cp == '\\0') goto busted;\n    nr = calloc(1, sizeof(*nr));\n    nr->refcnt = 1;\n    nr->left = rule_parse(cp, errlen, err);\n    if(nr->left == NULL) goto busted;\n    EAT_SPACE(*cp); if(**cp == '\\0') goto busted;\n    if(**cp == ')') return nr;\n    if((*cp)[0] == '&' && (*cp)[1] == '&') nr->oper = '&';\n    else if((*cp)[0] == '|' || (*cp)[1] == '|') nr->oper = '|';\n    else goto busted;\n    (*cp) += 2;\n    nr->right = rule_parse(cp, errlen, err);\n    if(nr->right == NULL) goto busted;\n    EAT_SPACE(*cp); if(**cp == '\\0') goto busted;\n    if(**cp != ')') goto busted;\n    (*cp)++;\n    return nr;\n  }\n  else {\n    char term[128];\n    int nargs = 0;\n    valnode_t args[MAX_VALNODE_ARGS];\n\n    fq_debug(FQ_DEBUG_ROUTE, \"parsing function: %s\\n\", *cp);\n    if(rule_getterm(cp, term, sizeof(term))) goto busted;\n    EAT_SPACE(*cp); if(**cp == '\\0') goto busted;\n    if(**cp != '(') goto busted;\n    (*cp)++;\n    while(**cp != '\\0' && **cp != ')') {\n      EAT_SPACE(*cp); if(**cp == '\\0') goto busted;\n      if(nargs > 0) {\n        if(**cp != ',') goto busted;\n        (*cp)++;\n        EAT_SPACE(*cp); if(**cp == '\\0') goto busted;\n      }\n      if(**cp == '\\\"') {\n        if(rule_getstring(cp, &args[nargs])) goto busted;\n        nargs++;\n      }\n      else if(!strcmp(*cp, \"true\")) {\n        args[nargs].value_type = RP_VALUE_BOOLEAN;\n        args[nargs].value.b = true;\n        nargs++;\n        (*cp) += strlen(\"true\");\n      }\n      else if(!strcmp(*cp, \"false\")) {\n        args[nargs].value_type = RP_VALUE_BOOLEAN;\n        args[nargs].value.b = false;\n        nargs++;\n        (*cp) += strlen(\"false\");\n      }\n      else {\n        char *endptr;\n        /* parse a double */\n        args[nargs].value_type = RP_VALUE_DOUBLE;\n        args[nargs].value.d = strtod(*cp, &endptr);\n        if(endptr == *cp) goto busted;\n        nargs++;\n        *cp = endptr;\n      }\n      EAT_SPACE(*cp); if(**cp == '\\0') goto busted;\n    }\n    if(**cp != ')') goto busted;\n    (*cp)++;\n    nr = calloc(1, sizeof(*nr));\n    nr->refcnt = 1;\n    nr->expr = rule_compose_expression(term, nargs, args, errlen, err);\n    if(!nr->expr) {\n      int i;\n      for(i=0;i<nargs;i++)\n        if(args[i].value_type == RP_VALUE_STRING)\n          free(args[i].value.s);\n      goto busted;\n    }\n    return nr;\n  }\n\nbusted:\n  fq_debug(FQ_DEBUG_ROUTE, \"parse failed at: %s\\n\", *cp);\n  if(nr) {\n    if(nr->expr) {\n      if(nr->expr->nargs) {\n        int i;\n        for(i=0;i<nr->expr->nargs;i++)\n          if(nr->expr->args[i].value_type == RP_VALUE_STRING)\n            free(nr->expr->args[i].value.s);\n        free(nr->expr->args);\n      }\n      free(nr->expr);\n    }\n    free(nr);\n  }\n  return NULL;\n}\nstatic rulenode_t *\nprog_compile(const char *program, int errlen, char *err) {\n  rulenode_t *nr;\n  EAT_SPACE(program);\n  if(*program == '\\0') {\n    return prog_compile(\"true()\", errlen, err);\n  }\n\n  if(errlen>0) err[0] = '\\0';\n  nr = rule_parse(&program, errlen, err);\n  EAT_SPACE(program);\n  if(*program) {\n    if(err && err[0] == '\\0') snprintf(err, errlen, \"trailing trash: %s\", program);\n    prog_free(nr);\n    return NULL;\n  }\n  return nr;\n}\n\n"
  },
  {
    "path": "fqs.c",
    "content": "/*\n * fqs\n *\n * fqs reads messages from stdin, one pre line, and sends it to the specified fq exchange.\n *\n */\n\n#define _GNU_SOURCE\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <unistd.h>\n#include <signal.h>\n#include <string.h>\n#include \"fq.h\"\n\nvoid logger(fq_client c, const char *s) {\n  fprintf(stderr, \"fq_logger: %s\\n\", s);\n}\n\nstatic void usage(const char *prog) {\n  printf(\"%s:\\n\", prog);\n  printf(\"\\t-h \\tshow this help message\\n\");\n  printf(\"\\t-a <host>:<port>\\tspecify the address to connect to (default: 127.0.0.1:8765)\\n\");\n  printf(\"\\t-x <exchange>\\tspecify the exchange to relay messages on (required)\\n\");\n  printf(\"\\t-r <route>\\tspecify the message route (required)\\n\");\n  printf(\"\\t-u <user>\\tspecify the user (default: user)\\n\");\n  printf(\"\\t-p <pass>\\tspecify the password (default: pass)\\n\");\n}\n\nint main(int argc, char **argv) {\n  char *host = NULL, *exchange = NULL, *route = NULL;\n  char *address = strdup(\"127.0.0.1:8765\");\n  char *user = \"user\";\n  char *pass = \"pass\";\n  int port = 0;\n  int c = 0;\n  if (argc == 1) {\n    usage(argv[0]);\n    exit(-1);\n  }\n  while((c = getopt(argc, argv, \"ha:x:r:u:p:\")) != EOF) {\n    switch(c) {\n    case 'h':\n      usage(argv[0]);\n      exit(0);\n      break;\n    case 'a':\n      address = strdup(optarg);\n      break;\n    case 'x':\n      exchange = strdup(optarg);\n      break;\n    case 'r':\n      route = strdup(optarg);\n      break;\n    case 'u':\n      user = strdup(optarg);\n      break;\n    case 'p':\n      pass = strdup(optarg);\n      break;\n    default:\n      usage(argv[0]);\n      exit(-1);\n    }\n  }\n  // Separate host and port in address string\n  // We have strdup'ed address so we can mutate it\n  host = address;\n  for (char *p = address; *p != 0; p++) {\n    if (*p == ':') {\n      *p = 0;\n      port = strtol(p+1, NULL, 10);\n      break;\n    }\n  }\n  if (!port) {\n    printf(\"Illegal port specification\\n\");\n    exit(-1);\n  }\n  if (!exchange) {\n    printf(\"Exchange argument required\");\n    exit(-1);\n  }\n  if (!route) {\n    printf(\"Route argument required\");\n    exit(-1);\n  }\n\n  fq_client cli;\n  fq_msg *m = NULL;\n  size_t exchange_len = strlen(exchange);\n  size_t route_len = strlen(route);\n  signal(SIGPIPE, SIG_IGN); // ignore SIGPIPE\n  fq_client_init(&cli, 0, logger);\n  fq_client_creds(cli, host, port, user, pass);\n  fq_client_heartbeat(cli, 1000);\n  fq_client_set_backlog(cli, 10000, 100);\n  fq_client_connect(cli);\n  while(true) {\n    char *line = NULL;\n    size_t line_cap = 0;\n    int line_len;\n    line_len = getline(&line, &line_cap, stdin);\n    if(line_len < 0) {\n      // wait for queues to drain\n      while(fq_client_data_backlog(cli) > 0) {\n        usleep(100);\n      }\n      exit(0);\n    }\n    m = fq_msg_alloc(line, line_len);\n    fq_msg_exchange(m, exchange, exchange_len);\n    fq_msg_route(m, route, route_len);\n    fq_msg_id(m, NULL);\n    fq_client_publish(cli, m);\n    fq_msg_free(m);\n  }\n  return 0;\n}\n"
  },
  {
    "path": "fqtool.c",
    "content": "/*\n * Copyright (c) 2014 Circonus, Inc.\n * All rights reserved.\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <unistd.h>\n#include <sys/uio.h>\n#include <fcntl.h>\n#include <errno.h>\n#include <sys/utsname.h>\n#include <arpa/inet.h>\n#include <signal.h>\n#include <string.h>\n#include <assert.h>\n#include \"getopt.h\"\n#include \"fq.h\"\n\nint usage(const char *);\nvoid logger(fq_client, const char *);\n\nvoid logger(fq_client c, const char *s) {\n  (void)c;\n  fprintf(stderr, \"fq_logger: %s\\n\", s);\n}\n\nint permanent = 1;\nint binding_trans = 1;\nchar *binding_prog = NULL;\nchar *exchange = NULL;\nchar *output = NULL;\nchar *format = \"payload\";\n\nenum {\n  M_ROUTE = 0,\n  M_HOST,\n  M_PAYLOAD,\n  M_NONE\n} o_fmt[M_NONE];\n#define M_COUNT M_NONE\nint output_fd = -1;\nbool inject_json = true;\n\nstatic void\nmy_auth_handler(fq_client c, int error) {\n  fq_bind_req *breq;\n \n  if(error) {\n    fprintf(stderr, \"Error encoutered.\\n\");\n    exit(-1);\n  }\n\n  if(!output)\n    fprintf(stderr, \"Queue made %s.\\n\", permanent ? \"permanent\" : \"transient\");\n  if(!binding_prog && !output) exit(0);\n\n  breq = malloc(sizeof(*breq));\n  memset(breq, 0, sizeof(*breq));\n  assert(strlen(exchange) < sizeof(breq->exchange.name));\n  memcpy(breq->exchange.name, exchange, strlen(exchange));\n  breq->exchange.len = strlen(exchange);\n  breq->flags = binding_trans ? FQ_BIND_TRANS : FQ_BIND_PERM;\n  breq->program = binding_prog;\n  fq_client_bind(c, breq);\n}\n\nstatic void\nmy_bind_handler(fq_client c, fq_bind_req *breq) {\n  (void)c;\n  if(breq->out__route_id == FQ_BIND_ILLEGAL) {\n    fprintf(stderr, \"Failure to bind...\\n\");\n    exit(-1);\n  }\n  if(!output) {\n    fprintf(stderr, \"Binding made %s.\\n\", binding_trans ? \"transient\" : \"permanent\");\n    exit(0);\n  }\n}\n\nstatic bool\nmy_message_handler(fq_client c, fq_msg *m) {\n  int i;\n  char *payload = (char *)m->payload;\n  long payload_len = m->payload_len;\n  char host[32] = \"\";\n  bool lineend = true;\n  bool started = false;\n  if(payload_len > 0 && payload[payload_len-1] == '\\n') {\n    payload_len--;\n  }\n\n  struct in_addr addr;\n  for(i=0;i<MAX_HOPS-1;i++)\n    if(m->hops[i+1] == 0 ||  /* IN ADDR ANY */\n       m->hops[i+1] == 0xffffffff ||  /* IN ADDR ANY */\n       (ntohl(m->hops[i+1]) & 0xff000000) == 0x7f000000) /* LOCALHOST */\n      break;\n  memcpy(&addr, m->hops+i, sizeof(unsigned int));\n  snprintf(host, sizeof(host), \"%s\", inet_ntoa(addr));\n\n#define OUTSTART() do { \\\n  if(started) write(output_fd, \" \", 1); started = true; \\\n} while(0)\n  for(i=0;i<M_COUNT;i++) {\n    switch(o_fmt[i]) {\n    case M_NONE: break;\n    case M_HOST: \n      OUTSTART();\n      write(output_fd, host, strlen(host));\n      break;\n    case M_ROUTE:\n      OUTSTART();\n      write(output_fd, m->route.name, m->route.len);\n      break;\n    case M_PAYLOAD:\n      OUTSTART();\n      if(inject_json && payload[0] == '{' && payload[payload_len-1] == '}') {\n        struct iovec iov[8];\n        write(output_fd, payload, payload_len-1);\n        iov[0].iov_base = \"\\\"_host\\\":\\\"\"; iov[0].iov_len = 9;\n        if(payload_len > 2) {\n          iov[0].iov_base = \",\\\"_host\\\":\\\"\"; iov[0].iov_len = 10;\n        }\n        iov[1].iov_base = host; iov[1].iov_len = strlen(host);\n        iov[2].iov_base = \"\\\"\"; iov[2].iov_len = 1;\n        writev(output_fd, iov, 3);\n        iov[0].iov_base = \",\\\"_route\\\":\\\"\"; iov[0].iov_len = 11;\n        iov[1].iov_base = m->route.name; iov[1].iov_len = m->route.len;\n        iov[2].iov_base = \"\\\"\"; iov[2].iov_len = 1;\n        writev(output_fd, iov, 3);\n        write(output_fd, \"}\", 1);\n      }\n      else {\n        write(output_fd, payload, payload_len);\n      }\n      break;\n    }\n  }\n  if(started && lineend) write(output_fd, \"\\n\", 1);\n  return true;\n}\n\nfq_hooks hooks = {\n .version = FQ_HOOKS_V4,\n .auth = my_auth_handler,\n .bind = my_bind_handler,\n .message = my_message_handler\n};\n\nint usage(const char *prog) {\n  printf(\"%s [-h host] [-P port] [-u user] [-p pass]\\n\", prog);\n  printf(\"\\t<-e name> <-q name> [-t <mem|disk>] [-D]\\n\");\n  printf(\"\\t[-(b|B) 'program'] <-d backlog>\\n\");\n  printf(\"\\n\");\n  printf(\"\\t-h <host>\\t\\tdefault: localhost\\n\");\n  printf(\"\\t-e <name>\\t\\texchange name\\n\");\n  printf(\"\\t-P <port>\\t\\tdefault: 8765\\n\");\n  printf(\"\\t-u <port>\\t\\tdefault: nobody\\n\");\n  printf(\"\\t-p <pass>\\t\\tdefault: nopass\\n\");\n  printf(\"\\t-q <name>\\t\\tqueue name\\n\");\n  printf(\"\\t-t <type>\\t\\tqueue type\\n\");\n  printf(\"\\t-d <n>\\t\\t\\tdefine queue depth (backlog)\\n\");\n  printf(\"\\t-D\\t\\t\\tdelete queue (make transient)\\n\");\n  printf(\"\\t-B <prog>\\t\\tinstall binding\\n\");\n  printf(\"\\t-b <prog>\\t\\tuninstall binding\\n\");\n  printf(\"\\t-o <outfile>\\t\\toutput file (- for stdout)\\n\");\n  return 0;\n}\n\nint main(int argc, char **argv) {\n  int opt;\n  char *user = (char *)\"nobody\";\n  char *pass = (char *)\"nopass\";\n  char *host = (char *)\"localhost\";\n  int port = 8765;\n  char *queuename = NULL;\n  char *qtype = (char *)\"mem\";\n  int backlog = -1;\n  char connstr[256];\n  fq_client c;\n\n  while((opt = getopt(argc, argv, \"Jo:f:Dh:e:P:u:p:q:t:B:b:d:\")) != EOF) {\n    switch(opt) {\n    case 'o': output = strdup(optarg); break;\n    case 'f': format = strdup(optarg); break;\n    case 'e': exchange = strdup(optarg); break;\n    case 'h': host = strdup(optarg); break;\n    case 'P': port = atoi(optarg); break;\n    case 'u': user = strdup(optarg); break;\n    case 'p': pass = strdup(optarg); break;\n    case 'q': queuename = strdup(optarg); break;\n    case 't': qtype = strdup(optarg); break;\n    case 'D': permanent = 0; break;\n    case 'B': binding_prog = strdup(optarg);\n              binding_trans = 0; break;\n    case 'b': binding_prog = strdup(optarg);\n              binding_trans = 1; break;\n    case 'd': backlog = atoi(optarg); break;\n    case 'J': inject_json = false; break;\n    default: exit(usage(argv[0]));\n    }\n  }\n  if(format) {\n    char *word, *brkt;\n    int o_i = 0;\n    for (word = strtok_r(format, \",\", &brkt);\n         word && o_i < M_COUNT;\n         word = strtok_r(NULL, \",\", &brkt)) {\n      if(!strcmp(word, \"host\")) o_fmt[o_i++] = M_HOST;\n      else if(!strcmp(word, \"route\")) o_fmt[o_i++] = M_ROUTE;\n      else if(!strcmp(word, \"payload\")) o_fmt[o_i++] = M_PAYLOAD;\n      else {\n        fprintf(stderr, \"Error: unknown format '%s'\\n\", word);\n        exit(-2);\n      }\n    }\n    for(;o_i<M_COUNT;o_i++) o_fmt[o_i] = M_NONE;\n  }\n  if(output) {\n    if(!strcmp(output, \"-\")) output_fd = STDOUT_FILENO;\n    else {\n      output_fd = open(output, O_CREAT|O_APPEND|O_WRONLY, 0666);\n      if(output_fd < 0) {\n        fprintf(stderr, \"Error opening output: %s\\n\", strerror(errno));\n        exit(-2);\n      }\n    }\n  }\n  if(!permanent && !queuename) {\n    struct utsname utsn;\n    char tqname[128];\n    char *progname;\n    char *nodename = \"unknown\";\n    progname = strrchr(argv[0], '/');\n    if(!progname) progname = argv[0];\n    else progname++;\n    if(uname(&utsn) == 0) nodename = utsn.nodename;\n    snprintf(tqname, sizeof(tqname), \"q-%s-%s-%d\", progname, nodename, getpid());\n    queuename = strdup(tqname);\n  }\n  if(!queuename) {\n    fprintf(stderr, \"Error: no queuename specified, but -D omitted.\\n\\n\");\n    exit(usage(argv[0]));\n  }\n  if(!exchange) {\n    fprintf(stderr, \"Error: no exchange specified.\\n\\n\");\n    exit(usage(argv[0]));\n  }\n  snprintf(connstr, sizeof(connstr), \"%s/%s/%s:%s,public,backlog=%d\",\n           user, queuename, qtype, permanent ? \"permanent\" : \"transient\",\n           backlog);\n\n  char *fq_debug = getenv(\"FQ_DEBUG\");\n  if(fq_debug) fq_debug_set_bits(atoi(fq_debug));\n  signal(SIGPIPE, SIG_IGN);\n  fq_client_init(&c, 0, logger);\n  if(fq_client_hooks(c, &hooks)) {\n    fprintf(stderr, \"Can't register hooks\\n\");\n    exit(-1);\n  }\n  fq_client_hooks(c, &hooks);\n  fq_client_creds(c, host, port, connstr, pass);\n  fq_client_heartbeat(c, 1000);\n  fq_client_set_backlog(c, 10000, 100);\n  fq_client_connect(c);\n\n  pause();\n  \n  return 0;\n}\n"
  },
  {
    "path": "http_parser.c",
    "content": "/* Based on src/http/ngx_http_parse.c from NGINX copyright Igor Sysoev\n *\n * Additional changes are licensed under the same terms as NGINX and\n * copyright Joyent, Inc. and other Node contributors. All rights reserved.\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n#include \"http_parser.h\"\n#include <assert.h>\n#include <stddef.h>\n#include <ctype.h>\n#include <stdlib.h>\n#include <string.h>\n#include <limits.h>\n\n#ifndef ULLONG_MAX\n# define ULLONG_MAX ((uint64_t) -1) /* 2^64-1 */\n#endif\n\n#ifndef MIN\n# define MIN(a,b) ((a) < (b) ? (a) : (b))\n#endif\n\n#ifndef ARRAY_SIZE\n# define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))\n#endif\n\n#ifndef BIT_AT\n# define BIT_AT(a, i)                                                \\\n  (!!((unsigned int) (a)[(unsigned int) (i) >> 3] &                  \\\n   (1 << ((unsigned int) (i) & 7))))\n#endif\n\n#ifndef ELEM_AT\n# define ELEM_AT(a, i, v) ((unsigned int) (i) < ARRAY_SIZE(a) ? (a)[(i)] : (v))\n#endif\n\n#define SET_ERRNO(e)                                                 \\\ndo {                                                                 \\\n  parser->http_errno = (e);                                          \\\n} while(0)\n\n#define CURRENT_STATE() p_state\n#define UPDATE_STATE(V) p_state = (enum state) (V);\n#define RETURN(V)                                                    \\\ndo {                                                                 \\\n  parser->state = CURRENT_STATE();                                   \\\n  return (V);                                                        \\\n} while (0);\n#define REEXECUTE()                                                  \\\n  --p;                                                               \\\n  break;\n\n\n#ifdef __GNUC__\n# define LIKELY(X) __builtin_expect(!!(X), 1)\n# define UNLIKELY(X) __builtin_expect(!!(X), 0)\n#else\n# define LIKELY(X) (X)\n# define UNLIKELY(X) (X)\n#endif\n\n\n/* Run the notify callback FOR, returning ER if it fails */\n#define CALLBACK_NOTIFY_(FOR, ER)                                    \\\ndo {                                                                 \\\n  assert(HTTP_PARSER_ERRNO(parser) == HPE_OK);                       \\\n                                                                     \\\n  if (LIKELY(settings->on_##FOR)) {                                  \\\n    parser->state = CURRENT_STATE();                                 \\\n    if (UNLIKELY(0 != settings->on_##FOR(parser))) {                 \\\n      SET_ERRNO(HPE_CB_##FOR);                                       \\\n    }                                                                \\\n    UPDATE_STATE(parser->state);                                     \\\n                                                                     \\\n    /* We either errored above or got paused; get out */             \\\n    if (UNLIKELY(HTTP_PARSER_ERRNO(parser) != HPE_OK)) {             \\\n      return (ER);                                                   \\\n    }                                                                \\\n  }                                                                  \\\n} while (0)\n\n/* Run the notify callback FOR and consume the current byte */\n#define CALLBACK_NOTIFY(FOR)            CALLBACK_NOTIFY_(FOR, p - data + 1)\n\n/* Run the notify callback FOR and don't consume the current byte */\n#define CALLBACK_NOTIFY_NOADVANCE(FOR)  CALLBACK_NOTIFY_(FOR, p - data)\n\n/* Run data callback FOR with LEN bytes, returning ER if it fails */\n#define CALLBACK_DATA_(FOR, LEN, ER)                                 \\\ndo {                                                                 \\\n  assert(HTTP_PARSER_ERRNO(parser) == HPE_OK);                       \\\n                                                                     \\\n  if (FOR##_mark) {                                                  \\\n    if (LIKELY(settings->on_##FOR)) {                                \\\n      parser->state = CURRENT_STATE();                               \\\n      if (UNLIKELY(0 !=                                              \\\n                   settings->on_##FOR(parser, FOR##_mark, (LEN)))) { \\\n        SET_ERRNO(HPE_CB_##FOR);                                     \\\n      }                                                              \\\n      UPDATE_STATE(parser->state);                                   \\\n                                                                     \\\n      /* We either errored above or got paused; get out */           \\\n      if (UNLIKELY(HTTP_PARSER_ERRNO(parser) != HPE_OK)) {           \\\n        return (ER);                                                 \\\n      }                                                              \\\n    }                                                                \\\n    FOR##_mark = NULL;                                               \\\n  }                                                                  \\\n} while (0)\n  \n/* Run the data callback FOR and consume the current byte */\n#define CALLBACK_DATA(FOR)                                           \\\n    CALLBACK_DATA_(FOR, p - FOR##_mark, p - data + 1)\n\n/* Run the data callback FOR and don't consume the current byte */\n#define CALLBACK_DATA_NOADVANCE(FOR)                                 \\\n    CALLBACK_DATA_(FOR, p - FOR##_mark, p - data)\n\n/* Set the mark FOR; non-destructive if mark is already set */\n#define MARK(FOR)                                                    \\\ndo {                                                                 \\\n  if (!FOR##_mark) {                                                 \\\n    FOR##_mark = p;                                                  \\\n  }                                                                  \\\n} while (0)\n\n/* Don't allow the total size of the HTTP headers (including the status\n * line) to exceed HTTP_MAX_HEADER_SIZE.  This check is here to protect\n * embedders against denial-of-service attacks where the attacker feeds\n * us a never-ending header that the embedder keeps buffering.\n *\n * This check is arguably the responsibility of embedders but we're doing\n * it on the embedder's behalf because most won't bother and this way we\n * make the web a little safer.  HTTP_MAX_HEADER_SIZE is still far bigger\n * than any reasonable request or response so this should never affect\n * day-to-day operation.\n */\n#define COUNT_HEADER_SIZE(V)                                         \\\ndo {                                                                 \\\n  parser->nread += (V);                                              \\\n  if (UNLIKELY(parser->nread > (HTTP_MAX_HEADER_SIZE))) {            \\\n    SET_ERRNO(HPE_HEADER_OVERFLOW);                                  \\\n    goto error;                                                      \\\n  }                                                                  \\\n} while (0)\n\n\n#define PROXY_CONNECTION \"proxy-connection\"\n#define CONNECTION \"connection\"\n#define CONTENT_LENGTH \"content-length\"\n#define TRANSFER_ENCODING \"transfer-encoding\"\n#define UPGRADE \"upgrade\"\n#define CHUNKED \"chunked\"\n#define KEEP_ALIVE \"keep-alive\"\n#define CLOSE \"close\"\n\n\nstatic const char *method_strings[] =\n  {\n#define XX(num, name, string) #string,\n  HTTP_METHOD_MAP(XX)\n#undef XX\n  };\n\n\n/* Tokens as defined by rfc 2616. Also lowercases them.\n *        token       = 1*<any CHAR except CTLs or separators>\n *     separators     = \"(\" | \")\" | \"<\" | \">\" | \"@\"\n *                    | \",\" | \";\" | \":\" | \"\\\" | <\">\n *                    | \"/\" | \"[\" | \"]\" | \"?\" | \"=\"\n *                    | \"{\" | \"}\" | SP | HT\n */\nstatic const char tokens[256] = {\n/*   0 nul    1 soh    2 stx    3 etx    4 eot    5 enq    6 ack    7 bel  */\n        0,       0,       0,       0,       0,       0,       0,       0,\n/*   8 bs     9 ht    10 nl    11 vt    12 np    13 cr    14 so    15 si   */\n        0,       0,       0,       0,       0,       0,       0,       0,\n/*  16 dle   17 dc1   18 dc2   19 dc3   20 dc4   21 nak   22 syn   23 etb */\n        0,       0,       0,       0,       0,       0,       0,       0,\n/*  24 can   25 em    26 sub   27 esc   28 fs    29 gs    30 rs    31 us  */\n        0,       0,       0,       0,       0,       0,       0,       0,\n/*  32 sp    33  !    34  \"    35  #    36  $    37  %    38  &    39  '  */\n        0,      '!',      0,      '#',     '$',     '%',     '&',    '\\'',\n/*  40  (    41  )    42  *    43  +    44  ,    45  -    46  .    47  /  */\n        0,       0,      '*',     '+',      0,      '-',     '.',      0,\n/*  48  0    49  1    50  2    51  3    52  4    53  5    54  6    55  7  */\n       '0',     '1',     '2',     '3',     '4',     '5',     '6',     '7',\n/*  56  8    57  9    58  :    59  ;    60  <    61  =    62  >    63  ?  */\n       '8',     '9',      0,       0,       0,       0,       0,       0,\n/*  64  @    65  A    66  B    67  C    68  D    69  E    70  F    71  G  */\n        0,      'a',     'b',     'c',     'd',     'e',     'f',     'g',\n/*  72  H    73  I    74  J    75  K    76  L    77  M    78  N    79  O  */\n       'h',     'i',     'j',     'k',     'l',     'm',     'n',     'o',\n/*  80  P    81  Q    82  R    83  S    84  T    85  U    86  V    87  W  */\n       'p',     'q',     'r',     's',     't',     'u',     'v',     'w',\n/*  88  X    89  Y    90  Z    91  [    92  \\    93  ]    94  ^    95  _  */\n       'x',     'y',     'z',      0,       0,       0,      '^',     '_',\n/*  96  `    97  a    98  b    99  c   100  d   101  e   102  f   103  g  */\n       '`',     'a',     'b',     'c',     'd',     'e',     'f',     'g',\n/* 104  h   105  i   106  j   107  k   108  l   109  m   110  n   111  o  */\n       'h',     'i',     'j',     'k',     'l',     'm',     'n',     'o',\n/* 112  p   113  q   114  r   115  s   116  t   117  u   118  v   119  w  */\n       'p',     'q',     'r',     's',     't',     'u',     'v',     'w',\n/* 120  x   121  y   122  z   123  {   124  |   125  }   126  ~   127 del */\n       'x',     'y',     'z',      0,      '|',      0,      '~',       0 };\n\n\nstatic const int8_t unhex[256] =\n  {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1\n  ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1\n  ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1\n  , 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1\n  ,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1\n  ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1\n  ,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1\n  ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1\n  };\n\n\n#if HTTP_PARSER_STRICT\n# define T(v) 0\n#else\n# define T(v) v\n#endif\n\n\nstatic const uint8_t normal_url_char[32] = {\n/*   0 nul    1 soh    2 stx    3 etx    4 eot    5 enq    6 ack    7 bel  */\n        0    |   0    |   0    |   0    |   0    |   0    |   0    |   0,\n/*   8 bs     9 ht    10 nl    11 vt    12 np    13 cr    14 so    15 si   */\n        0    | T(2)   |   0    |   0    | T(16)  |   0    |   0    |   0,\n/*  16 dle   17 dc1   18 dc2   19 dc3   20 dc4   21 nak   22 syn   23 etb */\n        0    |   0    |   0    |   0    |   0    |   0    |   0    |   0,\n/*  24 can   25 em    26 sub   27 esc   28 fs    29 gs    30 rs    31 us  */\n        0    |   0    |   0    |   0    |   0    |   0    |   0    |   0,\n/*  32 sp    33  !    34  \"    35  #    36  $    37  %    38  &    39  '  */\n        0    |   2    |   4    |   0    |   16   |   32   |   64   |  128,\n/*  40  (    41  )    42  *    43  +    44  ,    45  -    46  .    47  /  */\n        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,\n/*  48  0    49  1    50  2    51  3    52  4    53  5    54  6    55  7  */\n        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,\n/*  56  8    57  9    58  :    59  ;    60  <    61  =    62  >    63  ?  */\n        1    |   2    |   4    |   8    |   16   |   32   |   64   |   0,\n/*  64  @    65  A    66  B    67  C    68  D    69  E    70  F    71  G  */\n        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,\n/*  72  H    73  I    74  J    75  K    76  L    77  M    78  N    79  O  */\n        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,\n/*  80  P    81  Q    82  R    83  S    84  T    85  U    86  V    87  W  */\n        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,\n/*  88  X    89  Y    90  Z    91  [    92  \\    93  ]    94  ^    95  _  */\n        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,\n/*  96  `    97  a    98  b    99  c   100  d   101  e   102  f   103  g  */\n        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,\n/* 104  h   105  i   106  j   107  k   108  l   109  m   110  n   111  o  */\n        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,\n/* 112  p   113  q   114  r   115  s   116  t   117  u   118  v   119  w  */\n        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,\n/* 120  x   121  y   122  z   123  {   124  |   125  }   126  ~   127 del */\n        1    |   2    |   4    |   8    |   16   |   32   |   64   |   0, };\n\n#undef T\n\nenum state\n  { s_dead = 1 /* important that this is > 0 */\n\n  , s_start_req_or_res\n  , s_res_or_resp_H\n  , s_start_res\n  , s_res_H\n  , s_res_HT\n  , s_res_HTT\n  , s_res_HTTP\n  , s_res_first_http_major\n  , s_res_http_major\n  , s_res_first_http_minor\n  , s_res_http_minor\n  , s_res_first_status_code\n  , s_res_status_code\n  , s_res_status_start\n  , s_res_status\n  , s_res_line_almost_done\n\n  , s_start_req\n\n  , s_req_method\n  , s_req_spaces_before_url\n  , s_req_schema\n  , s_req_schema_slash\n  , s_req_schema_slash_slash\n  , s_req_server_start\n  , s_req_server\n  , s_req_server_with_at\n  , s_req_path\n  , s_req_query_string_start\n  , s_req_query_string\n  , s_req_fragment_start\n  , s_req_fragment\n  , s_req_http_start\n  , s_req_http_H\n  , s_req_http_HT\n  , s_req_http_HTT\n  , s_req_http_HTTP\n  , s_req_first_http_major\n  , s_req_http_major\n  , s_req_first_http_minor\n  , s_req_http_minor\n  , s_req_line_almost_done\n\n  , s_header_field_start\n  , s_header_field\n  , s_header_value_discard_ws\n  , s_header_value_discard_ws_almost_done\n  , s_header_value_discard_lws\n  , s_header_value_start\n  , s_header_value\n  , s_header_value_lws\n\n  , s_header_almost_done\n\n  , s_chunk_size_start\n  , s_chunk_size\n  , s_chunk_parameters\n  , s_chunk_size_almost_done\n\n  , s_headers_almost_done\n  , s_headers_done\n\n  /* Important: 's_headers_done' must be the last 'header' state. All\n   * states beyond this must be 'body' states. It is used for overflow\n   * checking. See the PARSING_HEADER() macro.\n   */\n\n  , s_chunk_data\n  , s_chunk_data_almost_done\n  , s_chunk_data_done\n\n  , s_body_identity\n  , s_body_identity_eof\n\n  , s_message_done\n  };\n\n\n#define PARSING_HEADER(state) (state <= s_headers_done)\n\n\nenum header_states\n  { h_general = 0\n  , h_C\n  , h_CO\n  , h_CON\n\n  , h_matching_connection\n  , h_matching_proxy_connection\n  , h_matching_content_length\n  , h_matching_transfer_encoding\n  , h_matching_upgrade\n\n  , h_connection\n  , h_content_length\n  , h_transfer_encoding\n  , h_upgrade\n\n  , h_matching_transfer_encoding_chunked\n  , h_matching_connection_token_start\n  , h_matching_connection_keep_alive\n  , h_matching_connection_close\n  , h_matching_connection_upgrade\n  , h_matching_connection_token\n\n  , h_transfer_encoding_chunked\n  , h_connection_keep_alive\n  , h_connection_close\n  , h_connection_upgrade\n  };\n\nenum http_host_state\n  {\n    s_http_host_dead = 1\n  , s_http_userinfo_start\n  , s_http_userinfo\n  , s_http_host_start\n  , s_http_host_v6_start\n  , s_http_host\n  , s_http_host_v6\n  , s_http_host_v6_end\n  , s_http_host_port_start\n  , s_http_host_port\n};\n\n/* Macros for character classes; depends on strict-mode  */\n#define CR                  '\\r'\n#define LF                  '\\n'\n#define LOWER(c)            (unsigned char)(c | 0x20)\n#define IS_ALPHA(c)         (LOWER(c) >= 'a' && LOWER(c) <= 'z')\n#define IS_NUM(c)           ((c) >= '0' && (c) <= '9')\n#define IS_ALPHANUM(c)      (IS_ALPHA(c) || IS_NUM(c))\n#define IS_HEX(c)           (IS_NUM(c) || (LOWER(c) >= 'a' && LOWER(c) <= 'f'))\n#define IS_MARK(c)          ((c) == '-' || (c) == '_' || (c) == '.' || \\\n  (c) == '!' || (c) == '~' || (c) == '*' || (c) == '\\'' || (c) == '(' || \\\n  (c) == ')')\n#define IS_USERINFO_CHAR(c) (IS_ALPHANUM(c) || IS_MARK(c) || (c) == '%' || \\\n  (c) == ';' || (c) == ':' || (c) == '&' || (c) == '=' || (c) == '+' || \\\n  (c) == '$' || (c) == ',')\n\n#define STRICT_TOKEN(c)     (tokens[(unsigned char)c])\n\n#if HTTP_PARSER_STRICT\n#define TOKEN(c)            (tokens[(unsigned char)c])\n#define IS_URL_CHAR(c)      (BIT_AT(normal_url_char, (unsigned char)c))\n#define IS_HOST_CHAR(c)     (IS_ALPHANUM(c) || (c) == '.' || (c) == '-')\n#else\n#define TOKEN(c)            ((c == ' ') ? ' ' : tokens[(unsigned char)c])\n#define IS_URL_CHAR(c)                                                         \\\n  (BIT_AT(normal_url_char, (unsigned char)c) || ((c) & 0x80))\n#define IS_HOST_CHAR(c)                                                        \\\n  (IS_ALPHANUM(c) || (c) == '.' || (c) == '-' || (c) == '_')\n#endif\n\n\n#define start_state (parser->type == HTTP_REQUEST ? s_start_req : s_start_res)\n\n\n#if HTTP_PARSER_STRICT\n# define STRICT_CHECK(cond)                                          \\\ndo {                                                                 \\\n  if (cond) {                                                        \\\n    SET_ERRNO(HPE_STRICT);                                           \\\n    goto error;                                                      \\\n  }                                                                  \\\n} while (0)\n# define NEW_MESSAGE() (http_should_keep_alive(parser) ? start_state : s_dead)\n#else\n# define STRICT_CHECK(cond)\n# define NEW_MESSAGE() start_state\n#endif\n\n\n/* Map errno values to strings for human-readable output */\n#define HTTP_STRERROR_GEN(n, s) { \"HPE_\" #n, s },\nstatic struct {\n  const char *name;\n  const char *description;\n} http_strerror_tab[] = {\n  HTTP_ERRNO_MAP(HTTP_STRERROR_GEN)\n};\n#undef HTTP_STRERROR_GEN\n\nint http_message_needs_eof(const http_parser *parser);\n\n/* Our URL parser.\n *\n * This is designed to be shared by http_parser_execute() for URL validation,\n * hence it has a state transition + byte-for-byte interface. In addition, it\n * is meant to be embedded in http_parser_parse_url(), which does the dirty\n * work of turning state transitions URL components for its API.\n *\n * This function should only be invoked with non-space characters. It is\n * assumed that the caller cares about (and can detect) the transition between\n * URL and non-URL states by looking for these.\n */\nstatic enum state\nparse_url_char(enum state s, const char ch)\n{\n  if (ch == ' ' || ch == '\\r' || ch == '\\n') {\n    return s_dead;\n  }\n\n#if HTTP_PARSER_STRICT\n  if (ch == '\\t' || ch == '\\f') {\n    return s_dead;\n  }\n#endif\n\n  switch (s) {\n    case s_req_spaces_before_url:\n      /* Proxied requests are followed by scheme of an absolute URI (alpha).\n       * All methods except CONNECT are followed by '/' or '*'.\n       */\n\n      if (ch == '/' || ch == '*') {\n        return s_req_path;\n      }\n\n      if (IS_ALPHA(ch)) {\n        return s_req_schema;\n      }\n\n      break;\n\n    case s_req_schema:\n      if (IS_ALPHA(ch)) {\n        return s;\n      }\n\n      if (ch == ':') {\n        return s_req_schema_slash;\n      }\n\n      break;\n\n    case s_req_schema_slash:\n      if (ch == '/') {\n        return s_req_schema_slash_slash;\n      }\n\n      break;\n\n    case s_req_schema_slash_slash:\n      if (ch == '/') {\n        return s_req_server_start;\n      }\n\n      break;\n\n    case s_req_server_with_at:\n      if (ch == '@') {\n        return s_dead;\n      }\n\n    /* FALLTHROUGH */\n    case s_req_server_start:\n    case s_req_server:\n      if (ch == '/') {\n        return s_req_path;\n      }\n\n      if (ch == '?') {\n        return s_req_query_string_start;\n      }\n\n      if (ch == '@') {\n        return s_req_server_with_at;\n      }\n\n      if (IS_USERINFO_CHAR(ch) || ch == '[' || ch == ']') {\n        return s_req_server;\n      }\n\n      break;\n\n    case s_req_path:\n      if (IS_URL_CHAR(ch)) {\n        return s;\n      }\n\n      switch (ch) {\n        case '?':\n          return s_req_query_string_start;\n\n        case '#':\n          return s_req_fragment_start;\n      }\n\n      break;\n\n    case s_req_query_string_start:\n    case s_req_query_string:\n      if (IS_URL_CHAR(ch)) {\n        return s_req_query_string;\n      }\n\n      switch (ch) {\n        case '?':\n          /* allow extra '?' in query string */\n          return s_req_query_string;\n\n        case '#':\n          return s_req_fragment_start;\n      }\n\n      break;\n\n    case s_req_fragment_start:\n      if (IS_URL_CHAR(ch)) {\n        return s_req_fragment;\n      }\n\n      switch (ch) {\n        case '?':\n          return s_req_fragment;\n\n        case '#':\n          return s;\n      }\n\n      break;\n\n    case s_req_fragment:\n      if (IS_URL_CHAR(ch)) {\n        return s;\n      }\n\n      switch (ch) {\n        case '?':\n        case '#':\n          return s;\n      }\n\n      break;\n\n    default:\n      break;\n  }\n\n  /* We should never fall out of the switch above unless there's an error */\n  return s_dead;\n}\n\nsize_t http_parser_execute (http_parser *parser,\n                            const http_parser_settings *settings,\n                            const char *data,\n                            size_t len)\n{\n  char c, ch;\n  int8_t unhex_val;\n  const char *p = data;\n  const char *header_field_mark = 0;\n  const char *header_value_mark = 0;\n  const char *url_mark = 0;\n  const char *body_mark = 0;\n  const char *status_mark = 0;\n  enum state p_state = (enum state) parser->state;\n\n  /* We're in an error state. Don't bother doing anything. */\n  if (HTTP_PARSER_ERRNO(parser) != HPE_OK) {\n    return 0;\n  }\n\n  if (len == 0) {\n    switch (CURRENT_STATE()) {\n      case s_body_identity_eof:\n        /* Use of CALLBACK_NOTIFY() here would erroneously return 1 byte read if\n         * we got paused.\n         */\n        CALLBACK_NOTIFY_NOADVANCE(message_complete);\n        return 0;\n\n      case s_dead:\n      case s_start_req_or_res:\n      case s_start_res:\n      case s_start_req:\n        return 0;\n\n      default:\n        SET_ERRNO(HPE_INVALID_EOF_STATE);\n        return 1;\n    }\n  }\n\n\n  if (CURRENT_STATE() == s_header_field)\n    header_field_mark = data;\n  if (CURRENT_STATE() == s_header_value)\n    header_value_mark = data;\n  switch (CURRENT_STATE()) {\n  case s_req_path:\n  case s_req_schema:\n  case s_req_schema_slash:\n  case s_req_schema_slash_slash:\n  case s_req_server_start:\n  case s_req_server:\n  case s_req_server_with_at:\n  case s_req_query_string_start:\n  case s_req_query_string:\n  case s_req_fragment_start:\n  case s_req_fragment:\n    url_mark = data;\n    break;\n  case s_res_status:\n    status_mark = data;\n    break;\n  default:\n    break;\n  }\n\n  for (p=data; p != data + len; p++) {\n    ch = *p;\n\n    if (PARSING_HEADER(CURRENT_STATE()))\n      COUNT_HEADER_SIZE(1);\n\n    switch (CURRENT_STATE()) {\n\n      case s_dead:\n        /* this state is used after a 'Connection: close' message\n         * the parser will error out if it reads another message\n         */\n        if (LIKELY(ch == CR || ch == LF))\n          break;\n\n        SET_ERRNO(HPE_CLOSED_CONNECTION);\n        goto error;\n\n      case s_start_req_or_res:\n      {\n        if (ch == CR || ch == LF)\n          break;\n        parser->flags = 0;\n        parser->content_length = ULLONG_MAX;\n\n        if (ch == 'H') {\n          UPDATE_STATE(s_res_or_resp_H);\n\n          CALLBACK_NOTIFY(message_begin);\n        } else {\n          parser->type = HTTP_REQUEST;\n          UPDATE_STATE(s_start_req);\n          REEXECUTE();\n        }\n\n        break;\n      }\n\n      case s_res_or_resp_H:\n        if (ch == 'T') {\n          parser->type = HTTP_RESPONSE;\n          UPDATE_STATE(s_res_HT);\n        } else {\n          if (UNLIKELY(ch != 'E')) {\n            SET_ERRNO(HPE_INVALID_CONSTANT);\n            goto error;\n          }\n\n          parser->type = HTTP_REQUEST;\n          parser->method = HTTP_HEAD;\n          parser->index = 2;\n          UPDATE_STATE(s_req_method);\n        }\n        break;\n\n      case s_start_res:\n      {\n        parser->flags = 0;\n        parser->content_length = ULLONG_MAX;\n\n        switch (ch) {\n          case 'H':\n            UPDATE_STATE(s_res_H);\n            break;\n\n          case CR:\n          case LF:\n            break;\n\n          default:\n            SET_ERRNO(HPE_INVALID_CONSTANT);\n            goto error;\n        }\n\n        CALLBACK_NOTIFY(message_begin);\n        break;\n      }\n\n      case s_res_H:\n        STRICT_CHECK(ch != 'T');\n        UPDATE_STATE(s_res_HT);\n        break;\n\n      case s_res_HT:\n        STRICT_CHECK(ch != 'T');\n        UPDATE_STATE(s_res_HTT);\n        break;\n\n      case s_res_HTT:\n        STRICT_CHECK(ch != 'P');\n        UPDATE_STATE(s_res_HTTP);\n        break;\n\n      case s_res_HTTP:\n        STRICT_CHECK(ch != '/');\n        UPDATE_STATE(s_res_first_http_major);\n        break;\n\n      case s_res_first_http_major:\n        if (UNLIKELY(ch < '0' || ch > '9')) {\n          SET_ERRNO(HPE_INVALID_VERSION);\n          goto error;\n        }\n\n        parser->http_major = ch - '0';\n        UPDATE_STATE(s_res_http_major);\n        break;\n\n      /* major HTTP version or dot */\n      case s_res_http_major:\n      {\n        if (ch == '.') {\n          UPDATE_STATE(s_res_first_http_minor);\n          break;\n        }\n\n        if (!IS_NUM(ch)) {\n          SET_ERRNO(HPE_INVALID_VERSION);\n          goto error;\n        }\n\n        parser->http_major *= 10;\n        parser->http_major += ch - '0';\n\n        if (UNLIKELY(parser->http_major > 999)) {\n          SET_ERRNO(HPE_INVALID_VERSION);\n          goto error;\n        }\n\n        break;\n      }\n\n      /* first digit of minor HTTP version */\n      case s_res_first_http_minor:\n        if (UNLIKELY(!IS_NUM(ch))) {\n          SET_ERRNO(HPE_INVALID_VERSION);\n          goto error;\n        }\n\n        parser->http_minor = ch - '0';\n        UPDATE_STATE(s_res_http_minor);\n        break;\n\n      /* minor HTTP version or end of request line */\n      case s_res_http_minor:\n      {\n        if (ch == ' ') {\n          UPDATE_STATE(s_res_first_status_code);\n          break;\n        }\n\n        if (UNLIKELY(!IS_NUM(ch))) {\n          SET_ERRNO(HPE_INVALID_VERSION);\n          goto error;\n        }\n\n        parser->http_minor *= 10;\n        parser->http_minor += ch - '0';\n\n        if (UNLIKELY(parser->http_minor > 999)) {\n          SET_ERRNO(HPE_INVALID_VERSION);\n          goto error;\n        }\n\n        break;\n      }\n\n      case s_res_first_status_code:\n      {\n        if (!IS_NUM(ch)) {\n          if (ch == ' ') {\n            break;\n          }\n\n          SET_ERRNO(HPE_INVALID_STATUS);\n          goto error;\n        }\n        parser->status_code = ch - '0';\n        UPDATE_STATE(s_res_status_code);\n        break;\n      }\n\n      case s_res_status_code:\n      {\n        if (!IS_NUM(ch)) {\n          switch (ch) {\n            case ' ':\n              UPDATE_STATE(s_res_status_start);\n              break;\n            case CR:\n              UPDATE_STATE(s_res_line_almost_done);\n              break;\n            case LF:\n              UPDATE_STATE(s_header_field_start);\n              break;\n            default:\n              SET_ERRNO(HPE_INVALID_STATUS);\n              goto error;\n          }\n          break;\n        }\n\n        parser->status_code *= 10;\n        parser->status_code += ch - '0';\n\n        if (UNLIKELY(parser->status_code > 999)) {\n          SET_ERRNO(HPE_INVALID_STATUS);\n          goto error;\n        }\n\n        break;\n      }\n\n      case s_res_status_start:\n      {\n        if (ch == CR) {\n          UPDATE_STATE(s_res_line_almost_done);\n          break;\n        }\n\n        if (ch == LF) {\n          UPDATE_STATE(s_header_field_start);\n          break;\n        }\n\n        MARK(status);\n        UPDATE_STATE(s_res_status);\n        parser->index = 0;\n        break;\n      }\n\n      case s_res_status:\n        if (ch == CR) {\n          UPDATE_STATE(s_res_line_almost_done);\n          CALLBACK_DATA(status);\n          break;\n        }\n\n        if (ch == LF) {\n          UPDATE_STATE(s_header_field_start);\n          CALLBACK_DATA(status);\n          break;\n        }\n\n        break;\n\n      case s_res_line_almost_done:\n        STRICT_CHECK(ch != LF);\n        UPDATE_STATE(s_header_field_start);\n        break;\n\n      case s_start_req:\n      {\n        if (ch == CR || ch == LF)\n          break;\n        parser->flags = 0;\n        parser->content_length = ULLONG_MAX;\n\n        if (UNLIKELY(!IS_ALPHA(ch))) {\n          SET_ERRNO(HPE_INVALID_METHOD);\n          goto error;\n        }\n\n        parser->method = (enum http_method) 0;\n        parser->index = 1;\n        switch (ch) {\n          case 'C': parser->method = HTTP_CONNECT; /* or COPY, CHECKOUT */ break;\n          case 'D': parser->method = HTTP_DELETE; break;\n          case 'G': parser->method = HTTP_GET; break;\n          case 'H': parser->method = HTTP_HEAD; break;\n          case 'L': parser->method = HTTP_LOCK; break;\n          case 'M': parser->method = HTTP_MKCOL; /* or MOVE, MKACTIVITY, MERGE, M-SEARCH, MKCALENDAR */ break;\n          case 'N': parser->method = HTTP_NOTIFY; break;\n          case 'O': parser->method = HTTP_OPTIONS; break;\n          case 'P': parser->method = HTTP_POST;\n            /* or PROPFIND|PROPPATCH|PUT|PATCH|PURGE */\n            break;\n          case 'R': parser->method = HTTP_REPORT; break;\n          case 'S': parser->method = HTTP_SUBSCRIBE; /* or SEARCH */ break;\n          case 'T': parser->method = HTTP_TRACE; break;\n          case 'U': parser->method = HTTP_UNLOCK; /* or UNSUBSCRIBE */ break;\n          default:\n            SET_ERRNO(HPE_INVALID_METHOD);\n            goto error;\n        }\n        UPDATE_STATE(s_req_method);\n\n        CALLBACK_NOTIFY(message_begin);\n\n        break;\n      }\n\n      case s_req_method:\n      {\n        const char *matcher;\n        if (UNLIKELY(ch == '\\0')) {\n          SET_ERRNO(HPE_INVALID_METHOD);\n          goto error;\n        }\n\n        matcher = method_strings[parser->method];\n        if (ch == ' ' && matcher[parser->index] == '\\0') {\n          UPDATE_STATE(s_req_spaces_before_url);\n        } else if (ch == matcher[parser->index]) {\n          ; /* nada */\n        } else if (parser->method == HTTP_CONNECT) {\n          if (parser->index == 1 && ch == 'H') {\n            parser->method = HTTP_CHECKOUT;\n          } else if (parser->index == 2  && ch == 'P') {\n            parser->method = HTTP_COPY;\n          } else {\n            SET_ERRNO(HPE_INVALID_METHOD);\n            goto error;\n          }\n        } else if (parser->method == HTTP_MKCOL) {\n          if (parser->index == 1 && ch == 'O') {\n            parser->method = HTTP_MOVE;\n          } else if (parser->index == 1 && ch == 'E') {\n            parser->method = HTTP_MERGE;\n          } else if (parser->index == 1 && ch == '-') {\n            parser->method = HTTP_MSEARCH;\n          } else if (parser->index == 2 && ch == 'A') {\n            parser->method = HTTP_MKACTIVITY;\n          } else if (parser->index == 3 && ch == 'A') {\n            parser->method = HTTP_MKCALENDAR;\n          } else {\n            SET_ERRNO(HPE_INVALID_METHOD);\n            goto error;\n          }\n        } else if (parser->method == HTTP_SUBSCRIBE) {\n          if (parser->index == 1 && ch == 'E') {\n            parser->method = HTTP_SEARCH;\n          } else {\n            SET_ERRNO(HPE_INVALID_METHOD);\n            goto error;\n          }\n        } else if (parser->index == 1 && parser->method == HTTP_POST) {\n          if (ch == 'R') {\n            parser->method = HTTP_PROPFIND; /* or HTTP_PROPPATCH */\n          } else if (ch == 'U') {\n            parser->method = HTTP_PUT; /* or HTTP_PURGE */\n          } else if (ch == 'A') {\n            parser->method = HTTP_PATCH;\n          } else {\n            SET_ERRNO(HPE_INVALID_METHOD);\n            goto error;\n          }\n        } else if (parser->index == 2) {\n          if (parser->method == HTTP_PUT) {\n            if (ch == 'R') {\n              parser->method = HTTP_PURGE;\n            } else {\n              SET_ERRNO(HPE_INVALID_METHOD);\n              goto error;\n            }\n          } else if (parser->method == HTTP_UNLOCK) {\n            if (ch == 'S') {\n              parser->method = HTTP_UNSUBSCRIBE;\n            } else {\n              SET_ERRNO(HPE_INVALID_METHOD);\n              goto error;\n            }\n          } else {\n            SET_ERRNO(HPE_INVALID_METHOD);\n            goto error;\n          }\n        } else if (parser->index == 4 && parser->method == HTTP_PROPFIND && ch == 'P') {\n          parser->method = HTTP_PROPPATCH;\n        } else {\n          SET_ERRNO(HPE_INVALID_METHOD);\n          goto error;\n        }\n\n        ++parser->index;\n        break;\n      }\n\n      case s_req_spaces_before_url:\n      {\n        if (ch == ' ') break;\n\n        MARK(url);\n        if (parser->method == HTTP_CONNECT) {\n          UPDATE_STATE(s_req_server_start);\n        }\n\n        UPDATE_STATE(parse_url_char(CURRENT_STATE(), ch));\n        if (UNLIKELY(CURRENT_STATE() == s_dead)) {\n          SET_ERRNO(HPE_INVALID_URL);\n          goto error;\n        }\n\n        break;\n      }\n\n      case s_req_schema:\n      case s_req_schema_slash:\n      case s_req_schema_slash_slash:\n      case s_req_server_start:\n      {\n        switch (ch) {\n          /* No whitespace allowed here */\n          case ' ':\n          case CR:\n          case LF:\n            SET_ERRNO(HPE_INVALID_URL);\n            goto error;\n          default:\n            UPDATE_STATE(parse_url_char(CURRENT_STATE(), ch));\n            if (UNLIKELY(CURRENT_STATE() == s_dead)) {\n              SET_ERRNO(HPE_INVALID_URL);\n              goto error;\n            }\n        }\n\n        break;\n      }\n\n      case s_req_server:\n      case s_req_server_with_at:\n      case s_req_path:\n      case s_req_query_string_start:\n      case s_req_query_string:\n      case s_req_fragment_start:\n      case s_req_fragment:\n      {\n        switch (ch) {\n          case ' ':\n            UPDATE_STATE(s_req_http_start);\n            CALLBACK_DATA(url);\n            break;\n          case CR:\n          case LF:\n            parser->http_major = 0;\n            parser->http_minor = 9;\n            UPDATE_STATE((ch == CR) ?\n              s_req_line_almost_done :\n              s_header_field_start);\n            CALLBACK_DATA(url);\n            break;\n          default:\n            UPDATE_STATE(parse_url_char(CURRENT_STATE(), ch));\n            if (UNLIKELY(CURRENT_STATE() == s_dead)) {\n              SET_ERRNO(HPE_INVALID_URL);\n              goto error;\n            }\n        }\n        break;\n      }\n\n      case s_req_http_start:\n        switch (ch) {\n          case 'H':\n            UPDATE_STATE(s_req_http_H);\n            break;\n          case ' ':\n            break;\n          default:\n            SET_ERRNO(HPE_INVALID_CONSTANT);\n            goto error;\n        }\n        break;\n\n      case s_req_http_H:\n        STRICT_CHECK(ch != 'T');\n        UPDATE_STATE(s_req_http_HT);\n        break;\n\n      case s_req_http_HT:\n        STRICT_CHECK(ch != 'T');\n        UPDATE_STATE(s_req_http_HTT);\n        break;\n\n      case s_req_http_HTT:\n        STRICT_CHECK(ch != 'P');\n        UPDATE_STATE(s_req_http_HTTP);\n        break;\n\n      case s_req_http_HTTP:\n        STRICT_CHECK(ch != '/');\n        UPDATE_STATE(s_req_first_http_major);\n        break;\n\n      /* first digit of major HTTP version */\n      case s_req_first_http_major:\n        if (UNLIKELY(ch < '1' || ch > '9')) {\n          SET_ERRNO(HPE_INVALID_VERSION);\n          goto error;\n        }\n\n        parser->http_major = ch - '0';\n        UPDATE_STATE(s_req_http_major);\n        break;\n\n      /* major HTTP version or dot */\n      case s_req_http_major:\n      {\n        if (ch == '.') {\n          UPDATE_STATE(s_req_first_http_minor);\n          break;\n        }\n\n        if (UNLIKELY(!IS_NUM(ch))) {\n          SET_ERRNO(HPE_INVALID_VERSION);\n          goto error;\n        }\n\n        parser->http_major *= 10;\n        parser->http_major += ch - '0';\n\n        if (UNLIKELY(parser->http_major > 999)) {\n          SET_ERRNO(HPE_INVALID_VERSION);\n          goto error;\n        }\n\n        break;\n      }\n\n      /* first digit of minor HTTP version */\n      case s_req_first_http_minor:\n        if (UNLIKELY(!IS_NUM(ch))) {\n          SET_ERRNO(HPE_INVALID_VERSION);\n          goto error;\n        }\n\n        parser->http_minor = ch - '0';\n        UPDATE_STATE(s_req_http_minor);\n        break;\n\n      /* minor HTTP version or end of request line */\n      case s_req_http_minor:\n      {\n        if (ch == CR) {\n          UPDATE_STATE(s_req_line_almost_done);\n          break;\n        }\n\n        if (ch == LF) {\n          UPDATE_STATE(s_header_field_start);\n          break;\n        }\n\n        /* XXX allow spaces after digit? */\n\n        if (UNLIKELY(!IS_NUM(ch))) {\n          SET_ERRNO(HPE_INVALID_VERSION);\n          goto error;\n        }\n\n        parser->http_minor *= 10;\n        parser->http_minor += ch - '0';\n\n        if (UNLIKELY(parser->http_minor > 999)) {\n          SET_ERRNO(HPE_INVALID_VERSION);\n          goto error;\n        }\n\n        break;\n      }\n\n      /* end of request line */\n      case s_req_line_almost_done:\n      {\n        if (UNLIKELY(ch != LF)) {\n          SET_ERRNO(HPE_LF_EXPECTED);\n          goto error;\n        }\n\n        UPDATE_STATE(s_header_field_start);\n        break;\n      }\n\n      case s_header_field_start:\n      {\n        if (ch == CR) {\n          UPDATE_STATE(s_headers_almost_done);\n          break;\n        }\n\n        if (ch == LF) {\n          /* they might be just sending \\n instead of \\r\\n so this would be\n           * the second \\n to denote the end of headers*/\n          UPDATE_STATE(s_headers_almost_done);\n          REEXECUTE();\n        }\n\n        c = TOKEN(ch);\n\n        if (UNLIKELY(!c)) {\n          SET_ERRNO(HPE_INVALID_HEADER_TOKEN);\n          goto error;\n        }\n\n        MARK(header_field);\n\n        parser->index = 0;\n        UPDATE_STATE(s_header_field);\n\n        switch (c) {\n          case 'c':\n            parser->header_state = h_C;\n            break;\n\n          case 'p':\n            parser->header_state = h_matching_proxy_connection;\n            break;\n\n          case 't':\n            parser->header_state = h_matching_transfer_encoding;\n            break;\n\n          case 'u':\n            parser->header_state = h_matching_upgrade;\n            break;\n\n          default:\n            parser->header_state = h_general;\n            break;\n        }\n        break;\n      }\n\n      case s_header_field:\n      {\n        const char* start = p;\n        for (; p != data + len; p++) {\n          ch = *p;\n          c = TOKEN(ch);\n\n          if (!c)\n            break;\n\n          switch (parser->header_state) {\n            case h_general:\n              break;\n\n            case h_C:\n              parser->index++;\n              parser->header_state = (c == 'o' ? h_CO : h_general);\n              break;\n\n            case h_CO:\n              parser->index++;\n              parser->header_state = (c == 'n' ? h_CON : h_general);\n              break;\n\n            case h_CON:\n              parser->index++;\n              switch (c) {\n                case 'n':\n                  parser->header_state = h_matching_connection;\n                  break;\n                case 't':\n                  parser->header_state = h_matching_content_length;\n                  break;\n                default:\n                  parser->header_state = h_general;\n                  break;\n              }\n              break;\n\n            /* connection */\n\n            case h_matching_connection:\n              parser->index++;\n              if (parser->index > sizeof(CONNECTION)-1\n                  || c != CONNECTION[parser->index]) {\n                parser->header_state = h_general;\n              } else if (parser->index == sizeof(CONNECTION)-2) {\n                parser->header_state = h_connection;\n              }\n              break;\n\n            /* proxy-connection */\n\n            case h_matching_proxy_connection:\n              parser->index++;\n              if (parser->index > sizeof(PROXY_CONNECTION)-1\n                  || c != PROXY_CONNECTION[parser->index]) {\n                parser->header_state = h_general;\n              } else if (parser->index == sizeof(PROXY_CONNECTION)-2) {\n                parser->header_state = h_connection;\n              }\n              break;\n\n            /* content-length */\n\n            case h_matching_content_length:\n              parser->index++;\n              if (parser->index > sizeof(CONTENT_LENGTH)-1\n                  || c != CONTENT_LENGTH[parser->index]) {\n                parser->header_state = h_general;\n              } else if (parser->index == sizeof(CONTENT_LENGTH)-2) {\n                parser->header_state = h_content_length;\n              }\n              break;\n\n            /* transfer-encoding */\n\n            case h_matching_transfer_encoding:\n              parser->index++;\n              if (parser->index > sizeof(TRANSFER_ENCODING)-1\n                  || c != TRANSFER_ENCODING[parser->index]) {\n                parser->header_state = h_general;\n              } else if (parser->index == sizeof(TRANSFER_ENCODING)-2) {\n                parser->header_state = h_transfer_encoding;\n              }\n              break;\n\n            /* upgrade */\n\n            case h_matching_upgrade:\n              parser->index++;\n              if (parser->index > sizeof(UPGRADE)-1\n                  || c != UPGRADE[parser->index]) {\n                parser->header_state = h_general;\n              } else if (parser->index == sizeof(UPGRADE)-2) {\n                parser->header_state = h_upgrade;\n              }\n              break;\n\n            case h_connection:\n            case h_content_length:\n            case h_transfer_encoding:\n            case h_upgrade:\n              if (ch != ' ') parser->header_state = h_general;\n              break;\n\n            default:\n              assert(0 && \"Unknown header_state\");\n              break;\n          }\n        }\n\n        COUNT_HEADER_SIZE(p - start);\n\n        if (p == data + len) {\n          --p;\n          break;\n        }\n\n        if (ch == ':') {\n          UPDATE_STATE(s_header_value_discard_ws);\n          CALLBACK_DATA(header_field);\n          break;\n        }\n\n        SET_ERRNO(HPE_INVALID_HEADER_TOKEN);\n        goto error;\n      }\n\n      case s_header_value_discard_ws:\n        if (ch == ' ' || ch == '\\t') break;\n\n        if (ch == CR) {\n          UPDATE_STATE(s_header_value_discard_ws_almost_done);\n          break;\n        }\n\n        if (ch == LF) {\n          UPDATE_STATE(s_header_value_discard_lws);\n          break;\n        }\n\n        /* FALLTHROUGH */\n\n      case s_header_value_start:\n      {\n        MARK(header_value);\n\n        UPDATE_STATE(s_header_value);\n        parser->index = 0;\n\n        c = LOWER(ch);\n\n        switch (parser->header_state) {\n          case h_upgrade:\n            parser->flags |= F_UPGRADE;\n            parser->header_state = h_general;\n            break;\n\n          case h_transfer_encoding:\n            /* looking for 'Transfer-Encoding: chunked' */\n            if ('c' == c) {\n              parser->header_state = h_matching_transfer_encoding_chunked;\n            } else {\n              parser->header_state = h_general;\n            }\n            break;\n\n          case h_content_length:\n            if (UNLIKELY(!IS_NUM(ch))) {\n              SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);\n              goto error;\n            }\n\n            parser->content_length = ch - '0';\n            break;\n\n          case h_connection:\n            /* looking for 'Connection: keep-alive' */\n            if (c == 'k') {\n              parser->header_state = h_matching_connection_keep_alive;\n            /* looking for 'Connection: close' */\n            } else if (c == 'c') {\n              parser->header_state = h_matching_connection_close;\n            } else if (c == 'u') {\n              parser->header_state = h_matching_connection_upgrade;\n            } else {\n              parser->header_state = h_matching_connection_token;\n            }\n            break;\n\n          /* Multi-value `Connection` header */\n          case h_matching_connection_token_start:\n            break;\n\n          default:\n            parser->header_state = h_general;\n            break;\n        }\n        break;\n      }\n\n      case s_header_value:\n      {\n        const char* start = p;\n        enum header_states h_state = (enum header_states) parser->header_state;\n        for (; p != data + len; p++) {\n          ch = *p;\n          if (ch == CR) {\n            UPDATE_STATE(s_header_almost_done);\n            parser->header_state = h_state;\n            CALLBACK_DATA(header_value);\n            break;\n          }\n\n          if (ch == LF) {\n            UPDATE_STATE(s_header_almost_done);\n            COUNT_HEADER_SIZE(p - start);\n            parser->header_state = h_state;\n            CALLBACK_DATA_NOADVANCE(header_value);\n            REEXECUTE();\n          }\n\n          c = LOWER(ch);\n\n          switch (h_state) {\n            case h_general:\n            {\n              const char* p_cr;\n              const char* p_lf;\n              size_t limit = data + len - p;\n\n              limit = MIN(limit, HTTP_MAX_HEADER_SIZE);\n\n              p_cr = (const char*) memchr(p, CR, limit);\n              p_lf = (const char*) memchr(p, LF, limit);\n              if (p_cr != NULL) {\n                if (p_lf != NULL && p_cr >= p_lf)\n                  p = p_lf;\n                else\n                  p = p_cr;\n              } else if (UNLIKELY(p_lf != NULL)) {\n                p = p_lf;\n              } else {\n                p = data + len;\n              }\n              --p;\n\n              break;\n            }\n\n            case h_connection:\n            case h_transfer_encoding:\n              assert(0 && \"Shouldn't get here.\");\n              break;\n\n            case h_content_length:\n            {\n              uint64_t t;\n\n              if (ch == ' ') break;\n\n              if (UNLIKELY(!IS_NUM(ch))) {\n                SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);\n                parser->header_state = h_state;\n                goto error;\n              }\n\n              t = parser->content_length;\n              t *= 10;\n              t += ch - '0';\n\n              /* Overflow? Test against a conservative limit for simplicity. */\n              if (UNLIKELY((ULLONG_MAX - 10) / 10 < parser->content_length)) {\n                SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);\n                parser->header_state = h_state;\n                goto error;\n              }\n\n              parser->content_length = t;\n              break;\n            }\n\n            /* Transfer-Encoding: chunked */\n            case h_matching_transfer_encoding_chunked:\n              parser->index++;\n              if (parser->index > sizeof(CHUNKED)-1\n                  || c != CHUNKED[parser->index]) {\n                h_state = h_general;\n              } else if (parser->index == sizeof(CHUNKED)-2) {\n                h_state = h_transfer_encoding_chunked;\n              }\n              break;\n\n            case h_matching_connection_token_start:\n              /* looking for 'Connection: keep-alive' */\n              if (c == 'k') {\n                h_state = h_matching_connection_keep_alive;\n              /* looking for 'Connection: close' */\n              } else if (c == 'c') {\n                h_state = h_matching_connection_close;\n              } else if (c == 'u') {\n                h_state = h_matching_connection_upgrade;\n              } else if (STRICT_TOKEN(c)) {\n                h_state = h_matching_connection_token;\n              } else if (c == ' ' || c == '\\t') {\n                /* Skip lws */\n              } else {\n                h_state = h_general;\n              }\n              break;\n\n            /* looking for 'Connection: keep-alive' */\n            case h_matching_connection_keep_alive:\n              parser->index++;\n              if (parser->index > sizeof(KEEP_ALIVE)-1\n                  || c != KEEP_ALIVE[parser->index]) {\n                h_state = h_matching_connection_token;\n              } else if (parser->index == sizeof(KEEP_ALIVE)-2) {\n                h_state = h_connection_keep_alive;\n              }\n              break;\n\n            /* looking for 'Connection: close' */\n            case h_matching_connection_close:\n              parser->index++;\n              if (parser->index > sizeof(CLOSE)-1 || c != CLOSE[parser->index]) {\n                h_state = h_matching_connection_token;\n              } else if (parser->index == sizeof(CLOSE)-2) {\n                h_state = h_connection_close;\n              }\n              break;\n\n            /* looking for 'Connection: upgrade' */\n            case h_matching_connection_upgrade:\n              parser->index++;\n              if (parser->index > sizeof(UPGRADE) - 1 ||\n                  c != UPGRADE[parser->index]) {\n                h_state = h_matching_connection_token;\n              } else if (parser->index == sizeof(UPGRADE)-2) {\n                h_state = h_connection_upgrade;\n              }\n              break;\n\n            case h_matching_connection_token:\n              if (ch == ',') {\n                h_state = h_matching_connection_token_start;\n                parser->index = 0;\n              }\n              break;\n\n            case h_transfer_encoding_chunked:\n              if (ch != ' ') h_state = h_general;\n              break;\n\n            case h_connection_keep_alive:\n            case h_connection_close:\n            case h_connection_upgrade:\n              if (ch == ',') {\n                if (h_state == h_connection_keep_alive) {\n                  parser->flags |= F_CONNECTION_KEEP_ALIVE;\n                } else if (h_state == h_connection_close) {\n                  parser->flags |= F_CONNECTION_CLOSE;\n                } else if (h_state == h_connection_upgrade) {\n                  parser->flags |= F_CONNECTION_UPGRADE;\n                }\n                h_state = h_matching_connection_token_start;\n                parser->index = 0;\n              } else if (ch != ' ') {\n                h_state = h_matching_connection_token;\n              }\n              break;\n\n            default:\n              UPDATE_STATE(s_header_value);\n              h_state = h_general;\n              break;\n          }\n        }\n        parser->header_state = h_state;\n\n        COUNT_HEADER_SIZE(p - start);\n\n        if (p == data + len)\n          --p;\n        break;\n      }\n\n      case s_header_almost_done:\n      {\n        STRICT_CHECK(ch != LF);\n\n        UPDATE_STATE(s_header_value_lws);\n        break;\n      }\n\n      case s_header_value_lws:\n      {\n        if (ch == ' ' || ch == '\\t') {\n          UPDATE_STATE(s_header_value_start);\n          REEXECUTE();\n        }\n\n        /* finished the header */\n        switch (parser->header_state) {\n          case h_connection_keep_alive:\n            parser->flags |= F_CONNECTION_KEEP_ALIVE;\n            break;\n          case h_connection_close:\n            parser->flags |= F_CONNECTION_CLOSE;\n            break;\n          case h_transfer_encoding_chunked:\n            parser->flags |= F_CHUNKED;\n            break;\n          case h_connection_upgrade:\n            parser->flags |= F_CONNECTION_UPGRADE;\n            break;\n          default:\n            break;\n        }\n\n        UPDATE_STATE(s_header_field_start);\n        REEXECUTE();\n      }\n\n      case s_header_value_discard_ws_almost_done:\n      {\n        STRICT_CHECK(ch != LF);\n        UPDATE_STATE(s_header_value_discard_lws);\n        break;\n      }\n\n      case s_header_value_discard_lws:\n      {\n        if (ch == ' ' || ch == '\\t') {\n          UPDATE_STATE(s_header_value_discard_ws);\n          break;\n        } else {\n          switch (parser->header_state) {\n            case h_connection_keep_alive:\n              parser->flags |= F_CONNECTION_KEEP_ALIVE;\n              break;\n            case h_connection_close:\n              parser->flags |= F_CONNECTION_CLOSE;\n              break;\n            case h_connection_upgrade:\n              parser->flags |= F_CONNECTION_UPGRADE;\n              break;\n            case h_transfer_encoding_chunked:\n              parser->flags |= F_CHUNKED;\n              break;\n            default:\n              break;\n          }\n\n          /* header value was empty */\n          MARK(header_value);\n          UPDATE_STATE(s_header_field_start);\n          CALLBACK_DATA_NOADVANCE(header_value);\n          REEXECUTE();\n        }\n      }\n\n      case s_headers_almost_done:\n      {\n        STRICT_CHECK(ch != LF);\n\n        if (parser->flags & F_TRAILING) {\n          /* End of a chunked request */\n          UPDATE_STATE(NEW_MESSAGE());\n          CALLBACK_NOTIFY(message_complete);\n          break;\n        }\n\n        UPDATE_STATE(s_headers_done);\n\n        /* Set this here so that on_headers_complete() callbacks can see it */\n        parser->upgrade =\n          ((parser->flags & (F_UPGRADE | F_CONNECTION_UPGRADE)) ==\n           (F_UPGRADE | F_CONNECTION_UPGRADE) ||\n           parser->method == HTTP_CONNECT);\n\n        /* Here we call the headers_complete callback. This is somewhat\n         * different than other callbacks because if the user returns 1, we\n         * will interpret that as saying that this message has no body. This\n         * is needed for the annoying case of recieving a response to a HEAD\n         * request.\n         *\n         * We'd like to use CALLBACK_NOTIFY_NOADVANCE() here but we cannot, so\n         * we have to simulate it by handling a change in errno below.\n         */\n        if (settings->on_headers_complete) {\n          switch (settings->on_headers_complete(parser)) {\n            case 0:\n              break;\n\n            case 1:\n              parser->flags |= F_SKIPBODY;\n              break;\n\n            default:\n              SET_ERRNO(HPE_CB_headers_complete);\n              RETURN(p - data); /* Error */\n          }\n        }\n\n        if (HTTP_PARSER_ERRNO(parser) != HPE_OK) {\n          RETURN(p - data);\n        }\n\n        REEXECUTE();\n      }\n\n      case s_headers_done:\n      {\n        STRICT_CHECK(ch != LF);\n\n        parser->nread = 0;\n\n        /* Exit, the rest of the connect is in a different protocol. */\n        if (parser->upgrade) {\n          UPDATE_STATE(NEW_MESSAGE());\n          CALLBACK_NOTIFY(message_complete);\n          RETURN((p - data) + 1);\n        }\n\n        if (parser->flags & F_SKIPBODY) {\n          UPDATE_STATE(NEW_MESSAGE());\n          CALLBACK_NOTIFY(message_complete);\n        } else if (parser->flags & F_CHUNKED) {\n          /* chunked encoding - ignore Content-Length header */\n          UPDATE_STATE(s_chunk_size_start);\n        } else {\n          if (parser->content_length == 0) {\n            /* Content-Length header given but zero: Content-Length: 0\\r\\n */\n            UPDATE_STATE(NEW_MESSAGE());\n            CALLBACK_NOTIFY(message_complete);\n          } else if (parser->content_length != ULLONG_MAX) {\n            /* Content-Length header given and non-zero */\n            UPDATE_STATE(s_body_identity);\n          } else {\n            if (parser->type == HTTP_REQUEST ||\n                !http_message_needs_eof(parser)) {\n              /* Assume content-length 0 - read the next */\n              UPDATE_STATE(NEW_MESSAGE());\n              CALLBACK_NOTIFY(message_complete);\n            } else {\n              /* Read body until EOF */\n              UPDATE_STATE(s_body_identity_eof);\n            }\n          }\n        }\n\n        break;\n      }\n\n      case s_body_identity:\n      {\n        uint64_t to_read = MIN(parser->content_length,\n                               (uint64_t) ((data + len) - p));\n\n        assert(parser->content_length != 0\n            && parser->content_length != ULLONG_MAX);\n\n        /* The difference between advancing content_length and p is because\n         * the latter will automaticaly advance on the next loop iteration.\n         * Further, if content_length ends up at 0, we want to see the last\n         * byte again for our message complete callback.\n         */\n        MARK(body);\n        parser->content_length -= to_read;\n        p += to_read - 1;\n\n        if (parser->content_length == 0) {\n          UPDATE_STATE(s_message_done);\n\n          /* Mimic CALLBACK_DATA_NOADVANCE() but with one extra byte.\n           *\n           * The alternative to doing this is to wait for the next byte to\n           * trigger the data callback, just as in every other case. The\n           * problem with this is that this makes it difficult for the test\n           * harness to distinguish between complete-on-EOF and\n           * complete-on-length. It's not clear that this distinction is\n           * important for applications, but let's keep it for now.\n           */\n          CALLBACK_DATA_(body, p - body_mark + 1, p - data);\n          REEXECUTE();\n        }\n\n        break;\n      }\n\n      /* read until EOF */\n      case s_body_identity_eof:\n        MARK(body);\n        p = data + len - 1;\n\n        break;\n\n      case s_message_done:\n        UPDATE_STATE(NEW_MESSAGE());\n        CALLBACK_NOTIFY(message_complete);\n        break;\n\n      case s_chunk_size_start:\n      {\n        assert(parser->nread == 1);\n        assert(parser->flags & F_CHUNKED);\n\n        unhex_val = unhex[(unsigned char)ch];\n        if (UNLIKELY(unhex_val == -1)) {\n          SET_ERRNO(HPE_INVALID_CHUNK_SIZE);\n          goto error;\n        }\n\n        parser->content_length = unhex_val;\n        UPDATE_STATE(s_chunk_size);\n        break;\n      }\n\n      case s_chunk_size:\n      {\n        uint64_t t;\n\n        assert(parser->flags & F_CHUNKED);\n\n        if (ch == CR) {\n          UPDATE_STATE(s_chunk_size_almost_done);\n          break;\n        }\n\n        unhex_val = unhex[(unsigned char)ch];\n\n        if (unhex_val == -1) {\n          if (ch == ';' || ch == ' ') {\n            UPDATE_STATE(s_chunk_parameters);\n            break;\n          }\n\n          SET_ERRNO(HPE_INVALID_CHUNK_SIZE);\n          goto error;\n        }\n\n        t = parser->content_length;\n        t *= 16;\n        t += unhex_val;\n\n        /* Overflow? Test against a conservative limit for simplicity. */\n        if (UNLIKELY((ULLONG_MAX - 16) / 16 < parser->content_length)) {\n          SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);\n          goto error;\n        }\n\n        parser->content_length = t;\n        break;\n      }\n\n      case s_chunk_parameters:\n      {\n        assert(parser->flags & F_CHUNKED);\n        /* just ignore this shit. TODO check for overflow */\n        if (ch == CR) {\n          UPDATE_STATE(s_chunk_size_almost_done);\n          break;\n        }\n        break;\n      }\n\n      case s_chunk_size_almost_done:\n      {\n        assert(parser->flags & F_CHUNKED);\n        STRICT_CHECK(ch != LF);\n\n        parser->nread = 0;\n\n        if (parser->content_length == 0) {\n          parser->flags |= F_TRAILING;\n          UPDATE_STATE(s_header_field_start);\n        } else {\n          UPDATE_STATE(s_chunk_data);\n        }\n        break;\n      }\n\n      case s_chunk_data:\n      {\n        uint64_t to_read = MIN(parser->content_length,\n                               (uint64_t) ((data + len) - p));\n\n        assert(parser->flags & F_CHUNKED);\n        assert(parser->content_length != 0\n            && parser->content_length != ULLONG_MAX);\n\n        /* See the explanation in s_body_identity for why the content\n         * length and data pointers are managed this way.\n         */\n        MARK(body);\n        parser->content_length -= to_read;\n        p += to_read - 1;\n\n        if (parser->content_length == 0) {\n          UPDATE_STATE(s_chunk_data_almost_done);\n        }\n\n        break;\n      }\n\n      case s_chunk_data_almost_done:\n        assert(parser->flags & F_CHUNKED);\n        assert(parser->content_length == 0);\n        STRICT_CHECK(ch != CR);\n        UPDATE_STATE(s_chunk_data_done);\n        CALLBACK_DATA(body);\n        break;\n\n      case s_chunk_data_done:\n        assert(parser->flags & F_CHUNKED);\n        STRICT_CHECK(ch != LF);\n        parser->nread = 0;\n        UPDATE_STATE(s_chunk_size_start);\n        break;\n\n      default:\n        assert(0 && \"unhandled state\");\n        SET_ERRNO(HPE_INVALID_INTERNAL_STATE);\n        goto error;\n    }\n  }\n\n  /* Run callbacks for any marks that we have leftover after we ran our of\n   * bytes. There should be at most one of these set, so it's OK to invoke\n   * them in series (unset marks will not result in callbacks).\n   *\n   * We use the NOADVANCE() variety of callbacks here because 'p' has already\n   * overflowed 'data' and this allows us to correct for the off-by-one that\n   * we'd otherwise have (since CALLBACK_DATA() is meant to be run with a 'p'\n   * value that's in-bounds).\n   */\n\n  assert(((header_field_mark ? 1 : 0) +\n          (header_value_mark ? 1 : 0) +\n          (url_mark ? 1 : 0)  +\n          (body_mark ? 1 : 0) +\n          (status_mark ? 1 : 0)) <= 1);\n\n  CALLBACK_DATA_NOADVANCE(header_field);\n  CALLBACK_DATA_NOADVANCE(header_value);\n  CALLBACK_DATA_NOADVANCE(url);\n  CALLBACK_DATA_NOADVANCE(body);\n  CALLBACK_DATA_NOADVANCE(status);\n\n  RETURN(len);\n\nerror:\n  if (HTTP_PARSER_ERRNO(parser) == HPE_OK) {\n    SET_ERRNO(HPE_UNKNOWN);\n  }\n\n  RETURN(p - data);\n}\n\n\n/* Does the parser need to see an EOF to find the end of the message? */\nint\nhttp_message_needs_eof (const http_parser *parser)\n{\n  if (parser->type == HTTP_REQUEST) {\n    return 0;\n  }\n\n  /* See RFC 2616 section 4.4 */\n  if (parser->status_code / 100 == 1 || /* 1xx e.g. Continue */\n      parser->status_code == 204 ||     /* No Content */\n      parser->status_code == 304 ||     /* Not Modified */\n      parser->flags & F_SKIPBODY) {     /* response to a HEAD request */\n    return 0;\n  }\n\n  if ((parser->flags & F_CHUNKED) || parser->content_length != ULLONG_MAX) {\n    return 0;\n  }\n\n  return 1;\n}\n\n\nint\nhttp_should_keep_alive (const http_parser *parser)\n{\n  if (parser->http_major > 0 && parser->http_minor > 0) {\n    /* HTTP/1.1 */\n    if (parser->flags & F_CONNECTION_CLOSE) {\n      return 0;\n    }\n  } else {\n    /* HTTP/1.0 or earlier */\n    if (!(parser->flags & F_CONNECTION_KEEP_ALIVE)) {\n      return 0;\n    }\n  }\n\n  return !http_message_needs_eof(parser);\n}\n\n\nconst char *\nhttp_method_str (enum http_method m)\n{\n  return ELEM_AT(method_strings, m, \"<unknown>\");\n}\n\n\nvoid\nhttp_parser_init (http_parser *parser, enum http_parser_type t)\n{\n  void *data = parser->data; /* preserve application data */\n  memset(parser, 0, sizeof(*parser));\n  parser->data = data;\n  parser->type = t;\n  parser->state = (t == HTTP_REQUEST ? s_start_req : (t == HTTP_RESPONSE ? s_start_res : s_start_req_or_res));\n  parser->http_errno = HPE_OK;\n}\n\nvoid\nhttp_parser_settings_init(http_parser_settings *settings)\n{\n  memset(settings, 0, sizeof(*settings));\n}\n\nconst char *\nhttp_errno_name(enum http_errno err) {\n  assert(err < (sizeof(http_strerror_tab)/sizeof(http_strerror_tab[0])));\n  return http_strerror_tab[err].name;\n}\n\nconst char *\nhttp_errno_description(enum http_errno err) {\n  assert(err < (sizeof(http_strerror_tab)/sizeof(http_strerror_tab[0])));\n  return http_strerror_tab[err].description;\n}\n\nstatic enum http_host_state\nhttp_parse_host_char(enum http_host_state s, const char ch) {\n  switch(s) {\n    case s_http_userinfo:\n    case s_http_userinfo_start:\n      if (ch == '@') {\n        return s_http_host_start;\n      }\n\n      if (IS_USERINFO_CHAR(ch)) {\n        return s_http_userinfo;\n      }\n      break;\n\n    case s_http_host_start:\n      if (ch == '[') {\n        return s_http_host_v6_start;\n      }\n\n      if (IS_HOST_CHAR(ch)) {\n        return s_http_host;\n      }\n\n      break;\n\n    case s_http_host:\n      if (IS_HOST_CHAR(ch)) {\n        return s_http_host;\n      }\n\n    /* FALLTHROUGH */\n    case s_http_host_v6_end:\n      if (ch == ':') {\n        return s_http_host_port_start;\n      }\n\n      break;\n\n    case s_http_host_v6:\n      if (ch == ']') {\n        return s_http_host_v6_end;\n      }\n\n    /* FALLTHROUGH */\n    case s_http_host_v6_start:\n      if (IS_HEX(ch) || ch == ':' || ch == '.') {\n        return s_http_host_v6;\n      }\n\n      break;\n\n    case s_http_host_port:\n    case s_http_host_port_start:\n      if (IS_NUM(ch)) {\n        return s_http_host_port;\n      }\n\n      break;\n\n    default:\n      break;\n  }\n  return s_http_host_dead;\n}\n\nstatic int\nhttp_parse_host(const char * buf, struct http_parser_url *u, int found_at) {\n  enum http_host_state s;\n\n  const char *p;\n  size_t buflen = u->field_data[UF_HOST].off + u->field_data[UF_HOST].len;\n\n  u->field_data[UF_HOST].len = 0;\n\n  s = found_at ? s_http_userinfo_start : s_http_host_start;\n\n  for (p = buf + u->field_data[UF_HOST].off; p < buf + buflen; p++) {\n    enum http_host_state new_s = http_parse_host_char(s, *p);\n\n    if (new_s == s_http_host_dead) {\n      return 1;\n    }\n\n    switch(new_s) {\n      case s_http_host:\n        if (s != s_http_host) {\n          u->field_data[UF_HOST].off = p - buf;\n        }\n        u->field_data[UF_HOST].len++;\n        break;\n\n      case s_http_host_v6:\n        if (s != s_http_host_v6) {\n          u->field_data[UF_HOST].off = p - buf;\n        }\n        u->field_data[UF_HOST].len++;\n        break;\n\n      case s_http_host_port:\n        if (s != s_http_host_port) {\n          u->field_data[UF_PORT].off = p - buf;\n          u->field_data[UF_PORT].len = 0;\n          u->field_set |= (1 << UF_PORT);\n        }\n        u->field_data[UF_PORT].len++;\n        break;\n\n      case s_http_userinfo:\n        if (s != s_http_userinfo) {\n          u->field_data[UF_USERINFO].off = p - buf ;\n          u->field_data[UF_USERINFO].len = 0;\n          u->field_set |= (1 << UF_USERINFO);\n        }\n        u->field_data[UF_USERINFO].len++;\n        break;\n\n      default:\n        break;\n    }\n    s = new_s;\n  }\n\n  /* Make sure we don't end somewhere unexpected */\n  switch (s) {\n    case s_http_host_start:\n    case s_http_host_v6_start:\n    case s_http_host_v6:\n    case s_http_host_port_start:\n    case s_http_userinfo:\n    case s_http_userinfo_start:\n      return 1;\n    default:\n      break;\n  }\n\n  return 0;\n}\n\nint\nhttp_parser_parse_url(const char *buf, size_t buflen, int is_connect,\n                      struct http_parser_url *u)\n{\n  enum state s;\n  const char *p;\n  enum http_parser_url_fields uf, old_uf;\n  int found_at = 0;\n\n  u->port = u->field_set = 0;\n  s = is_connect ? s_req_server_start : s_req_spaces_before_url;\n  old_uf = UF_MAX;\n\n  for (p = buf; p < buf + buflen; p++) {\n    s = parse_url_char(s, *p);\n\n    /* Figure out the next field that we're operating on */\n    switch (s) {\n      case s_dead:\n        return 1;\n\n      /* Skip delimeters */\n      case s_req_schema_slash:\n      case s_req_schema_slash_slash:\n      case s_req_server_start:\n      case s_req_query_string_start:\n      case s_req_fragment_start:\n        continue;\n\n      case s_req_schema:\n        uf = UF_SCHEMA;\n        break;\n\n      case s_req_server_with_at:\n        found_at = 1;\n\n      /* FALLTROUGH */\n      case s_req_server:\n        uf = UF_HOST;\n        break;\n\n      case s_req_path:\n        uf = UF_PATH;\n        break;\n\n      case s_req_query_string:\n        uf = UF_QUERY;\n        break;\n\n      case s_req_fragment:\n        uf = UF_FRAGMENT;\n        break;\n\n      default:\n        assert(!\"Unexpected state\");\n        return 1;\n    }\n\n    /* Nothing's changed; soldier on */\n    if (uf == old_uf) {\n      u->field_data[uf].len++;\n      continue;\n    }\n\n    u->field_data[uf].off = p - buf;\n    u->field_data[uf].len = 1;\n\n    u->field_set |= (1 << uf);\n    old_uf = uf;\n  }\n\n  /* host must be present if there is a schema */\n  /* parsing http:///toto will fail */\n  if ((u->field_set & ((1 << UF_SCHEMA) | (1 << UF_HOST))) != 0) {\n    if (http_parse_host(buf, u, found_at) != 0) {\n      return 1;\n    }\n  }\n\n  /* CONNECT requests can only contain \"hostname:port\" */\n  if (is_connect && u->field_set != ((1 << UF_HOST)|(1 << UF_PORT))) {\n    return 1;\n  }\n\n  if (u->field_set & (1 << UF_PORT)) {\n    /* Don't bother with endp; we've already validated the string */\n    unsigned long v = strtoul(buf + u->field_data[UF_PORT].off, NULL, 10);\n\n    /* Ports have a max value of 2^16 */\n    if (v > 0xffff) {\n      return 1;\n    }\n\n    u->port = (uint16_t) v;\n  }\n\n  return 0;\n}\n\nvoid\nhttp_parser_pause(http_parser *parser, int paused) {\n  /* Users should only be pausing/unpausing a parser that is not in an error\n   * state. In non-debug builds, there's not much that we can do about this\n   * other than ignore it.\n   */\n  if (HTTP_PARSER_ERRNO(parser) == HPE_OK ||\n      HTTP_PARSER_ERRNO(parser) == HPE_PAUSED) {\n    SET_ERRNO((paused) ? HPE_PAUSED : HPE_OK);\n  } else {\n    assert(0 && \"Attempting to pause parser in error state\");\n  }\n}\n\nint\nhttp_body_is_final(const struct http_parser *parser) {\n    return parser->state == s_message_done;\n}\n\nunsigned long\nhttp_parser_version(void) {\n  return HTTP_PARSER_VERSION_MAJOR * 0x10000 |\n         HTTP_PARSER_VERSION_MINOR * 0x00100 |\n         HTTP_PARSER_VERSION_PATCH * 0x00001;\n}\n"
  },
  {
    "path": "http_parser.h",
    "content": "/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n#ifndef http_parser_h\n#define http_parser_h\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/* Also update SONAME in the Makefile whenever you change these. */\n#define HTTP_PARSER_VERSION_MAJOR 2\n#define HTTP_PARSER_VERSION_MINOR 4\n#define HTTP_PARSER_VERSION_PATCH 2\n\n#include <sys/types.h>\n#if defined(_WIN32) && !defined(__MINGW32__) && (!defined(_MSC_VER) || _MSC_VER<1600)\n#include <BaseTsd.h>\n#include <stddef.h>\ntypedef __int8 int8_t;\ntypedef unsigned __int8 uint8_t;\ntypedef __int16 int16_t;\ntypedef unsigned __int16 uint16_t;\ntypedef __int32 int32_t;\ntypedef unsigned __int32 uint32_t;\ntypedef __int64 int64_t;\ntypedef unsigned __int64 uint64_t;\n#else\n#include <stdint.h>\n#endif\n\n/* Compile with -DHTTP_PARSER_STRICT=0 to make less checks, but run\n * faster\n */\n#ifndef HTTP_PARSER_STRICT\n# define HTTP_PARSER_STRICT 1\n#endif\n\n/* Maximium header size allowed. If the macro is not defined\n * before including this header then the default is used. To\n * change the maximum header size, define the macro in the build\n * environment (e.g. -DHTTP_MAX_HEADER_SIZE=<value>). To remove\n * the effective limit on the size of the header, define the macro\n * to a very large number (e.g. -DHTTP_MAX_HEADER_SIZE=0x7fffffff)\n */\n#ifndef HTTP_MAX_HEADER_SIZE\n# define HTTP_MAX_HEADER_SIZE (80*1024)\n#endif\n\ntypedef struct http_parser http_parser;\ntypedef struct http_parser_settings http_parser_settings;\n\n\n/* Callbacks should return non-zero to indicate an error. The parser will\n * then halt execution.\n *\n * The one exception is on_headers_complete. In a HTTP_RESPONSE parser\n * returning '1' from on_headers_complete will tell the parser that it\n * should not expect a body. This is used when receiving a response to a\n * HEAD request which may contain 'Content-Length' or 'Transfer-Encoding:\n * chunked' headers that indicate the presence of a body.\n *\n * http_data_cb does not return data chunks. It will be called arbitrarily\n * many times for each string. E.G. you might get 10 callbacks for \"on_url\"\n * each providing just a few characters more data.\n */\ntypedef int (*http_data_cb) (http_parser*, const char *at, size_t length);\ntypedef int (*http_cb) (http_parser*);\n\n\n/* Request Methods */\n#define HTTP_METHOD_MAP(XX)         \\\n  XX(0,  DELETE,      DELETE)       \\\n  XX(1,  GET,         GET)          \\\n  XX(2,  HEAD,        HEAD)         \\\n  XX(3,  POST,        POST)         \\\n  XX(4,  PUT,         PUT)          \\\n  /* pathological */                \\\n  XX(5,  CONNECT,     CONNECT)      \\\n  XX(6,  OPTIONS,     OPTIONS)      \\\n  XX(7,  TRACE,       TRACE)        \\\n  /* webdav */                      \\\n  XX(8,  COPY,        COPY)         \\\n  XX(9,  LOCK,        LOCK)         \\\n  XX(10, MKCOL,       MKCOL)        \\\n  XX(11, MOVE,        MOVE)         \\\n  XX(12, PROPFIND,    PROPFIND)     \\\n  XX(13, PROPPATCH,   PROPPATCH)    \\\n  XX(14, SEARCH,      SEARCH)       \\\n  XX(15, UNLOCK,      UNLOCK)       \\\n  /* subversion */                  \\\n  XX(16, REPORT,      REPORT)       \\\n  XX(17, MKACTIVITY,  MKACTIVITY)   \\\n  XX(18, CHECKOUT,    CHECKOUT)     \\\n  XX(19, MERGE,       MERGE)        \\\n  /* upnp */                        \\\n  XX(20, MSEARCH,     M-SEARCH)     \\\n  XX(21, NOTIFY,      NOTIFY)       \\\n  XX(22, SUBSCRIBE,   SUBSCRIBE)    \\\n  XX(23, UNSUBSCRIBE, UNSUBSCRIBE)  \\\n  /* RFC-5789 */                    \\\n  XX(24, PATCH,       PATCH)        \\\n  XX(25, PURGE,       PURGE)        \\\n  /* CalDAV */                      \\\n  XX(26, MKCALENDAR,  MKCALENDAR)   \\\n\nenum http_method\n  {\n#define XX(num, name, string) HTTP_##name = num,\n  HTTP_METHOD_MAP(XX)\n#undef XX\n  };\n\n\nenum http_parser_type { HTTP_REQUEST, HTTP_RESPONSE, HTTP_BOTH };\n\n\n/* Flag values for http_parser.flags field */\nenum flags\n  { F_CHUNKED               = 1 << 0\n  , F_CONNECTION_KEEP_ALIVE = 1 << 1\n  , F_CONNECTION_CLOSE      = 1 << 2\n  , F_CONNECTION_UPGRADE    = 1 << 3\n  , F_TRAILING              = 1 << 4\n  , F_UPGRADE               = 1 << 5\n  , F_SKIPBODY              = 1 << 6\n  };\n\n\n/* Map for errno-related constants\n * \n * The provided argument should be a macro that takes 2 arguments.\n */\n#define HTTP_ERRNO_MAP(XX)                                           \\\n  /* No error */                                                     \\\n  XX(OK, \"success\")                                                  \\\n                                                                     \\\n  /* Callback-related errors */                                      \\\n  XX(CB_message_begin, \"the on_message_begin callback failed\")       \\\n  XX(CB_url, \"the on_url callback failed\")                           \\\n  XX(CB_header_field, \"the on_header_field callback failed\")         \\\n  XX(CB_header_value, \"the on_header_value callback failed\")         \\\n  XX(CB_headers_complete, \"the on_headers_complete callback failed\") \\\n  XX(CB_body, \"the on_body callback failed\")                         \\\n  XX(CB_message_complete, \"the on_message_complete callback failed\") \\\n  XX(CB_status, \"the on_status callback failed\")                     \\\n                                                                     \\\n  /* Parsing-related errors */                                       \\\n  XX(INVALID_EOF_STATE, \"stream ended at an unexpected time\")        \\\n  XX(HEADER_OVERFLOW,                                                \\\n     \"too many header bytes seen; overflow detected\")                \\\n  XX(CLOSED_CONNECTION,                                              \\\n     \"data received after completed connection: close message\")      \\\n  XX(INVALID_VERSION, \"invalid HTTP version\")                        \\\n  XX(INVALID_STATUS, \"invalid HTTP status code\")                     \\\n  XX(INVALID_METHOD, \"invalid HTTP method\")                          \\\n  XX(INVALID_URL, \"invalid URL\")                                     \\\n  XX(INVALID_HOST, \"invalid host\")                                   \\\n  XX(INVALID_PORT, \"invalid port\")                                   \\\n  XX(INVALID_PATH, \"invalid path\")                                   \\\n  XX(INVALID_QUERY_STRING, \"invalid query string\")                   \\\n  XX(INVALID_FRAGMENT, \"invalid fragment\")                           \\\n  XX(LF_EXPECTED, \"LF character expected\")                           \\\n  XX(INVALID_HEADER_TOKEN, \"invalid character in header\")            \\\n  XX(INVALID_CONTENT_LENGTH,                                         \\\n     \"invalid character in content-length header\")                   \\\n  XX(INVALID_CHUNK_SIZE,                                             \\\n     \"invalid character in chunk size header\")                       \\\n  XX(INVALID_CONSTANT, \"invalid constant string\")                    \\\n  XX(INVALID_INTERNAL_STATE, \"encountered unexpected internal state\")\\\n  XX(STRICT, \"strict mode assertion failed\")                         \\\n  XX(PAUSED, \"parser is paused\")                                     \\\n  XX(UNKNOWN, \"an unknown error occurred\")\n\n\n/* Define HPE_* values for each errno value above */\n#define HTTP_ERRNO_GEN(n, s) HPE_##n,\nenum http_errno {\n  HTTP_ERRNO_MAP(HTTP_ERRNO_GEN)\n};\n#undef HTTP_ERRNO_GEN\n\n\n/* Get an http_errno value from an http_parser */\n#define HTTP_PARSER_ERRNO(p)            ((enum http_errno) (p)->http_errno)\n\n\nstruct http_parser {\n  /** PRIVATE **/\n  unsigned int type : 2;         /* enum http_parser_type */\n  unsigned int flags : 7;        /* F_* values from 'flags' enum; semi-public */\n  unsigned int state : 8;        /* enum state from http_parser.c */\n  unsigned int header_state : 8; /* enum header_state from http_parser.c */\n  unsigned int index : 8;        /* index into current matcher */\n\n  uint32_t nread;          /* # bytes read in various scenarios */\n  uint64_t content_length; /* # bytes in body (0 if no Content-Length header) */\n\n  /** READ-ONLY **/\n  unsigned short http_major;\n  unsigned short http_minor;\n  unsigned int status_code : 16; /* responses only */\n  unsigned int method : 8;       /* requests only */\n  unsigned int http_errno : 7;\n\n  /* 1 = Upgrade header was present and the parser has exited because of that.\n   * 0 = No upgrade header present.\n   * Should be checked when http_parser_execute() returns in addition to\n   * error checking.\n   */\n  unsigned int upgrade : 1;\n\n  /** PUBLIC **/\n  void *data; /* A pointer to get hook to the \"connection\" or \"socket\" object */\n};\n\n\nstruct http_parser_settings {\n  http_cb      on_message_begin;\n  http_data_cb on_url;\n  http_data_cb on_status;\n  http_data_cb on_header_field;\n  http_data_cb on_header_value;\n  http_cb      on_headers_complete;\n  http_data_cb on_body;\n  http_cb      on_message_complete;\n};\n\n\nenum http_parser_url_fields\n  { UF_SCHEMA           = 0\n  , UF_HOST             = 1\n  , UF_PORT             = 2\n  , UF_PATH             = 3\n  , UF_QUERY            = 4\n  , UF_FRAGMENT         = 5\n  , UF_USERINFO         = 6\n  , UF_MAX              = 7\n  };\n\n\n/* Result structure for http_parser_parse_url().\n *\n * Callers should index into field_data[] with UF_* values iff field_set\n * has the relevant (1 << UF_*) bit set. As a courtesy to clients (and\n * because we probably have padding left over), we convert any port to\n * a uint16_t.\n */\nstruct http_parser_url {\n  uint16_t field_set;           /* Bitmask of (1 << UF_*) values */\n  uint16_t port;                /* Converted UF_PORT string */\n\n  struct {\n    uint16_t off;               /* Offset into buffer in which field starts */\n    uint16_t len;               /* Length of run in buffer */\n  } field_data[UF_MAX];\n};\n\n\n/* Returns the library version. Bits 16-23 contain the major version number,\n * bits 8-15 the minor version number and bits 0-7 the patch level.\n * Usage example:\n *\n *   unsigned long version = http_parser_version();\n *   unsigned major = (version >> 16) & 255;\n *   unsigned minor = (version >> 8) & 255;\n *   unsigned patch = version & 255;\n *   printf(\"http_parser v%u.%u.%u\\n\", major, minor, patch);\n */\nunsigned long http_parser_version(void);\n\nvoid http_parser_init(http_parser *parser, enum http_parser_type type);\n\n\n/* Initialize http_parser_settings members to 0\n */\nvoid http_parser_settings_init(http_parser_settings *settings);\n\n\n/* Executes the parser. Returns number of parsed bytes. Sets\n * `parser->http_errno` on error. */\nsize_t http_parser_execute(http_parser *parser,\n                           const http_parser_settings *settings,\n                           const char *data,\n                           size_t len);\n\n\n/* If http_should_keep_alive() in the on_headers_complete or\n * on_message_complete callback returns 0, then this should be\n * the last message on the connection.\n * If you are the server, respond with the \"Connection: close\" header.\n * If you are the client, close the connection.\n */\nint http_should_keep_alive(const http_parser *parser);\n\n/* Returns a string version of the HTTP method. */\nconst char *http_method_str(enum http_method m);\n\n/* Return a string name of the given error */\nconst char *http_errno_name(enum http_errno err);\n\n/* Return a string description of the given error */\nconst char *http_errno_description(enum http_errno err);\n\n/* Parse a URL; return nonzero on failure */\nint http_parser_parse_url(const char *buf, size_t buflen,\n                          int is_connect,\n                          struct http_parser_url *u);\n\n/* Pause or un-pause the parser; a nonzero value pauses */\nvoid http_parser_pause(http_parser *parser, int paused);\n\n/* Checks if this is the final chunk of the body. */\nint http_body_is_final(const http_parser *parser);\n\n#ifdef __cplusplus\n}\n#endif\n#endif\n"
  },
  {
    "path": "java/Makefile",
    "content": "JAVAC=javac\nJAR=jar\n\nJAVAFILES=\\\n\tcom/omniti/labs/FqClient.java \\\n\tcom/omniti/labs/FqClientImplDebug.java \\\n\tcom/omniti/labs/FqClientImplInterface.java \\\n\tcom/omniti/labs/FqClientImplNoop.java \\\n\tcom/omniti/labs/FqCommand.java \\\n\tcom/omniti/labs/FqCommandProtocolError.java \\\n\tcom/omniti/labs/FqDataProtocolError.java \\\n\tcom/omniti/labs/FqHeartbeatException.java \\\n\tcom/omniti/labs/FqMessage.java\n\nall:\tfqclient.jar\n\nfqclient.jar:\t$(JAVAFILES:%=src/%)\n\tmkdir -p classes\n\t(cd src && $(JAVAC) -d ../classes $(JAVAFILES))\n\t$(JAR) cf $@ -C classes .\n\nfq_rcvr.class:\tfq_rcvr.java fqclient.jar\n\t$(JAVAC) -cp fqclient.jar fq_rcvr.java\n\nclean:\n\trm -f fqclient.jar\n\trm -f *.class\n\trm -rf classes\n"
  },
  {
    "path": "java/fq_rcvr.java",
    "content": "import com.circonus.FqClient;\nimport com.circonus.FqClientImplDebug;\nimport com.circonus.FqClientImplNoop;\nimport com.circonus.FqClientImplInterface;\nimport com.circonus.FqCommand;\nimport com.circonus.FqMessage;\n\npublic class fq_rcvr {\n  private static class FqTest extends FqClientImplDebug {\n\t\tprivate FqClient client;\n\t\tprivate long count = 0;\n\t\tprivate long incr = 0;\n\t\tprivate long s = 0;\n\t\tpublic void setClient(FqClient c) { client = c; }\n\t\tpublic void dispatch(FqMessage m) {\n\t\t\tcount++; incr++;\n\t\t\tclient.send(m);\n\t\t}\n\t\tpublic void dispatchAuth(FqCommand.Auth a) {\n\t\t\tif(a.success()) {\n\t\t\t\tclient.setHeartbeat(500);\n\t\t\t\tFqCommand.BindRequest breq = new FqCommand.BindRequest(\n\t\t\t\t  \"maryland\", \"prefix:\\\"test.prefix.\\\" sample(1)\", false\n\t\t\t\t);\n\t\t\t\tclient.send(breq);\n\t\t\t}\n\t\t}\n\t\tpublic void showRate() {\n\t\t\tlong now = System.nanoTime();\n\t\t\tif(s != 0) {\n\t\t\t\tSystem.err.println(count + \"  [\" + 1000000000.0 * (double)incr/((double)now-s) + \" m/s]\");\n\t\t\t\tincr = 0;\n\t\t\t}\n\t\t\ts = now;\n\t\t}\n\t}\n\tpublic static void main(String args[]) {\n    if(args.length != 4) {\n      System.err.println(\": <host> <port> <user> <pass>\");\n      System.exit(-1);\n    }\n\t\tSystem.err.println(args[0]);\n\t\tFqClient client = null;\n    FqTest impl = new FqTest();\n\t\ttry {\n\t\t  client = new FqClient(impl);\n\t\t\tclient.creds(args[0], new Integer(args[1]), args[2], args[3]);\n\t\t\tclient.connect();\n\t\t} catch(Exception e) {\n\t\t\tthrow new RuntimeException(e);\n\t\t}\n\n\t\twhile(true) {\n\t\t\tclient.send(new FqCommand.StatusRequest());\n\t\t\ttry { Thread.sleep(1000); } catch(InterruptedException ignore) { }\n\t\t}\n\n\t\t//if(client != null) client.shutdown();\n\t}\n}\n"
  },
  {
    "path": "java/pom.xml",
    "content": "<project>\n  <modelVersion>4.0.0</modelVersion>\n  <groupId>com.circonus</groupId>\n  <artifactId>fqclient</artifactId>\n  <version>0.0.1</version>\n  <packaging>jar</packaging>\n \n  <name>Fq Java Client</name>\n  <description>Fq Client for Java</description>\n  <url>https://github.com/circonus-labs/fq</url>\n \n  <licenses>\n    <license>\n      <name>BSD</name>\n      <url>https://github.com/circonus-labs/fq/blob/master/LICENSE</url>\n      <distribution>repo</distribution>\n    </license>\n  </licenses>\n \n  <scm>\n    <url>https://github.com/circonus-labs/fq</url>\n  </scm>\n    <developers>\n        <developer>\n            <id>postwait</id>\n            <name>Theo Schlossnagle</name>\n            <email>theo.schlossnagle (at) circonus.com</email>\n        </developer>\n    </developers>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-compiler-plugin</artifactId>\n                <version>2.3.1</version>\n                <configuration>\n                    <source>1.5</source>\n                    <target>1.5</target>\n                </configuration>\n            </plugin>\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-deploy-plugin</artifactId>\n                <version>2.7</version>\n            </plugin>\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-source-plugin</artifactId>\n                <version>2.3</version>\n                <executions>\n                    <execution>\n                        <id>attach-sources</id>\n                        <goals>\n                            <goal>jar-no-fork</goal>\n                        </goals>\n                    </execution>\n                </executions>\n            </plugin>\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-javadoc-plugin</artifactId>\n                <version>2.10.1</version>\n                <executions>\n                    <execution>\n                        <id>attach-javadocs</id>\n                        <goals>\n                            <goal>jar</goal>\n                        </goals>\n                        <configuration>\n                            <additionalparam>-Xdoclint:none</additionalparam>\n                        </configuration>\n                    </execution>\n                </executions>\n            </plugin>\n            <plugin>\n                <groupId>org.sonatype.plugins</groupId>\n                <artifactId>nexus-staging-maven-plugin</artifactId>\n                <version>1.6.3</version>\n                <extensions>true</extensions>\n                <configuration>\n                    <serverId>ossrh</serverId>\n                    <nexusUrl>https://oss.sonatype.org/</nexusUrl>\n                    <autoReleaseAfterClose>false</autoReleaseAfterClose>\n                </configuration>\n            </plugin>\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-gpg-plugin</artifactId>\n                <version>1.5</version>\n                <executions>\n                    <execution>\n                        <id>sign-artifacts</id>\n                        <phase>verify</phase>\n                        <goals>\n                            <goal>sign</goal>\n                        </goals>\n                    </execution>\n                </executions>\n            </plugin>\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-release-plugin</artifactId>\n                <version>2.5</version>\n                <configuration>\n                    <autoVersionSubmodules>true</autoVersionSubmodules>\n                    <useReleaseProfile>false</useReleaseProfile>\n                    <releaseProfiles>release</releaseProfiles>\n                    <goals>deploy</goals>\n                </configuration>\n            </plugin>\n        </plugins>\n    </build>\n</project>\n"
  },
  {
    "path": "java/src/com/omniti/labs/FqClient.java",
    "content": "/*\n * Copyright (c) 2013 OmniTI Computer Consulting, Inc.\n * All rights reserved.\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\npackage com.omniti.labs;\n\nimport java.io.IOException;\nimport java.net.InetSocketAddress;\nimport java.net.SocketAddress;\nimport java.net.Socket;\nimport java.net.SocketException;\nimport java.nio.ByteBuffer;\nimport java.nio.ByteOrder;\nimport java.nio.charset.StandardCharsets;\nimport java.nio.channels.Selector;\nimport java.nio.channels.spi.AbstractSelector;\nimport java.nio.channels.SelectionKey;\nimport java.nio.channels.SocketChannel;\nimport java.nio.channels.spi.SelectorProvider;\nimport java.util.concurrent.atomic.AtomicInteger;\nimport java.util.concurrent.ConcurrentLinkedQueue;\nimport java.util.concurrent.LinkedBlockingQueue;\nimport java.util.ArrayDeque;\nimport java.util.UUID;\nimport com.omniti.labs.FqClientImplInterface;\nimport com.omniti.labs.FqCommand;\nimport com.omniti.labs.FqMessage;\n\npublic class FqClient {\n  public final static int FQ_PROTO_CMD_MODE = 0xcc50cafe;\n  public final static int FQ_PROTO_DATA_MODE = 0xcc50face;\n  public final static int FQ_PROTO_PEER_MODE = 0xcc50fade;\n\n  private int mode;\n  private String host;\n  private SocketAddress hostaddr;\n  private int port;\n  private String user;\n  private String queue;\n  private String queue_type;\n  private String pass;\n/*\n  private int q_stall_time;\n  private int qmaxlen;\n*/\n  private final FqMessage endpost = new FqMessage();\n\n  private volatile boolean stop = false;\n  private volatile boolean shutting_down = false;\n  private boolean data_ready = false;\n  private short cmd_hb_ms = 0;\n  private short last_cmd_hb_ms = 0;\n  private long cmd_hb_last;\n  private long cmd_hb_last_sent = 0;\n  private FqClientImplInterface impl;\n  private AbstractSelector cmd_selector;\n  private SocketChannel cmd_socket;\n  private ByteBuffer cmd_in_buff;\n  private ByteBuffer data_in_buff;\n  private AbstractSelector data_selector;\n  private SelectionKey data_skey;\n  private SocketChannel data_socket;\n  private Object keylock = new Object();\n  private boolean connected = false;\n  private byte client_key[];\n  private Thread worker;\n  private Thread data_worker;\n  private Thread back_worker;\n  private Thread sender_worker;\n  private final Object sender_worker_lock = new Object();\n  private AtomicInteger qlen;\n  private ConcurrentLinkedQueue<FqCommand> cmdq;\n  private LinkedBlockingQueue<FqMessage> q;\n  private LinkedBlockingQueue<FqMessage> backq;\n  private FqCommand.Heartbeat reusable_hb;\n\n  private void initialize(FqClientImplInterface _impl, int _mode, int bsize)\n    throws FqClientImplInterface.InUseException {\n    impl = _impl;\n    impl.setClient(this);\n    mode = _mode;\n    qlen = new AtomicInteger(0);\n    cmdq = new ConcurrentLinkedQueue<FqCommand>();\n    q = new LinkedBlockingQueue<FqMessage>();\n    backq = new LinkedBlockingQueue<FqMessage>();\n    reusable_hb = new FqCommand.Heartbeat();\n    cmd_in_buff = ByteBuffer.allocate(65536);\n    data_in_buff = ByteBuffer.allocate(bsize);\n    data_in_buff.mark();\n  }\n  public FqClient(FqClientImplInterface _impl)\n    throws FqClientImplInterface.InUseException {\n    initialize(_impl, FQ_PROTO_DATA_MODE, 4194304);\n  }\n  public FqClient(FqClientImplInterface _impl, int _mode)\n    throws FqClientImplInterface.InUseException {\n    initialize(_impl, _mode, 4194304);\n  }\n  public FqClient(FqClientImplInterface _impl, int _mode, int _bsize)\n    throws FqClientImplInterface.InUseException {\n    initialize(_impl, _mode, _bsize);\n  }\n  public boolean isPeermode() { return (mode == FQ_PROTO_PEER_MODE); }\n  public FqClientImplInterface getImpl() { return impl; }\n  public void setHeartbeat(short ms) {\n    if(ms != cmd_hb_ms) {\n      cmd_hb_ms = ms;\n      send(new FqCommand.HeartbeatRequest(cmd_hb_ms));\n    }\n  }\n  public void setHeartbeat(int ms) {\n    setHeartbeat((short)ms);\n  }\n  public void set_backlog(int len, int stall) {\n/*\n    qmaxlen = len;\n    q_stall_time = stall;\n*/\n  }\n  public void send(FqCommand cmd) {\n    cmdq.offer(cmd);\n  }\n  public void send(FqMessage m) {\n    q.offer(m);\n  }\n  public void creds(int _port, String _source, String _pass)\n      throws java.net.UnknownHostException {\n    creds(null, _port, _source, _pass);\n  }\n  public void creds(String _host, String _source, String _pass)\n      throws java.net.UnknownHostException {\n    creds(_host, 0, _source, _pass);\n  }\n  public void creds(String _host, int _port,\n      String _source, String _pass)\n      throws java.net.UnknownHostException {\n    int cidx;\n    if(_host != null) host = _host;\n    if(host == null) host = \"127.0.0.1\";\n    if(_port != 0) port = _port % 0xffff;\n    if(port == 0) port = 8765;\n    user = _source;\n    if((cidx = user.indexOf(\"/\")) >= 0) {\n      queue = user.substring(cidx + 1);\n      user = user.substring(0, cidx);\n      if((cidx = queue.indexOf(\"/\")) >= 0) {\n        queue_type = queue.substring(cidx + 1);\n        queue = queue.substring(0, cidx);\n      }\n    }\n\n    if(queue == null || queue.length() == 0)\n      queue = UUID.randomUUID().toString();\n    if(queue_type == null || queue_type.length() == 0)\n      queue_type = \"mem\";\n    pass = _pass;\n    hostaddr = new InetSocketAddress(host, port);\n  }\n  private boolean client_do_auth() throws IOException, FqCommandProtocolError {\n    FqCommand.PlainAuth auth =\n      new FqCommand.PlainAuth(user,pass,queue,queue_type);\n    auth.send(this);\n    auth.process(this);\n    client_key = auth.getKey();\n    return (client_key != null);\n  }\n  public byte[] cmd_read_short_bytearray() throws IOException {\n    cmd_in_buff.clear();\n    cmd_in_buff.limit(2);\n    if(cmd_socket.read(cmd_in_buff) == -1) return null;\n    cmd_in_buff.flip();\n    Short strlen = cmd_in_buff.getShort();\n    cmd_in_buff.clear();\n    cmd_in_buff.limit(strlen);\n    if(cmd_socket.read(cmd_in_buff) == -1) return null;\n    byte a[] = new byte[strlen];\n    cmd_in_buff.flip();\n    cmd_in_buff.get(a);\n    return a;\n  }\n  public String cmd_read_short_string() throws IOException {\n    byte a[] = cmd_read_short_bytearray();\n    if(a == null) return null;\n    return new String(a, StandardCharsets.UTF_8);\n  }\n  public ByteBuffer cmd_read(int len) throws IOException {\n    if(len > cmd_in_buff.capacity()) {\n      ByteBuffer bb = ByteBuffer.allocate(len);\n      if(cmd_socket.read(bb) == -1) return null;\n      return bb;\n    }\n    cmd_in_buff.clear();\n    cmd_in_buff.limit(len);\n    if(cmd_socket.read(cmd_in_buff) == -1) return null;\n    return cmd_in_buff;\n  }\n  public long data_write(ByteBuffer bb) throws IOException {\n    return data_socket.write(bb);\n  }\n  public long data_write(ByteBuffer[] bb) throws IOException {\n    return data_socket.write(bb);\n  }\n  public int cmd_write(ByteBuffer bb) throws IOException {\n    return cmd_socket.write(bb);\n  }\n  private boolean client_data_connect_internal() {\n    boolean success = false;\n    try {\n      data_selector = (AbstractSelector)Selector.open();\n      data_socket = data_selector.provider().openSocketChannel();\n      data_socket.connect(hostaddr);\n      data_socket.socket().setTcpNoDelay(true);\n      ByteBuffer bb = ByteBuffer.allocate(4 + 2 + client_key.length);\n      bb.order(ByteOrder.BIG_ENDIAN);\n      bb.putInt(mode);\n      bb.putShort((short)client_key.length);\n      bb.put(client_key);\n      bb.flip();\n      data_write(bb);\n      data_socket.configureBlocking(false);\n      data_skey = data_socket.register(data_selector, SelectionKey.OP_READ);\n    } catch(Exception e) {\n      impl.connectError(e);\n      return success;\n    }\n    return success;\n  }\n  private void reset() {\n    try { cmd_socket.close(); } catch (Exception ce) { ce.printStackTrace(); }\n    try { data_socket.close(); } catch (Exception ce) { ce.printStackTrace(); }\n    data_ready = false;\n    cmd_hb_last_sent = 0;\n    cmd_hb_last = 0;\n  }\n  private boolean client_connect_internal() {\n    boolean success = false;\n    // Force close\n    reset();\n    try {\n      cmd_selector = (AbstractSelector)Selector.open();\n      cmd_socket = cmd_selector.provider().openSocketChannel();\n      cmd_socket.connect(hostaddr);\n      cmd_socket.socket().setTcpNoDelay(true);\n      cmd_socket.socket().setSoTimeout(5000);\n      setHeartbeat((cmd_hb_ms != 0) ? cmd_hb_ms : (short)10000);\n      ByteBuffer bb = ByteBuffer.allocate(4);\n      bb.order(ByteOrder.BIG_ENDIAN);\n      bb.putInt(FQ_PROTO_CMD_MODE);\n      bb.flip();\n      cmd_write(bb);\n      success = client_do_auth();\n    } catch(Exception e) {\n      impl.connectError(e);\n      return success;\n    }\n    return success;\n  }\n  public void connect() {\n    if(connected) return;\n    worker = new Thread() {\n        public void run() { worker(); }\n     };\n    worker.setName(\"Fq-cmd(\" + host + \")\");\n    worker.start();\n    data_worker = new Thread() {\n        public void run() { data_worker(); }\n     };\n    data_worker.setName(\"Fq-data-in(\" + host + \")\");\n    data_worker.start();\n    back_worker = new Thread() {\n        public void run() { back_worker(); }\n     };\n    back_worker.setName(\"Fq-back(\" + host + \")\");\n    back_worker.start();\n  }\n  public void recvHeartbeat() {\n    cmd_hb_last = System.nanoTime();\n  }\n  private void sendHeartbeat() throws IOException, FqHeartbeatException {\n    long t = System.nanoTime();\n    if((t - cmd_hb_last_sent) > ((long)cmd_hb_ms * 1000000)) {\n      reusable_hb.send(this);\n      cmd_hb_last_sent = t;\n    }\n    if(cmd_hb_ms != last_cmd_hb_ms) {\n      cmd_socket.socket().setSoTimeout(cmd_hb_ms * 2);\n      last_cmd_hb_ms = cmd_hb_ms;\n    }\n    long hb_ns = (long)cmd_hb_ms * (long)3 * (long)1000000;\n    if(cmd_hb_last != 0 && hb_ns != 0 &\n      cmd_hb_last < (t - hb_ns)) {\n      throw new FqHeartbeatException();\n    }\n  }\n  public int data_backlog() { return qlen.get(); }\n  private void worker() {\n    final ArrayDeque<FqCommand> responses = new ArrayDeque<>();\n    int backoff = 0;\n    while(!stop) {\n      responses.clear();\n      try {\n        if(client_connect_internal()) {\n          data_ready = true;\n          backoff = 0;\n        }\n        while(!stop && data_ready) {\n          FqCommand entry;\n          while(null != (entry = cmdq.poll())) {\n            entry.send(this);\n            if(entry.hasInBandResponse()) {\n              responses.addLast(entry);\n            }\n          }\n\n          sendHeartbeat();\n\n          entry = responses.pollFirst();\n          if(entry == null) entry = reusable_hb;\n          entry.process(this);\n        }\n      } catch(Exception e) {\n        impl.commandError(e);\n      }\n      try {\n        Thread.sleep(backoff / 1000, (backoff % 1000) * 1000);\n      } catch(InterruptedException ignore) {}\n      backoff += 10000;\n    }\n  }\n  private void data_worker_sender() {\n    FqMessage m;\n    while(!stop && cmd_socket.socket().isConnected()) {\n      m = null;\n      try { m = q.take(); } catch(InterruptedException ignore) { }\n      if(m == endpost) break;\n      if(m == null) continue;\n      try {\n        while(!m.send(this) && !stop && cmd_socket.socket().isConnected()) {\n          synchronized(keylock) {\n            data_skey.interestOps(SelectionKey.OP_READ|SelectionKey.OP_WRITE);\n            try { keylock.wait(); } catch(InterruptedException ignore) { }\n          }\n        }\n      } catch(IOException e) {\n        impl.dataError(e);\n        return;\n      } catch(FqDataProtocolError e) {\n        impl.dataError(e);\n        return;\n      }\n    }\n  }\n  private void waitForData(long ms) throws IOException {\n    data_selector.select(ms);\n    if(data_skey.isWritable()) {\n      synchronized(keylock) {\n        data_skey.interestOps(SelectionKey.OP_READ);\n        keylock.notify();\n      }\n    }\n  }\n  public int blockingRead(byte dst[], int offset, int len) throws IOException {\n    ByteBuffer rest = ByteBuffer.wrap(dst, offset, len);\n    int nread = 0;\n    // A quick non-blocking attempt\n    if((nread = data_socket.read(rest)) == len) {\n      return len; // done.\n    }\n    if (nread < 0) throw new IOException();\n\n    while(rest.position() < (offset+len)) {\n      int readlen;\n      waitForData(1000);\n      while((readlen = data_socket.read(rest)) > 0) {\n        nread += readlen;\n      }\n      if(readlen < 0) throw new IOException();\n    }\n    return nread;\n  }\n  public ByteBuffer fill_data_buffer(boolean force) throws IOException {\n    if(force || data_in_buff.position() == 0) {\n      waitForData(1000);\n      int rsize = data_socket.read(data_in_buff);\n      if(rsize < 0) throw new IOException(\"bad read\");\n    }\n    return data_in_buff;\n  }\n\n  private void data_worker_receiver() {\n    while(!stop && cmd_socket.socket().isConnected()) {\n      FqMessage m;\n      m = new FqMessage();\n      try { while(!stop && !m.read(this)) fill_data_buffer(true); }\n      catch (IOException e) { impl.dataError(e); reset(); return; }\n      catch (FqDataProtocolError e) { impl.dataError(e); reset(); return; }\n      if(m.isComplete()) {\n        do {\n          try {\n            backq.put(m);\n            m = null;\n          }\n          catch(InterruptedException ignore) { }\n        } while(m != null);\n      }\n    }\n  }\n  private void data_worker() {\n    int backoff = 0;\n    Error boom = null;\n    while(!stop) {\n      if(data_ready) {\n        try {\n          if(client_data_connect_internal()) {\n            backoff = 0;\n          }\n\n          sender_worker = new Thread() {\n            public void run() { data_worker_sender(); }\n           };\n          sender_worker.setName(\"Fq-data-out(\" + host + \")\");\n          sender_worker.start();\n          try {\n            data_worker_receiver();\n          } catch (Error e) {\n            impl.dataError(e);\n            q.offer(endpost);\n            reset();\n            boom = e;\n          }\n          try { sender_worker.interrupt(); } catch (Exception ignore) { ignore.printStackTrace(); }\n          synchronized(sender_worker_lock) {\n            sender_worker.join();\n          }\n  \n        } catch(Exception e) {\n          impl.dataError(e);\n        }\n      }\n      if(backoff < 1000000) backoff += 10000;\n      try {\n        Thread.sleep(backoff / 1000, (backoff % 1000) * 1000);\n      } catch(InterruptedException ignore) {}\n    }\n    shutting_down = true;\n    backq.offer(endpost);\n    if(boom != null) throw(boom);\n  }\n  private void back_worker() {\n    while(!shutting_down) {\n      try {\n        FqMessage m = backq.take();\n        if(m == endpost) break;\n        impl.dispatch(m);\n      } catch(InterruptedException ignore) { }\n    }\n  }\n  public void shutdown() {\n    stop = true;\n    q.offer(endpost);\n    try {\n      synchronized(sender_worker_lock) {\n        sender_worker.join();\n      }\n      data_worker.join();\n      worker.join();\n    } catch(InterruptedException ignore) { }\n  }\n}\n"
  },
  {
    "path": "java/src/com/omniti/labs/FqClientImplDebug.java",
    "content": "/*\n * Copyright (c) 2013 OmniTI Computer Consulting, Inc.\n * All rights reserved.\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\npackage com.omniti.labs;\n\nimport java.util.Date;\nimport java.util.Map;\n\npublic class FqClientImplDebug implements FqClientImplInterface {\n  protected FqClient client = null;\n  public void setClient(FqClient c) throws InUseException {\n    if(client != null) throw new InUseException();\n    client = c;\n  }\n  protected void genericError(Throwable e) {\n    e.printStackTrace();\n  }\n  public void connectError(Throwable e) { genericError(e); } \n  public void commandError(Throwable e) { genericError(e); }\n  public void dataError(Throwable e) { genericError(e); }\n  public void dispatch(FqMessage m) {\n    byte b[] = m.getPayload();\n    int len = (b == null) ? 0 : b.length;\n    System.err.println(\"m[\" + len + \"] via \" + m.getRoute() +\n      \" over \" + m.getExchange() + \" from \" + m.getSender());\n  }\n  public void dispatch(FqCommand cmd) {\n    System.err.println(cmd);\n  }\n  public void dispatchAuth(FqCommand.Auth cmd) {\n    dispatch(cmd);\n  }\n  public void dispatchHeartbeatRequest(FqCommand.HeartbeatRequest cmd) {\n    dispatch(cmd);\n  }\n  public void dispatchHeartbeat(FqCommand.Heartbeat cmd) { dispatch(cmd); }\n  public void dispatchBindRequest(FqCommand.BindRequest cmd) {\n    System.err.println(cmd.toString() + cmd.getBinding());\n  }\n  public void dispatchUnbindRequest(FqCommand.UnbindRequest cmd) {\n    System.err.println(cmd.toString() + cmd.getBinding() + \" \" + cmd.getSuccess());\n  }\n  public void dispatchStatusRequest(FqCommand.StatusRequest cmd) {\n    Date d = cmd.getDate();\n    Map<String,Long> m = cmd.getMap();\n    System.err.println(\"Status: \" + d);\n    for(Map.Entry<String, Long> entry : m.entrySet()) {\n      System.err.println(\"    \" + entry.getKey() + \" : \" + entry.getValue());\n    }\n  }\n}\n"
  },
  {
    "path": "java/src/com/omniti/labs/FqClientImplInterface.java",
    "content": "/*\n * Copyright (c) 2013 OmniTI Computer Consulting, Inc.\n * All rights reserved.\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\npackage com.omniti.labs;\n\nimport com.omniti.labs.FqCommand;\n\npublic interface FqClientImplInterface {\n  public class InUseException extends Exception { }\n  public void setClient(FqClient c) throws InUseException;\n  public void connectError(Throwable e);\n  public void commandError(Throwable e);\n  public void dataError(Throwable e);\n  public void dispatch(FqMessage m);\n  public void dispatch(FqCommand cmd);\n  public void dispatchAuth(FqCommand.Auth cmd);\n  public void dispatchHeartbeat(FqCommand.Heartbeat cmd);\n  public void dispatchHeartbeatRequest(FqCommand.HeartbeatRequest cmd);\n  public void dispatchBindRequest(FqCommand.BindRequest cmd);\n  public void dispatchUnbindRequest(FqCommand.UnbindRequest cmd);\n  public void dispatchStatusRequest(FqCommand.StatusRequest cmd);\n}\n"
  },
  {
    "path": "java/src/com/omniti/labs/FqClientImplNoop.java",
    "content": "/*\n * Copyright (c) 2013 OmniTI Computer Consulting, Inc.\n * All rights reserved.\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\npackage com.omniti.labs;\n\npublic class FqClientImplNoop implements FqClientImplInterface {\n  protected FqClient client;\n  public void setClient(FqClient c) throws InUseException {\n    if(client != null) throw new InUseException();\n    client = c;\n  }\n  protected void genericError(Throwable e) { }\n  public void connectError(Throwable e) { genericError(e); } \n  public void commandError(Throwable e) { genericError(e); }\n  public void dataError(Throwable e) { genericError(e); }\n  public void dispatch(FqMessage m) { }\n  public void dispatch(FqCommand cmd) { }\n  public void dispatchAuth(FqCommand.Auth cmd) { dispatch(cmd); }\n  public void dispatchHeartbeatRequest(FqCommand.HeartbeatRequest cmd) {\n    dispatch(cmd);\n  }\n  public void dispatchHeartbeat(FqCommand.Heartbeat cmd) { dispatch(cmd); }\n  public void dispatchBindRequest(FqCommand.BindRequest cmd) { dispatch(cmd); };\n  public void dispatchUnbindRequest(FqCommand.UnbindRequest cmd) { dispatch(cmd); };\n  public void dispatchStatusRequest(FqCommand.StatusRequest cmd) { dispatch(cmd); };\n}\n"
  },
  {
    "path": "java/src/com/omniti/labs/FqCommand.java",
    "content": "/*\n * Copyright (c) 2013 OmniTI Computer Consulting, Inc.\n * All rights reserved.\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\npackage com.omniti.labs;\n\nimport java.io.IOException;\nimport java.net.Socket;\nimport java.nio.ByteBuffer;\nimport java.nio.ByteOrder;\nimport java.nio.charset.StandardCharsets;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.Map;\nimport com.omniti.labs.FqClient;\n\npublic abstract class FqCommand {\n  public final static short FQ_PROTO_ERROR = (short)0xeeee;\n  public final static short FQ_PROTO_HB = (short)0xbea7;\n  public final static short FQ_PROTO_AUTH_CMD = (short)0xaaaa;\n  public final static short FQ_PROTO_AUTH_PLAIN = (short)0;\n  public final static short FQ_PROTO_AUTH_RESP = (short)0xaa00;\n  public final static short FQ_PROTO_HBREQ = (short)0x4848;\n  public final static short FQ_PROTO_BIND = (short)0xb171;\n  public final static short FQ_PROTO_BINDREQ = (short)0xb170;\n  public final static short FQ_PROTO_UNBIND = (short)0x171b;\n  public final static short FQ_PROTO_UNBINDREQ = (short)0x071b;\n  public final static short FQ_PROTO_STATUS = (short)0x57a7;\n  public final static short FQ_PROTO_STATUSREQ = (short)0xc7a7;\n\n  protected ByteBuffer bb;\n  private boolean composed = false;\n  public abstract short cmd();\n  public short response_cmd() { return cmd(); }\n  public void compose() { }\n  public void send(FqClient c) throws IOException {\n    if(composed) {\n      bb.position(0);\n    } else {\n      bb.putShort(cmd());\n      compose();\n      bb.flip();\n      composed = true;\n    }\n    c.cmd_write(bb);\n  }\n  public abstract boolean hasInBandResponse();\n  private static Heartbeat hb = new Heartbeat();\n\n  public Short getShortCmd(FqClient c)\n      throws IOException, FqCommandProtocolError {\n    Short cmd;\n    do {\n      ByteBuffer bb = c.cmd_read(2);\n      if(bb == null) return null;\n      bb.flip();\n      cmd = bb.getShort();\n      if(cmd == FQ_PROTO_HB) { c.recvHeartbeat(); }\n      if(cmd == FQ_PROTO_ERROR) {\n        throw new FqCommandProtocolError(c.cmd_read_short_string());\n      }\n    } while(cmd() != FQ_PROTO_HB && cmd == FQ_PROTO_HB);\n    return cmd;\n  }\n  public void process(FqClient c) throws IOException, FqCommandProtocolError {\n    Short cmd = getShortCmd(c);\n    // the hearbeat happens magically in getShortCmd\n    if(cmd == null) {\n      throw new FqCommandProtocolError(\"null cmd\");\n    }\n    else if(cmd != response_cmd()) {\n      throw new FqCommandProtocolError(response_cmd(), cmd);\n    }\n  }\n\n  protected void alloc(int size) {\n    bb = ByteBuffer.allocate(size);\n    bb.order(ByteOrder.BIG_ENDIAN);\n  }\n  public FqCommand(int size) {\n    alloc(size + 2);\n  }\n  public FqCommand() {\n  }\n\n  public static class Heartbeat extends FqCommand {\n    public Heartbeat() { super(0); }\n    public boolean hasInBandResponse() { return false; }\n    public short cmd() { return FQ_PROTO_HB; }\n  }\n  public static class HeartbeatRequest extends FqCommand {\n    short ms;\n    public HeartbeatRequest(int _ms) {\n      super(2);\n      ms = (short)(_ms & 0xffff);\n    }\n    public boolean hasInBandResponse() { return false; }\n    public short cmd() { return FQ_PROTO_HBREQ; }\n    public void compose() { bb.putShort(ms); }\n  }\n  public static abstract class Auth extends FqCommand {\n    protected byte[] key = null;\n    public boolean success() { return (key != null); }\n    public byte[] getKey() { return key; }\n  }  \n  public static class PlainAuth extends Auth {\n    private byte b_user[];\n    private byte b_pass[];\n    private byte b_queue[];\n    private byte b_queue_type[];\n  \n    public PlainAuth(String user, String pass,\n      String queue, String queue_type) {\n      b_user = user.getBytes(StandardCharsets.UTF_8);\n      b_queue = queue.getBytes(StandardCharsets.UTF_8);\n      b_queue_type = queue_type.getBytes(StandardCharsets.UTF_8);\n      b_pass = pass.getBytes(StandardCharsets.UTF_8);\n      int extra_space = \n        2 + /* plain */\n        2 + b_user.length + /* user */\n        2 + b_queue.length + 1 + b_queue_type.length + /* queue */\n        2 + b_pass.length;\n      alloc(2+extra_space);\n    }\n    public short cmd() { return FQ_PROTO_AUTH_CMD; }\n    public boolean hasInBandResponse() { return true; }\n    public void compose() {\n      bb.putShort(FQ_PROTO_AUTH_PLAIN);\n      bb.putShort((short)b_user.length);\n      bb.put(b_user);\n      bb.putShort((short)(b_queue.length + 1 + b_queue_type.length));\n      bb.put(b_queue);\n      bb.put((byte) 0);\n      bb.put(b_queue_type);\n      bb.putShort((short)b_pass.length);\n      bb.put(b_pass);\n    }\n    public void process(FqClient c) throws IOException, FqCommandProtocolError {\n      Short cmd, len;\n      bb = c.cmd_read(2);\n      if(bb == null) return;\n      bb.flip();\n      cmd = bb.getShort();\n      switch(cmd) {\n        case FQ_PROTO_AUTH_RESP:\n          key = c.cmd_read_short_bytearray();\n          if(key == null || key.length > 127)\n            throw new FqCommandProtocolError(\"bad key\");\n          break;\n        case FQ_PROTO_ERROR:\n          String error = c.cmd_read_short_string();\n          if(error != null) throw new FqCommandProtocolError(error);\n          /* fall through */\n        default:\n          throw new FqCommandProtocolError(cmd);\n      }\n      c.getImpl().dispatchAuth(this);\n    }\n  }\n  public static class BindRequest extends FqCommand {\n    public static final short FQ_BIND_PEER = 0x0001;\n    public static final short FQ_BIND_PERM = 0x0110;\n    public static final short FQ_BIND_TRANS = 0x0100;\n    private Integer binding;\n    private byte exchange[];\n    private byte program[];\n    private short flags;\n    \n    public BindRequest(byte _exchange[], String _program, short _flags) {\n      program = _program.getBytes(StandardCharsets.UTF_8);\n      exchange = _exchange;\n      flags = _flags;\n      int extra_space = \n        2 + /* flags */\n        2 + exchange.length + /* user */\n        2 + program.length;\n      alloc(2+extra_space);\n    }\n    public BindRequest(byte _exchange[], String _program, boolean _peermode) {\n      this(_exchange, _program, _peermode ? FQ_BIND_PEER : 0);\n    }\n    public BindRequest(String exchange, String p, boolean m) {\n      this(exchange.getBytes(StandardCharsets.UTF_8), p, m);\n    }\n    public short cmd() { return FQ_PROTO_BINDREQ; }\n    public short response_cmd() { return FQ_PROTO_BIND; }\n    public boolean hasInBandResponse() { return true; }\n    public void compose() {\n      bb.putShort(flags);\n      bb.putShort((short)exchange.length);\n      bb.put(exchange);\n      bb.putShort((short)program.length);\n      bb.put(program);\n    }\n    public void process(FqClient c) throws IOException, FqCommandProtocolError {\n      super.process(c);\n      Integer cmd;\n      bb = c.cmd_read(4);\n      if(bb == null) return;\n      bb.flip();\n      binding = bb.getInt();\n      c.getImpl().dispatchBindRequest(this);\n    }\n    public Integer getBinding() { return binding; }\n    public byte[] getExchange() { return exchange; }\n  }\n  public static class UnbindRequest extends FqCommand {\n    private BindRequest bind;\n    private Integer success;\n    \n    public UnbindRequest(BindRequest b) {\n      bind = b;\n      int extra_space = \n        2 + /* peermode */\n        4 + /* route_id */\n        2 + bind.getExchange().length;\n      alloc(2+extra_space);\n    }\n    public short cmd() { return FQ_PROTO_UNBINDREQ; }\n    public short response_cmd() { return FQ_PROTO_UNBIND; }\n    public boolean hasInBandResponse() { return true; }\n    public void compose() {\n      bb.putInt(bind.getBinding());\n      bb.putShort((short)bind.getExchange().length);\n      bb.put(bind.getExchange());\n    }\n    public void process(FqClient c) throws IOException, FqCommandProtocolError {\n      super.process(c);\n      Integer cmd;\n      bb = c.cmd_read(4);\n      if(bb == null) return;\n      bb.flip();\n      success = bb.getInt();\n      c.getImpl().dispatchUnbindRequest(this);\n    }\n    public Integer getBinding() { return bind.getBinding(); }\n    public boolean getSuccess() {\n      return (success != null && success.equals(bind.getBinding()));\n    }\n  }\n\n  public static class StatusRequest extends FqCommand {\n    protected Date last_update;\n    protected HashMap<String,Long> status = new HashMap<String,Long>();\n    public StatusRequest() { super(0); }\n    public short cmd() { return FQ_PROTO_STATUSREQ; }\n    public short response_cmd() { return FQ_PROTO_STATUS; }\n    public boolean hasInBandResponse() { return true; }\n    public void process(FqClient c) throws IOException, FqCommandProtocolError {\n      super.process(c);\n      last_update = new Date();\n      while(true) {\n        String key = c.cmd_read_short_string();\n        if(key == null || key.length() == 0) break;\n        Integer ivalue;\n        bb = c.cmd_read(4);\n        if(bb == null) throw new FqCommandProtocolError(\"status read failure\");\n        bb.flip();\n        ivalue = bb.getInt();\n        Long value = ivalue & (long)0xffffffff;\n        status.put(key,value);\n      }\n      c.getImpl().dispatchStatusRequest(this);\n    }\n    public Date getDate() { return last_update; }\n    public Map<String,Long> getMap() { return status; }\n  }\n}\n"
  },
  {
    "path": "java/src/com/omniti/labs/FqCommandProtocolError.java",
    "content": "/*\n * Copyright (c) 2013 OmniTI Computer Consulting, Inc.\n * All rights reserved.\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\npackage com.omniti.labs;\n\npublic class FqCommandProtocolError extends Exception {\n  private short expected;\n  private short recvd;\n  private String msg;\n  public String toString() {\n    if(msg != null) return msg;\n    return \"Expected \" + String.format(\"0x%04x\", expected) +\n           \", but received \" + String.format(\"0x%04x\", recvd);\n  }\n  public FqCommandProtocolError(short _expected, short _recvd) {\n    expected = _expected;\n    recvd = _recvd;\n  }\n  public FqCommandProtocolError(short _expected) {\n    expected = _expected;\n    recvd = 0;\n  }\n  public FqCommandProtocolError(String _msg) {\n    msg = _msg;\n    recvd = 0;\n  }\n}\n"
  },
  {
    "path": "java/src/com/omniti/labs/FqDataProtocolError.java",
    "content": "/*\n * Copyright (c) 2013 OmniTI Computer Consulting, Inc.\n * All rights reserved.\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\npackage com.omniti.labs;\n\npublic class FqDataProtocolError extends Exception {\n  public FqDataProtocolError(String a) {\n    super(a);\n  }\n}\n\n"
  },
  {
    "path": "java/src/com/omniti/labs/FqHeartbeatException.java",
    "content": "/*\n * Copyright (c) 2013 OmniTI Computer Consulting, Inc.\n * All rights reserved.\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\npackage com.omniti.labs;\n\npublic class FqHeartbeatException extends Exception {\n  public FqHeartbeatException() { super(); }\n}\n"
  },
  {
    "path": "java/src/com/omniti/labs/FqMessage.java",
    "content": "/*\n * Copyright (c) 2013 OmniTI Computer Consulting, Inc.\n * All rights reserved.\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\npackage com.omniti.labs;\n\nimport java.io.IOException;\nimport java.net.InetAddress;\nimport java.nio.ByteBuffer;\nimport java.nio.charset.StandardCharsets;\nimport java.util.UUID;\nimport com.omniti.labs.FqDataProtocolError;\n\npublic class FqMessage {\n  public static class MsgId {\n    protected byte d[];\n    public MsgId(byte v[]) {\n      d = new byte[16];\n      System.arraycopy(v,0,d,0,16);\n    }\n  }\n\n  private boolean _complete = false;\n\n  private int nhops = -1;\n  private InetAddress hops[];\n  private int route_len = -1;\n  private byte route[];\n  private int sender_len = -1;\n  private byte sender[];\n  private int exchange_len = -1;\n  private byte exchange[];\n  private MsgId sender_msgid;\n  private int payload_len = -1;\n  private byte payload[];\n\n  private ByteBuffer[] iovec;\n\n  public void setRoute(byte[] _r) { route = _r; route_len = _r.length; }\n  public void setSender(byte[] _r) { sender = _r; sender_len = _r.length; }\n  public void setExchange(byte[] _r) { exchange = _r; exchange_len = _r.length; }\n  public void setMsgId() {\n    UUID uuid = UUID.randomUUID();\n    ByteBuffer bb = ByteBuffer.wrap(new byte[16]);\n    bb.putLong(uuid.getMostSignificantBits());\n    bb.putLong(uuid.getLeastSignificantBits());\n    sender_msgid = new MsgId(bb.array());\n  }\n  public void setPayload(byte[] _r) { payload = _r; payload_len = _r.length; }\n\n  public String getRoute() { return new String(route, StandardCharsets.UTF_8); }\n  public String getExchange() { return new String(exchange, StandardCharsets.UTF_8); }\n  public String getSender() { return new String(sender, StandardCharsets.UTF_8); }\n  public MsgId getMsgId() { return sender_msgid; }\n  public byte[] getPayload() { return payload; }\n  public InetAddress[] getPath() { return hops; }\n\n  public boolean isComplete(boolean peermode) {\n    if(peermode) {\n      if(nhops < 0 || hops == null || sender_len < 0 || sender == null)\n        return false;\n    }\n    if(route_len <= 0 || route == null ||\n      exchange_len <= 0 || exchange == null ||\n      payload_len < 0 || payload == null || sender_msgid == null)\n      return false;\n    return true;\n  }\n  public boolean isComplete() { return _complete; }\n  public boolean read(FqClient c) throws IOException, FqDataProtocolError {\n    boolean success;\n    int limit, position;\n    if(isComplete()) return true;\n    ByteBuffer bb = c.fill_data_buffer(false);\n    // Save fill location\n    position = bb.position();\n    limit = bb.limit();\n    // Set read location\n    bb.reset();\n    bb.limit(position);\n\n    success = readInternal(c, bb);\n\n    if(!success) {\n      // compact while reading\n      bb.compact();\n      // after compaction, position is a fill position\n      position = bb.position();\n      // mark at zero (as we've compacted)\n      bb.position(0);\n      bb.mark();\n      // restore the fill position\n      bb.position(position);\n    } else {\n      // restore fill position\n      bb.limit(limit);\n      bb.position(position);\n    }\n    return success;\n  }\n  private boolean readInternal(FqClient c, ByteBuffer bb)\n    throws IOException, FqDataProtocolError {\n    if(isComplete()) return true;\n    if(exchange_len == -1) {\n      if(bb.remaining() < 1) return false;\n      byte len = bb.get();\n      exchange_len = len;\n      bb.mark();\n      if(exchange_len <= 0 || exchange_len > 127)\n        throw new FqDataProtocolError(\"invalid exchange_len: \" + exchange_len);\n    }\n    if(exchange == null) {\n      if(bb.remaining() < exchange_len) return false;\n      exchange = new byte[exchange_len];\n      bb.get(exchange);\n      bb.mark();\n    }\n    if(route_len == -1) {\n      if(bb.remaining() < 1) return false;\n      route_len = (int)bb.get();\n      bb.mark();\n      if(route_len < 0 || route_len > 127)\n        throw new FqDataProtocolError(\"invalid route_len: \" + route_len);\n    }\n    if(route == null) {\n      if(bb.remaining() < route_len) return false;\n      route = new byte[route_len];\n      bb.get(route);\n      bb.mark();\n    }\n    if(sender_msgid == null) {\n      if(bb.remaining() < 16) return false;\n      byte[] m = new byte[16];\n      bb.get(m);\n      bb.mark();\n      sender_msgid = new MsgId(m);\n    }\n    if(sender_len == -1) {\n      if(bb.remaining() < 1) return false;\n      sender_len = (int)bb.get();\n      bb.mark();\n      if(sender_len < 0 || sender_len > 127)\n        throw new FqDataProtocolError(\"invalid sender_len: \" + sender_len);\n    }\n    if(sender == null) {\n      if(bb.remaining() < sender_len) return false;\n      sender = new byte[sender_len];\n      bb.get(sender);\n      bb.mark();\n    }\n    if(nhops == -1) {\n      if(bb.remaining() < 1) return false;\n      nhops = (int)bb.get();\n      if(nhops < 0 || nhops > 32)\n        throw new FqDataProtocolError(\"invalid nhops: \" + nhops);\n      bb.mark();\n    }\n    if(hops == null) {\n      if(bb.remaining() < nhops * 4) return false;\n      hops = new InetAddress[nhops];\n      byte ip[] = new byte[4];\n      for(int i=0;i<nhops;i++) {\n        bb.get(ip);\n        hops[i] = InetAddress.getByAddress(ip);\n      }\n      bb.mark();\n    }\n    if(payload_len == -1) {\n      if(bb.remaining() < 4) return false;\n      payload_len = bb.getInt();\n      bb.mark();\n    }\n    if(payload == null && payload_len > 0) {\n      payload = new byte[payload_len];\n      if(bb.remaining() >= payload_len) {\n        bb.get(payload);\n      } else {\n        int havenow = bb.remaining();\n        bb.get(payload, 0, havenow);\n        int nread = c.blockingRead(payload, havenow, payload_len - havenow);\n        if((nread+havenow) != payload_len)\n          throw new FqDataProtocolError(\"payload read failure: \" + nread + \"+\" + havenow + \" != \" + payload_len);\n      }\n      bb.mark();\n    }\n    _complete = true;\n    return true;\n  }\n\n  public boolean send(FqClient c) throws IOException, FqDataProtocolError {\n    if(!isComplete(c.isPeermode())) throw new FqDataProtocolError(\"incomplete message\");\n    if(iovec == null) {\n      int i = 0;\n      iovec = new ByteBuffer[c.isPeermode() ? 11 : 7];\n      iovec[i  ] = ByteBuffer.allocate(1).put((byte)exchange_len);\n      iovec[i++].flip();\n      iovec[i++] = ByteBuffer.wrap(exchange);\n      iovec[i  ] = ByteBuffer.allocate(1).put((byte)route_len);\n      iovec[i++].flip();\n      iovec[i++] = ByteBuffer.wrap(route);\n      iovec[i++] = ByteBuffer.wrap(sender_msgid.d);\n      if(c.isPeermode()) {\n        iovec[i  ] = ByteBuffer.allocate(1).put((byte)sender_len);\n        iovec[i++].flip();\n        iovec[i++] = ByteBuffer.wrap(sender);\n        iovec[i  ] = ByteBuffer.allocate(1).put((byte)nhops);\n        iovec[i++].flip();\n        iovec[i  ] = ByteBuffer.allocate(nhops * 4);\n        for(int j=0; j<nhops; j++)\n          iovec[i].put(hops[j].getAddress());\n        iovec[i++].flip();\n      }\n      iovec[i  ] = ByteBuffer.allocate(4).putInt(payload_len);\n      iovec[i++].flip();\n      iovec[i++] = ByteBuffer.wrap(payload);\n    }\n    if(c.data_write(iovec) < 0) throw new IOException();\n    if(iovec[iovec.length-2].position() == 4 &&\n      iovec[iovec.length-1].position() == payload_len) {\n      iovec = null;\n      return true;\n    }\n    return false;\n  }\n}\n"
  },
  {
    "path": "java/src/main/java/com/circonus/FqClient.java",
    "content": "/*\n * Copyright (c) 2016 Circonus, Inc.\n * Copyright (c) 2013 OmniTI Computer Consulting, Inc.\n * All rights reserved.\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\npackage com.circonus;\n\nimport java.io.IOException;\nimport java.net.InetSocketAddress;\nimport java.net.SocketAddress;\nimport java.net.Socket;\nimport java.net.SocketException;\nimport java.nio.ByteBuffer;\nimport java.nio.ByteOrder;\nimport java.nio.charset.StandardCharsets;\nimport java.nio.channels.Selector;\nimport java.nio.channels.spi.AbstractSelector;\nimport java.nio.channels.SelectionKey;\nimport java.nio.channels.SocketChannel;\nimport java.nio.channels.spi.SelectorProvider;\nimport java.util.concurrent.atomic.AtomicInteger;\nimport java.util.concurrent.ConcurrentLinkedQueue;\nimport java.util.concurrent.LinkedBlockingQueue;\nimport java.util.ArrayDeque;\nimport java.util.UUID;\nimport com.circonus.FqClientImplInterface;\nimport com.circonus.FqCommand;\nimport com.circonus.FqMessage;\n\npublic class FqClient {\n  public final static int FQ_PROTO_CMD_MODE = 0xcc50cafe;\n  public final static int FQ_PROTO_DATA_MODE = 0xcc50face;\n  public final static int FQ_PROTO_PEER_MODE = 0xcc50fade;\n\n  private int mode;\n  private String host;\n  private SocketAddress hostaddr;\n  private int port;\n  private String user;\n  private String queue;\n  private String queue_type;\n  private String pass;\n/*\n  private int q_stall_time;\n  private int qmaxlen;\n*/\n  private final FqMessage endpost = new FqMessage();\n\n  private volatile boolean stop = false;\n  private volatile boolean shutting_down = false;\n  private boolean data_ready = false;\n  private short cmd_hb_ms = 0;\n  private short last_cmd_hb_ms = 0;\n  private long cmd_hb_last;\n  private long cmd_hb_last_sent = 0;\n  private FqClientImplInterface impl;\n  private AbstractSelector cmd_selector;\n  private SocketChannel cmd_socket;\n  private ByteBuffer cmd_in_buff;\n  private ByteBuffer data_in_buff;\n  private AbstractSelector data_selector;\n  private SelectionKey data_skey;\n  private SocketChannel data_socket;\n  private Object keylock = new Object();\n  private boolean connected = false;\n  private byte client_key[];\n  private Thread worker;\n  private Thread data_worker;\n  private Thread back_worker;\n  private Thread sender_worker;\n  private final Object sender_worker_lock = new Object();\n  private AtomicInteger qlen;\n  private ConcurrentLinkedQueue<FqCommand> cmdq;\n  private LinkedBlockingQueue<FqMessage> q;\n  private LinkedBlockingQueue<FqMessage> backq;\n  private FqCommand.Heartbeat reusable_hb;\n\n  private void initialize(FqClientImplInterface _impl, int _mode, int bsize)\n    throws FqClientImplInterface.InUseException {\n    impl = _impl;\n    impl.setClient(this);\n    mode = _mode;\n    qlen = new AtomicInteger(0);\n    cmdq = new ConcurrentLinkedQueue<FqCommand>();\n    q = new LinkedBlockingQueue<FqMessage>();\n    backq = new LinkedBlockingQueue<FqMessage>();\n    reusable_hb = new FqCommand.Heartbeat();\n    cmd_in_buff = ByteBuffer.allocate(65536);\n    data_in_buff = ByteBuffer.allocate(bsize);\n    data_in_buff.mark();\n  }\n  public FqClient(FqClientImplInterface _impl)\n    throws FqClientImplInterface.InUseException {\n    initialize(_impl, FQ_PROTO_DATA_MODE, 4194304);\n  }\n  public FqClient(FqClientImplInterface _impl, int _mode)\n    throws FqClientImplInterface.InUseException {\n    initialize(_impl, _mode, 4194304);\n  }\n  public FqClient(FqClientImplInterface _impl, int _mode, int _bsize)\n    throws FqClientImplInterface.InUseException {\n    initialize(_impl, _mode, _bsize);\n  }\n  public boolean isPeermode() { return (mode == FQ_PROTO_PEER_MODE); }\n  public FqClientImplInterface getImpl() { return impl; }\n  public void setHeartbeat(short ms) {\n    if(ms != cmd_hb_ms) {\n      cmd_hb_ms = ms;\n      send(new FqCommand.HeartbeatRequest(cmd_hb_ms));\n    }\n  }\n  public void setHeartbeat(int ms) {\n    setHeartbeat((short)ms);\n  }\n  public void set_backlog(int len, int stall) {\n/*\n    qmaxlen = len;\n    q_stall_time = stall;\n*/\n  }\n  public void send(FqCommand cmd) {\n    cmdq.offer(cmd);\n  }\n  public void send(FqMessage m) {\n    q.offer(m);\n  }\n  public void creds(int _port, String _source, String _pass)\n      throws java.net.UnknownHostException {\n    creds(null, _port, _source, _pass);\n  }\n  public void creds(String _host, String _source, String _pass)\n      throws java.net.UnknownHostException {\n    creds(_host, 0, _source, _pass);\n  }\n  public void creds(String _host, int _port,\n      String _source, String _pass)\n      throws java.net.UnknownHostException {\n    int cidx;\n    if(_host != null) host = _host;\n    if(host == null) host = \"127.0.0.1\";\n    if(_port != 0) port = _port % 0xffff;\n    if(port == 0) port = 8765;\n    user = _source;\n    if((cidx = user.indexOf(\"/\")) >= 0) {\n      queue = user.substring(cidx + 1);\n      user = user.substring(0, cidx);\n      if((cidx = queue.indexOf(\"/\")) >= 0) {\n        queue_type = queue.substring(cidx + 1);\n        queue = queue.substring(0, cidx);\n      }\n    }\n\n    if(queue == null || queue.length() == 0)\n      queue = UUID.randomUUID().toString();\n    if(queue_type == null || queue_type.length() == 0)\n      queue_type = \"mem\";\n    pass = _pass;\n    hostaddr = new InetSocketAddress(host, port);\n  }\n  private boolean client_do_auth() throws IOException, FqCommandProtocolError {\n    FqCommand.PlainAuth auth =\n      new FqCommand.PlainAuth(user,pass,queue,queue_type);\n    auth.send(this);\n    auth.process(this);\n    client_key = auth.getKey();\n    return (client_key != null);\n  }\n  public byte[] cmd_read_short_bytearray() throws IOException {\n    cmd_in_buff.clear();\n    cmd_in_buff.limit(2);\n    if(cmd_socket.read(cmd_in_buff) == -1) return null;\n    cmd_in_buff.flip();\n    Short strlen = cmd_in_buff.getShort();\n    cmd_in_buff.clear();\n    cmd_in_buff.limit(strlen);\n    if(cmd_socket.read(cmd_in_buff) == -1) return null;\n    byte a[] = new byte[strlen];\n    cmd_in_buff.flip();\n    cmd_in_buff.get(a);\n    return a;\n  }\n  public String cmd_read_short_string() throws IOException {\n    byte a[] = cmd_read_short_bytearray();\n    if(a == null) return null;\n    return new String(a, StandardCharsets.UTF_8);\n  }\n  public ByteBuffer cmd_read(int len) throws IOException {\n    if(len > cmd_in_buff.capacity()) {\n      ByteBuffer bb = ByteBuffer.allocate(len);\n      if(cmd_socket.read(bb) == -1) return null;\n      return bb;\n    }\n    cmd_in_buff.clear();\n    cmd_in_buff.limit(len);\n    if(cmd_socket.read(cmd_in_buff) == -1) return null;\n    return cmd_in_buff;\n  }\n  public long data_write(ByteBuffer bb) throws IOException {\n    return data_socket.write(bb);\n  }\n  public long data_write(ByteBuffer[] bb) throws IOException {\n    return data_socket.write(bb);\n  }\n  public int cmd_write(ByteBuffer bb) throws IOException {\n    return cmd_socket.write(bb);\n  }\n  private boolean client_data_connect_internal() {\n    boolean success = false;\n    try {\n      data_selector = (AbstractSelector)Selector.open();\n      data_socket = data_selector.provider().openSocketChannel();\n      data_socket.connect(hostaddr);\n      data_socket.socket().setTcpNoDelay(true);\n      ByteBuffer bb = ByteBuffer.allocate(4 + 2 + client_key.length);\n      bb.order(ByteOrder.BIG_ENDIAN);\n      bb.putInt(mode);\n      bb.putShort((short)client_key.length);\n      bb.put(client_key);\n      bb.flip();\n      data_write(bb);\n      data_socket.configureBlocking(false);\n      data_skey = data_socket.register(data_selector, SelectionKey.OP_READ);\n    } catch(Exception e) {\n      impl.connectError(e);\n      return success;\n    }\n    return success;\n  }\n  private void reset() {\n    try { cmd_socket.close(); } catch (Exception ce) { ce.printStackTrace(); }\n    try { data_socket.close(); } catch (Exception ce) { ce.printStackTrace(); }\n    data_ready = false;\n    cmd_hb_last_sent = 0;\n    cmd_hb_last = 0;\n  }\n  private boolean client_connect_internal() {\n    boolean success = false;\n    // Force close\n    reset();\n    try {\n      cmd_selector = (AbstractSelector)Selector.open();\n      cmd_socket = cmd_selector.provider().openSocketChannel();\n      cmd_socket.connect(hostaddr);\n      cmd_socket.socket().setTcpNoDelay(true);\n      cmd_socket.socket().setSoTimeout(5000);\n      setHeartbeat((cmd_hb_ms != 0) ? cmd_hb_ms : (short)10000);\n      ByteBuffer bb = ByteBuffer.allocate(4);\n      bb.order(ByteOrder.BIG_ENDIAN);\n      bb.putInt(FQ_PROTO_CMD_MODE);\n      bb.flip();\n      cmd_write(bb);\n      success = client_do_auth();\n    } catch(Exception e) {\n      impl.connectError(e);\n      return success;\n    }\n    return success;\n  }\n  public void connect() {\n    if(connected) return;\n    worker = new Thread() {\n        public void run() { worker(); }\n     };\n    worker.setName(\"Fq-cmd(\" + host + \")\");\n    worker.start();\n    data_worker = new Thread() {\n        public void run() { data_worker(); }\n     };\n    data_worker.setName(\"Fq-data-in(\" + host + \")\");\n    data_worker.start();\n    back_worker = new Thread() {\n        public void run() { back_worker(); }\n     };\n    back_worker.setName(\"Fq-back(\" + host + \")\");\n    back_worker.start();\n  }\n  public void recvHeartbeat() {\n    cmd_hb_last = System.nanoTime();\n  }\n  private void sendHeartbeat() throws IOException, FqHeartbeatException {\n    long t = System.nanoTime();\n    if((t - cmd_hb_last_sent) > ((long)cmd_hb_ms * 1000000)) {\n      reusable_hb.send(this);\n      cmd_hb_last_sent = t;\n    }\n    if(cmd_hb_ms != last_cmd_hb_ms) {\n      cmd_socket.socket().setSoTimeout(cmd_hb_ms * 2);\n      last_cmd_hb_ms = cmd_hb_ms;\n    }\n    long hb_ns = (long)cmd_hb_ms * (long)3 * (long)1000000;\n    if(cmd_hb_last != 0 && hb_ns != 0 &\n      cmd_hb_last < (t - hb_ns)) {\n      throw new FqHeartbeatException();\n    }\n  }\n  public int data_backlog() { return qlen.get(); }\n  private void worker() {\n    final ArrayDeque<FqCommand> responses = new ArrayDeque<>();\n    int backoff = 0;\n    while(!stop) {\n      responses.clear();\n      try {\n        if(client_connect_internal()) {\n          data_ready = true;\n          backoff = 0;\n        }\n        while(!stop && data_ready) {\n          FqCommand entry;\n          while(null != (entry = cmdq.poll())) {\n            entry.send(this);\n            if(entry.hasInBandResponse()) {\n              responses.addLast(entry);\n            }\n          }\n\n          sendHeartbeat();\n\n          entry = responses.pollFirst();\n          if(entry == null) entry = reusable_hb;\n          entry.process(this);\n        }\n      } catch(Exception e) {\n        impl.commandError(e);\n      }\n      try {\n        Thread.sleep(backoff / 1000, (backoff % 1000) * 1000);\n      } catch(InterruptedException ignore) {}\n      backoff += 10000;\n    }\n  }\n  private void data_worker_sender() {\n    FqMessage m;\n    while(!stop && cmd_socket.socket().isConnected()) {\n      m = null;\n      try { m = q.take(); } catch(InterruptedException ignore) { }\n      if(m == endpost) break;\n      if(m == null) continue;\n      try {\n        while(!m.send(this) && !stop && cmd_socket.socket().isConnected()) {\n          synchronized(keylock) {\n            data_skey.interestOps(SelectionKey.OP_READ|SelectionKey.OP_WRITE);\n            try { keylock.wait(); } catch(InterruptedException ignore) { }\n          }\n        }\n      } catch(IOException e) {\n        impl.dataError(e);\n        return;\n      } catch(FqDataProtocolError e) {\n        impl.dataError(e);\n        return;\n      }\n    }\n  }\n  private void waitForData(long ms) throws IOException {\n    data_selector.select(ms);\n    if(data_skey.isWritable()) {\n      synchronized(keylock) {\n        data_skey.interestOps(SelectionKey.OP_READ);\n        keylock.notify();\n      }\n    }\n  }\n  public int blockingRead(byte dst[], int offset, int len) throws IOException {\n    ByteBuffer rest = ByteBuffer.wrap(dst, offset, len);\n    int nread = 0;\n    // A quick non-blocking attempt\n    if((nread = data_socket.read(rest)) == len) {\n      return len; // done.\n    }\n    if (nread < 0) throw new IOException();\n\n    while(rest.position() < (offset+len)) {\n      int readlen;\n      waitForData(1000);\n      while((readlen = data_socket.read(rest)) > 0) {\n        nread += readlen;\n      }\n      if(readlen < 0) throw new IOException();\n    }\n    return nread;\n  }\n  public ByteBuffer fill_data_buffer(boolean force) throws IOException {\n    if(force || data_in_buff.position() == 0) {\n      waitForData(1000);\n      int rsize = data_socket.read(data_in_buff);\n      if(rsize < -1) throw new IOException(\"bad read\");\n    }\n    return data_in_buff;\n  }\n\n  private void data_worker_receiver() {\n    while(!stop && cmd_socket.socket().isConnected()) {\n      FqMessage m;\n      m = new FqMessage();\n      try { while(!stop && !m.read(this)) fill_data_buffer(true); }\n      catch (IOException e) { impl.dataError(e); reset(); return; }\n      catch (FqDataProtocolError e) { impl.dataError(e); reset(); return; }\n      if(m.isComplete()) {\n        do {\n          try {\n            backq.put(m);\n            m = null;\n          }\n          catch(InterruptedException ignore) { }\n        } while(m != null);\n      }\n    }\n  }\n  private void data_worker() {\n    int backoff = 0;\n    Error boom = null;\n    while(!stop) {\n      if(data_ready) {\n        try {\n          if(client_data_connect_internal()) {\n            backoff = 0;\n          }\n\n          sender_worker = new Thread() {\n            public void run() { data_worker_sender(); }\n           };\n          sender_worker.setName(\"Fq-data-out(\" + host + \")\");\n          sender_worker.start();\n          try {\n            data_worker_receiver();\n          } catch (Error e) {\n            impl.dataError(e);\n            q.offer(endpost);\n            reset();\n            boom = e;\n          }\n          try { sender_worker.interrupt(); } catch (Exception ignore) { ignore.printStackTrace(); }\n          synchronized(sender_worker_lock) {\n            sender_worker.join();\n          }\n  \n        } catch(Exception e) {\n          impl.dataError(e);\n        }\n      }\n      if(backoff < 1000000) backoff += 10000;\n      try {\n        Thread.sleep(backoff / 1000, (backoff % 1000) * 1000);\n      } catch(InterruptedException ignore) {}\n    }\n    shutting_down = true;\n    backq.offer(endpost);\n    if(boom != null) throw(boom);\n  }\n  private void back_worker() {\n    while(!shutting_down) {\n      try {\n        FqMessage m = backq.take();\n        if(m == endpost) break;\n        impl.dispatch(m);\n      } catch(InterruptedException ignore) { }\n    }\n  }\n  public void shutdown() {\n    stop = true;\n    q.offer(endpost);\n    try {\n      synchronized(sender_worker_lock) {\n        sender_worker.join();\n      }\n      data_worker.join();\n      worker.join();\n    } catch(InterruptedException ignore) { }\n  }\n}\n"
  },
  {
    "path": "java/src/main/java/com/circonus/FqClientImplDebug.java",
    "content": "/*\n * Copyright (c) 2016 Circonus, Inc.\n * Copyright (c) 2013 OmniTI Computer Consulting, Inc.\n * All rights reserved.\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\npackage com.circonus;\n\nimport java.util.Date;\nimport java.util.Map;\n\npublic class FqClientImplDebug implements FqClientImplInterface {\n  protected FqClient client = null;\n  public void setClient(FqClient c) throws InUseException {\n    if(client != null) throw new InUseException();\n    client = c;\n  }\n  protected void genericError(Throwable e) {\n    e.printStackTrace();\n  }\n  public void connectError(Throwable e) { genericError(e); } \n  public void commandError(Throwable e) { genericError(e); }\n  public void dataError(Throwable e) { genericError(e); }\n  public void dispatch(FqMessage m) {\n    byte b[] = m.getPayload();\n    int len = (b == null) ? 0 : b.length;\n    System.err.println(\"m[\" + len + \"] via \" + m.getRoute() +\n      \" over \" + m.getExchange() + \" from \" + m.getSender());\n  }\n  public void dispatch(FqCommand cmd) {\n    System.err.println(cmd);\n  }\n  public void dispatchAuth(FqCommand.Auth cmd) {\n    dispatch(cmd);\n  }\n  public void dispatchHeartbeatRequest(FqCommand.HeartbeatRequest cmd) {\n    dispatch(cmd);\n  }\n  public void dispatchHeartbeat(FqCommand.Heartbeat cmd) { dispatch(cmd); }\n  public void dispatchBindRequest(FqCommand.BindRequest cmd) {\n    System.err.println(cmd.toString() + cmd.getBinding());\n  }\n  public void dispatchUnbindRequest(FqCommand.UnbindRequest cmd) {\n    System.err.println(cmd.toString() + cmd.getBinding() + \" \" + cmd.getSuccess());\n  }\n  public void dispatchStatusRequest(FqCommand.StatusRequest cmd) {\n    Date d = cmd.getDate();\n    Map<String,Long> m = cmd.getMap();\n    System.err.println(\"Status: \" + d);\n    for(Map.Entry<String, Long> entry : m.entrySet()) {\n      System.err.println(\"    \" + entry.getKey() + \" : \" + entry.getValue());\n    }\n  }\n}\n"
  },
  {
    "path": "java/src/main/java/com/circonus/FqClientImplInterface.java",
    "content": "/*\n * Copyright (c) 2016 Circonus, Inc.\n * Copyright (c) 2013 OmniTI Computer Consulting, Inc.\n * All rights reserved.\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\npackage com.circonus;\n\nimport com.circonus.FqCommand;\n\npublic interface FqClientImplInterface {\n  public class InUseException extends Exception { }\n  public void setClient(FqClient c) throws InUseException;\n  public void connectError(Throwable e);\n  public void commandError(Throwable e);\n  public void dataError(Throwable e);\n  public void dispatch(FqMessage m);\n  public void dispatch(FqCommand cmd);\n  public void dispatchAuth(FqCommand.Auth cmd);\n  public void dispatchHeartbeat(FqCommand.Heartbeat cmd);\n  public void dispatchHeartbeatRequest(FqCommand.HeartbeatRequest cmd);\n  public void dispatchBindRequest(FqCommand.BindRequest cmd);\n  public void dispatchUnbindRequest(FqCommand.UnbindRequest cmd);\n  public void dispatchStatusRequest(FqCommand.StatusRequest cmd);\n}\n"
  },
  {
    "path": "java/src/main/java/com/circonus/FqClientImplNoop.java",
    "content": "/*\n * Copyright (c) 2016 Circonus, Inc.\n * Copyright (c) 2013 OmniTI Computer Consulting, Inc.\n * All rights reserved.\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\npackage com.circonus;\n\npublic class FqClientImplNoop implements FqClientImplInterface {\n  protected FqClient client;\n  public void setClient(FqClient c) throws InUseException {\n    if(client != null) throw new InUseException();\n    client = c;\n  }\n  protected void genericError(Throwable e) { }\n  public void connectError(Throwable e) { genericError(e); } \n  public void commandError(Throwable e) { genericError(e); }\n  public void dataError(Throwable e) { genericError(e); }\n  public void dispatch(FqMessage m) { }\n  public void dispatch(FqCommand cmd) { }\n  public void dispatchAuth(FqCommand.Auth cmd) { dispatch(cmd); }\n  public void dispatchHeartbeatRequest(FqCommand.HeartbeatRequest cmd) {\n    dispatch(cmd);\n  }\n  public void dispatchHeartbeat(FqCommand.Heartbeat cmd) { dispatch(cmd); }\n  public void dispatchBindRequest(FqCommand.BindRequest cmd) { dispatch(cmd); };\n  public void dispatchUnbindRequest(FqCommand.UnbindRequest cmd) { dispatch(cmd); };\n  public void dispatchStatusRequest(FqCommand.StatusRequest cmd) { dispatch(cmd); };\n}\n"
  },
  {
    "path": "java/src/main/java/com/circonus/FqCommand.java",
    "content": "/*\n * Copyright (c) 2016 Circonus, Inc.\n * Copyright (c) 2013 OmniTI Computer Consulting, Inc.\n * All rights reserved.\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\npackage com.circonus;\n\nimport java.io.IOException;\nimport java.net.Socket;\nimport java.nio.ByteBuffer;\nimport java.nio.ByteOrder;\nimport java.nio.charset.StandardCharsets;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.Map;\nimport com.circonus.FqClient;\n\npublic abstract class FqCommand {\n  public final static short FQ_PROTO_ERROR = (short)0xeeee;\n  public final static short FQ_PROTO_HB = (short)0xbea7;\n  public final static short FQ_PROTO_AUTH_CMD = (short)0xaaaa;\n  public final static short FQ_PROTO_AUTH_PLAIN = (short)0;\n  public final static short FQ_PROTO_AUTH_RESP = (short)0xaa00;\n  public final static short FQ_PROTO_HBREQ = (short)0x4848;\n  public final static short FQ_PROTO_BIND = (short)0xb171;\n  public final static short FQ_PROTO_BINDREQ = (short)0xb170;\n  public final static short FQ_PROTO_UNBIND = (short)0x171b;\n  public final static short FQ_PROTO_UNBINDREQ = (short)0x071b;\n  public final static short FQ_PROTO_STATUS = (short)0x57a7;\n  public final static short FQ_PROTO_STATUSREQ = (short)0xc7a7;\n\n  protected ByteBuffer bb;\n  private boolean composed = false;\n  public abstract short cmd();\n  public short response_cmd() { return cmd(); }\n  public void compose() { }\n  public void send(FqClient c) throws IOException {\n    if(composed) {\n      bb.position(0);\n    } else {\n      bb.putShort(cmd());\n      compose();\n      bb.flip();\n      composed = true;\n    }\n    c.cmd_write(bb);\n  }\n  public abstract boolean hasInBandResponse();\n  private static Heartbeat hb = new Heartbeat();\n\n  public Short getShortCmd(FqClient c)\n      throws IOException, FqCommandProtocolError {\n    Short cmd;\n    do {\n      ByteBuffer bb = c.cmd_read(2);\n      if(bb == null) return null;\n      bb.flip();\n      cmd = bb.getShort();\n      if(cmd == FQ_PROTO_HB) { c.recvHeartbeat(); }\n      if(cmd == FQ_PROTO_ERROR) {\n        throw new FqCommandProtocolError(c.cmd_read_short_string());\n      }\n    } while(cmd() != FQ_PROTO_HB && cmd == FQ_PROTO_HB);\n    return cmd;\n  }\n  public void process(FqClient c) throws IOException, FqCommandProtocolError {\n    Short cmd = getShortCmd(c);\n    // the hearbeat happens magically in getShortCmd\n    if(cmd == null) {\n      throw new FqCommandProtocolError(\"null cmd\");\n    }\n    else if(cmd != response_cmd()) {\n      throw new FqCommandProtocolError(response_cmd(), cmd);\n    }\n  }\n\n  protected void alloc(int size) {\n    bb = ByteBuffer.allocate(size);\n    bb.order(ByteOrder.BIG_ENDIAN);\n  }\n  public FqCommand(int size) {\n    alloc(size + 2);\n  }\n  public FqCommand() {\n  }\n\n  public static class Heartbeat extends FqCommand {\n    public Heartbeat() { super(0); }\n    public boolean hasInBandResponse() { return false; }\n    public short cmd() { return FQ_PROTO_HB; }\n  }\n  public static class HeartbeatRequest extends FqCommand {\n    short ms;\n    public HeartbeatRequest(int _ms) {\n      super(2);\n      ms = (short)(_ms & 0xffff);\n    }\n    public boolean hasInBandResponse() { return false; }\n    public short cmd() { return FQ_PROTO_HBREQ; }\n    public void compose() { bb.putShort(ms); }\n  }\n  public static abstract class Auth extends FqCommand {\n    protected byte[] key = null;\n    public boolean success() { return (key != null); }\n    public byte[] getKey() { return key; }\n  }  \n  public static class PlainAuth extends Auth {\n    private byte b_user[];\n    private byte b_pass[];\n    private byte b_queue[];\n    private byte b_queue_type[];\n  \n    public PlainAuth(String user, String pass,\n      String queue, String queue_type) {\n      b_user = user.getBytes(StandardCharsets.UTF_8);\n      b_queue = queue.getBytes(StandardCharsets.UTF_8);\n      b_queue_type = queue_type.getBytes(StandardCharsets.UTF_8);\n      b_pass = pass.getBytes(StandardCharsets.UTF_8);\n      int extra_space = \n        2 + /* plain */\n        2 + b_user.length + /* user */\n        2 + b_queue.length + 1 + b_queue_type.length + /* queue */\n        2 + b_pass.length;\n      alloc(2+extra_space);\n    }\n    public short cmd() { return FQ_PROTO_AUTH_CMD; }\n    public boolean hasInBandResponse() { return true; }\n    public void compose() {\n      bb.putShort(FQ_PROTO_AUTH_PLAIN);\n      bb.putShort((short)b_user.length);\n      bb.put(b_user);\n      bb.putShort((short)(b_queue.length + 1 + b_queue_type.length));\n      bb.put(b_queue);\n      bb.put((byte) 0);\n      bb.put(b_queue_type);\n      bb.putShort((short)b_pass.length);\n      bb.put(b_pass);\n    }\n    public void process(FqClient c) throws IOException, FqCommandProtocolError {\n      Short cmd, len;\n      bb = c.cmd_read(2);\n      if(bb == null) return;\n      bb.flip();\n      cmd = bb.getShort();\n      switch(cmd) {\n        case FQ_PROTO_AUTH_RESP:\n          key = c.cmd_read_short_bytearray();\n          if(key == null || key.length > 127)\n            throw new FqCommandProtocolError(\"bad key\");\n          break;\n        case FQ_PROTO_ERROR:\n          String error = c.cmd_read_short_string();\n          if(error != null) throw new FqCommandProtocolError(error);\n          /* fall through */\n        default:\n          throw new FqCommandProtocolError(cmd);\n      }\n      c.getImpl().dispatchAuth(this);\n    }\n  }\n  public static class BindRequest extends FqCommand {\n    public static final short FQ_BIND_PEER = 0x0001;\n    public static final short FQ_BIND_PERM = 0x0110;\n    public static final short FQ_BIND_TRANS = 0x0100;\n    private Integer binding;\n    private byte exchange[];\n    private byte program[];\n    private short flags;\n    \n    public BindRequest(byte _exchange[], String _program, short _flags) {\n      program = _program.getBytes(StandardCharsets.UTF_8);\n      exchange = _exchange;\n      flags = _flags;\n      int extra_space = \n        2 + /* flags */\n        2 + exchange.length + /* user */\n        2 + program.length;\n      alloc(2+extra_space);\n    }\n    public BindRequest(byte _exchange[], String _program, boolean _peermode) {\n      this(_exchange, _program, _peermode ? FQ_BIND_PEER : 0);\n    }\n    public BindRequest(String exchange, String p, boolean m) {\n      this(exchange.getBytes(StandardCharsets.UTF_8), p, m);\n    }\n    public short cmd() { return FQ_PROTO_BINDREQ; }\n    public short response_cmd() { return FQ_PROTO_BIND; }\n    public boolean hasInBandResponse() { return true; }\n    public void compose() {\n      bb.putShort(flags);\n      bb.putShort((short)exchange.length);\n      bb.put(exchange);\n      bb.putShort((short)program.length);\n      bb.put(program);\n    }\n    public void process(FqClient c) throws IOException, FqCommandProtocolError {\n      super.process(c);\n      Integer cmd;\n      bb = c.cmd_read(4);\n      if(bb == null) return;\n      bb.flip();\n      binding = bb.getInt();\n      c.getImpl().dispatchBindRequest(this);\n    }\n    public Integer getBinding() { return binding; }\n    public byte[] getExchange() { return exchange; }\n  }\n  public static class UnbindRequest extends FqCommand {\n    private BindRequest bind;\n    private Integer success;\n    \n    public UnbindRequest(BindRequest b) {\n      bind = b;\n      int extra_space = \n        2 + /* peermode */\n        4 + /* route_id */\n        2 + bind.getExchange().length;\n      alloc(2+extra_space);\n    }\n    public short cmd() { return FQ_PROTO_UNBINDREQ; }\n    public short response_cmd() { return FQ_PROTO_UNBIND; }\n    public boolean hasInBandResponse() { return true; }\n    public void compose() {\n      bb.putInt(bind.getBinding());\n      bb.putShort((short)bind.getExchange().length);\n      bb.put(bind.getExchange());\n    }\n    public void process(FqClient c) throws IOException, FqCommandProtocolError {\n      super.process(c);\n      Integer cmd;\n      bb = c.cmd_read(4);\n      if(bb == null) return;\n      bb.flip();\n      success = bb.getInt();\n      c.getImpl().dispatchUnbindRequest(this);\n    }\n    public Integer getBinding() { return bind.getBinding(); }\n    public boolean getSuccess() {\n      return (success != null && success.equals(bind.getBinding()));\n    }\n  }\n\n  public static class StatusRequest extends FqCommand {\n    protected Date last_update;\n    protected HashMap<String,Long> status = new HashMap<String,Long>();\n    public StatusRequest() { super(0); }\n    public short cmd() { return FQ_PROTO_STATUSREQ; }\n    public short response_cmd() { return FQ_PROTO_STATUS; }\n    public boolean hasInBandResponse() { return true; }\n    public void process(FqClient c) throws IOException, FqCommandProtocolError {\n      super.process(c);\n      last_update = new Date();\n      while(true) {\n        String key = c.cmd_read_short_string();\n        if(key == null || key.length() == 0) break;\n        Integer ivalue;\n        bb = c.cmd_read(4);\n        if(bb == null) throw new FqCommandProtocolError(\"status read failure\");\n        bb.flip();\n        ivalue = bb.getInt();\n        Long value = ivalue & (long)0xffffffff;\n        status.put(key,value);\n      }\n      c.getImpl().dispatchStatusRequest(this);\n    }\n    public Date getDate() { return last_update; }\n    public Map<String,Long> getMap() { return status; }\n  }\n}\n"
  },
  {
    "path": "java/src/main/java/com/circonus/FqCommandProtocolError.java",
    "content": "/*\n * Copyright (c) 2016 Circonus, Inc.\n * Copyright (c) 2013 OmniTI Computer Consulting, Inc.\n * All rights reserved.\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\npackage com.circonus;\n\npublic class FqCommandProtocolError extends Exception {\n  private short expected;\n  private short recvd;\n  private String msg;\n  public String toString() {\n    if(msg != null) return msg;\n    return \"Expected \" + String.format(\"0x%04x\", expected) +\n           \", but received \" + String.format(\"0x%04x\", recvd);\n  }\n  public FqCommandProtocolError(short _expected, short _recvd) {\n    expected = _expected;\n    recvd = _recvd;\n  }\n  public FqCommandProtocolError(short _expected) {\n    expected = _expected;\n    recvd = 0;\n  }\n  public FqCommandProtocolError(String _msg) {\n    msg = _msg;\n    recvd = 0;\n  }\n}\n"
  },
  {
    "path": "java/src/main/java/com/circonus/FqDataProtocolError.java",
    "content": "/*\n * Copyright (c) 2016 Circonus, Inc.\n * Copyright (c) 2013 OmniTI Computer Consulting, Inc.\n * All rights reserved.\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\npackage com.circonus;\n\npublic class FqDataProtocolError extends Exception {\n  public FqDataProtocolError(String a) {\n    super(a);\n  }\n}\n\n"
  },
  {
    "path": "java/src/main/java/com/circonus/FqHeartbeatException.java",
    "content": "/*\n * Copyright (c) 2016 Circonus, Inc.\n * Copyright (c) 2013 OmniTI Computer Consulting, Inc.\n * All rights reserved.\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\npackage com.circonus;\n\npublic class FqHeartbeatException extends Exception {\n  public FqHeartbeatException() { super(); }\n}\n"
  },
  {
    "path": "java/src/main/java/com/circonus/FqMessage.java",
    "content": "/*\n * Copyright (c) 2016 Circonus, Inc.\n * Copyright (c) 2013 OmniTI Computer Consulting, Inc.\n * All rights reserved.\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\npackage com.circonus;\n\nimport java.io.IOException;\nimport java.net.InetAddress;\nimport java.nio.ByteBuffer;\nimport java.nio.charset.StandardCharsets;\nimport java.util.UUID;\nimport com.circonus.FqDataProtocolError;\n\npublic class FqMessage {\n  public static class MsgId {\n    protected byte d[];\n    public MsgId(byte v[]) {\n      d = new byte[16];\n      System.arraycopy(v,0,d,0,16);\n    }\n  }\n\n  private boolean _complete = false;\n\n  private int nhops = -1;\n  private InetAddress hops[];\n  private int route_len = -1;\n  private byte route[];\n  private int sender_len = -1;\n  private byte sender[];\n  private int exchange_len = -1;\n  private byte exchange[];\n  private MsgId sender_msgid;\n  private int payload_len = -1;\n  private byte payload[];\n\n  private ByteBuffer[] iovec;\n\n  public void setRoute(byte[] _r) { route = _r; route_len = _r.length; }\n  public void setSender(byte[] _r) { sender = _r; sender_len = _r.length; }\n  public void setExchange(byte[] _r) { exchange = _r; exchange_len = _r.length; }\n  public void setMsgId() {\n    UUID uuid = UUID.randomUUID();\n    ByteBuffer bb = ByteBuffer.wrap(new byte[16]);\n    bb.putLong(uuid.getMostSignificantBits());\n    bb.putLong(uuid.getLeastSignificantBits());\n    sender_msgid = new MsgId(bb.array());\n  }\n  public void setPayload(byte[] _r) { payload = _r; payload_len = _r.length; }\n\n  public String getRoute() { return new String(route, StandardCharsets.UTF_8); }\n  public String getExchange() { return new String(exchange, StandardCharsets.UTF_8); }\n  public String getSender() { return new String(sender, StandardCharsets.UTF_8); }\n  public MsgId getMsgId() { return sender_msgid; }\n  public byte[] getPayload() { return payload; }\n  public InetAddress[] getPath() { return hops; }\n\n  public boolean isComplete(boolean peermode) {\n    if(peermode) {\n      if(nhops < 0 || hops == null || sender_len < 0 || sender == null)\n        return false;\n    }\n    if(route_len <= 0 || route == null ||\n      exchange_len <= 0 || exchange == null ||\n      payload_len < 0 || payload == null || sender_msgid == null)\n      return false;\n    return true;\n  }\n  public boolean isComplete() { return _complete; }\n  public boolean read(FqClient c) throws IOException, FqDataProtocolError {\n    boolean success;\n    int limit, position;\n    if(isComplete()) return true;\n    ByteBuffer bb = c.fill_data_buffer(false);\n    // Save fill location\n    position = bb.position();\n    limit = bb.limit();\n    // Set read location\n    bb.reset();\n    bb.limit(position);\n\n    success = readInternal(c, bb);\n\n    if(!success) {\n      // compact while reading\n      bb.compact();\n      // after compaction, position is a fill position\n      position = bb.position();\n      // mark at zero (as we've compacted)\n      bb.position(0);\n      bb.mark();\n      // restore the fill position\n      bb.position(position);\n    } else {\n      // restore fill position\n      bb.limit(limit);\n      bb.position(position);\n    }\n    return success;\n  }\n  private boolean readInternal(FqClient c, ByteBuffer bb)\n    throws IOException, FqDataProtocolError {\n    if(isComplete()) return true;\n    if(exchange_len == -1) {\n      if(bb.remaining() < 1) return false;\n      byte len = bb.get();\n      exchange_len = len;\n      bb.mark();\n      if(exchange_len <= 0 || exchange_len > 127)\n        throw new FqDataProtocolError(\"invalid exchange_len: \" + exchange_len);\n    }\n    if(exchange == null) {\n      if(bb.remaining() < exchange_len) return false;\n      exchange = new byte[exchange_len];\n      bb.get(exchange);\n      bb.mark();\n    }\n    if(route_len == -1) {\n      if(bb.remaining() < 1) return false;\n      route_len = (int)bb.get();\n      bb.mark();\n      if(route_len < 0 || route_len > 127)\n        throw new FqDataProtocolError(\"invalid route_len: \" + route_len);\n    }\n    if(route == null) {\n      if(bb.remaining() < route_len) return false;\n      route = new byte[route_len];\n      bb.get(route);\n      bb.mark();\n    }\n    if(sender_msgid == null) {\n      if(bb.remaining() < 16) return false;\n      byte[] m = new byte[16];\n      bb.get(m);\n      bb.mark();\n      sender_msgid = new MsgId(m);\n    }\n    if(sender_len == -1) {\n      if(bb.remaining() < 1) return false;\n      sender_len = (int)bb.get();\n      bb.mark();\n      if(sender_len < 0 || sender_len > 127)\n        throw new FqDataProtocolError(\"invalid sender_len: \" + sender_len);\n    }\n    if(sender == null) {\n      if(bb.remaining() < sender_len) return false;\n      sender = new byte[sender_len];\n      bb.get(sender);\n      bb.mark();\n    }\n    if(nhops == -1) {\n      if(bb.remaining() < 1) return false;\n      nhops = (int)bb.get();\n      if(nhops < 0 || nhops > 32)\n        throw new FqDataProtocolError(\"invalid nhops: \" + nhops);\n      bb.mark();\n    }\n    if(hops == null) {\n      if(bb.remaining() < nhops * 4) return false;\n      hops = new InetAddress[nhops];\n      byte ip[] = new byte[4];\n      for(int i=0;i<nhops;i++) {\n        bb.get(ip);\n        hops[i] = InetAddress.getByAddress(ip);\n      }\n      bb.mark();\n    }\n    if(payload_len == -1) {\n      if(bb.remaining() < 4) return false;\n      payload_len = bb.getInt();\n      bb.mark();\n    }\n    if(payload == null && payload_len > 0) {\n      payload = new byte[payload_len];\n      if(bb.remaining() >= payload_len) {\n        bb.get(payload);\n      } else {\n        int havenow = bb.remaining();\n        bb.get(payload, 0, havenow);\n        int nread = c.blockingRead(payload, havenow, payload_len - havenow);\n        if((nread+havenow) != payload_len)\n          throw new FqDataProtocolError(\"payload read failure: \" + nread + \"+\" + havenow + \" != \" + payload_len);\n      }\n      bb.mark();\n    }\n    _complete = true;\n    return true;\n  }\n\n  public boolean send(FqClient c) throws IOException, FqDataProtocolError {\n    if(!isComplete(c.isPeermode())) throw new FqDataProtocolError(\"incomplete message\");\n    if(iovec == null) {\n      int i = 0;\n      iovec = new ByteBuffer[c.isPeermode() ? 11 : 7];\n      iovec[i  ] = ByteBuffer.allocate(1).put((byte)exchange_len);\n      iovec[i++].flip();\n      iovec[i++] = ByteBuffer.wrap(exchange);\n      iovec[i  ] = ByteBuffer.allocate(1).put((byte)route_len);\n      iovec[i++].flip();\n      iovec[i++] = ByteBuffer.wrap(route);\n      iovec[i++] = ByteBuffer.wrap(sender_msgid.d);\n      if(c.isPeermode()) {\n        iovec[i  ] = ByteBuffer.allocate(1).put((byte)sender_len);\n        iovec[i++].flip();\n        iovec[i++] = ByteBuffer.wrap(sender);\n        iovec[i  ] = ByteBuffer.allocate(1).put((byte)nhops);\n        iovec[i++].flip();\n        iovec[i  ] = ByteBuffer.allocate(nhops * 4);\n        for(int j=0; j<nhops; j++)\n          iovec[i].put(hops[j].getAddress());\n        iovec[i++].flip();\n      }\n      iovec[i  ] = ByteBuffer.allocate(4).putInt(payload_len);\n      iovec[i++].flip();\n      iovec[i++] = ByteBuffer.wrap(payload);\n    }\n    if(c.data_write(iovec) < 0) throw new IOException();\n    if(iovec[iovec.length-2].position() == 4 &&\n      iovec[iovec.length-1].position() == payload_len) {\n      iovec = null;\n      return true;\n    }\n    return false;\n  }\n}\n"
  },
  {
    "path": "lua/fq-proxy",
    "content": "#!/usr/bin/env luajit\nlocal fqclient = require 'fqclient'\n\nlocal usage = [[\nArguments:\n  --src_host        (default localhost)\n  --src_port        (default 8765)\n  --src_user        (default user)\n  --src_pass        (default pass)\n  --src_exchange    (string)\n  --src_program     (default 'prefix:')\n\n  --dst_host        (default localhost)\n  --dst_port        (default 8765)\n  --dst_user        (default user)\n  --dst_pass        (default pass)\n  --dst_exchange    (string)\n  --dst_route       (default \"\")\n\n  --filter   filter messages with regexp\n  --print    print messages to stdout\n]]\n\nlocal function die(msg) print(msg) print(usage) os.exit(1) end\nlocal function shift() return table.remove(arg, 1) or die(\"missing argument\") end\n\nlocal src_host = \"localhost\"\nlocal src_port = 8765\nlocal src_user = \"user\"\nlocal src_pass = \"pass\"\nlocal src_exchange\nlocal src_program = 'prefix:'\n\nlocal dst_host = \"localhost\"\nlocal dst_port = 8765\nlocal dst_user = \"user\"\nlocal dst_pass = \"pass\"\nlocal dst_route = \"\"\nlocal dst_program = 'prefix:'\n\nlocal do_print = false\nlocal filter = false\n\nwhile #arg > 0 do\n  local key = shift()\n  if     key == \"--src_host\"     then src_host = shift()\n  elseif key == \"--src_port\"     then src_port = shift()\n  elseif key == \"--src_user\"     then src_user = shift()\n  elseif key == \"--src_pass\"     then src_pass = shift()\n  elseif key == \"--src_exchange\" then src_exchange = shift()\n  elseif key == \"--src_program\"  then src_program = shift()\n  elseif key == \"--dst_host\"     then dst_host = shift()\n  elseif key == \"--dst_port\"     then dst_port = shift()\n  elseif key == \"--dst_user\"     then dst_user = shift()\n  elseif key == \"--dst_pass\"     then dst_pass = shift()\n  elseif key == \"--dst_exchange\" then dst_exchange = shift()\n  elseif key == \"--dst_route\"    then dst_route = shift()\n  elseif key == \"--filter\"       then filter = shift()\n  elseif key == \"--print\"        then do_print = true\n  else\n    die(\"Unknown argument \" .. key)\n  end\nend\n\nif not src_exchange then die(\"src_exchange not set\") end\nif not dst_exchange then die(\"dst_exchange not set\") end\n\nfunction main()\n  local src_fq_client = fqclient.new(src_host, src_port, src_user, src_pass)\n  src_fq_client:bind(src_exchange, src_program)\n  src_fq_client:connect()\n  local dst_fq_client = fqclient.new(dst_host, dst_port, dst_user, dst_pass)\n  dst_fq_client:connect()\n  if filter then print(string.format(\"Filtering messages by '%s'\", filter)) end\n  src_fq_client:listen(function(msg)\n      if (not filter) or string.match(msg, filter) then\n        if do_print then print(msg) end\n        dst_fq_client:send(msg, dst_exchange, dst_route)\n      end\n  end)\nend\nmain()\n"
  },
  {
    "path": "lua/fq-receiver",
    "content": "#!/usr/bin/env luajit\nlocal fqclient = require 'fqclient'\n\nlocal usage = [[\nArguments:\n  --host        (default localhost)\n  --port        (default 8765)\n  --user        (default user)\n  --pass        (default pass)\n  --exchange    (string)\n  --program     (default 'prefix:')\n  --time        add timestamp prefix\n  --timef       (default '%.6f') add fromatted timestamp\n  -v,--verbose  print metadata\n  -h,--help     print this message\n]]\n\nlocal function die(msg)\n  print(msg)\n  print(usage)\n  os.exit(1)\nend\n\nlocal host = \"localhost\"\nlocal port = 8765\nlocal program = 'prefix:'\nlocal exchange\nlocal user = \"user\"\nlocal pass = \"pass\"\nlocal verbose = false\nlocal separator = \"\\n\"\nlocal time = false\nlocal time_format = \"[%.3f]\\t\"\n\nlocal function shift() return table.remove(arg, 1) end\nwhile #arg > 0 do\n  local key = shift()\n  if     key == \"--host\" then\n    host = shift()\n  elseif key == \"--port\" then\n    port = shift()\n  elseif key == \"--user\" then\n    user = shift()\n  elseif key == \"--pass\" then\n    pass = shift()\n  elseif key == \"--program\" then\n    program = shift()\n  elseif key == \"--exchange\" then\n    exchange = shift()\n  elseif key == \"--separator\" then\n    separator = shift()\n  elseif key == \"--time\" then\n    time = true\n  elseif key == \"--timef\" then\n    time = true\n    time_format = shift()\n  elseif key == \"-v\" or key == \"--verbose\" then\n    verbose = true\n  elseif key == \"-h\" or key == \"--help\" then\n    print(usage)\n    os.exit(0)\n  else\n    die(\"Unknown argument \" .. key)\n  end\nend\n\nif not exchange then die(\"No exchange provided\") end\n\nlocal fq_client = fqclient.new(host, port, user, pass)\nfq_client:bind(exchange, program)\nfq_client:connect()\nfq_client:listen_table(function(mtab)\n    if verbose then\n      io.write(string.format(\"--%s\\n\", time and string.format(time_format, fqclient.time()) or \"\"))\n      io.write(string.format(\"arrival_time: %d\\n\", mtab.arrival_time))\n      io.write(string.format(\"sender      : %s\\n\", mtab.sender))\n      io.write(string.format(\"route       : %s\\n\", mtab.route))\n      io.write(string.format(\"payload     : %s\\n\", mtab.payload))\n    else\n      if time then io.write(string.format(time_format, fqclient.time())) end\n      io.write(mtab.payload)\n      io.write(separator)\n    end\nend)\n"
  },
  {
    "path": "lua/fq-sender",
    "content": "#!/usr/bin/env luajit\nlocal fqclient = require 'fqclient'\nlocal ffi = require 'ffi';\n\nlocal USAGE = [[\nArguments:\n  --host      (default localhost)    fq host to connect\n  --port      (default 8765)         port to connect\n  --user      (default user)\n  --pass      (default pass)\n  --exchange  (string)\n  --route     (default \"\")\n  -v verbosity\n]]\n\nlocal function die(msg)\n  print(msg)\n  print(USAGE)\n  os.exit(1)\nend\n\nlocal host = \"localhost\"\nlocal port = 8765\nlocal user = \"user\"\nlocal pass = \"pass\"\nlocal exchange\nlocal route = \"\"\nlocal verbose = false\n\nlocal function shift() return table.remove(arg, 1) end\nwhile #arg > 0 do\n  local key = shift()\n  if     key == \"--host\" then\n    host = shift()\n  elseif key == \"--port\" then\n    port = shift()\n  elseif key == \"--user\" then\n    user = shift()\n  elseif key == \"--pass\" then\n    pass = shift()\n  elseif key == \"--exchange\" then\n    exchange = shift()\n  elseif key == \"--route\" then\n    route = shift()\n  elseif key == \"-v\" or key == \"--verbose\" then\n    verbose = true\n  elseif key == \"-h\" or key == \"--help\" then\n    print(USAGE)\n    os.exit(0)\n  else\n    die(\"Unknown argument \" .. key)\n  end\nend\n\nif not exchange then\n  die(\"Exchange not provided\")\nend\nif not route then\n  die(\"Route not provided\")\nend\n\nlocal log\nif verbose then\n  log = function(...) return io.stderr:write(string.format(...) .. \"\\n\") end\nelse\n  log = function() end\nend\n\nlocal function exit()\nend\n\nlog(\"Connecting to %s %d %s %s\\n\", host, port, user, pass)\nlocal fq_client = fqclient.new(host, port, user, pass)\nfq_client:connect()\nwhile true do\n  local msg = io.read()\n  if not msg then\n    fqclient:close()\n    os.exit(0)\n  end\n  fq_client:send(msg, exchange, route)\n  log('sent\\t{ \"exchange\":\"%s\", route:\"%s\", \"message\":\"%s\" }', exchange, route, msg)\nend\n"
  },
  {
    "path": "lua/fqclient.lua.tail",
    "content": "\nlocal gettimeofday_struct = ffi.new(\"timeval\")\nlocal function gettimeofday()\n  ffi.C.gettimeofday(gettimeofday_struct, nil)\n  return tonumber(gettimeofday_struct.tv_sec) + tonumber(gettimeofday_struct.tv_usec) / 1000000\nend\n\nlocal function charstar(str)\n  local len = string.len(str)\n  local buf = ffi.new(\"char[?]\", len+1, 0)\n  ffi.copy(buf, str, len)\n  return buf\nend\n\nlocal function m2tab(m)\n  return {\n    route        = ffi.string(m.route.name, m.route.len),\n    sender       = ffi.string(m.sender.name, m.sender.len),\n    exchange     = ffi.string(m.exchange.name, m.exchange.len),\n    arrival_time = tonumber(m.arrival_time),\n    payload      = ffi.string(m.payload, m.payload_len),\n  }\nend\n\nlocal function new(host, port, user, pass)\n  local binds = {}\n  local bind_programs = {} -- refs the ffi C strings inside binds\n  local conn = ffi.new(\"fq_client[?]\", 1);\n  local object = {}\n  local hooks = ffi.new(\"fq_hooks[?]\", 1);\n  hooks[0].version = ffi.C.FQ_HOOKS_V3;\n  hooks[0].sync = 1\n  hooks[0].unbind = nil\n  hooks[0].auth = function (c, err)\n    if object.auth_cb ~= nil then\n      object:auth_cb(err)\n    end\n    -- perform binds after auth has completed\n    for i,v in ipairs(binds) do\n      fq.fq_client_bind(c, v[0])\n    end\n  end\n  hooks[0].bind = function (c, breq)\n    if object.bind_cb ~= nil then\n      object:bind_cb(breq)\n    end\n  end\n  rv = fq.fq_client_init(conn, 0, nil)\n  fq.fq_client_hooks(conn[0], hooks)\n  fq.fq_client_creds(conn[0], host, port, user, pass)\n  fq.fq_client_heartbeat(conn[0], 1000);\n  fq.fq_client_set_backlog(conn[0], 10000, 100);\n  fq.fq_client_set_nonblock(conn[0], false);\n\n  object.conn = conn[0]\n\n  object.bind = function(object, exchange, program, flags)\n    local breq = ffi.new(\"fq_bind_req[?]\", 1)\n    ffi.copy(breq[0].exchange.name, exchange)\n    breq[0].exchange.len = exchange:len()\n    breq[0].flags = flags or fq.FQ_BIND_TRANS\n    local bind_program = charstar(program)\n    table.insert(bind_programs, bind_program)\n    breq[0].program = bind_program\n    table.insert(binds, breq)\n    return object\n  end\n\n  object.connect = function(object)\n    local rc = fq.fq_client_connect(object.conn);\n    if (rc == -1) then error(\"Connection failed\") end\n  end\n\n  object.close = function(object)\n    while fq.fq_client_data_backlog(object.conn) > 0 do\n      ffi.C.usleep(100)\n    end\n    fq.fq_client_destroy(object.conn)\n  end\n  \n  --- Recevie message if one is available, otherwise nil\n  object.recv = function(object)\n    jit.off()\n    local m = fq.fq_client_receive(object.conn)\n    if m ~= nil then -- need ~= nil check here.\n      local arrival_time = tonumber(m.arrival_time)\n      local payload      = ffi.string(m.payload, m.payload_len)\n      local route        = ffi.string(m.route.name, m.route.len)\n      local sender       = ffi.string(m.sender.name, m.sender.len)\n      local exchange     = ffi.string(m.exchange.name, m.exchange.len)\n      fq.fq_msg_deref(m)\n      return arrival_time, payload, route, sender, exchange\n    end\n    jit.on()\n  end\n  \n  object.listen_raw = function(object, callback)\n    -- poll on socket and execute callback when message is found\n    local sleep_micros     = 1\n    local sleep_micros_min = 2\n    local sleep_micros_max = 10E3\n    while true do\n      jit.off()\n      local m = fq.fq_client_receive(object.conn)\n      if m ~= nil then\n        callback(m)\n        fq.fq_msg_deref(m)\n        sleep_micros = 1\n      elseif sleep_micros < sleep_micros_max then\n        sleep_micros = sleep_micros * 2\n      end\n      if sleep_micros > sleep_micros_min then\n        ffi.C.usleep(sleep_micros)\n      end\n      jit.on()\n    end\n  end\n\n  object.listen = function(object, callback)\n    object:listen_raw(function(m) callback(ffi.string(m.payload)) end)\n  end\n\n  object.listen_table = function(object, callback)\n    object:listen_raw(function(m) callback(m2tab(m)) end)\n  end\n\n  object.send = function(object, message, exchange, route)\n    local cmsg = charstar(message)\n    local cexchange = charstar(exchange)\n    local croute = charstar(route)\n    local msg = fq.fq_msg_alloc(cmsg, string.len(message))\n    fq.fq_msg_exchange(msg, cexchange, string.len(exchange))\n    fq.fq_msg_route(msg, croute, string.len(route))\n\n    -- fq is set to be blocking so fq_client_publish will block\n    fq.fq_client_publish(object.conn, msg)\n    fq.fq_msg_deref(msg)\n  end\n\n  return object\nend\n\nreturn {\n  new = new,\n  usleep = ffi.C.usleep,\n  time = gettimeofday,\n}\n\n"
  },
  {
    "path": "lua/generatelua.sh",
    "content": "#!/usr/bin/env bash\n\nset -o errexit   # Exit script on first error.\nset -o nounset   # Treat references to unset variables as errors.\nset -o pipefail\n\nAWK='\nBEGIN { out=0 }\n/!lua start/ { out=1; next }\n/!lua stop/  { out=0; next }\n/^$/ {next} # skip whitespace\n/^#/ { while (/\\\\$/) { getline } next }\n(out == 1) { print }\n'\n\nFFI_HEAD='\nlocal ffi = require(\"ffi\")\nlocal fq = ffi.load(\"fq\")\nffi.cdef [[\nextern void usleep(int);\n\ntypedef long time_t;\n\ntypedef struct timeval {\n        time_t tv_sec;\n        time_t tv_usec;\n} timeval;\n\nint gettimeofday(struct timeval* t, void* tzp);\n\nstruct ck_stack_entry {\n\tstruct ck_stack_entry *next;\n};\ntypedef struct ck_stack_entry ck_stack_entry_t;\n\n// Those are defined in fqh as macros\nstatic const uint32_t FQ_PROTO_CMD_MODE  = 0xcc50cafe;\nstatic const uint32_t FQ_PROTO_DATA_MODE = 0xcc50face;\nstatic const uint32_t FQ_PROTO_PEER_MODE = 0xcc50fade;\nstatic const uint32_t FQ_PROTO_READ_STAT = 0x47455420;\nstatic const uint32_t FQ_PROTO_HTTP_GET  = 0x47455420;\nstatic const uint32_t FQ_PROTO_HTTP_PUT  = 0x50555420;\nstatic const uint32_t FQ_PROTO_HTTP_POST = 0x504f5354;\nstatic const uint32_t FQ_PROTO_HTTP_HEAD = 0x48454144;\n\nstatic const uint32_t FQ_BIND_PEER       = 0x00000001;\nstatic const uint32_t FQ_BIND_PERM       = 0x00000110;\nstatic const uint32_t FQ_BIND_TRANS      = 0x00000100;\n\nstatic const uint32_t FQ_PROTO_ERROR     = 0xeeee;\nstatic const uint32_t FQ_PROTO_AUTH_CMD  = 0xaaaa;\nstatic const uint32_t FQ_PROTO_AUTH_PLAIN = 0;\nstatic const uint32_t FQ_PROTO_AUTH_RESP = 0xaa00;\nstatic const uint32_t FQ_PROTO_HBREQ     = 0x4848;\nstatic const uint32_t FQ_PROTO_HB        = 0xbea7;\nstatic const uint32_t FQ_PROTO_BINDREQ   = 0xb170;\nstatic const uint32_t FQ_PROTO_BIND      = 0xb171;\nstatic const uint32_t FQ_PROTO_UNBINDREQ = 0x071b;\nstatic const uint32_t FQ_PROTO_UNBIND    = 0x171b;\nstatic const uint32_t FQ_PROTO_STATUS    = 0x57a7;\nstatic const uint32_t FQ_PROTO_STATUSREQ = 0xc7a7;\nstatic const uint32_t FQ_BIND_ILLEGAL = 0xffffffff;\n\nstatic const int MAX_RK_LEN = 127;\nstatic const int MAX_HOPS = 32;\nstatic const int FQ_HOOKS_V1 = 1;\nstatic const int FQ_HOOKS_V2 = 2;\nstatic const int FQ_HOOKS_V3 = 3;\n'\n\nFFI_TAIL=']]'\n\nprintf '%s\\n' \"$FFI_HEAD\" > fqclient.lua\ncat ../fq.h | awk \"$AWK\" | sed 's/MAX_RK_LEN/127/' >> fqclient.lua\nprintf '%s\\n' \"$FFI_TAIL\" >> fqclient.lua\ncat fqclient.lua.tail >> fqclient.lua\n"
  },
  {
    "path": "service-configs/50-circonus-fq.preset",
    "content": "# Ship disabled by default\ndisable circonus-fq.service\n"
  },
  {
    "path": "service-configs/circonus-fq.service",
    "content": "[Unit]\nDescription=FQ\nAfter=network.target\n\n[Service]\nUser=fq\nWorkingDirectory=/opt/circonus/var/lib/fq\nEnvironmentFile=-/opt/circonus/var/lib/fq/daemon_options\nExecStart=/opt/circonus/sbin/fqd -D ${DAEMON_OPTS}\n\n# Note that leaving the service as forking breaks restarts\nRestart=always\n\n[Install]\nWantedBy=multi-user.target\n"
  },
  {
    "path": "service-configs/daemon_options",
    "content": "# Additional fqd commandline arguments\nDAEMON_OPTS=\"\"\n"
  },
  {
    "path": "test/lua-support/init.lua",
    "content": "\n"
  },
  {
    "path": "test/run-tests.sh",
    "content": "cd \"$(dirname $0)\"\n\nLD_LIBRARY_PATH=\"../\"\nexport LD_LIBRARY_PATH\n\n/opt/circonus/bin/mtevbusted $@\n"
  },
  {
    "path": "test/test_spec.lua",
    "content": "local fqclient = require(\"../lua/fqclient.lua\")\n\nlocal function mkreader(exchange, program)\n  local key_auth = mtev.uuid()\n  local key_bind = mtev.uuid()\n  local key_read = mtev.uuid()\n  local fqc_read = fqclient.new(\"127.0.0.1\", 18765, \"busted-user-1\", \"busted-pw\")\n  fqc_read.auth_cb = function()\n    mtev.notify(key_auth, true)\n  end\n  fqc_read.bind_cb = function()\n    mtev.notify(key_bind, true)\n  end\n  fqc_read:bind(exchange, program) -- need to bind before connect\n  fqc_read:connect()\n  -- We have to call fq_read:recv() in a loop in order for call backs to execute.\n  mtev.coroutine_spawn(function()\n      while true do\n        local m = { fqc_read:recv() }\n        if #m > 0 then\n          mtev.log(\"debug\", \"RECV: %s\\n\", mtev.tojson(m):tostring())\n          mtev.notify(key_read, m[2]) -- just forward payload\n        else\n          mtev.sleep(.005)\n        end\n      end\n  end)\n  assert.truthy(mtev.waitfor(key_auth, 5))\n  assert.truthy(mtev.waitfor(key_bind, 5))\n  local reader = function(timeout)\n    local _, m = mtev.waitfor(key_read, timeout or 5)\n    return m\n  end\n  return reader\nend\n\ndescribe(\"fq\", function()\n\n  local fq, api\n\n  setup(function()\n      -- Setup fq process wrapper\n      fq = mtev.Proc:new {\n        path = \"../fqd\",\n        argv = {\n          \"fqd\", \"-D\",\n          '-n', '10.254.254.1',\n          '-c', './fqd.sqlite',\n          '-p', '18765',\n          '-v', 'conn,route,msg,io',\n        },\n        boot_match = \"Listening on port\",\n      }\n      -- write stderr output to out.log\n      fq:logwrite(\"out.log\")\n      -- Optional: Forward fqd output to error log\n      -- fq:loglog(\"error\")\n      api =  mtev.Api:http(\"127.0.0.1\", '18765')\n  end)\n\n  teardown(function()\n      fq:kill()\n  end)\n\n  it(\"should start\", function()\n       fq:start()\n       assert.truthy(fq:ready())\n  end)\n\n  it(\"should allow HTTP requests\", function()\n       assert.truthy(api:get(\"/stats.json\"):check())\n  end)\n\n  local fqc_send\n  local exchange = \"test-exchange\"\n  local program = \"prefix:\"\n  local route = \"test-route\"\n  local reader\n  it(\"should accept connections\", function()\n       fqc_send = fqclient.new(\"127.0.0.1\", 18765, \"busted-user-2\", \"busted-pw\")\n       fqc_send:connect()\n       reader = mkreader(exchange, \"prefix:\")\n  end)\n\n  it(\"should send/recv hello messages\", function()\n       local msg = \"Hello!\"\n       local N = 10\n       for i=1,N do\n         fqc_send:send(msg, exchange, route)\n       end\n       for i=1,N do\n         assert.equal(msg, reader())\n       end\n  end)\n\n  it(\"should send messages via HTTP\", function()\n       -- Submit message via HTTP\n       -- $ curl -X POST -H \"X-Fq-User: web\" -H 'X-Fq-Route: user-route' \\\n       --   -H 'X-Fq-Exchange: busted-exchange' 192.168.33.10:8765/submit \\\n       --   --data 'Hello world!'\n       -- {\"routed\":1,\"dropped\":0,\"no_route\":0,\"no_exchange\":0}\n       local payload = \"Some HTTP payload\"\n       r = api:post(\"/submit\", payload, {\n                      [\"X-Fq-User\"] = \"web\",\n                      [\"X-Fq-Route\"] = \"web-route\",\n                      [\"X-Fq-Exchange\"] = exchange,\n       }):check()\n       assert.equals(r:json().routed, 1)\n       assert.equals(payload, reader())\n  end)\n\n  it(\"should send messages via fqs\", function()\n       -- quick and dirty way to spin up fqs\n       mtev.sh(string.format(\n         [[printf 'hello fqs' | LD_LIBRARY_PATH=../:/opt/circonus/lib ../fqs -a 127.0.0.1:18765 -x \"%s\" -r \"%s\"]],\n         exchange, route))\n       assert.equals('hello fqs', reader())\n  end)\n\n  it(\"should allow multiple readers\", function()\n       local reader2 = mkreader(exchange, \"prefix:\")\n       local msg = \"hello reader 2!\"\n       fqc_send:send(msg, exchange, route)\n       assert.equals(msg, reader())\n       assert.equals(msg, reader2())\n  end)\n\n  it(\"should filter prefixes\", function()\n       local reader_x = mkreader(exchange, \"prefix:x\")\n       fqc_send:send(\"abc\", exchange, \"abc\")\n       fqc_send:send(\"xxx\", exchange, \"xxx\")\n       assert.equals(\"abc\", reader())\n       assert.equals(\"xxx\", reader())\n       assert.equals(\"xxx\", reader_x())\n  end)\n\nend)\n"
  },
  {
    "path": "web/css/bootstrap-theme.css",
    "content": ".btn-default,\n.btn-primary,\n.btn-success,\n.btn-info,\n.btn-warning,\n.btn-danger {\n  text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.2);\n  -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075);\n          box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075);\n}\n\n.btn-default:active,\n.btn-primary:active,\n.btn-success:active,\n.btn-info:active,\n.btn-warning:active,\n.btn-danger:active,\n.btn-default.active,\n.btn-primary.active,\n.btn-success.active,\n.btn-info.active,\n.btn-warning.active,\n.btn-danger.active {\n  -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n          box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n}\n\n.btn:active,\n.btn.active {\n  background-image: none;\n}\n\n.btn-default {\n  text-shadow: 0 1px 0 #fff;\n  background-image: -webkit-gradient(linear, left 0%, left 100%, from(#ffffff), to(#e6e6e6));\n  background-image: -webkit-linear-gradient(top, #ffffff, 0%, #e6e6e6, 100%);\n  background-image: -moz-linear-gradient(top, #ffffff 0%, #e6e6e6 100%);\n  background-image: linear-gradient(to bottom, #ffffff 0%, #e6e6e6 100%);\n  background-repeat: repeat-x;\n  border-color: #e0e0e0;\n  border-color: #ccc;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe6e6e6', GradientType=0);\n}\n\n.btn-default:active,\n.btn-default.active {\n  background-color: #e6e6e6;\n  border-color: #e0e0e0;\n}\n\n.btn-primary {\n  background-image: -webkit-gradient(linear, left 0%, left 100%, from(#428bca), to(#3071a9));\n  background-image: -webkit-linear-gradient(top, #428bca, 0%, #3071a9, 100%);\n  background-image: -moz-linear-gradient(top, #428bca 0%, #3071a9 100%);\n  background-image: linear-gradient(to bottom, #428bca 0%, #3071a9 100%);\n  background-repeat: repeat-x;\n  border-color: #2d6ca2;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3071a9', GradientType=0);\n}\n\n.btn-primary:active,\n.btn-primary.active {\n  background-color: #3071a9;\n  border-color: #2d6ca2;\n}\n\n.btn-success {\n  background-image: -webkit-gradient(linear, left 0%, left 100%, from(#5cb85c), to(#449d44));\n  background-image: -webkit-linear-gradient(top, #5cb85c, 0%, #449d44, 100%);\n  background-image: -moz-linear-gradient(top, #5cb85c 0%, #449d44 100%);\n  background-image: linear-gradient(to bottom, #5cb85c 0%, #449d44 100%);\n  background-repeat: repeat-x;\n  border-color: #419641;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);\n}\n\n.btn-success:active,\n.btn-success.active {\n  background-color: #449d44;\n  border-color: #419641;\n}\n\n.btn-warning {\n  background-image: -webkit-gradient(linear, left 0%, left 100%, from(#f0ad4e), to(#ec971f));\n  background-image: -webkit-linear-gradient(top, #f0ad4e, 0%, #ec971f, 100%);\n  background-image: -moz-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);\n  background-image: linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%);\n  background-repeat: repeat-x;\n  border-color: #eb9316;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);\n}\n\n.btn-warning:active,\n.btn-warning.active {\n  background-color: #ec971f;\n  border-color: #eb9316;\n}\n\n.btn-danger {\n  background-image: -webkit-gradient(linear, left 0%, left 100%, from(#d9534f), to(#c9302c));\n  background-image: -webkit-linear-gradient(top, #d9534f, 0%, #c9302c, 100%);\n  background-image: -moz-linear-gradient(top, #d9534f 0%, #c9302c 100%);\n  background-image: linear-gradient(to bottom, #d9534f 0%, #c9302c 100%);\n  background-repeat: repeat-x;\n  border-color: #c12e2a;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);\n}\n\n.btn-danger:active,\n.btn-danger.active {\n  background-color: #c9302c;\n  border-color: #c12e2a;\n}\n\n.btn-info {\n  background-image: -webkit-gradient(linear, left 0%, left 100%, from(#5bc0de), to(#31b0d5));\n  background-image: -webkit-linear-gradient(top, #5bc0de, 0%, #31b0d5, 100%);\n  background-image: -moz-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);\n  background-image: linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%);\n  background-repeat: repeat-x;\n  border-color: #2aabd2;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);\n}\n\n.btn-info:active,\n.btn-info.active {\n  background-color: #31b0d5;\n  border-color: #2aabd2;\n}\n\n.thumbnail,\n.img-thumbnail {\n  -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);\n          box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);\n}\n\n.dropdown-menu > li > a:hover,\n.dropdown-menu > li > a:focus,\n.dropdown-menu > .active > a,\n.dropdown-menu > .active > a:hover,\n.dropdown-menu > .active > a:focus {\n  background-color: #357ebd;\n  background-image: -webkit-gradient(linear, left 0%, left 100%, from(#428bca), to(#357ebd));\n  background-image: -webkit-linear-gradient(top, #428bca, 0%, #357ebd, 100%);\n  background-image: -moz-linear-gradient(top, #428bca 0%, #357ebd 100%);\n  background-image: linear-gradient(to bottom, #428bca 0%, #357ebd 100%);\n  background-repeat: repeat-x;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0);\n}\n\n.navbar {\n  background-image: -webkit-gradient(linear, left 0%, left 100%, from(#ffffff), to(#f8f8f8));\n  background-image: -webkit-linear-gradient(top, #ffffff, 0%, #f8f8f8, 100%);\n  background-image: -moz-linear-gradient(top, #ffffff 0%, #f8f8f8 100%);\n  background-image: linear-gradient(to bottom, #ffffff 0%, #f8f8f8 100%);\n  background-repeat: repeat-x;\n  border-radius: 4px;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);\n  -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 5px rgba(0, 0, 0, 0.075);\n          box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 5px rgba(0, 0, 0, 0.075);\n}\n\n.navbar .navbar-nav > .active > a {\n  background-color: #f8f8f8;\n}\n\n.navbar-brand,\n.navbar-nav > li > a {\n  text-shadow: 0 1px 0 rgba(255, 255, 255, 0.25);\n}\n\n.navbar-inverse {\n  background-image: -webkit-gradient(linear, left 0%, left 100%, from(#3c3c3c), to(#222222));\n  background-image: -webkit-linear-gradient(top, #3c3c3c, 0%, #222222, 100%);\n  background-image: -moz-linear-gradient(top, #3c3c3c 0%, #222222 100%);\n  background-image: linear-gradient(to bottom, #3c3c3c 0%, #222222 100%);\n  background-repeat: repeat-x;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);\n}\n\n.navbar-inverse .navbar-nav > .active > a {\n  background-color: #222222;\n}\n\n.navbar-inverse .navbar-brand,\n.navbar-inverse .navbar-nav > li > a {\n  text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);\n}\n\n.navbar-static-top,\n.navbar-fixed-top,\n.navbar-fixed-bottom {\n  border-radius: 0;\n}\n\n.alert {\n  text-shadow: 0 1px 0 rgba(255, 255, 255, 0.2);\n  -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 2px rgba(0, 0, 0, 0.05);\n          box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 2px rgba(0, 0, 0, 0.05);\n}\n\n.alert-success {\n  background-image: -webkit-gradient(linear, left 0%, left 100%, from(#dff0d8), to(#c8e5bc));\n  background-image: -webkit-linear-gradient(top, #dff0d8, 0%, #c8e5bc, 100%);\n  background-image: -moz-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);\n  background-image: linear-gradient(to bottom, #dff0d8 0%, #c8e5bc 100%);\n  background-repeat: repeat-x;\n  border-color: #b2dba1;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);\n}\n\n.alert-info {\n  background-image: -webkit-gradient(linear, left 0%, left 100%, from(#d9edf7), to(#b9def0));\n  background-image: -webkit-linear-gradient(top, #d9edf7, 0%, #b9def0, 100%);\n  background-image: -moz-linear-gradient(top, #d9edf7 0%, #b9def0 100%);\n  background-image: linear-gradient(to bottom, #d9edf7 0%, #b9def0 100%);\n  background-repeat: repeat-x;\n  border-color: #9acfea;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);\n}\n\n.alert-warning {\n  background-image: -webkit-gradient(linear, left 0%, left 100%, from(#fcf8e3), to(#f8efc0));\n  background-image: -webkit-linear-gradient(top, #fcf8e3, 0%, #f8efc0, 100%);\n  background-image: -moz-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);\n  background-image: linear-gradient(to bottom, #fcf8e3 0%, #f8efc0 100%);\n  background-repeat: repeat-x;\n  border-color: #f5e79e;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);\n}\n\n.alert-danger {\n  background-image: -webkit-gradient(linear, left 0%, left 100%, from(#f2dede), to(#e7c3c3));\n  background-image: -webkit-linear-gradient(top, #f2dede, 0%, #e7c3c3, 100%);\n  background-image: -moz-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);\n  background-image: linear-gradient(to bottom, #f2dede 0%, #e7c3c3 100%);\n  background-repeat: repeat-x;\n  border-color: #dca7a7;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);\n}\n\n.progress {\n  background-image: -webkit-gradient(linear, left 0%, left 100%, from(#ebebeb), to(#f5f5f5));\n  background-image: -webkit-linear-gradient(top, #ebebeb, 0%, #f5f5f5, 100%);\n  background-image: -moz-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);\n  background-image: linear-gradient(to bottom, #ebebeb 0%, #f5f5f5 100%);\n  background-repeat: repeat-x;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);\n}\n\n.progress-bar {\n  background-image: -webkit-gradient(linear, left 0%, left 100%, from(#428bca), to(#3071a9));\n  background-image: -webkit-linear-gradient(top, #428bca, 0%, #3071a9, 100%);\n  background-image: -moz-linear-gradient(top, #428bca 0%, #3071a9 100%);\n  background-image: linear-gradient(to bottom, #428bca 0%, #3071a9 100%);\n  background-repeat: repeat-x;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3071a9', GradientType=0);\n}\n\n.progress-bar-success {\n  background-image: -webkit-gradient(linear, left 0%, left 100%, from(#5cb85c), to(#449d44));\n  background-image: -webkit-linear-gradient(top, #5cb85c, 0%, #449d44, 100%);\n  background-image: -moz-linear-gradient(top, #5cb85c 0%, #449d44 100%);\n  background-image: linear-gradient(to bottom, #5cb85c 0%, #449d44 100%);\n  background-repeat: repeat-x;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);\n}\n\n.progress-bar-info {\n  background-image: -webkit-gradient(linear, left 0%, left 100%, from(#5bc0de), to(#31b0d5));\n  background-image: -webkit-linear-gradient(top, #5bc0de, 0%, #31b0d5, 100%);\n  background-image: -moz-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);\n  background-image: linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%);\n  background-repeat: repeat-x;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);\n}\n\n.progress-bar-warning {\n  background-image: -webkit-gradient(linear, left 0%, left 100%, from(#f0ad4e), to(#ec971f));\n  background-image: -webkit-linear-gradient(top, #f0ad4e, 0%, #ec971f, 100%);\n  background-image: -moz-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);\n  background-image: linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%);\n  background-repeat: repeat-x;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);\n}\n\n.progress-bar-danger {\n  background-image: -webkit-gradient(linear, left 0%, left 100%, from(#d9534f), to(#c9302c));\n  background-image: -webkit-linear-gradient(top, #d9534f, 0%, #c9302c, 100%);\n  background-image: -moz-linear-gradient(top, #d9534f 0%, #c9302c 100%);\n  background-image: linear-gradient(to bottom, #d9534f 0%, #c9302c 100%);\n  background-repeat: repeat-x;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);\n}\n\n.list-group {\n  border-radius: 4px;\n  -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);\n          box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);\n}\n\n.list-group-item.active,\n.list-group-item.active:hover,\n.list-group-item.active:focus {\n  text-shadow: 0 -1px 0 #3071a9;\n  background-image: -webkit-gradient(linear, left 0%, left 100%, from(#428bca), to(#3278b3));\n  background-image: -webkit-linear-gradient(top, #428bca, 0%, #3278b3, 100%);\n  background-image: -moz-linear-gradient(top, #428bca 0%, #3278b3 100%);\n  background-image: linear-gradient(to bottom, #428bca 0%, #3278b3 100%);\n  background-repeat: repeat-x;\n  border-color: #3278b3;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3278b3', GradientType=0);\n}\n\n.panel {\n  -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);\n          box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);\n}\n\n.panel-default > .panel-heading {\n  background-image: -webkit-gradient(linear, left 0%, left 100%, from(#f5f5f5), to(#e8e8e8));\n  background-image: -webkit-linear-gradient(top, #f5f5f5, 0%, #e8e8e8, 100%);\n  background-image: -moz-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);\n  background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);\n  background-repeat: repeat-x;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);\n}\n\n.panel-primary > .panel-heading {\n  background-image: -webkit-gradient(linear, left 0%, left 100%, from(#428bca), to(#357ebd));\n  background-image: -webkit-linear-gradient(top, #428bca, 0%, #357ebd, 100%);\n  background-image: -moz-linear-gradient(top, #428bca 0%, #357ebd 100%);\n  background-image: linear-gradient(to bottom, #428bca 0%, #357ebd 100%);\n  background-repeat: repeat-x;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0);\n}\n\n.panel-success > .panel-heading {\n  background-image: -webkit-gradient(linear, left 0%, left 100%, from(#dff0d8), to(#d0e9c6));\n  background-image: -webkit-linear-gradient(top, #dff0d8, 0%, #d0e9c6, 100%);\n  background-image: -moz-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);\n  background-image: linear-gradient(to bottom, #dff0d8 0%, #d0e9c6 100%);\n  background-repeat: repeat-x;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);\n}\n\n.panel-info > .panel-heading {\n  background-image: -webkit-gradient(linear, left 0%, left 100%, from(#d9edf7), to(#c4e3f3));\n  background-image: -webkit-linear-gradient(top, #d9edf7, 0%, #c4e3f3, 100%);\n  background-image: -moz-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);\n  background-image: linear-gradient(to bottom, #d9edf7 0%, #c4e3f3 100%);\n  background-repeat: repeat-x;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);\n}\n\n.panel-warning > .panel-heading {\n  background-image: -webkit-gradient(linear, left 0%, left 100%, from(#fcf8e3), to(#faf2cc));\n  background-image: -webkit-linear-gradient(top, #fcf8e3, 0%, #faf2cc, 100%);\n  background-image: -moz-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);\n  background-image: linear-gradient(to bottom, #fcf8e3 0%, #faf2cc 100%);\n  background-repeat: repeat-x;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);\n}\n\n.panel-danger > .panel-heading {\n  background-image: -webkit-gradient(linear, left 0%, left 100%, from(#f2dede), to(#ebcccc));\n  background-image: -webkit-linear-gradient(top, #f2dede, 0%, #ebcccc, 100%);\n  background-image: -moz-linear-gradient(top, #f2dede 0%, #ebcccc 100%);\n  background-image: linear-gradient(to bottom, #f2dede 0%, #ebcccc 100%);\n  background-repeat: repeat-x;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);\n}\n\n.well {\n  background-image: -webkit-gradient(linear, left 0%, left 100%, from(#e8e8e8), to(#f5f5f5));\n  background-image: -webkit-linear-gradient(top, #e8e8e8, 0%, #f5f5f5, 100%);\n  background-image: -moz-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);\n  background-image: linear-gradient(to bottom, #e8e8e8 0%, #f5f5f5 100%);\n  background-repeat: repeat-x;\n  border-color: #dcdcdc;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);\n  -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05), 0 1px 0 rgba(255, 255, 255, 0.1);\n          box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05), 0 1px 0 rgba(255, 255, 255, 0.1);\n}"
  },
  {
    "path": "web/css/bootstrap.css",
    "content": "/*!\n * Bootstrap v3.0.0\n *\n * Copyright 2013 Twitter, Inc\n * Licensed under the Apache License v2.0\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Designed and built with all the love in the world by @mdo and @fat.\n */\n\n/*! normalize.css v2.1.0 | MIT License | git.io/normalize */\n\narticle,\naside,\ndetails,\nfigcaption,\nfigure,\nfooter,\nheader,\nhgroup,\nmain,\nnav,\nsection,\nsummary {\n  display: block;\n}\n\naudio,\ncanvas,\nvideo {\n  display: inline-block;\n}\n\naudio:not([controls]) {\n  display: none;\n  height: 0;\n}\n\n[hidden] {\n  display: none;\n}\n\nhtml {\n  font-family: sans-serif;\n  -webkit-text-size-adjust: 100%;\n      -ms-text-size-adjust: 100%;\n}\n\nbody {\n  margin: 0;\n}\n\na:focus {\n  outline: thin dotted;\n}\n\na:active,\na:hover {\n  outline: 0;\n}\n\nh1 {\n  margin: 0.67em 0;\n  font-size: 2em;\n}\n\nabbr[title] {\n  border-bottom: 1px dotted;\n}\n\nb,\nstrong {\n  font-weight: bold;\n}\n\ndfn {\n  font-style: italic;\n}\n\nhr {\n  height: 0;\n  -moz-box-sizing: content-box;\n       box-sizing: content-box;\n}\n\nmark {\n  color: #000;\n  background: #ff0;\n}\n\ncode,\nkbd,\npre,\nsamp {\n  font-family: monospace, serif;\n  font-size: 1em;\n}\n\npre {\n  white-space: pre-wrap;\n}\n\nq {\n  quotes: \"\\201C\" \"\\201D\" \"\\2018\" \"\\2019\";\n}\n\nsmall {\n  font-size: 80%;\n}\n\nsub,\nsup {\n  position: relative;\n  font-size: 75%;\n  line-height: 0;\n  vertical-align: baseline;\n}\n\nsup {\n  top: -0.5em;\n}\n\nsub {\n  bottom: -0.25em;\n}\n\nimg {\n  border: 0;\n}\n\nsvg:not(:root) {\n  overflow: hidden;\n}\n\nfigure {\n  margin: 0;\n}\n\nfieldset {\n  padding: 0.35em 0.625em 0.75em;\n  margin: 0 2px;\n  border: 1px solid #c0c0c0;\n}\n\nlegend {\n  padding: 0;\n  border: 0;\n}\n\nbutton,\ninput,\nselect,\ntextarea {\n  margin: 0;\n  font-family: inherit;\n  font-size: 100%;\n}\n\nbutton,\ninput {\n  line-height: normal;\n}\n\nbutton,\nselect {\n  text-transform: none;\n}\n\nbutton,\nhtml input[type=\"button\"],\ninput[type=\"reset\"],\ninput[type=\"submit\"] {\n  cursor: pointer;\n  -webkit-appearance: button;\n}\n\nbutton[disabled],\nhtml input[disabled] {\n  cursor: default;\n}\n\ninput[type=\"checkbox\"],\ninput[type=\"radio\"] {\n  padding: 0;\n  box-sizing: border-box;\n}\n\ninput[type=\"search\"] {\n  -webkit-box-sizing: content-box;\n     -moz-box-sizing: content-box;\n          box-sizing: content-box;\n  -webkit-appearance: textfield;\n}\n\ninput[type=\"search\"]::-webkit-search-cancel-button,\ninput[type=\"search\"]::-webkit-search-decoration {\n  -webkit-appearance: none;\n}\n\nbutton::-moz-focus-inner,\ninput::-moz-focus-inner {\n  padding: 0;\n  border: 0;\n}\n\ntextarea {\n  overflow: auto;\n  vertical-align: top;\n}\n\ntable {\n  border-collapse: collapse;\n  border-spacing: 0;\n}\n\n@media print {\n  * {\n    color: #000 !important;\n    text-shadow: none !important;\n    background: transparent !important;\n    box-shadow: none !important;\n  }\n  a,\n  a:visited {\n    text-decoration: underline;\n  }\n  a[href]:after {\n    content: \" (\" attr(href) \")\";\n  }\n  abbr[title]:after {\n    content: \" (\" attr(title) \")\";\n  }\n  .ir a:after,\n  a[href^=\"javascript:\"]:after,\n  a[href^=\"#\"]:after {\n    content: \"\";\n  }\n  pre,\n  blockquote {\n    border: 1px solid #999;\n    page-break-inside: avoid;\n  }\n  thead {\n    display: table-header-group;\n  }\n  tr,\n  img {\n    page-break-inside: avoid;\n  }\n  img {\n    max-width: 100% !important;\n  }\n  @page  {\n    margin: 2cm .5cm;\n  }\n  p,\n  h2,\n  h3 {\n    orphans: 3;\n    widows: 3;\n  }\n  h2,\n  h3 {\n    page-break-after: avoid;\n  }\n  .navbar {\n    display: none;\n  }\n  .table td,\n  .table th {\n    background-color: #fff !important;\n  }\n  .btn > .caret,\n  .dropup > .btn > .caret {\n    border-top-color: #000 !important;\n  }\n  .label {\n    border: 1px solid #000;\n  }\n  .table {\n    border-collapse: collapse !important;\n  }\n  .table-bordered th,\n  .table-bordered td {\n    border: 1px solid #ddd !important;\n  }\n}\n\n*,\n*:before,\n*:after {\n  -webkit-box-sizing: border-box;\n     -moz-box-sizing: border-box;\n          box-sizing: border-box;\n}\n\nhtml {\n  font-size: 62.5%;\n  -webkit-tap-highlight-color: rgba(0, 0, 0, 0);\n}\n\nbody {\n  font-family: \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n  font-size: 14px;\n  line-height: 1.428571429;\n  color: #333333;\n  background-color: #ffffff;\n}\n\ninput,\nbutton,\nselect,\ntextarea {\n  font-family: inherit;\n  font-size: inherit;\n  line-height: inherit;\n}\n\nbutton,\ninput,\nselect[multiple],\ntextarea {\n  background-image: none;\n}\n\na {\n  color: #428bca;\n  text-decoration: none;\n}\n\na:hover,\na:focus {\n  color: #2a6496;\n  text-decoration: underline;\n}\n\na:focus {\n  outline: thin dotted #333;\n  outline: 5px auto -webkit-focus-ring-color;\n  outline-offset: -2px;\n}\n\nimg {\n  vertical-align: middle;\n}\n\n.img-responsive {\n  display: block;\n  height: auto;\n  max-width: 100%;\n}\n\n.img-rounded {\n  border-radius: 6px;\n}\n\n.img-thumbnail {\n  display: inline-block;\n  height: auto;\n  max-width: 100%;\n  padding: 4px;\n  line-height: 1.428571429;\n  background-color: #ffffff;\n  border: 1px solid #dddddd;\n  border-radius: 4px;\n  -webkit-transition: all 0.2s ease-in-out;\n          transition: all 0.2s ease-in-out;\n}\n\n.img-circle {\n  border-radius: 50%;\n}\n\nhr {\n  margin-top: 20px;\n  margin-bottom: 20px;\n  border: 0;\n  border-top: 1px solid #eeeeee;\n}\n\n.sr-only {\n  position: absolute;\n  width: 1px;\n  height: 1px;\n  padding: 0;\n  margin: -1px;\n  overflow: hidden;\n  clip: rect(0 0 0 0);\n  border: 0;\n}\n\np {\n  margin: 0 0 10px;\n}\n\n.lead {\n  margin-bottom: 20px;\n  font-size: 16.099999999999998px;\n  font-weight: 200;\n  line-height: 1.4;\n}\n\n@media (min-width: 768px) {\n  .lead {\n    font-size: 21px;\n  }\n}\n\nsmall {\n  font-size: 85%;\n}\n\ncite {\n  font-style: normal;\n}\n\n.text-muted {\n  color: #999999;\n}\n\n.text-primary {\n  color: #428bca;\n}\n\n.text-warning {\n  color: #c09853;\n}\n\n.text-danger {\n  color: #b94a48;\n}\n\n.text-success {\n  color: #468847;\n}\n\n.text-info {\n  color: #3a87ad;\n}\n\n.text-left {\n  text-align: left;\n}\n\n.text-right {\n  text-align: right;\n}\n\n.text-center {\n  text-align: center;\n}\n\nh1,\nh2,\nh3,\nh4,\nh5,\nh6,\n.h1,\n.h2,\n.h3,\n.h4,\n.h5,\n.h6 {\n  font-family: \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n  font-weight: 500;\n  line-height: 1.1;\n}\n\nh1 small,\nh2 small,\nh3 small,\nh4 small,\nh5 small,\nh6 small,\n.h1 small,\n.h2 small,\n.h3 small,\n.h4 small,\n.h5 small,\n.h6 small {\n  font-weight: normal;\n  line-height: 1;\n  color: #999999;\n}\n\nh1,\nh2,\nh3 {\n  margin-top: 20px;\n  margin-bottom: 10px;\n}\n\nh4,\nh5,\nh6 {\n  margin-top: 10px;\n  margin-bottom: 10px;\n}\n\nh1,\n.h1 {\n  font-size: 36px;\n}\n\nh2,\n.h2 {\n  font-size: 30px;\n}\n\nh3,\n.h3 {\n  font-size: 24px;\n}\n\nh4,\n.h4 {\n  font-size: 18px;\n}\n\nh5,\n.h5 {\n  font-size: 14px;\n}\n\nh6,\n.h6 {\n  font-size: 12px;\n}\n\nh1 small,\n.h1 small {\n  font-size: 24px;\n}\n\nh2 small,\n.h2 small {\n  font-size: 18px;\n}\n\nh3 small,\n.h3 small,\nh4 small,\n.h4 small {\n  font-size: 14px;\n}\n\n.page-header {\n  padding-bottom: 9px;\n  margin: 40px 0 20px;\n  border-bottom: 1px solid #eeeeee;\n}\n\nul,\nol {\n  margin-top: 0;\n  margin-bottom: 10px;\n}\n\nul ul,\nol ul,\nul ol,\nol ol {\n  margin-bottom: 0;\n}\n\n.list-unstyled {\n  padding-left: 0;\n  list-style: none;\n}\n\n.list-inline {\n  padding-left: 0;\n  list-style: none;\n}\n\n.list-inline > li {\n  display: inline-block;\n  padding-right: 5px;\n  padding-left: 5px;\n}\n\ndl {\n  margin-bottom: 20px;\n}\n\ndt,\ndd {\n  line-height: 1.428571429;\n}\n\ndt {\n  font-weight: bold;\n}\n\ndd {\n  margin-left: 0;\n}\n\n@media (min-width: 768px) {\n  .dl-horizontal dt {\n    float: left;\n    width: 160px;\n    overflow: hidden;\n    clear: left;\n    text-align: right;\n    text-overflow: ellipsis;\n    white-space: nowrap;\n  }\n  .dl-horizontal dd {\n    margin-left: 180px;\n  }\n  .dl-horizontal dd:before,\n  .dl-horizontal dd:after {\n    display: table;\n    content: \" \";\n  }\n  .dl-horizontal dd:after {\n    clear: both;\n  }\n  .dl-horizontal dd:before,\n  .dl-horizontal dd:after {\n    display: table;\n    content: \" \";\n  }\n  .dl-horizontal dd:after {\n    clear: both;\n  }\n}\n\nabbr[title],\nabbr[data-original-title] {\n  cursor: help;\n  border-bottom: 1px dotted #999999;\n}\n\nabbr.initialism {\n  font-size: 90%;\n  text-transform: uppercase;\n}\n\nblockquote {\n  padding: 10px 20px;\n  margin: 0 0 20px;\n  border-left: 5px solid #eeeeee;\n}\n\nblockquote p {\n  font-size: 17.5px;\n  font-weight: 300;\n  line-height: 1.25;\n}\n\nblockquote p:last-child {\n  margin-bottom: 0;\n}\n\nblockquote small {\n  display: block;\n  line-height: 1.428571429;\n  color: #999999;\n}\n\nblockquote small:before {\n  content: '\\2014 \\00A0';\n}\n\nblockquote.pull-right {\n  padding-right: 15px;\n  padding-left: 0;\n  border-right: 5px solid #eeeeee;\n  border-left: 0;\n}\n\nblockquote.pull-right p,\nblockquote.pull-right small {\n  text-align: right;\n}\n\nblockquote.pull-right small:before {\n  content: '';\n}\n\nblockquote.pull-right small:after {\n  content: '\\00A0 \\2014';\n}\n\nq:before,\nq:after,\nblockquote:before,\nblockquote:after {\n  content: \"\";\n}\n\naddress {\n  display: block;\n  margin-bottom: 20px;\n  font-style: normal;\n  line-height: 1.428571429;\n}\n\ncode,\npre {\n  font-family: Monaco, Menlo, Consolas, \"Courier New\", monospace;\n}\n\ncode {\n  padding: 2px 4px;\n  font-size: 90%;\n  color: #c7254e;\n  white-space: nowrap;\n  background-color: #f9f2f4;\n  border-radius: 4px;\n}\n\npre {\n  display: block;\n  padding: 9.5px;\n  margin: 0 0 10px;\n  font-size: 13px;\n  line-height: 1.428571429;\n  color: #333333;\n  word-break: break-all;\n  word-wrap: break-word;\n  background-color: #f5f5f5;\n  border: 1px solid #cccccc;\n  border-radius: 4px;\n}\n\npre.prettyprint {\n  margin-bottom: 20px;\n}\n\npre code {\n  padding: 0;\n  font-size: inherit;\n  color: inherit;\n  white-space: pre-wrap;\n  background-color: transparent;\n  border: 0;\n}\n\n.pre-scrollable {\n  max-height: 340px;\n  overflow-y: scroll;\n}\n\n.container {\n  padding-right: 15px;\n  padding-left: 15px;\n  margin-right: auto;\n  margin-left: auto;\n}\n\n.container:before,\n.container:after {\n  display: table;\n  content: \" \";\n}\n\n.container:after {\n  clear: both;\n}\n\n.container:before,\n.container:after {\n  display: table;\n  content: \" \";\n}\n\n.container:after {\n  clear: both;\n}\n\n.row {\n  margin-right: -15px;\n  margin-left: -15px;\n}\n\n.row:before,\n.row:after {\n  display: table;\n  content: \" \";\n}\n\n.row:after {\n  clear: both;\n}\n\n.row:before,\n.row:after {\n  display: table;\n  content: \" \";\n}\n\n.row:after {\n  clear: both;\n}\n\n.col-xs-1,\n.col-xs-2,\n.col-xs-3,\n.col-xs-4,\n.col-xs-5,\n.col-xs-6,\n.col-xs-7,\n.col-xs-8,\n.col-xs-9,\n.col-xs-10,\n.col-xs-11,\n.col-xs-12,\n.col-sm-1,\n.col-sm-2,\n.col-sm-3,\n.col-sm-4,\n.col-sm-5,\n.col-sm-6,\n.col-sm-7,\n.col-sm-8,\n.col-sm-9,\n.col-sm-10,\n.col-sm-11,\n.col-sm-12,\n.col-md-1,\n.col-md-2,\n.col-md-3,\n.col-md-4,\n.col-md-5,\n.col-md-6,\n.col-md-7,\n.col-md-8,\n.col-md-9,\n.col-md-10,\n.col-md-11,\n.col-md-12,\n.col-lg-1,\n.col-lg-2,\n.col-lg-3,\n.col-lg-4,\n.col-lg-5,\n.col-lg-6,\n.col-lg-7,\n.col-lg-8,\n.col-lg-9,\n.col-lg-10,\n.col-lg-11,\n.col-lg-12 {\n  position: relative;\n  min-height: 1px;\n  padding-right: 15px;\n  padding-left: 15px;\n}\n\n.col-xs-1,\n.col-xs-2,\n.col-xs-3,\n.col-xs-4,\n.col-xs-5,\n.col-xs-6,\n.col-xs-7,\n.col-xs-8,\n.col-xs-9,\n.col-xs-10,\n.col-xs-11 {\n  float: left;\n}\n\n.col-xs-1 {\n  width: 8.333333333333332%;\n}\n\n.col-xs-2 {\n  width: 16.666666666666664%;\n}\n\n.col-xs-3 {\n  width: 25%;\n}\n\n.col-xs-4 {\n  width: 33.33333333333333%;\n}\n\n.col-xs-5 {\n  width: 41.66666666666667%;\n}\n\n.col-xs-6 {\n  width: 50%;\n}\n\n.col-xs-7 {\n  width: 58.333333333333336%;\n}\n\n.col-xs-8 {\n  width: 66.66666666666666%;\n}\n\n.col-xs-9 {\n  width: 75%;\n}\n\n.col-xs-10 {\n  width: 83.33333333333334%;\n}\n\n.col-xs-11 {\n  width: 91.66666666666666%;\n}\n\n.col-xs-12 {\n  width: 100%;\n}\n\n@media (min-width: 768px) {\n  .container {\n    max-width: 750px;\n  }\n  .col-sm-1,\n  .col-sm-2,\n  .col-sm-3,\n  .col-sm-4,\n  .col-sm-5,\n  .col-sm-6,\n  .col-sm-7,\n  .col-sm-8,\n  .col-sm-9,\n  .col-sm-10,\n  .col-sm-11 {\n    float: left;\n  }\n  .col-sm-1 {\n    width: 8.333333333333332%;\n  }\n  .col-sm-2 {\n    width: 16.666666666666664%;\n  }\n  .col-sm-3 {\n    width: 25%;\n  }\n  .col-sm-4 {\n    width: 33.33333333333333%;\n  }\n  .col-sm-5 {\n    width: 41.66666666666667%;\n  }\n  .col-sm-6 {\n    width: 50%;\n  }\n  .col-sm-7 {\n    width: 58.333333333333336%;\n  }\n  .col-sm-8 {\n    width: 66.66666666666666%;\n  }\n  .col-sm-9 {\n    width: 75%;\n  }\n  .col-sm-10 {\n    width: 83.33333333333334%;\n  }\n  .col-sm-11 {\n    width: 91.66666666666666%;\n  }\n  .col-sm-12 {\n    width: 100%;\n  }\n  .col-sm-push-1 {\n    left: 8.333333333333332%;\n  }\n  .col-sm-push-2 {\n    left: 16.666666666666664%;\n  }\n  .col-sm-push-3 {\n    left: 25%;\n  }\n  .col-sm-push-4 {\n    left: 33.33333333333333%;\n  }\n  .col-sm-push-5 {\n    left: 41.66666666666667%;\n  }\n  .col-sm-push-6 {\n    left: 50%;\n  }\n  .col-sm-push-7 {\n    left: 58.333333333333336%;\n  }\n  .col-sm-push-8 {\n    left: 66.66666666666666%;\n  }\n  .col-sm-push-9 {\n    left: 75%;\n  }\n  .col-sm-push-10 {\n    left: 83.33333333333334%;\n  }\n  .col-sm-push-11 {\n    left: 91.66666666666666%;\n  }\n  .col-sm-pull-1 {\n    right: 8.333333333333332%;\n  }\n  .col-sm-pull-2 {\n    right: 16.666666666666664%;\n  }\n  .col-sm-pull-3 {\n    right: 25%;\n  }\n  .col-sm-pull-4 {\n    right: 33.33333333333333%;\n  }\n  .col-sm-pull-5 {\n    right: 41.66666666666667%;\n  }\n  .col-sm-pull-6 {\n    right: 50%;\n  }\n  .col-sm-pull-7 {\n    right: 58.333333333333336%;\n  }\n  .col-sm-pull-8 {\n    right: 66.66666666666666%;\n  }\n  .col-sm-pull-9 {\n    right: 75%;\n  }\n  .col-sm-pull-10 {\n    right: 83.33333333333334%;\n  }\n  .col-sm-pull-11 {\n    right: 91.66666666666666%;\n  }\n  .col-sm-offset-1 {\n    margin-left: 8.333333333333332%;\n  }\n  .col-sm-offset-2 {\n    margin-left: 16.666666666666664%;\n  }\n  .col-sm-offset-3 {\n    margin-left: 25%;\n  }\n  .col-sm-offset-4 {\n    margin-left: 33.33333333333333%;\n  }\n  .col-sm-offset-5 {\n    margin-left: 41.66666666666667%;\n  }\n  .col-sm-offset-6 {\n    margin-left: 50%;\n  }\n  .col-sm-offset-7 {\n    margin-left: 58.333333333333336%;\n  }\n  .col-sm-offset-8 {\n    margin-left: 66.66666666666666%;\n  }\n  .col-sm-offset-9 {\n    margin-left: 75%;\n  }\n  .col-sm-offset-10 {\n    margin-left: 83.33333333333334%;\n  }\n  .col-sm-offset-11 {\n    margin-left: 91.66666666666666%;\n  }\n}\n\n@media (min-width: 992px) {\n  .container {\n    max-width: 970px;\n  }\n  .col-md-1,\n  .col-md-2,\n  .col-md-3,\n  .col-md-4,\n  .col-md-5,\n  .col-md-6,\n  .col-md-7,\n  .col-md-8,\n  .col-md-9,\n  .col-md-10,\n  .col-md-11 {\n    float: left;\n  }\n  .col-md-1 {\n    width: 8.333333333333332%;\n  }\n  .col-md-2 {\n    width: 16.666666666666664%;\n  }\n  .col-md-3 {\n    width: 25%;\n  }\n  .col-md-4 {\n    width: 33.33333333333333%;\n  }\n  .col-md-5 {\n    width: 41.66666666666667%;\n  }\n  .col-md-6 {\n    width: 50%;\n  }\n  .col-md-7 {\n    width: 58.333333333333336%;\n  }\n  .col-md-8 {\n    width: 66.66666666666666%;\n  }\n  .col-md-9 {\n    width: 75%;\n  }\n  .col-md-10 {\n    width: 83.33333333333334%;\n  }\n  .col-md-11 {\n    width: 91.66666666666666%;\n  }\n  .col-md-12 {\n    width: 100%;\n  }\n  .col-md-push-0 {\n    left: auto;\n  }\n  .col-md-push-1 {\n    left: 8.333333333333332%;\n  }\n  .col-md-push-2 {\n    left: 16.666666666666664%;\n  }\n  .col-md-push-3 {\n    left: 25%;\n  }\n  .col-md-push-4 {\n    left: 33.33333333333333%;\n  }\n  .col-md-push-5 {\n    left: 41.66666666666667%;\n  }\n  .col-md-push-6 {\n    left: 50%;\n  }\n  .col-md-push-7 {\n    left: 58.333333333333336%;\n  }\n  .col-md-push-8 {\n    left: 66.66666666666666%;\n  }\n  .col-md-push-9 {\n    left: 75%;\n  }\n  .col-md-push-10 {\n    left: 83.33333333333334%;\n  }\n  .col-md-push-11 {\n    left: 91.66666666666666%;\n  }\n  .col-md-pull-0 {\n    right: auto;\n  }\n  .col-md-pull-1 {\n    right: 8.333333333333332%;\n  }\n  .col-md-pull-2 {\n    right: 16.666666666666664%;\n  }\n  .col-md-pull-3 {\n    right: 25%;\n  }\n  .col-md-pull-4 {\n    right: 33.33333333333333%;\n  }\n  .col-md-pull-5 {\n    right: 41.66666666666667%;\n  }\n  .col-md-pull-6 {\n    right: 50%;\n  }\n  .col-md-pull-7 {\n    right: 58.333333333333336%;\n  }\n  .col-md-pull-8 {\n    right: 66.66666666666666%;\n  }\n  .col-md-pull-9 {\n    right: 75%;\n  }\n  .col-md-pull-10 {\n    right: 83.33333333333334%;\n  }\n  .col-md-pull-11 {\n    right: 91.66666666666666%;\n  }\n  .col-md-offset-0 {\n    margin-left: 0;\n  }\n  .col-md-offset-1 {\n    margin-left: 8.333333333333332%;\n  }\n  .col-md-offset-2 {\n    margin-left: 16.666666666666664%;\n  }\n  .col-md-offset-3 {\n    margin-left: 25%;\n  }\n  .col-md-offset-4 {\n    margin-left: 33.33333333333333%;\n  }\n  .col-md-offset-5 {\n    margin-left: 41.66666666666667%;\n  }\n  .col-md-offset-6 {\n    margin-left: 50%;\n  }\n  .col-md-offset-7 {\n    margin-left: 58.333333333333336%;\n  }\n  .col-md-offset-8 {\n    margin-left: 66.66666666666666%;\n  }\n  .col-md-offset-9 {\n    margin-left: 75%;\n  }\n  .col-md-offset-10 {\n    margin-left: 83.33333333333334%;\n  }\n  .col-md-offset-11 {\n    margin-left: 91.66666666666666%;\n  }\n}\n\n@media (min-width: 1200px) {\n  .container {\n    max-width: 1170px;\n  }\n  .col-lg-1,\n  .col-lg-2,\n  .col-lg-3,\n  .col-lg-4,\n  .col-lg-5,\n  .col-lg-6,\n  .col-lg-7,\n  .col-lg-8,\n  .col-lg-9,\n  .col-lg-10,\n  .col-lg-11 {\n    float: left;\n  }\n  .col-lg-1 {\n    width: 8.333333333333332%;\n  }\n  .col-lg-2 {\n    width: 16.666666666666664%;\n  }\n  .col-lg-3 {\n    width: 25%;\n  }\n  .col-lg-4 {\n    width: 33.33333333333333%;\n  }\n  .col-lg-5 {\n    width: 41.66666666666667%;\n  }\n  .col-lg-6 {\n    width: 50%;\n  }\n  .col-lg-7 {\n    width: 58.333333333333336%;\n  }\n  .col-lg-8 {\n    width: 66.66666666666666%;\n  }\n  .col-lg-9 {\n    width: 75%;\n  }\n  .col-lg-10 {\n    width: 83.33333333333334%;\n  }\n  .col-lg-11 {\n    width: 91.66666666666666%;\n  }\n  .col-lg-12 {\n    width: 100%;\n  }\n  .col-lg-push-0 {\n    left: auto;\n  }\n  .col-lg-push-1 {\n    left: 8.333333333333332%;\n  }\n  .col-lg-push-2 {\n    left: 16.666666666666664%;\n  }\n  .col-lg-push-3 {\n    left: 25%;\n  }\n  .col-lg-push-4 {\n    left: 33.33333333333333%;\n  }\n  .col-lg-push-5 {\n    left: 41.66666666666667%;\n  }\n  .col-lg-push-6 {\n    left: 50%;\n  }\n  .col-lg-push-7 {\n    left: 58.333333333333336%;\n  }\n  .col-lg-push-8 {\n    left: 66.66666666666666%;\n  }\n  .col-lg-push-9 {\n    left: 75%;\n  }\n  .col-lg-push-10 {\n    left: 83.33333333333334%;\n  }\n  .col-lg-push-11 {\n    left: 91.66666666666666%;\n  }\n  .col-lg-pull-0 {\n    right: auto;\n  }\n  .col-lg-pull-1 {\n    right: 8.333333333333332%;\n  }\n  .col-lg-pull-2 {\n    right: 16.666666666666664%;\n  }\n  .col-lg-pull-3 {\n    right: 25%;\n  }\n  .col-lg-pull-4 {\n    right: 33.33333333333333%;\n  }\n  .col-lg-pull-5 {\n    right: 41.66666666666667%;\n  }\n  .col-lg-pull-6 {\n    right: 50%;\n  }\n  .col-lg-pull-7 {\n    right: 58.333333333333336%;\n  }\n  .col-lg-pull-8 {\n    right: 66.66666666666666%;\n  }\n  .col-lg-pull-9 {\n    right: 75%;\n  }\n  .col-lg-pull-10 {\n    right: 83.33333333333334%;\n  }\n  .col-lg-pull-11 {\n    right: 91.66666666666666%;\n  }\n  .col-lg-offset-0 {\n    margin-left: 0;\n  }\n  .col-lg-offset-1 {\n    margin-left: 8.333333333333332%;\n  }\n  .col-lg-offset-2 {\n    margin-left: 16.666666666666664%;\n  }\n  .col-lg-offset-3 {\n    margin-left: 25%;\n  }\n  .col-lg-offset-4 {\n    margin-left: 33.33333333333333%;\n  }\n  .col-lg-offset-5 {\n    margin-left: 41.66666666666667%;\n  }\n  .col-lg-offset-6 {\n    margin-left: 50%;\n  }\n  .col-lg-offset-7 {\n    margin-left: 58.333333333333336%;\n  }\n  .col-lg-offset-8 {\n    margin-left: 66.66666666666666%;\n  }\n  .col-lg-offset-9 {\n    margin-left: 75%;\n  }\n  .col-lg-offset-10 {\n    margin-left: 83.33333333333334%;\n  }\n  .col-lg-offset-11 {\n    margin-left: 91.66666666666666%;\n  }\n}\n\ntable {\n  max-width: 100%;\n  background-color: transparent;\n}\n\nth {\n  text-align: left;\n}\n\n.table {\n  width: 100%;\n  margin-bottom: 20px;\n}\n\n.table thead > tr > th,\n.table tbody > tr > th,\n.table tfoot > tr > th,\n.table thead > tr > td,\n.table tbody > tr > td,\n.table tfoot > tr > td {\n  padding: 8px;\n  line-height: 1.428571429;\n  vertical-align: top;\n  border-top: 1px solid #dddddd;\n}\n\n.table thead > tr > th {\n  vertical-align: bottom;\n  border-bottom: 2px solid #dddddd;\n}\n\n.table caption + thead tr:first-child th,\n.table colgroup + thead tr:first-child th,\n.table thead:first-child tr:first-child th,\n.table caption + thead tr:first-child td,\n.table colgroup + thead tr:first-child td,\n.table thead:first-child tr:first-child td {\n  border-top: 0;\n}\n\n.table tbody + tbody {\n  border-top: 2px solid #dddddd;\n}\n\n.table .table {\n  background-color: #ffffff;\n}\n\n.table-condensed thead > tr > th,\n.table-condensed tbody > tr > th,\n.table-condensed tfoot > tr > th,\n.table-condensed thead > tr > td,\n.table-condensed tbody > tr > td,\n.table-condensed tfoot > tr > td {\n  padding: 5px;\n}\n\n.table-bordered {\n  border: 1px solid #dddddd;\n}\n\n.table-bordered > thead > tr > th,\n.table-bordered > tbody > tr > th,\n.table-bordered > tfoot > tr > th,\n.table-bordered > thead > tr > td,\n.table-bordered > tbody > tr > td,\n.table-bordered > tfoot > tr > td {\n  border: 1px solid #dddddd;\n}\n\n.table-bordered > thead > tr > th,\n.table-bordered > thead > tr > td {\n  border-bottom-width: 2px;\n}\n\n.table-striped > tbody > tr:nth-child(odd) > td,\n.table-striped > tbody > tr:nth-child(odd) > th {\n  background-color: #f9f9f9;\n}\n\n.table-hover > tbody > tr:hover > td,\n.table-hover > tbody > tr:hover > th {\n  background-color: #f5f5f5;\n}\n\ntable col[class*=\"col-\"] {\n  display: table-column;\n  float: none;\n}\n\ntable td[class*=\"col-\"],\ntable th[class*=\"col-\"] {\n  display: table-cell;\n  float: none;\n}\n\n.table > thead > tr > td.active,\n.table > tbody > tr > td.active,\n.table > tfoot > tr > td.active,\n.table > thead > tr > th.active,\n.table > tbody > tr > th.active,\n.table > tfoot > tr > th.active,\n.table > thead > tr.active > td,\n.table > tbody > tr.active > td,\n.table > tfoot > tr.active > td,\n.table > thead > tr.active > th,\n.table > tbody > tr.active > th,\n.table > tfoot > tr.active > th {\n  background-color: #f5f5f5;\n}\n\n.table > thead > tr > td.success,\n.table > tbody > tr > td.success,\n.table > tfoot > tr > td.success,\n.table > thead > tr > th.success,\n.table > tbody > tr > th.success,\n.table > tfoot > tr > th.success,\n.table > thead > tr.success > td,\n.table > tbody > tr.success > td,\n.table > tfoot > tr.success > td,\n.table > thead > tr.success > th,\n.table > tbody > tr.success > th,\n.table > tfoot > tr.success > th {\n  background-color: #dff0d8;\n  border-color: #d6e9c6;\n}\n\n.table-hover > tbody > tr > td.success:hover,\n.table-hover > tbody > tr > th.success:hover,\n.table-hover > tbody > tr.success:hover > td {\n  background-color: #d0e9c6;\n  border-color: #c9e2b3;\n}\n\n.table > thead > tr > td.danger,\n.table > tbody > tr > td.danger,\n.table > tfoot > tr > td.danger,\n.table > thead > tr > th.danger,\n.table > tbody > tr > th.danger,\n.table > tfoot > tr > th.danger,\n.table > thead > tr.danger > td,\n.table > tbody > tr.danger > td,\n.table > tfoot > tr.danger > td,\n.table > thead > tr.danger > th,\n.table > tbody > tr.danger > th,\n.table > tfoot > tr.danger > th {\n  background-color: #f2dede;\n  border-color: #eed3d7;\n}\n\n.table-hover > tbody > tr > td.danger:hover,\n.table-hover > tbody > tr > th.danger:hover,\n.table-hover > tbody > tr.danger:hover > td {\n  background-color: #ebcccc;\n  border-color: #e6c1c7;\n}\n\n.table > thead > tr > td.warning,\n.table > tbody > tr > td.warning,\n.table > tfoot > tr > td.warning,\n.table > thead > tr > th.warning,\n.table > tbody > tr > th.warning,\n.table > tfoot > tr > th.warning,\n.table > thead > tr.warning > td,\n.table > tbody > tr.warning > td,\n.table > tfoot > tr.warning > td,\n.table > thead > tr.warning > th,\n.table > tbody > tr.warning > th,\n.table > tfoot > tr.warning > th {\n  background-color: #fcf8e3;\n  border-color: #fbeed5;\n}\n\n.table-hover > tbody > tr > td.warning:hover,\n.table-hover > tbody > tr > th.warning:hover,\n.table-hover > tbody > tr.warning:hover > td {\n  background-color: #faf2cc;\n  border-color: #f8e5be;\n}\n\n@media (max-width: 768px) {\n  .table-responsive {\n    width: 100%;\n    margin-bottom: 15px;\n    overflow-x: scroll;\n    overflow-y: hidden;\n    border: 1px solid #dddddd;\n  }\n  .table-responsive > .table {\n    margin-bottom: 0;\n    background-color: #fff;\n  }\n  .table-responsive > .table > thead > tr > th,\n  .table-responsive > .table > tbody > tr > th,\n  .table-responsive > .table > tfoot > tr > th,\n  .table-responsive > .table > thead > tr > td,\n  .table-responsive > .table > tbody > tr > td,\n  .table-responsive > .table > tfoot > tr > td {\n    white-space: nowrap;\n  }\n  .table-responsive > .table-bordered {\n    border: 0;\n  }\n  .table-responsive > .table-bordered > thead > tr > th:first-child,\n  .table-responsive > .table-bordered > tbody > tr > th:first-child,\n  .table-responsive > .table-bordered > tfoot > tr > th:first-child,\n  .table-responsive > .table-bordered > thead > tr > td:first-child,\n  .table-responsive > .table-bordered > tbody > tr > td:first-child,\n  .table-responsive > .table-bordered > tfoot > tr > td:first-child {\n    border-left: 0;\n  }\n  .table-responsive > .table-bordered > thead > tr > th:last-child,\n  .table-responsive > .table-bordered > tbody > tr > th:last-child,\n  .table-responsive > .table-bordered > tfoot > tr > th:last-child,\n  .table-responsive > .table-bordered > thead > tr > td:last-child,\n  .table-responsive > .table-bordered > tbody > tr > td:last-child,\n  .table-responsive > .table-bordered > tfoot > tr > td:last-child {\n    border-right: 0;\n  }\n  .table-responsive > .table-bordered > thead > tr:last-child > th,\n  .table-responsive > .table-bordered > tbody > tr:last-child > th,\n  .table-responsive > .table-bordered > tfoot > tr:last-child > th,\n  .table-responsive > .table-bordered > thead > tr:last-child > td,\n  .table-responsive > .table-bordered > tbody > tr:last-child > td,\n  .table-responsive > .table-bordered > tfoot > tr:last-child > td {\n    border-bottom: 0;\n  }\n}\n\nfieldset {\n  padding: 0;\n  margin: 0;\n  border: 0;\n}\n\nlegend {\n  display: block;\n  width: 100%;\n  padding: 0;\n  margin-bottom: 20px;\n  font-size: 21px;\n  line-height: inherit;\n  color: #333333;\n  border: 0;\n  border-bottom: 1px solid #e5e5e5;\n}\n\nlabel {\n  display: inline-block;\n  margin-bottom: 5px;\n  font-weight: bold;\n}\n\ninput[type=\"search\"] {\n  -webkit-box-sizing: border-box;\n     -moz-box-sizing: border-box;\n          box-sizing: border-box;\n}\n\ninput[type=\"radio\"],\ninput[type=\"checkbox\"] {\n  margin: 4px 0 0;\n  margin-top: 1px \\9;\n  /* IE8-9 */\n\n  line-height: normal;\n}\n\ninput[type=\"file\"] {\n  display: block;\n}\n\nselect[multiple],\nselect[size] {\n  height: auto;\n}\n\nselect optgroup {\n  font-family: inherit;\n  font-size: inherit;\n  font-style: inherit;\n}\n\ninput[type=\"file\"]:focus,\ninput[type=\"radio\"]:focus,\ninput[type=\"checkbox\"]:focus {\n  outline: thin dotted #333;\n  outline: 5px auto -webkit-focus-ring-color;\n  outline-offset: -2px;\n}\n\ninput[type=\"number\"]::-webkit-outer-spin-button,\ninput[type=\"number\"]::-webkit-inner-spin-button {\n  height: auto;\n}\n\n.form-control:-moz-placeholder {\n  color: #999999;\n}\n\n.form-control::-moz-placeholder {\n  color: #999999;\n}\n\n.form-control:-ms-input-placeholder {\n  color: #999999;\n}\n\n.form-control::-webkit-input-placeholder {\n  color: #999999;\n}\n\n.form-control {\n  display: block;\n  width: 100%;\n  height: 34px;\n  padding: 6px 12px;\n  font-size: 14px;\n  line-height: 1.428571429;\n  color: #555555;\n  vertical-align: middle;\n  background-color: #ffffff;\n  border: 1px solid #cccccc;\n  border-radius: 4px;\n  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n          box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n  -webkit-transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s;\n          transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s;\n}\n\n.form-control:focus {\n  border-color: #66afe9;\n  outline: 0;\n  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(102, 175, 233, 0.6);\n          box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(102, 175, 233, 0.6);\n}\n\n.form-control[disabled],\n.form-control[readonly],\nfieldset[disabled] .form-control {\n  cursor: not-allowed;\n  background-color: #eeeeee;\n}\n\ntextarea.form-control {\n  height: auto;\n}\n\n.form-group {\n  margin-bottom: 15px;\n}\n\n.radio,\n.checkbox {\n  display: block;\n  min-height: 20px;\n  padding-left: 20px;\n  margin-top: 10px;\n  margin-bottom: 10px;\n  vertical-align: middle;\n}\n\n.radio label,\n.checkbox label {\n  display: inline;\n  margin-bottom: 0;\n  font-weight: normal;\n  cursor: pointer;\n}\n\n.radio input[type=\"radio\"],\n.radio-inline input[type=\"radio\"],\n.checkbox input[type=\"checkbox\"],\n.checkbox-inline input[type=\"checkbox\"] {\n  float: left;\n  margin-left: -20px;\n}\n\n.radio + .radio,\n.checkbox + .checkbox {\n  margin-top: -5px;\n}\n\n.radio-inline,\n.checkbox-inline {\n  display: inline-block;\n  padding-left: 20px;\n  margin-bottom: 0;\n  font-weight: normal;\n  vertical-align: middle;\n  cursor: pointer;\n}\n\n.radio-inline + .radio-inline,\n.checkbox-inline + .checkbox-inline {\n  margin-top: 0;\n  margin-left: 10px;\n}\n\ninput[type=\"radio\"][disabled],\ninput[type=\"checkbox\"][disabled],\n.radio[disabled],\n.radio-inline[disabled],\n.checkbox[disabled],\n.checkbox-inline[disabled],\nfieldset[disabled] input[type=\"radio\"],\nfieldset[disabled] input[type=\"checkbox\"],\nfieldset[disabled] .radio,\nfieldset[disabled] .radio-inline,\nfieldset[disabled] .checkbox,\nfieldset[disabled] .checkbox-inline {\n  cursor: not-allowed;\n}\n\n.input-sm {\n  height: 30px;\n  padding: 5px 10px;\n  font-size: 12px;\n  line-height: 1.5;\n  border-radius: 3px;\n}\n\nselect.input-sm {\n  height: 30px;\n  line-height: 30px;\n}\n\ntextarea.input-sm {\n  height: auto;\n}\n\n.input-lg {\n  height: 45px;\n  padding: 10px 16px;\n  font-size: 18px;\n  line-height: 1.33;\n  border-radius: 6px;\n}\n\nselect.input-lg {\n  height: 45px;\n  line-height: 45px;\n}\n\ntextarea.input-lg {\n  height: auto;\n}\n\n.has-warning .help-block,\n.has-warning .control-label {\n  color: #c09853;\n}\n\n.has-warning .form-control {\n  border-color: #c09853;\n  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n          box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n}\n\n.has-warning .form-control:focus {\n  border-color: #a47e3c;\n  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #dbc59e;\n          box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #dbc59e;\n}\n\n.has-warning .input-group-addon {\n  color: #c09853;\n  background-color: #fcf8e3;\n  border-color: #c09853;\n}\n\n.has-error .help-block,\n.has-error .control-label {\n  color: #b94a48;\n}\n\n.has-error .form-control {\n  border-color: #b94a48;\n  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n          box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n}\n\n.has-error .form-control:focus {\n  border-color: #953b39;\n  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #d59392;\n          box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #d59392;\n}\n\n.has-error .input-group-addon {\n  color: #b94a48;\n  background-color: #f2dede;\n  border-color: #b94a48;\n}\n\n.has-success .help-block,\n.has-success .control-label {\n  color: #468847;\n}\n\n.has-success .form-control {\n  border-color: #468847;\n  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n          box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n}\n\n.has-success .form-control:focus {\n  border-color: #356635;\n  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7aba7b;\n          box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7aba7b;\n}\n\n.has-success .input-group-addon {\n  color: #468847;\n  background-color: #dff0d8;\n  border-color: #468847;\n}\n\n.form-control-static {\n  padding-top: 7px;\n  margin-bottom: 0;\n}\n\n.help-block {\n  display: block;\n  margin-top: 5px;\n  margin-bottom: 10px;\n  color: #737373;\n}\n\n@media (min-width: 768px) {\n  .form-inline .form-group {\n    display: inline-block;\n    margin-bottom: 0;\n    vertical-align: middle;\n  }\n  .form-inline .form-control {\n    display: inline-block;\n  }\n  .form-inline .radio,\n  .form-inline .checkbox {\n    display: inline-block;\n    padding-left: 0;\n    margin-top: 0;\n    margin-bottom: 0;\n  }\n  .form-inline .radio input[type=\"radio\"],\n  .form-inline .checkbox input[type=\"checkbox\"] {\n    float: none;\n    margin-left: 0;\n  }\n}\n\n.form-horizontal .control-label,\n.form-horizontal .radio,\n.form-horizontal .checkbox,\n.form-horizontal .radio-inline,\n.form-horizontal .checkbox-inline {\n  padding-top: 7px;\n  margin-top: 0;\n  margin-bottom: 0;\n}\n\n.form-horizontal .form-group {\n  margin-right: -15px;\n  margin-left: -15px;\n}\n\n.form-horizontal .form-group:before,\n.form-horizontal .form-group:after {\n  display: table;\n  content: \" \";\n}\n\n.form-horizontal .form-group:after {\n  clear: both;\n}\n\n.form-horizontal .form-group:before,\n.form-horizontal .form-group:after {\n  display: table;\n  content: \" \";\n}\n\n.form-horizontal .form-group:after {\n  clear: both;\n}\n\n@media (min-width: 768px) {\n  .form-horizontal .control-label {\n    text-align: right;\n  }\n}\n\n.btn {\n  display: inline-block;\n  padding: 6px 12px;\n  margin-bottom: 0;\n  font-size: 14px;\n  font-weight: normal;\n  line-height: 1.428571429;\n  text-align: center;\n  white-space: nowrap;\n  vertical-align: middle;\n  cursor: pointer;\n  border: 1px solid transparent;\n  border-radius: 4px;\n  -webkit-user-select: none;\n     -moz-user-select: none;\n      -ms-user-select: none;\n       -o-user-select: none;\n          user-select: none;\n}\n\n.btn:focus {\n  outline: thin dotted #333;\n  outline: 5px auto -webkit-focus-ring-color;\n  outline-offset: -2px;\n}\n\n.btn:hover,\n.btn:focus {\n  color: #333333;\n  text-decoration: none;\n}\n\n.btn:active,\n.btn.active {\n  background-image: none;\n  outline: 0;\n  -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n          box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n}\n\n.btn.disabled,\n.btn[disabled],\nfieldset[disabled] .btn {\n  pointer-events: none;\n  cursor: not-allowed;\n  opacity: 0.65;\n  filter: alpha(opacity=65);\n  -webkit-box-shadow: none;\n          box-shadow: none;\n}\n\n.btn-default {\n  color: #333333;\n  background-color: #ffffff;\n  border-color: #cccccc;\n}\n\n.btn-default:hover,\n.btn-default:focus,\n.btn-default:active,\n.btn-default.active,\n.open .dropdown-toggle.btn-default {\n  color: #333333;\n  background-color: #ebebeb;\n  border-color: #adadad;\n}\n\n.btn-default:active,\n.btn-default.active,\n.open .dropdown-toggle.btn-default {\n  background-image: none;\n}\n\n.btn-default.disabled,\n.btn-default[disabled],\nfieldset[disabled] .btn-default,\n.btn-default.disabled:hover,\n.btn-default[disabled]:hover,\nfieldset[disabled] .btn-default:hover,\n.btn-default.disabled:focus,\n.btn-default[disabled]:focus,\nfieldset[disabled] .btn-default:focus,\n.btn-default.disabled:active,\n.btn-default[disabled]:active,\nfieldset[disabled] .btn-default:active,\n.btn-default.disabled.active,\n.btn-default[disabled].active,\nfieldset[disabled] .btn-default.active {\n  background-color: #ffffff;\n  border-color: #cccccc;\n}\n\n.btn-primary {\n  color: #ffffff;\n  background-color: #428bca;\n  border-color: #357ebd;\n}\n\n.btn-primary:hover,\n.btn-primary:focus,\n.btn-primary:active,\n.btn-primary.active,\n.open .dropdown-toggle.btn-primary {\n  color: #ffffff;\n  background-color: #3276b1;\n  border-color: #285e8e;\n}\n\n.btn-primary:active,\n.btn-primary.active,\n.open .dropdown-toggle.btn-primary {\n  background-image: none;\n}\n\n.btn-primary.disabled,\n.btn-primary[disabled],\nfieldset[disabled] .btn-primary,\n.btn-primary.disabled:hover,\n.btn-primary[disabled]:hover,\nfieldset[disabled] .btn-primary:hover,\n.btn-primary.disabled:focus,\n.btn-primary[disabled]:focus,\nfieldset[disabled] .btn-primary:focus,\n.btn-primary.disabled:active,\n.btn-primary[disabled]:active,\nfieldset[disabled] .btn-primary:active,\n.btn-primary.disabled.active,\n.btn-primary[disabled].active,\nfieldset[disabled] .btn-primary.active {\n  background-color: #428bca;\n  border-color: #357ebd;\n}\n\n.btn-warning {\n  color: #ffffff;\n  background-color: #f0ad4e;\n  border-color: #eea236;\n}\n\n.btn-warning:hover,\n.btn-warning:focus,\n.btn-warning:active,\n.btn-warning.active,\n.open .dropdown-toggle.btn-warning {\n  color: #ffffff;\n  background-color: #ed9c28;\n  border-color: #d58512;\n}\n\n.btn-warning:active,\n.btn-warning.active,\n.open .dropdown-toggle.btn-warning {\n  background-image: none;\n}\n\n.btn-warning.disabled,\n.btn-warning[disabled],\nfieldset[disabled] .btn-warning,\n.btn-warning.disabled:hover,\n.btn-warning[disabled]:hover,\nfieldset[disabled] .btn-warning:hover,\n.btn-warning.disabled:focus,\n.btn-warning[disabled]:focus,\nfieldset[disabled] .btn-warning:focus,\n.btn-warning.disabled:active,\n.btn-warning[disabled]:active,\nfieldset[disabled] .btn-warning:active,\n.btn-warning.disabled.active,\n.btn-warning[disabled].active,\nfieldset[disabled] .btn-warning.active {\n  background-color: #f0ad4e;\n  border-color: #eea236;\n}\n\n.btn-danger {\n  color: #ffffff;\n  background-color: #d9534f;\n  border-color: #d43f3a;\n}\n\n.btn-danger:hover,\n.btn-danger:focus,\n.btn-danger:active,\n.btn-danger.active,\n.open .dropdown-toggle.btn-danger {\n  color: #ffffff;\n  background-color: #d2322d;\n  border-color: #ac2925;\n}\n\n.btn-danger:active,\n.btn-danger.active,\n.open .dropdown-toggle.btn-danger {\n  background-image: none;\n}\n\n.btn-danger.disabled,\n.btn-danger[disabled],\nfieldset[disabled] .btn-danger,\n.btn-danger.disabled:hover,\n.btn-danger[disabled]:hover,\nfieldset[disabled] .btn-danger:hover,\n.btn-danger.disabled:focus,\n.btn-danger[disabled]:focus,\nfieldset[disabled] .btn-danger:focus,\n.btn-danger.disabled:active,\n.btn-danger[disabled]:active,\nfieldset[disabled] .btn-danger:active,\n.btn-danger.disabled.active,\n.btn-danger[disabled].active,\nfieldset[disabled] .btn-danger.active {\n  background-color: #d9534f;\n  border-color: #d43f3a;\n}\n\n.btn-success {\n  color: #ffffff;\n  background-color: #5cb85c;\n  border-color: #4cae4c;\n}\n\n.btn-success:hover,\n.btn-success:focus,\n.btn-success:active,\n.btn-success.active,\n.open .dropdown-toggle.btn-success {\n  color: #ffffff;\n  background-color: #47a447;\n  border-color: #398439;\n}\n\n.btn-success:active,\n.btn-success.active,\n.open .dropdown-toggle.btn-success {\n  background-image: none;\n}\n\n.btn-success.disabled,\n.btn-success[disabled],\nfieldset[disabled] .btn-success,\n.btn-success.disabled:hover,\n.btn-success[disabled]:hover,\nfieldset[disabled] .btn-success:hover,\n.btn-success.disabled:focus,\n.btn-success[disabled]:focus,\nfieldset[disabled] .btn-success:focus,\n.btn-success.disabled:active,\n.btn-success[disabled]:active,\nfieldset[disabled] .btn-success:active,\n.btn-success.disabled.active,\n.btn-success[disabled].active,\nfieldset[disabled] .btn-success.active {\n  background-color: #5cb85c;\n  border-color: #4cae4c;\n}\n\n.btn-info {\n  color: #ffffff;\n  background-color: #5bc0de;\n  border-color: #46b8da;\n}\n\n.btn-info:hover,\n.btn-info:focus,\n.btn-info:active,\n.btn-info.active,\n.open .dropdown-toggle.btn-info {\n  color: #ffffff;\n  background-color: #39b3d7;\n  border-color: #269abc;\n}\n\n.btn-info:active,\n.btn-info.active,\n.open .dropdown-toggle.btn-info {\n  background-image: none;\n}\n\n.btn-info.disabled,\n.btn-info[disabled],\nfieldset[disabled] .btn-info,\n.btn-info.disabled:hover,\n.btn-info[disabled]:hover,\nfieldset[disabled] .btn-info:hover,\n.btn-info.disabled:focus,\n.btn-info[disabled]:focus,\nfieldset[disabled] .btn-info:focus,\n.btn-info.disabled:active,\n.btn-info[disabled]:active,\nfieldset[disabled] .btn-info:active,\n.btn-info.disabled.active,\n.btn-info[disabled].active,\nfieldset[disabled] .btn-info.active {\n  background-color: #5bc0de;\n  border-color: #46b8da;\n}\n\n.btn-link {\n  font-weight: normal;\n  color: #428bca;\n  cursor: pointer;\n  border-radius: 0;\n}\n\n.btn-link,\n.btn-link:active,\n.btn-link[disabled],\nfieldset[disabled] .btn-link {\n  background-color: transparent;\n  -webkit-box-shadow: none;\n          box-shadow: none;\n}\n\n.btn-link,\n.btn-link:hover,\n.btn-link:focus,\n.btn-link:active {\n  border-color: transparent;\n}\n\n.btn-link:hover,\n.btn-link:focus {\n  color: #2a6496;\n  text-decoration: underline;\n  background-color: transparent;\n}\n\n.btn-link[disabled]:hover,\nfieldset[disabled] .btn-link:hover,\n.btn-link[disabled]:focus,\nfieldset[disabled] .btn-link:focus {\n  color: #999999;\n  text-decoration: none;\n}\n\n.btn-lg {\n  padding: 10px 16px;\n  font-size: 18px;\n  line-height: 1.33;\n  border-radius: 6px;\n}\n\n.btn-sm,\n.btn-xs {\n  padding: 5px 10px;\n  font-size: 12px;\n  line-height: 1.5;\n  border-radius: 3px;\n}\n\n.btn-xs {\n  padding: 1px 5px;\n}\n\n.btn-block {\n  display: block;\n  width: 100%;\n  padding-right: 0;\n  padding-left: 0;\n}\n\n.btn-block + .btn-block {\n  margin-top: 5px;\n}\n\ninput[type=\"submit\"].btn-block,\ninput[type=\"reset\"].btn-block,\ninput[type=\"button\"].btn-block {\n  width: 100%;\n}\n\n.fade {\n  opacity: 0;\n  -webkit-transition: opacity 0.15s linear;\n          transition: opacity 0.15s linear;\n}\n\n.fade.in {\n  opacity: 1;\n}\n\n.collapse {\n  display: none;\n}\n\n.collapse.in {\n  display: block;\n}\n\n.collapsing {\n  position: relative;\n  height: 0;\n  overflow: hidden;\n  -webkit-transition: height 0.35s ease;\n          transition: height 0.35s ease;\n}\n\n@font-face {\n  font-family: 'Glyphicons Halflings';\n  src: url('../fonts/glyphicons-halflings-regular.eot');\n  src: url('../fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'), url('../fonts/glyphicons-halflings-regular.woff') format('woff'), url('../fonts/glyphicons-halflings-regular.ttf') format('truetype'), url('../fonts/glyphicons-halflings-regular.svg#glyphicons-halflingsregular') format('svg');\n}\n\n.glyphicon {\n  position: relative;\n  top: 1px;\n  display: inline-block;\n  font-family: 'Glyphicons Halflings';\n  -webkit-font-smoothing: antialiased;\n  font-style: normal;\n  font-weight: normal;\n  line-height: 1;\n}\n\n.glyphicon-asterisk:before {\n  content: \"\\2a\";\n}\n\n.glyphicon-plus:before {\n  content: \"\\2b\";\n}\n\n.glyphicon-euro:before {\n  content: \"\\20ac\";\n}\n\n.glyphicon-minus:before {\n  content: \"\\2212\";\n}\n\n.glyphicon-cloud:before {\n  content: \"\\2601\";\n}\n\n.glyphicon-envelope:before {\n  content: \"\\2709\";\n}\n\n.glyphicon-pencil:before {\n  content: \"\\270f\";\n}\n\n.glyphicon-glass:before {\n  content: \"\\e001\";\n}\n\n.glyphicon-music:before {\n  content: \"\\e002\";\n}\n\n.glyphicon-search:before {\n  content: \"\\e003\";\n}\n\n.glyphicon-heart:before {\n  content: \"\\e005\";\n}\n\n.glyphicon-star:before {\n  content: \"\\e006\";\n}\n\n.glyphicon-star-empty:before {\n  content: \"\\e007\";\n}\n\n.glyphicon-user:before {\n  content: \"\\e008\";\n}\n\n.glyphicon-film:before {\n  content: \"\\e009\";\n}\n\n.glyphicon-th-large:before {\n  content: \"\\e010\";\n}\n\n.glyphicon-th:before {\n  content: \"\\e011\";\n}\n\n.glyphicon-th-list:before {\n  content: \"\\e012\";\n}\n\n.glyphicon-ok:before {\n  content: \"\\e013\";\n}\n\n.glyphicon-remove:before {\n  content: \"\\e014\";\n}\n\n.glyphicon-zoom-in:before {\n  content: \"\\e015\";\n}\n\n.glyphicon-zoom-out:before {\n  content: \"\\e016\";\n}\n\n.glyphicon-off:before {\n  content: \"\\e017\";\n}\n\n.glyphicon-signal:before {\n  content: \"\\e018\";\n}\n\n.glyphicon-cog:before {\n  content: \"\\e019\";\n}\n\n.glyphicon-trash:before {\n  content: \"\\e020\";\n}\n\n.glyphicon-home:before {\n  content: \"\\e021\";\n}\n\n.glyphicon-file:before {\n  content: \"\\e022\";\n}\n\n.glyphicon-time:before {\n  content: \"\\e023\";\n}\n\n.glyphicon-road:before {\n  content: \"\\e024\";\n}\n\n.glyphicon-download-alt:before {\n  content: \"\\e025\";\n}\n\n.glyphicon-download:before {\n  content: \"\\e026\";\n}\n\n.glyphicon-upload:before {\n  content: \"\\e027\";\n}\n\n.glyphicon-inbox:before {\n  content: \"\\e028\";\n}\n\n.glyphicon-play-circle:before {\n  content: \"\\e029\";\n}\n\n.glyphicon-repeat:before {\n  content: \"\\e030\";\n}\n\n.glyphicon-refresh:before {\n  content: \"\\e031\";\n}\n\n.glyphicon-list-alt:before {\n  content: \"\\e032\";\n}\n\n.glyphicon-flag:before {\n  content: \"\\e034\";\n}\n\n.glyphicon-headphones:before {\n  content: \"\\e035\";\n}\n\n.glyphicon-volume-off:before {\n  content: \"\\e036\";\n}\n\n.glyphicon-volume-down:before {\n  content: \"\\e037\";\n}\n\n.glyphicon-volume-up:before {\n  content: \"\\e038\";\n}\n\n.glyphicon-qrcode:before {\n  content: \"\\e039\";\n}\n\n.glyphicon-barcode:before {\n  content: \"\\e040\";\n}\n\n.glyphicon-tag:before {\n  content: \"\\e041\";\n}\n\n.glyphicon-tags:before {\n  content: \"\\e042\";\n}\n\n.glyphicon-book:before {\n  content: \"\\e043\";\n}\n\n.glyphicon-print:before {\n  content: \"\\e045\";\n}\n\n.glyphicon-font:before {\n  content: \"\\e047\";\n}\n\n.glyphicon-bold:before {\n  content: \"\\e048\";\n}\n\n.glyphicon-italic:before {\n  content: \"\\e049\";\n}\n\n.glyphicon-text-height:before {\n  content: \"\\e050\";\n}\n\n.glyphicon-text-width:before {\n  content: \"\\e051\";\n}\n\n.glyphicon-align-left:before {\n  content: \"\\e052\";\n}\n\n.glyphicon-align-center:before {\n  content: \"\\e053\";\n}\n\n.glyphicon-align-right:before {\n  content: \"\\e054\";\n}\n\n.glyphicon-align-justify:before {\n  content: \"\\e055\";\n}\n\n.glyphicon-list:before {\n  content: \"\\e056\";\n}\n\n.glyphicon-indent-left:before {\n  content: \"\\e057\";\n}\n\n.glyphicon-indent-right:before {\n  content: \"\\e058\";\n}\n\n.glyphicon-facetime-video:before {\n  content: \"\\e059\";\n}\n\n.glyphicon-picture:before {\n  content: \"\\e060\";\n}\n\n.glyphicon-map-marker:before {\n  content: \"\\e062\";\n}\n\n.glyphicon-adjust:before {\n  content: \"\\e063\";\n}\n\n.glyphicon-tint:before {\n  content: \"\\e064\";\n}\n\n.glyphicon-edit:before {\n  content: \"\\e065\";\n}\n\n.glyphicon-share:before {\n  content: \"\\e066\";\n}\n\n.glyphicon-check:before {\n  content: \"\\e067\";\n}\n\n.glyphicon-move:before {\n  content: \"\\e068\";\n}\n\n.glyphicon-step-backward:before {\n  content: \"\\e069\";\n}\n\n.glyphicon-fast-backward:before {\n  content: \"\\e070\";\n}\n\n.glyphicon-backward:before {\n  content: \"\\e071\";\n}\n\n.glyphicon-play:before {\n  content: \"\\e072\";\n}\n\n.glyphicon-pause:before {\n  content: \"\\e073\";\n}\n\n.glyphicon-stop:before {\n  content: \"\\e074\";\n}\n\n.glyphicon-forward:before {\n  content: \"\\e075\";\n}\n\n.glyphicon-fast-forward:before {\n  content: \"\\e076\";\n}\n\n.glyphicon-step-forward:before {\n  content: \"\\e077\";\n}\n\n.glyphicon-eject:before {\n  content: \"\\e078\";\n}\n\n.glyphicon-chevron-left:before {\n  content: \"\\e079\";\n}\n\n.glyphicon-chevron-right:before {\n  content: \"\\e080\";\n}\n\n.glyphicon-plus-sign:before {\n  content: \"\\e081\";\n}\n\n.glyphicon-minus-sign:before {\n  content: \"\\e082\";\n}\n\n.glyphicon-remove-sign:before {\n  content: \"\\e083\";\n}\n\n.glyphicon-ok-sign:before {\n  content: \"\\e084\";\n}\n\n.glyphicon-question-sign:before {\n  content: \"\\e085\";\n}\n\n.glyphicon-info-sign:before {\n  content: \"\\e086\";\n}\n\n.glyphicon-screenshot:before {\n  content: \"\\e087\";\n}\n\n.glyphicon-remove-circle:before {\n  content: \"\\e088\";\n}\n\n.glyphicon-ok-circle:before {\n  content: \"\\e089\";\n}\n\n.glyphicon-ban-circle:before {\n  content: \"\\e090\";\n}\n\n.glyphicon-arrow-left:before {\n  content: \"\\e091\";\n}\n\n.glyphicon-arrow-right:before {\n  content: \"\\e092\";\n}\n\n.glyphicon-arrow-up:before {\n  content: \"\\e093\";\n}\n\n.glyphicon-arrow-down:before {\n  content: \"\\e094\";\n}\n\n.glyphicon-share-alt:before {\n  content: \"\\e095\";\n}\n\n.glyphicon-resize-full:before {\n  content: \"\\e096\";\n}\n\n.glyphicon-resize-small:before {\n  content: \"\\e097\";\n}\n\n.glyphicon-exclamation-sign:before {\n  content: \"\\e101\";\n}\n\n.glyphicon-gift:before {\n  content: \"\\e102\";\n}\n\n.glyphicon-leaf:before {\n  content: \"\\e103\";\n}\n\n.glyphicon-eye-open:before {\n  content: \"\\e105\";\n}\n\n.glyphicon-eye-close:before {\n  content: \"\\e106\";\n}\n\n.glyphicon-warning-sign:before {\n  content: \"\\e107\";\n}\n\n.glyphicon-plane:before {\n  content: \"\\e108\";\n}\n\n.glyphicon-random:before {\n  content: \"\\e110\";\n}\n\n.glyphicon-comment:before {\n  content: \"\\e111\";\n}\n\n.glyphicon-magnet:before {\n  content: \"\\e112\";\n}\n\n.glyphicon-chevron-up:before {\n  content: \"\\e113\";\n}\n\n.glyphicon-chevron-down:before {\n  content: \"\\e114\";\n}\n\n.glyphicon-retweet:before {\n  content: \"\\e115\";\n}\n\n.glyphicon-shopping-cart:before {\n  content: \"\\e116\";\n}\n\n.glyphicon-folder-close:before {\n  content: \"\\e117\";\n}\n\n.glyphicon-folder-open:before {\n  content: \"\\e118\";\n}\n\n.glyphicon-resize-vertical:before {\n  content: \"\\e119\";\n}\n\n.glyphicon-resize-horizontal:before {\n  content: \"\\e120\";\n}\n\n.glyphicon-hdd:before {\n  content: \"\\e121\";\n}\n\n.glyphicon-bullhorn:before {\n  content: \"\\e122\";\n}\n\n.glyphicon-certificate:before {\n  content: \"\\e124\";\n}\n\n.glyphicon-thumbs-up:before {\n  content: \"\\e125\";\n}\n\n.glyphicon-thumbs-down:before {\n  content: \"\\e126\";\n}\n\n.glyphicon-hand-right:before {\n  content: \"\\e127\";\n}\n\n.glyphicon-hand-left:before {\n  content: \"\\e128\";\n}\n\n.glyphicon-hand-up:before {\n  content: \"\\e129\";\n}\n\n.glyphicon-hand-down:before {\n  content: \"\\e130\";\n}\n\n.glyphicon-circle-arrow-right:before {\n  content: \"\\e131\";\n}\n\n.glyphicon-circle-arrow-left:before {\n  content: \"\\e132\";\n}\n\n.glyphicon-circle-arrow-up:before {\n  content: \"\\e133\";\n}\n\n.glyphicon-circle-arrow-down:before {\n  content: \"\\e134\";\n}\n\n.glyphicon-globe:before {\n  content: \"\\e135\";\n}\n\n.glyphicon-tasks:before {\n  content: \"\\e137\";\n}\n\n.glyphicon-filter:before {\n  content: \"\\e138\";\n}\n\n.glyphicon-fullscreen:before {\n  content: \"\\e140\";\n}\n\n.glyphicon-dashboard:before {\n  content: \"\\e141\";\n}\n\n.glyphicon-heart-empty:before {\n  content: \"\\e143\";\n}\n\n.glyphicon-link:before {\n  content: \"\\e144\";\n}\n\n.glyphicon-phone:before {\n  content: \"\\e145\";\n}\n\n.glyphicon-usd:before {\n  content: \"\\e148\";\n}\n\n.glyphicon-gbp:before {\n  content: \"\\e149\";\n}\n\n.glyphicon-sort:before {\n  content: \"\\e150\";\n}\n\n.glyphicon-sort-by-alphabet:before {\n  content: \"\\e151\";\n}\n\n.glyphicon-sort-by-alphabet-alt:before {\n  content: \"\\e152\";\n}\n\n.glyphicon-sort-by-order:before {\n  content: \"\\e153\";\n}\n\n.glyphicon-sort-by-order-alt:before {\n  content: \"\\e154\";\n}\n\n.glyphicon-sort-by-attributes:before {\n  content: \"\\e155\";\n}\n\n.glyphicon-sort-by-attributes-alt:before {\n  content: \"\\e156\";\n}\n\n.glyphicon-unchecked:before {\n  content: \"\\e157\";\n}\n\n.glyphicon-expand:before {\n  content: \"\\e158\";\n}\n\n.glyphicon-collapse-down:before {\n  content: \"\\e159\";\n}\n\n.glyphicon-collapse-up:before {\n  content: \"\\e160\";\n}\n\n.glyphicon-log-in:before {\n  content: \"\\e161\";\n}\n\n.glyphicon-flash:before {\n  content: \"\\e162\";\n}\n\n.glyphicon-log-out:before {\n  content: \"\\e163\";\n}\n\n.glyphicon-new-window:before {\n  content: \"\\e164\";\n}\n\n.glyphicon-record:before {\n  content: \"\\e165\";\n}\n\n.glyphicon-save:before {\n  content: \"\\e166\";\n}\n\n.glyphicon-open:before {\n  content: \"\\e167\";\n}\n\n.glyphicon-saved:before {\n  content: \"\\e168\";\n}\n\n.glyphicon-import:before {\n  content: \"\\e169\";\n}\n\n.glyphicon-export:before {\n  content: \"\\e170\";\n}\n\n.glyphicon-send:before {\n  content: \"\\e171\";\n}\n\n.glyphicon-floppy-disk:before {\n  content: \"\\e172\";\n}\n\n.glyphicon-floppy-saved:before {\n  content: \"\\e173\";\n}\n\n.glyphicon-floppy-remove:before {\n  content: \"\\e174\";\n}\n\n.glyphicon-floppy-save:before {\n  content: \"\\e175\";\n}\n\n.glyphicon-floppy-open:before {\n  content: \"\\e176\";\n}\n\n.glyphicon-credit-card:before {\n  content: \"\\e177\";\n}\n\n.glyphicon-transfer:before {\n  content: \"\\e178\";\n}\n\n.glyphicon-cutlery:before {\n  content: \"\\e179\";\n}\n\n.glyphicon-header:before {\n  content: \"\\e180\";\n}\n\n.glyphicon-compressed:before {\n  content: \"\\e181\";\n}\n\n.glyphicon-earphone:before {\n  content: \"\\e182\";\n}\n\n.glyphicon-phone-alt:before {\n  content: \"\\e183\";\n}\n\n.glyphicon-tower:before {\n  content: \"\\e184\";\n}\n\n.glyphicon-stats:before {\n  content: \"\\e185\";\n}\n\n.glyphicon-sd-video:before {\n  content: \"\\e186\";\n}\n\n.glyphicon-hd-video:before {\n  content: \"\\e187\";\n}\n\n.glyphicon-subtitles:before {\n  content: \"\\e188\";\n}\n\n.glyphicon-sound-stereo:before {\n  content: \"\\e189\";\n}\n\n.glyphicon-sound-dolby:before {\n  content: \"\\e190\";\n}\n\n.glyphicon-sound-5-1:before {\n  content: \"\\e191\";\n}\n\n.glyphicon-sound-6-1:before {\n  content: \"\\e192\";\n}\n\n.glyphicon-sound-7-1:before {\n  content: \"\\e193\";\n}\n\n.glyphicon-copyright-mark:before {\n  content: \"\\e194\";\n}\n\n.glyphicon-registration-mark:before {\n  content: \"\\e195\";\n}\n\n.glyphicon-cloud-download:before {\n  content: \"\\e197\";\n}\n\n.glyphicon-cloud-upload:before {\n  content: \"\\e198\";\n}\n\n.glyphicon-tree-conifer:before {\n  content: \"\\e199\";\n}\n\n.glyphicon-tree-deciduous:before {\n  content: \"\\e200\";\n}\n\n.glyphicon-briefcase:before {\n  content: \"\\1f4bc\";\n}\n\n.glyphicon-calendar:before {\n  content: \"\\1f4c5\";\n}\n\n.glyphicon-pushpin:before {\n  content: \"\\1f4cc\";\n}\n\n.glyphicon-paperclip:before {\n  content: \"\\1f4ce\";\n}\n\n.glyphicon-camera:before {\n  content: \"\\1f4f7\";\n}\n\n.glyphicon-lock:before {\n  content: \"\\1f512\";\n}\n\n.glyphicon-bell:before {\n  content: \"\\1f514\";\n}\n\n.glyphicon-bookmark:before {\n  content: \"\\1f516\";\n}\n\n.glyphicon-fire:before {\n  content: \"\\1f525\";\n}\n\n.glyphicon-wrench:before {\n  content: \"\\1f527\";\n}\n\n.caret {\n  display: inline-block;\n  width: 0;\n  height: 0;\n  margin-left: 2px;\n  vertical-align: middle;\n  border-top: 4px solid #000000;\n  border-right: 4px solid transparent;\n  border-bottom: 0 dotted;\n  border-left: 4px solid transparent;\n  content: \"\";\n}\n\n.dropdown {\n  position: relative;\n}\n\n.dropdown-toggle:focus {\n  outline: 0;\n}\n\n.dropdown-menu {\n  position: absolute;\n  top: 100%;\n  left: 0;\n  z-index: 1000;\n  display: none;\n  float: left;\n  min-width: 160px;\n  padding: 5px 0;\n  margin: 2px 0 0;\n  font-size: 14px;\n  list-style: none;\n  background-color: #ffffff;\n  border: 1px solid #cccccc;\n  border: 1px solid rgba(0, 0, 0, 0.15);\n  border-radius: 4px;\n  -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);\n          box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);\n  background-clip: padding-box;\n}\n\n.dropdown-menu.pull-right {\n  right: 0;\n  left: auto;\n}\n\n.dropdown-menu .divider {\n  height: 1px;\n  margin: 9px 0;\n  overflow: hidden;\n  background-color: #e5e5e5;\n}\n\n.dropdown-menu > li > a {\n  display: block;\n  padding: 3px 20px;\n  clear: both;\n  font-weight: normal;\n  line-height: 1.428571429;\n  color: #333333;\n  white-space: nowrap;\n}\n\n.dropdown-menu > li > a:hover,\n.dropdown-menu > li > a:focus {\n  color: #ffffff;\n  text-decoration: none;\n  background-color: #428bca;\n}\n\n.dropdown-menu > .active > a,\n.dropdown-menu > .active > a:hover,\n.dropdown-menu > .active > a:focus {\n  color: #ffffff;\n  text-decoration: none;\n  background-color: #428bca;\n  outline: 0;\n}\n\n.dropdown-menu > .disabled > a,\n.dropdown-menu > .disabled > a:hover,\n.dropdown-menu > .disabled > a:focus {\n  color: #999999;\n}\n\n.dropdown-menu > .disabled > a:hover,\n.dropdown-menu > .disabled > a:focus {\n  text-decoration: none;\n  cursor: not-allowed;\n  background-color: transparent;\n  background-image: none;\n  filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);\n}\n\n.open > .dropdown-menu {\n  display: block;\n}\n\n.open > a {\n  outline: 0;\n}\n\n.dropdown-header {\n  display: block;\n  padding: 3px 20px;\n  font-size: 12px;\n  line-height: 1.428571429;\n  color: #999999;\n}\n\n.dropdown-backdrop {\n  position: fixed;\n  top: 0;\n  right: 0;\n  bottom: 0;\n  left: 0;\n  z-index: 990;\n}\n\n.pull-right > .dropdown-menu {\n  right: 0;\n  left: auto;\n}\n\n.dropup .caret,\n.navbar-fixed-bottom .dropdown .caret {\n  border-top: 0 dotted;\n  border-bottom: 4px solid #000000;\n  content: \"\";\n}\n\n.dropup .dropdown-menu,\n.navbar-fixed-bottom .dropdown .dropdown-menu {\n  top: auto;\n  bottom: 100%;\n  margin-bottom: 1px;\n}\n\n@media (min-width: 768px) {\n  .navbar-right .dropdown-menu {\n    right: 0;\n    left: auto;\n  }\n}\n\n.btn-default .caret {\n  border-top-color: #333333;\n}\n\n.btn-primary .caret,\n.btn-success .caret,\n.btn-warning .caret,\n.btn-danger .caret,\n.btn-info .caret {\n  border-top-color: #fff;\n}\n\n.dropup .btn-default .caret {\n  border-bottom-color: #333333;\n}\n\n.dropup .btn-primary .caret,\n.dropup .btn-success .caret,\n.dropup .btn-warning .caret,\n.dropup .btn-danger .caret,\n.dropup .btn-info .caret {\n  border-bottom-color: #fff;\n}\n\n.btn-group,\n.btn-group-vertical {\n  position: relative;\n  display: inline-block;\n  vertical-align: middle;\n}\n\n.btn-group > .btn,\n.btn-group-vertical > .btn {\n  position: relative;\n  float: left;\n}\n\n.btn-group > .btn:hover,\n.btn-group-vertical > .btn:hover,\n.btn-group > .btn:focus,\n.btn-group-vertical > .btn:focus,\n.btn-group > .btn:active,\n.btn-group-vertical > .btn:active,\n.btn-group > .btn.active,\n.btn-group-vertical > .btn.active {\n  z-index: 2;\n}\n\n.btn-group > .btn:focus,\n.btn-group-vertical > .btn:focus {\n  outline: none;\n}\n\n.btn-group .btn + .btn,\n.btn-group .btn + .btn-group,\n.btn-group .btn-group + .btn,\n.btn-group .btn-group + .btn-group {\n  margin-left: -1px;\n}\n\n.btn-toolbar:before,\n.btn-toolbar:after {\n  display: table;\n  content: \" \";\n}\n\n.btn-toolbar:after {\n  clear: both;\n}\n\n.btn-toolbar:before,\n.btn-toolbar:after {\n  display: table;\n  content: \" \";\n}\n\n.btn-toolbar:after {\n  clear: both;\n}\n\n.btn-toolbar .btn-group {\n  float: left;\n}\n\n.btn-toolbar > .btn + .btn,\n.btn-toolbar > .btn-group + .btn,\n.btn-toolbar > .btn + .btn-group,\n.btn-toolbar > .btn-group + .btn-group {\n  margin-left: 5px;\n}\n\n.btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) {\n  border-radius: 0;\n}\n\n.btn-group > .btn:first-child {\n  margin-left: 0;\n}\n\n.btn-group > .btn:first-child:not(:last-child):not(.dropdown-toggle) {\n  border-top-right-radius: 0;\n  border-bottom-right-radius: 0;\n}\n\n.btn-group > .btn:last-child:not(:first-child),\n.btn-group > .dropdown-toggle:not(:first-child) {\n  border-bottom-left-radius: 0;\n  border-top-left-radius: 0;\n}\n\n.btn-group > .btn-group {\n  float: left;\n}\n\n.btn-group > .btn-group:not(:first-child):not(:last-child) > .btn {\n  border-radius: 0;\n}\n\n.btn-group > .btn-group:first-child > .btn:last-child,\n.btn-group > .btn-group:first-child > .dropdown-toggle {\n  border-top-right-radius: 0;\n  border-bottom-right-radius: 0;\n}\n\n.btn-group > .btn-group:last-child > .btn:first-child {\n  border-bottom-left-radius: 0;\n  border-top-left-radius: 0;\n}\n\n.btn-group .dropdown-toggle:active,\n.btn-group.open .dropdown-toggle {\n  outline: 0;\n}\n\n.btn-group-xs > .btn {\n  padding: 5px 10px;\n  padding: 1px 5px;\n  font-size: 12px;\n  line-height: 1.5;\n  border-radius: 3px;\n}\n\n.btn-group-sm > .btn {\n  padding: 5px 10px;\n  font-size: 12px;\n  line-height: 1.5;\n  border-radius: 3px;\n}\n\n.btn-group-lg > .btn {\n  padding: 10px 16px;\n  font-size: 18px;\n  line-height: 1.33;\n  border-radius: 6px;\n}\n\n.btn-group > .btn + .dropdown-toggle {\n  padding-right: 8px;\n  padding-left: 8px;\n}\n\n.btn-group > .btn-lg + .dropdown-toggle {\n  padding-right: 12px;\n  padding-left: 12px;\n}\n\n.btn-group.open .dropdown-toggle {\n  -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n          box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n}\n\n.btn .caret {\n  margin-left: 0;\n}\n\n.btn-lg .caret {\n  border-width: 5px 5px 0;\n  border-bottom-width: 0;\n}\n\n.dropup .btn-lg .caret {\n  border-width: 0 5px 5px;\n}\n\n.btn-group-vertical > .btn,\n.btn-group-vertical > .btn-group {\n  display: block;\n  float: none;\n  width: 100%;\n  max-width: 100%;\n}\n\n.btn-group-vertical > .btn-group:before,\n.btn-group-vertical > .btn-group:after {\n  display: table;\n  content: \" \";\n}\n\n.btn-group-vertical > .btn-group:after {\n  clear: both;\n}\n\n.btn-group-vertical > .btn-group:before,\n.btn-group-vertical > .btn-group:after {\n  display: table;\n  content: \" \";\n}\n\n.btn-group-vertical > .btn-group:after {\n  clear: both;\n}\n\n.btn-group-vertical > .btn-group > .btn {\n  float: none;\n}\n\n.btn-group-vertical > .btn + .btn,\n.btn-group-vertical > .btn + .btn-group,\n.btn-group-vertical > .btn-group + .btn,\n.btn-group-vertical > .btn-group + .btn-group {\n  margin-top: -1px;\n  margin-left: 0;\n}\n\n.btn-group-vertical > .btn:not(:first-child):not(:last-child) {\n  border-radius: 0;\n}\n\n.btn-group-vertical > .btn:first-child:not(:last-child) {\n  border-top-right-radius: 4px;\n  border-bottom-right-radius: 0;\n  border-bottom-left-radius: 0;\n}\n\n.btn-group-vertical > .btn:last-child:not(:first-child) {\n  border-top-right-radius: 0;\n  border-bottom-left-radius: 4px;\n  border-top-left-radius: 0;\n}\n\n.btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn {\n  border-radius: 0;\n}\n\n.btn-group-vertical > .btn-group:first-child > .btn:last-child,\n.btn-group-vertical > .btn-group:first-child > .dropdown-toggle {\n  border-bottom-right-radius: 0;\n  border-bottom-left-radius: 0;\n}\n\n.btn-group-vertical > .btn-group:last-child > .btn:first-child {\n  border-top-right-radius: 0;\n  border-top-left-radius: 0;\n}\n\n.btn-group-justified {\n  display: table;\n  width: 100%;\n  border-collapse: separate;\n  table-layout: fixed;\n}\n\n.btn-group-justified .btn {\n  display: table-cell;\n  float: none;\n  width: 1%;\n}\n\n[data-toggle=\"buttons\"] > .btn > input[type=\"radio\"],\n[data-toggle=\"buttons\"] > .btn > input[type=\"checkbox\"] {\n  display: none;\n}\n\n.input-group {\n  position: relative;\n  display: table;\n  border-collapse: separate;\n}\n\n.input-group.col {\n  float: none;\n  padding-right: 0;\n  padding-left: 0;\n}\n\n.input-group .form-control {\n  width: 100%;\n  margin-bottom: 0;\n}\n\n.input-group-lg > .form-control,\n.input-group-lg > .input-group-addon,\n.input-group-lg > .input-group-btn > .btn {\n  height: 45px;\n  padding: 10px 16px;\n  font-size: 18px;\n  line-height: 1.33;\n  border-radius: 6px;\n}\n\nselect.input-group-lg > .form-control,\nselect.input-group-lg > .input-group-addon,\nselect.input-group-lg > .input-group-btn > .btn {\n  height: 45px;\n  line-height: 45px;\n}\n\ntextarea.input-group-lg > .form-control,\ntextarea.input-group-lg > .input-group-addon,\ntextarea.input-group-lg > .input-group-btn > .btn {\n  height: auto;\n}\n\n.input-group-sm > .form-control,\n.input-group-sm > .input-group-addon,\n.input-group-sm > .input-group-btn > .btn {\n  height: 30px;\n  padding: 5px 10px;\n  font-size: 12px;\n  line-height: 1.5;\n  border-radius: 3px;\n}\n\nselect.input-group-sm > .form-control,\nselect.input-group-sm > .input-group-addon,\nselect.input-group-sm > .input-group-btn > .btn {\n  height: 30px;\n  line-height: 30px;\n}\n\ntextarea.input-group-sm > .form-control,\ntextarea.input-group-sm > .input-group-addon,\ntextarea.input-group-sm > .input-group-btn > .btn {\n  height: auto;\n}\n\n.input-group-addon,\n.input-group-btn,\n.input-group .form-control {\n  display: table-cell;\n}\n\n.input-group-addon:not(:first-child):not(:last-child),\n.input-group-btn:not(:first-child):not(:last-child),\n.input-group .form-control:not(:first-child):not(:last-child) {\n  border-radius: 0;\n}\n\n.input-group-addon,\n.input-group-btn {\n  width: 1%;\n  white-space: nowrap;\n  vertical-align: middle;\n}\n\n.input-group-addon {\n  padding: 6px 12px;\n  font-size: 14px;\n  font-weight: normal;\n  line-height: 1;\n  text-align: center;\n  background-color: #eeeeee;\n  border: 1px solid #cccccc;\n  border-radius: 4px;\n}\n\n.input-group-addon.input-sm {\n  padding: 5px 10px;\n  font-size: 12px;\n  border-radius: 3px;\n}\n\n.input-group-addon.input-lg {\n  padding: 10px 16px;\n  font-size: 18px;\n  border-radius: 6px;\n}\n\n.input-group-addon input[type=\"radio\"],\n.input-group-addon input[type=\"checkbox\"] {\n  margin-top: 0;\n}\n\n.input-group .form-control:first-child,\n.input-group-addon:first-child,\n.input-group-btn:first-child > .btn,\n.input-group-btn:first-child > .dropdown-toggle,\n.input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle) {\n  border-top-right-radius: 0;\n  border-bottom-right-radius: 0;\n}\n\n.input-group-addon:first-child {\n  border-right: 0;\n}\n\n.input-group .form-control:last-child,\n.input-group-addon:last-child,\n.input-group-btn:last-child > .btn,\n.input-group-btn:last-child > .dropdown-toggle,\n.input-group-btn:first-child > .btn:not(:first-child) {\n  border-bottom-left-radius: 0;\n  border-top-left-radius: 0;\n}\n\n.input-group-addon:last-child {\n  border-left: 0;\n}\n\n.input-group-btn {\n  position: relative;\n  white-space: nowrap;\n}\n\n.input-group-btn > .btn {\n  position: relative;\n}\n\n.input-group-btn > .btn + .btn {\n  margin-left: -4px;\n}\n\n.input-group-btn > .btn:hover,\n.input-group-btn > .btn:active {\n  z-index: 2;\n}\n\n.nav {\n  padding-left: 0;\n  margin-bottom: 0;\n  list-style: none;\n}\n\n.nav:before,\n.nav:after {\n  display: table;\n  content: \" \";\n}\n\n.nav:after {\n  clear: both;\n}\n\n.nav:before,\n.nav:after {\n  display: table;\n  content: \" \";\n}\n\n.nav:after {\n  clear: both;\n}\n\n.nav > li {\n  position: relative;\n  display: block;\n}\n\n.nav > li > a {\n  position: relative;\n  display: block;\n  padding: 10px 15px;\n}\n\n.nav > li > a:hover,\n.nav > li > a:focus {\n  text-decoration: none;\n  background-color: #eeeeee;\n}\n\n.nav > li.disabled > a {\n  color: #999999;\n}\n\n.nav > li.disabled > a:hover,\n.nav > li.disabled > a:focus {\n  color: #999999;\n  text-decoration: none;\n  cursor: not-allowed;\n  background-color: transparent;\n}\n\n.nav .open > a,\n.nav .open > a:hover,\n.nav .open > a:focus {\n  background-color: #eeeeee;\n  border-color: #428bca;\n}\n\n.nav .nav-divider {\n  height: 1px;\n  margin: 9px 0;\n  overflow: hidden;\n  background-color: #e5e5e5;\n}\n\n.nav > li > a > img {\n  max-width: none;\n}\n\n.nav-tabs {\n  border-bottom: 1px solid #dddddd;\n}\n\n.nav-tabs > li {\n  float: left;\n  margin-bottom: -1px;\n}\n\n.nav-tabs > li > a {\n  margin-right: 2px;\n  line-height: 1.428571429;\n  border: 1px solid transparent;\n  border-radius: 4px 4px 0 0;\n}\n\n.nav-tabs > li > a:hover {\n  border-color: #eeeeee #eeeeee #dddddd;\n}\n\n.nav-tabs > li.active > a,\n.nav-tabs > li.active > a:hover,\n.nav-tabs > li.active > a:focus {\n  color: #555555;\n  cursor: default;\n  background-color: #ffffff;\n  border: 1px solid #dddddd;\n  border-bottom-color: transparent;\n}\n\n.nav-tabs.nav-justified {\n  width: 100%;\n  border-bottom: 0;\n}\n\n.nav-tabs.nav-justified > li {\n  float: none;\n}\n\n.nav-tabs.nav-justified > li > a {\n  text-align: center;\n}\n\n@media (min-width: 768px) {\n  .nav-tabs.nav-justified > li {\n    display: table-cell;\n    width: 1%;\n  }\n}\n\n.nav-tabs.nav-justified > li > a {\n  margin-right: 0;\n  border-bottom: 1px solid #dddddd;\n}\n\n.nav-tabs.nav-justified > .active > a {\n  border-bottom-color: #ffffff;\n}\n\n.nav-pills > li {\n  float: left;\n}\n\n.nav-pills > li > a {\n  border-radius: 5px;\n}\n\n.nav-pills > li + li {\n  margin-left: 2px;\n}\n\n.nav-pills > li.active > a,\n.nav-pills > li.active > a:hover,\n.nav-pills > li.active > a:focus {\n  color: #ffffff;\n  background-color: #428bca;\n}\n\n.nav-stacked > li {\n  float: none;\n}\n\n.nav-stacked > li + li {\n  margin-top: 2px;\n  margin-left: 0;\n}\n\n.nav-justified {\n  width: 100%;\n}\n\n.nav-justified > li {\n  float: none;\n}\n\n.nav-justified > li > a {\n  text-align: center;\n}\n\n@media (min-width: 768px) {\n  .nav-justified > li {\n    display: table-cell;\n    width: 1%;\n  }\n}\n\n.nav-tabs-justified {\n  border-bottom: 0;\n}\n\n.nav-tabs-justified > li > a {\n  margin-right: 0;\n  border-bottom: 1px solid #dddddd;\n}\n\n.nav-tabs-justified > .active > a {\n  border-bottom-color: #ffffff;\n}\n\n.tabbable:before,\n.tabbable:after {\n  display: table;\n  content: \" \";\n}\n\n.tabbable:after {\n  clear: both;\n}\n\n.tabbable:before,\n.tabbable:after {\n  display: table;\n  content: \" \";\n}\n\n.tabbable:after {\n  clear: both;\n}\n\n.tab-content > .tab-pane,\n.pill-content > .pill-pane {\n  display: none;\n}\n\n.tab-content > .active,\n.pill-content > .active {\n  display: block;\n}\n\n.nav .caret {\n  border-top-color: #428bca;\n  border-bottom-color: #428bca;\n}\n\n.nav a:hover .caret {\n  border-top-color: #2a6496;\n  border-bottom-color: #2a6496;\n}\n\n.nav-tabs .dropdown-menu {\n  margin-top: -1px;\n  border-top-right-radius: 0;\n  border-top-left-radius: 0;\n}\n\n.navbar {\n  position: relative;\n  z-index: 1000;\n  min-height: 50px;\n  margin-bottom: 20px;\n  border: 1px solid transparent;\n}\n\n.navbar:before,\n.navbar:after {\n  display: table;\n  content: \" \";\n}\n\n.navbar:after {\n  clear: both;\n}\n\n.navbar:before,\n.navbar:after {\n  display: table;\n  content: \" \";\n}\n\n.navbar:after {\n  clear: both;\n}\n\n@media (min-width: 768px) {\n  .navbar {\n    border-radius: 4px;\n  }\n}\n\n.navbar-header:before,\n.navbar-header:after {\n  display: table;\n  content: \" \";\n}\n\n.navbar-header:after {\n  clear: both;\n}\n\n.navbar-header:before,\n.navbar-header:after {\n  display: table;\n  content: \" \";\n}\n\n.navbar-header:after {\n  clear: both;\n}\n\n@media (min-width: 768px) {\n  .navbar-header {\n    float: left;\n  }\n}\n\n.navbar-collapse {\n  max-height: 340px;\n  padding-right: 15px;\n  padding-left: 15px;\n  overflow-x: visible;\n  border-top: 1px solid transparent;\n  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1);\n  -webkit-overflow-scrolling: touch;\n}\n\n.navbar-collapse:before,\n.navbar-collapse:after {\n  display: table;\n  content: \" \";\n}\n\n.navbar-collapse:after {\n  clear: both;\n}\n\n.navbar-collapse:before,\n.navbar-collapse:after {\n  display: table;\n  content: \" \";\n}\n\n.navbar-collapse:after {\n  clear: both;\n}\n\n.navbar-collapse.in {\n  overflow-y: auto;\n}\n\n@media (min-width: 768px) {\n  .navbar-collapse {\n    width: auto;\n    border-top: 0;\n    box-shadow: none;\n  }\n  .navbar-collapse.collapse {\n    display: block !important;\n    height: auto !important;\n    padding-bottom: 0;\n    overflow: visible !important;\n  }\n  .navbar-collapse.in {\n    overflow-y: visible;\n  }\n  .navbar-collapse .navbar-nav.navbar-left:first-child {\n    margin-left: -15px;\n  }\n  .navbar-collapse .navbar-nav.navbar-right:last-child {\n    margin-right: -15px;\n  }\n  .navbar-collapse .navbar-text:last-child {\n    margin-right: 0;\n  }\n}\n\n.container > .navbar-header,\n.container > .navbar-collapse {\n  margin-right: -15px;\n  margin-left: -15px;\n}\n\n@media (min-width: 768px) {\n  .container > .navbar-header,\n  .container > .navbar-collapse {\n    margin-right: 0;\n    margin-left: 0;\n  }\n}\n\n.navbar-static-top {\n  border-width: 0 0 1px;\n}\n\n@media (min-width: 768px) {\n  .navbar-static-top {\n    border-radius: 0;\n  }\n}\n\n.navbar-fixed-top,\n.navbar-fixed-bottom {\n  position: fixed;\n  right: 0;\n  left: 0;\n  border-width: 0 0 1px;\n}\n\n@media (min-width: 768px) {\n  .navbar-fixed-top,\n  .navbar-fixed-bottom {\n    border-radius: 0;\n  }\n}\n\n.navbar-fixed-top {\n  top: 0;\n  z-index: 1030;\n}\n\n.navbar-fixed-bottom {\n  bottom: 0;\n  margin-bottom: 0;\n}\n\n.navbar-brand {\n  float: left;\n  padding: 15px 15px;\n  font-size: 18px;\n  line-height: 20px;\n}\n\n.navbar-brand:hover,\n.navbar-brand:focus {\n  text-decoration: none;\n}\n\n@media (min-width: 768px) {\n  .navbar > .container .navbar-brand {\n    margin-left: -15px;\n  }\n}\n\n.navbar-toggle {\n  position: relative;\n  float: right;\n  padding: 9px 10px;\n  margin-top: 8px;\n  margin-right: 15px;\n  margin-bottom: 8px;\n  background-color: transparent;\n  border: 1px solid transparent;\n  border-radius: 4px;\n}\n\n.navbar-toggle .icon-bar {\n  display: block;\n  width: 22px;\n  height: 2px;\n  border-radius: 1px;\n}\n\n.navbar-toggle .icon-bar + .icon-bar {\n  margin-top: 4px;\n}\n\n@media (min-width: 768px) {\n  .navbar-toggle {\n    display: none;\n  }\n}\n\n.navbar-nav {\n  margin: 7.5px -15px;\n}\n\n.navbar-nav > li > a {\n  padding-top: 10px;\n  padding-bottom: 10px;\n  line-height: 20px;\n}\n\n@media (max-width: 767px) {\n  .navbar-nav .open .dropdown-menu {\n    position: static;\n    float: none;\n    width: auto;\n    margin-top: 0;\n    background-color: transparent;\n    border: 0;\n    box-shadow: none;\n  }\n  .navbar-nav .open .dropdown-menu > li > a,\n  .navbar-nav .open .dropdown-menu .dropdown-header {\n    padding: 5px 15px 5px 25px;\n  }\n  .navbar-nav .open .dropdown-menu > li > a {\n    line-height: 20px;\n  }\n  .navbar-nav .open .dropdown-menu > li > a:hover,\n  .navbar-nav .open .dropdown-menu > li > a:focus {\n    background-image: none;\n  }\n}\n\n@media (min-width: 768px) {\n  .navbar-nav {\n    float: left;\n    margin: 0;\n  }\n  .navbar-nav > li {\n    float: left;\n  }\n  .navbar-nav > li > a {\n    padding-top: 15px;\n    padding-bottom: 15px;\n  }\n}\n\n@media (min-width: 768px) {\n  .navbar-left {\n    float: left !important;\n  }\n  .navbar-right {\n    float: right !important;\n  }\n}\n\n.navbar-form {\n  padding: 10px 15px;\n  margin-top: 8px;\n  margin-right: -15px;\n  margin-bottom: 8px;\n  margin-left: -15px;\n  border-top: 1px solid transparent;\n  border-bottom: 1px solid transparent;\n  -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);\n          box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);\n}\n\n@media (min-width: 768px) {\n  .navbar-form .form-group {\n    display: inline-block;\n    margin-bottom: 0;\n    vertical-align: middle;\n  }\n  .navbar-form .form-control {\n    display: inline-block;\n  }\n  .navbar-form .radio,\n  .navbar-form .checkbox {\n    display: inline-block;\n    padding-left: 0;\n    margin-top: 0;\n    margin-bottom: 0;\n  }\n  .navbar-form .radio input[type=\"radio\"],\n  .navbar-form .checkbox input[type=\"checkbox\"] {\n    float: none;\n    margin-left: 0;\n  }\n}\n\n@media (max-width: 767px) {\n  .navbar-form .form-group {\n    margin-bottom: 5px;\n  }\n}\n\n@media (min-width: 768px) {\n  .navbar-form {\n    width: auto;\n    padding-top: 0;\n    padding-bottom: 0;\n    margin-right: 0;\n    margin-left: 0;\n    border: 0;\n    -webkit-box-shadow: none;\n            box-shadow: none;\n  }\n}\n\n.navbar-nav > li > .dropdown-menu {\n  margin-top: 0;\n  border-top-right-radius: 0;\n  border-top-left-radius: 0;\n}\n\n.navbar-fixed-bottom .navbar-nav > li > .dropdown-menu {\n  border-bottom-right-radius: 0;\n  border-bottom-left-radius: 0;\n}\n\n.navbar-nav.pull-right > li > .dropdown-menu,\n.navbar-nav > li > .dropdown-menu.pull-right {\n  right: 0;\n  left: auto;\n}\n\n.navbar-btn {\n  margin-top: 8px;\n  margin-bottom: 8px;\n}\n\n.navbar-text {\n  float: left;\n  margin-top: 15px;\n  margin-bottom: 15px;\n}\n\n@media (min-width: 768px) {\n  .navbar-text {\n    margin-right: 15px;\n    margin-left: 15px;\n  }\n}\n\n.navbar-default {\n  background-color: #f8f8f8;\n  border-color: #e7e7e7;\n}\n\n.navbar-default .navbar-brand {\n  color: #777777;\n}\n\n.navbar-default .navbar-brand:hover,\n.navbar-default .navbar-brand:focus {\n  color: #5e5e5e;\n  background-color: transparent;\n}\n\n.navbar-default .navbar-text {\n  color: #777777;\n}\n\n.navbar-default .navbar-nav > li > a {\n  color: #777777;\n}\n\n.navbar-default .navbar-nav > li > a:hover,\n.navbar-default .navbar-nav > li > a:focus {\n  color: #333333;\n  background-color: transparent;\n}\n\n.navbar-default .navbar-nav > .active > a,\n.navbar-default .navbar-nav > .active > a:hover,\n.navbar-default .navbar-nav > .active > a:focus {\n  color: #555555;\n  background-color: #e7e7e7;\n}\n\n.navbar-default .navbar-nav > .disabled > a,\n.navbar-default .navbar-nav > .disabled > a:hover,\n.navbar-default .navbar-nav > .disabled > a:focus {\n  color: #cccccc;\n  background-color: transparent;\n}\n\n.navbar-default .navbar-toggle {\n  border-color: #dddddd;\n}\n\n.navbar-default .navbar-toggle:hover,\n.navbar-default .navbar-toggle:focus {\n  background-color: #dddddd;\n}\n\n.navbar-default .navbar-toggle .icon-bar {\n  background-color: #cccccc;\n}\n\n.navbar-default .navbar-collapse,\n.navbar-default .navbar-form {\n  border-color: #e6e6e6;\n}\n\n.navbar-default .navbar-nav > .dropdown > a:hover .caret,\n.navbar-default .navbar-nav > .dropdown > a:focus .caret {\n  border-top-color: #333333;\n  border-bottom-color: #333333;\n}\n\n.navbar-default .navbar-nav > .open > a,\n.navbar-default .navbar-nav > .open > a:hover,\n.navbar-default .navbar-nav > .open > a:focus {\n  color: #555555;\n  background-color: #e7e7e7;\n}\n\n.navbar-default .navbar-nav > .open > a .caret,\n.navbar-default .navbar-nav > .open > a:hover .caret,\n.navbar-default .navbar-nav > .open > a:focus .caret {\n  border-top-color: #555555;\n  border-bottom-color: #555555;\n}\n\n.navbar-default .navbar-nav > .dropdown > a .caret {\n  border-top-color: #777777;\n  border-bottom-color: #777777;\n}\n\n@media (max-width: 767px) {\n  .navbar-default .navbar-nav .open .dropdown-menu > li > a {\n    color: #777777;\n  }\n  .navbar-default .navbar-nav .open .dropdown-menu > li > a:hover,\n  .navbar-default .navbar-nav .open .dropdown-menu > li > a:focus {\n    color: #333333;\n    background-color: transparent;\n  }\n  .navbar-default .navbar-nav .open .dropdown-menu > .active > a,\n  .navbar-default .navbar-nav .open .dropdown-menu > .active > a:hover,\n  .navbar-default .navbar-nav .open .dropdown-menu > .active > a:focus {\n    color: #555555;\n    background-color: #e7e7e7;\n  }\n  .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a,\n  .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:hover,\n  .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:focus {\n    color: #cccccc;\n    background-color: transparent;\n  }\n}\n\n.navbar-default .navbar-link {\n  color: #777777;\n}\n\n.navbar-default .navbar-link:hover {\n  color: #333333;\n}\n\n.navbar-inverse {\n  background-color: #222222;\n  border-color: #080808;\n}\n\n.navbar-inverse .navbar-brand {\n  color: #999999;\n}\n\n.navbar-inverse .navbar-brand:hover,\n.navbar-inverse .navbar-brand:focus {\n  color: #ffffff;\n  background-color: transparent;\n}\n\n.navbar-inverse .navbar-text {\n  color: #999999;\n}\n\n.navbar-inverse .navbar-nav > li > a {\n  color: #999999;\n}\n\n.navbar-inverse .navbar-nav > li > a:hover,\n.navbar-inverse .navbar-nav > li > a:focus {\n  color: #ffffff;\n  background-color: transparent;\n}\n\n.navbar-inverse .navbar-nav > .active > a,\n.navbar-inverse .navbar-nav > .active > a:hover,\n.navbar-inverse .navbar-nav > .active > a:focus {\n  color: #ffffff;\n  background-color: #080808;\n}\n\n.navbar-inverse .navbar-nav > .disabled > a,\n.navbar-inverse .navbar-nav > .disabled > a:hover,\n.navbar-inverse .navbar-nav > .disabled > a:focus {\n  color: #444444;\n  background-color: transparent;\n}\n\n.navbar-inverse .navbar-toggle {\n  border-color: #333333;\n}\n\n.navbar-inverse .navbar-toggle:hover,\n.navbar-inverse .navbar-toggle:focus {\n  background-color: #333333;\n}\n\n.navbar-inverse .navbar-toggle .icon-bar {\n  background-color: #ffffff;\n}\n\n.navbar-inverse .navbar-collapse,\n.navbar-inverse .navbar-form {\n  border-color: #101010;\n}\n\n.navbar-inverse .navbar-nav > .open > a,\n.navbar-inverse .navbar-nav > .open > a:hover,\n.navbar-inverse .navbar-nav > .open > a:focus {\n  color: #ffffff;\n  background-color: #080808;\n}\n\n.navbar-inverse .navbar-nav > .dropdown > a:hover .caret {\n  border-top-color: #ffffff;\n  border-bottom-color: #ffffff;\n}\n\n.navbar-inverse .navbar-nav > .dropdown > a .caret {\n  border-top-color: #999999;\n  border-bottom-color: #999999;\n}\n\n.navbar-inverse .navbar-nav > .open > a .caret,\n.navbar-inverse .navbar-nav > .open > a:hover .caret,\n.navbar-inverse .navbar-nav > .open > a:focus .caret {\n  border-top-color: #ffffff;\n  border-bottom-color: #ffffff;\n}\n\n@media (max-width: 767px) {\n  .navbar-inverse .navbar-nav .open .dropdown-menu > .dropdown-header {\n    border-color: #080808;\n  }\n  .navbar-inverse .navbar-nav .open .dropdown-menu > li > a {\n    color: #999999;\n  }\n  .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:hover,\n  .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:focus {\n    color: #ffffff;\n    background-color: transparent;\n  }\n  .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a,\n  .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:hover,\n  .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:focus {\n    color: #ffffff;\n    background-color: #080808;\n  }\n  .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a,\n  .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:hover,\n  .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:focus {\n    color: #444444;\n    background-color: transparent;\n  }\n}\n\n.navbar-inverse .navbar-link {\n  color: #999999;\n}\n\n.navbar-inverse .navbar-link:hover {\n  color: #ffffff;\n}\n\n.breadcrumb {\n  padding: 8px 15px;\n  margin-bottom: 20px;\n  list-style: none;\n  background-color: #f5f5f5;\n  border-radius: 4px;\n}\n\n.breadcrumb > li {\n  display: inline-block;\n}\n\n.breadcrumb > li + li:before {\n  padding: 0 5px;\n  color: #cccccc;\n  content: \"/\\00a0\";\n}\n\n.breadcrumb > .active {\n  color: #999999;\n}\n\n.pagination {\n  display: inline-block;\n  padding-left: 0;\n  margin: 20px 0;\n  border-radius: 4px;\n}\n\n.pagination > li {\n  display: inline;\n}\n\n.pagination > li > a,\n.pagination > li > span {\n  position: relative;\n  float: left;\n  padding: 6px 12px;\n  margin-left: -1px;\n  line-height: 1.428571429;\n  text-decoration: none;\n  background-color: #ffffff;\n  border: 1px solid #dddddd;\n}\n\n.pagination > li:first-child > a,\n.pagination > li:first-child > span {\n  margin-left: 0;\n  border-bottom-left-radius: 4px;\n  border-top-left-radius: 4px;\n}\n\n.pagination > li:last-child > a,\n.pagination > li:last-child > span {\n  border-top-right-radius: 4px;\n  border-bottom-right-radius: 4px;\n}\n\n.pagination > li > a:hover,\n.pagination > li > span:hover,\n.pagination > li > a:focus,\n.pagination > li > span:focus {\n  background-color: #eeeeee;\n}\n\n.pagination > .active > a,\n.pagination > .active > span,\n.pagination > .active > a:hover,\n.pagination > .active > span:hover,\n.pagination > .active > a:focus,\n.pagination > .active > span:focus {\n  z-index: 2;\n  color: #ffffff;\n  cursor: default;\n  background-color: #428bca;\n  border-color: #428bca;\n}\n\n.pagination > .disabled > span,\n.pagination > .disabled > a,\n.pagination > .disabled > a:hover,\n.pagination > .disabled > a:focus {\n  color: #999999;\n  cursor: not-allowed;\n  background-color: #ffffff;\n  border-color: #dddddd;\n}\n\n.pagination-lg > li > a,\n.pagination-lg > li > span {\n  padding: 10px 16px;\n  font-size: 18px;\n}\n\n.pagination-lg > li:first-child > a,\n.pagination-lg > li:first-child > span {\n  border-bottom-left-radius: 6px;\n  border-top-left-radius: 6px;\n}\n\n.pagination-lg > li:last-child > a,\n.pagination-lg > li:last-child > span {\n  border-top-right-radius: 6px;\n  border-bottom-right-radius: 6px;\n}\n\n.pagination-sm > li > a,\n.pagination-sm > li > span {\n  padding: 5px 10px;\n  font-size: 12px;\n}\n\n.pagination-sm > li:first-child > a,\n.pagination-sm > li:first-child > span {\n  border-bottom-left-radius: 3px;\n  border-top-left-radius: 3px;\n}\n\n.pagination-sm > li:last-child > a,\n.pagination-sm > li:last-child > span {\n  border-top-right-radius: 3px;\n  border-bottom-right-radius: 3px;\n}\n\n.pager {\n  padding-left: 0;\n  margin: 20px 0;\n  text-align: center;\n  list-style: none;\n}\n\n.pager:before,\n.pager:after {\n  display: table;\n  content: \" \";\n}\n\n.pager:after {\n  clear: both;\n}\n\n.pager:before,\n.pager:after {\n  display: table;\n  content: \" \";\n}\n\n.pager:after {\n  clear: both;\n}\n\n.pager li {\n  display: inline;\n}\n\n.pager li > a,\n.pager li > span {\n  display: inline-block;\n  padding: 5px 14px;\n  background-color: #ffffff;\n  border: 1px solid #dddddd;\n  border-radius: 15px;\n}\n\n.pager li > a:hover,\n.pager li > a:focus {\n  text-decoration: none;\n  background-color: #eeeeee;\n}\n\n.pager .next > a,\n.pager .next > span {\n  float: right;\n}\n\n.pager .previous > a,\n.pager .previous > span {\n  float: left;\n}\n\n.pager .disabled > a,\n.pager .disabled > a:hover,\n.pager .disabled > a:focus,\n.pager .disabled > span {\n  color: #999999;\n  cursor: not-allowed;\n  background-color: #ffffff;\n}\n\n.label {\n  display: inline;\n  padding: .2em .6em .3em;\n  font-size: 75%;\n  font-weight: bold;\n  line-height: 1;\n  color: #ffffff;\n  text-align: center;\n  white-space: nowrap;\n  vertical-align: baseline;\n  border-radius: .25em;\n}\n\n.label[href]:hover,\n.label[href]:focus {\n  color: #ffffff;\n  text-decoration: none;\n  cursor: pointer;\n}\n\n.label:empty {\n  display: none;\n}\n\n.label-default {\n  background-color: #999999;\n}\n\n.label-default[href]:hover,\n.label-default[href]:focus {\n  background-color: #808080;\n}\n\n.label-primary {\n  background-color: #428bca;\n}\n\n.label-primary[href]:hover,\n.label-primary[href]:focus {\n  background-color: #3071a9;\n}\n\n.label-success {\n  background-color: #5cb85c;\n}\n\n.label-success[href]:hover,\n.label-success[href]:focus {\n  background-color: #449d44;\n}\n\n.label-info {\n  background-color: #5bc0de;\n}\n\n.label-info[href]:hover,\n.label-info[href]:focus {\n  background-color: #31b0d5;\n}\n\n.label-warning {\n  background-color: #f0ad4e;\n}\n\n.label-warning[href]:hover,\n.label-warning[href]:focus {\n  background-color: #ec971f;\n}\n\n.label-danger {\n  background-color: #d9534f;\n}\n\n.label-danger[href]:hover,\n.label-danger[href]:focus {\n  background-color: #c9302c;\n}\n\n.badge {\n  display: inline-block;\n  min-width: 10px;\n  padding: 3px 7px;\n  font-size: 12px;\n  font-weight: bold;\n  line-height: 1;\n  color: #ffffff;\n  text-align: center;\n  white-space: nowrap;\n  vertical-align: baseline;\n  background-color: #999999;\n  border-radius: 10px;\n}\n\n.badge:empty {\n  display: none;\n}\n\na.badge:hover,\na.badge:focus {\n  color: #ffffff;\n  text-decoration: none;\n  cursor: pointer;\n}\n\n.btn .badge {\n  position: relative;\n  top: -1px;\n}\n\na.list-group-item.active > .badge,\n.nav-pills > .active > a > .badge {\n  color: #428bca;\n  background-color: #ffffff;\n}\n\n.nav-pills > li > a > .badge {\n  margin-left: 3px;\n}\n\n.jumbotron {\n  padding: 30px;\n  margin-bottom: 30px;\n  font-size: 21px;\n  font-weight: 200;\n  line-height: 2.1428571435;\n  color: inherit;\n  background-color: #eeeeee;\n}\n\n.jumbotron h1 {\n  line-height: 1;\n  color: inherit;\n}\n\n.jumbotron p {\n  line-height: 1.4;\n}\n\n.container .jumbotron {\n  border-radius: 6px;\n}\n\n@media screen and (min-width: 768px) {\n  .jumbotron {\n    padding-top: 48px;\n    padding-bottom: 48px;\n  }\n  .container .jumbotron {\n    padding-right: 60px;\n    padding-left: 60px;\n  }\n  .jumbotron h1 {\n    font-size: 63px;\n  }\n}\n\n.thumbnail {\n  display: inline-block;\n  display: block;\n  height: auto;\n  max-width: 100%;\n  padding: 4px;\n  line-height: 1.428571429;\n  background-color: #ffffff;\n  border: 1px solid #dddddd;\n  border-radius: 4px;\n  -webkit-transition: all 0.2s ease-in-out;\n          transition: all 0.2s ease-in-out;\n}\n\n.thumbnail > img {\n  display: block;\n  height: auto;\n  max-width: 100%;\n}\n\na.thumbnail:hover,\na.thumbnail:focus {\n  border-color: #428bca;\n}\n\n.thumbnail > img {\n  margin-right: auto;\n  margin-left: auto;\n}\n\n.thumbnail .caption {\n  padding: 9px;\n  color: #333333;\n}\n\n.alert {\n  padding: 15px;\n  margin-bottom: 20px;\n  border: 1px solid transparent;\n  border-radius: 4px;\n}\n\n.alert h4 {\n  margin-top: 0;\n  color: inherit;\n}\n\n.alert .alert-link {\n  font-weight: bold;\n}\n\n.alert > p,\n.alert > ul {\n  margin-bottom: 0;\n}\n\n.alert > p + p {\n  margin-top: 5px;\n}\n\n.alert-dismissable {\n  padding-right: 35px;\n}\n\n.alert-dismissable .close {\n  position: relative;\n  top: -2px;\n  right: -21px;\n  color: inherit;\n}\n\n.alert-success {\n  color: #468847;\n  background-color: #dff0d8;\n  border-color: #d6e9c6;\n}\n\n.alert-success hr {\n  border-top-color: #c9e2b3;\n}\n\n.alert-success .alert-link {\n  color: #356635;\n}\n\n.alert-info {\n  color: #3a87ad;\n  background-color: #d9edf7;\n  border-color: #bce8f1;\n}\n\n.alert-info hr {\n  border-top-color: #a6e1ec;\n}\n\n.alert-info .alert-link {\n  color: #2d6987;\n}\n\n.alert-warning {\n  color: #c09853;\n  background-color: #fcf8e3;\n  border-color: #fbeed5;\n}\n\n.alert-warning hr {\n  border-top-color: #f8e5be;\n}\n\n.alert-warning .alert-link {\n  color: #a47e3c;\n}\n\n.alert-danger {\n  color: #b94a48;\n  background-color: #f2dede;\n  border-color: #eed3d7;\n}\n\n.alert-danger hr {\n  border-top-color: #e6c1c7;\n}\n\n.alert-danger .alert-link {\n  color: #953b39;\n}\n\n@-webkit-keyframes progress-bar-stripes {\n  from {\n    background-position: 40px 0;\n  }\n  to {\n    background-position: 0 0;\n  }\n}\n\n@-moz-keyframes progress-bar-stripes {\n  from {\n    background-position: 40px 0;\n  }\n  to {\n    background-position: 0 0;\n  }\n}\n\n@-o-keyframes progress-bar-stripes {\n  from {\n    background-position: 0 0;\n  }\n  to {\n    background-position: 40px 0;\n  }\n}\n\n@keyframes progress-bar-stripes {\n  from {\n    background-position: 40px 0;\n  }\n  to {\n    background-position: 0 0;\n  }\n}\n\n.progress {\n  height: 20px;\n  margin-bottom: 20px;\n  overflow: hidden;\n  background-color: #f5f5f5;\n  border-radius: 4px;\n  -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);\n          box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);\n}\n\n.progress-bar {\n  float: left;\n  width: 0;\n  height: 100%;\n  font-size: 12px;\n  color: #ffffff;\n  text-align: center;\n  background-color: #428bca;\n  -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);\n          box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);\n  -webkit-transition: width 0.6s ease;\n          transition: width 0.6s ease;\n}\n\n.progress-striped .progress-bar {\n  background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));\n  background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n  background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n  background-size: 40px 40px;\n}\n\n.progress.active .progress-bar {\n  -webkit-animation: progress-bar-stripes 2s linear infinite;\n     -moz-animation: progress-bar-stripes 2s linear infinite;\n      -ms-animation: progress-bar-stripes 2s linear infinite;\n       -o-animation: progress-bar-stripes 2s linear infinite;\n          animation: progress-bar-stripes 2s linear infinite;\n}\n\n.progress-bar-success {\n  background-color: #5cb85c;\n}\n\n.progress-striped .progress-bar-success {\n  background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));\n  background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n  background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n}\n\n.progress-bar-info {\n  background-color: #5bc0de;\n}\n\n.progress-striped .progress-bar-info {\n  background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));\n  background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n  background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n}\n\n.progress-bar-warning {\n  background-color: #f0ad4e;\n}\n\n.progress-striped .progress-bar-warning {\n  background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));\n  background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n  background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n}\n\n.progress-bar-danger {\n  background-color: #d9534f;\n}\n\n.progress-striped .progress-bar-danger {\n  background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));\n  background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n  background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n}\n\n.media,\n.media-body {\n  overflow: hidden;\n  zoom: 1;\n}\n\n.media,\n.media .media {\n  margin-top: 15px;\n}\n\n.media:first-child {\n  margin-top: 0;\n}\n\n.media-object {\n  display: block;\n}\n\n.media-heading {\n  margin: 0 0 5px;\n}\n\n.media > .pull-left {\n  margin-right: 10px;\n}\n\n.media > .pull-right {\n  margin-left: 10px;\n}\n\n.media-list {\n  padding-left: 0;\n  list-style: none;\n}\n\n.list-group {\n  padding-left: 0;\n  margin-bottom: 20px;\n}\n\n.list-group-item {\n  position: relative;\n  display: block;\n  padding: 10px 15px;\n  margin-bottom: -1px;\n  background-color: #ffffff;\n  border: 1px solid #dddddd;\n}\n\n.list-group-item:first-child {\n  border-top-right-radius: 4px;\n  border-top-left-radius: 4px;\n}\n\n.list-group-item:last-child {\n  margin-bottom: 0;\n  border-bottom-right-radius: 4px;\n  border-bottom-left-radius: 4px;\n}\n\n.list-group-item > .badge {\n  float: right;\n}\n\n.list-group-item > .badge + .badge {\n  margin-right: 5px;\n}\n\na.list-group-item {\n  color: #555555;\n}\n\na.list-group-item .list-group-item-heading {\n  color: #333333;\n}\n\na.list-group-item:hover,\na.list-group-item:focus {\n  text-decoration: none;\n  background-color: #f5f5f5;\n}\n\n.list-group-item.active,\n.list-group-item.active:hover,\n.list-group-item.active:focus {\n  z-index: 2;\n  color: #ffffff;\n  background-color: #428bca;\n  border-color: #428bca;\n}\n\n.list-group-item.active .list-group-item-heading,\n.list-group-item.active:hover .list-group-item-heading,\n.list-group-item.active:focus .list-group-item-heading {\n  color: inherit;\n}\n\n.list-group-item.active .list-group-item-text,\n.list-group-item.active:hover .list-group-item-text,\n.list-group-item.active:focus .list-group-item-text {\n  color: #e1edf7;\n}\n\n.list-group-item-heading {\n  margin-top: 0;\n  margin-bottom: 5px;\n}\n\n.list-group-item-text {\n  margin-bottom: 0;\n  line-height: 1.3;\n}\n\n.panel {\n  margin-bottom: 20px;\n  background-color: #ffffff;\n  border: 1px solid transparent;\n  border-radius: 4px;\n  -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05);\n          box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05);\n}\n\n.panel-body {\n  padding: 15px;\n}\n\n.panel-body:before,\n.panel-body:after {\n  display: table;\n  content: \" \";\n}\n\n.panel-body:after {\n  clear: both;\n}\n\n.panel-body:before,\n.panel-body:after {\n  display: table;\n  content: \" \";\n}\n\n.panel-body:after {\n  clear: both;\n}\n\n.panel > .list-group {\n  margin-bottom: 0;\n}\n\n.panel > .list-group .list-group-item {\n  border-width: 1px 0;\n}\n\n.panel > .list-group .list-group-item:first-child {\n  border-top-right-radius: 0;\n  border-top-left-radius: 0;\n}\n\n.panel > .list-group .list-group-item:last-child {\n  border-bottom: 0;\n}\n\n.panel-heading + .list-group .list-group-item:first-child {\n  border-top-width: 0;\n}\n\n.panel > .table {\n  margin-bottom: 0;\n}\n\n.panel > .panel-body + .table {\n  border-top: 1px solid #dddddd;\n}\n\n.panel-heading {\n  padding: 10px 15px;\n  border-bottom: 1px solid transparent;\n  border-top-right-radius: 3px;\n  border-top-left-radius: 3px;\n}\n\n.panel-title {\n  margin-top: 0;\n  margin-bottom: 0;\n  font-size: 16px;\n}\n\n.panel-title > a {\n  color: inherit;\n}\n\n.panel-footer {\n  padding: 10px 15px;\n  background-color: #f5f5f5;\n  border-top: 1px solid #dddddd;\n  border-bottom-right-radius: 3px;\n  border-bottom-left-radius: 3px;\n}\n\n.panel-group .panel {\n  margin-bottom: 0;\n  overflow: hidden;\n  border-radius: 4px;\n}\n\n.panel-group .panel + .panel {\n  margin-top: 5px;\n}\n\n.panel-group .panel-heading {\n  border-bottom: 0;\n}\n\n.panel-group .panel-heading + .panel-collapse .panel-body {\n  border-top: 1px solid #dddddd;\n}\n\n.panel-group .panel-footer {\n  border-top: 0;\n}\n\n.panel-group .panel-footer + .panel-collapse .panel-body {\n  border-bottom: 1px solid #dddddd;\n}\n\n.panel-default {\n  border-color: #dddddd;\n}\n\n.panel-default > .panel-heading {\n  color: #333333;\n  background-color: #f5f5f5;\n  border-color: #dddddd;\n}\n\n.panel-default > .panel-heading + .panel-collapse .panel-body {\n  border-top-color: #dddddd;\n}\n\n.panel-default > .panel-footer + .panel-collapse .panel-body {\n  border-bottom-color: #dddddd;\n}\n\n.panel-primary {\n  border-color: #428bca;\n}\n\n.panel-primary > .panel-heading {\n  color: #ffffff;\n  background-color: #428bca;\n  border-color: #428bca;\n}\n\n.panel-primary > .panel-heading + .panel-collapse .panel-body {\n  border-top-color: #428bca;\n}\n\n.panel-primary > .panel-footer + .panel-collapse .panel-body {\n  border-bottom-color: #428bca;\n}\n\n.panel-success {\n  border-color: #d6e9c6;\n}\n\n.panel-success > .panel-heading {\n  color: #468847;\n  background-color: #dff0d8;\n  border-color: #d6e9c6;\n}\n\n.panel-success > .panel-heading + .panel-collapse .panel-body {\n  border-top-color: #d6e9c6;\n}\n\n.panel-success > .panel-footer + .panel-collapse .panel-body {\n  border-bottom-color: #d6e9c6;\n}\n\n.panel-warning {\n  border-color: #fbeed5;\n}\n\n.panel-warning > .panel-heading {\n  color: #c09853;\n  background-color: #fcf8e3;\n  border-color: #fbeed5;\n}\n\n.panel-warning > .panel-heading + .panel-collapse .panel-body {\n  border-top-color: #fbeed5;\n}\n\n.panel-warning > .panel-footer + .panel-collapse .panel-body {\n  border-bottom-color: #fbeed5;\n}\n\n.panel-danger {\n  border-color: #eed3d7;\n}\n\n.panel-danger > .panel-heading {\n  color: #b94a48;\n  background-color: #f2dede;\n  border-color: #eed3d7;\n}\n\n.panel-danger > .panel-heading + .panel-collapse .panel-body {\n  border-top-color: #eed3d7;\n}\n\n.panel-danger > .panel-footer + .panel-collapse .panel-body {\n  border-bottom-color: #eed3d7;\n}\n\n.panel-info {\n  border-color: #bce8f1;\n}\n\n.panel-info > .panel-heading {\n  color: #3a87ad;\n  background-color: #d9edf7;\n  border-color: #bce8f1;\n}\n\n.panel-info > .panel-heading + .panel-collapse .panel-body {\n  border-top-color: #bce8f1;\n}\n\n.panel-info > .panel-footer + .panel-collapse .panel-body {\n  border-bottom-color: #bce8f1;\n}\n\n.well {\n  min-height: 20px;\n  padding: 19px;\n  margin-bottom: 20px;\n  background-color: #f5f5f5;\n  border: 1px solid #e3e3e3;\n  border-radius: 4px;\n  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);\n          box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);\n}\n\n.well blockquote {\n  border-color: #ddd;\n  border-color: rgba(0, 0, 0, 0.15);\n}\n\n.well-lg {\n  padding: 24px;\n  border-radius: 6px;\n}\n\n.well-sm {\n  padding: 9px;\n  border-radius: 3px;\n}\n\n.close {\n  float: right;\n  font-size: 21px;\n  font-weight: bold;\n  line-height: 1;\n  color: #000000;\n  text-shadow: 0 1px 0 #ffffff;\n  opacity: 0.2;\n  filter: alpha(opacity=20);\n}\n\n.close:hover,\n.close:focus {\n  color: #000000;\n  text-decoration: none;\n  cursor: pointer;\n  opacity: 0.5;\n  filter: alpha(opacity=50);\n}\n\nbutton.close {\n  padding: 0;\n  cursor: pointer;\n  background: transparent;\n  border: 0;\n  -webkit-appearance: none;\n}\n\n.modal-open {\n  overflow: hidden;\n}\n\nbody.modal-open,\n.modal-open .navbar-fixed-top,\n.modal-open .navbar-fixed-bottom {\n  margin-right: 15px;\n}\n\n.modal {\n  position: fixed;\n  top: 0;\n  right: 0;\n  bottom: 0;\n  left: 0;\n  z-index: 1040;\n  display: none;\n  overflow: auto;\n  overflow-y: scroll;\n}\n\n.modal.fade .modal-dialog {\n  -webkit-transform: translate(0, -25%);\n      -ms-transform: translate(0, -25%);\n          transform: translate(0, -25%);\n  -webkit-transition: -webkit-transform 0.3s ease-out;\n     -moz-transition: -moz-transform 0.3s ease-out;\n       -o-transition: -o-transform 0.3s ease-out;\n          transition: transform 0.3s ease-out;\n}\n\n.modal.in .modal-dialog {\n  -webkit-transform: translate(0, 0);\n      -ms-transform: translate(0, 0);\n          transform: translate(0, 0);\n}\n\n.modal-dialog {\n  z-index: 1050;\n  width: auto;\n  padding: 10px;\n  margin-right: auto;\n  margin-left: auto;\n}\n\n.modal-content {\n  position: relative;\n  background-color: #ffffff;\n  border: 1px solid #999999;\n  border: 1px solid rgba(0, 0, 0, 0.2);\n  border-radius: 6px;\n  outline: none;\n  -webkit-box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5);\n          box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5);\n  background-clip: padding-box;\n}\n\n.modal-backdrop {\n  position: fixed;\n  top: 0;\n  right: 0;\n  bottom: 0;\n  left: 0;\n  z-index: 1030;\n  background-color: #000000;\n}\n\n.modal-backdrop.fade {\n  opacity: 0;\n  filter: alpha(opacity=0);\n}\n\n.modal-backdrop.in {\n  opacity: 0.5;\n  filter: alpha(opacity=50);\n}\n\n.modal-header {\n  min-height: 16.428571429px;\n  padding: 15px;\n  border-bottom: 1px solid #e5e5e5;\n}\n\n.modal-header .close {\n  margin-top: -2px;\n}\n\n.modal-title {\n  margin: 0;\n  line-height: 1.428571429;\n}\n\n.modal-body {\n  position: relative;\n  padding: 20px;\n}\n\n.modal-footer {\n  padding: 19px 20px 20px;\n  margin-top: 15px;\n  text-align: right;\n  border-top: 1px solid #e5e5e5;\n}\n\n.modal-footer:before,\n.modal-footer:after {\n  display: table;\n  content: \" \";\n}\n\n.modal-footer:after {\n  clear: both;\n}\n\n.modal-footer:before,\n.modal-footer:after {\n  display: table;\n  content: \" \";\n}\n\n.modal-footer:after {\n  clear: both;\n}\n\n.modal-footer .btn + .btn {\n  margin-bottom: 0;\n  margin-left: 5px;\n}\n\n.modal-footer .btn-group .btn + .btn {\n  margin-left: -1px;\n}\n\n.modal-footer .btn-block + .btn-block {\n  margin-left: 0;\n}\n\n@media screen and (min-width: 768px) {\n  .modal-dialog {\n    right: auto;\n    left: 50%;\n    width: 600px;\n    padding-top: 30px;\n    padding-bottom: 30px;\n  }\n  .modal-content {\n    -webkit-box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5);\n            box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5);\n  }\n}\n\n.tooltip {\n  position: absolute;\n  z-index: 1030;\n  display: block;\n  font-size: 12px;\n  line-height: 1.4;\n  opacity: 0;\n  filter: alpha(opacity=0);\n  visibility: visible;\n}\n\n.tooltip.in {\n  opacity: 0.9;\n  filter: alpha(opacity=90);\n}\n\n.tooltip.top {\n  padding: 5px 0;\n  margin-top: -3px;\n}\n\n.tooltip.right {\n  padding: 0 5px;\n  margin-left: 3px;\n}\n\n.tooltip.bottom {\n  padding: 5px 0;\n  margin-top: 3px;\n}\n\n.tooltip.left {\n  padding: 0 5px;\n  margin-left: -3px;\n}\n\n.tooltip-inner {\n  max-width: 200px;\n  padding: 3px 8px;\n  color: #ffffff;\n  text-align: center;\n  text-decoration: none;\n  background-color: #000000;\n  border-radius: 4px;\n}\n\n.tooltip-arrow {\n  position: absolute;\n  width: 0;\n  height: 0;\n  border-color: transparent;\n  border-style: solid;\n}\n\n.tooltip.top .tooltip-arrow {\n  bottom: 0;\n  left: 50%;\n  margin-left: -5px;\n  border-top-color: #000000;\n  border-width: 5px 5px 0;\n}\n\n.tooltip.top-left .tooltip-arrow {\n  bottom: 0;\n  left: 5px;\n  border-top-color: #000000;\n  border-width: 5px 5px 0;\n}\n\n.tooltip.top-right .tooltip-arrow {\n  right: 5px;\n  bottom: 0;\n  border-top-color: #000000;\n  border-width: 5px 5px 0;\n}\n\n.tooltip.right .tooltip-arrow {\n  top: 50%;\n  left: 0;\n  margin-top: -5px;\n  border-right-color: #000000;\n  border-width: 5px 5px 5px 0;\n}\n\n.tooltip.left .tooltip-arrow {\n  top: 50%;\n  right: 0;\n  margin-top: -5px;\n  border-left-color: #000000;\n  border-width: 5px 0 5px 5px;\n}\n\n.tooltip.bottom .tooltip-arrow {\n  top: 0;\n  left: 50%;\n  margin-left: -5px;\n  border-bottom-color: #000000;\n  border-width: 0 5px 5px;\n}\n\n.tooltip.bottom-left .tooltip-arrow {\n  top: 0;\n  left: 5px;\n  border-bottom-color: #000000;\n  border-width: 0 5px 5px;\n}\n\n.tooltip.bottom-right .tooltip-arrow {\n  top: 0;\n  right: 5px;\n  border-bottom-color: #000000;\n  border-width: 0 5px 5px;\n}\n\n.popover {\n  position: absolute;\n  top: 0;\n  left: 0;\n  z-index: 1010;\n  display: none;\n  max-width: 276px;\n  padding: 1px;\n  text-align: left;\n  white-space: normal;\n  background-color: #ffffff;\n  border: 1px solid #cccccc;\n  border: 1px solid rgba(0, 0, 0, 0.2);\n  border-radius: 6px;\n  -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);\n          box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);\n  background-clip: padding-box;\n}\n\n.popover.top {\n  margin-top: -10px;\n}\n\n.popover.right {\n  margin-left: 10px;\n}\n\n.popover.bottom {\n  margin-top: 10px;\n}\n\n.popover.left {\n  margin-left: -10px;\n}\n\n.popover-title {\n  padding: 8px 14px;\n  margin: 0;\n  font-size: 14px;\n  font-weight: normal;\n  line-height: 18px;\n  background-color: #f7f7f7;\n  border-bottom: 1px solid #ebebeb;\n  border-radius: 5px 5px 0 0;\n}\n\n.popover-content {\n  padding: 9px 14px;\n}\n\n.popover .arrow,\n.popover .arrow:after {\n  position: absolute;\n  display: block;\n  width: 0;\n  height: 0;\n  border-color: transparent;\n  border-style: solid;\n}\n\n.popover .arrow {\n  border-width: 11px;\n}\n\n.popover .arrow:after {\n  border-width: 10px;\n  content: \"\";\n}\n\n.popover.top .arrow {\n  bottom: -11px;\n  left: 50%;\n  margin-left: -11px;\n  border-top-color: #999999;\n  border-top-color: rgba(0, 0, 0, 0.25);\n  border-bottom-width: 0;\n}\n\n.popover.top .arrow:after {\n  bottom: 1px;\n  margin-left: -10px;\n  border-top-color: #ffffff;\n  border-bottom-width: 0;\n  content: \" \";\n}\n\n.popover.right .arrow {\n  top: 50%;\n  left: -11px;\n  margin-top: -11px;\n  border-right-color: #999999;\n  border-right-color: rgba(0, 0, 0, 0.25);\n  border-left-width: 0;\n}\n\n.popover.right .arrow:after {\n  bottom: -10px;\n  left: 1px;\n  border-right-color: #ffffff;\n  border-left-width: 0;\n  content: \" \";\n}\n\n.popover.bottom .arrow {\n  top: -11px;\n  left: 50%;\n  margin-left: -11px;\n  border-bottom-color: #999999;\n  border-bottom-color: rgba(0, 0, 0, 0.25);\n  border-top-width: 0;\n}\n\n.popover.bottom .arrow:after {\n  top: 1px;\n  margin-left: -10px;\n  border-bottom-color: #ffffff;\n  border-top-width: 0;\n  content: \" \";\n}\n\n.popover.left .arrow {\n  top: 50%;\n  right: -11px;\n  margin-top: -11px;\n  border-left-color: #999999;\n  border-left-color: rgba(0, 0, 0, 0.25);\n  border-right-width: 0;\n}\n\n.popover.left .arrow:after {\n  right: 1px;\n  bottom: -10px;\n  border-left-color: #ffffff;\n  border-right-width: 0;\n  content: \" \";\n}\n\n.carousel {\n  position: relative;\n}\n\n.carousel-inner {\n  position: relative;\n  width: 100%;\n  overflow: hidden;\n}\n\n.carousel-inner > .item {\n  position: relative;\n  display: none;\n  -webkit-transition: 0.6s ease-in-out left;\n          transition: 0.6s ease-in-out left;\n}\n\n.carousel-inner > .item > img,\n.carousel-inner > .item > a > img {\n  display: block;\n  height: auto;\n  max-width: 100%;\n  line-height: 1;\n}\n\n.carousel-inner > .active,\n.carousel-inner > .next,\n.carousel-inner > .prev {\n  display: block;\n}\n\n.carousel-inner > .active {\n  left: 0;\n}\n\n.carousel-inner > .next,\n.carousel-inner > .prev {\n  position: absolute;\n  top: 0;\n  width: 100%;\n}\n\n.carousel-inner > .next {\n  left: 100%;\n}\n\n.carousel-inner > .prev {\n  left: -100%;\n}\n\n.carousel-inner > .next.left,\n.carousel-inner > .prev.right {\n  left: 0;\n}\n\n.carousel-inner > .active.left {\n  left: -100%;\n}\n\n.carousel-inner > .active.right {\n  left: 100%;\n}\n\n.carousel-control {\n  position: absolute;\n  top: 0;\n  bottom: 0;\n  left: 0;\n  width: 15%;\n  font-size: 20px;\n  color: #ffffff;\n  text-align: center;\n  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);\n  opacity: 0.5;\n  filter: alpha(opacity=50);\n}\n\n.carousel-control.left {\n  background-image: -webkit-gradient(linear, 0 top, 100% top, from(rgba(0, 0, 0, 0.5)), to(rgba(0, 0, 0, 0.0001)));\n  background-image: -webkit-linear-gradient(left, color-stop(rgba(0, 0, 0, 0.5) 0), color-stop(rgba(0, 0, 0, 0.0001) 100%));\n  background-image: -moz-linear-gradient(left, rgba(0, 0, 0, 0.5) 0, rgba(0, 0, 0, 0.0001) 100%);\n  background-image: linear-gradient(to right, rgba(0, 0, 0, 0.5) 0, rgba(0, 0, 0, 0.0001) 100%);\n  background-repeat: repeat-x;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);\n}\n\n.carousel-control.right {\n  right: 0;\n  left: auto;\n  background-image: -webkit-gradient(linear, 0 top, 100% top, from(rgba(0, 0, 0, 0.0001)), to(rgba(0, 0, 0, 0.5)));\n  background-image: -webkit-linear-gradient(left, color-stop(rgba(0, 0, 0, 0.0001) 0), color-stop(rgba(0, 0, 0, 0.5) 100%));\n  background-image: -moz-linear-gradient(left, rgba(0, 0, 0, 0.0001) 0, rgba(0, 0, 0, 0.5) 100%);\n  background-image: linear-gradient(to right, rgba(0, 0, 0, 0.0001) 0, rgba(0, 0, 0, 0.5) 100%);\n  background-repeat: repeat-x;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);\n}\n\n.carousel-control:hover,\n.carousel-control:focus {\n  color: #ffffff;\n  text-decoration: none;\n  opacity: 0.9;\n  filter: alpha(opacity=90);\n}\n\n.carousel-control .icon-prev,\n.carousel-control .icon-next,\n.carousel-control .glyphicon-chevron-left,\n.carousel-control .glyphicon-chevron-right {\n  position: absolute;\n  top: 50%;\n  left: 50%;\n  z-index: 5;\n  display: inline-block;\n}\n\n.carousel-control .icon-prev,\n.carousel-control .icon-next {\n  width: 20px;\n  height: 20px;\n  margin-top: -10px;\n  margin-left: -10px;\n  font-family: serif;\n}\n\n.carousel-control .icon-prev:before {\n  content: '\\2039';\n}\n\n.carousel-control .icon-next:before {\n  content: '\\203a';\n}\n\n.carousel-indicators {\n  position: absolute;\n  bottom: 10px;\n  left: 50%;\n  z-index: 15;\n  width: 60%;\n  padding-left: 0;\n  margin-left: -30%;\n  text-align: center;\n  list-style: none;\n}\n\n.carousel-indicators li {\n  display: inline-block;\n  width: 10px;\n  height: 10px;\n  margin: 1px;\n  text-indent: -999px;\n  cursor: pointer;\n  border: 1px solid #ffffff;\n  border-radius: 10px;\n}\n\n.carousel-indicators .active {\n  width: 12px;\n  height: 12px;\n  margin: 0;\n  background-color: #ffffff;\n}\n\n.carousel-caption {\n  position: absolute;\n  right: 15%;\n  bottom: 20px;\n  left: 15%;\n  z-index: 10;\n  padding-top: 20px;\n  padding-bottom: 20px;\n  color: #ffffff;\n  text-align: center;\n  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);\n}\n\n.carousel-caption .btn {\n  text-shadow: none;\n}\n\n@media screen and (min-width: 768px) {\n  .carousel-control .icon-prev,\n  .carousel-control .icon-next {\n    width: 30px;\n    height: 30px;\n    margin-top: -15px;\n    margin-left: -15px;\n    font-size: 30px;\n  }\n  .carousel-caption {\n    right: 20%;\n    left: 20%;\n    padding-bottom: 30px;\n  }\n  .carousel-indicators {\n    bottom: 20px;\n  }\n}\n\n.clearfix:before,\n.clearfix:after {\n  display: table;\n  content: \" \";\n}\n\n.clearfix:after {\n  clear: both;\n}\n\n.pull-right {\n  float: right !important;\n}\n\n.pull-left {\n  float: left !important;\n}\n\n.hide {\n  display: none !important;\n}\n\n.show {\n  display: block !important;\n}\n\n.invisible {\n  visibility: hidden;\n}\n\n.text-hide {\n  font: 0/0 a;\n  color: transparent;\n  text-shadow: none;\n  background-color: transparent;\n  border: 0;\n}\n\n.affix {\n  position: fixed;\n}\n\n@-ms-viewport {\n  width: device-width;\n}\n\n@media screen and (max-width: 400px) {\n  @-ms-viewport {\n    width: 320px;\n  }\n}\n\n.hidden {\n  display: none !important;\n  visibility: hidden !important;\n}\n\n.visible-xs {\n  display: none !important;\n}\n\ntr.visible-xs {\n  display: none !important;\n}\n\nth.visible-xs,\ntd.visible-xs {\n  display: none !important;\n}\n\n@media (max-width: 767px) {\n  .visible-xs {\n    display: block !important;\n  }\n  tr.visible-xs {\n    display: table-row !important;\n  }\n  th.visible-xs,\n  td.visible-xs {\n    display: table-cell !important;\n  }\n}\n\n@media (min-width: 768px) and (max-width: 991px) {\n  .visible-xs.visible-sm {\n    display: block !important;\n  }\n  tr.visible-xs.visible-sm {\n    display: table-row !important;\n  }\n  th.visible-xs.visible-sm,\n  td.visible-xs.visible-sm {\n    display: table-cell !important;\n  }\n}\n\n@media (min-width: 992px) and (max-width: 1199px) {\n  .visible-xs.visible-md {\n    display: block !important;\n  }\n  tr.visible-xs.visible-md {\n    display: table-row !important;\n  }\n  th.visible-xs.visible-md,\n  td.visible-xs.visible-md {\n    display: table-cell !important;\n  }\n}\n\n@media (min-width: 1200px) {\n  .visible-xs.visible-lg {\n    display: block !important;\n  }\n  tr.visible-xs.visible-lg {\n    display: table-row !important;\n  }\n  th.visible-xs.visible-lg,\n  td.visible-xs.visible-lg {\n    display: table-cell !important;\n  }\n}\n\n.visible-sm {\n  display: none !important;\n}\n\ntr.visible-sm {\n  display: none !important;\n}\n\nth.visible-sm,\ntd.visible-sm {\n  display: none !important;\n}\n\n@media (max-width: 767px) {\n  .visible-sm.visible-xs {\n    display: block !important;\n  }\n  tr.visible-sm.visible-xs {\n    display: table-row !important;\n  }\n  th.visible-sm.visible-xs,\n  td.visible-sm.visible-xs {\n    display: table-cell !important;\n  }\n}\n\n@media (min-width: 768px) and (max-width: 991px) {\n  .visible-sm {\n    display: block !important;\n  }\n  tr.visible-sm {\n    display: table-row !important;\n  }\n  th.visible-sm,\n  td.visible-sm {\n    display: table-cell !important;\n  }\n}\n\n@media (min-width: 992px) and (max-width: 1199px) {\n  .visible-sm.visible-md {\n    display: block !important;\n  }\n  tr.visible-sm.visible-md {\n    display: table-row !important;\n  }\n  th.visible-sm.visible-md,\n  td.visible-sm.visible-md {\n    display: table-cell !important;\n  }\n}\n\n@media (min-width: 1200px) {\n  .visible-sm.visible-lg {\n    display: block !important;\n  }\n  tr.visible-sm.visible-lg {\n    display: table-row !important;\n  }\n  th.visible-sm.visible-lg,\n  td.visible-sm.visible-lg {\n    display: table-cell !important;\n  }\n}\n\n.visible-md {\n  display: none !important;\n}\n\ntr.visible-md {\n  display: none !important;\n}\n\nth.visible-md,\ntd.visible-md {\n  display: none !important;\n}\n\n@media (max-width: 767px) {\n  .visible-md.visible-xs {\n    display: block !important;\n  }\n  tr.visible-md.visible-xs {\n    display: table-row !important;\n  }\n  th.visible-md.visible-xs,\n  td.visible-md.visible-xs {\n    display: table-cell !important;\n  }\n}\n\n@media (min-width: 768px) and (max-width: 991px) {\n  .visible-md.visible-sm {\n    display: block !important;\n  }\n  tr.visible-md.visible-sm {\n    display: table-row !important;\n  }\n  th.visible-md.visible-sm,\n  td.visible-md.visible-sm {\n    display: table-cell !important;\n  }\n}\n\n@media (min-width: 992px) and (max-width: 1199px) {\n  .visible-md {\n    display: block !important;\n  }\n  tr.visible-md {\n    display: table-row !important;\n  }\n  th.visible-md,\n  td.visible-md {\n    display: table-cell !important;\n  }\n}\n\n@media (min-width: 1200px) {\n  .visible-md.visible-lg {\n    display: block !important;\n  }\n  tr.visible-md.visible-lg {\n    display: table-row !important;\n  }\n  th.visible-md.visible-lg,\n  td.visible-md.visible-lg {\n    display: table-cell !important;\n  }\n}\n\n.visible-lg {\n  display: none !important;\n}\n\ntr.visible-lg {\n  display: none !important;\n}\n\nth.visible-lg,\ntd.visible-lg {\n  display: none !important;\n}\n\n@media (max-width: 767px) {\n  .visible-lg.visible-xs {\n    display: block !important;\n  }\n  tr.visible-lg.visible-xs {\n    display: table-row !important;\n  }\n  th.visible-lg.visible-xs,\n  td.visible-lg.visible-xs {\n    display: table-cell !important;\n  }\n}\n\n@media (min-width: 768px) and (max-width: 991px) {\n  .visible-lg.visible-sm {\n    display: block !important;\n  }\n  tr.visible-lg.visible-sm {\n    display: table-row !important;\n  }\n  th.visible-lg.visible-sm,\n  td.visible-lg.visible-sm {\n    display: table-cell !important;\n  }\n}\n\n@media (min-width: 992px) and (max-width: 1199px) {\n  .visible-lg.visible-md {\n    display: block !important;\n  }\n  tr.visible-lg.visible-md {\n    display: table-row !important;\n  }\n  th.visible-lg.visible-md,\n  td.visible-lg.visible-md {\n    display: table-cell !important;\n  }\n}\n\n@media (min-width: 1200px) {\n  .visible-lg {\n    display: block !important;\n  }\n  tr.visible-lg {\n    display: table-row !important;\n  }\n  th.visible-lg,\n  td.visible-lg {\n    display: table-cell !important;\n  }\n}\n\n.hidden-xs {\n  display: block !important;\n}\n\ntr.hidden-xs {\n  display: table-row !important;\n}\n\nth.hidden-xs,\ntd.hidden-xs {\n  display: table-cell !important;\n}\n\n@media (max-width: 767px) {\n  .hidden-xs {\n    display: none !important;\n  }\n  tr.hidden-xs {\n    display: none !important;\n  }\n  th.hidden-xs,\n  td.hidden-xs {\n    display: none !important;\n  }\n}\n\n@media (min-width: 768px) and (max-width: 991px) {\n  .hidden-xs.hidden-sm {\n    display: none !important;\n  }\n  tr.hidden-xs.hidden-sm {\n    display: none !important;\n  }\n  th.hidden-xs.hidden-sm,\n  td.hidden-xs.hidden-sm {\n    display: none !important;\n  }\n}\n\n@media (min-width: 992px) and (max-width: 1199px) {\n  .hidden-xs.hidden-md {\n    display: none !important;\n  }\n  tr.hidden-xs.hidden-md {\n    display: none !important;\n  }\n  th.hidden-xs.hidden-md,\n  td.hidden-xs.hidden-md {\n    display: none !important;\n  }\n}\n\n@media (min-width: 1200px) {\n  .hidden-xs.hidden-lg {\n    display: none !important;\n  }\n  tr.hidden-xs.hidden-lg {\n    display: none !important;\n  }\n  th.hidden-xs.hidden-lg,\n  td.hidden-xs.hidden-lg {\n    display: none !important;\n  }\n}\n\n.hidden-sm {\n  display: block !important;\n}\n\ntr.hidden-sm {\n  display: table-row !important;\n}\n\nth.hidden-sm,\ntd.hidden-sm {\n  display: table-cell !important;\n}\n\n@media (max-width: 767px) {\n  .hidden-sm.hidden-xs {\n    display: none !important;\n  }\n  tr.hidden-sm.hidden-xs {\n    display: none !important;\n  }\n  th.hidden-sm.hidden-xs,\n  td.hidden-sm.hidden-xs {\n    display: none !important;\n  }\n}\n\n@media (min-width: 768px) and (max-width: 991px) {\n  .hidden-sm {\n    display: none !important;\n  }\n  tr.hidden-sm {\n    display: none !important;\n  }\n  th.hidden-sm,\n  td.hidden-sm {\n    display: none !important;\n  }\n}\n\n@media (min-width: 992px) and (max-width: 1199px) {\n  .hidden-sm.hidden-md {\n    display: none !important;\n  }\n  tr.hidden-sm.hidden-md {\n    display: none !important;\n  }\n  th.hidden-sm.hidden-md,\n  td.hidden-sm.hidden-md {\n    display: none !important;\n  }\n}\n\n@media (min-width: 1200px) {\n  .hidden-sm.hidden-lg {\n    display: none !important;\n  }\n  tr.hidden-sm.hidden-lg {\n    display: none !important;\n  }\n  th.hidden-sm.hidden-lg,\n  td.hidden-sm.hidden-lg {\n    display: none !important;\n  }\n}\n\n.hidden-md {\n  display: block !important;\n}\n\ntr.hidden-md {\n  display: table-row !important;\n}\n\nth.hidden-md,\ntd.hidden-md {\n  display: table-cell !important;\n}\n\n@media (max-width: 767px) {\n  .hidden-md.hidden-xs {\n    display: none !important;\n  }\n  tr.hidden-md.hidden-xs {\n    display: none !important;\n  }\n  th.hidden-md.hidden-xs,\n  td.hidden-md.hidden-xs {\n    display: none !important;\n  }\n}\n\n@media (min-width: 768px) and (max-width: 991px) {\n  .hidden-md.hidden-sm {\n    display: none !important;\n  }\n  tr.hidden-md.hidden-sm {\n    display: none !important;\n  }\n  th.hidden-md.hidden-sm,\n  td.hidden-md.hidden-sm {\n    display: none !important;\n  }\n}\n\n@media (min-width: 992px) and (max-width: 1199px) {\n  .hidden-md {\n    display: none !important;\n  }\n  tr.hidden-md {\n    display: none !important;\n  }\n  th.hidden-md,\n  td.hidden-md {\n    display: none !important;\n  }\n}\n\n@media (min-width: 1200px) {\n  .hidden-md.hidden-lg {\n    display: none !important;\n  }\n  tr.hidden-md.hidden-lg {\n    display: none !important;\n  }\n  th.hidden-md.hidden-lg,\n  td.hidden-md.hidden-lg {\n    display: none !important;\n  }\n}\n\n.hidden-lg {\n  display: block !important;\n}\n\ntr.hidden-lg {\n  display: table-row !important;\n}\n\nth.hidden-lg,\ntd.hidden-lg {\n  display: table-cell !important;\n}\n\n@media (max-width: 767px) {\n  .hidden-lg.hidden-xs {\n    display: none !important;\n  }\n  tr.hidden-lg.hidden-xs {\n    display: none !important;\n  }\n  th.hidden-lg.hidden-xs,\n  td.hidden-lg.hidden-xs {\n    display: none !important;\n  }\n}\n\n@media (min-width: 768px) and (max-width: 991px) {\n  .hidden-lg.hidden-sm {\n    display: none !important;\n  }\n  tr.hidden-lg.hidden-sm {\n    display: none !important;\n  }\n  th.hidden-lg.hidden-sm,\n  td.hidden-lg.hidden-sm {\n    display: none !important;\n  }\n}\n\n@media (min-width: 992px) and (max-width: 1199px) {\n  .hidden-lg.hidden-md {\n    display: none !important;\n  }\n  tr.hidden-lg.hidden-md {\n    display: none !important;\n  }\n  th.hidden-lg.hidden-md,\n  td.hidden-lg.hidden-md {\n    display: none !important;\n  }\n}\n\n@media (min-width: 1200px) {\n  .hidden-lg {\n    display: none !important;\n  }\n  tr.hidden-lg {\n    display: none !important;\n  }\n  th.hidden-lg,\n  td.hidden-lg {\n    display: none !important;\n  }\n}\n\n.visible-print {\n  display: none !important;\n}\n\ntr.visible-print {\n  display: none !important;\n}\n\nth.visible-print,\ntd.visible-print {\n  display: none !important;\n}\n\n@media print {\n  .visible-print {\n    display: block !important;\n  }\n  tr.visible-print {\n    display: table-row !important;\n  }\n  th.visible-print,\n  td.visible-print {\n    display: table-cell !important;\n  }\n  .hidden-print {\n    display: none !important;\n  }\n  tr.hidden-print {\n    display: none !important;\n  }\n  th.hidden-print,\n  td.hidden-print {\n    display: none !important;\n  }\n}"
  },
  {
    "path": "web/css/colorbrewer.css",
    "content": "/* This product includes color specifications and designs developed by Cynthia Brewer (http://colorbrewer.org/). */\n.YlGn .q0-3{fill:rgb(247,252,185)}\n.YlGn .q1-3{fill:rgb(173,221,142)}\n.YlGn .q2-3{fill:rgb(49,163,84)}\n.YlGn .q0-4{fill:rgb(255,255,204)}\n.YlGn .q1-4{fill:rgb(194,230,153)}\n.YlGn .q2-4{fill:rgb(120,198,121)}\n.YlGn .q3-4{fill:rgb(35,132,67)}\n.YlGn .q0-5{fill:rgb(255,255,204)}\n.YlGn .q1-5{fill:rgb(194,230,153)}\n.YlGn .q2-5{fill:rgb(120,198,121)}\n.YlGn .q3-5{fill:rgb(49,163,84)}\n.YlGn .q4-5{fill:rgb(0,104,55)}\n.YlGn .q0-6{fill:rgb(255,255,204)}\n.YlGn .q1-6{fill:rgb(217,240,163)}\n.YlGn .q2-6{fill:rgb(173,221,142)}\n.YlGn .q3-6{fill:rgb(120,198,121)}\n.YlGn .q4-6{fill:rgb(49,163,84)}\n.YlGn .q5-6{fill:rgb(0,104,55)}\n.YlGn .q0-7{fill:rgb(255,255,204)}\n.YlGn .q1-7{fill:rgb(217,240,163)}\n.YlGn .q2-7{fill:rgb(173,221,142)}\n.YlGn .q3-7{fill:rgb(120,198,121)}\n.YlGn .q4-7{fill:rgb(65,171,93)}\n.YlGn .q5-7{fill:rgb(35,132,67)}\n.YlGn .q6-7{fill:rgb(0,90,50)}\n.YlGn .q0-8{fill:rgb(255,255,229)}\n.YlGn .q1-8{fill:rgb(247,252,185)}\n.YlGn .q2-8{fill:rgb(217,240,163)}\n.YlGn .q3-8{fill:rgb(173,221,142)}\n.YlGn .q4-8{fill:rgb(120,198,121)}\n.YlGn .q5-8{fill:rgb(65,171,93)}\n.YlGn .q6-8{fill:rgb(35,132,67)}\n.YlGn .q7-8{fill:rgb(0,90,50)}\n.YlGn .q0-9{fill:rgb(255,255,229)}\n.YlGn .q1-9{fill:rgb(247,252,185)}\n.YlGn .q2-9{fill:rgb(217,240,163)}\n.YlGn .q3-9{fill:rgb(173,221,142)}\n.YlGn .q4-9{fill:rgb(120,198,121)}\n.YlGn .q5-9{fill:rgb(65,171,93)}\n.YlGn .q6-9{fill:rgb(35,132,67)}\n.YlGn .q7-9{fill:rgb(0,104,55)}\n.YlGn .q8-9{fill:rgb(0,69,41)}\n.YlGnBu .q0-3{fill:rgb(237,248,177)}\n.YlGnBu .q1-3{fill:rgb(127,205,187)}\n.YlGnBu .q2-3{fill:rgb(44,127,184)}\n.YlGnBu .q0-4{fill:rgb(255,255,204)}\n.YlGnBu .q1-4{fill:rgb(161,218,180)}\n.YlGnBu .q2-4{fill:rgb(65,182,196)}\n.YlGnBu .q3-4{fill:rgb(34,94,168)}\n.YlGnBu .q0-5{fill:rgb(255,255,204)}\n.YlGnBu .q1-5{fill:rgb(161,218,180)}\n.YlGnBu .q2-5{fill:rgb(65,182,196)}\n.YlGnBu .q3-5{fill:rgb(44,127,184)}\n.YlGnBu .q4-5{fill:rgb(37,52,148)}\n.YlGnBu .q0-6{fill:rgb(255,255,204)}\n.YlGnBu .q1-6{fill:rgb(199,233,180)}\n.YlGnBu .q2-6{fill:rgb(127,205,187)}\n.YlGnBu .q3-6{fill:rgb(65,182,196)}\n.YlGnBu .q4-6{fill:rgb(44,127,184)}\n.YlGnBu .q5-6{fill:rgb(37,52,148)}\n.YlGnBu .q0-7{fill:rgb(255,255,204)}\n.YlGnBu .q1-7{fill:rgb(199,233,180)}\n.YlGnBu .q2-7{fill:rgb(127,205,187)}\n.YlGnBu .q3-7{fill:rgb(65,182,196)}\n.YlGnBu .q4-7{fill:rgb(29,145,192)}\n.YlGnBu .q5-7{fill:rgb(34,94,168)}\n.YlGnBu .q6-7{fill:rgb(12,44,132)}\n.YlGnBu .q0-8{fill:rgb(255,255,217)}\n.YlGnBu .q1-8{fill:rgb(237,248,177)}\n.YlGnBu .q2-8{fill:rgb(199,233,180)}\n.YlGnBu .q3-8{fill:rgb(127,205,187)}\n.YlGnBu .q4-8{fill:rgb(65,182,196)}\n.YlGnBu .q5-8{fill:rgb(29,145,192)}\n.YlGnBu .q6-8{fill:rgb(34,94,168)}\n.YlGnBu .q7-8{fill:rgb(12,44,132)}\n.YlGnBu .q0-9{fill:rgb(255,255,217)}\n.YlGnBu .q1-9{fill:rgb(237,248,177)}\n.YlGnBu .q2-9{fill:rgb(199,233,180)}\n.YlGnBu .q3-9{fill:rgb(127,205,187)}\n.YlGnBu .q4-9{fill:rgb(65,182,196)}\n.YlGnBu .q5-9{fill:rgb(29,145,192)}\n.YlGnBu .q6-9{fill:rgb(34,94,168)}\n.YlGnBu .q7-9{fill:rgb(37,52,148)}\n.YlGnBu .q8-9{fill:rgb(8,29,88)}\n.GnBu .q0-3{fill:rgb(224,243,219)}\n.GnBu .q1-3{fill:rgb(168,221,181)}\n.GnBu .q2-3{fill:rgb(67,162,202)}\n.GnBu .q0-4{fill:rgb(240,249,232)}\n.GnBu .q1-4{fill:rgb(186,228,188)}\n.GnBu .q2-4{fill:rgb(123,204,196)}\n.GnBu .q3-4{fill:rgb(43,140,190)}\n.GnBu .q0-5{fill:rgb(240,249,232)}\n.GnBu .q1-5{fill:rgb(186,228,188)}\n.GnBu .q2-5{fill:rgb(123,204,196)}\n.GnBu .q3-5{fill:rgb(67,162,202)}\n.GnBu .q4-5{fill:rgb(8,104,172)}\n.GnBu .q0-6{fill:rgb(240,249,232)}\n.GnBu .q1-6{fill:rgb(204,235,197)}\n.GnBu .q2-6{fill:rgb(168,221,181)}\n.GnBu .q3-6{fill:rgb(123,204,196)}\n.GnBu .q4-6{fill:rgb(67,162,202)}\n.GnBu .q5-6{fill:rgb(8,104,172)}\n.GnBu .q0-7{fill:rgb(240,249,232)}\n.GnBu .q1-7{fill:rgb(204,235,197)}\n.GnBu .q2-7{fill:rgb(168,221,181)}\n.GnBu .q3-7{fill:rgb(123,204,196)}\n.GnBu .q4-7{fill:rgb(78,179,211)}\n.GnBu .q5-7{fill:rgb(43,140,190)}\n.GnBu .q6-7{fill:rgb(8,88,158)}\n.GnBu .q0-8{fill:rgb(247,252,240)}\n.GnBu .q1-8{fill:rgb(224,243,219)}\n.GnBu .q2-8{fill:rgb(204,235,197)}\n.GnBu .q3-8{fill:rgb(168,221,181)}\n.GnBu .q4-8{fill:rgb(123,204,196)}\n.GnBu .q5-8{fill:rgb(78,179,211)}\n.GnBu .q6-8{fill:rgb(43,140,190)}\n.GnBu .q7-8{fill:rgb(8,88,158)}\n.GnBu .q0-9{fill:rgb(247,252,240)}\n.GnBu .q1-9{fill:rgb(224,243,219)}\n.GnBu .q2-9{fill:rgb(204,235,197)}\n.GnBu .q3-9{fill:rgb(168,221,181)}\n.GnBu .q4-9{fill:rgb(123,204,196)}\n.GnBu .q5-9{fill:rgb(78,179,211)}\n.GnBu .q6-9{fill:rgb(43,140,190)}\n.GnBu .q7-9{fill:rgb(8,104,172)}\n.GnBu .q8-9{fill:rgb(8,64,129)}\n.BuGn .q0-3{fill:rgb(229,245,249)}\n.BuGn .q1-3{fill:rgb(153,216,201)}\n.BuGn .q2-3{fill:rgb(44,162,95)}\n.BuGn .q0-4{fill:rgb(237,248,251)}\n.BuGn .q1-4{fill:rgb(178,226,226)}\n.BuGn .q2-4{fill:rgb(102,194,164)}\n.BuGn .q3-4{fill:rgb(35,139,69)}\n.BuGn .q0-5{fill:rgb(237,248,251)}\n.BuGn .q1-5{fill:rgb(178,226,226)}\n.BuGn .q2-5{fill:rgb(102,194,164)}\n.BuGn .q3-5{fill:rgb(44,162,95)}\n.BuGn .q4-5{fill:rgb(0,109,44)}\n.BuGn .q0-6{fill:rgb(237,248,251)}\n.BuGn .q1-6{fill:rgb(204,236,230)}\n.BuGn .q2-6{fill:rgb(153,216,201)}\n.BuGn .q3-6{fill:rgb(102,194,164)}\n.BuGn .q4-6{fill:rgb(44,162,95)}\n.BuGn .q5-6{fill:rgb(0,109,44)}\n.BuGn .q0-7{fill:rgb(237,248,251)}\n.BuGn .q1-7{fill:rgb(204,236,230)}\n.BuGn .q2-7{fill:rgb(153,216,201)}\n.BuGn .q3-7{fill:rgb(102,194,164)}\n.BuGn .q4-7{fill:rgb(65,174,118)}\n.BuGn .q5-7{fill:rgb(35,139,69)}\n.BuGn .q6-7{fill:rgb(0,88,36)}\n.BuGn .q0-8{fill:rgb(247,252,253)}\n.BuGn .q1-8{fill:rgb(229,245,249)}\n.BuGn .q2-8{fill:rgb(204,236,230)}\n.BuGn .q3-8{fill:rgb(153,216,201)}\n.BuGn .q4-8{fill:rgb(102,194,164)}\n.BuGn .q5-8{fill:rgb(65,174,118)}\n.BuGn .q6-8{fill:rgb(35,139,69)}\n.BuGn .q7-8{fill:rgb(0,88,36)}\n.BuGn .q0-9{fill:rgb(247,252,253)}\n.BuGn .q1-9{fill:rgb(229,245,249)}\n.BuGn .q2-9{fill:rgb(204,236,230)}\n.BuGn .q3-9{fill:rgb(153,216,201)}\n.BuGn .q4-9{fill:rgb(102,194,164)}\n.BuGn .q5-9{fill:rgb(65,174,118)}\n.BuGn .q6-9{fill:rgb(35,139,69)}\n.BuGn .q7-9{fill:rgb(0,109,44)}\n.BuGn .q8-9{fill:rgb(0,68,27)}\n.PuBuGn .q0-3{fill:rgb(236,226,240)}\n.PuBuGn .q1-3{fill:rgb(166,189,219)}\n.PuBuGn .q2-3{fill:rgb(28,144,153)}\n.PuBuGn .q0-4{fill:rgb(246,239,247)}\n.PuBuGn .q1-4{fill:rgb(189,201,225)}\n.PuBuGn .q2-4{fill:rgb(103,169,207)}\n.PuBuGn .q3-4{fill:rgb(2,129,138)}\n.PuBuGn .q0-5{fill:rgb(246,239,247)}\n.PuBuGn .q1-5{fill:rgb(189,201,225)}\n.PuBuGn .q2-5{fill:rgb(103,169,207)}\n.PuBuGn .q3-5{fill:rgb(28,144,153)}\n.PuBuGn .q4-5{fill:rgb(1,108,89)}\n.PuBuGn .q0-6{fill:rgb(246,239,247)}\n.PuBuGn .q1-6{fill:rgb(208,209,230)}\n.PuBuGn .q2-6{fill:rgb(166,189,219)}\n.PuBuGn .q3-6{fill:rgb(103,169,207)}\n.PuBuGn .q4-6{fill:rgb(28,144,153)}\n.PuBuGn .q5-6{fill:rgb(1,108,89)}\n.PuBuGn .q0-7{fill:rgb(246,239,247)}\n.PuBuGn .q1-7{fill:rgb(208,209,230)}\n.PuBuGn .q2-7{fill:rgb(166,189,219)}\n.PuBuGn .q3-7{fill:rgb(103,169,207)}\n.PuBuGn .q4-7{fill:rgb(54,144,192)}\n.PuBuGn .q5-7{fill:rgb(2,129,138)}\n.PuBuGn .q6-7{fill:rgb(1,100,80)}\n.PuBuGn .q0-8{fill:rgb(255,247,251)}\n.PuBuGn .q1-8{fill:rgb(236,226,240)}\n.PuBuGn .q2-8{fill:rgb(208,209,230)}\n.PuBuGn .q3-8{fill:rgb(166,189,219)}\n.PuBuGn .q4-8{fill:rgb(103,169,207)}\n.PuBuGn .q5-8{fill:rgb(54,144,192)}\n.PuBuGn .q6-8{fill:rgb(2,129,138)}\n.PuBuGn .q7-8{fill:rgb(1,100,80)}\n.PuBuGn .q0-9{fill:rgb(255,247,251)}\n.PuBuGn .q1-9{fill:rgb(236,226,240)}\n.PuBuGn .q2-9{fill:rgb(208,209,230)}\n.PuBuGn .q3-9{fill:rgb(166,189,219)}\n.PuBuGn .q4-9{fill:rgb(103,169,207)}\n.PuBuGn .q5-9{fill:rgb(54,144,192)}\n.PuBuGn .q6-9{fill:rgb(2,129,138)}\n.PuBuGn .q7-9{fill:rgb(1,108,89)}\n.PuBuGn .q8-9{fill:rgb(1,70,54)}\n.PuBu .q0-3{fill:rgb(236,231,242)}\n.PuBu .q1-3{fill:rgb(166,189,219)}\n.PuBu .q2-3{fill:rgb(43,140,190)}\n.PuBu .q0-4{fill:rgb(241,238,246)}\n.PuBu .q1-4{fill:rgb(189,201,225)}\n.PuBu .q2-4{fill:rgb(116,169,207)}\n.PuBu .q3-4{fill:rgb(5,112,176)}\n.PuBu .q0-5{fill:rgb(241,238,246)}\n.PuBu .q1-5{fill:rgb(189,201,225)}\n.PuBu .q2-5{fill:rgb(116,169,207)}\n.PuBu .q3-5{fill:rgb(43,140,190)}\n.PuBu .q4-5{fill:rgb(4,90,141)}\n.PuBu .q0-6{fill:rgb(241,238,246)}\n.PuBu .q1-6{fill:rgb(208,209,230)}\n.PuBu .q2-6{fill:rgb(166,189,219)}\n.PuBu .q3-6{fill:rgb(116,169,207)}\n.PuBu .q4-6{fill:rgb(43,140,190)}\n.PuBu .q5-6{fill:rgb(4,90,141)}\n.PuBu .q0-7{fill:rgb(241,238,246)}\n.PuBu .q1-7{fill:rgb(208,209,230)}\n.PuBu .q2-7{fill:rgb(166,189,219)}\n.PuBu .q3-7{fill:rgb(116,169,207)}\n.PuBu .q4-7{fill:rgb(54,144,192)}\n.PuBu .q5-7{fill:rgb(5,112,176)}\n.PuBu .q6-7{fill:rgb(3,78,123)}\n.PuBu .q0-8{fill:rgb(255,247,251)}\n.PuBu .q1-8{fill:rgb(236,231,242)}\n.PuBu .q2-8{fill:rgb(208,209,230)}\n.PuBu .q3-8{fill:rgb(166,189,219)}\n.PuBu .q4-8{fill:rgb(116,169,207)}\n.PuBu .q5-8{fill:rgb(54,144,192)}\n.PuBu .q6-8{fill:rgb(5,112,176)}\n.PuBu .q7-8{fill:rgb(3,78,123)}\n.PuBu .q0-9{fill:rgb(255,247,251)}\n.PuBu .q1-9{fill:rgb(236,231,242)}\n.PuBu .q2-9{fill:rgb(208,209,230)}\n.PuBu .q3-9{fill:rgb(166,189,219)}\n.PuBu .q4-9{fill:rgb(116,169,207)}\n.PuBu .q5-9{fill:rgb(54,144,192)}\n.PuBu .q6-9{fill:rgb(5,112,176)}\n.PuBu .q7-9{fill:rgb(4,90,141)}\n.PuBu .q8-9{fill:rgb(2,56,88)}\n.BuPu .q0-3{fill:rgb(224,236,244)}\n.BuPu .q1-3{fill:rgb(158,188,218)}\n.BuPu .q2-3{fill:rgb(136,86,167)}\n.BuPu .q0-4{fill:rgb(237,248,251)}\n.BuPu .q1-4{fill:rgb(179,205,227)}\n.BuPu .q2-4{fill:rgb(140,150,198)}\n.BuPu .q3-4{fill:rgb(136,65,157)}\n.BuPu .q0-5{fill:rgb(237,248,251)}\n.BuPu .q1-5{fill:rgb(179,205,227)}\n.BuPu .q2-5{fill:rgb(140,150,198)}\n.BuPu .q3-5{fill:rgb(136,86,167)}\n.BuPu .q4-5{fill:rgb(129,15,124)}\n.BuPu .q0-6{fill:rgb(237,248,251)}\n.BuPu .q1-6{fill:rgb(191,211,230)}\n.BuPu .q2-6{fill:rgb(158,188,218)}\n.BuPu .q3-6{fill:rgb(140,150,198)}\n.BuPu .q4-6{fill:rgb(136,86,167)}\n.BuPu .q5-6{fill:rgb(129,15,124)}\n.BuPu .q0-7{fill:rgb(237,248,251)}\n.BuPu .q1-7{fill:rgb(191,211,230)}\n.BuPu .q2-7{fill:rgb(158,188,218)}\n.BuPu .q3-7{fill:rgb(140,150,198)}\n.BuPu .q4-7{fill:rgb(140,107,177)}\n.BuPu .q5-7{fill:rgb(136,65,157)}\n.BuPu .q6-7{fill:rgb(110,1,107)}\n.BuPu .q0-8{fill:rgb(247,252,253)}\n.BuPu .q1-8{fill:rgb(224,236,244)}\n.BuPu .q2-8{fill:rgb(191,211,230)}\n.BuPu .q3-8{fill:rgb(158,188,218)}\n.BuPu .q4-8{fill:rgb(140,150,198)}\n.BuPu .q5-8{fill:rgb(140,107,177)}\n.BuPu .q6-8{fill:rgb(136,65,157)}\n.BuPu .q7-8{fill:rgb(110,1,107)}\n.BuPu .q0-9{fill:rgb(247,252,253)}\n.BuPu .q1-9{fill:rgb(224,236,244)}\n.BuPu .q2-9{fill:rgb(191,211,230)}\n.BuPu .q3-9{fill:rgb(158,188,218)}\n.BuPu .q4-9{fill:rgb(140,150,198)}\n.BuPu .q5-9{fill:rgb(140,107,177)}\n.BuPu .q6-9{fill:rgb(136,65,157)}\n.BuPu .q7-9{fill:rgb(129,15,124)}\n.BuPu .q8-9{fill:rgb(77,0,75)}\n.RdPu .q0-3{fill:rgb(253,224,221)}\n.RdPu .q1-3{fill:rgb(250,159,181)}\n.RdPu .q2-3{fill:rgb(197,27,138)}\n.RdPu .q0-4{fill:rgb(254,235,226)}\n.RdPu .q1-4{fill:rgb(251,180,185)}\n.RdPu .q2-4{fill:rgb(247,104,161)}\n.RdPu .q3-4{fill:rgb(174,1,126)}\n.RdPu .q0-5{fill:rgb(254,235,226)}\n.RdPu .q1-5{fill:rgb(251,180,185)}\n.RdPu .q2-5{fill:rgb(247,104,161)}\n.RdPu .q3-5{fill:rgb(197,27,138)}\n.RdPu .q4-5{fill:rgb(122,1,119)}\n.RdPu .q0-6{fill:rgb(254,235,226)}\n.RdPu .q1-6{fill:rgb(252,197,192)}\n.RdPu .q2-6{fill:rgb(250,159,181)}\n.RdPu .q3-6{fill:rgb(247,104,161)}\n.RdPu .q4-6{fill:rgb(197,27,138)}\n.RdPu .q5-6{fill:rgb(122,1,119)}\n.RdPu .q0-7{fill:rgb(254,235,226)}\n.RdPu .q1-7{fill:rgb(252,197,192)}\n.RdPu .q2-7{fill:rgb(250,159,181)}\n.RdPu .q3-7{fill:rgb(247,104,161)}\n.RdPu .q4-7{fill:rgb(221,52,151)}\n.RdPu .q5-7{fill:rgb(174,1,126)}\n.RdPu .q6-7{fill:rgb(122,1,119)}\n.RdPu .q0-8{fill:rgb(255,247,243)}\n.RdPu .q1-8{fill:rgb(253,224,221)}\n.RdPu .q2-8{fill:rgb(252,197,192)}\n.RdPu .q3-8{fill:rgb(250,159,181)}\n.RdPu .q4-8{fill:rgb(247,104,161)}\n.RdPu .q5-8{fill:rgb(221,52,151)}\n.RdPu .q6-8{fill:rgb(174,1,126)}\n.RdPu .q7-8{fill:rgb(122,1,119)}\n.RdPu .q0-9{fill:rgb(255,247,243)}\n.RdPu .q1-9{fill:rgb(253,224,221)}\n.RdPu .q2-9{fill:rgb(252,197,192)}\n.RdPu .q3-9{fill:rgb(250,159,181)}\n.RdPu .q4-9{fill:rgb(247,104,161)}\n.RdPu .q5-9{fill:rgb(221,52,151)}\n.RdPu .q6-9{fill:rgb(174,1,126)}\n.RdPu .q7-9{fill:rgb(122,1,119)}\n.RdPu .q8-9{fill:rgb(73,0,106)}\n.PuRd .q0-3{fill:rgb(231,225,239)}\n.PuRd .q1-3{fill:rgb(201,148,199)}\n.PuRd .q2-3{fill:rgb(221,28,119)}\n.PuRd .q0-4{fill:rgb(241,238,246)}\n.PuRd .q1-4{fill:rgb(215,181,216)}\n.PuRd .q2-4{fill:rgb(223,101,176)}\n.PuRd .q3-4{fill:rgb(206,18,86)}\n.PuRd .q0-5{fill:rgb(241,238,246)}\n.PuRd .q1-5{fill:rgb(215,181,216)}\n.PuRd .q2-5{fill:rgb(223,101,176)}\n.PuRd .q3-5{fill:rgb(221,28,119)}\n.PuRd .q4-5{fill:rgb(152,0,67)}\n.PuRd .q0-6{fill:rgb(241,238,246)}\n.PuRd .q1-6{fill:rgb(212,185,218)}\n.PuRd .q2-6{fill:rgb(201,148,199)}\n.PuRd .q3-6{fill:rgb(223,101,176)}\n.PuRd .q4-6{fill:rgb(221,28,119)}\n.PuRd .q5-6{fill:rgb(152,0,67)}\n.PuRd .q0-7{fill:rgb(241,238,246)}\n.PuRd .q1-7{fill:rgb(212,185,218)}\n.PuRd .q2-7{fill:rgb(201,148,199)}\n.PuRd .q3-7{fill:rgb(223,101,176)}\n.PuRd .q4-7{fill:rgb(231,41,138)}\n.PuRd .q5-7{fill:rgb(206,18,86)}\n.PuRd .q6-7{fill:rgb(145,0,63)}\n.PuRd .q0-8{fill:rgb(247,244,249)}\n.PuRd .q1-8{fill:rgb(231,225,239)}\n.PuRd .q2-8{fill:rgb(212,185,218)}\n.PuRd .q3-8{fill:rgb(201,148,199)}\n.PuRd .q4-8{fill:rgb(223,101,176)}\n.PuRd .q5-8{fill:rgb(231,41,138)}\n.PuRd .q6-8{fill:rgb(206,18,86)}\n.PuRd .q7-8{fill:rgb(145,0,63)}\n.PuRd .q0-9{fill:rgb(247,244,249)}\n.PuRd .q1-9{fill:rgb(231,225,239)}\n.PuRd .q2-9{fill:rgb(212,185,218)}\n.PuRd .q3-9{fill:rgb(201,148,199)}\n.PuRd .q4-9{fill:rgb(223,101,176)}\n.PuRd .q5-9{fill:rgb(231,41,138)}\n.PuRd .q6-9{fill:rgb(206,18,86)}\n.PuRd .q7-9{fill:rgb(152,0,67)}\n.PuRd .q8-9{fill:rgb(103,0,31)}\n.OrRd .q0-3{fill:rgb(254,232,200)}\n.OrRd .q1-3{fill:rgb(253,187,132)}\n.OrRd .q2-3{fill:rgb(227,74,51)}\n.OrRd .q0-4{fill:rgb(254,240,217)}\n.OrRd .q1-4{fill:rgb(253,204,138)}\n.OrRd .q2-4{fill:rgb(252,141,89)}\n.OrRd .q3-4{fill:rgb(215,48,31)}\n.OrRd .q0-5{fill:rgb(254,240,217)}\n.OrRd .q1-5{fill:rgb(253,204,138)}\n.OrRd .q2-5{fill:rgb(252,141,89)}\n.OrRd .q3-5{fill:rgb(227,74,51)}\n.OrRd .q4-5{fill:rgb(179,0,0)}\n.OrRd .q0-6{fill:rgb(254,240,217)}\n.OrRd .q1-6{fill:rgb(253,212,158)}\n.OrRd .q2-6{fill:rgb(253,187,132)}\n.OrRd .q3-6{fill:rgb(252,141,89)}\n.OrRd .q4-6{fill:rgb(227,74,51)}\n.OrRd .q5-6{fill:rgb(179,0,0)}\n.OrRd .q0-7{fill:rgb(254,240,217)}\n.OrRd .q1-7{fill:rgb(253,212,158)}\n.OrRd .q2-7{fill:rgb(253,187,132)}\n.OrRd .q3-7{fill:rgb(252,141,89)}\n.OrRd .q4-7{fill:rgb(239,101,72)}\n.OrRd .q5-7{fill:rgb(215,48,31)}\n.OrRd .q6-7{fill:rgb(153,0,0)}\n.OrRd .q0-8{fill:rgb(255,247,236)}\n.OrRd .q1-8{fill:rgb(254,232,200)}\n.OrRd .q2-8{fill:rgb(253,212,158)}\n.OrRd .q3-8{fill:rgb(253,187,132)}\n.OrRd .q4-8{fill:rgb(252,141,89)}\n.OrRd .q5-8{fill:rgb(239,101,72)}\n.OrRd .q6-8{fill:rgb(215,48,31)}\n.OrRd .q7-8{fill:rgb(153,0,0)}\n.OrRd .q0-9{fill:rgb(255,247,236)}\n.OrRd .q1-9{fill:rgb(254,232,200)}\n.OrRd .q2-9{fill:rgb(253,212,158)}\n.OrRd .q3-9{fill:rgb(253,187,132)}\n.OrRd .q4-9{fill:rgb(252,141,89)}\n.OrRd .q5-9{fill:rgb(239,101,72)}\n.OrRd .q6-9{fill:rgb(215,48,31)}\n.OrRd .q7-9{fill:rgb(179,0,0)}\n.OrRd .q8-9{fill:rgb(127,0,0)}\n.YlOrRd .q0-3{fill:rgb(255,237,160)}\n.YlOrRd .q1-3{fill:rgb(254,178,76)}\n.YlOrRd .q2-3{fill:rgb(240,59,32)}\n.YlOrRd .q0-4{fill:rgb(255,255,178)}\n.YlOrRd .q1-4{fill:rgb(254,204,92)}\n.YlOrRd .q2-4{fill:rgb(253,141,60)}\n.YlOrRd .q3-4{fill:rgb(227,26,28)}\n.YlOrRd .q0-5{fill:rgb(255,255,178)}\n.YlOrRd .q1-5{fill:rgb(254,204,92)}\n.YlOrRd .q2-5{fill:rgb(253,141,60)}\n.YlOrRd .q3-5{fill:rgb(240,59,32)}\n.YlOrRd .q4-5{fill:rgb(189,0,38)}\n.YlOrRd .q0-6{fill:rgb(255,255,178)}\n.YlOrRd .q1-6{fill:rgb(254,217,118)}\n.YlOrRd .q2-6{fill:rgb(254,178,76)}\n.YlOrRd .q3-6{fill:rgb(253,141,60)}\n.YlOrRd .q4-6{fill:rgb(240,59,32)}\n.YlOrRd .q5-6{fill:rgb(189,0,38)}\n.YlOrRd .q0-7{fill:rgb(255,255,178)}\n.YlOrRd .q1-7{fill:rgb(254,217,118)}\n.YlOrRd .q2-7{fill:rgb(254,178,76)}\n.YlOrRd .q3-7{fill:rgb(253,141,60)}\n.YlOrRd .q4-7{fill:rgb(252,78,42)}\n.YlOrRd .q5-7{fill:rgb(227,26,28)}\n.YlOrRd .q6-7{fill:rgb(177,0,38)}\n.YlOrRd .q0-8{fill:rgb(255,255,204)}\n.YlOrRd .q1-8{fill:rgb(255,237,160)}\n.YlOrRd .q2-8{fill:rgb(254,217,118)}\n.YlOrRd .q3-8{fill:rgb(254,178,76)}\n.YlOrRd .q4-8{fill:rgb(253,141,60)}\n.YlOrRd .q5-8{fill:rgb(252,78,42)}\n.YlOrRd .q6-8{fill:rgb(227,26,28)}\n.YlOrRd .q7-8{fill:rgb(177,0,38)}\n.YlOrRd .q0-9{fill:rgb(255,255,204)}\n.YlOrRd .q1-9{fill:rgb(255,237,160)}\n.YlOrRd .q2-9{fill:rgb(254,217,118)}\n.YlOrRd .q3-9{fill:rgb(254,178,76)}\n.YlOrRd .q4-9{fill:rgb(253,141,60)}\n.YlOrRd .q5-9{fill:rgb(252,78,42)}\n.YlOrRd .q6-9{fill:rgb(227,26,28)}\n.YlOrRd .q7-9{fill:rgb(189,0,38)}\n.YlOrRd .q8-9{fill:rgb(128,0,38)}\n.YlOrBr .q0-3{fill:rgb(255,247,188)}\n.YlOrBr .q1-3{fill:rgb(254,196,79)}\n.YlOrBr .q2-3{fill:rgb(217,95,14)}\n.YlOrBr .q0-4{fill:rgb(255,255,212)}\n.YlOrBr .q1-4{fill:rgb(254,217,142)}\n.YlOrBr .q2-4{fill:rgb(254,153,41)}\n.YlOrBr .q3-4{fill:rgb(204,76,2)}\n.YlOrBr .q0-5{fill:rgb(255,255,212)}\n.YlOrBr .q1-5{fill:rgb(254,217,142)}\n.YlOrBr .q2-5{fill:rgb(254,153,41)}\n.YlOrBr .q3-5{fill:rgb(217,95,14)}\n.YlOrBr .q4-5{fill:rgb(153,52,4)}\n.YlOrBr .q0-6{fill:rgb(255,255,212)}\n.YlOrBr .q1-6{fill:rgb(254,227,145)}\n.YlOrBr .q2-6{fill:rgb(254,196,79)}\n.YlOrBr .q3-6{fill:rgb(254,153,41)}\n.YlOrBr .q4-6{fill:rgb(217,95,14)}\n.YlOrBr .q5-6{fill:rgb(153,52,4)}\n.YlOrBr .q0-7{fill:rgb(255,255,212)}\n.YlOrBr .q1-7{fill:rgb(254,227,145)}\n.YlOrBr .q2-7{fill:rgb(254,196,79)}\n.YlOrBr .q3-7{fill:rgb(254,153,41)}\n.YlOrBr .q4-7{fill:rgb(236,112,20)}\n.YlOrBr .q5-7{fill:rgb(204,76,2)}\n.YlOrBr .q6-7{fill:rgb(140,45,4)}\n.YlOrBr .q0-8{fill:rgb(255,255,229)}\n.YlOrBr .q1-8{fill:rgb(255,247,188)}\n.YlOrBr .q2-8{fill:rgb(254,227,145)}\n.YlOrBr .q3-8{fill:rgb(254,196,79)}\n.YlOrBr .q4-8{fill:rgb(254,153,41)}\n.YlOrBr .q5-8{fill:rgb(236,112,20)}\n.YlOrBr .q6-8{fill:rgb(204,76,2)}\n.YlOrBr .q7-8{fill:rgb(140,45,4)}\n.YlOrBr .q0-9{fill:rgb(255,255,229)}\n.YlOrBr .q1-9{fill:rgb(255,247,188)}\n.YlOrBr .q2-9{fill:rgb(254,227,145)}\n.YlOrBr .q3-9{fill:rgb(254,196,79)}\n.YlOrBr .q4-9{fill:rgb(254,153,41)}\n.YlOrBr .q5-9{fill:rgb(236,112,20)}\n.YlOrBr .q6-9{fill:rgb(204,76,2)}\n.YlOrBr .q7-9{fill:rgb(153,52,4)}\n.YlOrBr .q8-9{fill:rgb(102,37,6)}\n.Purples .q0-3{fill:rgb(239,237,245)}\n.Purples .q1-3{fill:rgb(188,189,220)}\n.Purples .q2-3{fill:rgb(117,107,177)}\n.Purples .q0-4{fill:rgb(242,240,247)}\n.Purples .q1-4{fill:rgb(203,201,226)}\n.Purples .q2-4{fill:rgb(158,154,200)}\n.Purples .q3-4{fill:rgb(106,81,163)}\n.Purples .q0-5{fill:rgb(242,240,247)}\n.Purples .q1-5{fill:rgb(203,201,226)}\n.Purples .q2-5{fill:rgb(158,154,200)}\n.Purples .q3-5{fill:rgb(117,107,177)}\n.Purples .q4-5{fill:rgb(84,39,143)}\n.Purples .q0-6{fill:rgb(242,240,247)}\n.Purples .q1-6{fill:rgb(218,218,235)}\n.Purples .q2-6{fill:rgb(188,189,220)}\n.Purples .q3-6{fill:rgb(158,154,200)}\n.Purples .q4-6{fill:rgb(117,107,177)}\n.Purples .q5-6{fill:rgb(84,39,143)}\n.Purples .q0-7{fill:rgb(242,240,247)}\n.Purples .q1-7{fill:rgb(218,218,235)}\n.Purples .q2-7{fill:rgb(188,189,220)}\n.Purples .q3-7{fill:rgb(158,154,200)}\n.Purples .q4-7{fill:rgb(128,125,186)}\n.Purples .q5-7{fill:rgb(106,81,163)}\n.Purples .q6-7{fill:rgb(74,20,134)}\n.Purples .q0-8{fill:rgb(252,251,253)}\n.Purples .q1-8{fill:rgb(239,237,245)}\n.Purples .q2-8{fill:rgb(218,218,235)}\n.Purples .q3-8{fill:rgb(188,189,220)}\n.Purples .q4-8{fill:rgb(158,154,200)}\n.Purples .q5-8{fill:rgb(128,125,186)}\n.Purples .q6-8{fill:rgb(106,81,163)}\n.Purples .q7-8{fill:rgb(74,20,134)}\n.Purples .q0-9{fill:rgb(252,251,253)}\n.Purples .q1-9{fill:rgb(239,237,245)}\n.Purples .q2-9{fill:rgb(218,218,235)}\n.Purples .q3-9{fill:rgb(188,189,220)}\n.Purples .q4-9{fill:rgb(158,154,200)}\n.Purples .q5-9{fill:rgb(128,125,186)}\n.Purples .q6-9{fill:rgb(106,81,163)}\n.Purples .q7-9{fill:rgb(84,39,143)}\n.Purples .q8-9{fill:rgb(63,0,125)}\n.Blues .q0-3{fill:rgb(222,235,247)}\n.Blues .q1-3{fill:rgb(158,202,225)}\n.Blues .q2-3{fill:rgb(49,130,189)}\n.Blues .q0-4{fill:rgb(239,243,255)}\n.Blues .q1-4{fill:rgb(189,215,231)}\n.Blues .q2-4{fill:rgb(107,174,214)}\n.Blues .q3-4{fill:rgb(33,113,181)}\n.Blues .q0-5{fill:rgb(239,243,255)}\n.Blues .q1-5{fill:rgb(189,215,231)}\n.Blues .q2-5{fill:rgb(107,174,214)}\n.Blues .q3-5{fill:rgb(49,130,189)}\n.Blues .q4-5{fill:rgb(8,81,156)}\n.Blues .q0-6{fill:rgb(239,243,255)}\n.Blues .q1-6{fill:rgb(198,219,239)}\n.Blues .q2-6{fill:rgb(158,202,225)}\n.Blues .q3-6{fill:rgb(107,174,214)}\n.Blues .q4-6{fill:rgb(49,130,189)}\n.Blues .q5-6{fill:rgb(8,81,156)}\n.Blues .q0-7{fill:rgb(239,243,255)}\n.Blues .q1-7{fill:rgb(198,219,239)}\n.Blues .q2-7{fill:rgb(158,202,225)}\n.Blues .q3-7{fill:rgb(107,174,214)}\n.Blues .q4-7{fill:rgb(66,146,198)}\n.Blues .q5-7{fill:rgb(33,113,181)}\n.Blues .q6-7{fill:rgb(8,69,148)}\n.Blues .q0-8{fill:rgb(247,251,255)}\n.Blues .q1-8{fill:rgb(222,235,247)}\n.Blues .q2-8{fill:rgb(198,219,239)}\n.Blues .q3-8{fill:rgb(158,202,225)}\n.Blues .q4-8{fill:rgb(107,174,214)}\n.Blues .q5-8{fill:rgb(66,146,198)}\n.Blues .q6-8{fill:rgb(33,113,181)}\n.Blues .q7-8{fill:rgb(8,69,148)}\n.Blues .q0-9{fill:rgb(247,251,255)}\n.Blues .q1-9{fill:rgb(222,235,247)}\n.Blues .q2-9{fill:rgb(198,219,239)}\n.Blues .q3-9{fill:rgb(158,202,225)}\n.Blues .q4-9{fill:rgb(107,174,214)}\n.Blues .q5-9{fill:rgb(66,146,198)}\n.Blues .q6-9{fill:rgb(33,113,181)}\n.Blues .q7-9{fill:rgb(8,81,156)}\n.Blues .q8-9{fill:rgb(8,48,107)}\n.Greens .q0-3{fill:rgb(229,245,224)}\n.Greens .q1-3{fill:rgb(161,217,155)}\n.Greens .q2-3{fill:rgb(49,163,84)}\n.Greens .q0-4{fill:rgb(237,248,233)}\n.Greens .q1-4{fill:rgb(186,228,179)}\n.Greens .q2-4{fill:rgb(116,196,118)}\n.Greens .q3-4{fill:rgb(35,139,69)}\n.Greens .q0-5{fill:rgb(237,248,233)}\n.Greens .q1-5{fill:rgb(186,228,179)}\n.Greens .q2-5{fill:rgb(116,196,118)}\n.Greens .q3-5{fill:rgb(49,163,84)}\n.Greens .q4-5{fill:rgb(0,109,44)}\n.Greens .q0-6{fill:rgb(237,248,233)}\n.Greens .q1-6{fill:rgb(199,233,192)}\n.Greens .q2-6{fill:rgb(161,217,155)}\n.Greens .q3-6{fill:rgb(116,196,118)}\n.Greens .q4-6{fill:rgb(49,163,84)}\n.Greens .q5-6{fill:rgb(0,109,44)}\n.Greens .q0-7{fill:rgb(237,248,233)}\n.Greens .q1-7{fill:rgb(199,233,192)}\n.Greens .q2-7{fill:rgb(161,217,155)}\n.Greens .q3-7{fill:rgb(116,196,118)}\n.Greens .q4-7{fill:rgb(65,171,93)}\n.Greens .q5-7{fill:rgb(35,139,69)}\n.Greens .q6-7{fill:rgb(0,90,50)}\n.Greens .q0-8{fill:rgb(247,252,245)}\n.Greens .q1-8{fill:rgb(229,245,224)}\n.Greens .q2-8{fill:rgb(199,233,192)}\n.Greens .q3-8{fill:rgb(161,217,155)}\n.Greens .q4-8{fill:rgb(116,196,118)}\n.Greens .q5-8{fill:rgb(65,171,93)}\n.Greens .q6-8{fill:rgb(35,139,69)}\n.Greens .q7-8{fill:rgb(0,90,50)}\n.Greens .q0-9{fill:rgb(247,252,245)}\n.Greens .q1-9{fill:rgb(229,245,224)}\n.Greens .q2-9{fill:rgb(199,233,192)}\n.Greens .q3-9{fill:rgb(161,217,155)}\n.Greens .q4-9{fill:rgb(116,196,118)}\n.Greens .q5-9{fill:rgb(65,171,93)}\n.Greens .q6-9{fill:rgb(35,139,69)}\n.Greens .q7-9{fill:rgb(0,109,44)}\n.Greens .q8-9{fill:rgb(0,68,27)}\n.Oranges .q0-3{fill:rgb(254,230,206)}\n.Oranges .q1-3{fill:rgb(253,174,107)}\n.Oranges .q2-3{fill:rgb(230,85,13)}\n.Oranges .q0-4{fill:rgb(254,237,222)}\n.Oranges .q1-4{fill:rgb(253,190,133)}\n.Oranges .q2-4{fill:rgb(253,141,60)}\n.Oranges .q3-4{fill:rgb(217,71,1)}\n.Oranges .q0-5{fill:rgb(254,237,222)}\n.Oranges .q1-5{fill:rgb(253,190,133)}\n.Oranges .q2-5{fill:rgb(253,141,60)}\n.Oranges .q3-5{fill:rgb(230,85,13)}\n.Oranges .q4-5{fill:rgb(166,54,3)}\n.Oranges .q0-6{fill:rgb(254,237,222)}\n.Oranges .q1-6{fill:rgb(253,208,162)}\n.Oranges .q2-6{fill:rgb(253,174,107)}\n.Oranges .q3-6{fill:rgb(253,141,60)}\n.Oranges .q4-6{fill:rgb(230,85,13)}\n.Oranges .q5-6{fill:rgb(166,54,3)}\n.Oranges .q0-7{fill:rgb(254,237,222)}\n.Oranges .q1-7{fill:rgb(253,208,162)}\n.Oranges .q2-7{fill:rgb(253,174,107)}\n.Oranges .q3-7{fill:rgb(253,141,60)}\n.Oranges .q4-7{fill:rgb(241,105,19)}\n.Oranges .q5-7{fill:rgb(217,72,1)}\n.Oranges .q6-7{fill:rgb(140,45,4)}\n.Oranges .q0-8{fill:rgb(255,245,235)}\n.Oranges .q1-8{fill:rgb(254,230,206)}\n.Oranges .q2-8{fill:rgb(253,208,162)}\n.Oranges .q3-8{fill:rgb(253,174,107)}\n.Oranges .q4-8{fill:rgb(253,141,60)}\n.Oranges .q5-8{fill:rgb(241,105,19)}\n.Oranges .q6-8{fill:rgb(217,72,1)}\n.Oranges .q7-8{fill:rgb(140,45,4)}\n.Oranges .q0-9{fill:rgb(255,245,235)}\n.Oranges .q1-9{fill:rgb(254,230,206)}\n.Oranges .q2-9{fill:rgb(253,208,162)}\n.Oranges .q3-9{fill:rgb(253,174,107)}\n.Oranges .q4-9{fill:rgb(253,141,60)}\n.Oranges .q5-9{fill:rgb(241,105,19)}\n.Oranges .q6-9{fill:rgb(217,72,1)}\n.Oranges .q7-9{fill:rgb(166,54,3)}\n.Oranges .q8-9{fill:rgb(127,39,4)}\n.Reds .q0-3{fill:rgb(254,224,210)}\n.Reds .q1-3{fill:rgb(252,146,114)}\n.Reds .q2-3{fill:rgb(222,45,38)}\n.Reds .q0-4{fill:rgb(254,229,217)}\n.Reds .q1-4{fill:rgb(252,174,145)}\n.Reds .q2-4{fill:rgb(251,106,74)}\n.Reds .q3-4{fill:rgb(203,24,29)}\n.Reds .q0-5{fill:rgb(254,229,217)}\n.Reds .q1-5{fill:rgb(252,174,145)}\n.Reds .q2-5{fill:rgb(251,106,74)}\n.Reds .q3-5{fill:rgb(222,45,38)}\n.Reds .q4-5{fill:rgb(165,15,21)}\n.Reds .q0-6{fill:rgb(254,229,217)}\n.Reds .q1-6{fill:rgb(252,187,161)}\n.Reds .q2-6{fill:rgb(252,146,114)}\n.Reds .q3-6{fill:rgb(251,106,74)}\n.Reds .q4-6{fill:rgb(222,45,38)}\n.Reds .q5-6{fill:rgb(165,15,21)}\n.Reds .q0-7{fill:rgb(254,229,217)}\n.Reds .q1-7{fill:rgb(252,187,161)}\n.Reds .q2-7{fill:rgb(252,146,114)}\n.Reds .q3-7{fill:rgb(251,106,74)}\n.Reds .q4-7{fill:rgb(239,59,44)}\n.Reds .q5-7{fill:rgb(203,24,29)}\n.Reds .q6-7{fill:rgb(153,0,13)}\n.Reds .q0-8{fill:rgb(255,245,240)}\n.Reds .q1-8{fill:rgb(254,224,210)}\n.Reds .q2-8{fill:rgb(252,187,161)}\n.Reds .q3-8{fill:rgb(252,146,114)}\n.Reds .q4-8{fill:rgb(251,106,74)}\n.Reds .q5-8{fill:rgb(239,59,44)}\n.Reds .q6-8{fill:rgb(203,24,29)}\n.Reds .q7-8{fill:rgb(153,0,13)}\n.Reds .q0-9{fill:rgb(255,245,240)}\n.Reds .q1-9{fill:rgb(254,224,210)}\n.Reds .q2-9{fill:rgb(252,187,161)}\n.Reds .q3-9{fill:rgb(252,146,114)}\n.Reds .q4-9{fill:rgb(251,106,74)}\n.Reds .q5-9{fill:rgb(239,59,44)}\n.Reds .q6-9{fill:rgb(203,24,29)}\n.Reds .q7-9{fill:rgb(165,15,21)}\n.Reds .q8-9{fill:rgb(103,0,13)}\n.Greys .q0-3{fill:rgb(240,240,240)}\n.Greys .q1-3{fill:rgb(189,189,189)}\n.Greys .q2-3{fill:rgb(99,99,99)}\n.Greys .q0-4{fill:rgb(247,247,247)}\n.Greys .q1-4{fill:rgb(204,204,204)}\n.Greys .q2-4{fill:rgb(150,150,150)}\n.Greys .q3-4{fill:rgb(82,82,82)}\n.Greys .q0-5{fill:rgb(247,247,247)}\n.Greys .q1-5{fill:rgb(204,204,204)}\n.Greys .q2-5{fill:rgb(150,150,150)}\n.Greys .q3-5{fill:rgb(99,99,99)}\n.Greys .q4-5{fill:rgb(37,37,37)}\n.Greys .q0-6{fill:rgb(247,247,247)}\n.Greys .q1-6{fill:rgb(217,217,217)}\n.Greys .q2-6{fill:rgb(189,189,189)}\n.Greys .q3-6{fill:rgb(150,150,150)}\n.Greys .q4-6{fill:rgb(99,99,99)}\n.Greys .q5-6{fill:rgb(37,37,37)}\n.Greys .q0-7{fill:rgb(247,247,247)}\n.Greys .q1-7{fill:rgb(217,217,217)}\n.Greys .q2-7{fill:rgb(189,189,189)}\n.Greys .q3-7{fill:rgb(150,150,150)}\n.Greys .q4-7{fill:rgb(115,115,115)}\n.Greys .q5-7{fill:rgb(82,82,82)}\n.Greys .q6-7{fill:rgb(37,37,37)}\n.Greys .q0-8{fill:rgb(255,255,255)}\n.Greys .q1-8{fill:rgb(240,240,240)}\n.Greys .q2-8{fill:rgb(217,217,217)}\n.Greys .q3-8{fill:rgb(189,189,189)}\n.Greys .q4-8{fill:rgb(150,150,150)}\n.Greys .q5-8{fill:rgb(115,115,115)}\n.Greys .q6-8{fill:rgb(82,82,82)}\n.Greys .q7-8{fill:rgb(37,37,37)}\n.Greys .q0-9{fill:rgb(255,255,255)}\n.Greys .q1-9{fill:rgb(240,240,240)}\n.Greys .q2-9{fill:rgb(217,217,217)}\n.Greys .q3-9{fill:rgb(189,189,189)}\n.Greys .q4-9{fill:rgb(150,150,150)}\n.Greys .q5-9{fill:rgb(115,115,115)}\n.Greys .q6-9{fill:rgb(82,82,82)}\n.Greys .q7-9{fill:rgb(37,37,37)}\n.Greys .q8-9{fill:rgb(0,0,0)}\n.PuOr .q0-3{fill:rgb(241,163,64)}\n.PuOr .q1-3{fill:rgb(247,247,247)}\n.PuOr .q2-3{fill:rgb(153,142,195)}\n.PuOr .q0-4{fill:rgb(230,97,1)}\n.PuOr .q1-4{fill:rgb(253,184,99)}\n.PuOr .q2-4{fill:rgb(178,171,210)}\n.PuOr .q3-4{fill:rgb(94,60,153)}\n.PuOr .q0-5{fill:rgb(230,97,1)}\n.PuOr .q1-5{fill:rgb(253,184,99)}\n.PuOr .q2-5{fill:rgb(247,247,247)}\n.PuOr .q3-5{fill:rgb(178,171,210)}\n.PuOr .q4-5{fill:rgb(94,60,153)}\n.PuOr .q0-6{fill:rgb(179,88,6)}\n.PuOr .q1-6{fill:rgb(241,163,64)}\n.PuOr .q2-6{fill:rgb(254,224,182)}\n.PuOr .q3-6{fill:rgb(216,218,235)}\n.PuOr .q4-6{fill:rgb(153,142,195)}\n.PuOr .q5-6{fill:rgb(84,39,136)}\n.PuOr .q0-7{fill:rgb(179,88,6)}\n.PuOr .q1-7{fill:rgb(241,163,64)}\n.PuOr .q2-7{fill:rgb(254,224,182)}\n.PuOr .q3-7{fill:rgb(247,247,247)}\n.PuOr .q4-7{fill:rgb(216,218,235)}\n.PuOr .q5-7{fill:rgb(153,142,195)}\n.PuOr .q6-7{fill:rgb(84,39,136)}\n.PuOr .q0-8{fill:rgb(179,88,6)}\n.PuOr .q1-8{fill:rgb(224,130,20)}\n.PuOr .q2-8{fill:rgb(253,184,99)}\n.PuOr .q3-8{fill:rgb(254,224,182)}\n.PuOr .q4-8{fill:rgb(216,218,235)}\n.PuOr .q5-8{fill:rgb(178,171,210)}\n.PuOr .q6-8{fill:rgb(128,115,172)}\n.PuOr .q7-8{fill:rgb(84,39,136)}\n.PuOr .q0-9{fill:rgb(179,88,6)}\n.PuOr .q1-9{fill:rgb(224,130,20)}\n.PuOr .q2-9{fill:rgb(253,184,99)}\n.PuOr .q3-9{fill:rgb(254,224,182)}\n.PuOr .q4-9{fill:rgb(247,247,247)}\n.PuOr .q5-9{fill:rgb(216,218,235)}\n.PuOr .q6-9{fill:rgb(178,171,210)}\n.PuOr .q7-9{fill:rgb(128,115,172)}\n.PuOr .q8-9{fill:rgb(84,39,136)}\n.PuOr .q0-10{fill:rgb(127,59,8)}\n.PuOr .q1-10{fill:rgb(179,88,6)}\n.PuOr .q2-10{fill:rgb(224,130,20)}\n.PuOr .q3-10{fill:rgb(253,184,99)}\n.PuOr .q4-10{fill:rgb(254,224,182)}\n.PuOr .q5-10{fill:rgb(216,218,235)}\n.PuOr .q6-10{fill:rgb(178,171,210)}\n.PuOr .q7-10{fill:rgb(128,115,172)}\n.PuOr .q8-10{fill:rgb(84,39,136)}\n.PuOr .q9-10{fill:rgb(45,0,75)}\n.PuOr .q0-11{fill:rgb(127,59,8)}\n.PuOr .q1-11{fill:rgb(179,88,6)}\n.PuOr .q2-11{fill:rgb(224,130,20)}\n.PuOr .q3-11{fill:rgb(253,184,99)}\n.PuOr .q4-11{fill:rgb(254,224,182)}\n.PuOr .q5-11{fill:rgb(247,247,247)}\n.PuOr .q6-11{fill:rgb(216,218,235)}\n.PuOr .q7-11{fill:rgb(178,171,210)}\n.PuOr .q8-11{fill:rgb(128,115,172)}\n.PuOr .q9-11{fill:rgb(84,39,136)}\n.PuOr .q10-11{fill:rgb(45,0,75)}\n.BrBG .q0-3{fill:rgb(216,179,101)}\n.BrBG .q1-3{fill:rgb(245,245,245)}\n.BrBG .q2-3{fill:rgb(90,180,172)}\n.BrBG .q0-4{fill:rgb(166,97,26)}\n.BrBG .q1-4{fill:rgb(223,194,125)}\n.BrBG .q2-4{fill:rgb(128,205,193)}\n.BrBG .q3-4{fill:rgb(1,133,113)}\n.BrBG .q0-5{fill:rgb(166,97,26)}\n.BrBG .q1-5{fill:rgb(223,194,125)}\n.BrBG .q2-5{fill:rgb(245,245,245)}\n.BrBG .q3-5{fill:rgb(128,205,193)}\n.BrBG .q4-5{fill:rgb(1,133,113)}\n.BrBG .q0-6{fill:rgb(140,81,10)}\n.BrBG .q1-6{fill:rgb(216,179,101)}\n.BrBG .q2-6{fill:rgb(246,232,195)}\n.BrBG .q3-6{fill:rgb(199,234,229)}\n.BrBG .q4-6{fill:rgb(90,180,172)}\n.BrBG .q5-6{fill:rgb(1,102,94)}\n.BrBG .q0-7{fill:rgb(140,81,10)}\n.BrBG .q1-7{fill:rgb(216,179,101)}\n.BrBG .q2-7{fill:rgb(246,232,195)}\n.BrBG .q3-7{fill:rgb(245,245,245)}\n.BrBG .q4-7{fill:rgb(199,234,229)}\n.BrBG .q5-7{fill:rgb(90,180,172)}\n.BrBG .q6-7{fill:rgb(1,102,94)}\n.BrBG .q0-8{fill:rgb(140,81,10)}\n.BrBG .q1-8{fill:rgb(191,129,45)}\n.BrBG .q2-8{fill:rgb(223,194,125)}\n.BrBG .q3-8{fill:rgb(246,232,195)}\n.BrBG .q4-8{fill:rgb(199,234,229)}\n.BrBG .q5-8{fill:rgb(128,205,193)}\n.BrBG .q6-8{fill:rgb(53,151,143)}\n.BrBG .q7-8{fill:rgb(1,102,94)}\n.BrBG .q0-9{fill:rgb(140,81,10)}\n.BrBG .q1-9{fill:rgb(191,129,45)}\n.BrBG .q2-9{fill:rgb(223,194,125)}\n.BrBG .q3-9{fill:rgb(246,232,195)}\n.BrBG .q4-9{fill:rgb(245,245,245)}\n.BrBG .q5-9{fill:rgb(199,234,229)}\n.BrBG .q6-9{fill:rgb(128,205,193)}\n.BrBG .q7-9{fill:rgb(53,151,143)}\n.BrBG .q8-9{fill:rgb(1,102,94)}\n.BrBG .q0-10{fill:rgb(84,48,5)}\n.BrBG .q1-10{fill:rgb(140,81,10)}\n.BrBG .q2-10{fill:rgb(191,129,45)}\n.BrBG .q3-10{fill:rgb(223,194,125)}\n.BrBG .q4-10{fill:rgb(246,232,195)}\n.BrBG .q5-10{fill:rgb(199,234,229)}\n.BrBG .q6-10{fill:rgb(128,205,193)}\n.BrBG .q7-10{fill:rgb(53,151,143)}\n.BrBG .q8-10{fill:rgb(1,102,94)}\n.BrBG .q9-10{fill:rgb(0,60,48)}\n.BrBG .q0-11{fill:rgb(84,48,5)}\n.BrBG .q1-11{fill:rgb(140,81,10)}\n.BrBG .q2-11{fill:rgb(191,129,45)}\n.BrBG .q3-11{fill:rgb(223,194,125)}\n.BrBG .q4-11{fill:rgb(246,232,195)}\n.BrBG .q5-11{fill:rgb(245,245,245)}\n.BrBG .q6-11{fill:rgb(199,234,229)}\n.BrBG .q7-11{fill:rgb(128,205,193)}\n.BrBG .q8-11{fill:rgb(53,151,143)}\n.BrBG .q9-11{fill:rgb(1,102,94)}\n.BrBG .q10-11{fill:rgb(0,60,48)}\n.PRGn .q0-3{fill:rgb(175,141,195)}\n.PRGn .q1-3{fill:rgb(247,247,247)}\n.PRGn .q2-3{fill:rgb(127,191,123)}\n.PRGn .q0-4{fill:rgb(123,50,148)}\n.PRGn .q1-4{fill:rgb(194,165,207)}\n.PRGn .q2-4{fill:rgb(166,219,160)}\n.PRGn .q3-4{fill:rgb(0,136,55)}\n.PRGn .q0-5{fill:rgb(123,50,148)}\n.PRGn .q1-5{fill:rgb(194,165,207)}\n.PRGn .q2-5{fill:rgb(247,247,247)}\n.PRGn .q3-5{fill:rgb(166,219,160)}\n.PRGn .q4-5{fill:rgb(0,136,55)}\n.PRGn .q0-6{fill:rgb(118,42,131)}\n.PRGn .q1-6{fill:rgb(175,141,195)}\n.PRGn .q2-6{fill:rgb(231,212,232)}\n.PRGn .q3-6{fill:rgb(217,240,211)}\n.PRGn .q4-6{fill:rgb(127,191,123)}\n.PRGn .q5-6{fill:rgb(27,120,55)}\n.PRGn .q0-7{fill:rgb(118,42,131)}\n.PRGn .q1-7{fill:rgb(175,141,195)}\n.PRGn .q2-7{fill:rgb(231,212,232)}\n.PRGn .q3-7{fill:rgb(247,247,247)}\n.PRGn .q4-7{fill:rgb(217,240,211)}\n.PRGn .q5-7{fill:rgb(127,191,123)}\n.PRGn .q6-7{fill:rgb(27,120,55)}\n.PRGn .q0-8{fill:rgb(118,42,131)}\n.PRGn .q1-8{fill:rgb(153,112,171)}\n.PRGn .q2-8{fill:rgb(194,165,207)}\n.PRGn .q3-8{fill:rgb(231,212,232)}\n.PRGn .q4-8{fill:rgb(217,240,211)}\n.PRGn .q5-8{fill:rgb(166,219,160)}\n.PRGn .q6-8{fill:rgb(90,174,97)}\n.PRGn .q7-8{fill:rgb(27,120,55)}\n.PRGn .q0-9{fill:rgb(118,42,131)}\n.PRGn .q1-9{fill:rgb(153,112,171)}\n.PRGn .q2-9{fill:rgb(194,165,207)}\n.PRGn .q3-9{fill:rgb(231,212,232)}\n.PRGn .q4-9{fill:rgb(247,247,247)}\n.PRGn .q5-9{fill:rgb(217,240,211)}\n.PRGn .q6-9{fill:rgb(166,219,160)}\n.PRGn .q7-9{fill:rgb(90,174,97)}\n.PRGn .q8-9{fill:rgb(27,120,55)}\n.PRGn .q0-10{fill:rgb(64,0,75)}\n.PRGn .q1-10{fill:rgb(118,42,131)}\n.PRGn .q2-10{fill:rgb(153,112,171)}\n.PRGn .q3-10{fill:rgb(194,165,207)}\n.PRGn .q4-10{fill:rgb(231,212,232)}\n.PRGn .q5-10{fill:rgb(217,240,211)}\n.PRGn .q6-10{fill:rgb(166,219,160)}\n.PRGn .q7-10{fill:rgb(90,174,97)}\n.PRGn .q8-10{fill:rgb(27,120,55)}\n.PRGn .q9-10{fill:rgb(0,68,27)}\n.PRGn .q0-11{fill:rgb(64,0,75)}\n.PRGn .q1-11{fill:rgb(118,42,131)}\n.PRGn .q2-11{fill:rgb(153,112,171)}\n.PRGn .q3-11{fill:rgb(194,165,207)}\n.PRGn .q4-11{fill:rgb(231,212,232)}\n.PRGn .q5-11{fill:rgb(247,247,247)}\n.PRGn .q6-11{fill:rgb(217,240,211)}\n.PRGn .q7-11{fill:rgb(166,219,160)}\n.PRGn .q8-11{fill:rgb(90,174,97)}\n.PRGn .q9-11{fill:rgb(27,120,55)}\n.PRGn .q10-11{fill:rgb(0,68,27)}\n.PiYG .q0-3{fill:rgb(233,163,201)}\n.PiYG .q1-3{fill:rgb(247,247,247)}\n.PiYG .q2-3{fill:rgb(161,215,106)}\n.PiYG .q0-4{fill:rgb(208,28,139)}\n.PiYG .q1-4{fill:rgb(241,182,218)}\n.PiYG .q2-4{fill:rgb(184,225,134)}\n.PiYG .q3-4{fill:rgb(77,172,38)}\n.PiYG .q0-5{fill:rgb(208,28,139)}\n.PiYG .q1-5{fill:rgb(241,182,218)}\n.PiYG .q2-5{fill:rgb(247,247,247)}\n.PiYG .q3-5{fill:rgb(184,225,134)}\n.PiYG .q4-5{fill:rgb(77,172,38)}\n.PiYG .q0-6{fill:rgb(197,27,125)}\n.PiYG .q1-6{fill:rgb(233,163,201)}\n.PiYG .q2-6{fill:rgb(253,224,239)}\n.PiYG .q3-6{fill:rgb(230,245,208)}\n.PiYG .q4-6{fill:rgb(161,215,106)}\n.PiYG .q5-6{fill:rgb(77,146,33)}\n.PiYG .q0-7{fill:rgb(197,27,125)}\n.PiYG .q1-7{fill:rgb(233,163,201)}\n.PiYG .q2-7{fill:rgb(253,224,239)}\n.PiYG .q3-7{fill:rgb(247,247,247)}\n.PiYG .q4-7{fill:rgb(230,245,208)}\n.PiYG .q5-7{fill:rgb(161,215,106)}\n.PiYG .q6-7{fill:rgb(77,146,33)}\n.PiYG .q0-8{fill:rgb(197,27,125)}\n.PiYG .q1-8{fill:rgb(222,119,174)}\n.PiYG .q2-8{fill:rgb(241,182,218)}\n.PiYG .q3-8{fill:rgb(253,224,239)}\n.PiYG .q4-8{fill:rgb(230,245,208)}\n.PiYG .q5-8{fill:rgb(184,225,134)}\n.PiYG .q6-8{fill:rgb(127,188,65)}\n.PiYG .q7-8{fill:rgb(77,146,33)}\n.PiYG .q0-9{fill:rgb(197,27,125)}\n.PiYG .q1-9{fill:rgb(222,119,174)}\n.PiYG .q2-9{fill:rgb(241,182,218)}\n.PiYG .q3-9{fill:rgb(253,224,239)}\n.PiYG .q4-9{fill:rgb(247,247,247)}\n.PiYG .q5-9{fill:rgb(230,245,208)}\n.PiYG .q6-9{fill:rgb(184,225,134)}\n.PiYG .q7-9{fill:rgb(127,188,65)}\n.PiYG .q8-9{fill:rgb(77,146,33)}\n.PiYG .q0-10{fill:rgb(142,1,82)}\n.PiYG .q1-10{fill:rgb(197,27,125)}\n.PiYG .q2-10{fill:rgb(222,119,174)}\n.PiYG .q3-10{fill:rgb(241,182,218)}\n.PiYG .q4-10{fill:rgb(253,224,239)}\n.PiYG .q5-10{fill:rgb(230,245,208)}\n.PiYG .q6-10{fill:rgb(184,225,134)}\n.PiYG .q7-10{fill:rgb(127,188,65)}\n.PiYG .q8-10{fill:rgb(77,146,33)}\n.PiYG .q9-10{fill:rgb(39,100,25)}\n.PiYG .q0-11{fill:rgb(142,1,82)}\n.PiYG .q1-11{fill:rgb(197,27,125)}\n.PiYG .q2-11{fill:rgb(222,119,174)}\n.PiYG .q3-11{fill:rgb(241,182,218)}\n.PiYG .q4-11{fill:rgb(253,224,239)}\n.PiYG .q5-11{fill:rgb(247,247,247)}\n.PiYG .q6-11{fill:rgb(230,245,208)}\n.PiYG .q7-11{fill:rgb(184,225,134)}\n.PiYG .q8-11{fill:rgb(127,188,65)}\n.PiYG .q9-11{fill:rgb(77,146,33)}\n.PiYG .q10-11{fill:rgb(39,100,25)}\n.RdBu .q0-3{fill:rgb(239,138,98)}\n.RdBu .q1-3{fill:rgb(247,247,247)}\n.RdBu .q2-3{fill:rgb(103,169,207)}\n.RdBu .q0-4{fill:rgb(202,0,32)}\n.RdBu .q1-4{fill:rgb(244,165,130)}\n.RdBu .q2-4{fill:rgb(146,197,222)}\n.RdBu .q3-4{fill:rgb(5,113,176)}\n.RdBu .q0-5{fill:rgb(202,0,32)}\n.RdBu .q1-5{fill:rgb(244,165,130)}\n.RdBu .q2-5{fill:rgb(247,247,247)}\n.RdBu .q3-5{fill:rgb(146,197,222)}\n.RdBu .q4-5{fill:rgb(5,113,176)}\n.RdBu .q0-6{fill:rgb(178,24,43)}\n.RdBu .q1-6{fill:rgb(239,138,98)}\n.RdBu .q2-6{fill:rgb(253,219,199)}\n.RdBu .q3-6{fill:rgb(209,229,240)}\n.RdBu .q4-6{fill:rgb(103,169,207)}\n.RdBu .q5-6{fill:rgb(33,102,172)}\n.RdBu .q0-7{fill:rgb(178,24,43)}\n.RdBu .q1-7{fill:rgb(239,138,98)}\n.RdBu .q2-7{fill:rgb(253,219,199)}\n.RdBu .q3-7{fill:rgb(247,247,247)}\n.RdBu .q4-7{fill:rgb(209,229,240)}\n.RdBu .q5-7{fill:rgb(103,169,207)}\n.RdBu .q6-7{fill:rgb(33,102,172)}\n.RdBu .q0-8{fill:rgb(178,24,43)}\n.RdBu .q1-8{fill:rgb(214,96,77)}\n.RdBu .q2-8{fill:rgb(244,165,130)}\n.RdBu .q3-8{fill:rgb(253,219,199)}\n.RdBu .q4-8{fill:rgb(209,229,240)}\n.RdBu .q5-8{fill:rgb(146,197,222)}\n.RdBu .q6-8{fill:rgb(67,147,195)}\n.RdBu .q7-8{fill:rgb(33,102,172)}\n.RdBu .q0-9{fill:rgb(178,24,43)}\n.RdBu .q1-9{fill:rgb(214,96,77)}\n.RdBu .q2-9{fill:rgb(244,165,130)}\n.RdBu .q3-9{fill:rgb(253,219,199)}\n.RdBu .q4-9{fill:rgb(247,247,247)}\n.RdBu .q5-9{fill:rgb(209,229,240)}\n.RdBu .q6-9{fill:rgb(146,197,222)}\n.RdBu .q7-9{fill:rgb(67,147,195)}\n.RdBu .q8-9{fill:rgb(33,102,172)}\n.RdBu .q0-10{fill:rgb(103,0,31)}\n.RdBu .q1-10{fill:rgb(178,24,43)}\n.RdBu .q2-10{fill:rgb(214,96,77)}\n.RdBu .q3-10{fill:rgb(244,165,130)}\n.RdBu .q4-10{fill:rgb(253,219,199)}\n.RdBu .q5-10{fill:rgb(209,229,240)}\n.RdBu .q6-10{fill:rgb(146,197,222)}\n.RdBu .q7-10{fill:rgb(67,147,195)}\n.RdBu .q8-10{fill:rgb(33,102,172)}\n.RdBu .q9-10{fill:rgb(5,48,97)}\n.RdBu .q0-11{fill:rgb(103,0,31)}\n.RdBu .q1-11{fill:rgb(178,24,43)}\n.RdBu .q2-11{fill:rgb(214,96,77)}\n.RdBu .q3-11{fill:rgb(244,165,130)}\n.RdBu .q4-11{fill:rgb(253,219,199)}\n.RdBu .q5-11{fill:rgb(247,247,247)}\n.RdBu .q6-11{fill:rgb(209,229,240)}\n.RdBu .q7-11{fill:rgb(146,197,222)}\n.RdBu .q8-11{fill:rgb(67,147,195)}\n.RdBu .q9-11{fill:rgb(33,102,172)}\n.RdBu .q10-11{fill:rgb(5,48,97)}\n.RdGy .q0-3{fill:rgb(239,138,98)}\n.RdGy .q1-3{fill:rgb(255,255,255)}\n.RdGy .q2-3{fill:rgb(153,153,153)}\n.RdGy .q0-4{fill:rgb(202,0,32)}\n.RdGy .q1-4{fill:rgb(244,165,130)}\n.RdGy .q2-4{fill:rgb(186,186,186)}\n.RdGy .q3-4{fill:rgb(64,64,64)}\n.RdGy .q0-5{fill:rgb(202,0,32)}\n.RdGy .q1-5{fill:rgb(244,165,130)}\n.RdGy .q2-5{fill:rgb(255,255,255)}\n.RdGy .q3-5{fill:rgb(186,186,186)}\n.RdGy .q4-5{fill:rgb(64,64,64)}\n.RdGy .q0-6{fill:rgb(178,24,43)}\n.RdGy .q1-6{fill:rgb(239,138,98)}\n.RdGy .q2-6{fill:rgb(253,219,199)}\n.RdGy .q3-6{fill:rgb(224,224,224)}\n.RdGy .q4-6{fill:rgb(153,153,153)}\n.RdGy .q5-6{fill:rgb(77,77,77)}\n.RdGy .q0-7{fill:rgb(178,24,43)}\n.RdGy .q1-7{fill:rgb(239,138,98)}\n.RdGy .q2-7{fill:rgb(253,219,199)}\n.RdGy .q3-7{fill:rgb(255,255,255)}\n.RdGy .q4-7{fill:rgb(224,224,224)}\n.RdGy .q5-7{fill:rgb(153,153,153)}\n.RdGy .q6-7{fill:rgb(77,77,77)}\n.RdGy .q0-8{fill:rgb(178,24,43)}\n.RdGy .q1-8{fill:rgb(214,96,77)}\n.RdGy .q2-8{fill:rgb(244,165,130)}\n.RdGy .q3-8{fill:rgb(253,219,199)}\n.RdGy .q4-8{fill:rgb(224,224,224)}\n.RdGy .q5-8{fill:rgb(186,186,186)}\n.RdGy .q6-8{fill:rgb(135,135,135)}\n.RdGy .q7-8{fill:rgb(77,77,77)}\n.RdGy .q0-9{fill:rgb(178,24,43)}\n.RdGy .q1-9{fill:rgb(214,96,77)}\n.RdGy .q2-9{fill:rgb(244,165,130)}\n.RdGy .q3-9{fill:rgb(253,219,199)}\n.RdGy .q4-9{fill:rgb(255,255,255)}\n.RdGy .q5-9{fill:rgb(224,224,224)}\n.RdGy .q6-9{fill:rgb(186,186,186)}\n.RdGy .q7-9{fill:rgb(135,135,135)}\n.RdGy .q8-9{fill:rgb(77,77,77)}\n.RdGy .q0-10{fill:rgb(103,0,31)}\n.RdGy .q1-10{fill:rgb(178,24,43)}\n.RdGy .q2-10{fill:rgb(214,96,77)}\n.RdGy .q3-10{fill:rgb(244,165,130)}\n.RdGy .q4-10{fill:rgb(253,219,199)}\n.RdGy .q5-10{fill:rgb(224,224,224)}\n.RdGy .q6-10{fill:rgb(186,186,186)}\n.RdGy .q7-10{fill:rgb(135,135,135)}\n.RdGy .q8-10{fill:rgb(77,77,77)}\n.RdGy .q9-10{fill:rgb(26,26,26)}\n.RdGy .q0-11{fill:rgb(103,0,31)}\n.RdGy .q1-11{fill:rgb(178,24,43)}\n.RdGy .q2-11{fill:rgb(214,96,77)}\n.RdGy .q3-11{fill:rgb(244,165,130)}\n.RdGy .q4-11{fill:rgb(253,219,199)}\n.RdGy .q5-11{fill:rgb(255,255,255)}\n.RdGy .q6-11{fill:rgb(224,224,224)}\n.RdGy .q7-11{fill:rgb(186,186,186)}\n.RdGy .q8-11{fill:rgb(135,135,135)}\n.RdGy .q9-11{fill:rgb(77,77,77)}\n.RdGy .q10-11{fill:rgb(26,26,26)}\n.RdYlBu .q0-3{fill:rgb(252,141,89)}\n.RdYlBu .q1-3{fill:rgb(255,255,191)}\n.RdYlBu .q2-3{fill:rgb(145,191,219)}\n.RdYlBu .q0-4{fill:rgb(215,25,28)}\n.RdYlBu .q1-4{fill:rgb(253,174,97)}\n.RdYlBu .q2-4{fill:rgb(171,217,233)}\n.RdYlBu .q3-4{fill:rgb(44,123,182)}\n.RdYlBu .q0-5{fill:rgb(215,25,28)}\n.RdYlBu .q1-5{fill:rgb(253,174,97)}\n.RdYlBu .q2-5{fill:rgb(255,255,191)}\n.RdYlBu .q3-5{fill:rgb(171,217,233)}\n.RdYlBu .q4-5{fill:rgb(44,123,182)}\n.RdYlBu .q0-6{fill:rgb(215,48,39)}\n.RdYlBu .q1-6{fill:rgb(252,141,89)}\n.RdYlBu .q2-6{fill:rgb(254,224,144)}\n.RdYlBu .q3-6{fill:rgb(224,243,248)}\n.RdYlBu .q4-6{fill:rgb(145,191,219)}\n.RdYlBu .q5-6{fill:rgb(69,117,180)}\n.RdYlBu .q0-7{fill:rgb(215,48,39)}\n.RdYlBu .q1-7{fill:rgb(252,141,89)}\n.RdYlBu .q2-7{fill:rgb(254,224,144)}\n.RdYlBu .q3-7{fill:rgb(255,255,191)}\n.RdYlBu .q4-7{fill:rgb(224,243,248)}\n.RdYlBu .q5-7{fill:rgb(145,191,219)}\n.RdYlBu .q6-7{fill:rgb(69,117,180)}\n.RdYlBu .q0-8{fill:rgb(215,48,39)}\n.RdYlBu .q1-8{fill:rgb(244,109,67)}\n.RdYlBu .q2-8{fill:rgb(253,174,97)}\n.RdYlBu .q3-8{fill:rgb(254,224,144)}\n.RdYlBu .q4-8{fill:rgb(224,243,248)}\n.RdYlBu .q5-8{fill:rgb(171,217,233)}\n.RdYlBu .q6-8{fill:rgb(116,173,209)}\n.RdYlBu .q7-8{fill:rgb(69,117,180)}\n.RdYlBu .q0-9{fill:rgb(215,48,39)}\n.RdYlBu .q1-9{fill:rgb(244,109,67)}\n.RdYlBu .q2-9{fill:rgb(253,174,97)}\n.RdYlBu .q3-9{fill:rgb(254,224,144)}\n.RdYlBu .q4-9{fill:rgb(255,255,191)}\n.RdYlBu .q5-9{fill:rgb(224,243,248)}\n.RdYlBu .q6-9{fill:rgb(171,217,233)}\n.RdYlBu .q7-9{fill:rgb(116,173,209)}\n.RdYlBu .q8-9{fill:rgb(69,117,180)}\n.RdYlBu .q0-10{fill:rgb(165,0,38)}\n.RdYlBu .q1-10{fill:rgb(215,48,39)}\n.RdYlBu .q2-10{fill:rgb(244,109,67)}\n.RdYlBu .q3-10{fill:rgb(253,174,97)}\n.RdYlBu .q4-10{fill:rgb(254,224,144)}\n.RdYlBu .q5-10{fill:rgb(224,243,248)}\n.RdYlBu .q6-10{fill:rgb(171,217,233)}\n.RdYlBu .q7-10{fill:rgb(116,173,209)}\n.RdYlBu .q8-10{fill:rgb(69,117,180)}\n.RdYlBu .q9-10{fill:rgb(49,54,149)}\n.RdYlBu .q0-11{fill:rgb(165,0,38)}\n.RdYlBu .q1-11{fill:rgb(215,48,39)}\n.RdYlBu .q2-11{fill:rgb(244,109,67)}\n.RdYlBu .q3-11{fill:rgb(253,174,97)}\n.RdYlBu .q4-11{fill:rgb(254,224,144)}\n.RdYlBu .q5-11{fill:rgb(255,255,191)}\n.RdYlBu .q6-11{fill:rgb(224,243,248)}\n.RdYlBu .q7-11{fill:rgb(171,217,233)}\n.RdYlBu .q8-11{fill:rgb(116,173,209)}\n.RdYlBu .q9-11{fill:rgb(69,117,180)}\n.RdYlBu .q10-11{fill:rgb(49,54,149)}\n.Spectral .q0-3{fill:rgb(252,141,89)}\n.Spectral .q1-3{fill:rgb(255,255,191)}\n.Spectral .q2-3{fill:rgb(153,213,148)}\n.Spectral .q0-4{fill:rgb(215,25,28)}\n.Spectral .q1-4{fill:rgb(253,174,97)}\n.Spectral .q2-4{fill:rgb(171,221,164)}\n.Spectral .q3-4{fill:rgb(43,131,186)}\n.Spectral .q0-5{fill:rgb(215,25,28)}\n.Spectral .q1-5{fill:rgb(253,174,97)}\n.Spectral .q2-5{fill:rgb(255,255,191)}\n.Spectral .q3-5{fill:rgb(171,221,164)}\n.Spectral .q4-5{fill:rgb(43,131,186)}\n.Spectral .q0-6{fill:rgb(213,62,79)}\n.Spectral .q1-6{fill:rgb(252,141,89)}\n.Spectral .q2-6{fill:rgb(254,224,139)}\n.Spectral .q3-6{fill:rgb(230,245,152)}\n.Spectral .q4-6{fill:rgb(153,213,148)}\n.Spectral .q5-6{fill:rgb(50,136,189)}\n.Spectral .q0-7{fill:rgb(213,62,79)}\n.Spectral .q1-7{fill:rgb(252,141,89)}\n.Spectral .q2-7{fill:rgb(254,224,139)}\n.Spectral .q3-7{fill:rgb(255,255,191)}\n.Spectral .q4-7{fill:rgb(230,245,152)}\n.Spectral .q5-7{fill:rgb(153,213,148)}\n.Spectral .q6-7{fill:rgb(50,136,189)}\n.Spectral .q0-8{fill:rgb(213,62,79)}\n.Spectral .q1-8{fill:rgb(244,109,67)}\n.Spectral .q2-8{fill:rgb(253,174,97)}\n.Spectral .q3-8{fill:rgb(254,224,139)}\n.Spectral .q4-8{fill:rgb(230,245,152)}\n.Spectral .q5-8{fill:rgb(171,221,164)}\n.Spectral .q6-8{fill:rgb(102,194,165)}\n.Spectral .q7-8{fill:rgb(50,136,189)}\n.Spectral .q0-9{fill:rgb(213,62,79)}\n.Spectral .q1-9{fill:rgb(244,109,67)}\n.Spectral .q2-9{fill:rgb(253,174,97)}\n.Spectral .q3-9{fill:rgb(254,224,139)}\n.Spectral .q4-9{fill:rgb(255,255,191)}\n.Spectral .q5-9{fill:rgb(230,245,152)}\n.Spectral .q6-9{fill:rgb(171,221,164)}\n.Spectral .q7-9{fill:rgb(102,194,165)}\n.Spectral .q8-9{fill:rgb(50,136,189)}\n.Spectral .q0-10{fill:rgb(158,1,66)}\n.Spectral .q1-10{fill:rgb(213,62,79)}\n.Spectral .q2-10{fill:rgb(244,109,67)}\n.Spectral .q3-10{fill:rgb(253,174,97)}\n.Spectral .q4-10{fill:rgb(254,224,139)}\n.Spectral .q5-10{fill:rgb(230,245,152)}\n.Spectral .q6-10{fill:rgb(171,221,164)}\n.Spectral .q7-10{fill:rgb(102,194,165)}\n.Spectral .q8-10{fill:rgb(50,136,189)}\n.Spectral .q9-10{fill:rgb(94,79,162)}\n.Spectral .q0-11{fill:rgb(158,1,66)}\n.Spectral .q1-11{fill:rgb(213,62,79)}\n.Spectral .q2-11{fill:rgb(244,109,67)}\n.Spectral .q3-11{fill:rgb(253,174,97)}\n.Spectral .q4-11{fill:rgb(254,224,139)}\n.Spectral .q5-11{fill:rgb(255,255,191)}\n.Spectral .q6-11{fill:rgb(230,245,152)}\n.Spectral .q7-11{fill:rgb(171,221,164)}\n.Spectral .q8-11{fill:rgb(102,194,165)}\n.Spectral .q9-11{fill:rgb(50,136,189)}\n.Spectral .q10-11{fill:rgb(94,79,162)}\n.RdYlGn .q0-3{fill:rgb(252,141,89)}\n.RdYlGn .q1-3{fill:rgb(255,255,191)}\n.RdYlGn .q2-3{fill:rgb(145,207,96)}\n.RdYlGn .q0-4{fill:rgb(215,25,28)}\n.RdYlGn .q1-4{fill:rgb(253,174,97)}\n.RdYlGn .q2-4{fill:rgb(166,217,106)}\n.RdYlGn .q3-4{fill:rgb(26,150,65)}\n.RdYlGn .q0-5{fill:rgb(215,25,28)}\n.RdYlGn .q1-5{fill:rgb(253,174,97)}\n.RdYlGn .q2-5{fill:rgb(255,255,191)}\n.RdYlGn .q3-5{fill:rgb(166,217,106)}\n.RdYlGn .q4-5{fill:rgb(26,150,65)}\n.RdYlGn .q0-6{fill:rgb(215,48,39)}\n.RdYlGn .q1-6{fill:rgb(252,141,89)}\n.RdYlGn .q2-6{fill:rgb(254,224,139)}\n.RdYlGn .q3-6{fill:rgb(217,239,139)}\n.RdYlGn .q4-6{fill:rgb(145,207,96)}\n.RdYlGn .q5-6{fill:rgb(26,152,80)}\n.RdYlGn .q0-7{fill:rgb(215,48,39)}\n.RdYlGn .q1-7{fill:rgb(252,141,89)}\n.RdYlGn .q2-7{fill:rgb(254,224,139)}\n.RdYlGn .q3-7{fill:rgb(255,255,191)}\n.RdYlGn .q4-7{fill:rgb(217,239,139)}\n.RdYlGn .q5-7{fill:rgb(145,207,96)}\n.RdYlGn .q6-7{fill:rgb(26,152,80)}\n.RdYlGn .q0-8{fill:rgb(215,48,39)}\n.RdYlGn .q1-8{fill:rgb(244,109,67)}\n.RdYlGn .q2-8{fill:rgb(253,174,97)}\n.RdYlGn .q3-8{fill:rgb(254,224,139)}\n.RdYlGn .q4-8{fill:rgb(217,239,139)}\n.RdYlGn .q5-8{fill:rgb(166,217,106)}\n.RdYlGn .q6-8{fill:rgb(102,189,99)}\n.RdYlGn .q7-8{fill:rgb(26,152,80)}\n.RdYlGn .q0-9{fill:rgb(215,48,39)}\n.RdYlGn .q1-9{fill:rgb(244,109,67)}\n.RdYlGn .q2-9{fill:rgb(253,174,97)}\n.RdYlGn .q3-9{fill:rgb(254,224,139)}\n.RdYlGn .q4-9{fill:rgb(255,255,191)}\n.RdYlGn .q5-9{fill:rgb(217,239,139)}\n.RdYlGn .q6-9{fill:rgb(166,217,106)}\n.RdYlGn .q7-9{fill:rgb(102,189,99)}\n.RdYlGn .q8-9{fill:rgb(26,152,80)}\n.RdYlGn .q0-10{fill:rgb(165,0,38)}\n.RdYlGn .q1-10{fill:rgb(215,48,39)}\n.RdYlGn .q2-10{fill:rgb(244,109,67)}\n.RdYlGn .q3-10{fill:rgb(253,174,97)}\n.RdYlGn .q4-10{fill:rgb(254,224,139)}\n.RdYlGn .q5-10{fill:rgb(217,239,139)}\n.RdYlGn .q6-10{fill:rgb(166,217,106)}\n.RdYlGn .q7-10{fill:rgb(102,189,99)}\n.RdYlGn .q8-10{fill:rgb(26,152,80)}\n.RdYlGn .q9-10{fill:rgb(0,104,55)}\n.RdYlGn .q0-11{fill:rgb(165,0,38)}\n.RdYlGn .q1-11{fill:rgb(215,48,39)}\n.RdYlGn .q2-11{fill:rgb(244,109,67)}\n.RdYlGn .q3-11{fill:rgb(253,174,97)}\n.RdYlGn .q4-11{fill:rgb(254,224,139)}\n.RdYlGn .q5-11{fill:rgb(255,255,191)}\n.RdYlGn .q6-11{fill:rgb(217,239,139)}\n.RdYlGn .q7-11{fill:rgb(166,217,106)}\n.RdYlGn .q8-11{fill:rgb(102,189,99)}\n.RdYlGn .q9-11{fill:rgb(26,152,80)}\n.RdYlGn .q10-11{fill:rgb(0,104,55)}\n.Accent .q0-3{fill:rgb(127,201,127)}\n.Accent .q1-3{fill:rgb(190,174,212)}\n.Accent .q2-3{fill:rgb(253,192,134)}\n.Accent .q0-4{fill:rgb(127,201,127)}\n.Accent .q1-4{fill:rgb(190,174,212)}\n.Accent .q2-4{fill:rgb(253,192,134)}\n.Accent .q3-4{fill:rgb(255,255,153)}\n.Accent .q0-5{fill:rgb(127,201,127)}\n.Accent .q1-5{fill:rgb(190,174,212)}\n.Accent .q2-5{fill:rgb(253,192,134)}\n.Accent .q3-5{fill:rgb(255,255,153)}\n.Accent .q4-5{fill:rgb(56,108,176)}\n.Accent .q0-6{fill:rgb(127,201,127)}\n.Accent .q1-6{fill:rgb(190,174,212)}\n.Accent .q2-6{fill:rgb(253,192,134)}\n.Accent .q3-6{fill:rgb(255,255,153)}\n.Accent .q4-6{fill:rgb(56,108,176)}\n.Accent .q5-6{fill:rgb(240,2,127)}\n.Accent .q0-7{fill:rgb(127,201,127)}\n.Accent .q1-7{fill:rgb(190,174,212)}\n.Accent .q2-7{fill:rgb(253,192,134)}\n.Accent .q3-7{fill:rgb(255,255,153)}\n.Accent .q4-7{fill:rgb(56,108,176)}\n.Accent .q5-7{fill:rgb(240,2,127)}\n.Accent .q6-7{fill:rgb(191,91,23)}\n.Accent .q0-8{fill:rgb(127,201,127)}\n.Accent .q1-8{fill:rgb(190,174,212)}\n.Accent .q2-8{fill:rgb(253,192,134)}\n.Accent .q3-8{fill:rgb(255,255,153)}\n.Accent .q4-8{fill:rgb(56,108,176)}\n.Accent .q5-8{fill:rgb(240,2,127)}\n.Accent .q6-8{fill:rgb(191,91,23)}\n.Accent .q7-8{fill:rgb(102,102,102)}\n.Dark2 .q0-3{fill:rgb(27,158,119)}\n.Dark2 .q1-3{fill:rgb(217,95,2)}\n.Dark2 .q2-3{fill:rgb(117,112,179)}\n.Dark2 .q0-4{fill:rgb(27,158,119)}\n.Dark2 .q1-4{fill:rgb(217,95,2)}\n.Dark2 .q2-4{fill:rgb(117,112,179)}\n.Dark2 .q3-4{fill:rgb(231,41,138)}\n.Dark2 .q0-5{fill:rgb(27,158,119)}\n.Dark2 .q1-5{fill:rgb(217,95,2)}\n.Dark2 .q2-5{fill:rgb(117,112,179)}\n.Dark2 .q3-5{fill:rgb(231,41,138)}\n.Dark2 .q4-5{fill:rgb(102,166,30)}\n.Dark2 .q0-6{fill:rgb(27,158,119)}\n.Dark2 .q1-6{fill:rgb(217,95,2)}\n.Dark2 .q2-6{fill:rgb(117,112,179)}\n.Dark2 .q3-6{fill:rgb(231,41,138)}\n.Dark2 .q4-6{fill:rgb(102,166,30)}\n.Dark2 .q5-6{fill:rgb(230,171,2)}\n.Dark2 .q0-7{fill:rgb(27,158,119)}\n.Dark2 .q1-7{fill:rgb(217,95,2)}\n.Dark2 .q2-7{fill:rgb(117,112,179)}\n.Dark2 .q3-7{fill:rgb(231,41,138)}\n.Dark2 .q4-7{fill:rgb(102,166,30)}\n.Dark2 .q5-7{fill:rgb(230,171,2)}\n.Dark2 .q6-7{fill:rgb(166,118,29)}\n.Dark2 .q0-8{fill:rgb(27,158,119)}\n.Dark2 .q1-8{fill:rgb(217,95,2)}\n.Dark2 .q2-8{fill:rgb(117,112,179)}\n.Dark2 .q3-8{fill:rgb(231,41,138)}\n.Dark2 .q4-8{fill:rgb(102,166,30)}\n.Dark2 .q5-8{fill:rgb(230,171,2)}\n.Dark2 .q6-8{fill:rgb(166,118,29)}\n.Dark2 .q7-8{fill:rgb(102,102,102)}\n.Paired .q0-3{fill:rgb(166,206,227)}\n.Paired .q1-3{fill:rgb(31,120,180)}\n.Paired .q2-3{fill:rgb(178,223,138)}\n.Paired .q0-4{fill:rgb(166,206,227)}\n.Paired .q1-4{fill:rgb(31,120,180)}\n.Paired .q2-4{fill:rgb(178,223,138)}\n.Paired .q3-4{fill:rgb(51,160,44)}\n.Paired .q0-5{fill:rgb(166,206,227)}\n.Paired .q1-5{fill:rgb(31,120,180)}\n.Paired .q2-5{fill:rgb(178,223,138)}\n.Paired .q3-5{fill:rgb(51,160,44)}\n.Paired .q4-5{fill:rgb(251,154,153)}\n.Paired .q0-6{fill:rgb(166,206,227)}\n.Paired .q1-6{fill:rgb(31,120,180)}\n.Paired .q2-6{fill:rgb(178,223,138)}\n.Paired .q3-6{fill:rgb(51,160,44)}\n.Paired .q4-6{fill:rgb(251,154,153)}\n.Paired .q5-6{fill:rgb(227,26,28)}\n.Paired .q0-7{fill:rgb(166,206,227)}\n.Paired .q1-7{fill:rgb(31,120,180)}\n.Paired .q2-7{fill:rgb(178,223,138)}\n.Paired .q3-7{fill:rgb(51,160,44)}\n.Paired .q4-7{fill:rgb(251,154,153)}\n.Paired .q5-7{fill:rgb(227,26,28)}\n.Paired .q6-7{fill:rgb(253,191,111)}\n.Paired .q0-8{fill:rgb(166,206,227)}\n.Paired .q1-8{fill:rgb(31,120,180)}\n.Paired .q2-8{fill:rgb(178,223,138)}\n.Paired .q3-8{fill:rgb(51,160,44)}\n.Paired .q4-8{fill:rgb(251,154,153)}\n.Paired .q5-8{fill:rgb(227,26,28)}\n.Paired .q6-8{fill:rgb(253,191,111)}\n.Paired .q7-8{fill:rgb(255,127,0)}\n.Paired .q0-9{fill:rgb(166,206,227)}\n.Paired .q1-9{fill:rgb(31,120,180)}\n.Paired .q2-9{fill:rgb(178,223,138)}\n.Paired .q3-9{fill:rgb(51,160,44)}\n.Paired .q4-9{fill:rgb(251,154,153)}\n.Paired .q5-9{fill:rgb(227,26,28)}\n.Paired .q6-9{fill:rgb(253,191,111)}\n.Paired .q7-9{fill:rgb(255,127,0)}\n.Paired .q8-9{fill:rgb(202,178,214)}\n.Paired .q0-10{fill:rgb(166,206,227)}\n.Paired .q1-10{fill:rgb(31,120,180)}\n.Paired .q2-10{fill:rgb(178,223,138)}\n.Paired .q3-10{fill:rgb(51,160,44)}\n.Paired .q4-10{fill:rgb(251,154,153)}\n.Paired .q5-10{fill:rgb(227,26,28)}\n.Paired .q6-10{fill:rgb(253,191,111)}\n.Paired .q7-10{fill:rgb(255,127,0)}\n.Paired .q8-10{fill:rgb(202,178,214)}\n.Paired .q9-10{fill:rgb(106,61,154)}\n.Paired .q0-11{fill:rgb(166,206,227)}\n.Paired .q1-11{fill:rgb(31,120,180)}\n.Paired .q2-11{fill:rgb(178,223,138)}\n.Paired .q3-11{fill:rgb(51,160,44)}\n.Paired .q4-11{fill:rgb(251,154,153)}\n.Paired .q5-11{fill:rgb(227,26,28)}\n.Paired .q6-11{fill:rgb(253,191,111)}\n.Paired .q7-11{fill:rgb(255,127,0)}\n.Paired .q8-11{fill:rgb(202,178,214)}\n.Paired .q9-11{fill:rgb(106,61,154)}\n.Paired .q10-11{fill:rgb(255,255,153)}\n.Paired .q0-12{fill:rgb(166,206,227)}\n.Paired .q1-12{fill:rgb(31,120,180)}\n.Paired .q2-12{fill:rgb(178,223,138)}\n.Paired .q3-12{fill:rgb(51,160,44)}\n.Paired .q4-12{fill:rgb(251,154,153)}\n.Paired .q5-12{fill:rgb(227,26,28)}\n.Paired .q6-12{fill:rgb(253,191,111)}\n.Paired .q7-12{fill:rgb(255,127,0)}\n.Paired .q8-12{fill:rgb(202,178,214)}\n.Paired .q9-12{fill:rgb(106,61,154)}\n.Paired .q10-12{fill:rgb(255,255,153)}\n.Paired .q11-12{fill:rgb(177,89,40)}\n.Pastel1 .q0-3{fill:rgb(251,180,174)}\n.Pastel1 .q1-3{fill:rgb(179,205,227)}\n.Pastel1 .q2-3{fill:rgb(204,235,197)}\n.Pastel1 .q0-4{fill:rgb(251,180,174)}\n.Pastel1 .q1-4{fill:rgb(179,205,227)}\n.Pastel1 .q2-4{fill:rgb(204,235,197)}\n.Pastel1 .q3-4{fill:rgb(222,203,228)}\n.Pastel1 .q0-5{fill:rgb(251,180,174)}\n.Pastel1 .q1-5{fill:rgb(179,205,227)}\n.Pastel1 .q2-5{fill:rgb(204,235,197)}\n.Pastel1 .q3-5{fill:rgb(222,203,228)}\n.Pastel1 .q4-5{fill:rgb(254,217,166)}\n.Pastel1 .q0-6{fill:rgb(251,180,174)}\n.Pastel1 .q1-6{fill:rgb(179,205,227)}\n.Pastel1 .q2-6{fill:rgb(204,235,197)}\n.Pastel1 .q3-6{fill:rgb(222,203,228)}\n.Pastel1 .q4-6{fill:rgb(254,217,166)}\n.Pastel1 .q5-6{fill:rgb(255,255,204)}\n.Pastel1 .q0-7{fill:rgb(251,180,174)}\n.Pastel1 .q1-7{fill:rgb(179,205,227)}\n.Pastel1 .q2-7{fill:rgb(204,235,197)}\n.Pastel1 .q3-7{fill:rgb(222,203,228)}\n.Pastel1 .q4-7{fill:rgb(254,217,166)}\n.Pastel1 .q5-7{fill:rgb(255,255,204)}\n.Pastel1 .q6-7{fill:rgb(229,216,189)}\n.Pastel1 .q0-8{fill:rgb(251,180,174)}\n.Pastel1 .q1-8{fill:rgb(179,205,227)}\n.Pastel1 .q2-8{fill:rgb(204,235,197)}\n.Pastel1 .q3-8{fill:rgb(222,203,228)}\n.Pastel1 .q4-8{fill:rgb(254,217,166)}\n.Pastel1 .q5-8{fill:rgb(255,255,204)}\n.Pastel1 .q6-8{fill:rgb(229,216,189)}\n.Pastel1 .q7-8{fill:rgb(253,218,236)}\n.Pastel1 .q0-9{fill:rgb(251,180,174)}\n.Pastel1 .q1-9{fill:rgb(179,205,227)}\n.Pastel1 .q2-9{fill:rgb(204,235,197)}\n.Pastel1 .q3-9{fill:rgb(222,203,228)}\n.Pastel1 .q4-9{fill:rgb(254,217,166)}\n.Pastel1 .q5-9{fill:rgb(255,255,204)}\n.Pastel1 .q6-9{fill:rgb(229,216,189)}\n.Pastel1 .q7-9{fill:rgb(253,218,236)}\n.Pastel1 .q8-9{fill:rgb(242,242,242)}\n.Pastel2 .q0-3{fill:rgb(179,226,205)}\n.Pastel2 .q1-3{fill:rgb(253,205,172)}\n.Pastel2 .q2-3{fill:rgb(203,213,232)}\n.Pastel2 .q0-4{fill:rgb(179,226,205)}\n.Pastel2 .q1-4{fill:rgb(253,205,172)}\n.Pastel2 .q2-4{fill:rgb(203,213,232)}\n.Pastel2 .q3-4{fill:rgb(244,202,228)}\n.Pastel2 .q0-5{fill:rgb(179,226,205)}\n.Pastel2 .q1-5{fill:rgb(253,205,172)}\n.Pastel2 .q2-5{fill:rgb(203,213,232)}\n.Pastel2 .q3-5{fill:rgb(244,202,228)}\n.Pastel2 .q4-5{fill:rgb(230,245,201)}\n.Pastel2 .q0-6{fill:rgb(179,226,205)}\n.Pastel2 .q1-6{fill:rgb(253,205,172)}\n.Pastel2 .q2-6{fill:rgb(203,213,232)}\n.Pastel2 .q3-6{fill:rgb(244,202,228)}\n.Pastel2 .q4-6{fill:rgb(230,245,201)}\n.Pastel2 .q5-6{fill:rgb(255,242,174)}\n.Pastel2 .q0-7{fill:rgb(179,226,205)}\n.Pastel2 .q1-7{fill:rgb(253,205,172)}\n.Pastel2 .q2-7{fill:rgb(203,213,232)}\n.Pastel2 .q3-7{fill:rgb(244,202,228)}\n.Pastel2 .q4-7{fill:rgb(230,245,201)}\n.Pastel2 .q5-7{fill:rgb(255,242,174)}\n.Pastel2 .q6-7{fill:rgb(241,226,204)}\n.Pastel2 .q0-8{fill:rgb(179,226,205)}\n.Pastel2 .q1-8{fill:rgb(253,205,172)}\n.Pastel2 .q2-8{fill:rgb(203,213,232)}\n.Pastel2 .q3-8{fill:rgb(244,202,228)}\n.Pastel2 .q4-8{fill:rgb(230,245,201)}\n.Pastel2 .q5-8{fill:rgb(255,242,174)}\n.Pastel2 .q6-8{fill:rgb(241,226,204)}\n.Pastel2 .q7-8{fill:rgb(204,204,204)}\n.Set1 .q0-3{fill:rgb(228,26,28)}\n.Set1 .q1-3{fill:rgb(55,126,184)}\n.Set1 .q2-3{fill:rgb(77,175,74)}\n.Set1 .q0-4{fill:rgb(228,26,28)}\n.Set1 .q1-4{fill:rgb(55,126,184)}\n.Set1 .q2-4{fill:rgb(77,175,74)}\n.Set1 .q3-4{fill:rgb(152,78,163)}\n.Set1 .q0-5{fill:rgb(228,26,28)}\n.Set1 .q1-5{fill:rgb(55,126,184)}\n.Set1 .q2-5{fill:rgb(77,175,74)}\n.Set1 .q3-5{fill:rgb(152,78,163)}\n.Set1 .q4-5{fill:rgb(255,127,0)}\n.Set1 .q0-6{fill:rgb(228,26,28)}\n.Set1 .q1-6{fill:rgb(55,126,184)}\n.Set1 .q2-6{fill:rgb(77,175,74)}\n.Set1 .q3-6{fill:rgb(152,78,163)}\n.Set1 .q4-6{fill:rgb(255,127,0)}\n.Set1 .q5-6{fill:rgb(255,255,51)}\n.Set1 .q0-7{fill:rgb(228,26,28)}\n.Set1 .q1-7{fill:rgb(55,126,184)}\n.Set1 .q2-7{fill:rgb(77,175,74)}\n.Set1 .q3-7{fill:rgb(152,78,163)}\n.Set1 .q4-7{fill:rgb(255,127,0)}\n.Set1 .q5-7{fill:rgb(255,255,51)}\n.Set1 .q6-7{fill:rgb(166,86,40)}\n.Set1 .q0-8{fill:rgb(228,26,28)}\n.Set1 .q1-8{fill:rgb(55,126,184)}\n.Set1 .q2-8{fill:rgb(77,175,74)}\n.Set1 .q3-8{fill:rgb(152,78,163)}\n.Set1 .q4-8{fill:rgb(255,127,0)}\n.Set1 .q5-8{fill:rgb(255,255,51)}\n.Set1 .q6-8{fill:rgb(166,86,40)}\n.Set1 .q7-8{fill:rgb(247,129,191)}\n.Set1 .q0-9{fill:rgb(228,26,28)}\n.Set1 .q1-9{fill:rgb(55,126,184)}\n.Set1 .q2-9{fill:rgb(77,175,74)}\n.Set1 .q3-9{fill:rgb(152,78,163)}\n.Set1 .q4-9{fill:rgb(255,127,0)}\n.Set1 .q5-9{fill:rgb(255,255,51)}\n.Set1 .q6-9{fill:rgb(166,86,40)}\n.Set1 .q7-9{fill:rgb(247,129,191)}\n.Set1 .q8-9{fill:rgb(153,153,153)}\n.Set2 .q0-3{fill:rgb(102,194,165)}\n.Set2 .q1-3{fill:rgb(252,141,98)}\n.Set2 .q2-3{fill:rgb(141,160,203)}\n.Set2 .q0-4{fill:rgb(102,194,165)}\n.Set2 .q1-4{fill:rgb(252,141,98)}\n.Set2 .q2-4{fill:rgb(141,160,203)}\n.Set2 .q3-4{fill:rgb(231,138,195)}\n.Set2 .q0-5{fill:rgb(102,194,165)}\n.Set2 .q1-5{fill:rgb(252,141,98)}\n.Set2 .q2-5{fill:rgb(141,160,203)}\n.Set2 .q3-5{fill:rgb(231,138,195)}\n.Set2 .q4-5{fill:rgb(166,216,84)}\n.Set2 .q0-6{fill:rgb(102,194,165)}\n.Set2 .q1-6{fill:rgb(252,141,98)}\n.Set2 .q2-6{fill:rgb(141,160,203)}\n.Set2 .q3-6{fill:rgb(231,138,195)}\n.Set2 .q4-6{fill:rgb(166,216,84)}\n.Set2 .q5-6{fill:rgb(255,217,47)}\n.Set2 .q0-7{fill:rgb(102,194,165)}\n.Set2 .q1-7{fill:rgb(252,141,98)}\n.Set2 .q2-7{fill:rgb(141,160,203)}\n.Set2 .q3-7{fill:rgb(231,138,195)}\n.Set2 .q4-7{fill:rgb(166,216,84)}\n.Set2 .q5-7{fill:rgb(255,217,47)}\n.Set2 .q6-7{fill:rgb(229,196,148)}\n.Set2 .q0-8{fill:rgb(102,194,165)}\n.Set2 .q1-8{fill:rgb(252,141,98)}\n.Set2 .q2-8{fill:rgb(141,160,203)}\n.Set2 .q3-8{fill:rgb(231,138,195)}\n.Set2 .q4-8{fill:rgb(166,216,84)}\n.Set2 .q5-8{fill:rgb(255,217,47)}\n.Set2 .q6-8{fill:rgb(229,196,148)}\n.Set2 .q7-8{fill:rgb(179,179,179)}\n.Set3 .q0-3{fill:rgb(141,211,199)}\n.Set3 .q1-3{fill:rgb(255,255,179)}\n.Set3 .q2-3{fill:rgb(190,186,218)}\n.Set3 .q0-4{fill:rgb(141,211,199)}\n.Set3 .q1-4{fill:rgb(255,255,179)}\n.Set3 .q2-4{fill:rgb(190,186,218)}\n.Set3 .q3-4{fill:rgb(251,128,114)}\n.Set3 .q0-5{fill:rgb(141,211,199)}\n.Set3 .q1-5{fill:rgb(255,255,179)}\n.Set3 .q2-5{fill:rgb(190,186,218)}\n.Set3 .q3-5{fill:rgb(251,128,114)}\n.Set3 .q4-5{fill:rgb(128,177,211)}\n.Set3 .q0-6{fill:rgb(141,211,199)}\n.Set3 .q1-6{fill:rgb(255,255,179)}\n.Set3 .q2-6{fill:rgb(190,186,218)}\n.Set3 .q3-6{fill:rgb(251,128,114)}\n.Set3 .q4-6{fill:rgb(128,177,211)}\n.Set3 .q5-6{fill:rgb(253,180,98)}\n.Set3 .q0-7{fill:rgb(141,211,199)}\n.Set3 .q1-7{fill:rgb(255,255,179)}\n.Set3 .q2-7{fill:rgb(190,186,218)}\n.Set3 .q3-7{fill:rgb(251,128,114)}\n.Set3 .q4-7{fill:rgb(128,177,211)}\n.Set3 .q5-7{fill:rgb(253,180,98)}\n.Set3 .q6-7{fill:rgb(179,222,105)}\n.Set3 .q0-8{fill:rgb(141,211,199)}\n.Set3 .q1-8{fill:rgb(255,255,179)}\n.Set3 .q2-8{fill:rgb(190,186,218)}\n.Set3 .q3-8{fill:rgb(251,128,114)}\n.Set3 .q4-8{fill:rgb(128,177,211)}\n.Set3 .q5-8{fill:rgb(253,180,98)}\n.Set3 .q6-8{fill:rgb(179,222,105)}\n.Set3 .q7-8{fill:rgb(252,205,229)}\n.Set3 .q0-9{fill:rgb(141,211,199)}\n.Set3 .q1-9{fill:rgb(255,255,179)}\n.Set3 .q2-9{fill:rgb(190,186,218)}\n.Set3 .q3-9{fill:rgb(251,128,114)}\n.Set3 .q4-9{fill:rgb(128,177,211)}\n.Set3 .q5-9{fill:rgb(253,180,98)}\n.Set3 .q6-9{fill:rgb(179,222,105)}\n.Set3 .q7-9{fill:rgb(252,205,229)}\n.Set3 .q8-9{fill:rgb(217,217,217)}\n.Set3 .q0-10{fill:rgb(141,211,199)}\n.Set3 .q1-10{fill:rgb(255,255,179)}\n.Set3 .q2-10{fill:rgb(190,186,218)}\n.Set3 .q3-10{fill:rgb(251,128,114)}\n.Set3 .q4-10{fill:rgb(128,177,211)}\n.Set3 .q5-10{fill:rgb(253,180,98)}\n.Set3 .q6-10{fill:rgb(179,222,105)}\n.Set3 .q7-10{fill:rgb(252,205,229)}\n.Set3 .q8-10{fill:rgb(217,217,217)}\n.Set3 .q9-10{fill:rgb(188,128,189)}\n.Set3 .q0-11{fill:rgb(141,211,199)}\n.Set3 .q1-11{fill:rgb(255,255,179)}\n.Set3 .q2-11{fill:rgb(190,186,218)}\n.Set3 .q3-11{fill:rgb(251,128,114)}\n.Set3 .q4-11{fill:rgb(128,177,211)}\n.Set3 .q5-11{fill:rgb(253,180,98)}\n.Set3 .q6-11{fill:rgb(179,222,105)}\n.Set3 .q7-11{fill:rgb(252,205,229)}\n.Set3 .q8-11{fill:rgb(217,217,217)}\n.Set3 .q9-11{fill:rgb(188,128,189)}\n.Set3 .q10-11{fill:rgb(204,235,197)}\n.Set3 .q0-12{fill:rgb(141,211,199)}\n.Set3 .q1-12{fill:rgb(255,255,179)}\n.Set3 .q2-12{fill:rgb(190,186,218)}\n.Set3 .q3-12{fill:rgb(251,128,114)}\n.Set3 .q4-12{fill:rgb(128,177,211)}\n.Set3 .q5-12{fill:rgb(253,180,98)}\n.Set3 .q6-12{fill:rgb(179,222,105)}\n.Set3 .q7-12{fill:rgb(252,205,229)}\n.Set3 .q8-12{fill:rgb(217,217,217)}\n.Set3 .q9-12{fill:rgb(188,128,189)}\n.Set3 .q10-12{fill:rgb(204,235,197)}\n.Set3 .q11-12{fill:rgb(255,237,111)}\n"
  },
  {
    "path": "web/css/theme.css",
    "content": "body {\n  padding-top: 75px;\n  padding-bottom: 30px;\n}\n\n.container > div.navbar-header {\n  background: transparent url(../i/product-logo-rev2.png) no-repeat scroll left top;\n  background-size: 50px 50px;\n  background-position: 0 2px;\n  padding-left: 55px;\n}\n\n.theme-dropdown .dropdown-menu {\n  display: block;\n  position: static;\n  margin-bottom: 20px;\n}\n\n.theme-showcase > p > .btn {\n  margin: 5px 0;\n}\n.label-none {\n  background-color: #fff;\n}\n\nspan.exchange-rate {\n  text-align:right;\n  display: inline-block;\n  width: 8em;\n}\nspan.exchange-value {\n  text-align:right;\n  float: right;\n  display: inline-block;\n  width: auto;\n}\nspan.exchange-value:before, span.client-value:before, span.queue-value:before {\ncontent:\"(\";\n}\nspan.exchange-value:after, span.client-value:after, span.queue-value:after {\ncontent:\")\";\n}\nspan.exchange-rate:after, span.client-rate:after, span.queue-rate:after {\ncontent:\"/s\";\n}\n\n.aggregate-only {\ndisplay:none;\n}\n\ndiv#exchange-5f616767726567617465 .aggregate-only {\ndisplay:block;\n}\n\ndiv#queues div.panel-heading {\n  padding-bottom: 0;\n}\ndiv.queue-name-title a {\n margin-left: 1em;\n}\ndiv.backlog-container {\n  width:80px;\n}\ndiv.queue-name-title a:hover {\n  text-decoration:none;\n}\ntable.clients td {\n  font-size: 12px;\n}\ntable.clients th,\ntable.clients td {\n  width: 8%;\n  text-align:right;\n}\ntable.clients th.gap {\n  width:1em;\n}\ntable.clients th.top-span {\n  width:auto;\n  border-bottom:1;\n  border-color:#999;\n}\ntable.clients th.client-user,\ntable.clients td.client-user {\n  text-align:left;\n}\ntable.clients th.client-address,\ntable.clients td.client-address {\n  width: 18%;\n  text-align:center;\n}\n\n@media (min-width: 768px) {\ndl.dl-horizontal {\n  margin-bottom:0;\n}\n.dl-horizontal dt {\n  width:100px;\n}\n.dl-horizontal dd {\n  margin-left: 6em;\n}\n}\n"
  },
  {
    "path": "web/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <meta name=\"description\" content=\"\">\n    <meta name=\"author\" content=\"\">\n    <link rel=\"shortcut icon\" href=\"favicon.png\">\n\n    <title>Circonus Fq Operational Dashboard</title>\n\n    <!-- Bootstrap core CSS -->\n    <link href=\"css/bootstrap.css\" rel=\"stylesheet\">\n    <!-- Bootstrap theme -->\n    <link href=\"css/bootstrap-theme.min.css\" rel=\"stylesheet\">\n\n    <!-- Custom styles for this template -->\n    <link href=\"css/colorbrewer.css\" rel=\"stylesheet\">\n    <link href=\"css/theme.css\" rel=\"stylesheet\">\n\n    <!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->\n    <!--[if lt IE 9]>\n      <script src=\"js/html5shiv.js\"></script>\n      <script src=\"js/respond.min.js\"></script>\n    <![endif]-->\n  </head>\n\n  <body>\n\n    <!-- Fixed navbar -->\n    <div class=\"navbar navbar-fixed-top\">\n      <div class=\"container\">\n        <div class=\"navbar-header\">\n          <a class=\"navbar-brand\" href=\"#\">Circonus Fq <span id=\"fq-version\"></span></a>\n        </div>\n      </div>\n    </div>\n\n    <div class=\"container theme-showcase\">\n\n      <ul id=\"viewTab\" class=\"nav nav-tabs\">\n        <li class=\"active\"><a href=\"#stateview\" data-toggle=\"tab\">Overview</a></li>\n        <li><a href=\"#queueview\" data-toggle=\"tab\">Queues</a></li>\n        <li><a href=\"#routeview\" data-toggle=\"tab\">Routes</a></li>\n      </ul>\n      <div id=\"viewTabContent\" class=\"tab-content\">\n        <div class=\"tab-pane fade active in\" id=\"stateview\">\n          <div class=\"panel panel-primary\">\n            <div class=\"panel-heading\">\n              <h4 class=\"panel-title\">Exchanges</h4>\n            </div>\n            <div class=\"panel-body\" id=\"exchanges\">\n              <div class=\"row\">\n                <div class=\"col-md-4 exchange-detail\" id=\"exchange-5f616767726567617465\">\n                  <div class=\"panel panel-default\">\n                    <div class=\"panel-heading\">\n                      <h4 class=\"panel-title\">_aggregate</h4>\n                    </div>\n                    <div class=\"panel-body\">\n<dl class=\"dl-horizontal\">\n<dt>Messages In</dt><dd><span class=\"exchange-messages-rate exchange-rate\"></span> <span class=\"exchange-messages exchange-value\"></span></dd>\n<dt>Octets In</dt><dd><span class=\"exchange-octets-rate exchange-rate\"></span> <span class=\"exchange-octets exchange-value\"></span></dd>\n<dt>Routed</dt><dd><span class=\"exchange-routed-rate exchange-rate\"></span> <span class=\"exchange-routed exchange-value\"></span></dd>\n<dt>Dropped</dt><dd><span class=\"exchange-dropped-rate exchange-rate\"></span> <span class=\"exchange-dropped exchange-value\"></span></dd>\n<dt>Size dropped</dt><dd><span class=\"exchange-size-dropped-rate exchange-rate\"></span> <span class=\"exchange-size-dropped exchange-value\"></span></dd>\n<dt>No Route</dt><dd><span class=\"exchange-no-route-rate exchange-rate\"></span> <span class=\"exchange-no-route exchange-value\"></span></dd>\n<dt class=\"aggregate-only\">No Exchange</dt><dd class=\"aggregate-only\"><span class=\"exchange-no-exchange-rate exchange-rate\"></span> <span class=\"exchange-no-exchange exchange-value\"></span></dd>\n</dl>\n                    </div>\n                  </div>\n                </div>\n              </div>\n            </div>\n          </div>\n        </div>\n        <div class=\"tab-pane fade\" id=\"queueview\">\n          <div class=\"panel panel-primary\">\n            <div class=\"panel-heading\">\n              <h4 class=\"panel-title\">Queues</h4>\n            </div>\n            <div class=\"panel-body\" id=\"queues\">\n            </div>\n          </div>\n        </div>\n       <div class=\"tab-pane fade\" id=\"routeview\">\n          <div class=\"panel panel-primary\">\n            <div class=\"panel-heading\">\n              <h4 class=\"panel-title\">Routes</h4>\n            </div>\n            <div class=\"panel-body\" id=\"routes\">\n            </div>\n          </div>\n        </div>\n      </div>\n      </div>\n      </div>\n\n<div style=\"display:none\">\n  <div class=\"row\" id=\"queue-template\">\n    <div class=\"col-md-12 queue-detail\">\n  <div class=\"panel panel-default\">\n    <div class=\"panel-heading\" role=\"tab\">\n        <div class=\"row\">\n          <div class=\"col-md-6\">\n            <div class=\"pull-left\"><span class=\"queue-exposure\"></span> <span class=\"queue-type\"></span> <span class=\"queue-policy\"></span></div>\n            <div class=\"queue-name-title\"><a data-toggle=\"collapse\" data-parent=\"#accordion\" href=\"#\" aria-expanded=\"true\"><span class=\"queue-name\"></span></a></div>\n          </div>\n          <div class=\"col-md-2\">\n            <td class=\"queue-dropped-out\">Dropped: <span class=\"queue-dropped-out-rate queue-rate\"></span>&nbsp;<span class=\"queue-dropped-out-value queue-value\"></span></td>\n          </div>\n          <div class=\"col-md-4\">\n            <div class=\"pull-right backlog-container text-right\"><small><span class=\"backlog\">0</span></small></div>\n            <div class=\"progress progress-striped active\">\n              <div class=\"progress-bar progress-bar-success\" role=\"progressbar\" aria-valuenow=\"0\" aria-valuemin=\"0\" aria-valuemax=\"300\" style=\"width: 5%\"></div>\n            </div>\n          </div>\n        </div>\n    </div>\n    <div class=\"panel-collapse collapse in\" role=\"tabpanel\" aria-labelledby=\"headingOne\">\n      <div class=\"panel-body\">\n      <table class=\"table table-condensed table-striped clients\">\n      <thead>\n        <tr><th class=\"client-user\" rowspan=2>User</th>\n            <th class=\"client-address\" rowspan=2>Address</th>\n            <th class=\"top-span\" colspan=2>Tx (to Client)</th>\n            <th class=\"gap\" rowspan=2></th>\n            <th class=\"top-span\" colspan=7>Rx (from Client)</th>\n        </tr>\n        <tr><th>Msgs</th><th>Octets</th>\n            <th>Msgs</th><th>Octets</th>\n            <th>Routed</th>\n            <th>Dropped</th>\n            <th>Size&nbsp;Dropped</th>\n            <th>No&nbsp;Route</th>\n            <th>No&nbsp;Exchange</th>\n        </tr>\n      </thead>\n      <tbody>\n      </tbody>\n      </table>\n      </div>\n    </div>\n  </div>\n    </div>\n  </div>\n\n  <table>\n  <tr id=\"client-template\">\n    <td class=\"client-user\"></td>\n    <td class=\"client-address\"></td>\n    <td class=\"client-msgs-out\"><span class=\"client-msgs-out-rate client-rate\"></span><br/><span class=\"client-msgs-out-value client-value\"></span></td>\n    <td class=\"client-octets-out\"><span class=\"client-octets-out-rate client-rate\"></span><br/><span class=\"client-octets-out-value client-value\"></span></td>\n    <td class=\"gap\"></td>\n    <td class=\"client-msgs-in\"><span class=\"client-msgs-in-rate client-rate\"></span><br/><span class=\"client-msgs-in-value client-value\"></span></td>\n    <td class=\"client-octets-in\"><span class=\"client-octets-in-rate client-rate\"></span><br/><span class=\"client-octets-in-value client-value\"></span></td>\n    <td class=\"client-routed\"><span class=\"client-routed-rate client-rate\"></span><br/><span class=\"client-routed-value client-value\"></span></td>\n    <td class=\"client-dropped\"><span class=\"client-dropped-rate client-rate\"></span><br/><span class=\"client-dropped-value client-value\"></span></td>\n    <td class=\"client-size-dropped\"><span class=\"client-size-dropped-rate client-rate\"></span><br/><span class=\"client-size-dropped-value client-value\"></span></td>\n    <td class=\"client-no-route\"><span class=\"client-no-route-rate client-rate\"></span><br/><span class=\"client-no-route-value client-value\"></span></td>\n    <td class=\"client-no-exchange\"><span class=\"client-no-exchange-rate client-rate\"></span><br/><span class=\"client-no-exchange-value client-value\"></span></td>\n  </tr>\n  </table>\n\n  <div id=\"route-exchange-template\">\n    <h3 class=\"exchange-name\"></h3>\n    <div class=\"row\">\n      <div class=\"col-md-1\"></div>\n      <div class=\"col-md-1\"><h5>Prefix</h5></div>\n      <div class=\"col-md-6\"><h5>Program</h5></div>\n      <div class=\"col-md-2 text-right\"><h5>Invocations</h5></div>\n      <div class=\"col-md-2 text-right\"><h5>Approximate Latency</h5></div>\n    </div>\n  </div>\n  <div class=\"row route-row\" id=\"route-detail-template\">\n    <div class=\"col-md-1\">\n      <span class=\"route-mode\"></span>\n    </div>\n    <div class=\"col-md-1\">\n      <span class=\"route-prefix\"></span>\n    </div>\n    <div class=\"col-md-6\">\n      <div class=\"route-program\"></div>\n    </div>\n    <div class=\"col-md-2 text-right\">\n      <span class=\"route-invocations\"></span>\n    </div>\n    <div class=\"col-md-2 text-right\">\n      <span class=\"route-avg-ns\"></span>\n    </div>\n  </div>\n</div>\n\n    <!-- Bootstrap core JavaScript\n    ================================================== -->\n    <!-- Placed at the end of the document so the pages load faster -->\n    <script src=\"js/jquery-3.5.1.min.js\"></script>\n    <script src=\"js/bootstrap.min.js\"></script>\n    <script src=\"js/d3.min.js\"></script>\n    <script src=\"js/colorbrewer.js\"></script>\n    <script src=\"js/fq.js\"></script>\n\n  </body>\n</html>\n\n"
  },
  {
    "path": "web/js/bootstrap.js",
    "content": "/**\n* bootstrap.js v3.0.0 by @fat and @mdo\n* Copyright 2013 Twitter Inc.\n* http://www.apache.org/licenses/LICENSE-2.0\n*/\nif (!jQuery) { throw new Error(\"Bootstrap requires jQuery\") }\n\n/* ========================================================================\n * Bootstrap: transition.js v3.0.0\n * http://twbs.github.com/bootstrap/javascript.html#transitions\n * ========================================================================\n * Copyright 2013 Twitter, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n * ======================================================================== */\n\n\n+function ($) { \"use strict\";\n\n  // CSS TRANSITION SUPPORT (Shoutout: http://www.modernizr.com/)\n  // ============================================================\n\n  function transitionEnd() {\n    var el = document.createElement('bootstrap')\n\n    var transEndEventNames = {\n      'WebkitTransition' : 'webkitTransitionEnd'\n    , 'MozTransition'    : 'transitionend'\n    , 'OTransition'      : 'oTransitionEnd otransitionend'\n    , 'transition'       : 'transitionend'\n    }\n\n    for (var name in transEndEventNames) {\n      if (el.style[name] !== undefined) {\n        return { end: transEndEventNames[name] }\n      }\n    }\n  }\n\n  // http://blog.alexmaccaw.com/css-transitions\n  $.fn.emulateTransitionEnd = function (duration) {\n    var called = false, $el = this\n    $(this).one($.support.transition.end, function () { called = true })\n    var callback = function () { if (!called) $($el).trigger($.support.transition.end) }\n    setTimeout(callback, duration)\n    return this\n  }\n\n  $(function () {\n    $.support.transition = transitionEnd()\n  })\n\n}(window.jQuery);\n\n/* ========================================================================\n * Bootstrap: alert.js v3.0.0\n * http://twbs.github.com/bootstrap/javascript.html#alerts\n * ========================================================================\n * Copyright 2013 Twitter, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n * ======================================================================== */\n\n\n+function ($) { \"use strict\";\n\n  // ALERT CLASS DEFINITION\n  // ======================\n\n  var dismiss = '[data-dismiss=\"alert\"]'\n  var Alert   = function (el) {\n    $(el).on('click', dismiss, this.close)\n  }\n\n  Alert.prototype.close = function (e) {\n    var $this    = $(this)\n    var selector = $this.attr('data-target')\n\n    if (!selector) {\n      selector = $this.attr('href')\n      selector = selector && selector.replace(/.*(?=#[^\\s]*$)/, '') // strip for ie7\n    }\n\n    var $parent = $(selector)\n\n    if (e) e.preventDefault()\n\n    if (!$parent.length) {\n      $parent = $this.hasClass('alert') ? $this : $this.parent()\n    }\n\n    $parent.trigger(e = $.Event('close.bs.alert'))\n\n    if (e.isDefaultPrevented()) return\n\n    $parent.removeClass('in')\n\n    function removeElement() {\n      $parent.trigger('closed.bs.alert').remove()\n    }\n\n    $.support.transition && $parent.hasClass('fade') ?\n      $parent\n        .one($.support.transition.end, removeElement)\n        .emulateTransitionEnd(150) :\n      removeElement()\n  }\n\n\n  // ALERT PLUGIN DEFINITION\n  // =======================\n\n  var old = $.fn.alert\n\n  $.fn.alert = function (option) {\n    return this.each(function () {\n      var $this = $(this)\n      var data  = $this.data('bs.alert')\n\n      if (!data) $this.data('bs.alert', (data = new Alert(this)))\n      if (typeof option == 'string') data[option].call($this)\n    })\n  }\n\n  $.fn.alert.Constructor = Alert\n\n\n  // ALERT NO CONFLICT\n  // =================\n\n  $.fn.alert.noConflict = function () {\n    $.fn.alert = old\n    return this\n  }\n\n\n  // ALERT DATA-API\n  // ==============\n\n  $(document).on('click.bs.alert.data-api', dismiss, Alert.prototype.close)\n\n}(window.jQuery);\n\n/* ========================================================================\n * Bootstrap: button.js v3.0.0\n * http://twbs.github.com/bootstrap/javascript.html#buttons\n * ========================================================================\n * Copyright 2013 Twitter, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n * ======================================================================== */\n\n\n+function ($) { \"use strict\";\n\n  // BUTTON PUBLIC CLASS DEFINITION\n  // ==============================\n\n  var Button = function (element, options) {\n    this.$element = $(element)\n    this.options  = $.extend({}, Button.DEFAULTS, options)\n  }\n\n  Button.DEFAULTS = {\n    loadingText: 'loading...'\n  }\n\n  Button.prototype.setState = function (state) {\n    var d    = 'disabled'\n    var $el  = this.$element\n    var val  = $el.is('input') ? 'val' : 'html'\n    var data = $el.data()\n\n    state = state + 'Text'\n\n    if (!data.resetText) $el.data('resetText', $el[val]())\n\n    $el[val](data[state] || this.options[state])\n\n    // push to event loop to allow forms to submit\n    setTimeout(function () {\n      state == 'loadingText' ?\n        $el.addClass(d).attr(d, d) :\n        $el.removeClass(d).removeAttr(d);\n    }, 0)\n  }\n\n  Button.prototype.toggle = function () {\n    var $parent = this.$element.closest('[data-toggle=\"buttons\"]')\n\n    if ($parent.length) {\n      var $input = this.$element.find('input')\n        .prop('checked', !this.$element.hasClass('active'))\n        .trigger('change')\n      if ($input.prop('type') === 'radio') $parent.find('.active').removeClass('active')\n    }\n\n    this.$element.toggleClass('active')\n  }\n\n\n  // BUTTON PLUGIN DEFINITION\n  // ========================\n\n  var old = $.fn.button\n\n  $.fn.button = function (option) {\n    return this.each(function () {\n      var $this   = $(this)\n      var data    = $this.data('bs.button')\n      var options = typeof option == 'object' && option\n\n      if (!data) $this.data('bs.button', (data = new Button(this, options)))\n\n      if (option == 'toggle') data.toggle()\n      else if (option) data.setState(option)\n    })\n  }\n\n  $.fn.button.Constructor = Button\n\n\n  // BUTTON NO CONFLICT\n  // ==================\n\n  $.fn.button.noConflict = function () {\n    $.fn.button = old\n    return this\n  }\n\n\n  // BUTTON DATA-API\n  // ===============\n\n  $(document).on('click.bs.button.data-api', '[data-toggle^=button]', function (e) {\n    var $btn = $(e.target)\n    if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn')\n    $btn.button('toggle')\n    e.preventDefault()\n  })\n\n}(window.jQuery);\n\n/* ========================================================================\n * Bootstrap: carousel.js v3.0.0\n * http://twbs.github.com/bootstrap/javascript.html#carousel\n * ========================================================================\n * Copyright 2012 Twitter, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n * ======================================================================== */\n\n\n+function ($) { \"use strict\";\n\n  // CAROUSEL CLASS DEFINITION\n  // =========================\n\n  var Carousel = function (element, options) {\n    this.$element    = $(element)\n    this.$indicators = this.$element.find('.carousel-indicators')\n    this.options     = options\n    this.paused      =\n    this.sliding     =\n    this.interval    =\n    this.$active     =\n    this.$items      = null\n\n    this.options.pause == 'hover' && this.$element\n      .on('mouseenter', $.proxy(this.pause, this))\n      .on('mouseleave', $.proxy(this.cycle, this))\n  }\n\n  Carousel.DEFAULTS = {\n    interval: 5000\n  , pause: 'hover'\n  , wrap: true\n  }\n\n  Carousel.prototype.cycle =  function (e) {\n    e || (this.paused = false)\n\n    this.interval && clearInterval(this.interval)\n\n    this.options.interval\n      && !this.paused\n      && (this.interval = setInterval($.proxy(this.next, this), this.options.interval))\n\n    return this\n  }\n\n  Carousel.prototype.getActiveIndex = function () {\n    this.$active = this.$element.find('.item.active')\n    this.$items  = this.$active.parent().children()\n\n    return this.$items.index(this.$active)\n  }\n\n  Carousel.prototype.to = function (pos) {\n    var that        = this\n    var activeIndex = this.getActiveIndex()\n\n    if (pos > (this.$items.length - 1) || pos < 0) return\n\n    if (this.sliding)       return this.$element.one('slid', function () { that.to(pos) })\n    if (activeIndex == pos) return this.pause().cycle()\n\n    return this.slide(pos > activeIndex ? 'next' : 'prev', $(this.$items[pos]))\n  }\n\n  Carousel.prototype.pause = function (e) {\n    e || (this.paused = true)\n\n    if (this.$element.find('.next, .prev').length && $.support.transition.end) {\n      this.$element.trigger($.support.transition.end)\n      this.cycle(true)\n    }\n\n    this.interval = clearInterval(this.interval)\n\n    return this\n  }\n\n  Carousel.prototype.next = function () {\n    if (this.sliding) return\n    return this.slide('next')\n  }\n\n  Carousel.prototype.prev = function () {\n    if (this.sliding) return\n    return this.slide('prev')\n  }\n\n  Carousel.prototype.slide = function (type, next) {\n    var $active   = this.$element.find('.item.active')\n    var $next     = next || $active[type]()\n    var isCycling = this.interval\n    var direction = type == 'next' ? 'left' : 'right'\n    var fallback  = type == 'next' ? 'first' : 'last'\n    var that      = this\n\n    if (!$next.length) {\n      if (!this.options.wrap) return\n      $next = this.$element.find('.item')[fallback]()\n    }\n\n    this.sliding = true\n\n    isCycling && this.pause()\n\n    var e = $.Event('slide.bs.carousel', { relatedTarget: $next[0], direction: direction })\n\n    if ($next.hasClass('active')) return\n\n    if (this.$indicators.length) {\n      this.$indicators.find('.active').removeClass('active')\n      this.$element.one('slid', function () {\n        var $nextIndicator = $(that.$indicators.children()[that.getActiveIndex()])\n        $nextIndicator && $nextIndicator.addClass('active')\n      })\n    }\n\n    if ($.support.transition && this.$element.hasClass('slide')) {\n      this.$element.trigger(e)\n      if (e.isDefaultPrevented()) return\n      $next.addClass(type)\n      $next[0].offsetWidth // force reflow\n      $active.addClass(direction)\n      $next.addClass(direction)\n      $active\n        .one($.support.transition.end, function () {\n          $next.removeClass([type, direction].join(' ')).addClass('active')\n          $active.removeClass(['active', direction].join(' '))\n          that.sliding = false\n          setTimeout(function () { that.$element.trigger('slid') }, 0)\n        })\n        .emulateTransitionEnd(600)\n    } else {\n      this.$element.trigger(e)\n      if (e.isDefaultPrevented()) return\n      $active.removeClass('active')\n      $next.addClass('active')\n      this.sliding = false\n      this.$element.trigger('slid')\n    }\n\n    isCycling && this.cycle()\n\n    return this\n  }\n\n\n  // CAROUSEL PLUGIN DEFINITION\n  // ==========================\n\n  var old = $.fn.carousel\n\n  $.fn.carousel = function (option) {\n    return this.each(function () {\n      var $this   = $(this)\n      var data    = $this.data('bs.carousel')\n      var options = $.extend({}, Carousel.DEFAULTS, $this.data(), typeof option == 'object' && option)\n      var action  = typeof option == 'string' ? option : options.slide\n\n      if (!data) $this.data('bs.carousel', (data = new Carousel(this, options)))\n      if (typeof option == 'number') data.to(option)\n      else if (action) data[action]()\n      else if (options.interval) data.pause().cycle()\n    })\n  }\n\n  $.fn.carousel.Constructor = Carousel\n\n\n  // CAROUSEL NO CONFLICT\n  // ====================\n\n  $.fn.carousel.noConflict = function () {\n    $.fn.carousel = old\n    return this\n  }\n\n\n  // CAROUSEL DATA-API\n  // =================\n\n  $(document).on('click.bs.carousel.data-api', '[data-slide], [data-slide-to]', function (e) {\n    var $this   = $(this), href\n    var $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\\s]+$)/, '')) //strip for ie7\n    var options = $.extend({}, $target.data(), $this.data())\n    var slideIndex = $this.attr('data-slide-to')\n    if (slideIndex) options.interval = false\n\n    $target.carousel(options)\n\n    if (slideIndex = $this.attr('data-slide-to')) {\n      $target.data('bs.carousel').to(slideIndex)\n    }\n\n    e.preventDefault()\n  })\n\n  $(window).on('load', function () {\n    $('[data-ride=\"carousel\"]').each(function () {\n      var $carousel = $(this)\n      $carousel.carousel($carousel.data())\n    })\n  })\n\n}(window.jQuery);\n\n/* ========================================================================\n * Bootstrap: collapse.js v3.0.0\n * http://twbs.github.com/bootstrap/javascript.html#collapse\n * ========================================================================\n * Copyright 2012 Twitter, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n * ======================================================================== */\n\n\n+function ($) { \"use strict\";\n\n  // COLLAPSE PUBLIC CLASS DEFINITION\n  // ================================\n\n  var Collapse = function (element, options) {\n    this.$element      = $(element)\n    this.options       = $.extend({}, Collapse.DEFAULTS, options)\n    this.transitioning = null\n\n    if (this.options.parent) this.$parent = $(this.options.parent)\n    if (this.options.toggle) this.toggle()\n  }\n\n  Collapse.DEFAULTS = {\n    toggle: true\n  }\n\n  Collapse.prototype.dimension = function () {\n    var hasWidth = this.$element.hasClass('width')\n    return hasWidth ? 'width' : 'height'\n  }\n\n  Collapse.prototype.show = function () {\n    if (this.transitioning || this.$element.hasClass('in')) return\n\n    var startEvent = $.Event('show.bs.collapse')\n    this.$element.trigger(startEvent)\n    if (startEvent.isDefaultPrevented()) return\n\n    var actives = this.$parent && this.$parent.find('> .panel > .in')\n\n    if (actives && actives.length) {\n      var hasData = actives.data('bs.collapse')\n      if (hasData && hasData.transitioning) return\n      actives.collapse('hide')\n      hasData || actives.data('bs.collapse', null)\n    }\n\n    var dimension = this.dimension()\n\n    this.$element\n      .removeClass('collapse')\n      .addClass('collapsing')\n      [dimension](0)\n\n    this.transitioning = 1\n\n    var complete = function () {\n      this.$element\n        .removeClass('collapsing')\n        .addClass('in')\n        [dimension]('auto')\n      this.transitioning = 0\n      this.$element.trigger('shown.bs.collapse')\n    }\n\n    if (!$.support.transition) return complete.call(this)\n\n    var scrollSize = $.camelCase(['scroll', dimension].join('-'))\n\n    this.$element\n      .one($.support.transition.end, $.proxy(complete, this))\n      .emulateTransitionEnd(350)\n      [dimension](this.$element[0][scrollSize])\n  }\n\n  Collapse.prototype.hide = function () {\n    if (this.transitioning || !this.$element.hasClass('in')) return\n\n    var startEvent = $.Event('hide.bs.collapse')\n    this.$element.trigger(startEvent)\n    if (startEvent.isDefaultPrevented()) return\n\n    var dimension = this.dimension()\n\n    this.$element\n      [dimension](this.$element[dimension]())\n      [0].offsetHeight\n\n    this.$element\n      .addClass('collapsing')\n      .removeClass('collapse')\n      .removeClass('in')\n\n    this.transitioning = 1\n\n    var complete = function () {\n      this.transitioning = 0\n      this.$element\n        .trigger('hidden.bs.collapse')\n        .removeClass('collapsing')\n        .addClass('collapse')\n    }\n\n    if (!$.support.transition) return complete.call(this)\n\n    this.$element\n      [dimension](0)\n      .one($.support.transition.end, $.proxy(complete, this))\n      .emulateTransitionEnd(350)\n  }\n\n  Collapse.prototype.toggle = function () {\n    this[this.$element.hasClass('in') ? 'hide' : 'show']()\n  }\n\n\n  // COLLAPSE PLUGIN DEFINITION\n  // ==========================\n\n  var old = $.fn.collapse\n\n  $.fn.collapse = function (option) {\n    return this.each(function () {\n      var $this   = $(this)\n      var data    = $this.data('bs.collapse')\n      var options = $.extend({}, Collapse.DEFAULTS, $this.data(), typeof option == 'object' && option)\n\n      if (!data) $this.data('bs.collapse', (data = new Collapse(this, options)))\n      if (typeof option == 'string') data[option]()\n    })\n  }\n\n  $.fn.collapse.Constructor = Collapse\n\n\n  // COLLAPSE NO CONFLICT\n  // ====================\n\n  $.fn.collapse.noConflict = function () {\n    $.fn.collapse = old\n    return this\n  }\n\n\n  // COLLAPSE DATA-API\n  // =================\n\n  $(document).on('click.bs.collapse.data-api', '[data-toggle=collapse]', function (e) {\n    var $this   = $(this), href\n    var target  = $this.attr('data-target')\n        || e.preventDefault()\n        || (href = $this.attr('href')) && href.replace(/.*(?=#[^\\s]+$)/, '') //strip for ie7\n    var $target = $(target)\n    var data    = $target.data('bs.collapse')\n    var option  = data ? 'toggle' : $this.data()\n    var parent  = $this.attr('data-parent')\n    var $parent = parent && $(parent)\n\n    if (!data || !data.transitioning) {\n      if ($parent) $parent.find('[data-toggle=collapse][data-parent=\"' + parent + '\"]').not($this).addClass('collapsed')\n      $this[$target.hasClass('in') ? 'addClass' : 'removeClass']('collapsed')\n    }\n\n    $target.collapse(option)\n  })\n\n}(window.jQuery);\n\n/* ========================================================================\n * Bootstrap: dropdown.js v3.0.0\n * http://twbs.github.com/bootstrap/javascript.html#dropdowns\n * ========================================================================\n * Copyright 2012 Twitter, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n * ======================================================================== */\n\n\n+function ($) { \"use strict\";\n\n  // DROPDOWN CLASS DEFINITION\n  // =========================\n\n  var backdrop = '.dropdown-backdrop'\n  var toggle   = '[data-toggle=dropdown]'\n  var Dropdown = function (element) {\n    var $el = $(element).on('click.bs.dropdown', this.toggle)\n  }\n\n  Dropdown.prototype.toggle = function (e) {\n    var $this = $(this)\n\n    if ($this.is('.disabled, :disabled')) return\n\n    var $parent  = getParent($this)\n    var isActive = $parent.hasClass('open')\n\n    clearMenus()\n\n    if (!isActive) {\n      if ('ontouchstart' in document.documentElement && !$parent.closest('.navbar-nav').length) {\n        // if mobile we we use a backdrop because click events don't delegate\n        $('<div class=\"dropdown-backdrop\"/>').insertAfter($(this)).on('click', clearMenus)\n      }\n\n      $parent.trigger(e = $.Event('show.bs.dropdown'))\n\n      if (e.isDefaultPrevented()) return\n\n      $parent\n        .toggleClass('open')\n        .trigger('shown.bs.dropdown')\n\n      $this.focus()\n    }\n\n    return false\n  }\n\n  Dropdown.prototype.keydown = function (e) {\n    if (!/(38|40|27)/.test(e.keyCode)) return\n\n    var $this = $(this)\n\n    e.preventDefault()\n    e.stopPropagation()\n\n    if ($this.is('.disabled, :disabled')) return\n\n    var $parent  = getParent($this)\n    var isActive = $parent.hasClass('open')\n\n    if (!isActive || (isActive && e.keyCode == 27)) {\n      if (e.which == 27) $parent.find(toggle).focus()\n      return $this.click()\n    }\n\n    var $items = $('[role=menu] li:not(.divider):visible a', $parent)\n\n    if (!$items.length) return\n\n    var index = $items.index($items.filter(':focus'))\n\n    if (e.keyCode == 38 && index > 0)                 index--                        // up\n    if (e.keyCode == 40 && index < $items.length - 1) index++                        // down\n    if (!~index)                                      index=0\n\n    $items.eq(index).focus()\n  }\n\n  function clearMenus() {\n    $(backdrop).remove()\n    $(toggle).each(function (e) {\n      var $parent = getParent($(this))\n      if (!$parent.hasClass('open')) return\n      $parent.trigger(e = $.Event('hide.bs.dropdown'))\n      if (e.isDefaultPrevented()) return\n      $parent.removeClass('open').trigger('hidden.bs.dropdown')\n    })\n  }\n\n  function getParent($this) {\n    var selector = $this.attr('data-target')\n\n    if (!selector) {\n      selector = $this.attr('href')\n      selector = selector && /#/.test(selector) && selector.replace(/.*(?=#[^\\s]*$)/, '') //strip for ie7\n    }\n\n    var $parent = selector && $(selector)\n\n    return $parent && $parent.length ? $parent : $this.parent()\n  }\n\n\n  // DROPDOWN PLUGIN DEFINITION\n  // ==========================\n\n  var old = $.fn.dropdown\n\n  $.fn.dropdown = function (option) {\n    return this.each(function () {\n      var $this = $(this)\n      var data  = $this.data('dropdown')\n\n      if (!data) $this.data('dropdown', (data = new Dropdown(this)))\n      if (typeof option == 'string') data[option].call($this)\n    })\n  }\n\n  $.fn.dropdown.Constructor = Dropdown\n\n\n  // DROPDOWN NO CONFLICT\n  // ====================\n\n  $.fn.dropdown.noConflict = function () {\n    $.fn.dropdown = old\n    return this\n  }\n\n\n  // APPLY TO STANDARD DROPDOWN ELEMENTS\n  // ===================================\n\n  $(document)\n    .on('click.bs.dropdown.data-api', clearMenus)\n    .on('click.bs.dropdown.data-api', '.dropdown form', function (e) { e.stopPropagation() })\n    .on('click.bs.dropdown.data-api'  , toggle, Dropdown.prototype.toggle)\n    .on('keydown.bs.dropdown.data-api', toggle + ', [role=menu]' , Dropdown.prototype.keydown)\n\n}(window.jQuery);\n\n/* ========================================================================\n * Bootstrap: modal.js v3.0.0\n * http://twbs.github.com/bootstrap/javascript.html#modals\n * ========================================================================\n * Copyright 2012 Twitter, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n * ======================================================================== */\n\n\n+function ($) { \"use strict\";\n\n  // MODAL CLASS DEFINITION\n  // ======================\n\n  var Modal = function (element, options) {\n    this.options   = options\n    this.$element  = $(element)\n    this.$backdrop =\n    this.isShown   = null\n\n    if (this.options.remote) this.$element.load(this.options.remote)\n  }\n\n  Modal.DEFAULTS = {\n      backdrop: true\n    , keyboard: true\n    , show: true\n  }\n\n  Modal.prototype.toggle = function (_relatedTarget) {\n    return this[!this.isShown ? 'show' : 'hide'](_relatedTarget)\n  }\n\n  Modal.prototype.show = function (_relatedTarget) {\n    var that = this\n    var e    = $.Event('show.bs.modal', { relatedTarget: _relatedTarget })\n\n    this.$element.trigger(e)\n\n    if (this.isShown || e.isDefaultPrevented()) return\n\n    this.isShown = true\n\n    this.escape()\n\n    this.$element.on('click.dismiss.modal', '[data-dismiss=\"modal\"]', $.proxy(this.hide, this))\n\n    this.backdrop(function () {\n      var transition = $.support.transition && that.$element.hasClass('fade')\n\n      if (!that.$element.parent().length) {\n        that.$element.appendTo(document.body) // don't move modals dom position\n      }\n\n      that.$element.show()\n\n      if (transition) {\n        that.$element[0].offsetWidth // force reflow\n      }\n\n      that.$element\n        .addClass('in')\n        .attr('aria-hidden', false)\n\n      that.enforceFocus()\n\n      var e = $.Event('shown.bs.modal', { relatedTarget: _relatedTarget })\n\n      transition ?\n        that.$element.find('.modal-dialog') // wait for modal to slide in\n          .one($.support.transition.end, function () {\n            that.$element.focus().trigger(e)\n          })\n          .emulateTransitionEnd(300) :\n        that.$element.focus().trigger(e)\n    })\n  }\n\n  Modal.prototype.hide = function (e) {\n    if (e) e.preventDefault()\n\n    e = $.Event('hide.bs.modal')\n\n    this.$element.trigger(e)\n\n    if (!this.isShown || e.isDefaultPrevented()) return\n\n    this.isShown = false\n\n    this.escape()\n\n    $(document).off('focusin.bs.modal')\n\n    this.$element\n      .removeClass('in')\n      .attr('aria-hidden', true)\n      .off('click.dismiss.modal')\n\n    $.support.transition && this.$element.hasClass('fade') ?\n      this.$element\n        .one($.support.transition.end, $.proxy(this.hideModal, this))\n        .emulateTransitionEnd(300) :\n      this.hideModal()\n  }\n\n  Modal.prototype.enforceFocus = function () {\n    $(document)\n      .off('focusin.bs.modal') // guard against infinite focus loop\n      .on('focusin.bs.modal', $.proxy(function (e) {\n        if (this.$element[0] !== e.target && !this.$element.has(e.target).length) {\n          this.$element.focus()\n        }\n      }, this))\n  }\n\n  Modal.prototype.escape = function () {\n    if (this.isShown && this.options.keyboard) {\n      this.$element.on('keyup.dismiss.bs.modal', $.proxy(function (e) {\n        e.which == 27 && this.hide()\n      }, this))\n    } else if (!this.isShown) {\n      this.$element.off('keyup.dismiss.bs.modal')\n    }\n  }\n\n  Modal.prototype.hideModal = function () {\n    var that = this\n    this.$element.hide()\n    this.backdrop(function () {\n      that.removeBackdrop()\n      that.$element.trigger('hidden.bs.modal')\n    })\n  }\n\n  Modal.prototype.removeBackdrop = function () {\n    this.$backdrop && this.$backdrop.remove()\n    this.$backdrop = null\n  }\n\n  Modal.prototype.backdrop = function (callback) {\n    var that    = this\n    var animate = this.$element.hasClass('fade') ? 'fade' : ''\n\n    if (this.isShown && this.options.backdrop) {\n      var doAnimate = $.support.transition && animate\n\n      this.$backdrop = $('<div class=\"modal-backdrop ' + animate + '\" />')\n        .appendTo(document.body)\n\n      this.$element.on('click.dismiss.modal', $.proxy(function (e) {\n        if (e.target !== e.currentTarget) return\n        this.options.backdrop == 'static'\n          ? this.$element[0].focus.call(this.$element[0])\n          : this.hide.call(this)\n      }, this))\n\n      if (doAnimate) this.$backdrop[0].offsetWidth // force reflow\n\n      this.$backdrop.addClass('in')\n\n      if (!callback) return\n\n      doAnimate ?\n        this.$backdrop\n          .one($.support.transition.end, callback)\n          .emulateTransitionEnd(150) :\n        callback()\n\n    } else if (!this.isShown && this.$backdrop) {\n      this.$backdrop.removeClass('in')\n\n      $.support.transition && this.$element.hasClass('fade')?\n        this.$backdrop\n          .one($.support.transition.end, callback)\n          .emulateTransitionEnd(150) :\n        callback()\n\n    } else if (callback) {\n      callback()\n    }\n  }\n\n\n  // MODAL PLUGIN DEFINITION\n  // =======================\n\n  var old = $.fn.modal\n\n  $.fn.modal = function (option, _relatedTarget) {\n    return this.each(function () {\n      var $this   = $(this)\n      var data    = $this.data('bs.modal')\n      var options = $.extend({}, Modal.DEFAULTS, $this.data(), typeof option == 'object' && option)\n\n      if (!data) $this.data('bs.modal', (data = new Modal(this, options)))\n      if (typeof option == 'string') data[option](_relatedTarget)\n      else if (options.show) data.show(_relatedTarget)\n    })\n  }\n\n  $.fn.modal.Constructor = Modal\n\n\n  // MODAL NO CONFLICT\n  // =================\n\n  $.fn.modal.noConflict = function () {\n    $.fn.modal = old\n    return this\n  }\n\n\n  // MODAL DATA-API\n  // ==============\n\n  $(document).on('click.bs.modal.data-api', '[data-toggle=\"modal\"]', function (e) {\n    var $this   = $(this)\n    var href    = $this.attr('href')\n    var $target = $($this.attr('data-target') || (href && href.replace(/.*(?=#[^\\s]+$)/, ''))) //strip for ie7\n    var option  = $target.data('modal') ? 'toggle' : $.extend({ remote: !/#/.test(href) && href }, $target.data(), $this.data())\n\n    e.preventDefault()\n\n    $target\n      .modal(option, this)\n      .one('hide', function () {\n        $this.is(':visible') && $this.focus()\n      })\n  })\n\n  $(document)\n    .on('show.bs.modal',  '.modal', function () { $(document.body).addClass('modal-open') })\n    .on('hidden.bs.modal', '.modal', function () { $(document.body).removeClass('modal-open') })\n\n}(window.jQuery);\n\n/* ========================================================================\n * Bootstrap: tooltip.js v3.0.0\n * http://twbs.github.com/bootstrap/javascript.html#tooltip\n * Inspired by the original jQuery.tipsy by Jason Frame\n * ========================================================================\n * Copyright 2012 Twitter, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n * ======================================================================== */\n\n\n+function ($) { \"use strict\";\n\n  // TOOLTIP PUBLIC CLASS DEFINITION\n  // ===============================\n\n  var Tooltip = function (element, options) {\n    this.type       =\n    this.options    =\n    this.enabled    =\n    this.timeout    =\n    this.hoverState =\n    this.$element   = null\n\n    this.init('tooltip', element, options)\n  }\n\n  Tooltip.DEFAULTS = {\n    animation: true\n  , placement: 'top'\n  , selector: false\n  , template: '<div class=\"tooltip\"><div class=\"tooltip-arrow\"></div><div class=\"tooltip-inner\"></div></div>'\n  , trigger: 'hover focus'\n  , title: ''\n  , delay: 0\n  , html: false\n  , container: false\n  }\n\n  Tooltip.prototype.init = function (type, element, options) {\n    this.enabled  = true\n    this.type     = type\n    this.$element = $(element)\n    this.options  = this.getOptions(options)\n\n    var triggers = this.options.trigger.split(' ')\n\n    for (var i = triggers.length; i--;) {\n      var trigger = triggers[i]\n\n      if (trigger == 'click') {\n        this.$element.on('click.' + this.type, this.options.selector, $.proxy(this.toggle, this))\n      } else if (trigger != 'manual') {\n        var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focus'\n        var eventOut = trigger == 'hover' ? 'mouseleave' : 'blur'\n\n        this.$element.on(eventIn  + '.' + this.type, this.options.selector, $.proxy(this.enter, this))\n        this.$element.on(eventOut + '.' + this.type, this.options.selector, $.proxy(this.leave, this))\n      }\n    }\n\n    this.options.selector ?\n      (this._options = $.extend({}, this.options, { trigger: 'manual', selector: '' })) :\n      this.fixTitle()\n  }\n\n  Tooltip.prototype.getDefaults = function () {\n    return Tooltip.DEFAULTS\n  }\n\n  Tooltip.prototype.getOptions = function (options) {\n    options = $.extend({}, this.getDefaults(), this.$element.data(), options)\n\n    if (options.delay && typeof options.delay == 'number') {\n      options.delay = {\n        show: options.delay\n      , hide: options.delay\n      }\n    }\n\n    return options\n  }\n\n  Tooltip.prototype.getDelegateOptions = function () {\n    var options  = {}\n    var defaults = this.getDefaults()\n\n    this._options && $.each(this._options, function (key, value) {\n      if (defaults[key] != value) options[key] = value\n    })\n\n    return options\n  }\n\n  Tooltip.prototype.enter = function (obj) {\n    var self = obj instanceof this.constructor ?\n      obj : $(obj.currentTarget)[this.type](this.getDelegateOptions()).data('bs.' + this.type)\n\n    clearTimeout(self.timeout)\n\n    self.hoverState = 'in'\n\n    if (!self.options.delay || !self.options.delay.show) return self.show()\n\n    self.timeout = setTimeout(function () {\n      if (self.hoverState == 'in') self.show()\n    }, self.options.delay.show)\n  }\n\n  Tooltip.prototype.leave = function (obj) {\n    var self = obj instanceof this.constructor ?\n      obj : $(obj.currentTarget)[this.type](this.getDelegateOptions()).data('bs.' + this.type)\n\n    clearTimeout(self.timeout)\n\n    self.hoverState = 'out'\n\n    if (!self.options.delay || !self.options.delay.hide) return self.hide()\n\n    self.timeout = setTimeout(function () {\n      if (self.hoverState == 'out') self.hide()\n    }, self.options.delay.hide)\n  }\n\n  Tooltip.prototype.show = function () {\n    var e = $.Event('show.bs.'+ this.type)\n\n    if (this.hasContent() && this.enabled) {\n      this.$element.trigger(e)\n\n      if (e.isDefaultPrevented()) return\n\n      var $tip = this.tip()\n\n      this.setContent()\n\n      if (this.options.animation) $tip.addClass('fade')\n\n      var placement = typeof this.options.placement == 'function' ?\n        this.options.placement.call(this, $tip[0], this.$element[0]) :\n        this.options.placement\n\n      var autoToken = /\\s?auto?\\s?/i\n      var autoPlace = autoToken.test(placement)\n      if (autoPlace) placement = placement.replace(autoToken, '') || 'top'\n\n      $tip\n        .detach()\n        .css({ top: 0, left: 0, display: 'block' })\n        .addClass(placement)\n\n      this.options.container ? $tip.appendTo(this.options.container) : $tip.insertAfter(this.$element)\n\n      var pos          = this.getPosition()\n      var actualWidth  = $tip[0].offsetWidth\n      var actualHeight = $tip[0].offsetHeight\n\n      if (autoPlace) {\n        var $parent = this.$element.parent()\n\n        var orgPlacement = placement\n        var docScroll    = document.documentElement.scrollTop || document.body.scrollTop\n        var parentWidth  = this.options.container == 'body' ? window.innerWidth  : $parent.outerWidth()\n        var parentHeight = this.options.container == 'body' ? window.innerHeight : $parent.outerHeight()\n        var parentLeft   = this.options.container == 'body' ? 0 : $parent.offset().left\n\n        placement = placement == 'bottom' && pos.top   + pos.height  + actualHeight - docScroll > parentHeight  ? 'top'    :\n                    placement == 'top'    && pos.top   - docScroll   - actualHeight < 0                         ? 'bottom' :\n                    placement == 'right'  && pos.right + actualWidth > parentWidth                              ? 'left'   :\n                    placement == 'left'   && pos.left  - actualWidth < parentLeft                               ? 'right'  :\n                    placement\n\n        $tip\n          .removeClass(orgPlacement)\n          .addClass(placement)\n      }\n\n      var calculatedOffset = this.getCalculatedOffset(placement, pos, actualWidth, actualHeight)\n\n      this.applyPlacement(calculatedOffset, placement)\n      this.$element.trigger('shown.bs.' + this.type)\n    }\n  }\n\n  Tooltip.prototype.applyPlacement = function(offset, placement) {\n    var replace\n    var $tip   = this.tip()\n    var width  = $tip[0].offsetWidth\n    var height = $tip[0].offsetHeight\n\n    // manually read margins because getBoundingClientRect includes difference\n    var marginTop = parseInt($tip.css('margin-top'), 10)\n    var marginLeft = parseInt($tip.css('margin-left'), 10)\n\n    // we must check for NaN for ie 8/9\n    if (isNaN(marginTop))  marginTop  = 0\n    if (isNaN(marginLeft)) marginLeft = 0\n\n    offset.top  = offset.top  + marginTop\n    offset.left = offset.left + marginLeft\n\n    $tip\n      .offset(offset)\n      .addClass('in')\n\n    // check to see if placing tip in new offset caused the tip to resize itself\n    var actualWidth  = $tip[0].offsetWidth\n    var actualHeight = $tip[0].offsetHeight\n\n    if (placement == 'top' && actualHeight != height) {\n      replace = true\n      offset.top = offset.top + height - actualHeight\n    }\n\n    if (/bottom|top/.test(placement)) {\n      var delta = 0\n\n      if (offset.left < 0) {\n        delta       = offset.left * -2\n        offset.left = 0\n\n        $tip.offset(offset)\n\n        actualWidth  = $tip[0].offsetWidth\n        actualHeight = $tip[0].offsetHeight\n      }\n\n      this.replaceArrow(delta - width + actualWidth, actualWidth, 'left')\n    } else {\n      this.replaceArrow(actualHeight - height, actualHeight, 'top')\n    }\n\n    if (replace) $tip.offset(offset)\n  }\n\n  Tooltip.prototype.replaceArrow = function(delta, dimension, position) {\n    this.arrow().css(position, delta ? (50 * (1 - delta / dimension) + \"%\") : '')\n  }\n\n  Tooltip.prototype.setContent = function () {\n    var $tip  = this.tip()\n    var title = this.getTitle()\n\n    $tip.find('.tooltip-inner')[this.options.html ? 'html' : 'text'](title)\n    $tip.removeClass('fade in top bottom left right')\n  }\n\n  Tooltip.prototype.hide = function () {\n    var that = this\n    var $tip = this.tip()\n    var e    = $.Event('hide.bs.' + this.type)\n\n    function complete() {\n      if (that.hoverState != 'in') $tip.detach()\n    }\n\n    this.$element.trigger(e)\n\n    if (e.isDefaultPrevented()) return\n\n    $tip.removeClass('in')\n\n    $.support.transition && this.$tip.hasClass('fade') ?\n      $tip\n        .one($.support.transition.end, complete)\n        .emulateTransitionEnd(150) :\n      complete()\n\n    this.$element.trigger('hidden.bs.' + this.type)\n\n    return this\n  }\n\n  Tooltip.prototype.fixTitle = function () {\n    var $e = this.$element\n    if ($e.attr('title') || typeof($e.attr('data-original-title')) != 'string') {\n      $e.attr('data-original-title', $e.attr('title') || '').attr('title', '')\n    }\n  }\n\n  Tooltip.prototype.hasContent = function () {\n    return this.getTitle()\n  }\n\n  Tooltip.prototype.getPosition = function () {\n    var el = this.$element[0]\n    return $.extend({}, (typeof el.getBoundingClientRect == 'function') ? el.getBoundingClientRect() : {\n      width: el.offsetWidth\n    , height: el.offsetHeight\n    }, this.$element.offset())\n  }\n\n  Tooltip.prototype.getCalculatedOffset = function (placement, pos, actualWidth, actualHeight) {\n    return placement == 'bottom' ? { top: pos.top + pos.height,   left: pos.left + pos.width / 2 - actualWidth / 2  } :\n           placement == 'top'    ? { top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2  } :\n           placement == 'left'   ? { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth } :\n        /* placement == 'right' */ { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width   }\n  }\n\n  Tooltip.prototype.getTitle = function () {\n    var title\n    var $e = this.$element\n    var o  = this.options\n\n    title = $e.attr('data-original-title')\n      || (typeof o.title == 'function' ? o.title.call($e[0]) :  o.title)\n\n    return title\n  }\n\n  Tooltip.prototype.tip = function () {\n    return this.$tip = this.$tip || $(this.options.template)\n  }\n\n  Tooltip.prototype.arrow = function () {\n    return this.$arrow = this.$arrow || this.tip().find('.tooltip-arrow')\n  }\n\n  Tooltip.prototype.validate = function () {\n    if (!this.$element[0].parentNode) {\n      this.hide()\n      this.$element = null\n      this.options  = null\n    }\n  }\n\n  Tooltip.prototype.enable = function () {\n    this.enabled = true\n  }\n\n  Tooltip.prototype.disable = function () {\n    this.enabled = false\n  }\n\n  Tooltip.prototype.toggleEnabled = function () {\n    this.enabled = !this.enabled\n  }\n\n  Tooltip.prototype.toggle = function (e) {\n    var self = e ? $(e.currentTarget)[this.type](this.getDelegateOptions()).data('bs.' + this.type) : this\n    self.tip().hasClass('in') ? self.leave(self) : self.enter(self)\n  }\n\n  Tooltip.prototype.destroy = function () {\n    this.hide().$element.off('.' + this.type).removeData('bs.' + this.type)\n  }\n\n\n  // TOOLTIP PLUGIN DEFINITION\n  // =========================\n\n  var old = $.fn.tooltip\n\n  $.fn.tooltip = function (option) {\n    return this.each(function () {\n      var $this   = $(this)\n      var data    = $this.data('bs.tooltip')\n      var options = typeof option == 'object' && option\n\n      if (!data) $this.data('bs.tooltip', (data = new Tooltip(this, options)))\n      if (typeof option == 'string') data[option]()\n    })\n  }\n\n  $.fn.tooltip.Constructor = Tooltip\n\n\n  // TOOLTIP NO CONFLICT\n  // ===================\n\n  $.fn.tooltip.noConflict = function () {\n    $.fn.tooltip = old\n    return this\n  }\n\n}(window.jQuery);\n\n/* ========================================================================\n * Bootstrap: popover.js v3.0.0\n * http://twbs.github.com/bootstrap/javascript.html#popovers\n * ========================================================================\n * Copyright 2012 Twitter, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n * ======================================================================== */\n\n\n+function ($) { \"use strict\";\n\n  // POPOVER PUBLIC CLASS DEFINITION\n  // ===============================\n\n  var Popover = function (element, options) {\n    this.init('popover', element, options)\n  }\n\n  if (!$.fn.tooltip) throw new Error('Popover requires tooltip.js')\n\n  Popover.DEFAULTS = $.extend({} , $.fn.tooltip.Constructor.DEFAULTS, {\n    placement: 'right'\n  , trigger: 'click'\n  , content: ''\n  , template: '<div class=\"popover\"><div class=\"arrow\"></div><h3 class=\"popover-title\"></h3><div class=\"popover-content\"></div></div>'\n  })\n\n\n  // NOTE: POPOVER EXTENDS tooltip.js\n  // ================================\n\n  Popover.prototype = $.extend({}, $.fn.tooltip.Constructor.prototype)\n\n  Popover.prototype.constructor = Popover\n\n  Popover.prototype.getDefaults = function () {\n    return Popover.DEFAULTS\n  }\n\n  Popover.prototype.setContent = function () {\n    var $tip    = this.tip()\n    var title   = this.getTitle()\n    var content = this.getContent()\n\n    $tip.find('.popover-title')[this.options.html ? 'html' : 'text'](title)\n    $tip.find('.popover-content')[this.options.html ? 'html' : 'text'](content)\n\n    $tip.removeClass('fade top bottom left right in')\n\n    // IE8 doesn't accept hiding via the `:empty` pseudo selector, we have to do\n    // this manually by checking the contents.\n    if (!$tip.find('.popover-title').html()) $tip.find('.popover-title').hide()\n  }\n\n  Popover.prototype.hasContent = function () {\n    return this.getTitle() || this.getContent()\n  }\n\n  Popover.prototype.getContent = function () {\n    var $e = this.$element\n    var o  = this.options\n\n    return $e.attr('data-content')\n      || (typeof o.content == 'function' ?\n            o.content.call($e[0]) :\n            o.content)\n  }\n\n  Popover.prototype.arrow = function () {\n    return this.$arrow = this.$arrow || this.tip().find('.arrow')\n  }\n\n  Popover.prototype.tip = function () {\n    if (!this.$tip) this.$tip = $(this.options.template)\n    return this.$tip\n  }\n\n\n  // POPOVER PLUGIN DEFINITION\n  // =========================\n\n  var old = $.fn.popover\n\n  $.fn.popover = function (option) {\n    return this.each(function () {\n      var $this   = $(this)\n      var data    = $this.data('bs.popover')\n      var options = typeof option == 'object' && option\n\n      if (!data) $this.data('bs.popover', (data = new Popover(this, options)))\n      if (typeof option == 'string') data[option]()\n    })\n  }\n\n  $.fn.popover.Constructor = Popover\n\n\n  // POPOVER NO CONFLICT\n  // ===================\n\n  $.fn.popover.noConflict = function () {\n    $.fn.popover = old\n    return this\n  }\n\n}(window.jQuery);\n\n/* ========================================================================\n * Bootstrap: scrollspy.js v3.0.0\n * http://twbs.github.com/bootstrap/javascript.html#scrollspy\n * ========================================================================\n * Copyright 2012 Twitter, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n * ======================================================================== */\n\n\n+function ($) { \"use strict\";\n\n  // SCROLLSPY CLASS DEFINITION\n  // ==========================\n\n  function ScrollSpy(element, options) {\n    var href\n    var process  = $.proxy(this.process, this)\n\n    this.$element       = $(element).is('body') ? $(window) : $(element)\n    this.$body          = $('body')\n    this.$scrollElement = this.$element.on('scroll.bs.scroll-spy.data-api', process)\n    this.options        = $.extend({}, ScrollSpy.DEFAULTS, options)\n    this.selector       = (this.options.target\n      || ((href = $(element).attr('href')) && href.replace(/.*(?=#[^\\s]+$)/, '')) //strip for ie7\n      || '') + ' .nav li > a'\n    this.offsets        = $([])\n    this.targets        = $([])\n    this.activeTarget   = null\n\n    this.refresh()\n    this.process()\n  }\n\n  ScrollSpy.DEFAULTS = {\n    offset: 10\n  }\n\n  ScrollSpy.prototype.refresh = function () {\n    var offsetMethod = this.$element[0] == window ? 'offset' : 'position'\n\n    this.offsets = $([])\n    this.targets = $([])\n\n    var self     = this\n    var $targets = this.$body\n      .find(this.selector)\n      .map(function () {\n        var $el   = $(this)\n        var href  = $el.data('target') || $el.attr('href')\n        var $href = /^#\\w/.test(href) && $(href)\n\n        return ($href\n          && $href.length\n          && [[ $href[offsetMethod]().top + (!$.isWindow(self.$scrollElement.get(0)) && self.$scrollElement.scrollTop()), href ]]) || null\n      })\n      .sort(function (a, b) { return a[0] - b[0] })\n      .each(function () {\n        self.offsets.push(this[0])\n        self.targets.push(this[1])\n      })\n  }\n\n  ScrollSpy.prototype.process = function () {\n    var scrollTop    = this.$scrollElement.scrollTop() + this.options.offset\n    var scrollHeight = this.$scrollElement[0].scrollHeight || this.$body[0].scrollHeight\n    var maxScroll    = scrollHeight - this.$scrollElement.height()\n    var offsets      = this.offsets\n    var targets      = this.targets\n    var activeTarget = this.activeTarget\n    var i\n\n    if (scrollTop >= maxScroll) {\n      return activeTarget != (i = targets.last()[0]) && this.activate(i)\n    }\n\n    for (i = offsets.length; i--;) {\n      activeTarget != targets[i]\n        && scrollTop >= offsets[i]\n        && (!offsets[i + 1] || scrollTop <= offsets[i + 1])\n        && this.activate( targets[i] )\n    }\n  }\n\n  ScrollSpy.prototype.activate = function (target) {\n    this.activeTarget = target\n\n    $(this.selector)\n      .parents('.active')\n      .removeClass('active')\n\n    var selector = this.selector\n      + '[data-target=\"' + target + '\"],'\n      + this.selector + '[href=\"' + target + '\"]'\n\n    var active = $(selector)\n      .parents('li')\n      .addClass('active')\n\n    if (active.parent('.dropdown-menu').length)  {\n      active = active\n        .closest('li.dropdown')\n        .addClass('active')\n    }\n\n    active.trigger('activate')\n  }\n\n\n  // SCROLLSPY PLUGIN DEFINITION\n  // ===========================\n\n  var old = $.fn.scrollspy\n\n  $.fn.scrollspy = function (option) {\n    return this.each(function () {\n      var $this   = $(this)\n      var data    = $this.data('bs.scrollspy')\n      var options = typeof option == 'object' && option\n\n      if (!data) $this.data('bs.scrollspy', (data = new ScrollSpy(this, options)))\n      if (typeof option == 'string') data[option]()\n    })\n  }\n\n  $.fn.scrollspy.Constructor = ScrollSpy\n\n\n  // SCROLLSPY NO CONFLICT\n  // =====================\n\n  $.fn.scrollspy.noConflict = function () {\n    $.fn.scrollspy = old\n    return this\n  }\n\n\n  // SCROLLSPY DATA-API\n  // ==================\n\n  $(window).on('load', function () {\n    $('[data-spy=\"scroll\"]').each(function () {\n      var $spy = $(this)\n      $spy.scrollspy($spy.data())\n    })\n  })\n\n}(window.jQuery);\n\n/* ========================================================================\n * Bootstrap: tab.js v3.0.0\n * http://twbs.github.com/bootstrap/javascript.html#tabs\n * ========================================================================\n * Copyright 2012 Twitter, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n * ======================================================================== */\n\n\n+function ($) { \"use strict\";\n\n  // TAB CLASS DEFINITION\n  // ====================\n\n  var Tab = function (element) {\n    this.element = $(element)\n  }\n\n  Tab.prototype.show = function () {\n    var $this    = this.element\n    var $ul      = $this.closest('ul:not(.dropdown-menu)')\n    var selector = $this.attr('data-target')\n\n    if (!selector) {\n      selector = $this.attr('href')\n      selector = selector && selector.replace(/.*(?=#[^\\s]*$)/, '') //strip for ie7\n    }\n\n    if ($this.parent('li').hasClass('active')) return\n\n    var previous = $ul.find('.active:last a')[0]\n    var e        = $.Event('show.bs.tab', {\n      relatedTarget: previous\n    })\n\n    $this.trigger(e)\n\n    if (e.isDefaultPrevented()) return\n\n    var $target = $(selector)\n\n    this.activate($this.parent('li'), $ul)\n    this.activate($target, $target.parent(), function () {\n      $this.trigger({\n        type: 'shown.bs.tab'\n      , relatedTarget: previous\n      })\n    })\n  }\n\n  Tab.prototype.activate = function (element, container, callback) {\n    var $active    = container.find('> .active')\n    var transition = callback\n      && $.support.transition\n      && $active.hasClass('fade')\n\n    function next() {\n      $active\n        .removeClass('active')\n        .find('> .dropdown-menu > .active')\n        .removeClass('active')\n\n      element.addClass('active')\n\n      if (transition) {\n        element[0].offsetWidth // reflow for transition\n        element.addClass('in')\n      } else {\n        element.removeClass('fade')\n      }\n\n      if (element.parent('.dropdown-menu')) {\n        element.closest('li.dropdown').addClass('active')\n      }\n\n      callback && callback()\n    }\n\n    transition ?\n      $active\n        .one($.support.transition.end, next)\n        .emulateTransitionEnd(150) :\n      next()\n\n    $active.removeClass('in')\n  }\n\n\n  // TAB PLUGIN DEFINITION\n  // =====================\n\n  var old = $.fn.tab\n\n  $.fn.tab = function ( option ) {\n    return this.each(function () {\n      var $this = $(this)\n      var data  = $this.data('bs.tab')\n\n      if (!data) $this.data('bs.tab', (data = new Tab(this)))\n      if (typeof option == 'string') data[option]()\n    })\n  }\n\n  $.fn.tab.Constructor = Tab\n\n\n  // TAB NO CONFLICT\n  // ===============\n\n  $.fn.tab.noConflict = function () {\n    $.fn.tab = old\n    return this\n  }\n\n\n  // TAB DATA-API\n  // ============\n\n  $(document).on('click.bs.tab.data-api', '[data-toggle=\"tab\"], [data-toggle=\"pill\"]', function (e) {\n    e.preventDefault()\n    $(this).tab('show')\n  })\n\n}(window.jQuery);\n\n/* ========================================================================\n * Bootstrap: affix.js v3.0.0\n * http://twbs.github.com/bootstrap/javascript.html#affix\n * ========================================================================\n * Copyright 2012 Twitter, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n * ======================================================================== */\n\n\n+function ($) { \"use strict\";\n\n  // AFFIX CLASS DEFINITION\n  // ======================\n\n  var Affix = function (element, options) {\n    this.options = $.extend({}, Affix.DEFAULTS, options)\n    this.$window = $(window)\n      .on('scroll.bs.affix.data-api', $.proxy(this.checkPosition, this))\n      .on('click.bs.affix.data-api',  $.proxy(this.checkPositionWithEventLoop, this))\n\n    this.$element = $(element)\n    this.affixed  =\n    this.unpin    = null\n\n    this.checkPosition()\n  }\n\n  Affix.RESET = 'affix affix-top affix-bottom'\n\n  Affix.DEFAULTS = {\n    offset: 0\n  }\n\n  Affix.prototype.checkPositionWithEventLoop = function () {\n    setTimeout($.proxy(this.checkPosition, this), 1)\n  }\n\n  Affix.prototype.checkPosition = function () {\n    if (!this.$element.is(':visible')) return\n\n    var scrollHeight = $(document).height()\n    var scrollTop    = this.$window.scrollTop()\n    var position     = this.$element.offset()\n    var offset       = this.options.offset\n    var offsetTop    = offset.top\n    var offsetBottom = offset.bottom\n\n    if (typeof offset != 'object')         offsetBottom = offsetTop = offset\n    if (typeof offsetTop == 'function')    offsetTop    = offset.top()\n    if (typeof offsetBottom == 'function') offsetBottom = offset.bottom()\n\n    var affix = this.unpin   != null && (scrollTop + this.unpin <= position.top) ? false :\n                offsetBottom != null && (position.top + this.$element.height() >= scrollHeight - offsetBottom) ? 'bottom' :\n                offsetTop    != null && (scrollTop <= offsetTop) ? 'top' : false\n\n    if (this.affixed === affix) return\n    if (this.unpin) this.$element.css('top', '')\n\n    this.affixed = affix\n    this.unpin   = affix == 'bottom' ? position.top - scrollTop : null\n\n    this.$element.removeClass(Affix.RESET).addClass('affix' + (affix ? '-' + affix : ''))\n\n    if (affix == 'bottom') {\n      this.$element.offset({ top: document.body.offsetHeight - offsetBottom - this.$element.height() })\n    }\n  }\n\n\n  // AFFIX PLUGIN DEFINITION\n  // =======================\n\n  var old = $.fn.affix\n\n  $.fn.affix = function (option) {\n    return this.each(function () {\n      var $this   = $(this)\n      var data    = $this.data('bs.affix')\n      var options = typeof option == 'object' && option\n\n      if (!data) $this.data('bs.affix', (data = new Affix(this, options)))\n      if (typeof option == 'string') data[option]()\n    })\n  }\n\n  $.fn.affix.Constructor = Affix\n\n\n  // AFFIX NO CONFLICT\n  // =================\n\n  $.fn.affix.noConflict = function () {\n    $.fn.affix = old\n    return this\n  }\n\n\n  // AFFIX DATA-API\n  // ==============\n\n  $(window).on('load', function () {\n    $('[data-spy=\"affix\"]').each(function () {\n      var $spy = $(this)\n      var data = $spy.data()\n\n      data.offset = data.offset || {}\n\n      if (data.offsetBottom) data.offset.bottom = data.offsetBottom\n      if (data.offsetTop)    data.offset.top    = data.offsetTop\n\n      $spy.affix(data)\n    })\n  })\n\n}(window.jQuery);\n"
  },
  {
    "path": "web/js/colorbrewer.js",
    "content": "// This product includes color specifications and designs developed by Cynthia Brewer (http://colorbrewer.org/).\nvar colorbrewer = {YlGn: {\n3: [\"#f7fcb9\",\"#addd8e\",\"#31a354\"],\n4: [\"#ffffcc\",\"#c2e699\",\"#78c679\",\"#238443\"],\n5: [\"#ffffcc\",\"#c2e699\",\"#78c679\",\"#31a354\",\"#006837\"],\n6: [\"#ffffcc\",\"#d9f0a3\",\"#addd8e\",\"#78c679\",\"#31a354\",\"#006837\"],\n7: [\"#ffffcc\",\"#d9f0a3\",\"#addd8e\",\"#78c679\",\"#41ab5d\",\"#238443\",\"#005a32\"],\n8: [\"#ffffe5\",\"#f7fcb9\",\"#d9f0a3\",\"#addd8e\",\"#78c679\",\"#41ab5d\",\"#238443\",\"#005a32\"],\n9: [\"#ffffe5\",\"#f7fcb9\",\"#d9f0a3\",\"#addd8e\",\"#78c679\",\"#41ab5d\",\"#238443\",\"#006837\",\"#004529\"]\n},YlGnBu: {\n3: [\"#edf8b1\",\"#7fcdbb\",\"#2c7fb8\"],\n4: [\"#ffffcc\",\"#a1dab4\",\"#41b6c4\",\"#225ea8\"],\n5: [\"#ffffcc\",\"#a1dab4\",\"#41b6c4\",\"#2c7fb8\",\"#253494\"],\n6: [\"#ffffcc\",\"#c7e9b4\",\"#7fcdbb\",\"#41b6c4\",\"#2c7fb8\",\"#253494\"],\n7: [\"#ffffcc\",\"#c7e9b4\",\"#7fcdbb\",\"#41b6c4\",\"#1d91c0\",\"#225ea8\",\"#0c2c84\"],\n8: [\"#ffffd9\",\"#edf8b1\",\"#c7e9b4\",\"#7fcdbb\",\"#41b6c4\",\"#1d91c0\",\"#225ea8\",\"#0c2c84\"],\n9: [\"#ffffd9\",\"#edf8b1\",\"#c7e9b4\",\"#7fcdbb\",\"#41b6c4\",\"#1d91c0\",\"#225ea8\",\"#253494\",\"#081d58\"]\n},GnBu: {\n3: [\"#e0f3db\",\"#a8ddb5\",\"#43a2ca\"],\n4: [\"#f0f9e8\",\"#bae4bc\",\"#7bccc4\",\"#2b8cbe\"],\n5: [\"#f0f9e8\",\"#bae4bc\",\"#7bccc4\",\"#43a2ca\",\"#0868ac\"],\n6: [\"#f0f9e8\",\"#ccebc5\",\"#a8ddb5\",\"#7bccc4\",\"#43a2ca\",\"#0868ac\"],\n7: [\"#f0f9e8\",\"#ccebc5\",\"#a8ddb5\",\"#7bccc4\",\"#4eb3d3\",\"#2b8cbe\",\"#08589e\"],\n8: [\"#f7fcf0\",\"#e0f3db\",\"#ccebc5\",\"#a8ddb5\",\"#7bccc4\",\"#4eb3d3\",\"#2b8cbe\",\"#08589e\"],\n9: [\"#f7fcf0\",\"#e0f3db\",\"#ccebc5\",\"#a8ddb5\",\"#7bccc4\",\"#4eb3d3\",\"#2b8cbe\",\"#0868ac\",\"#084081\"]\n},BuGn: {\n3: [\"#e5f5f9\",\"#99d8c9\",\"#2ca25f\"],\n4: [\"#edf8fb\",\"#b2e2e2\",\"#66c2a4\",\"#238b45\"],\n5: [\"#edf8fb\",\"#b2e2e2\",\"#66c2a4\",\"#2ca25f\",\"#006d2c\"],\n6: [\"#edf8fb\",\"#ccece6\",\"#99d8c9\",\"#66c2a4\",\"#2ca25f\",\"#006d2c\"],\n7: [\"#edf8fb\",\"#ccece6\",\"#99d8c9\",\"#66c2a4\",\"#41ae76\",\"#238b45\",\"#005824\"],\n8: [\"#f7fcfd\",\"#e5f5f9\",\"#ccece6\",\"#99d8c9\",\"#66c2a4\",\"#41ae76\",\"#238b45\",\"#005824\"],\n9: [\"#f7fcfd\",\"#e5f5f9\",\"#ccece6\",\"#99d8c9\",\"#66c2a4\",\"#41ae76\",\"#238b45\",\"#006d2c\",\"#00441b\"]\n},PuBuGn: {\n3: [\"#ece2f0\",\"#a6bddb\",\"#1c9099\"],\n4: [\"#f6eff7\",\"#bdc9e1\",\"#67a9cf\",\"#02818a\"],\n5: [\"#f6eff7\",\"#bdc9e1\",\"#67a9cf\",\"#1c9099\",\"#016c59\"],\n6: [\"#f6eff7\",\"#d0d1e6\",\"#a6bddb\",\"#67a9cf\",\"#1c9099\",\"#016c59\"],\n7: [\"#f6eff7\",\"#d0d1e6\",\"#a6bddb\",\"#67a9cf\",\"#3690c0\",\"#02818a\",\"#016450\"],\n8: [\"#fff7fb\",\"#ece2f0\",\"#d0d1e6\",\"#a6bddb\",\"#67a9cf\",\"#3690c0\",\"#02818a\",\"#016450\"],\n9: [\"#fff7fb\",\"#ece2f0\",\"#d0d1e6\",\"#a6bddb\",\"#67a9cf\",\"#3690c0\",\"#02818a\",\"#016c59\",\"#014636\"]\n},PuBu: {\n3: [\"#ece7f2\",\"#a6bddb\",\"#2b8cbe\"],\n4: [\"#f1eef6\",\"#bdc9e1\",\"#74a9cf\",\"#0570b0\"],\n5: [\"#f1eef6\",\"#bdc9e1\",\"#74a9cf\",\"#2b8cbe\",\"#045a8d\"],\n6: [\"#f1eef6\",\"#d0d1e6\",\"#a6bddb\",\"#74a9cf\",\"#2b8cbe\",\"#045a8d\"],\n7: [\"#f1eef6\",\"#d0d1e6\",\"#a6bddb\",\"#74a9cf\",\"#3690c0\",\"#0570b0\",\"#034e7b\"],\n8: [\"#fff7fb\",\"#ece7f2\",\"#d0d1e6\",\"#a6bddb\",\"#74a9cf\",\"#3690c0\",\"#0570b0\",\"#034e7b\"],\n9: [\"#fff7fb\",\"#ece7f2\",\"#d0d1e6\",\"#a6bddb\",\"#74a9cf\",\"#3690c0\",\"#0570b0\",\"#045a8d\",\"#023858\"]\n},BuPu: {\n3: [\"#e0ecf4\",\"#9ebcda\",\"#8856a7\"],\n4: [\"#edf8fb\",\"#b3cde3\",\"#8c96c6\",\"#88419d\"],\n5: [\"#edf8fb\",\"#b3cde3\",\"#8c96c6\",\"#8856a7\",\"#810f7c\"],\n6: [\"#edf8fb\",\"#bfd3e6\",\"#9ebcda\",\"#8c96c6\",\"#8856a7\",\"#810f7c\"],\n7: [\"#edf8fb\",\"#bfd3e6\",\"#9ebcda\",\"#8c96c6\",\"#8c6bb1\",\"#88419d\",\"#6e016b\"],\n8: [\"#f7fcfd\",\"#e0ecf4\",\"#bfd3e6\",\"#9ebcda\",\"#8c96c6\",\"#8c6bb1\",\"#88419d\",\"#6e016b\"],\n9: [\"#f7fcfd\",\"#e0ecf4\",\"#bfd3e6\",\"#9ebcda\",\"#8c96c6\",\"#8c6bb1\",\"#88419d\",\"#810f7c\",\"#4d004b\"]\n},RdPu: {\n3: [\"#fde0dd\",\"#fa9fb5\",\"#c51b8a\"],\n4: [\"#feebe2\",\"#fbb4b9\",\"#f768a1\",\"#ae017e\"],\n5: [\"#feebe2\",\"#fbb4b9\",\"#f768a1\",\"#c51b8a\",\"#7a0177\"],\n6: [\"#feebe2\",\"#fcc5c0\",\"#fa9fb5\",\"#f768a1\",\"#c51b8a\",\"#7a0177\"],\n7: [\"#feebe2\",\"#fcc5c0\",\"#fa9fb5\",\"#f768a1\",\"#dd3497\",\"#ae017e\",\"#7a0177\"],\n8: [\"#fff7f3\",\"#fde0dd\",\"#fcc5c0\",\"#fa9fb5\",\"#f768a1\",\"#dd3497\",\"#ae017e\",\"#7a0177\"],\n9: [\"#fff7f3\",\"#fde0dd\",\"#fcc5c0\",\"#fa9fb5\",\"#f768a1\",\"#dd3497\",\"#ae017e\",\"#7a0177\",\"#49006a\"]\n},PuRd: {\n3: [\"#e7e1ef\",\"#c994c7\",\"#dd1c77\"],\n4: [\"#f1eef6\",\"#d7b5d8\",\"#df65b0\",\"#ce1256\"],\n5: [\"#f1eef6\",\"#d7b5d8\",\"#df65b0\",\"#dd1c77\",\"#980043\"],\n6: [\"#f1eef6\",\"#d4b9da\",\"#c994c7\",\"#df65b0\",\"#dd1c77\",\"#980043\"],\n7: [\"#f1eef6\",\"#d4b9da\",\"#c994c7\",\"#df65b0\",\"#e7298a\",\"#ce1256\",\"#91003f\"],\n8: [\"#f7f4f9\",\"#e7e1ef\",\"#d4b9da\",\"#c994c7\",\"#df65b0\",\"#e7298a\",\"#ce1256\",\"#91003f\"],\n9: [\"#f7f4f9\",\"#e7e1ef\",\"#d4b9da\",\"#c994c7\",\"#df65b0\",\"#e7298a\",\"#ce1256\",\"#980043\",\"#67001f\"]\n},OrRd: {\n3: [\"#fee8c8\",\"#fdbb84\",\"#e34a33\"],\n4: [\"#fef0d9\",\"#fdcc8a\",\"#fc8d59\",\"#d7301f\"],\n5: [\"#fef0d9\",\"#fdcc8a\",\"#fc8d59\",\"#e34a33\",\"#b30000\"],\n6: [\"#fef0d9\",\"#fdd49e\",\"#fdbb84\",\"#fc8d59\",\"#e34a33\",\"#b30000\"],\n7: [\"#fef0d9\",\"#fdd49e\",\"#fdbb84\",\"#fc8d59\",\"#ef6548\",\"#d7301f\",\"#990000\"],\n8: [\"#fff7ec\",\"#fee8c8\",\"#fdd49e\",\"#fdbb84\",\"#fc8d59\",\"#ef6548\",\"#d7301f\",\"#990000\"],\n9: [\"#fff7ec\",\"#fee8c8\",\"#fdd49e\",\"#fdbb84\",\"#fc8d59\",\"#ef6548\",\"#d7301f\",\"#b30000\",\"#7f0000\"]\n},YlOrRd: {\n3: [\"#ffeda0\",\"#feb24c\",\"#f03b20\"],\n4: [\"#ffffb2\",\"#fecc5c\",\"#fd8d3c\",\"#e31a1c\"],\n5: [\"#ffffb2\",\"#fecc5c\",\"#fd8d3c\",\"#f03b20\",\"#bd0026\"],\n6: [\"#ffffb2\",\"#fed976\",\"#feb24c\",\"#fd8d3c\",\"#f03b20\",\"#bd0026\"],\n7: [\"#ffffb2\",\"#fed976\",\"#feb24c\",\"#fd8d3c\",\"#fc4e2a\",\"#e31a1c\",\"#b10026\"],\n8: [\"#ffffcc\",\"#ffeda0\",\"#fed976\",\"#feb24c\",\"#fd8d3c\",\"#fc4e2a\",\"#e31a1c\",\"#b10026\"],\n9: [\"#ffffcc\",\"#ffeda0\",\"#fed976\",\"#feb24c\",\"#fd8d3c\",\"#fc4e2a\",\"#e31a1c\",\"#bd0026\",\"#800026\"]\n},YlOrBr: {\n3: [\"#fff7bc\",\"#fec44f\",\"#d95f0e\"],\n4: [\"#ffffd4\",\"#fed98e\",\"#fe9929\",\"#cc4c02\"],\n5: [\"#ffffd4\",\"#fed98e\",\"#fe9929\",\"#d95f0e\",\"#993404\"],\n6: [\"#ffffd4\",\"#fee391\",\"#fec44f\",\"#fe9929\",\"#d95f0e\",\"#993404\"],\n7: [\"#ffffd4\",\"#fee391\",\"#fec44f\",\"#fe9929\",\"#ec7014\",\"#cc4c02\",\"#8c2d04\"],\n8: [\"#ffffe5\",\"#fff7bc\",\"#fee391\",\"#fec44f\",\"#fe9929\",\"#ec7014\",\"#cc4c02\",\"#8c2d04\"],\n9: [\"#ffffe5\",\"#fff7bc\",\"#fee391\",\"#fec44f\",\"#fe9929\",\"#ec7014\",\"#cc4c02\",\"#993404\",\"#662506\"]\n},Purples: {\n3: [\"#efedf5\",\"#bcbddc\",\"#756bb1\"],\n4: [\"#f2f0f7\",\"#cbc9e2\",\"#9e9ac8\",\"#6a51a3\"],\n5: [\"#f2f0f7\",\"#cbc9e2\",\"#9e9ac8\",\"#756bb1\",\"#54278f\"],\n6: [\"#f2f0f7\",\"#dadaeb\",\"#bcbddc\",\"#9e9ac8\",\"#756bb1\",\"#54278f\"],\n7: [\"#f2f0f7\",\"#dadaeb\",\"#bcbddc\",\"#9e9ac8\",\"#807dba\",\"#6a51a3\",\"#4a1486\"],\n8: [\"#fcfbfd\",\"#efedf5\",\"#dadaeb\",\"#bcbddc\",\"#9e9ac8\",\"#807dba\",\"#6a51a3\",\"#4a1486\"],\n9: [\"#fcfbfd\",\"#efedf5\",\"#dadaeb\",\"#bcbddc\",\"#9e9ac8\",\"#807dba\",\"#6a51a3\",\"#54278f\",\"#3f007d\"]\n},Blues: {\n3: [\"#deebf7\",\"#9ecae1\",\"#3182bd\"],\n4: [\"#eff3ff\",\"#bdd7e7\",\"#6baed6\",\"#2171b5\"],\n5: [\"#eff3ff\",\"#bdd7e7\",\"#6baed6\",\"#3182bd\",\"#08519c\"],\n6: [\"#eff3ff\",\"#c6dbef\",\"#9ecae1\",\"#6baed6\",\"#3182bd\",\"#08519c\"],\n7: [\"#eff3ff\",\"#c6dbef\",\"#9ecae1\",\"#6baed6\",\"#4292c6\",\"#2171b5\",\"#084594\"],\n8: [\"#f7fbff\",\"#deebf7\",\"#c6dbef\",\"#9ecae1\",\"#6baed6\",\"#4292c6\",\"#2171b5\",\"#084594\"],\n9: [\"#f7fbff\",\"#deebf7\",\"#c6dbef\",\"#9ecae1\",\"#6baed6\",\"#4292c6\",\"#2171b5\",\"#08519c\",\"#08306b\"]\n},Greens: {\n3: [\"#e5f5e0\",\"#a1d99b\",\"#31a354\"],\n4: [\"#edf8e9\",\"#bae4b3\",\"#74c476\",\"#238b45\"],\n5: [\"#edf8e9\",\"#bae4b3\",\"#74c476\",\"#31a354\",\"#006d2c\"],\n6: [\"#edf8e9\",\"#c7e9c0\",\"#a1d99b\",\"#74c476\",\"#31a354\",\"#006d2c\"],\n7: [\"#edf8e9\",\"#c7e9c0\",\"#a1d99b\",\"#74c476\",\"#41ab5d\",\"#238b45\",\"#005a32\"],\n8: [\"#f7fcf5\",\"#e5f5e0\",\"#c7e9c0\",\"#a1d99b\",\"#74c476\",\"#41ab5d\",\"#238b45\",\"#005a32\"],\n9: [\"#f7fcf5\",\"#e5f5e0\",\"#c7e9c0\",\"#a1d99b\",\"#74c476\",\"#41ab5d\",\"#238b45\",\"#006d2c\",\"#00441b\"]\n},Oranges: {\n3: [\"#fee6ce\",\"#fdae6b\",\"#e6550d\"],\n4: [\"#feedde\",\"#fdbe85\",\"#fd8d3c\",\"#d94701\"],\n5: [\"#feedde\",\"#fdbe85\",\"#fd8d3c\",\"#e6550d\",\"#a63603\"],\n6: [\"#feedde\",\"#fdd0a2\",\"#fdae6b\",\"#fd8d3c\",\"#e6550d\",\"#a63603\"],\n7: [\"#feedde\",\"#fdd0a2\",\"#fdae6b\",\"#fd8d3c\",\"#f16913\",\"#d94801\",\"#8c2d04\"],\n8: [\"#fff5eb\",\"#fee6ce\",\"#fdd0a2\",\"#fdae6b\",\"#fd8d3c\",\"#f16913\",\"#d94801\",\"#8c2d04\"],\n9: [\"#fff5eb\",\"#fee6ce\",\"#fdd0a2\",\"#fdae6b\",\"#fd8d3c\",\"#f16913\",\"#d94801\",\"#a63603\",\"#7f2704\"]\n},Reds: {\n3: [\"#fee0d2\",\"#fc9272\",\"#de2d26\"],\n4: [\"#fee5d9\",\"#fcae91\",\"#fb6a4a\",\"#cb181d\"],\n5: [\"#fee5d9\",\"#fcae91\",\"#fb6a4a\",\"#de2d26\",\"#a50f15\"],\n6: [\"#fee5d9\",\"#fcbba1\",\"#fc9272\",\"#fb6a4a\",\"#de2d26\",\"#a50f15\"],\n7: [\"#fee5d9\",\"#fcbba1\",\"#fc9272\",\"#fb6a4a\",\"#ef3b2c\",\"#cb181d\",\"#99000d\"],\n8: [\"#fff5f0\",\"#fee0d2\",\"#fcbba1\",\"#fc9272\",\"#fb6a4a\",\"#ef3b2c\",\"#cb181d\",\"#99000d\"],\n9: [\"#fff5f0\",\"#fee0d2\",\"#fcbba1\",\"#fc9272\",\"#fb6a4a\",\"#ef3b2c\",\"#cb181d\",\"#a50f15\",\"#67000d\"]\n},Greys: {\n3: [\"#f0f0f0\",\"#bdbdbd\",\"#636363\"],\n4: [\"#f7f7f7\",\"#cccccc\",\"#969696\",\"#525252\"],\n5: [\"#f7f7f7\",\"#cccccc\",\"#969696\",\"#636363\",\"#252525\"],\n6: [\"#f7f7f7\",\"#d9d9d9\",\"#bdbdbd\",\"#969696\",\"#636363\",\"#252525\"],\n7: [\"#f7f7f7\",\"#d9d9d9\",\"#bdbdbd\",\"#969696\",\"#737373\",\"#525252\",\"#252525\"],\n8: [\"#ffffff\",\"#f0f0f0\",\"#d9d9d9\",\"#bdbdbd\",\"#969696\",\"#737373\",\"#525252\",\"#252525\"],\n9: [\"#ffffff\",\"#f0f0f0\",\"#d9d9d9\",\"#bdbdbd\",\"#969696\",\"#737373\",\"#525252\",\"#252525\",\"#000000\"]\n},PuOr: {\n3: [\"#f1a340\",\"#f7f7f7\",\"#998ec3\"],\n4: [\"#e66101\",\"#fdb863\",\"#b2abd2\",\"#5e3c99\"],\n5: [\"#e66101\",\"#fdb863\",\"#f7f7f7\",\"#b2abd2\",\"#5e3c99\"],\n6: [\"#b35806\",\"#f1a340\",\"#fee0b6\",\"#d8daeb\",\"#998ec3\",\"#542788\"],\n7: [\"#b35806\",\"#f1a340\",\"#fee0b6\",\"#f7f7f7\",\"#d8daeb\",\"#998ec3\",\"#542788\"],\n8: [\"#b35806\",\"#e08214\",\"#fdb863\",\"#fee0b6\",\"#d8daeb\",\"#b2abd2\",\"#8073ac\",\"#542788\"],\n9: [\"#b35806\",\"#e08214\",\"#fdb863\",\"#fee0b6\",\"#f7f7f7\",\"#d8daeb\",\"#b2abd2\",\"#8073ac\",\"#542788\"],\n10: [\"#7f3b08\",\"#b35806\",\"#e08214\",\"#fdb863\",\"#fee0b6\",\"#d8daeb\",\"#b2abd2\",\"#8073ac\",\"#542788\",\"#2d004b\"],\n11: [\"#7f3b08\",\"#b35806\",\"#e08214\",\"#fdb863\",\"#fee0b6\",\"#f7f7f7\",\"#d8daeb\",\"#b2abd2\",\"#8073ac\",\"#542788\",\"#2d004b\"]\n},BrBG: {\n3: [\"#d8b365\",\"#f5f5f5\",\"#5ab4ac\"],\n4: [\"#a6611a\",\"#dfc27d\",\"#80cdc1\",\"#018571\"],\n5: [\"#a6611a\",\"#dfc27d\",\"#f5f5f5\",\"#80cdc1\",\"#018571\"],\n6: [\"#8c510a\",\"#d8b365\",\"#f6e8c3\",\"#c7eae5\",\"#5ab4ac\",\"#01665e\"],\n7: [\"#8c510a\",\"#d8b365\",\"#f6e8c3\",\"#f5f5f5\",\"#c7eae5\",\"#5ab4ac\",\"#01665e\"],\n8: [\"#8c510a\",\"#bf812d\",\"#dfc27d\",\"#f6e8c3\",\"#c7eae5\",\"#80cdc1\",\"#35978f\",\"#01665e\"],\n9: [\"#8c510a\",\"#bf812d\",\"#dfc27d\",\"#f6e8c3\",\"#f5f5f5\",\"#c7eae5\",\"#80cdc1\",\"#35978f\",\"#01665e\"],\n10: [\"#543005\",\"#8c510a\",\"#bf812d\",\"#dfc27d\",\"#f6e8c3\",\"#c7eae5\",\"#80cdc1\",\"#35978f\",\"#01665e\",\"#003c30\"],\n11: [\"#543005\",\"#8c510a\",\"#bf812d\",\"#dfc27d\",\"#f6e8c3\",\"#f5f5f5\",\"#c7eae5\",\"#80cdc1\",\"#35978f\",\"#01665e\",\"#003c30\"]\n},PRGn: {\n3: [\"#af8dc3\",\"#f7f7f7\",\"#7fbf7b\"],\n4: [\"#7b3294\",\"#c2a5cf\",\"#a6dba0\",\"#008837\"],\n5: [\"#7b3294\",\"#c2a5cf\",\"#f7f7f7\",\"#a6dba0\",\"#008837\"],\n6: [\"#762a83\",\"#af8dc3\",\"#e7d4e8\",\"#d9f0d3\",\"#7fbf7b\",\"#1b7837\"],\n7: [\"#762a83\",\"#af8dc3\",\"#e7d4e8\",\"#f7f7f7\",\"#d9f0d3\",\"#7fbf7b\",\"#1b7837\"],\n8: [\"#762a83\",\"#9970ab\",\"#c2a5cf\",\"#e7d4e8\",\"#d9f0d3\",\"#a6dba0\",\"#5aae61\",\"#1b7837\"],\n9: [\"#762a83\",\"#9970ab\",\"#c2a5cf\",\"#e7d4e8\",\"#f7f7f7\",\"#d9f0d3\",\"#a6dba0\",\"#5aae61\",\"#1b7837\"],\n10: [\"#40004b\",\"#762a83\",\"#9970ab\",\"#c2a5cf\",\"#e7d4e8\",\"#d9f0d3\",\"#a6dba0\",\"#5aae61\",\"#1b7837\",\"#00441b\"],\n11: [\"#40004b\",\"#762a83\",\"#9970ab\",\"#c2a5cf\",\"#e7d4e8\",\"#f7f7f7\",\"#d9f0d3\",\"#a6dba0\",\"#5aae61\",\"#1b7837\",\"#00441b\"]\n},PiYG: {\n3: [\"#e9a3c9\",\"#f7f7f7\",\"#a1d76a\"],\n4: [\"#d01c8b\",\"#f1b6da\",\"#b8e186\",\"#4dac26\"],\n5: [\"#d01c8b\",\"#f1b6da\",\"#f7f7f7\",\"#b8e186\",\"#4dac26\"],\n6: [\"#c51b7d\",\"#e9a3c9\",\"#fde0ef\",\"#e6f5d0\",\"#a1d76a\",\"#4d9221\"],\n7: [\"#c51b7d\",\"#e9a3c9\",\"#fde0ef\",\"#f7f7f7\",\"#e6f5d0\",\"#a1d76a\",\"#4d9221\"],\n8: [\"#c51b7d\",\"#de77ae\",\"#f1b6da\",\"#fde0ef\",\"#e6f5d0\",\"#b8e186\",\"#7fbc41\",\"#4d9221\"],\n9: [\"#c51b7d\",\"#de77ae\",\"#f1b6da\",\"#fde0ef\",\"#f7f7f7\",\"#e6f5d0\",\"#b8e186\",\"#7fbc41\",\"#4d9221\"],\n10: [\"#8e0152\",\"#c51b7d\",\"#de77ae\",\"#f1b6da\",\"#fde0ef\",\"#e6f5d0\",\"#b8e186\",\"#7fbc41\",\"#4d9221\",\"#276419\"],\n11: [\"#8e0152\",\"#c51b7d\",\"#de77ae\",\"#f1b6da\",\"#fde0ef\",\"#f7f7f7\",\"#e6f5d0\",\"#b8e186\",\"#7fbc41\",\"#4d9221\",\"#276419\"]\n},RdBu: {\n3: [\"#ef8a62\",\"#f7f7f7\",\"#67a9cf\"],\n4: [\"#ca0020\",\"#f4a582\",\"#92c5de\",\"#0571b0\"],\n5: [\"#ca0020\",\"#f4a582\",\"#f7f7f7\",\"#92c5de\",\"#0571b0\"],\n6: [\"#b2182b\",\"#ef8a62\",\"#fddbc7\",\"#d1e5f0\",\"#67a9cf\",\"#2166ac\"],\n7: [\"#b2182b\",\"#ef8a62\",\"#fddbc7\",\"#f7f7f7\",\"#d1e5f0\",\"#67a9cf\",\"#2166ac\"],\n8: [\"#b2182b\",\"#d6604d\",\"#f4a582\",\"#fddbc7\",\"#d1e5f0\",\"#92c5de\",\"#4393c3\",\"#2166ac\"],\n9: [\"#b2182b\",\"#d6604d\",\"#f4a582\",\"#fddbc7\",\"#f7f7f7\",\"#d1e5f0\",\"#92c5de\",\"#4393c3\",\"#2166ac\"],\n10: [\"#67001f\",\"#b2182b\",\"#d6604d\",\"#f4a582\",\"#fddbc7\",\"#d1e5f0\",\"#92c5de\",\"#4393c3\",\"#2166ac\",\"#053061\"],\n11: [\"#67001f\",\"#b2182b\",\"#d6604d\",\"#f4a582\",\"#fddbc7\",\"#f7f7f7\",\"#d1e5f0\",\"#92c5de\",\"#4393c3\",\"#2166ac\",\"#053061\"]\n},RdGy: {\n3: [\"#ef8a62\",\"#ffffff\",\"#999999\"],\n4: [\"#ca0020\",\"#f4a582\",\"#bababa\",\"#404040\"],\n5: [\"#ca0020\",\"#f4a582\",\"#ffffff\",\"#bababa\",\"#404040\"],\n6: [\"#b2182b\",\"#ef8a62\",\"#fddbc7\",\"#e0e0e0\",\"#999999\",\"#4d4d4d\"],\n7: [\"#b2182b\",\"#ef8a62\",\"#fddbc7\",\"#ffffff\",\"#e0e0e0\",\"#999999\",\"#4d4d4d\"],\n8: [\"#b2182b\",\"#d6604d\",\"#f4a582\",\"#fddbc7\",\"#e0e0e0\",\"#bababa\",\"#878787\",\"#4d4d4d\"],\n9: [\"#b2182b\",\"#d6604d\",\"#f4a582\",\"#fddbc7\",\"#ffffff\",\"#e0e0e0\",\"#bababa\",\"#878787\",\"#4d4d4d\"],\n10: [\"#67001f\",\"#b2182b\",\"#d6604d\",\"#f4a582\",\"#fddbc7\",\"#e0e0e0\",\"#bababa\",\"#878787\",\"#4d4d4d\",\"#1a1a1a\"],\n11: [\"#67001f\",\"#b2182b\",\"#d6604d\",\"#f4a582\",\"#fddbc7\",\"#ffffff\",\"#e0e0e0\",\"#bababa\",\"#878787\",\"#4d4d4d\",\"#1a1a1a\"]\n},RdYlBu: {\n3: [\"#fc8d59\",\"#ffffbf\",\"#91bfdb\"],\n4: [\"#d7191c\",\"#fdae61\",\"#abd9e9\",\"#2c7bb6\"],\n5: [\"#d7191c\",\"#fdae61\",\"#ffffbf\",\"#abd9e9\",\"#2c7bb6\"],\n6: [\"#d73027\",\"#fc8d59\",\"#fee090\",\"#e0f3f8\",\"#91bfdb\",\"#4575b4\"],\n7: [\"#d73027\",\"#fc8d59\",\"#fee090\",\"#ffffbf\",\"#e0f3f8\",\"#91bfdb\",\"#4575b4\"],\n8: [\"#d73027\",\"#f46d43\",\"#fdae61\",\"#fee090\",\"#e0f3f8\",\"#abd9e9\",\"#74add1\",\"#4575b4\"],\n9: [\"#d73027\",\"#f46d43\",\"#fdae61\",\"#fee090\",\"#ffffbf\",\"#e0f3f8\",\"#abd9e9\",\"#74add1\",\"#4575b4\"],\n10: [\"#a50026\",\"#d73027\",\"#f46d43\",\"#fdae61\",\"#fee090\",\"#e0f3f8\",\"#abd9e9\",\"#74add1\",\"#4575b4\",\"#313695\"],\n11: [\"#a50026\",\"#d73027\",\"#f46d43\",\"#fdae61\",\"#fee090\",\"#ffffbf\",\"#e0f3f8\",\"#abd9e9\",\"#74add1\",\"#4575b4\",\"#313695\"]\n},Spectral: {\n3: [\"#fc8d59\",\"#ffffbf\",\"#99d594\"],\n4: [\"#d7191c\",\"#fdae61\",\"#abdda4\",\"#2b83ba\"],\n5: [\"#d7191c\",\"#fdae61\",\"#ffffbf\",\"#abdda4\",\"#2b83ba\"],\n6: [\"#d53e4f\",\"#fc8d59\",\"#fee08b\",\"#e6f598\",\"#99d594\",\"#3288bd\"],\n7: [\"#d53e4f\",\"#fc8d59\",\"#fee08b\",\"#ffffbf\",\"#e6f598\",\"#99d594\",\"#3288bd\"],\n8: [\"#d53e4f\",\"#f46d43\",\"#fdae61\",\"#fee08b\",\"#e6f598\",\"#abdda4\",\"#66c2a5\",\"#3288bd\"],\n9: [\"#d53e4f\",\"#f46d43\",\"#fdae61\",\"#fee08b\",\"#ffffbf\",\"#e6f598\",\"#abdda4\",\"#66c2a5\",\"#3288bd\"],\n10: [\"#9e0142\",\"#d53e4f\",\"#f46d43\",\"#fdae61\",\"#fee08b\",\"#e6f598\",\"#abdda4\",\"#66c2a5\",\"#3288bd\",\"#5e4fa2\"],\n11: [\"#9e0142\",\"#d53e4f\",\"#f46d43\",\"#fdae61\",\"#fee08b\",\"#ffffbf\",\"#e6f598\",\"#abdda4\",\"#66c2a5\",\"#3288bd\",\"#5e4fa2\"]\n},RdYlGn: {\n3: [\"#fc8d59\",\"#ffffbf\",\"#91cf60\"],\n4: [\"#d7191c\",\"#fdae61\",\"#a6d96a\",\"#1a9641\"],\n5: [\"#d7191c\",\"#fdae61\",\"#ffffbf\",\"#a6d96a\",\"#1a9641\"],\n6: [\"#d73027\",\"#fc8d59\",\"#fee08b\",\"#d9ef8b\",\"#91cf60\",\"#1a9850\"],\n7: [\"#d73027\",\"#fc8d59\",\"#fee08b\",\"#ffffbf\",\"#d9ef8b\",\"#91cf60\",\"#1a9850\"],\n8: [\"#d73027\",\"#f46d43\",\"#fdae61\",\"#fee08b\",\"#d9ef8b\",\"#a6d96a\",\"#66bd63\",\"#1a9850\"],\n9: [\"#d73027\",\"#f46d43\",\"#fdae61\",\"#fee08b\",\"#ffffbf\",\"#d9ef8b\",\"#a6d96a\",\"#66bd63\",\"#1a9850\"],\n10: [\"#a50026\",\"#d73027\",\"#f46d43\",\"#fdae61\",\"#fee08b\",\"#d9ef8b\",\"#a6d96a\",\"#66bd63\",\"#1a9850\",\"#006837\"],\n11: [\"#a50026\",\"#d73027\",\"#f46d43\",\"#fdae61\",\"#fee08b\",\"#ffffbf\",\"#d9ef8b\",\"#a6d96a\",\"#66bd63\",\"#1a9850\",\"#006837\"]\n},Accent: {\n3: [\"#7fc97f\",\"#beaed4\",\"#fdc086\"],\n4: [\"#7fc97f\",\"#beaed4\",\"#fdc086\",\"#ffff99\"],\n5: [\"#7fc97f\",\"#beaed4\",\"#fdc086\",\"#ffff99\",\"#386cb0\"],\n6: [\"#7fc97f\",\"#beaed4\",\"#fdc086\",\"#ffff99\",\"#386cb0\",\"#f0027f\"],\n7: [\"#7fc97f\",\"#beaed4\",\"#fdc086\",\"#ffff99\",\"#386cb0\",\"#f0027f\",\"#bf5b17\"],\n8: [\"#7fc97f\",\"#beaed4\",\"#fdc086\",\"#ffff99\",\"#386cb0\",\"#f0027f\",\"#bf5b17\",\"#666666\"]\n},Dark2: {\n3: [\"#1b9e77\",\"#d95f02\",\"#7570b3\"],\n4: [\"#1b9e77\",\"#d95f02\",\"#7570b3\",\"#e7298a\"],\n5: [\"#1b9e77\",\"#d95f02\",\"#7570b3\",\"#e7298a\",\"#66a61e\"],\n6: [\"#1b9e77\",\"#d95f02\",\"#7570b3\",\"#e7298a\",\"#66a61e\",\"#e6ab02\"],\n7: [\"#1b9e77\",\"#d95f02\",\"#7570b3\",\"#e7298a\",\"#66a61e\",\"#e6ab02\",\"#a6761d\"],\n8: [\"#1b9e77\",\"#d95f02\",\"#7570b3\",\"#e7298a\",\"#66a61e\",\"#e6ab02\",\"#a6761d\",\"#666666\"]\n},Paired: {\n3: [\"#a6cee3\",\"#1f78b4\",\"#b2df8a\"],\n4: [\"#a6cee3\",\"#1f78b4\",\"#b2df8a\",\"#33a02c\"],\n5: [\"#a6cee3\",\"#1f78b4\",\"#b2df8a\",\"#33a02c\",\"#fb9a99\"],\n6: [\"#a6cee3\",\"#1f78b4\",\"#b2df8a\",\"#33a02c\",\"#fb9a99\",\"#e31a1c\"],\n7: [\"#a6cee3\",\"#1f78b4\",\"#b2df8a\",\"#33a02c\",\"#fb9a99\",\"#e31a1c\",\"#fdbf6f\"],\n8: [\"#a6cee3\",\"#1f78b4\",\"#b2df8a\",\"#33a02c\",\"#fb9a99\",\"#e31a1c\",\"#fdbf6f\",\"#ff7f00\"],\n9: [\"#a6cee3\",\"#1f78b4\",\"#b2df8a\",\"#33a02c\",\"#fb9a99\",\"#e31a1c\",\"#fdbf6f\",\"#ff7f00\",\"#cab2d6\"],\n10: [\"#a6cee3\",\"#1f78b4\",\"#b2df8a\",\"#33a02c\",\"#fb9a99\",\"#e31a1c\",\"#fdbf6f\",\"#ff7f00\",\"#cab2d6\",\"#6a3d9a\"],\n11: [\"#a6cee3\",\"#1f78b4\",\"#b2df8a\",\"#33a02c\",\"#fb9a99\",\"#e31a1c\",\"#fdbf6f\",\"#ff7f00\",\"#cab2d6\",\"#6a3d9a\",\"#ffff99\"],\n12: [\"#a6cee3\",\"#1f78b4\",\"#b2df8a\",\"#33a02c\",\"#fb9a99\",\"#e31a1c\",\"#fdbf6f\",\"#ff7f00\",\"#cab2d6\",\"#6a3d9a\",\"#ffff99\",\"#b15928\"]\n},Pastel1: {\n3: [\"#fbb4ae\",\"#b3cde3\",\"#ccebc5\"],\n4: [\"#fbb4ae\",\"#b3cde3\",\"#ccebc5\",\"#decbe4\"],\n5: [\"#fbb4ae\",\"#b3cde3\",\"#ccebc5\",\"#decbe4\",\"#fed9a6\"],\n6: [\"#fbb4ae\",\"#b3cde3\",\"#ccebc5\",\"#decbe4\",\"#fed9a6\",\"#ffffcc\"],\n7: [\"#fbb4ae\",\"#b3cde3\",\"#ccebc5\",\"#decbe4\",\"#fed9a6\",\"#ffffcc\",\"#e5d8bd\"],\n8: [\"#fbb4ae\",\"#b3cde3\",\"#ccebc5\",\"#decbe4\",\"#fed9a6\",\"#ffffcc\",\"#e5d8bd\",\"#fddaec\"],\n9: [\"#fbb4ae\",\"#b3cde3\",\"#ccebc5\",\"#decbe4\",\"#fed9a6\",\"#ffffcc\",\"#e5d8bd\",\"#fddaec\",\"#f2f2f2\"]\n},Pastel2: {\n3: [\"#b3e2cd\",\"#fdcdac\",\"#cbd5e8\"],\n4: [\"#b3e2cd\",\"#fdcdac\",\"#cbd5e8\",\"#f4cae4\"],\n5: [\"#b3e2cd\",\"#fdcdac\",\"#cbd5e8\",\"#f4cae4\",\"#e6f5c9\"],\n6: [\"#b3e2cd\",\"#fdcdac\",\"#cbd5e8\",\"#f4cae4\",\"#e6f5c9\",\"#fff2ae\"],\n7: [\"#b3e2cd\",\"#fdcdac\",\"#cbd5e8\",\"#f4cae4\",\"#e6f5c9\",\"#fff2ae\",\"#f1e2cc\"],\n8: [\"#b3e2cd\",\"#fdcdac\",\"#cbd5e8\",\"#f4cae4\",\"#e6f5c9\",\"#fff2ae\",\"#f1e2cc\",\"#cccccc\"]\n},Set1: {\n3: [\"#e41a1c\",\"#377eb8\",\"#4daf4a\"],\n4: [\"#e41a1c\",\"#377eb8\",\"#4daf4a\",\"#984ea3\"],\n5: [\"#e41a1c\",\"#377eb8\",\"#4daf4a\",\"#984ea3\",\"#ff7f00\"],\n6: [\"#e41a1c\",\"#377eb8\",\"#4daf4a\",\"#984ea3\",\"#ff7f00\",\"#ffff33\"],\n7: [\"#e41a1c\",\"#377eb8\",\"#4daf4a\",\"#984ea3\",\"#ff7f00\",\"#ffff33\",\"#a65628\"],\n8: [\"#e41a1c\",\"#377eb8\",\"#4daf4a\",\"#984ea3\",\"#ff7f00\",\"#ffff33\",\"#a65628\",\"#f781bf\"],\n9: [\"#e41a1c\",\"#377eb8\",\"#4daf4a\",\"#984ea3\",\"#ff7f00\",\"#ffff33\",\"#a65628\",\"#f781bf\",\"#999999\"]\n},Set2: {\n3: [\"#66c2a5\",\"#fc8d62\",\"#8da0cb\"],\n4: [\"#66c2a5\",\"#fc8d62\",\"#8da0cb\",\"#e78ac3\"],\n5: [\"#66c2a5\",\"#fc8d62\",\"#8da0cb\",\"#e78ac3\",\"#a6d854\"],\n6: [\"#66c2a5\",\"#fc8d62\",\"#8da0cb\",\"#e78ac3\",\"#a6d854\",\"#ffd92f\"],\n7: [\"#66c2a5\",\"#fc8d62\",\"#8da0cb\",\"#e78ac3\",\"#a6d854\",\"#ffd92f\",\"#e5c494\"],\n8: [\"#66c2a5\",\"#fc8d62\",\"#8da0cb\",\"#e78ac3\",\"#a6d854\",\"#ffd92f\",\"#e5c494\",\"#b3b3b3\"]\n},Set3: {\n3: [\"#8dd3c7\",\"#ffffb3\",\"#bebada\"],\n4: [\"#8dd3c7\",\"#ffffb3\",\"#bebada\",\"#fb8072\"],\n5: [\"#8dd3c7\",\"#ffffb3\",\"#bebada\",\"#fb8072\",\"#80b1d3\"],\n6: [\"#8dd3c7\",\"#ffffb3\",\"#bebada\",\"#fb8072\",\"#80b1d3\",\"#fdb462\"],\n7: [\"#8dd3c7\",\"#ffffb3\",\"#bebada\",\"#fb8072\",\"#80b1d3\",\"#fdb462\",\"#b3de69\"],\n8: [\"#8dd3c7\",\"#ffffb3\",\"#bebada\",\"#fb8072\",\"#80b1d3\",\"#fdb462\",\"#b3de69\",\"#fccde5\"],\n9: [\"#8dd3c7\",\"#ffffb3\",\"#bebada\",\"#fb8072\",\"#80b1d3\",\"#fdb462\",\"#b3de69\",\"#fccde5\",\"#d9d9d9\"],\n10: [\"#8dd3c7\",\"#ffffb3\",\"#bebada\",\"#fb8072\",\"#80b1d3\",\"#fdb462\",\"#b3de69\",\"#fccde5\",\"#d9d9d9\",\"#bc80bd\"],\n11: [\"#8dd3c7\",\"#ffffb3\",\"#bebada\",\"#fb8072\",\"#80b1d3\",\"#fdb462\",\"#b3de69\",\"#fccde5\",\"#d9d9d9\",\"#bc80bd\",\"#ccebc5\"],\n12: [\"#8dd3c7\",\"#ffffb3\",\"#bebada\",\"#fb8072\",\"#80b1d3\",\"#fdb462\",\"#b3de69\",\"#fccde5\",\"#d9d9d9\",\"#bc80bd\",\"#ccebc5\",\"#ffed6f\"]\n}};\n"
  },
  {
    "path": "web/js/fq.js",
    "content": "Array.prototype.equals = function (array) {\n    if (!array) return false;\n    if (this.length != array.length) return false;\n\n    for (var i = 0, l=this.length; i < l; i++) {\n        if (this[i] instanceof Array && array[i] instanceof Array) {\n            if (!this[i].equals(array[i])) return false;       \n        }           \n        else if (this[i] != array[i]) { \n            return false;   \n        }           \n    }       \n    return true;\n}\n\nfunction pretty_number(a) {\n  var i = 0;\n  var unit = '';\n  if(a.toFixed === undefined) return a;\n  while(i < 6 && a > 1024) { a = a/1024; i++; }\n  switch(i) {\n    case 1: unit = 'k'; break;\n    case 2: unit = 'M'; break;\n    case 3: unit = 'G'; break;\n    case 4: unit = 'T'; break;\n    case 5: unit = 'P'; break;\n    case 6: unit = 'E'; break;\n    default:\n  }\n  return a.toFixed(Math.min(i,3)) + unit;\n}\n\nfunction hexify(a) {\n  return a.split(/|/)\n          .map(function(a) {\n            var r = a.charCodeAt(0).toString(16);\n            if(r.length < 2) return \"0\" + r;\n            return r;\n          }).join('')\n}\nfunction $badge(n) {\n  return $(\"<span class=\\\"badge\\\"/>\").text(n);\n}\nfunction $label(n, type) {\n  if(!type) type = \"default\";\n  return $(\"<span class=\\\"label label-\"+type+\"\\\"/>\").text(n);\n}\nfunction alphaKeys(obj) {\n  var keys = []\n  for(var key in obj) if(obj.hasOwnProperty(key)) keys.push(key);\n  return keys.sort();\n}\nvar last_stats = {}\nvar last_stats_time;\nvar current_stats_time;\nfunction rate_calc(type, name, data, part) {\n  if(!last_stats_time ||\n     !last_stats[type] ||\n     !last_stats[type][name]) return undefined;\n  var dt = current_stats_time - last_stats_time;\n  var dv = data[part] - last_stats[type][name][part];\n  return (dv/dt)*1000; /* /ms -> /s */\n}\n\nfunction rate_queue_calc(type, name, data, i, part) {\n  if(!last_stats_time ||\n     !last_stats[type] ||\n     !last_stats[type][name]) return undefined;\n  if(last_stats[type][name].clients.length != data.length) return undefined;\n  var dt = current_stats_time - last_stats_time;\n  var dv = data[i][part] - last_stats[type][name].clients[i][part];\n  return (dv/dt)*1000; /* /ms -> /s */\n}\n\nfunction update_exchange(name,detail) {\n  var id = \"exchange-\" + hexify(name);\n  var $tgt = $(\"div#\" + id);\n  if($tgt.length == 0) {\n    if(name == \"_aggregate\") return;\n    var $template = $(\"div#exchange-5f616767726567617465\"); /* _aggregate */\n    $tgt = $template.clone();\n    $tgt.attr('id', id);\n    $tgt.find('h4.panel-title').text(name);\n    var $ip = $(\"#exchanges > div.row:last\");\n    $ip.append($tgt);\n    if($ip.find(\">div\").length == 3) {\n      $(\"div#exchanges\").append($(\"<div class=\\\"row\\\"></div>\"));\n    }\n  }\n  [\"no_exchange\", \"messages\", \"octets\", \"no_route\", \"routed\", \"dropped\", \"size_dropped\"].forEach(function(attr) {\n    var rate = rate_calc(\"exchanges\", name, detail, attr);\n    if(rate !== undefined)\n      rate = parseFloat(rate).toFixed(0);\n    else rate = \"\";\n    var value = pretty_number(parseFloat(detail[attr]));\n    if(/NaN/.test(value)) value = \"\";\n    if(/NaN/.test(rate)) rate = \"\";\n    var ctag = attr.replace(/_/g, \"-\");\n    $tgt.find(\".exchange-\"+ctag).text(value);\n    $tgt.find(\".exchange-\"+ctag+\"-rate\").text(rate);\n  });\n}\n\nfunction update_routes(name,detail) {\n  if(name == \"_aggregate\") return;\n  var routes = $(\"#routes\");\n  var re = \"route-exchange-\" + hexify(name);\n  var $panel = routes.find(\"#\" + re);\n  if($panel.length == 0) {\n    $panel = $(\"#route-exchange-template\").clone();\n    $panel.attr('id', re);\n    routes.append($panel);\n    $panel.find(\".exchange-name\").text(name);\n  } else {\n    $panel = $($panel[0]);\n  }\n \n  $panel.find(\".route-row\").addClass(\"updating\");\n  var sortedRoutes = alphaKeys(detail);\n  sortedRoutes.forEach(function(rid) {\n    var r = detail[rid];\n    var $route = $panel.find(\"#route-\" + rid);\n    if($route.length == 0) {\n      $route = $(\"#route-detail-template\").clone();\n      $route.attr('id', \"#route-\" + rid);\n      $panel.append($route);\n    } else {\n      $route = $($route[0]);\n    }\n    $route.find(\".route-prefix\").html($label(r.prefix || '[blank / no prefix]', \"success\"));\n    $route.find(\".route-mode\").html($label(r.permanent ? \"permanent\" : \"transient\", r.permanent ? \"primary\" : \"default\"));\n    var prog_sans_prefix = r.program.replace(/^prefix:\"(?:\\\\.|[^\"])*\"\\s*/, \"\");\n    $route.find(\".route-program\").text(prog_sans_prefix || '[ no program / match all ]');\n    $route.find(\".route-invocations\").text(pretty_number(parseInt(r.invocations)));\n    $route.find(\".route-avg-ns\").text(\"\" + r.avg_ns + \"ns\");\n    $route.removeClass(\"updating\");\n  });\n  \n  $panel.find(\".route-row.updating\").remove();\n}\n\nfunction clear_exchanges() {\n  $(\"#exchanges > div.row:not(:first-child)\").remove()\n  $(\"#exchanges > div.row > div.exchange-detail:not(:first-child)\").remove();\n  $(\"#routes\").empty();\n}\n\nfunction mk_client(c) {\n  return c.name;\n}\n\nfunction update_queue_row(name,detail) {\n  var id = \"queue-\" + hexify(name);\n  var $tgt = $(\"div#\" + id);\n  if($tgt.length == 0) {\n    $tgt = $(\"#queue-template\").clone();\n    $tgt.attr('id', id);\n    var $collapse = $tgt.find(\".panel-collapse\");\n    $tgt.find(\".panel-heading a\").attr('aria-controls', id + \"-clients\").on('click',\n      function() { $collapse.collapse('toggle'); }\n    );\n    $collapse.attr('id', id + \"-clients\");\n    $(\"#queues\").append($tgt);\n    $(document).on('.data-api');\n  }\n \n  $tgt.find(\".queue-name\").text(name); \n  $tgt.find(\".queue-type\").empty()\n    .append($label(\"type:\" + detail.type, \"success\"));\n  $tgt.find(\".queue-exposure\").empty()\n    .append($label(detail.private?\"private\":\"public\",\n                   detail.private?\"default\":\"primary\"));\n  $tgt.find(\".queue-policy\").empty()\n    .append($label(\"policy:\" + detail.policy, \"danger\"));\n\n  var dropoutrate = rate_calc(\"queues\", name, detail, \"dropped_to\");\n  if(dropoutrate !== undefined) dropoutrate = parseFloat(dropoutrate).toFixed(0);\n  else dropoutrate = \"\";\n  var dropoutvalue = pretty_number(parseFloat(detail.dropped_to));\n  console.log(dropoutvalue, dropoutrate);\n  $tgt.find(\".queue-dropped-out-rate\").text(dropoutrate);\n  $tgt.find(\".queue-dropped-out-value\").text(dropoutvalue);\n\n  var $pb = $tgt.find(\".progress-bar\");\n  var pct = 0;\n  if(detail.backlog_limit > 0) pct = Math.floor(100 * detail.backlog / detail.backlog_limit);\n  $pb.attr(\"style\", \"width: \" + pct + \"%\");\n  $pb.attr('aria-valuemax', detail.backlog_limit);\n  $pb.attr('aria-valuenow', detail.backlog);\n  \n  $pb.removeClass(\"progress-bar-success\");\n  $pb.removeClass(\"progress-bar-warning\");\n  $pb.addClass( pct > 75 ? \"progress-bar-warning\" : \"progress-bar-success\" );\n  if(detail.backlog_limit > 0)\n    $tgt.find(\".backlog\").text(detail.backlog + \"/\" + detail.backlog_limit);\n  else\n    $tgt.find(\".backlog\").text(detail.backlog);\n\n  var $clients = $tgt.find(\"table.clients tbody\");\n  if($clients.find(\">tr\").length != detail.clients.length) {\n    $clients.empty();\n    detail.clients.forEach(function() {\n      $clients.append($(\"#client-template\").clone().removeAttr('id'));\n    });\n  }\n  $clients = $tgt.find(\"table.clients tbody > tr\");\n  for(var i=0;i<$clients.length;i++) {\n    var $c = $($clients[i]), c = detail.clients[i];\n    $c.find(\".client-user\").text(c.user);\n    $c.find(\".client-address\").text(c.remote_addr + \":\" + c.remote_port);\n\n\n    [\"msgs_in\", \"octets_in\", \"msgs_out\", \"octets_out\",\n     \"routed\", \"dropped\", \"size_dropped\", \"no_route\", \"no_exchange\"].forEach(function(attr) {\n      var rate = rate_queue_calc(\"queues\", name, detail.clients, i, attr);\n      if(rate !== undefined) rate = parseFloat(rate).toFixed(0);\n      else rate = \"\";\n      var value = pretty_number(parseFloat(c[attr]));\n      var ctag = attr.replace(/_/g, \"-\");\n      $c.find(\".client-\" + ctag + \"-rate\").text(rate);\n      $c.find(\".client-\" + ctag + \"-value\").text(value);\n    });\n  }\n}\n\n\nfunction refresh_stats() {\n  $.ajax(\"/stats.json\").done(function (x) {\n    if(!x) return;\n    current_stats_time = Date.now();\n    if(x.version) $(\"#fq-version\").text(x.version);\n\n    var sortedExchanges = alphaKeys(x.exchanges);\n    if(!alphaKeys(last_stats.exchanges || {}).equals(sortedExchanges)) clear_exchanges();\n    sortedExchanges.forEach(function(exchange) {\n      update_exchange(exchange, x.exchanges[exchange]);\n      update_routes(exchange, x.exchanges[exchange].routes);\n    });\n\n    var sortedQueues = alphaKeys(x.queues);\n    if(!alphaKeys(last_stats.queues || {}).equals(sortedQueues))\n      $(\"#queues\").empty();\n    sortedQueues.forEach(function(queue) {\n      update_queue_row(queue, x.queues[queue]);\n    });\n\n    last_stats_time = current_stats_time;\n    last_stats = x;\n  });\n}\n\nsetInterval(refresh_stats, 1000);\nrefresh_stats();\n"
  }
]