summaryrefslogtreecommitdiffstats
path: root/cpp/sca/modules
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--cpp/sca/modules/Makefile.am2
-rw-r--r--cpp/sca/modules/atom/atom-test.cpp18
-rw-r--r--cpp/sca/modules/atom/atom.hpp50
-rw-r--r--cpp/sca/modules/cache/Makefile.am26
-rw-r--r--cpp/sca/modules/cache/cache-test.cpp59
-rw-r--r--cpp/sca/modules/cache/cache.hpp125
-rwxr-xr-xcpp/sca/modules/cache/memcached-test31
-rw-r--r--cpp/sca/modules/eval/driver.hpp23
-rw-r--r--cpp/sca/modules/eval/environment.hpp8
-rw-r--r--cpp/sca/modules/eval/eval-test.cpp30
-rw-r--r--cpp/sca/modules/eval/eval.hpp2
-rw-r--r--cpp/sca/modules/eval/io.hpp (renamed from cpp/sca/modules/eval/read.hpp)18
-rw-r--r--cpp/sca/modules/eval/primitive.hpp68
-rw-r--r--cpp/sca/modules/http/conf/mime.types (renamed from cpp/sca/modules/http/test-conf/mime.types)0
-rw-r--r--cpp/sca/modules/http/curl-test.cpp164
-rw-r--r--cpp/sca/modules/http/curl.hpp234
-rw-r--r--cpp/sca/modules/http/htdocs/entry.xml2
-rw-r--r--cpp/sca/modules/http/htdocs/feed.xml2
-rw-r--r--cpp/sca/modules/http/htdocs/index.html (renamed from cpp/sca/modules/http/test-htdocs/index.html)0
-rw-r--r--cpp/sca/modules/http/htdocs/json-request.txt1
-rw-r--r--cpp/sca/modules/http/htdocs/json-result.txt1
-rwxr-xr-xcpp/sca/modules/http/http-test22
-rwxr-xr-xcpp/sca/modules/http/httpd-conf36
-rwxr-xr-xcpp/sca/modules/http/httpd-test57
-rw-r--r--cpp/sca/modules/http/httpd-test.scm33
-rw-r--r--cpp/sca/modules/http/mod.cpp223
-rw-r--r--cpp/sca/modules/json/json-test.cpp22
-rw-r--r--cpp/sca/modules/json/json.hpp94
28 files changed, 1027 insertions, 324 deletions
diff --git a/cpp/sca/modules/Makefile.am b/cpp/sca/modules/Makefile.am
index fef28b5120..dd085903f0 100644
--- a/cpp/sca/modules/Makefile.am
+++ b/cpp/sca/modules/Makefile.am
@@ -15,5 +15,5 @@
# specific language governing permissions and limitations
# under the License.
-SUBDIRS = atom eval http json
+SUBDIRS = atom cache eval http json
diff --git a/cpp/sca/modules/atom/atom-test.cpp b/cpp/sca/modules/atom/atom-test.cpp
index 618f534001..7c14b954a0 100644
--- a/cpp/sca/modules/atom/atom-test.cpp
+++ b/cpp/sca/modules/atom/atom-test.cpp
@@ -33,7 +33,7 @@
namespace tuscany {
namespace atom {
-std::ostringstream* writer(std::ostringstream* os, const std::string& s) {
+std::ostringstream* writer(const std::string& s, std::ostringstream* os) {
(*os) << s;
return os;
}
@@ -78,19 +78,19 @@ bool testEntry() {
<< (list<value>() << element << "price" << std::string("$2.99"));
const list<value> a = mklist<value>(std::string("item"), std::string("cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b"), i);
std::ostringstream os;
- writeEntry<std::ostringstream*>(writer, &os, a);
+ writeATOMEntry<std::ostringstream*>(writer, &os, a);
assert(os.str() == itemEntry);
}
{
const list<value> a = readEntry(mklist(itemEntry));
std::ostringstream os;
- writeEntry<std::ostringstream*>(writer, &os, a);
+ writeATOMEntry<std::ostringstream*>(writer, &os, a);
assert(os.str() == itemEntry);
}
{
const list<value> a = readEntry(mklist(incompleteEntry));
std::ostringstream os;
- writeEntry<std::ostringstream*>(writer, &os, a);
+ writeATOMEntry<std::ostringstream*>(writer, &os, a);
assert(os.str() == completedEntry);
}
return true;
@@ -131,13 +131,13 @@ std::string itemFeed("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
bool testFeed() {
{
std::ostringstream os;
- writeFeed<std::ostringstream*>(writer, &os, mklist<value>("Feed", "1234"));
+ writeATOMFeed<std::ostringstream*>(writer, &os, mklist<value>("Feed", "1234"));
assert(os.str() == emptyFeed);
}
{
const list<value> a = readFeed(mklist(emptyFeed));
std::ostringstream os;
- writeFeed<std::ostringstream*>(writer, &os, a);
+ writeATOMFeed<std::ostringstream*>(writer, &os, a);
assert(os.str() == emptyFeed);
}
{
@@ -152,7 +152,7 @@ bool testFeed() {
<< (list<value>() << element << "price" << "$3.55")));
const list<value> a = cons<value>("Feed", cons<value>("1234", i));
std::ostringstream os;
- writeFeed<std::ostringstream*>(writer, &os, a);
+ writeATOMFeed<std::ostringstream*>(writer, &os, a);
assert(os.str() == itemFeed);
}
{
@@ -167,13 +167,13 @@ bool testFeed() {
<< (list<value>() << "price" << "$3.55")));
const list<value> a = cons<value>("Feed", cons<value>("1234", i));
std::ostringstream os;
- writeFeed<std::ostringstream*>(writer, &os, a);
+ writeATOMFeed<std::ostringstream*>(writer, &os, a);
assert(os.str() == itemFeed);
}
{
const list<value> a = readFeed(mklist(itemFeed));
std::ostringstream os;
- writeFeed<std::ostringstream*>(writer, &os, a);
+ writeATOMFeed<std::ostringstream*>(writer, &os, a);
assert(os.str() == itemFeed);
}
return true;
diff --git a/cpp/sca/modules/atom/atom.hpp b/cpp/sca/modules/atom/atom.hpp
index 78b60da70f..2077081320 100644
--- a/cpp/sca/modules/atom/atom.hpp
+++ b/cpp/sca/modules/atom/atom.hpp
@@ -38,7 +38,7 @@ namespace atom {
/**
* Convert a list of elements to a list of values representing an ATOM entry.
*/
-const list<value> entry(const list<value>& e) {
+const list<value> entryValue(const list<value>& e) {
const list<value> lt = filter<value>(selector(mklist<value>(element, "title")), e);
const value t = isNil(lt)? value(std::string("")) : elementValue(car(lt));
const list<value> li = filter<value>(selector(mklist<value>(element, "id")), e);
@@ -50,10 +50,10 @@ const list<value> entry(const list<value>& e) {
/**
* Convert a list of elements to a list of values representing ATOM entries.
*/
-const list<value> entries(const list<value>& e) {
+const list<value> entriesValues(const list<value>& e) {
if (isNil(e))
return list<value>();
- return cons<value>(entry(car(e)), entries(cdr(e)));
+ return cons<value>(entryValue(car(e)), entriesValues(cdr(e)));
}
/**
@@ -63,7 +63,7 @@ const failable<list<value>, std::string> readEntry(const list<std::string>& ilis
const list<value> e = readXML(ilist);
if (isNil(e))
return std::string("Empty entry");
- return entry(car(e));
+ return entryValue(car(e));
}
/**
@@ -78,7 +78,7 @@ const failable<list<value>, std::string> readFeed(const list<std::string>& ilist
const list<value> e = filter<value>(selector(mklist<value>(element, "entry")), car(f));
if (isNil(e))
return mklist<value>(elementValue(car(t)), elementValue(car(i)));
- return cons<value>(elementValue(car(t)), cons(elementValue(car(i)), entries(e)));
+ return cons<value>(elementValue(car(t)), cons(elementValue(car(i)), entriesValues(e)));
}
/**
@@ -107,20 +107,12 @@ const list<value> entriesElements(const list<value>& l) {
* Convert a list of values representing an ATOM entry to an ATOM entry.
* The first two values in the list are the entry id and title.
*/
-template<typename R> const failable<R, std::string> writeEntry(const lambda<R(R, std::string)>& reduce, const R& initial, const list<value>& l) {
+template<typename R> const failable<R, std::string> writeATOMEntry(const lambda<R(std::string, R)>& reduce, const R& initial, const list<value>& l) {
return writeXML<R>(reduce, initial, mklist<value>(entryElement(l)));
}
-/**
- * Convert a list of values representing an ATOM entry to a list of strings.
- * The first two values in the list are the entry id and title.
- */
-const list<std::string> writeStrings(const list<std::string>& listSoFar, const std::string& s) {
- return cons(s, listSoFar);
-}
-
-const failable<list<std::string>, std::string> writeEntry(const list<value>& l) {
- const failable<list<std::string>, std::string> ls = writeEntry<list<std::string> >(writeStrings, list<std::string>(), l);
+const failable<list<std::string>, std::string> writeATOMEntry(const list<value>& l) {
+ const failable<list<std::string>, std::string> ls = writeATOMEntry<list<std::string> >(rcons<std::string>, list<std::string>(), l);
if (!hasValue(ls))
return ls;
return reverse(list<std::string>(ls));
@@ -130,7 +122,7 @@ const failable<list<std::string>, std::string> writeEntry(const list<value>& l)
* Convert a list of values representing an ATOM feed to an ATOM feed.
* The first two values in the list are the feed id and title.
*/
-template<typename R> const failable<R, std::string> writeFeed(const lambda<R(R, std::string)>& reduce, const R& initial, const list<value>& l) {
+template<typename R> const failable<R, std::string> writeATOMFeed(const lambda<R(std::string, R)>& reduce, const R& initial, const list<value>& l) {
const list<value> f = list<value>()
<< element << "feed" << (list<value>() << attribute << "xmlns" << "http://www.w3.org/2005/Atom")
<< (list<value>() << element << "title" << (list<value>() << attribute << "type" << "text") << car(l))
@@ -145,13 +137,33 @@ template<typename R> const failable<R, std::string> writeFeed(const lambda<R(R,
* Convert a list of values representing an ATOM feed to a list of strings.
* The first two values in the list are the feed id and title.
*/
-const failable<list<std::string>, std::string> writeFeed(const list<value>& l) {
- const failable<list<std::string>, std::string> ls = writeFeed<list<std::string> >(writeStrings, list<std::string>(), l);
+const failable<list<std::string>, std::string> writeATOMFeed(const list<value>& l) {
+ const failable<list<std::string>, std::string> ls = writeATOMFeed<list<std::string> >(rcons<std::string>, list<std::string>(), l);
if (!hasValue(ls))
return ls;
return reverse(list<std::string>(ls));
}
+/**
+ * Convert an ATOM entry containing a value to an ATOM entry containing an item element.
+ */
+const list<value> entryValuesToElements(const list<value> val) {
+ return cons(car(val), cons(cadr(val), valuesToElements(mklist<value>(cons<value>("item", (list<value>)caddr(val))))));
+}
+
+/**
+ * Convert an ATOM feed containing values to an ATOM feed containing elements.
+ */
+const list<value> feedValuesToElementsLoop(const list<value> val) {
+ if (isNil(val))
+ return list<value>();
+ return cons<value>(entryValuesToElements(car(val)), feedValuesToElementsLoop(cdr(val)));
+}
+
+const list<value> feedValuesToElements(const list<value>& val) {
+ return cons(car<value>(val), cons<value>(cadr<value>(val), feedValuesToElementsLoop(cddr<value>(val))));
+}
+
}
}
diff --git a/cpp/sca/modules/cache/Makefile.am b/cpp/sca/modules/cache/Makefile.am
new file mode 100644
index 0000000000..581b8b6682
--- /dev/null
+++ b/cpp/sca/modules/cache/Makefile.am
@@ -0,0 +1,26 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+noinst_PROGRAMS = cache-test
+
+INCLUDES = -I. -I$(top_builddir)/kernel -I${LIBXML2_INCLUDE} -I${APR_INCLUDE}
+
+cache_test_SOURCES = cache-test.cpp
+cache_test_LDADD = -lpthread -L${LIBXML2_LIB} -lxml2 -L${APR_LIB} -lapr-1 -laprutil-1
+
+TESTS = memcached-test
+
diff --git a/cpp/sca/modules/cache/cache-test.cpp b/cpp/sca/modules/cache/cache-test.cpp
new file mode 100644
index 0000000000..776ac72363
--- /dev/null
+++ b/cpp/sca/modules/cache/cache-test.cpp
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* $Rev$ $Date$ */
+
+/**
+ * Test Memcached access functions.
+ */
+
+#include <assert.h>
+#include <iostream>
+#include <string>
+#include "cache.hpp"
+
+namespace tuscany {
+namespace cache {
+
+bool testCache() {
+ Cache cache;
+ addServer("localhost", 11311, cache);
+
+ assert(hasValue(post("a", "AAA", cache)));
+ assert(get("a", cache) == value(std::string("AAA")));
+ assert(hasValue(put("a", "aaa", cache)));
+ assert(get("a", cache) == value(std::string("aaa")));
+ assert(hasValue(del("a", cache)));
+ assert(!hasValue(get("a", cache)));
+
+ return true;
+}
+
+}
+}
+
+int main() {
+ std::cout << "Testing..." << std::endl;
+
+ tuscany::cache::testCache();
+
+ std::cout << "OK" << std::endl;
+
+ return 0;
+}
diff --git a/cpp/sca/modules/cache/cache.hpp b/cpp/sca/modules/cache/cache.hpp
new file mode 100644
index 0000000000..d4a24ca3d9
--- /dev/null
+++ b/cpp/sca/modules/cache/cache.hpp
@@ -0,0 +1,125 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* $Rev$ $Date$ */
+
+#ifndef tuscany_cache_hpp
+#define tuscany_cache_hpp
+
+/**
+ * Memcached access functions.
+ */
+
+#include "apr.h"
+#include "apu.h"
+#include "apr_general.h"
+#include "apr_strings.h"
+#include "apr_hash.h"
+#include "apr_memcache.h"
+#include "apr_network_io.h"
+
+#include <string>
+#include "list.hpp"
+#include "value.hpp"
+#include "monad.hpp"
+
+namespace tuscany {
+namespace cache {
+
+class Cache {
+public:
+ Cache() {
+ apr_pool_create(&pool, NULL);
+ apr_memcache_create(pool, 1, 0, &mc);
+ }
+ ~Cache() {
+ apr_pool_destroy(pool);
+ }
+
+ operator apr_memcache_t*() const {
+ return mc;
+ }
+
+ operator apr_pool_t*() const {
+ return pool;
+ }
+
+private:
+ apr_pool_t* pool;
+ apr_memcache_t* mc;
+
+};
+
+const failable<bool, std::string> addServer(const std::string& host, const int port, Cache& cache) {
+ apr_memcache_server_t *server;
+ const apr_status_t sc = apr_memcache_server_create(cache, host.c_str(), port, 0, 1, 1, 60, &server);
+ if (sc != APR_SUCCESS)
+ return "Could not create server";
+ const apr_status_t as = apr_memcache_add_server(cache, server);
+ if (as != APR_SUCCESS)
+ return "Could not add server";
+ return true;
+}
+
+const failable<bool, std::string> post(const value& key, const value& val, Cache& cache) {
+ const std::string v(val);
+ const apr_status_t rc = apr_memcache_add(cache, std::string(key).c_str(), const_cast<char*>(v.c_str()), v.size(), 0, 27);
+ if (rc != APR_SUCCESS)
+ return "Could not add entry";
+ return true;
+}
+
+const failable<bool, std::string> put(const value& key, const value& val, Cache& cache) {
+ const std::string v(val);
+ const apr_status_t rc = apr_memcache_replace(cache, std::string(key).c_str(), const_cast<char*>(v.c_str()), v.size(), 0, 27);
+ if (rc != APR_SUCCESS)
+ return "Could not add entry";
+ return true;
+}
+
+const failable<value, std::string> get(const value& key, Cache& cache) {
+ apr_pool_t* vpool;
+ const apr_status_t pc = apr_pool_create(&vpool, cache);
+ if (pc != APR_SUCCESS)
+ return std::string("Could not allocate memory");
+
+ char *data;
+ apr_size_t size;
+ const apr_status_t rc = apr_memcache_getp(cache, cache, std::string(key).c_str(), &data, &size, NULL);
+ if (rc != APR_SUCCESS) {
+ apr_pool_destroy(vpool);
+ return std::string("Could not get entry");
+ }
+
+ const value val(std::string(data, size));
+ apr_pool_destroy(vpool);
+ return val;
+}
+
+const failable<bool, std::string> del(const value& key, Cache& cache) {
+ const apr_status_t rc = apr_memcache_delete(cache, std::string(key).c_str(), 0);
+ if (rc != APR_SUCCESS)
+ return "Could not add entry";
+ return true;
+}
+
+}
+}
+
+#endif /* tuscany_cache_hpp */
diff --git a/cpp/sca/modules/cache/memcached-test b/cpp/sca/modules/cache/memcached-test
new file mode 100755
index 0000000000..2f9870a49b
--- /dev/null
+++ b/cpp/sca/modules/cache/memcached-test
@@ -0,0 +1,31 @@
+#!/bin/sh
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# Setup
+cmd="memcached -l 127.0.0.1 -m 4 -p 11311"
+$cmd &
+sleep 1
+
+# Test
+./cache-test
+rc=$?
+
+# Cleanup
+ps -f | grep -v grep | grep "$cmd" | awk '{ print $2 }' | xargs kill
+return $rc
diff --git a/cpp/sca/modules/eval/driver.hpp b/cpp/sca/modules/eval/driver.hpp
index f777973ebf..4c69ecb0a1 100644
--- a/cpp/sca/modules/eval/driver.hpp
+++ b/cpp/sca/modules/eval/driver.hpp
@@ -36,40 +36,39 @@ namespace eval {
const std::string evalOutputPrompt("; ");
const std::string evalInputPrompt("=> ");
-const bool promptForInput(std::ostream& out, const std::string str) {
+const bool promptForInput(const std::string str, std::ostream& out) {
out << "\n\n" << str;
return true;
}
-const bool announceOutput(std::ostream& out, const std::string str) {
+const bool announceOutput(const std::string str, std::ostream& out) {
out << "\n" << str;
return true;
}
-const bool userPrint(std::ostream& out, const value object) {
- if(isCompoundProcedure(object))
- out << mklist<value>(compoundProcedureSymbol, procedureParameters(object), procedureBody(object), "<procedure-env>");
- out << object;
+const bool userPrint(const value val, std::ostream& out) {
+ if(isCompoundProcedure(val))
+ writeValue(mklist<value>(compoundProcedureSymbol, procedureParameters(val), procedureBody(val), "<procedure-env>"), out);
+ writeValue(val, out);
return true;
}
const value evalDriverLoop(std::istream& in, std::ostream& out, Env& globalEnv, const gc_pool& pool) {
- promptForInput(out, evalInputPrompt);
- value input = read(in);
+ promptForInput(evalInputPrompt, out);
+ value input = readValue(in);
if (isNil(input))
return input;
const value output = evalExpr(input, globalEnv, pool);
- announceOutput(out, evalOutputPrompt);
- userPrint(out, output);
+ announceOutput(evalOutputPrompt, out);
+ userPrint(output, out);
return evalDriverLoop(in, out, globalEnv, pool);
}
const bool evalDriverRun(std::istream& in, std::ostream& out) {
gc_pool pool;
- setupEvalOut(out);
+ setupDisplay(out);
Env globalEnv = setupEnvironment(pool);
evalDriverLoop(in, out, globalEnv, pool);
- cleanupEnvironment(globalEnv);
return true;
}
diff --git a/cpp/sca/modules/eval/environment.hpp b/cpp/sca/modules/eval/environment.hpp
index 90a1d88854..fa9667b1ba 100644
--- a/cpp/sca/modules/eval/environment.hpp
+++ b/cpp/sca/modules/eval/environment.hpp
@@ -151,14 +151,6 @@ const Env setupEnvironment(const gc_pool& pool) {
return env;
}
-const bool cleanupEnvironment(Env& env) {
- if (isNil(env))
- return true;
- *firstFrame(env) = list<value>();
- Env enclosing = enclosingEnvironment(env);
- return cleanupEnvironment(enclosing);
-}
-
const value lookupEnvLoop(const value& var, const Env& env);
const value lookupEnvScan(const value& var, const list<value>& vars, const list<value>& vals, const Env& env) {
diff --git a/cpp/sca/modules/eval/eval-test.cpp b/cpp/sca/modules/eval/eval-test.cpp
index 584c8470b1..7d92c79eba 100644
--- a/cpp/sca/modules/eval/eval-test.cpp
+++ b/cpp/sca/modules/eval/eval-test.cpp
@@ -39,7 +39,6 @@ bool testEnv() {
defineVariable("x", env, env);
assert(lookupVariableValue(value("x"), env) == env);
assert(lookupVariableValue("a", env) == value(1));
- cleanupEnvironment(env);
return true;
}
@@ -59,23 +58,41 @@ bool testEnvGC() {
bool testRead() {
std::istringstream is("abcd");
- assert(read(is) == "abcd");
+ assert(readValue(is) == "abcd");
std::istringstream is2("123");
- assert(read(is2) == value(123));
+ assert(readValue(is2) == value(123));
std::istringstream is3("(abcd)");
- assert(read(is3) == mklist(value("abcd")));
+ assert(readValue(is3) == mklist(value("abcd")));
std::istringstream is4("(abcd xyz)");
- assert(read(is4) == mklist<value>("abcd", "xyz"));
+ assert(readValue(is4) == mklist<value>("abcd", "xyz"));
std::istringstream is5("(abcd (xyz tuv))");
- assert(read(is5) == mklist<value>("abcd", mklist<value>("xyz", "tuv")));
+ assert(readValue(is5) == mklist<value>("abcd", mklist<value>("xyz", "tuv")));
return true;
}
+bool testWrite() {
+ const list<value> i = list<value>()
+ << (list<value>() << "item" << "cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b"
+ << (list<value>() << "item"
+ << (list<value>() << "name" << "Apple")
+ << (list<value>() << "price" << "$2.99")))
+ << (list<value>() << "item" << "cart-53d67a61-aa5e-4e5e-8401-39edeba8b83c"
+ << (list<value>() << "item"
+ << (list<value>() << "name" << "Orange")
+ << (list<value>() << "price" << "$3.55")));
+ const list<value> a = cons<value>("Feed", cons<value>("feed-1234", i));
+ std::ostringstream os;
+ writeValue(a, os);
+ std::istringstream is(os.str());
+ assert(readValue(is) == a);
+ return true;
+}
+
const std::string testSchemeNumber(
"(define (testNumber) (if (= 1 1) (display \"testNumber ok\") (error \"testNumber\"))) "
"(testNumber)");
@@ -178,6 +195,7 @@ int main() {
tuscany::eval::testEnv();
tuscany::eval::testEnvGC();
tuscany::eval::testRead();
+ tuscany::eval::testWrite();
tuscany::eval::testEval();
tuscany::eval::testEvalExpr();
tuscany::eval::testEvalGC();
diff --git a/cpp/sca/modules/eval/eval.hpp b/cpp/sca/modules/eval/eval.hpp
index 78051c5a2b..01d242f07b 100644
--- a/cpp/sca/modules/eval/eval.hpp
+++ b/cpp/sca/modules/eval/eval.hpp
@@ -30,7 +30,7 @@
#include "list.hpp"
#include "value.hpp"
#include "primitive.hpp"
-#include "read.hpp"
+#include "io.hpp"
#include "environment.hpp"
namespace tuscany {
diff --git a/cpp/sca/modules/eval/read.hpp b/cpp/sca/modules/eval/io.hpp
index 994462f145..2a55e67bbf 100644
--- a/cpp/sca/modules/eval/read.hpp
+++ b/cpp/sca/modules/eval/io.hpp
@@ -19,11 +19,11 @@
/* $Rev$ $Date$ */
-#ifndef tuscany_eval_read_hpp
-#define tuscany_eval_read_hpp
+#ifndef tuscany_eval_io_hpp
+#define tuscany_eval_io_hpp
/**
- * Script evaluator read functions.
+ * Script evaluator IO functions.
*/
#include <iostream>
@@ -96,7 +96,7 @@ const value readQuoted(std::istream& in);
const value readIdentifier(const char chr, std::istream& in);
const value readString(const char chr, std::istream& in);
const value readNumber(const char chr, std::istream& in);
-const value read(std::istream& in);
+const value readValue(std::istream& in);
const value readToken(std::istream& in) {
const char firstChar = readChar(in);
@@ -121,7 +121,7 @@ const value readToken(std::istream& in) {
}
const value readQuoted(std::istream& in) {
- return mklist(quoteSymbol, read(in));
+ return mklist(quoteSymbol, readValue(in));
}
const list<value> readList(const list<value>& listSoFar, std::istream& in) {
@@ -172,13 +172,17 @@ const value readNumber(const char chr, std::istream& in) {
return stringToNumber(listToString(readNumberHelper(mklist(chr), in)));
}
-const value read(std::istream& in) {
+const value readValue(std::istream& in) {
const value nextToken = readToken(in);
if(isLeftParenthesis(nextToken))
return readList(list<value> (), in);
return nextToken;
}
+const bool writeValue(const value& val, std::ostream& out) {
+ out << val;
+}
+
}
}
-#endif /* tuscany_eval_read_hpp */
+#endif /* tuscany_eval_io_hpp */
diff --git a/cpp/sca/modules/eval/primitive.hpp b/cpp/sca/modules/eval/primitive.hpp
index 423e5af07b..9d62d6b1a6 100644
--- a/cpp/sca/modules/eval/primitive.hpp
+++ b/cpp/sca/modules/eval/primitive.hpp
@@ -40,73 +40,67 @@ const value primitiveSymbol("primitive");
const value quoteSymbol("'");
const value lambdaSymbol("lambda");
-std::ostream* evalOut = &std::cout;
+std::ostream* displayOut = &std::cout;
-const bool setupEvalOut(std::ostream& out) {
- evalOut = &out;
+const bool setupDisplay(std::ostream& out) {
+ displayOut = &out;
return true;
}
-const value valueCar(list<value>& args) {
+const value carProc(list<value>& args) {
return car((list<value> )car(args));
}
-const value valueCdr(list<value>& args) {
+const value cdrProc(list<value>& args) {
return cdr((list<value> )car(args));
}
-const value valueCons(list<value>& args) {
+const value consProc(list<value>& args) {
return cons(car(args), (list<value> )cadr(args));
}
-const value valueList(list<value>& args) {
+const value listProc(list<value>& args) {
return args;
}
-const value valueNul(list<value>& args) {
+const value nulProc(list<value>& args) {
return (bool)isNil(car(args));
}
-const value valueEqual(list<value>& args) {
+const value equalProc(list<value>& args) {
return (bool)(car(args) == cadr(args));
}
-const value valueAdd(list<value>& args) {
+const value addProc(list<value>& args) {
if (isNil(cdr(args)))
return (double)car(args);
return (double)car(args) + (double)cadr(args);
}
-const value valueSub(list<value>& args) {
+const value subProc(list<value>& args) {
if (isNil(cdr(args)))
return (double)0 - (double)car(args);
return (double)car(args) - (double)cadr(args);
}
-const value valueMul(list<value>& args) {
+const value mulProc(list<value>& args) {
return (double)car(args) * (double)cadr(args);
}
-const value valueDiv(list<value>& args) {
+const value divProc(list<value>& args) {
return (double)car(args) / (double)cadr(args);
}
-const value valueDisplay(list<value>& args) {
- *evalOut << car(args);
+const value displayProc(list<value>& args) {
+ *displayOut << car(args);
return true;
}
-const value valueComment(list<value>& args) {
- *evalOut << "; " << car(args);
+const value commentProc(list<value>& args) {
return true;
}
-const value valueError(list<value>& args) {
- std::cerr << (std::string)car(args);
- return true;
-}
-
-const value valueUuid(list<value>& args) {
+const value uuidProc(list<value>& args) {
apr_uuid_t uuid;
apr_uuid_get(&uuid);
char buf[APR_UUID_FORMATTED_LENGTH];
@@ -164,20 +158,20 @@ const list<value> primitiveProcedureNames() {
}
const list<value> primitiveProcedureObjects() {
- list<value> l = mklist(primitiveProcedure(valueCar));
- l = cons(primitiveProcedure(valueCdr), l);
- l = cons(primitiveProcedure(valueCons), l);
- l = cons(primitiveProcedure(valueList), l);
- l = cons(primitiveProcedure(valueNul), l);
- l = cons(primitiveProcedure(valueEqual), l);
- l = cons(primitiveProcedure(valueAdd), l);
- l = cons(primitiveProcedure(valueSub), l);
- l = cons(primitiveProcedure(valueMul), l);
- l = cons(primitiveProcedure(valueDiv), l);
- l = cons(primitiveProcedure(valueEqual), l);
- l = cons(primitiveProcedure(valueDisplay), l);
- l = cons(primitiveProcedure(valueUuid), l);
- l = cons(primitiveProcedure(valueComment), l);
+ list<value> l = mklist(primitiveProcedure(carProc));
+ l = cons(primitiveProcedure(cdrProc), l);
+ l = cons(primitiveProcedure(consProc), l);
+ l = cons(primitiveProcedure(listProc), l);
+ l = cons(primitiveProcedure(nulProc), l);
+ l = cons(primitiveProcedure(equalProc), l);
+ l = cons(primitiveProcedure(addProc), l);
+ l = cons(primitiveProcedure(subProc), l);
+ l = cons(primitiveProcedure(mulProc), l);
+ l = cons(primitiveProcedure(divProc), l);
+ l = cons(primitiveProcedure(equalProc), l);
+ l = cons(primitiveProcedure(displayProc), l);
+ l = cons(primitiveProcedure(uuidProc), l);
+ l = cons(primitiveProcedure(commentProc), l);
return l;
}
diff --git a/cpp/sca/modules/http/test-conf/mime.types b/cpp/sca/modules/http/conf/mime.types
index 4279f51bca..4279f51bca 100644
--- a/cpp/sca/modules/http/test-conf/mime.types
+++ b/cpp/sca/modules/http/conf/mime.types
diff --git a/cpp/sca/modules/http/curl-test.cpp b/cpp/sca/modules/http/curl-test.cpp
index 4e23a036da..b02a2fac4d 100644
--- a/cpp/sca/modules/http/curl-test.cpp
+++ b/cpp/sca/modules/http/curl-test.cpp
@@ -24,6 +24,8 @@
*/
#include <assert.h>
+#include <sys/time.h>
+#include <time.h>
#include <iostream>
#include <sstream>
#include <string>
@@ -37,25 +39,164 @@ bool contains(const std::string& str, const std::string& pattern) {
return str.find(pattern) != str.npos;
}
-std::ostringstream* curlWriter(std::ostringstream* os, const std::string& s) {
+std::ostringstream* curlWriter(const std::string& s, std::ostringstream* os) {
(*os) << s;
return os;
}
-const bool testCURL() {
+const bool testEval() {
+ CURLHandle ch;
+ const value val = evalExpr(mklist<value>(std::string("echo"), std::string("Hello")), "http://localhost:8091/test", ch);
+ assert(val == std::string("Hello"));
+ return true;
+}
+
+const bool testEvalLoop(const int count, CURLHandle& ch) {
+ if (count == 0)
+ return true;
+ const value val = evalExpr(mklist<value>(std::string("echo"), std::string("Hello")), "http://localhost:8091/test", ch);
+ assert(val == std::string("Hello"));
+ return testEvalLoop(count - 1, ch);
+}
+
+const value blob(std::string(3000, 'A'));
+const list<value> blobs = mklist(blob, blob, blob, blob, blob);
+
+const bool testBlobEvalLoop(const int count, CURLHandle& ch) {
+ if (count == 0)
+ return true;
+ const value val = evalExpr(mklist<value>(std::string("echo"), blobs), "http://localhost:8091/test", ch);
+ assert(val == blobs);
+ return testBlobEvalLoop(count - 1, ch);
+}
+
+const bool testEvalPerf() {
+ const int count = 50;
+ CURLHandle ch;
+ struct timeval start;
+ struct timeval end;
+ {
+ testEvalLoop(5, ch);
+
+ gettimeofday(&start, NULL);
+
+ testEvalLoop(count, ch);
+
+ gettimeofday(&end, NULL);
+ long t = (end.tv_sec * 1000 + end.tv_usec / 1000) - (start.tv_sec * 1000 + start.tv_usec / 1000);
+ std::cout << "JSON-RPC eval echo test " << (t / count) << " ms" << std::endl;
+ }
+ {
+ testBlobEvalLoop(5, ch);
+
+ gettimeofday(&start, NULL);
+
+ testBlobEvalLoop(count, ch);
+
+ gettimeofday(&end, NULL);
+ long t = (end.tv_sec * 1000 + end.tv_usec / 1000) - (start.tv_sec * 1000 + start.tv_usec / 1000);
+ std::cout << "JSON-RPC eval blob test " << (t / count) << " ms" << std::endl;
+ }
+}
+
+const bool testGet() {
+ CURLHandle ch;
{
std::ostringstream os;
- const failable<std::ostringstream*, int> r = get<std::ostringstream*>(curlWriter, &os, "http://localhost:9091");
+ const failable<list<std::ostringstream*>, std::string> r = get<std::ostringstream*>(curlWriter, &os, "http://localhost:8091", ch);
assert(hasValue(r));
+ assert(contains(os.str(), "HTTP/1.1 200 OK"));
assert(contains(os.str(), "It works"));
}
{
- std::ostringstream os;
- const failable<list<std::string>, int> r = get("http://localhost:9091");
+ const failable<value, std::string> r = get("http://localhost:8091", ch);
assert(hasValue(r));
- write(r, os);
- assert(contains(os.str(), "It works"));
+ const value val = r;
+ assert(contains(val, "It works"));
+ }
+ return true;
+}
+
+const bool testFeed() {
+ return true;
+}
+
+bool testPost() {
+ const list<value> i = list<value>()
+ << (list<value>() << "name" << std::string("Apple"))
+ << (list<value>() << "price" << std::string("$2.99"));
+ const list<value> a = mklist<value>(std::string("item"), std::string("cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b"), i);
+ CURLHandle ch;
+ value rc = post(a, "http://localhost:8091/test", ch);
+ assert(rc == value(true));
+ return true;
+}
+
+const bool testPostLoop(const int count, const value& val, CURLHandle& ch) {
+ if (count == 0)
+ return true;
+ const value rc = post(val, "http://localhost:8091/test", ch);
+ assert(rc == value(true));
+ return testPostLoop(count - 1, val, ch);
+}
+
+const bool testPostPerf() {
+ const int count = 50;
+ CURLHandle ch;
+ struct timeval start;
+ struct timeval end;
+ {
+ const list<value> i = list<value>()
+ << (list<value>() << "name" << std::string("Apple"))
+ << (list<value>() << "price" << std::string("$2.99"));
+ const list<value> val = mklist<value>(std::string("item"), std::string("cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b"), i);
+ testPostLoop(5, val, ch);
+
+ gettimeofday(&start, NULL);
+
+ testPostLoop(count, val, ch);
+
+ gettimeofday(&end, NULL);
+ long t = (end.tv_sec * 1000 + end.tv_usec / 1000) - (start.tv_sec * 1000 + start.tv_usec / 1000);
+ std::cout << "ATOMPub POST small test " << (t / count) << " ms" << std::endl;
+ }
+ {
+ const list<value> i = list<value>()
+ << (list<value>() << "name" << std::string("Apple"))
+ << (list<value>() << "blob1" << blob)
+ << (list<value>() << "blob2" << blob)
+ << (list<value>() << "blob3" << blob)
+ << (list<value>() << "blob4" << blob)
+ << (list<value>() << "blob5" << blob)
+ << (list<value>() << "price" << std::string("$2.99"));
+ const list<value> val = mklist<value>(std::string("item"), std::string("cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b"), i);
+ testPostLoop(5, val, ch);
+
+ gettimeofday(&start, NULL);
+
+ testPostLoop(count, val, ch);
+
+ gettimeofday(&end, NULL);
+ long t = (end.tv_sec * 1000 + end.tv_usec / 1000) - (start.tv_sec * 1000 + start.tv_usec / 1000);
+ std::cout << "ATOMPub POST blob test " << (t / count) << " ms" << std::endl;
}
+}
+
+const bool testPut() {
+ const list<value> i = list<value>()
+ << (list<value>() << "name" << std::string("Apple"))
+ << (list<value>() << "price" << std::string("$2.99"));
+ const list<value> a = mklist<value>(std::string("item"), std::string("cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b"), i);
+ CURLHandle ch;
+ value rc = put(a, "http://localhost:8091/test", ch);
+ assert(rc == value(true));
+ return true;
+}
+
+const bool testDel() {
+ CURLHandle ch;
+ value rc = del("http://localhost:8091/test/123456789", ch);
+ assert(rc == value(true));
return true;
}
@@ -65,7 +206,14 @@ const bool testCURL() {
int main() {
std::cout << "Testing..." << std::endl;
- tuscany::http::testCURL();
+ tuscany::http::testGet();
+ tuscany::http::testPost();
+ //tuscany::http::testPostPerf();
+ tuscany::http::testEval();
+ //tuscany::http::testEvalPerf();
+ tuscany::http::testFeed();
+ tuscany::http::testPut();
+ tuscany::http::testDel();
std::cout << "OK" << std::endl;
diff --git a/cpp/sca/modules/http/curl.hpp b/cpp/sca/modules/http/curl.hpp
index 2ea23010cb..c0d79cef13 100644
--- a/cpp/sca/modules/http/curl.hpp
+++ b/cpp/sca/modules/http/curl.hpp
@@ -34,11 +34,18 @@
#include "value.hpp"
#include "element.hpp"
#include "monad.hpp"
+#include "../atom/atom.hpp"
+#include "../json/json.hpp"
namespace tuscany {
namespace http {
/**
+ * Set to true to log HTTP content.
+ */
+bool logContent = false;
+
+/**
* CURL library context, one per process.
*/
class CURLContext {
@@ -72,13 +79,37 @@ private:
};
/**
+ * Context passed to the read callback function.
+ */
+class CURLReadContext {
+public:
+ CURLReadContext(const list<std::string>& ilist) : ilist(ilist) {
+ }
+ list<std::string> ilist;
+};
+
+/**
+ * Called by CURL to read data to send.
+ */
+size_t readCallback(void *ptr, size_t size, size_t nmemb, void *data) {
+ CURLReadContext& rcx = *static_cast<CURLReadContext*>(data);
+ if (isNil(rcx.ilist))
+ return 0;
+ rcx.ilist = fragment(rcx.ilist, size * nmemb);
+ const std::string s = car(rcx.ilist);
+ rcx.ilist = cdr(rcx.ilist);
+ s.copy((char*)ptr, s.length());
+ return s.length();
+}
+
+/**
* Context passed to CURL write callback function.
*/
template<typename R> class CURLWriteContext {
public:
- CURLWriteContext(const lambda<R(R, std::string)>& reduce, const R& accum) : reduce(reduce), accum(accum) {
+ CURLWriteContext(const lambda<R(std::string, R)>& reduce, const R& accum) : reduce(reduce), accum(accum) {
}
- const lambda<R(R, std::string)> reduce;
+ const lambda<R(std::string, R)> reduce;
R accum;
};
@@ -88,7 +119,7 @@ public:
template<typename R> size_t writeCallback(void *ptr, size_t size, size_t nmemb, void *data) {
CURLWriteContext<R>& wcx = *(static_cast<CURLWriteContext<R>*> (data));
const size_t realsize = size * nmemb;
- wcx.accum = wcx.reduce(wcx.accum, std::string((const char*)ptr, realsize));
+ wcx.accum = wcx.reduce(std::string((const char*)ptr, realsize), wcx.accum);
return realsize;
}
@@ -98,50 +129,199 @@ template<typename R> size_t writeCallback(void *ptr, size_t size, size_t nmemb,
template<typename R> size_t headerCallback(void *ptr, size_t size, size_t nmemb, void *data) {
CURLWriteContext<R>& wcx = *(static_cast<CURLWriteContext<R>*> (data));
const size_t realsize = size * nmemb;
- wcx.accum = wcx.reduce(wcx.accum, std::string((const char*)ptr, realsize));
+ wcx.accum = wcx.reduce(std::string((const char*)ptr, realsize), wcx.accum);
return realsize;
}
/**
- * HTTP GET, get a resource from a URL.
+ * Apply an HTTP verb to a list containing a list of headers and a list of content, and
+ * a reduce function used to process the response.
*/
-template<typename R> const failable<R, int> get(const lambda<R(R, std::string)>& reduce, const R& initial, const std::string& url) {
- CURLWriteContext<R> wcx(reduce, initial);
+curl_slist* headers(curl_slist* cl, const list<std::string> h) {
+ if (isNil(h))
+ return cl;
+ return headers(curl_slist_append(cl, std::string(car(h)).c_str()), cdr(h));
+}
+
+template<typename R> const failable<list<R>, std::string> apply(const list<list<std::string> > req, const lambda<R(std::string, R)>& reduce, const R& initial, const std::string& url, const std::string& verb, CURLHandle& ch) {
// Init the curl session
- CURLHandle ch;
- curl_easy_setopt(ch, CURLOPT_USERAGENT, "libcurl-agent/1.0");
- curl_easy_setopt(ch, CURLOPT_URL, url.c_str());
+ curl_easy_reset(ch);
+ curl_easy_setopt(ch, CURLOPT_USERAGENT, "libcurl/1.0");
+
+ //TODO use HTTP chunking, for now just convert request to a single string
+ std::ostringstream os;
+ write(cadr(req), os);
+ const std::string s = os.str();
+ const int sz = s.length();
+ if (sz < 1400)
+ curl_easy_setopt(ch, CURLOPT_TCP_NODELAY, true);
+
+ // Setup the read, header and write callbacks
+ CURLReadContext rcx(mklist(s));
+ curl_easy_setopt(ch, CURLOPT_READFUNCTION, (size_t (*)(void*, size_t, size_t, void*))readCallback);
+ curl_easy_setopt(ch, CURLOPT_READDATA, &rcx);
+ CURLWriteContext<R> hcx(reduce, initial);
+ curl_easy_setopt(ch, CURLOPT_HEADERFUNCTION, (size_t (*)(void*, size_t, size_t, void*))headerCallback<R>);
+ curl_easy_setopt(ch, CURLOPT_HEADERDATA, &hcx);
+ CURLWriteContext<R> wcx(reduce, initial);
curl_easy_setopt(ch, CURLOPT_WRITEFUNCTION, (size_t (*)(void*, size_t, size_t, void*))writeCallback<R>);
curl_easy_setopt(ch, CURLOPT_WRITEDATA, &wcx);
- curl_easy_setopt(ch, CURLOPT_HEADERFUNCTION, (size_t (*)(void*, size_t, size_t, void*))headerCallback<R>);
- curl_easy_setopt(ch, CURLOPT_HEADERDATA, &wcx);
-
- // Perform the HTTP GET
+
+ // Set the request headers
+ curl_slist* hl = headers(NULL, car(req));
+ if (hl != NULL)
+ curl_easy_setopt(ch, CURLOPT_HTTPHEADER, hl);
+
+ // Apply the HTTP verb
+ curl_easy_setopt(ch, CURLOPT_URL, url.c_str());
+ if (verb == "POST") {
+ curl_easy_setopt(ch, CURLOPT_POST, true);
+ curl_easy_setopt(ch, CURLOPT_POSTFIELDSIZE, sz);
+ } else if (verb == "PUT") {
+ curl_easy_setopt(ch, CURLOPT_UPLOAD, true);
+ curl_easy_setopt(ch, CURLOPT_INFILESIZE, sz);
+ } else if (verb == "DELETE")
+ curl_easy_setopt(ch, CURLOPT_CUSTOMREQUEST, "DELETE");
const CURLcode rc = curl_easy_perform(ch);
- if (rc)
- return rc;
+
+ if (hl != NULL)
+ curl_slist_free_all(hl);
// Return the HTTP return code or content
+ if (rc)
+ return std::string(curl_easy_strerror(rc));
long httprc;
curl_easy_getinfo (ch, CURLINFO_RESPONSE_CODE, &httprc);
- if (httprc != 200)
- return httprc;
- return wcx.accum;
+ if (httprc != 200 && httprc != 201) {
+ std::ostringstream es;
+ es << "HTTP code " << httprc;
+ return es.str();
+ }
+ return mklist<R>(hcx.accum, wcx.accum);
}
/**
- * HTTP GET, get a list of values representing a resource from a URL.
+ * Evaluate an expression remotely, at the given URL.
*/
-const list<std::string> writeStringList(const list<std::string>& listSoFar, const std::string& s) {
- return cons(s, listSoFar);
+const failable<value, std::string> evalExpr(const value expr, const std::string& url, CURLHandle& ch) {
+
+ // Convert expression to a JSON-RPC request
+ json::JSONContext cx;
+ const failable<list<std::string>, std::string> jsreq = jsonRequest(1, car<value>(expr), cdr<value>(expr), cx);
+ if (!hasValue(jsreq))
+ return std::string(jsreq);
+
+ if (logContent) {
+ std::cout<< "content: " << std::endl;
+ write(jsreq, std::cout);
+ std::cout<< std::endl;
+ std::cout.flush();
+ }
+
+ // POST it to the URL
+ const list<std::string> h = mklist<std::string>("Content-Type: application/json-rpc");
+ const failable<list<list<std::string> >, std::string> res = apply<list<std::string> >(mklist<list<std::string> >(h, jsreq), rcons<std::string>, list<std::string>(), url, "POST", ch);
+ if (!hasValue(res))
+ return std::string(res);
+
+ // Return result
+ if (logContent) {
+ std::cout << "content:" << std::endl;
+ write(cadr<list<std::string> >(res), std::cout);
+ std::cout << std::endl;
+ }
+ const list<value> val = elementsToValues(json::readJSON(cadr<list<std::string> >(res), cx));
+ return cadr<value>(cadr<value>(val));
+}
+
+/**
+ * HTTP GET, return the resource at the given URL.
+ */
+template<typename R> const failable<list<R>, std::string> get(const lambda<R(std::string, R)>& reduce, const R& initial, const std::string& url, CURLHandle& ch) {
+ const list<list<std::string> > req = mklist(list<std::string>(), list<std::string>());
+ return apply(req, reduce, initial, url, "GET", ch);
+}
+
+/**
+ * HTTP GET, return a list of values representing the resource at the given URL.
+ */
+const failable<value, std::string> get(const std::string& url, CURLHandle& ch) {
+
+ // Get the contents of the resource at the given URL
+ const failable<list<list<std::string> >, std::string> res = get<list<std::string> >(rcons<std::string>, list<std::string>(), url, ch);
+ if (!hasValue(res))
+ return std::string(res);
+ const list<list<std::string> > ls = res;
+
+ // TODO Return an ATOM feed
+ const std::string ct;
+ if (ct.find("application/atom+xml") != std::string::npos) {
+ }
+
+ // Return the content as a string value
+ std::ostringstream os;
+ write(reverse(cadr(ls)), os);
+ return value(os.str());
}
-const failable<list<std::string>, int> get(const std::string& url) {
- const failable<list<std::string>, int> r = get<list<std::string> >(writeStringList, list<std::string>(), url);
- if (!hasValue(r))
- return r;
- return reverse(list<std::string>(r));
+/**
+ * HTTP POST.
+ */
+const failable<value, std::string> post(const value& val, const std::string& url, CURLHandle& ch) {
+
+ // Convert value to an ATOM entry
+ const failable<list<std::string>, std::string> entry = atom::writeATOMEntry(atom::entryValuesToElements(val));
+ if (!hasValue(entry))
+ return std::string(entry);
+ if (logContent) {
+ std::cout << "content:" << std::endl;
+ write(list<std::string>(entry), std::cout);
+ std::cout << std::endl;
+ }
+
+ // POST it to the URL
+ const list<std::string> h = mklist<std::string>("Content-Type: application/atom+xml");
+ const list<list<std::string> > req = mklist<list<std::string> >(h, entry);
+ const failable<list<list<std::string> >, std::string> res = apply<list<std::string> >(req, rcons<std::string>, list<std::string>(), url, "POST", ch);
+ if (!hasValue(res))
+ return std::string(res);
+ return value(true);
+}
+
+/**
+ * HTTP PUT.
+ */
+const failable<value, std::string> put(const value& val, const std::string& url, CURLHandle& ch) {
+
+ // Convert value to an ATOM entry
+ const failable<list<std::string>, std::string> entry = atom::writeATOMEntry(atom::entryValuesToElements(val));
+ if (!hasValue(entry))
+ return std::string(entry);
+ if (logContent) {
+ std::cout << "content:" << std::endl;
+ write(list<std::string>(entry), std::cout);
+ std::cout << std::endl;
+ }
+
+ // POST it to the URL
+ const list<std::string> h = mklist<std::string>("Content-Type: application/atom+xml");
+ const list<list<std::string> > req = mklist<list<std::string> >(h, entry);
+ const failable<list<list<std::string> >, std::string> res = apply<list<std::string> >(req, rcons<std::string>, list<std::string>(), url, "PUT", ch);
+ if (!hasValue(res))
+ return std::string(res);
+ return value(true);
+}
+
+/**
+ * HTTP DELETE.
+ */
+const failable<value, std::string> del(const std::string& url, CURLHandle& ch) {
+ const list<list<std::string> > req = mklist(list<std::string>(), list<std::string>());
+ const failable<list<list<std::string> >, std::string> res = apply<list<std::string> >(req, rcons<std::string>, list<std::string>(), url, "DELETE", ch);
+ if (!hasValue(res))
+ return std::string(res);
+ return value(true);
}
}
diff --git a/cpp/sca/modules/http/htdocs/entry.xml b/cpp/sca/modules/http/htdocs/entry.xml
new file mode 100644
index 0000000000..4906fbedc8
--- /dev/null
+++ b/cpp/sca/modules/http/htdocs/entry.xml
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<entry xmlns="http://www.w3.org/2005/Atom"><title type="text">Item</title><id>111</id><content type="application/xml"><item><javaClass>services.Item</javaClass><name>Apple</name><currency>USD</currency><symbol>$</symbol><price>2.99</price></item></content><link href="111"/></entry>
diff --git a/cpp/sca/modules/http/htdocs/feed.xml b/cpp/sca/modules/http/htdocs/feed.xml
new file mode 100644
index 0000000000..4ca3183739
--- /dev/null
+++ b/cpp/sca/modules/http/htdocs/feed.xml
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<feed xmlns="http://www.w3.org/2005/Atom"><title type="text">Sample Feed</title><id>123456789</id><entry xmlns="http://www.w3.org/2005/Atom"><title type="text">Item</title><id>111</id><content type="application/xml"><item>javaClass</item></content><link href="111"/></entry><entry xmlns="http://www.w3.org/2005/Atom"><title type="text">Item</title><id>222</id><content type="application/xml"><item>javaClass</item></content><link href="222"/></entry><entry xmlns="http://www.w3.org/2005/Atom"><title type="text">Item</title><id>333</id><content type="application/xml"><item>javaClass</item></content><link href="333"/></entry></feed>
diff --git a/cpp/sca/modules/http/test-htdocs/index.html b/cpp/sca/modules/http/htdocs/index.html
index 1bfb3e30c2..1bfb3e30c2 100644
--- a/cpp/sca/modules/http/test-htdocs/index.html
+++ b/cpp/sca/modules/http/htdocs/index.html
diff --git a/cpp/sca/modules/http/htdocs/json-request.txt b/cpp/sca/modules/http/htdocs/json-request.txt
new file mode 100644
index 0000000000..b4bd07fc46
--- /dev/null
+++ b/cpp/sca/modules/http/htdocs/json-request.txt
@@ -0,0 +1 @@
+{"id":1,"method":"echo","params":["Hello"]}
diff --git a/cpp/sca/modules/http/htdocs/json-result.txt b/cpp/sca/modules/http/htdocs/json-result.txt
new file mode 100644
index 0000000000..121bf74902
--- /dev/null
+++ b/cpp/sca/modules/http/htdocs/json-result.txt
@@ -0,0 +1 @@
+{"id":1,"result":"Hello"} \ No newline at end of file
diff --git a/cpp/sca/modules/http/http-test b/cpp/sca/modules/http/http-test
index b0dd21cbd1..e369cc34f7 100755
--- a/cpp/sca/modules/http/http-test
+++ b/cpp/sca/modules/http/http-test
@@ -17,18 +17,24 @@
# specific language governing permissions and limitations
# under the License.
-mkdir -p tmp
-mkdir -p tmp/conf
-cp test-conf/* tmp/conf
-cat >tmp/conf/httpd.conf <<EOF
-ServerName 127.0.0.1
-Listen 9091
-DocumentRoot `pwd`/test-htdocs
+# Setup
+./httpd-conf tmp 8091 htdocs
+cat >>tmp/conf/httpd.conf <<EOF
+<Location /test>
+SetHandler mod_tuscany
+SCAContribution `pwd`/
+SCAComponent httpd-test
+SCAImplementation httpd-test.scm
+</Location>
EOF
-mkdir -p tmp/logs
apachectl -k start -d `pwd`/tmp
sleep 1
+
+# Test
./curl-test
rc=$?
+
+# Cleanup
apachectl -k stop -d `pwd`/tmp
+sleep 1
return $rc
diff --git a/cpp/sca/modules/http/httpd-conf b/cpp/sca/modules/http/httpd-conf
new file mode 100755
index 0000000000..e076e22bb1
--- /dev/null
+++ b/cpp/sca/modules/http/httpd-conf
@@ -0,0 +1,36 @@
+#!/bin/sh
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# Generate a minimal httpd.conf
+here=`readlink -f $0`; here=`dirname $here`
+root=`readlink -f $1`
+port=$2
+htdocs=`readlink -f $3`
+
+mkdir -p $root
+mkdir -p $root/logs
+mkdir -p $root/conf
+cat >$root/conf/httpd.conf <<EOF
+ServerName 127.0.0.1
+Listen $port
+DocumentRoot $htdocs
+TypesConfig $here/conf/mime.types
+LoadModule mod_tuscany $here/.libs/libmod_tuscany.so
+EOF
+
diff --git a/cpp/sca/modules/http/httpd-test b/cpp/sca/modules/http/httpd-test
index 3e4c5f82de..04c584496c 100755
--- a/cpp/sca/modules/http/httpd-test
+++ b/cpp/sca/modules/http/httpd-test
@@ -18,20 +18,59 @@
# under the License.
echo "Testing..."
-mkdir -p tmp
-mkdir -p tmp/conf
-cp test-conf/* tmp/conf
-cat >tmp/conf/httpd.conf <<EOF
-ServerName 127.0.0.1
-Listen 9090
-DocumentRoot `pwd`/test-htdocs
+
+# Setup
+./httpd-conf tmp 8090 htdocs
+cat >>tmp/conf/httpd.conf <<EOF
+<Location /test>
+SetHandler mod_tuscany
+SCAContribution `pwd`/
+SCAComponent httpd-test
+SCAImplementation httpd-test.scm
+</Location>
EOF
-mkdir -p tmp/logs
apachectl -k start -d `pwd`/tmp
sleep 1
-curl http://localhost:9090/index.html 2>&1 | grep "It works" >/dev/null
+
+# Test HTTP GET
+curl http://localhost:8090/index.html 2>/dev/null >tmp/index.html
+diff tmp/index.html htdocs/index.html
rc=$?
+
+# Test ATOMPub
+if [ "$rc" = "0" ]; then
+ curl http://localhost:8090/test/ >tmp/feed.xml 2>/dev/null
+ diff tmp/feed.xml htdocs/feed.xml
+ rc=$?
+fi
+if [ "$rc" = "0" ]; then
+ curl http://localhost:8090/test/111 >tmp/entry.xml 2>/dev/null
+ diff tmp/entry.xml htdocs/entry.xml
+ rc=$?
+fi
+if [ "$rc" = "0" ]; then
+ curl http://localhost:8090/test/ -X POST -H "Content-type: application/atom+xml" --data @htdocs/entry.xml 2>/dev/null
+ rc=$?
+fi
+if [ "$rc" = "0" ]; then
+ curl http://localhost:8090/test/111 -X PUT -H "Content-type: application/atom+xml" --data @htdocs/entry.xml 2>/dev/null
+ rc=$?
+fi
+if [ "$rc" = "0" ]; then
+ curl http://localhost:8090/test/111 -X DELETE 2>/dev/null
+ rc=$?
+fi
+
+# Test JSON-RPC
+if [ "$rc" = "0" ]; then
+ curl http://localhost:8090/test/ -X POST -H "Content-type: application/json-rpc" --data @htdocs/json-request.txt >tmp/json-result.txt 2>/dev/null
+ diff tmp/json-result.txt htdocs/json-result.txt
+ rc=$?
+fi
+
+# Cleanup
apachectl -k stop -d `pwd`/tmp
+sleep 1
if [ "$rc" = "0" ]; then
echo "OK"
fi
diff --git a/cpp/sca/modules/http/httpd-test.scm b/cpp/sca/modules/http/httpd-test.scm
new file mode 100644
index 0000000000..a3ddf8dda8
--- /dev/null
+++ b/cpp/sca/modules/http/httpd-test.scm
@@ -0,0 +1,33 @@
+(; "JSON-RPC test case")
+
+(define (echo x) x)
+
+(; "ATOMPub test case")
+
+(define (getall)
+ '("Sample Feed" "123456789"
+ ("Item" "111" (javaClass "services.Item") (name "Apple") (currency "USD") (symbol "$") (price 2.99))
+ ("Item" "222" (javaClass "services.Item") (name "Orange") (currency "USD") (symbol "$") (price 3.55))
+ ("Item" "333" (javaClass "services.Item") (name "Pear") (currency "USD") (symbol "$") (price 1.55)))
+)
+
+(define (get id)
+ (define entry '((javaClass "services.Item") (name "Apple") (currency "USD") (symbol "$") (price 2.99)))
+ (cons "Item" (list id entry))
+)
+
+(define (post entry)
+ (display entry)
+ "123456789"
+)
+
+(define (put entry)
+ (display entry)
+ true
+)
+
+(define (delete . args)
+ (display args)
+ true
+)
+
diff --git a/cpp/sca/modules/http/mod.cpp b/cpp/sca/modules/http/mod.cpp
index 88891a668c..811ee0c633 100644
--- a/cpp/sca/modules/http/mod.cpp
+++ b/cpp/sca/modules/http/mod.cpp
@@ -64,6 +64,12 @@ namespace tuscany {
namespace httpd {
/**
+ * Set to true to log requests and content.
+ */
+bool logRequests = false;
+bool logContent = false;
+
+/**
* Server configuration.
*/
struct ServerConf {
@@ -139,10 +145,8 @@ const std::string contentType(const request_rec* r) {
}
/**
- * Log HTTP request info to standard out for now, for debugging purposes.
+ * Log HTTP request info.
*/
-bool logRequests = true;
-
int logHeader(void* r, const char* key, const char* value) {
std::cout << "header key: " << key << ", value: " << value << std::endl;
return 1;
@@ -161,6 +165,7 @@ const bool logRequest(request_rec* r) {
apr_table_do(logHeader, r, r->headers_in, NULL);
std::cout << "uri: " << optional(r->uri) << std::endl;
std::cout << "path info: " << optional(r->path_info) << std::endl;
+ std::cout << "filename: " << optional(r->filename) << std::endl;
std::cout << "path: " << pathTokens(r) << std::endl;
std::cout << "args: " << optional(r->args) << std::endl;
std::cout.flush();
@@ -170,34 +175,32 @@ const bool logRequest(request_rec* r) {
/**
* Evaluate an expression against a component implementation.
*/
-const value evalExprLoop(std::istream& is, const value& req, eval::Env& globalEnv) {
- value in = eval::read(is);
+const value evalExprLoop(std::istream& is, const value& expr, eval::Env& globalEnv, const gc_pool& pool) {
+ value in = eval::readValue(is);
if (isNil(in))
- return eval::evalApply(req, globalEnv);
- eval::evalApply(in, globalEnv);
- return evalExprLoop(is, req, globalEnv);
+ return eval::evalExpr(expr, globalEnv, pool);
+ eval::evalExpr(in, globalEnv, pool);
+ return evalExprLoop(is, expr, globalEnv, pool);
}
-const failable<value, int> evalExpr(const value& expr, const std::string& contrib, const std::string& impl) {
- // Setup the evaluator
- eval::Env globalEnv = eval::setupEnvironment();
- std::ostringstream nullos;
- eval::setupEvalOut(nullos);
-
+const failable<value, std::string> evalExpr(const value& expr, const std::string& contrib, const std::string& impl) {
// Retrieve the component implementation
const std::string path = contrib + impl;
std::ifstream is(path.c_str(), std::ios_base::in);
if (is.fail() || is.bad())
- return HTTP_NOT_FOUND;
+ return std::string("HTTP_NOT_FOUND");
// Evaluate the expr
+ gc_pool pool;
+ eval::Env globalEnv = eval::setupEnvironment(pool);
std::cout<< "expr: " << expr << std::endl;
std::cout.flush();
- const value val = evalExprLoop(is, expr, globalEnv);
+ const value val = evalExprLoop(is, expr, globalEnv, pool);
std::cout<< "val: " << val << std::endl;
std::cout.flush();
+
if (isNil(val))
- return HTTP_INTERNAL_SERVER_ERROR;
+ return std::string("Could not evaluate expression");
return val;
}
@@ -230,74 +233,17 @@ const list<value> queryParams(list<list<value> > a) {
}
/**
- * Convert a value to a JSON result.
- */
-const failable<list<std::string>, int> jsonResult(json::JSONContext& cx, const value& id, const failable<value, int>& val) {
- if (!hasValue(val))
- return int(val);
- const list<value> r = mklist<value>(mklist<value>("id", id), mklist<value>("result", val));
- failable<list<std::string>, std::string> ls = json::write(cx, valuesToElements(r));
- if (!hasValue(ls))
- return HTTP_INTERNAL_SERVER_ERROR;
- std::cout<< "content: " << std::endl;
- write(ls, std::cout);
- std::cout<< std::endl;
- std::cout.flush();
- return list<std::string>(ls);
-}
-
-/**
- * Convert a value to an ATOM entry.
- */
-const list<value> feedEntryResult(const list<value> e) {
- return cons(car(e), cons(cadr(e), valuesToElements(mklist<value>(cons<value>("item", (list<value>)caddr(e))))));
-}
-
-/**
- * Convert a value to an ATOM feed.
- */
-const list<value> feedEntriesResults(const list<value> e) {
- if (isNil(e))
- return list<value>();
- return cons<value>(feedEntryResult(car(e)), feedEntriesResults(cdr(e)));
-}
-
-const failable<list<std::string>, int> feedResult(const failable<value, int>& val) {
- if (!hasValue(val))
- return int(val);
- const value v = val;
- list<value> f = cons(car<value>(v), cons<value>(cadr<value>(v), feedEntriesResults(cddr<value>(v))));
- failable<list<std::string>, std::string> ls = atom::writeFeed(f);
- if (!hasValue(ls))
- return HTTP_INTERNAL_SERVER_ERROR;
- return list<std::string>(ls);
-}
-
-/**
- * Convert a value to an ATOM entry result.
- */
-const failable<list<std::string>, int> entryResult(const failable<value, int>& val) {
- if (!hasValue(val))
- return int(val);
- const value v = val;
- list<value> e = feedEntryResult(v);
- std::cout<< "entry: " << e << std::endl;
- failable<list<std::string>, std::string> ls = atom::writeEntry(e);
- if (!hasValue(ls))
- return HTTP_INTERNAL_SERVER_ERROR;
- return list<std::string>(ls);
-}
-
-/**
* Write an HTTP result.
*/
-const int writeResult(const failable<list<std::string>, int> ls, const std::string& ct, request_rec* r) {
+const failable<int, std::string> writeResult(const failable<list<std::string>, std::string> ls, const std::string& ct, request_rec* r) {
if (!hasValue(ls))
- return ls;
+ return std::string(ls);
std::ostringstream os;
write(ls, os);
- std::cout<< "content: " << os.str() << std::endl;
- std::cout.flush();
+ if (logContent) {
+ std::cout<< "content: " << std::endl << os.str() << std::endl;
+ std::cout.flush();
+ }
std::string etag(ap_md5(r->pool, (const unsigned char*)std::string(os.str()).c_str()));
const char* match = apr_table_get(r->headers_in, "If-None-Match");
@@ -314,7 +260,7 @@ const int writeResult(const failable<list<std::string>, int> ls, const std::stri
/**
* Handle an HTTP GET.
*/
-const int get(request_rec* r) {
+const failable<int, std::string> get(request_rec* r) {
// Inspect the query string
const list<list<value> > args = queryArgs(r);
@@ -326,26 +272,34 @@ const int get(request_rec* r) {
// Extract the request id, method and params
const value id = cadr(ia);
- const value method = std::string(cadr(ma)).c_str();
+ const value func = std::string(cadr(ma)).c_str();
const list<value> params = queryParams(args);
// Evaluate the request expression
- const failable<value, int> val = evalExpr(cons(method, params), contribution(r), implementation(r));
+ const failable<value, std::string> val = evalExpr(cons(func, eval::quotedParameters(params)), contribution(r), implementation(r));
+ if (!hasValue(val))
+ return std::string(val);
// Return JSON result
json::JSONContext cx;
- return writeResult(jsonResult(cx, id, val), "application/json-rpc", r);
+ return writeResult(json::jsonResult(id, val, cx), "application/json-rpc", r);
}
// Evaluate an ATOM GET request and return an ATOM feed
- if (length(path(r)) < 2) {
- const failable<value, int> val = evalExpr(cons<value>("getall"), contribution(r), implementation(r));
- return writeResult(feedResult(val), "application/atom+xml", r);
+ if (isNil(path(r))) {
+ const failable<value, std::string> val = evalExpr(mklist<value>("getall"), contribution(r), implementation(r));
+ if (!hasValue(val))
+ return std::string(val);
+ const value feed = val;
+ return writeResult(atom::writeATOMFeed(atom::feedValuesToElements(feed)), "application/atom+xml;type=feed", r);
}
// Evaluate an ATOM GET and return an ATOM entry
- const failable<value, int> val = evalExpr(cons<value>("get", cdr(path(r))), contribution(r), implementation(r));
- return writeResult(entryResult(val), "application/atom+xml", r);
+ const failable<value, std::string> val = evalExpr(cons<value>("get", path(r)), contribution(r), implementation(r));
+ if (!hasValue(val))
+ return std::string(val);
+ const value entry = val;
+ return writeResult(atom::writeATOMEntry(atom::entryValuesToElements(entry)), "application/atom+xml;type=entry", r);
}
@@ -370,10 +324,10 @@ const list<list<value> > postArgs(list<value> a) {
return cons(l, postArgs(cdr(a)));
}
-const char* url(const std::string& loc, request_rec* r) {
+const char* url(const value& v, request_rec* r) {
std::string u = r->uri;
u.append("/");
- u.append(loc);
+ u.append(v);
return ap_construct_url(r->pool, u.c_str(), r);
}
@@ -388,43 +342,49 @@ const value feedEntry(const list<value> e) {
/**
* Handle an HTTP POST.
*/
-const int post(request_rec* r) {
- const std::string ct = contentType(r);
+const failable<int, std::string> post(request_rec* r) {
+ const list<std::string> ls = read(r);
+ if (logContent) {
+ std::cout<< "content: " << std::endl;
+ write(ls, std::cout);
+ std::cout<< std::endl;
+ std::cout.flush();
+ }
// Evaluate a JSON-RPC request and return a JSON result
+ const std::string ct = contentType(r);
if (ct.find("application/json-rpc") != std::string::npos || ct.find("text/plain") != std::string::npos) {
json::JSONContext cx;
- const list<value> json = elementsToValues(json::read(cx, read(r)));
+ const list<value> json = elementsToValues(json::readJSON(ls, cx));
const list<list<value> > args = postArgs(json);
// Extract the request id, method and params
const value id = cadr(assoc(value("id"), args));
- const value method = std::string(cadr(assoc(value("method"), args))).c_str();
+ const value func = std::string(cadr(assoc(value("method"), args))).c_str();
const list<value> params = (list<value>)cadr(assoc(value("params"), args));
// Evaluate the request expression
- const failable<value, int> val = evalExpr(cons(method, params), contribution(r), implementation(r));
+ const failable<value, std::string> val = evalExpr(cons(func, eval::quotedParameters(params)), contribution(r), implementation(r));
+ if (!hasValue(val))
+ return std::string(val);
// Return JSON result
- return writeResult(jsonResult(cx, id, val), "application/json-rpc", r);
+ return writeResult(json::jsonResult(id, val, cx), "application/json-rpc", r);
}
// Evaluate an ATOM POST request and return the created resource location
if (ct.find("application/atom+xml") != std::string::npos) {
- const list<std::string> c = read(r);
- std::cout << "POST content: " << c << std::endl;
- const list<value> e = atom::readEntry(c);
- std::cout << "POST entry: " << e << std::endl;
- const value v = feedEntry(e);
- std::cout << "POST param: " << v << std::endl;
// Evaluate the request expression
- const failable<value, int> val = evalExpr(mklist<value>("post", mklist(v)), contribution(r), implementation(r));
-
- const char* u = url("abcd", r);
- apr_table_setn(r->headers_out, "Location", u);
- apr_table_setn(r->headers_out, "Content-Location", u);
- return HTTP_CREATED;
+ const value entry = feedEntry(atom::readEntry(ls));
+ const failable<value, std::string> val = evalExpr(cons<value>("post", eval::quotedParameters(mklist<value>(entry))), contribution(r), implementation(r));
+ if (!hasValue(val))
+ return std::string(val);
+
+ // Return the created resource location
+ apr_table_setn(r->headers_out, "Location", url(val, r));
+ r->status = HTTP_CREATED;
+ return OK;
}
return HTTP_NOT_IMPLEMENTED;
@@ -433,25 +393,48 @@ const int post(request_rec* r) {
/**
* Handle an HTTP PUT.
*/
-const int put(request_rec* r) {
- // TODO later
+const failable<int, std::string> put(request_rec* r) {
+ const list<std::string> ls = read(r);
+ std::cout<< "content: " << std::endl;
+ write(ls, std::cout);
+ std::cout<< std::endl;
+ std::cout.flush();
+
+ // Evaluate an ATOM PUT request
+ const value entry = feedEntry(atom::readEntry(ls));
+ const failable<value, std::string> val = evalExpr(cons<value>("put", eval::quotedParameters(mklist<value>(entry))), contribution(r), implementation(r));
+ if (!hasValue(val))
+ return std::string(val);
+ if (val == value(false))
+ return HTTP_NOT_FOUND;
return OK;
}
/**
* Handle an HTTP DELETE.
*/
-const int del(request_rec* r) {
+const failable<int, std::string> del(request_rec* r) {
// Evaluate an ATOM delete request
- const failable<value, int> val = evalExpr(cons<value>("delete", cdr(path(r))), contribution(r), implementation(r));
+ const failable<value, std::string> val = evalExpr(cons<value>("delete", path(r)), contribution(r), implementation(r));
if (!hasValue(val))
- return val;
+ return std::string(val);
+ if (val == value(false))
+ return HTTP_NOT_FOUND;
return OK;
}
/**
- * HTTP handler entry point.
+ * Report request execution status.
+ */
+const int reportStatus(const failable<int, std::string> rc) {
+ if (!hasValue(rc))
+ return HTTP_INTERNAL_SERVER_ERROR;
+ return rc;
+}
+
+/**
+ * HTTP request handler entry point.
*/
int handler(request_rec *r) {
if(strcmp(r->handler, "mod_tuscany"))
@@ -468,19 +451,19 @@ int handler(request_rec *r) {
ap_should_client_block(r);
if(r->read_chunked == true && r->remaining == 0)
r->chunked = true;
- apr_table_setn(r->headers_out, "Connection", "close");
+ //apr_table_setn(r->headers_out, "Connection", "close");
// Handle HTTP method
if (r->header_only)
return OK;
if(r->method_number == M_GET)
- return get(r);
+ return reportStatus(get(r));
if(r->method_number == M_POST)
- return post(r);
+ return reportStatus(post(r));
if(r->method_number == M_PUT)
- return put(r);
+ return reportStatus(put(r));
if(r->method_number == M_DELETE)
- return del(r);
+ return reportStatus(del(r));
return HTTP_NOT_IMPLEMENTED;
}
diff --git a/cpp/sca/modules/json/json-test.cpp b/cpp/sca/modules/json/json-test.cpp
index bf0ecab1f4..0d4e8f6a16 100644
--- a/cpp/sca/modules/json/json-test.cpp
+++ b/cpp/sca/modules/json/json-test.cpp
@@ -43,7 +43,7 @@ bool testJSEval() {
return true;
}
-std::ostringstream* jsonWriter(std::ostringstream* os, const std::string& s) {
+std::ostringstream* jsonWriter(const std::string& s, std::ostringstream* os) {
(*os) << s;
return os;
}
@@ -57,7 +57,7 @@ bool testJSON() {
const list<value> cr = mklist<value>(mklist<value> (attribute, "name", std::string("jdoe")), cons<value>(element, cons<value>("address", ad)), cons<value>(element, cons<value>("account", ac)));
const list<value> c = mklist<value>(cons<value>(element, cons<value>("customer", cr)));
std::ostringstream os;
- write<std::ostringstream*>(cx, jsonWriter, &os, c);
+ writeJSON<std::ostringstream*>(jsonWriter, &os, c, cx);
assert(os.str() == "{\"customer\":{\"name\":\"jdoe\",\"address\":{\"city\":\"san francisco\",\"state\":\"ca\"},\"account\":{\"id\":\"1234\",\"balance\":1000}}}");
}
{
@@ -65,16 +65,16 @@ bool testJSON() {
const list<value> l = mklist<value> (mklist<value> (element, "phones", phones), mklist<value> (element, "lastName", std::string("test\ttab")), mklist<value> (element, "firstName", std::string("test1")));
std::ostringstream os;
- write<std::ostringstream*>(cx, jsonWriter, &os, l);
+ writeJSON<std::ostringstream*>(jsonWriter, &os, l, cx);
assert(os.str() == "{\"phones\":[\"408-1234\",\"650-1234\"],\"lastName\":\"test\\u0009tab\",\"firstName\":\"test1\"}");
std::istringstream is(os.str());
const list<std::string> il = streamList(is);
- const list<value> r = read(cx, il);
+ const list<value> r = readJSON(il, cx);
assert(r == l);
std::ostringstream wos;
- write(write(cx, r), wos);
+ write(writeJSON(r, cx), wos);
assert(wos.str() == os.str());
}
return true;
@@ -84,7 +84,7 @@ bool testJSONRPC() {
JSONContext cx;
{
const std::string lm("{\"id\": 1, \"method\": \"system.listMethods\", \"params\": []}");
- const list<value> e = read(cx, mklist(lm));
+ const list<value> e = readJSON(mklist(lm), cx);
const list<value> v = elementsToValues(e);
assert(assoc<value>("id", v) == mklist<value>("id", 1));
assert(assoc<value>("method", v) == mklist<value>("method", std::string("system.listMethods")));
@@ -92,16 +92,16 @@ bool testJSONRPC() {
}
{
const std::string i("{\"id\":3,\"result\":[{\"price\":\"$2.99\",\"name\":\"Apple\"},{\"price\":\"$3.55\",\"name\":\"Orange\"},{\"price\":\"$1.55\",\"name\":\"Pear\"}]}");
- const list<value> e = read(cx, mklist(i));
+ const list<value> e = readJSON(mklist(i), cx);
const std::string i2("{\"id\":3,\"result\":{\"0\":{\"price\":\"$2.99\",\"name\":\"Apple\"},\"1\":{\"price\":\"$3.55\",\"name\":\"Orange\"},\"2\":{\"price\":\"$1.55\",\"name\":\"Pear\"}}}");
- const list<value> e2 = read(cx, mklist(i));
+ const list<value> e2 = readJSON(mklist(i), cx);
assert(e == e2);
}
{
const std::string i("{\"id\":3,\"result\":[{\"price\":\"$2.99\",\"name\":\"Apple\"},{\"price\":\"$3.55\",\"name\":\"Orange\"},{\"price\":\"$1.55\",\"name\":\"Pear\"}]}");
- const list<value> e = read(cx, mklist(i));
+ const list<value> e = readJSON(mklist(i), cx);
std::ostringstream os;
- write(write(cx, e), os);
+ write(writeJSON(e, cx), os);
assert(os.str() == i);
const list<value> v = elementsToValues(e);
const list<value> r = valuesToElements(v);
@@ -111,7 +111,7 @@ bool testJSONRPC() {
const list<value> r = mklist<value>(mklist<value>("id", 1), mklist<value>("result", mklist<value>(std::string("Service.get"), std::string("Service.getTotal"))));
const list<value> e = valuesToElements(r);
std::ostringstream os;
- write(write(cx, e), os);
+ write(writeJSON(e, cx), os);
assert(os.str() == "{\"id\":1,\"result\":[\"Service.get\",\"Service.getTotal\"]}");
}
return true;
diff --git a/cpp/sca/modules/json/json.hpp b/cpp/sca/modules/json/json.hpp
index 8e4666196e..0d21cfe359 100644
--- a/cpp/sca/modules/json/json.hpp
+++ b/cpp/sca/modules/json/json.hpp
@@ -142,9 +142,9 @@ private:
/**
* Converts JS properties to values.
*/
-const list<value> jsPropertiesToValues(const JSONContext& cx, const list<value>& propertiesSoFar, JSObject* o, JSObject* i) {
+const list<value> jsPropertiesToValues(const list<value>& propertiesSoFar, JSObject* o, JSObject* i, const JSONContext& cx) {
- const value jsValToValue(const JSONContext& cx, const jsval& jsv);
+ const value jsValToValue(const jsval& jsv, const JSONContext& cx);
jsid id;
if(!JS_NextProperty(cx, i, &id) || id == JSVAL_VOID)
@@ -152,20 +152,20 @@ const list<value> jsPropertiesToValues(const JSONContext& cx, const list<value>&
jsval jsv;
if(!JS_GetPropertyById(cx, o, id, &jsv))
return propertiesSoFar;
- const value val = jsValToValue(cx, jsv);
+ const value val = jsValToValue(jsv, cx);
jsval idv;
JS_IdToValue(cx, id, &idv);
if(JSVAL_IS_STRING(idv)) {
const value type = isList(val)? element : element;
- return jsPropertiesToValues(cx, cons<value> (mklist<value> (type, JS_GetStringBytes(JSVAL_TO_STRING(idv)), val), propertiesSoFar), o, i);
+ return jsPropertiesToValues(cons<value> (mklist<value> (type, JS_GetStringBytes(JSVAL_TO_STRING(idv)), val), propertiesSoFar), o, i, cx);
}
- return jsPropertiesToValues(cx, cons(val, propertiesSoFar), o, i);
+ return jsPropertiesToValues(cons(val, propertiesSoFar), o, i, cx);
}
/**
* Converts a JS val to a value.
*/
-const value jsValToValue(const JSONContext& cx, const jsval& jsv) {
+const value jsValToValue(const jsval& jsv, const JSONContext& cx) {
switch(JS_TypeOfValue(cx, jsv)) {
case JSTYPE_STRING: {
return value(std::string(JS_GetStringBytes(JSVAL_TO_STRING(jsv))));
@@ -183,7 +183,7 @@ const value jsValToValue(const JSONContext& cx, const jsval& jsv) {
JSObject* i = JS_NewPropertyIterator(cx, o);
if(i == NULL)
return value(list<value> ());
- const value pv = jsPropertiesToValues(cx, list<value> (), o, i);
+ const value pv = jsPropertiesToValues(list<value> (), o, i, cx);
return pv;
}
default: {
@@ -195,44 +195,44 @@ const value jsValToValue(const JSONContext& cx, const jsval& jsv) {
/**
* Consumes JSON strings and populates a JS object.
*/
-failable<bool, std::string> consume(const JSONContext& cx, JSONParser* parser, const list<std::string>& ilist) {
+failable<bool, std::string> consume(JSONParser* parser, const list<std::string>& ilist, const JSONContext& cx) {
if (isNil(ilist))
return true;
JSString* jstr = JS_NewStringCopyZ(cx, car(ilist).c_str());
if(!JS_ConsumeJSONText(cx, parser, JS_GetStringChars(jstr), JS_GetStringLength(jstr)))
return "JS_ConsumeJSONText failed";
- return consume(cx, parser, cdr(ilist));
+ return consume(parser, cdr(ilist), cx);
}
/**
* Convert a list of strings representing a JSON document to a list of values.
*/
-const failable<list<value>, std::string> read(const JSONContext& cx, const list<std::string>& ilist) {
+const failable<list<value>, std::string> readJSON(const list<std::string>& ilist, const JSONContext& cx) {
jsval val;
JSONParser* parser = JS_BeginJSONParse(cx, &val);
if(parser == NULL)
return std::string("JS_BeginJSONParse failed");
- const failable<bool, std::string> consumed = consume(cx, parser, ilist);
+ const failable<bool, std::string> consumed = consume(parser, ilist, cx);
if(!JS_FinishJSONParse(cx, parser, JSVAL_NULL))
return std::string("JS_FinishJSONParse failed");
if(!hasValue(consumed))
return std::string(consumed);
- return list<value>(jsValToValue(cx, val));
+ return list<value>(jsValToValue(val, cx));
}
/**
* Converts a list of values to JS array elements.
*/
-JSObject* valuesToJSElements(const JSONContext& cx, JSObject* a, const list<value>& l, int i) {
- const jsval valueToJSVal(const JSONContext& cx, const value& val);
+JSObject* valuesToJSElements(JSObject* a, const list<value>& l, int i, const JSONContext& cx) {
+ const jsval valueToJSVal(const value& val, const JSONContext& cx);
if (isNil(l))
return a;
- jsval pv = valueToJSVal(cx, car(l));
+ jsval pv = valueToJSVal(car(l), cx);
JS_SetElement(cx, a, i, &pv);
- return valuesToJSElements(cx, a, cdr(l), ++i);
+ return valuesToJSElements(a, cdr(l), ++i, cx);
}
/**
@@ -255,26 +255,26 @@ const bool isJSArray(const list<value>& l) {
/**
* Converts a list of values to JS properties.
*/
-JSObject* valuesToJSProperties(const JSONContext& cx, JSObject* o, const list<value>& l) {
- const jsval valueToJSVal(const JSONContext& cx, const value& val);
+JSObject* valuesToJSProperties(JSObject* o, const list<value>& l, const JSONContext& cx) {
+ const jsval valueToJSVal(const value& val, const JSONContext& cx);
if(isNil(l))
return o;
const list<value> p = car(l);
- jsval pv = valueToJSVal(cx, caddr(p));
+ jsval pv = valueToJSVal(caddr(p), cx);
JS_SetProperty(cx, o, ((std::string)cadr(p)).c_str(), &pv);
- return valuesToJSProperties(cx, o, cdr(l));
+ return valuesToJSProperties(o, cdr(l), cx);
}
/**
* Converts a value to a JS val.
*/
-const jsval valueToJSVal(const JSONContext& cx, const value& val) {
+const jsval valueToJSVal(const value& val, const JSONContext& cx) {
switch(type(val)) {
case value::String:
case value::Symbol: {
return STRING_TO_JSVAL(JS_NewStringCopyZ(cx, ((std::string)val).c_str()));
}
- case value::Boolean: {
+ case value::Bool: {
return BOOLEAN_TO_JSVAL((bool)val);
}
case value::Number: {
@@ -282,8 +282,8 @@ const jsval valueToJSVal(const JSONContext& cx, const value& val) {
}
case value::List: {
if (isJSArray(val))
- return OBJECT_TO_JSVAL(valuesToJSElements(cx, JS_NewArrayObject(cx, 0, NULL), val, 0));
- return OBJECT_TO_JSVAL(valuesToJSProperties(cx, JS_NewObject(cx, NULL, NULL, NULL), val));
+ return OBJECT_TO_JSVAL(valuesToJSElements(JS_NewArrayObject(cx, 0, NULL), val, 0, cx));
+ return OBJECT_TO_JSVAL(valuesToJSProperties(JS_NewObject(cx, NULL, NULL, NULL), val, cx));
}
default: {
return JSVAL_VOID;
@@ -291,7 +291,7 @@ const jsval valueToJSVal(const JSONContext& cx, const value& val) {
}
}
-const failable<bool, std::string> writeList(const JSONContext& cx, const list<value>& l, JSObject* o) {
+const failable<bool, std::string> writeList(const list<value>& l, JSObject* o, const JSONContext& cx) {
if (isNil(l))
return true;
@@ -299,14 +299,14 @@ const failable<bool, std::string> writeList(const JSONContext& cx, const list<va
const value token(car(l));
if (isTaggedList(token, attribute)) {
- jsval pv = valueToJSVal(cx, attributeValue(token));
+ jsval pv = valueToJSVal(attributeValue(token), cx);
JS_SetProperty(cx, o, std::string(attributeName(token)).c_str(), &pv);
} else if (isTaggedList(token, element)) {
// Write the value of an element
if (elementHasValue(token)) {
- jsval pv = valueToJSVal(cx, elementValue(token));
+ jsval pv = valueToJSVal(elementValue(token), cx);
JS_SetProperty(cx, o, std::string(elementName(token)).c_str(), &pv);
} else {
@@ -317,14 +317,14 @@ const failable<bool, std::string> writeList(const JSONContext& cx, const list<va
JS_SetProperty(cx, o, std::string(elementName(token)).c_str(), &pv);
// Write its children
- const failable<bool, std::string> w = writeList(cx, elementChildren(token), child);
+ const failable<bool, std::string> w = writeList(elementChildren(token), child, cx);
if (!hasValue(w))
return w;
}
}
// Go on
- return writeList(cx, cdr(l), o);
+ return writeList(cdr(l), o, cx);
}
/**
@@ -332,10 +332,10 @@ const failable<bool, std::string> writeList(const JSONContext& cx, const list<va
*/
template<typename R> class WriteContext {
public:
- WriteContext(const JSONContext& cx, const lambda<R(R, std::string)>& reduce, const R& accum) : cx(cx), reduce(reduce), accum(accum) {
+ WriteContext(const lambda<R(std::string, R)>& reduce, const R& accum, const JSONContext& cx) : cx(cx), reduce(reduce), accum(accum) {
}
const JSONContext& cx;
- const lambda<R(R, std::string)> reduce;
+ const lambda<R(std::string, R)> reduce;
R accum;
};
@@ -345,21 +345,21 @@ public:
template<typename R> JSBool writeCallback(const jschar *buf, uint32 len, void *data) {
WriteContext<R>& wcx = *(static_cast<WriteContext<R>*> (data));
JSString* jstr = JS_NewUCStringCopyN(wcx.cx, buf, len);
- wcx.accum = wcx.reduce(wcx.accum, std::string(JS_GetStringBytes(jstr), JS_GetStringLength(jstr)));
+ wcx.accum = wcx.reduce(std::string(JS_GetStringBytes(jstr), JS_GetStringLength(jstr)), wcx.accum);
return JS_TRUE;
}
/**
* Convert a list of values to a JSON document.
*/
-template<typename R> const failable<R, std::string> write(const JSONContext& cx, const lambda<R(R, std::string)>& reduce, const R& initial, const list<value>& l) {
+template<typename R> const failable<R, std::string> writeJSON(const lambda<R(std::string, R)>& reduce, const R& initial, const list<value>& l, const JSONContext& cx) {
JSObject* o = JS_NewObject(cx, NULL, NULL, NULL);
jsval val = OBJECT_TO_JSVAL(o);
- const failable<bool, std::string> w = writeList(cx, l, o);
+ const failable<bool, std::string> w = writeList(l, o, cx);
if (!hasValue(w))
return std::string(w);
- WriteContext<R> wcx(cx, reduce, initial);
+ WriteContext<R> wcx(reduce, initial, cx);
if (!JS_Stringify(cx, &val, NULL, JSVAL_NULL, writeCallback<R>, &wcx))
return std::string("JS_Stringify failed");
return wcx.accum;
@@ -368,17 +368,29 @@ template<typename R> const failable<R, std::string> write(const JSONContext& cx,
/**
* Convert a list of values to a list of strings representing a JSON document.
*/
-const list<std::string> writeStrings(const list<std::string>& listSoFar, const std::string& s) {
- return cons(s, listSoFar);
-}
-
-const failable<list<std::string>, std::string> write(const JSONContext& cx, const list<value>& l) {
- const failable<list<std::string>, std::string> ls = write<list<std::string> >(cx, writeStrings, list<std::string>(), l);
+const failable<list<std::string>, std::string> writeJSON(const list<value>& l, const JSONContext& cx) {
+ const failable<list<std::string>, std::string> ls = writeJSON<list<std::string> >(rcons<std::string>, list<std::string>(), l, cx);
if (!hasValue(ls))
return ls;
return reverse(list<std::string>(ls));
}
+/**
+ * Convert a function + params to a JSON request.
+ */
+const failable<list<std::string>, std::string> jsonRequest(const value& id, const value& func, const value& params, json::JSONContext& cx) {
+ const list<value> r = mklist<value>(mklist<value>("id", id), mklist<value>("method", func), mklist<value>("params", params));
+ failable<list<std::string>, std::string> ls = writeJSON(valuesToElements(r), cx);
+ return ls;
+}
+
+/**
+ * Convert a value to a JSON result.
+ */
+const failable<list<std::string>, std::string> jsonResult(const value& id, const value& val, JSONContext& cx) {
+ return writeJSON(valuesToElements(mklist<value>(mklist<value>("id", id), mklist<value>("result", val))), cx);
+}
+
}
}