summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKeith Packard <keithp@keithp.com>2018-10-07 09:22:44 -0700
committerKeith Packard <keithp@keithp.com>2018-10-07 09:38:43 -0700
commitaf678be97e419a1167a0630f28bcaba82d67929b (patch)
tree728c79d5eee0097b42d05669baeb836accc11350
parent7b248524d4bd2c3ab64899b133230d131258396c (diff)
map-server: Implement altos-map in C
This app is all about startup time, and java starts slowly. Re-implement in C to improve server performance. Signed-off-by: Keith Packard <keithp@keithp.com>
-rw-r--r--configure.ac3
-rw-r--r--map-server/Makefile.am2
-rw-r--r--map-server/altos-map/.gitignore5
-rw-r--r--map-server/altos-map/Makefile.am64
-rw-r--r--map-server/altos-map/altos-map.c286
-rwxr-xr-xmap-server/altos-map/altos-mapc-fake (renamed from map-server/altos-map/altos-map-fake)2
-rw-r--r--map-server/altos-mapj/.gitignore6
-rw-r--r--map-server/altos-mapj/AltosMap.java (renamed from map-server/altos-map/AltosMap.java)0
-rw-r--r--map-server/altos-mapj/Makefile.am62
-rw-r--r--map-server/altos-mapj/Manifest.txt (renamed from map-server/altos-map/Manifest.txt)0
-rwxr-xr-xmap-server/altos-mapj/altos-mapj-fake6
11 files changed, 369 insertions, 67 deletions
diff --git a/configure.ac b/configure.ac
index dae2499d..6e02f968 100644
--- a/configure.ac
+++ b/configure.ac
@@ -417,6 +417,8 @@ if test "x$HAVE_NICKLE" = "xno"; then
AC_MSG_ERROR([Please install nickle to build AltOs])
fi
+PKG_CHECK_MODULES([JANSSON], [jansson])
+
AC_ARG_WITH([readline],
[AS_HELP_STRING([--with-readline],
[enable readline functionality in ao-dbg @<:@default=auto@:>@])],
@@ -574,6 +576,7 @@ ao-utils/Makefile
map-server/Makefile
map-server/altos-mapd/Makefile
map-server/altos-map/Makefile
+map-server/altos-mapj/Makefile
src/Version
])
diff --git a/map-server/Makefile.am b/map-server/Makefile.am
index f9b8a727..39ff8014 100644
--- a/map-server/Makefile.am
+++ b/map-server/Makefile.am
@@ -1 +1 @@
-SUBDIRS=altos-mapd altos-map
+SUBDIRS=altos-mapd altos-map altos-mapj
diff --git a/map-server/altos-map/.gitignore b/map-server/altos-map/.gitignore
index ea012eef..1158b56a 100644
--- a/map-server/altos-map/.gitignore
+++ b/map-server/altos-map/.gitignore
@@ -1,6 +1 @@
altos-map
-altos-map-jdb
-altos-map-test
-*.jar
-*.stamp
-classes
diff --git a/map-server/altos-map/Makefile.am b/map-server/altos-map/Makefile.am
index f7c9bdc8..6925957b 100644
--- a/map-server/altos-map/Makefile.am
+++ b/map-server/altos-map/Makefile.am
@@ -1,62 +1,6 @@
-JAVAROOT=classes
-AM_JAVACFLAGS=-target 1.6 -encoding UTF-8 -Xlint:deprecation -Xlint:unchecked -source 6
+bin_PROGRAMS = altos-map
-altoslibdir=$(libdir)/altos
-
-CLASSPATH_ENV=mkdir -p $(JAVAROOT); CLASSPATH="$(JAVAROOT):../../altoslib/*"
-
-bin_SCRIPTS=altos-map
-
-altosmap_JAVA = \
- AltosMap.java
-
-ALTOSLIB_CLASS=\
- altoslib_$(ALTOSLIB_VERSION).jar
-
-JAR=altosmap.jar
-
-FATJAR=altosmap-fat.jar
-
-all-local: classes/altosmap $(JAR) altos-map altos-map-test altos-map-jdb
-
-install-altosmapJAVA: altosmap.jar
- @$(NORMAL_INSTALL)
- test -z "$(altosmapdir)" || $(MKDIR_P) "$(DESTDIR)$(altosmapdir)"
- echo " $(INSTALL_DATA)" "$<" "'$(DESTDIR)$(altosmapdir)/altosmap.jar'"; \
- $(INSTALL_DATA) "$<" "$(DESTDIR)$(altosmapdir)"
-
-classes/altosmap:
- mkdir -p classes/altosmap
-
-$(JAR): classaltosmap.stamp Manifest.txt $(ALTOSLIB_CLASS)
- jar cfm $@ Manifest.txt \
- -C classes altosmap
-
-altosmapdir=$(datadir)/java
-
-$(FATJAR): classaltosmap.stamp Manifest-fat.txt $(ALTOSLIB_CLASS)
- jar cfm $@ Manifest-fat.txt \
- -C classes altosmap
-
-altos-map: Makefile
- echo "#!/bin/sh" > $@
- echo 'exec java -Djava.library.path="$(altoslibdir)" -jar "$(altosmapdir)/altosmap.jar" "$$@"' >> $@
- chmod +x $@
-
-altos-map-test: Makefile
- echo '#!/bin/sh' > $@
- echo 'dir="$$(dirname $$0)"' >> $@
- echo 'cd "$$dir"' >> $@
- echo 'altosmap="$$(pwd -P)"' >> $@
- echo 'exec java -jar "$$altosmap/altosmap.jar" "$$@"' >> $@
- chmod +x $@
-
-altos-map-jdb: Makefile
- echo "#!/bin/sh" > $@
- echo 'exec jdb altosmap/AltosMap "$$@"' >> $@
- chmod +x $@
-
-$(ALTOSLIB_CLASS):
- -rm -f "$@"
- $(LN_S) ../../altoslib/"$@" .
+altos_map_SOURCES = altos-map.c
+altos_map_LDADD = $(JANSSON_LIBS)
+altos_map_CFLAGS = $(JANSSON_CFLAGS) $(WARN_CFLAGS)
diff --git a/map-server/altos-map/altos-map.c b/map-server/altos-map/altos-map.c
new file mode 100644
index 00000000..690ec728
--- /dev/null
+++ b/map-server/altos-map/altos-map.c
@@ -0,0 +1,286 @@
+/*
+ * Copyright © 2018 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#include <jansson.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/ip.h>
+
+#define ALTOS_MAP_PORT 16717
+
+#define MISSING 0x7fffffff
+
+#define ALTOS_MAP_PROTOCOL_VERSION "1.0.0"
+
+static char *
+reason_string(int code)
+{
+ switch (code) {
+ case 200:
+ return "OK";
+ case 400:
+ return "Bad Request";
+ case 403:
+ return "Forbidden";
+ case 404:
+ return "Not Found";
+ case 408:
+ return "Request Timeout";
+ default:
+ return "Failure";
+ }
+}
+
+static void
+write_status(int status)
+{
+ printf("Status: %d %s\n", status, reason_string(status));
+}
+
+static void
+write_type(char * type)
+{
+ printf("Content-Type: %s\n", type);
+}
+
+static void
+fail(int status, char *format, ...)
+{
+ va_list ap;
+
+ write_status(status);
+ write_type("text/html");
+ printf("\n");
+ printf("<html>\n");
+ printf("<head><title>Map Fetch Failure</title></head>\n");
+ printf("<body>\n");
+ va_start(ap, format);
+ vprintf(format, ap);
+ va_end(ap);
+ printf("</body>\n");
+ printf("</html>\n");
+ exit(1);
+}
+
+static char *
+getenv_copy(const char *name)
+{
+ const char *value = getenv(name);
+
+ if (!value)
+ return NULL;
+
+ return strdup(value);
+}
+
+static double
+parse_double(char *string)
+{
+ char *end;
+ double value;
+
+ value = strtod(string, &end);
+ if (*end)
+ fail(400, "Invalid double %s", string);
+ return value;
+}
+
+static int
+parse_int(char *string)
+{
+ char *end;
+ long int value;
+
+ value = strtol(string, &end, 10);
+ if (*end)
+ fail(400, "Invalid int %s", string);
+ if (value < INT_MIN || INT_MAX < value)
+ fail(400, "Int value out of range %ld", value);
+ return (int) value;
+}
+
+
+static int
+connect_service(void)
+{
+ struct sockaddr_in altos_map_addr = {
+ .sin_family = AF_INET,
+ .sin_port = htons(ALTOS_MAP_PORT),
+ .sin_addr = {
+ .s_addr = htonl(INADDR_LOOPBACK),
+ },
+ };
+
+ int s = socket(AF_INET, SOCK_STREAM, 0);
+
+ if (s < 0)
+ return -1;
+
+ if (connect (s, (const struct sockaddr *) &altos_map_addr, sizeof (altos_map_addr)) < 0) {
+ close (s);
+ return -1;
+ }
+
+ return s;
+}
+
+int main(int argc, char **argv)
+{
+
+ char *query_string = getenv_copy("QUERY_STRING");
+
+ if (query_string == NULL)
+ fail(400, "%s", "Missing query string");
+
+ char *remote_addr = getenv_copy("REMOTE_ADDR");
+
+ if (remote_addr == NULL)
+ fail(400, "%s", "Missing remote address");
+
+ double lon = MISSING;
+ double lat = MISSING;
+ int zoom = MISSING;
+ char *version = NULL;
+
+ char *query, *query_save = NULL;
+ char *query_start = query_string;
+
+ while ((query = strtok_r(query_start, "&", &query_save)) != NULL) {
+ query_start = NULL;
+
+ char *token, *token_save = NULL;
+ char *token_start = query;
+
+ char *name = NULL;
+ char *value = NULL;
+
+ while ((token = strtok_r(token_start, "=", &token_save)) != NULL) {
+ token_start = NULL;
+ if (name == NULL)
+ name = token;
+ else if (value == NULL)
+ value = token;
+ else
+ break;
+ }
+
+ if (name && value) {
+ if (!strcmp(name, "lon"))
+ lon = parse_double(value);
+ else if (!strcmp(name, "lat"))
+ lat = parse_double(value);
+ else if (!strcmp(name, "zoom"))
+ zoom = parse_int(value);
+ else if (!strcmp(name, "version"))
+ version = value;
+ else
+ fail(400, "Extra query param \"%s\"", query);
+ }
+ }
+
+ if (version != NULL) {
+ printf("Content-Type: text/plain\n");
+ printf("\n");
+ printf("%s\n", ALTOS_MAP_PROTOCOL_VERSION);
+ return 0;
+ }
+ if (lon == MISSING)
+ fail(400, "Missing longitude");
+ if (lat == MISSING)
+ fail(400, "Missing latitude");
+ if (zoom == MISSING)
+ fail(400, "Missing zoom");
+
+ int s = -1;
+ int tries = 0;
+
+ while (tries < 10 && s < 0) {
+ s = connect_service();
+ if (s < 0) {
+ usleep(100 * 1000);
+ tries++;
+ }
+ }
+
+ if (s < 0)
+ fail(408, "Cannot connect AltOS map daemon");
+
+ json_t *request = json_pack("{s:f s:f s:i s:s}", "lat", lat, "lon", lon, "zoom", zoom, "remote_addr", remote_addr);
+
+ if (request == NULL)
+ fail(400, "Cannot create JSON request");
+
+ if (json_dumpfd(request, s, 0) < 0)
+ fail(400, "Cannot write JSON request");
+
+ json_error_t error;
+ json_t *reply = json_loadfd(s, 0, &error);
+
+ if (!reply)
+ fail(400, "Cannot read JSON reply");
+
+ int status;
+
+ if (json_unpack(reply, "{s:i}", "status", &status) < 0)
+ fail(400, "No status returned");
+
+ if (status != 200)
+ fail(status, "Bad cache status");
+
+ char *filename, *content_type;
+
+ if (json_unpack(reply, "{s:s s:s}", "filename", &filename, "content_type", &content_type) < 0)
+ fail(400, "JSON reply parse failure");
+
+ int fd = open(filename, O_RDONLY);
+
+ if (fd < 0)
+ fail(400, "%s: %s", filename, strerror(errno));
+
+ struct stat statb;
+
+ if (fstat(fd, &statb) < 0)
+ fail(400, "%s: %s", filename, strerror(errno));
+
+ printf("Content-Type: %s\n", content_type);
+ printf("Content-Length: %lu\n", (unsigned long) statb.st_size);
+ printf("\n");
+ fflush(stdout);
+
+ char buf[4096];
+ ssize_t bytes_read;
+
+ while ((bytes_read = read(fd, buf, sizeof (buf))) > 0) {
+ ssize_t total_write = 0;
+ while (total_write < bytes_read) {
+ ssize_t bytes_write = write(1, buf + total_write, bytes_read - total_write);
+ if (bytes_write <= 0)
+ return 1;
+ total_write += bytes_write;
+ }
+ }
+ if (bytes_read < 0)
+ return 1;
+ return 0;
+}
diff --git a/map-server/altos-map/altos-map-fake b/map-server/altos-map/altos-mapc-fake
index a78bbd64..9e22f76f 100755
--- a/map-server/altos-map/altos-map-fake
+++ b/map-server/altos-map/altos-mapc-fake
@@ -2,5 +2,5 @@
# map-N43.799102,W120.586281-hybrid-20.jpg
export QUERY_STRING="lat=43.799102&lon=-120.586281&zoom=20"
export REMOTE_ADDR="127.0.0.1"
-./altos-map-test
+./altos-mapc
diff --git a/map-server/altos-mapj/.gitignore b/map-server/altos-mapj/.gitignore
new file mode 100644
index 00000000..c5d593e6
--- /dev/null
+++ b/map-server/altos-mapj/.gitignore
@@ -0,0 +1,6 @@
+altos-mapj
+altos-mapj-jdb
+altos-mapj-test
+*.jar
+*.stamp
+classes
diff --git a/map-server/altos-map/AltosMap.java b/map-server/altos-mapj/AltosMap.java
index ad0a4f5f..ad0a4f5f 100644
--- a/map-server/altos-map/AltosMap.java
+++ b/map-server/altos-mapj/AltosMap.java
diff --git a/map-server/altos-mapj/Makefile.am b/map-server/altos-mapj/Makefile.am
new file mode 100644
index 00000000..f447adb6
--- /dev/null
+++ b/map-server/altos-mapj/Makefile.am
@@ -0,0 +1,62 @@
+JAVAROOT=classes
+AM_JAVACFLAGS=-target 1.6 -encoding UTF-8 -Xlint:deprecation -Xlint:unchecked -source 6
+
+altoslibdir=$(libdir)/altos
+
+CLASSPATH_ENV=mkdir -p $(JAVAROOT); CLASSPATH="$(JAVAROOT):../../altoslib/*"
+
+bin_SCRIPTS=altos-mapj
+
+altosmap_JAVA = \
+ AltosMap.java
+
+ALTOSLIB_CLASS=\
+ altoslib_$(ALTOSLIB_VERSION).jar
+
+JAR=altosmap.jar
+
+FATJAR=altosmap-fat.jar
+
+all-local: classes/altosmap $(JAR) altos-mapj altos-mapj-test altos-mapj-jdb
+
+install-altosmapJAVA: altosmap.jar
+ @$(NORMAL_INSTALL)
+ test -z "$(altosmapdir)" || $(MKDIR_P) "$(DESTDIR)$(altosmapdir)"
+ echo " $(INSTALL_DATA)" "$<" "'$(DESTDIR)$(altosmapdir)/altosmap.jar'"; \
+ $(INSTALL_DATA) "$<" "$(DESTDIR)$(altosmapdir)"
+
+classes/altosmap:
+ mkdir -p classes/altosmap
+
+$(JAR): classaltosmap.stamp Manifest.txt $(ALTOSLIB_CLASS)
+ jar cfm $@ Manifest.txt \
+ -C classes altosmap
+
+altosmapdir=$(datadir)/java
+
+$(FATJAR): classaltosmap.stamp Manifest-fat.txt $(ALTOSLIB_CLASS)
+ jar cfm $@ Manifest-fat.txt \
+ -C classes altosmap
+
+altos-mapj: Makefile
+ echo "#!/bin/sh" > $@
+ echo 'exec java -Djava.library.path="$(altoslibdir)" -jar "$(altosmapdir)/altosmap.jar" "$$@"' >> $@
+ chmod +x $@
+
+altos-mapj-test: Makefile
+ echo '#!/bin/sh' > $@
+ echo 'dir="$$(dirname $$0)"' >> $@
+ echo 'cd "$$dir"' >> $@
+ echo 'altosmap="$$(pwd -P)"' >> $@
+ echo 'exec java -jar "$$altosmap/altosmap.jar" "$$@"' >> $@
+ chmod +x $@
+
+altos-mapj-jdb: Makefile
+ echo "#!/bin/sh" > $@
+ echo 'exec jdb altosmap/AltosMap "$$@"' >> $@
+ chmod +x $@
+
+$(ALTOSLIB_CLASS):
+ -rm -f "$@"
+ $(LN_S) ../../altoslib/"$@" .
+
diff --git a/map-server/altos-map/Manifest.txt b/map-server/altos-mapj/Manifest.txt
index 1a285b40..1a285b40 100644
--- a/map-server/altos-map/Manifest.txt
+++ b/map-server/altos-mapj/Manifest.txt
diff --git a/map-server/altos-mapj/altos-mapj-fake b/map-server/altos-mapj/altos-mapj-fake
new file mode 100755
index 00000000..f600fec7
--- /dev/null
+++ b/map-server/altos-mapj/altos-mapj-fake
@@ -0,0 +1,6 @@
+#!/bin/sh
+# map-N43.799102,W120.586281-hybrid-20.jpg
+export QUERY_STRING="lat=43.799102&lon=-120.586281&zoom=20"
+export REMOTE_ADDR="127.0.0.1"
+./altos-mapj-test
+