summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjsdelfino <jsdelfino@13f79535-47bb-0310-9956-ffa450edef68>2011-06-13 07:57:13 +0000
committerjsdelfino <jsdelfino@13f79535-47bb-0310-9956-ffa450edef68>2011-06-13 07:57:13 +0000
commitba223e693c0f6e652fd70c05d83d69956262ff09 (patch)
treea29db0715d2274f13de355a7e4fbcf5919399282
parenteb7ad1a0241d049f10c9e62a8eb4d9e1dd3000a9 (diff)
Improve support for HTTP verbs and enable calls inside a domain without having to repeat the domain name in all the URLs.
git-svn-id: http://svn.us.apache.org/repos/asf/tuscany@1135047 13f79535-47bb-0310-9956-ffa450edef68
-rw-r--r--sca-cpp/trunk/components/http/Makefile.am19
-rw-r--r--sca-cpp/trunk/components/http/client-test.cpp39
-rw-r--r--sca-cpp/trunk/components/http/content-test.scm23
-rw-r--r--sca-cpp/trunk/components/http/http.composite49
-rw-r--r--sca-cpp/trunk/components/http/httppatch.componentType29
-rw-r--r--sca-cpp/trunk/components/http/httppatch.cpp93
-rw-r--r--sca-cpp/trunk/components/http/httppost.componentType29
-rw-r--r--sca-cpp/trunk/components/http/httppost.cpp93
-rw-r--r--sca-cpp/trunk/components/http/httpput.componentType29
-rw-r--r--sca-cpp/trunk/components/http/httpput.cpp93
-rw-r--r--sca-cpp/trunk/components/http/server-test.scm44
-rw-r--r--sca-cpp/trunk/components/http/url-test.scm2
-rw-r--r--sca-cpp/trunk/modules/edit/apps/testhttp/app.composite157
-rw-r--r--sca-cpp/trunk/modules/edit/palettes/http/palette.composite27
-rw-r--r--sca-cpp/trunk/modules/http/http.hpp199
-rw-r--r--sca-cpp/trunk/modules/http/httpd.hpp21
-rw-r--r--sca-cpp/trunk/modules/server/mod-eval.hpp18
-rw-r--r--sca-cpp/trunk/modules/server/mod-wiring.cpp5
18 files changed, 874 insertions, 95 deletions
diff --git a/sca-cpp/trunk/components/http/Makefile.am b/sca-cpp/trunk/components/http/Makefile.am
index 4388ec8057..1a081b8da6 100644
--- a/sca-cpp/trunk/components/http/Makefile.am
+++ b/sca-cpp/trunk/components/http/Makefile.am
@@ -19,8 +19,8 @@ compdir=$(prefix)/components/http
EXTRA_DIST = http.composite httpget.componentType httpdelete.componentType *.scm
-comp_LTLIBRARIES = libhttpget.la libhttpdelete.la
-noinst_DATA = libhttpget.so libhttpdelete.so
+comp_LTLIBRARIES = libhttpget.la libhttpdelete.la libhttppost.la libhttpput.la
+noinst_DATA = libhttpget.so libhttpdelete.so libhttppost.so libhttpput.so
libhttpget_la_SOURCES = httpget.cpp
libhttpget_la_LDFLAGS = -lxml2 -lmozjs -curl
@@ -32,6 +32,21 @@ libhttpdelete_la_LDFLAGS = -lxml2 -lmozjs -curl
libhttpdelete.so:
ln -s .libs/libhttpdelete.so
+libhttppost_la_SOURCES = httppost.cpp
+libhttppost_la_LDFLAGS = -lxml2 -lmozjs -curl
+libhttppost.so:
+ ln -s .libs/libhttppost.so
+
+libhttpput_la_SOURCES = httpput.cpp
+libhttpput_la_LDFLAGS = -lxml2 -lmozjs -curl
+libhttpput.so:
+ ln -s .libs/libhttpput.so
+
+libhttppatch_la_SOURCES = httppatch.cpp
+libhttppatch_la_LDFLAGS = -lxml2 -lmozjs -curl
+libhttppatch.so:
+ ln -s .libs/libhttppatch.so
+
client_test_SOURCES = client-test.cpp
client_test_LDFLAGS = -lxml2 -lcurl -lmozjs
diff --git a/sca-cpp/trunk/components/http/client-test.cpp b/sca-cpp/trunk/components/http/client-test.cpp
index a83bf55252..3ce9f1ae68 100644
--- a/sca-cpp/trunk/components/http/client-test.cpp
+++ b/sca-cpp/trunk/components/http/client-test.cpp
@@ -36,15 +36,16 @@
namespace tuscany {
namespace http {
-const string uri("http://localhost:8090/httpget");
+const string getURI("http://localhost:8090/httpget");
+const string postURI("http://localhost:8090/httppost");
+const string putURI("http://localhost:8090/httpput");
+const string deleteURI("http://localhost:8090/httpdelete");
bool testGet() {
http::CURLSession cs("", "", "", "");
- const failable<value> val = http::get(uri, cs);
+ const failable<value> val = http::get(getURI, cs);
assert(hasContent(val));
- assert(contains(string(car<value>(cadr<value>(content(val)))), "It works"));
-
return true;
}
@@ -53,9 +54,8 @@ struct getLoop {
getLoop(http::CURLSession cs) : cs(cs) {
}
const bool operator()() const {
- const failable<value> val = http::get(uri, cs);
+ const failable<value> val = http::get(getURI, cs);
assert(hasContent(val));
- assert(contains(string(car<value>(cadr<value>(content(val)))), "It works"));
return true;
}
};
@@ -69,6 +69,30 @@ bool testGetPerf() {
return true;
}
+bool testPost() {
+ http::CURLSession cs("", "", "", "");
+
+ const failable<value> val = http::get(postURI, cs);
+ assert(hasContent(val));
+ return true;
+}
+
+bool testPut() {
+ http::CURLSession cs("", "", "", "");
+
+ const failable<value> val = http::get(putURI, cs);
+ assert(hasContent(val));
+ return true;
+}
+
+bool testDelete() {
+ http::CURLSession cs("", "", "", "");
+
+ const failable<value> val = http::get(deleteURI, cs);
+ assert(hasContent(val));
+ return true;
+}
+
}
}
@@ -77,6 +101,9 @@ int main() {
tuscany::http::testGet();
tuscany::http::testGetPerf();
+ tuscany::http::testPost();
+ tuscany::http::testPut();
+ tuscany::http::testDelete();
tuscany::cout << "OK" << tuscany::endl;
diff --git a/sca-cpp/trunk/components/http/content-test.scm b/sca-cpp/trunk/components/http/content-test.scm
new file mode 100644
index 0000000000..f381546190
--- /dev/null
+++ b/sca-cpp/trunk/components/http/content-test.scm
@@ -0,0 +1,23 @@
+; 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.
+
+; Content test case
+
+(define (get id)
+ (list (list 'entry '(title "Item") '(id "111") '(content (item (name "Apple") (currencyCode "USD") (currencySymbol "$") (price 2.99)))))
+)
+
diff --git a/sca-cpp/trunk/components/http/http.composite b/sca-cpp/trunk/components/http/http.composite
index 43d9fb6413..48fe1c3b55 100644
--- a/sca-cpp/trunk/components/http/http.composite
+++ b/sca-cpp/trunk/components/http/http.composite
@@ -29,6 +29,41 @@
<reference name="url" target="url-test"/>
</component>
+ <component name="httppost">
+ <implementation.cpp path="." library="libhttppost"/>
+ <service name="httppost">
+ <binding.http uri="httppost"/>
+ </service>
+ <reference name="url" target="url-test"/>
+ <reference name="content" target="content-test"/>
+ </component>
+
+ <component name="httpput">
+ <implementation.cpp path="." library="libhttpput"/>
+ <service name="httpput">
+ <binding.http uri="httpput"/>
+ </service>
+ <reference name="url" target="url-test"/>
+ <reference name="content" target="content-test"/>
+ </component>
+
+ <component name="httppatch">
+ <implementation.cpp path="." library="libhttppatch"/>
+ <service name="httppatch">
+ <binding.http uri="httppatch"/>
+ </service>
+ <reference name="url" target="url-test"/>
+ <reference name="content" target="content-test"/>
+ </component>
+
+ <component name="httpdelete">
+ <implementation.cpp path="." library="libhttpdelete"/>
+ <service name="httpdelete">
+ <binding.http uri="httpdelete"/>
+ </service>
+ <reference name="url" target="url-test"/>
+ </component>
+
<component name="url-test">
<implementation.scheme script="url-test.scm"/>
<service name="url-test">
@@ -36,4 +71,18 @@
</service>
</component>
+ <component name="content-test">
+ <implementation.scheme script="content-test.scm"/>
+ <service name="content-test">
+ <binding.http uri="content-test"/>
+ </service>
+ </component>
+
+ <component name="scheme-test">
+ <implementation.scheme script="server-test.scm"/>
+ <service name="test">
+ <binding.http uri="test"/>
+ </service>
+ </component>
+
</composite>
diff --git a/sca-cpp/trunk/components/http/httppatch.componentType b/sca-cpp/trunk/components/http/httppatch.componentType
new file mode 100644
index 0000000000..8f3173be85
--- /dev/null
+++ b/sca-cpp/trunk/components/http/httppatch.componentType
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * 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.
+-->
+<componentType xmlns="http://docs.oasis-open.org/ns/opencsa/sca/200912"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:t="http://tuscany.apache.org/xmlns/sca/1.1"
+ targetNamespace="http://tuscany.apache.org/xmlns/sca/components">
+
+ <service name="http"/>
+ <reference name="url"/>
+ <reference name="content"/>
+
+</composite>
diff --git a/sca-cpp/trunk/components/http/httppatch.cpp b/sca-cpp/trunk/components/http/httppatch.cpp
new file mode 100644
index 0000000000..4debca045e
--- /dev/null
+++ b/sca-cpp/trunk/components/http/httppatch.cpp
@@ -0,0 +1,93 @@
+/*
+ * 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$ */
+
+/**
+ * HTTP client component implementation.
+ */
+
+#include "string.hpp"
+#include "function.hpp"
+#include "list.hpp"
+#include "value.hpp"
+#include "monad.hpp"
+#include "../../modules/http/http.hpp"
+
+namespace tuscany {
+namespace httppatch {
+
+/**
+ * Evaluate an HTTP patch.
+ */
+const failable<value> get(const lambda<value(const list<value>&)> url, const lambda<value(const list<value>&)> val, http::CURLSession& ch) {
+ debug("httppatch::get");
+ const value u = url(mklist<value>("get", list<value>()));
+ const value v = val(mklist<value>("get", list<value>()));
+ debug(u, "httppatch::get::url");
+ debug(v, "httppatch::get::val");
+ return http::patch(v, u, ch);
+}
+
+/**
+ * Component implementation lambda function.
+ */
+class applyhttp {
+public:
+ applyhttp(const lambda<value(const list<value>&)> url, const lambda<value(const list<value>&)> val, http::CURLSession& ch) : url(url), val(val), ch(ch) {
+ }
+
+ const value operator()(const list<value>& params) const {
+ debug(params, "httppatch::applyhttp::params");
+ const value func(car(params));
+ if (func == "get")
+ return get(url, val, ch);
+ return tuscany::mkfailure<tuscany::value>();
+ }
+
+private:
+ const lambda<value(const list<value>&)> url;
+ const lambda<value(const list<value>&)> val;
+ http::CURLSession& ch;
+};
+
+/**
+ * Start the component.
+ */
+const failable<value> start(const list<value>& params) {
+ // Create a CURL session
+ http::CURLSession& ch = *(new (gc_new<http::CURLSession>()) http::CURLSession("", "", "", ""));
+
+ // Return the component implementation lambda function
+ return value(lambda<value(const list<value>&)>(applyhttp(car(params), cadr(params), ch)));
+}
+
+}
+}
+
+extern "C" {
+
+const tuscany::value apply(const tuscany::list<tuscany::value>& params) {
+ const tuscany::value func(car(params));
+ if (func == "start")
+ return tuscany::httppatch::start(cdr(params));
+ return tuscany::mkfailure<tuscany::value>();
+}
+
+}
diff --git a/sca-cpp/trunk/components/http/httppost.componentType b/sca-cpp/trunk/components/http/httppost.componentType
new file mode 100644
index 0000000000..8f3173be85
--- /dev/null
+++ b/sca-cpp/trunk/components/http/httppost.componentType
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * 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.
+-->
+<componentType xmlns="http://docs.oasis-open.org/ns/opencsa/sca/200912"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:t="http://tuscany.apache.org/xmlns/sca/1.1"
+ targetNamespace="http://tuscany.apache.org/xmlns/sca/components">
+
+ <service name="http"/>
+ <reference name="url"/>
+ <reference name="content"/>
+
+</composite>
diff --git a/sca-cpp/trunk/components/http/httppost.cpp b/sca-cpp/trunk/components/http/httppost.cpp
new file mode 100644
index 0000000000..9784c4513c
--- /dev/null
+++ b/sca-cpp/trunk/components/http/httppost.cpp
@@ -0,0 +1,93 @@
+/*
+ * 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$ */
+
+/**
+ * HTTP client component implementation.
+ */
+
+#include "string.hpp"
+#include "function.hpp"
+#include "list.hpp"
+#include "value.hpp"
+#include "monad.hpp"
+#include "../../modules/http/http.hpp"
+
+namespace tuscany {
+namespace httppost {
+
+/**
+ * Evaluate an HTTP post.
+ */
+const failable<value> get(const lambda<value(const list<value>&)> url, const lambda<value(const list<value>&)> val, http::CURLSession& ch) {
+ debug("httppost::get");
+ const value u = url(mklist<value>("get", list<value>()));
+ const value v = val(mklist<value>("get", list<value>()));
+ debug(u, "httppost::get::url");
+ debug(v, "httppost::get::val");
+ return http::post(v, u, ch);
+}
+
+/**
+ * Component implementation lambda function.
+ */
+class applyhttp {
+public:
+ applyhttp(const lambda<value(const list<value>&)> url, const lambda<value(const list<value>&)> val, http::CURLSession& ch) : url(url), val(val), ch(ch) {
+ }
+
+ const value operator()(const list<value>& params) const {
+ debug(params, "httppost::applyhttp::params");
+ const value func(car(params));
+ if (func == "get")
+ return get(url, val, ch);
+ return tuscany::mkfailure<tuscany::value>();
+ }
+
+private:
+ const lambda<value(const list<value>&)> url;
+ const lambda<value(const list<value>&)> val;
+ http::CURLSession& ch;
+};
+
+/**
+ * Start the component.
+ */
+const failable<value> start(const list<value>& params) {
+ // Create a CURL session
+ http::CURLSession& ch = *(new (gc_new<http::CURLSession>()) http::CURLSession("", "", "", ""));
+
+ // Return the component implementation lambda function
+ return value(lambda<value(const list<value>&)>(applyhttp(car(params), cadr(params), ch)));
+}
+
+}
+}
+
+extern "C" {
+
+const tuscany::value apply(const tuscany::list<tuscany::value>& params) {
+ const tuscany::value func(car(params));
+ if (func == "start")
+ return tuscany::httppost::start(cdr(params));
+ return tuscany::mkfailure<tuscany::value>();
+}
+
+}
diff --git a/sca-cpp/trunk/components/http/httpput.componentType b/sca-cpp/trunk/components/http/httpput.componentType
new file mode 100644
index 0000000000..8f3173be85
--- /dev/null
+++ b/sca-cpp/trunk/components/http/httpput.componentType
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * 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.
+-->
+<componentType xmlns="http://docs.oasis-open.org/ns/opencsa/sca/200912"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:t="http://tuscany.apache.org/xmlns/sca/1.1"
+ targetNamespace="http://tuscany.apache.org/xmlns/sca/components">
+
+ <service name="http"/>
+ <reference name="url"/>
+ <reference name="content"/>
+
+</composite>
diff --git a/sca-cpp/trunk/components/http/httpput.cpp b/sca-cpp/trunk/components/http/httpput.cpp
new file mode 100644
index 0000000000..4ec63dabce
--- /dev/null
+++ b/sca-cpp/trunk/components/http/httpput.cpp
@@ -0,0 +1,93 @@
+/*
+ * 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$ */
+
+/**
+ * HTTP client component implementation.
+ */
+
+#include "string.hpp"
+#include "function.hpp"
+#include "list.hpp"
+#include "value.hpp"
+#include "monad.hpp"
+#include "../../modules/http/http.hpp"
+
+namespace tuscany {
+namespace httpput {
+
+/**
+ * Evaluate an HTTP put.
+ */
+const failable<value> get(const lambda<value(const list<value>&)> url, const lambda<value(const list<value>&)> val, http::CURLSession& ch) {
+ debug("httpput::get");
+ const value u = url(mklist<value>("get", list<value>()));
+ const value v = val(mklist<value>("get", list<value>()));
+ debug(u, "httpput::get::url");
+ debug(v, "httpput::get::val");
+ return http::put(v, u, ch);
+}
+
+/**
+ * Component implementation lambda function.
+ */
+class applyhttp {
+public:
+ applyhttp(const lambda<value(const list<value>&)> url, const lambda<value(const list<value>&)> val, http::CURLSession& ch) : url(url), val(val), ch(ch) {
+ }
+
+ const value operator()(const list<value>& params) const {
+ debug(params, "httpput::applyhttp::params");
+ const value func(car(params));
+ if (func == "get")
+ return get(url, val, ch);
+ return tuscany::mkfailure<tuscany::value>();
+ }
+
+private:
+ const lambda<value(const list<value>&)> url;
+ const lambda<value(const list<value>&)> val;
+ http::CURLSession& ch;
+};
+
+/**
+ * Start the component.
+ */
+const failable<value> start(const list<value>& params) {
+ // Create a CURL session
+ http::CURLSession& ch = *(new (gc_new<http::CURLSession>()) http::CURLSession("", "", "", ""));
+
+ // Return the component implementation lambda function
+ return value(lambda<value(const list<value>&)>(applyhttp(car(params), cadr(params), ch)));
+}
+
+}
+}
+
+extern "C" {
+
+const tuscany::value apply(const tuscany::list<tuscany::value>& params) {
+ const tuscany::value func(car(params));
+ if (func == "start")
+ return tuscany::httpput::start(cdr(params));
+ return tuscany::mkfailure<tuscany::value>();
+}
+
+}
diff --git a/sca-cpp/trunk/components/http/server-test.scm b/sca-cpp/trunk/components/http/server-test.scm
new file mode 100644
index 0000000000..4bbff6e5c2
--- /dev/null
+++ b/sca-cpp/trunk/components/http/server-test.scm
@@ -0,0 +1,44 @@
+; 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.
+
+; JSON-RPC test case
+
+(define (echo x) x)
+
+; ATOMPub test case
+
+(define (get id)
+ (if (nul id)
+ '((feed (title "Sample Feed") (id "123456789") (entry
+ (((title "Item") (id "111") (content (item (name "Apple") (currencyCode "USD") (currencySymbol "$") (price 2.99))))
+ ((title "Item") (id "222") (content (item (name "Orange") (currencyCode "USD") (currencySymbol "$") (price 3.55))))
+ ((title "Item") (id "333") (content (item (name "Pear") (currencyCode "USD") (currencySymbol "$") (price 1.55))))))))
+
+ (list (list 'entry '(title "Item") (list 'id (car id)) '(content (item (name "Apple") (currencyCode "USD") (currencySymbol "$") (price 2.99))))))
+)
+
+(define (post collection item)
+ '("123456789")
+)
+
+(define (put id item)
+ true
+)
+
+(define (delete id)
+ true
+)
diff --git a/sca-cpp/trunk/components/http/url-test.scm b/sca-cpp/trunk/components/http/url-test.scm
index 29e6629aa5..37a5b345b2 100644
--- a/sca-cpp/trunk/components/http/url-test.scm
+++ b/sca-cpp/trunk/components/http/url-test.scm
@@ -18,6 +18,6 @@
; URL test case
(define (get id)
- "http://localhost:8090/index.html"
+ "http://localhost:8090/test"
)
diff --git a/sca-cpp/trunk/modules/edit/apps/testhttp/app.composite b/sca-cpp/trunk/modules/edit/apps/testhttp/app.composite
index a632809760..93016ae915 100644
--- a/sca-cpp/trunk/modules/edit/apps/testhttp/app.composite
+++ b/sca-cpp/trunk/modules/edit/apps/testhttp/app.composite
@@ -1,22 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!--
- * 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.
--->
<composite xmlns="http://docs.oasis-open.org/ns/opencsa/sca/200912" name="app" targetNamespace="http://app">
<service name="call" promote="call"/>
<service name="get" promote="get"/>
@@ -25,9 +7,9 @@
<service name="get4" promote="get4"/>
<service name="get5" promote="get5"/>
<service name="get6" promote="get6"/>
- <service name="parse" promote="parse"/>
<service name="item" promote="property"/>
- <component x="36" y="287" name="call" title="call" color="green1">
+ <service name="parse" promote="parse"/>
+ <component x="20" y="300" name="call" title="call" color="green1">
<implementation.python script="nuvem/call.py"/>
<service name="call"/>
<reference target="name" name="name"/>
@@ -38,32 +20,38 @@
<service name="first"/>
<reference target="rest" name="list"/>
</component>
- <component x="20" y="8" name="get" title="get" color="green1">
+ <component x="750" y="320" name="format1" title="format" color="magenta1">
+ <implementation.python script="nuvem/format_.py"/>
+ <service name="format"/>
+ <reference target="text12" name="pattern"/>
+ <reference target="list2" name="values"/>
+ </component>
+ <component x="20" y="0" name="get" title="get" color="green1">
<implementation.cpp path="lib/http" library="libhttpget"/>
<service name="get"/>
<reference target="text" name="url"/>
</component>
- <component x="18" y="61" name="get2" title="get" color="green1">
+ <component x="20" y="70" name="get2" title="get" color="green1">
<implementation.cpp path="lib/http" library="libhttpget"/>
<service name="get"/>
<reference target="text2" name="url"/>
</component>
- <component x="14" y="112" name="get3" title="get" color="green1">
+ <component x="20" y="150" name="get3" title="get" color="green1">
<implementation.cpp path="lib/http" library="libhttpget"/>
<service name="get"/>
<reference target="text3" name="url"/>
</component>
- <component x="19" y="170" name="get4" title="get" color="green1">
+ <component x="370" y="0" name="get4" title="get" color="green1">
<implementation.cpp path="lib/http" library="libhttpget"/>
<service name="get"/>
<reference target="text4" name="url"/>
</component>
- <component x="241" y="6" name="get5" title="get" color="green1">
+ <component x="230" y="0" name="get5" title="get" color="green1">
<implementation.cpp path="lib/http" library="libhttpget"/>
<service name="get"/>
<reference target="text5" name="url"/>
</component>
- <component x="16" y="227" name="get6" title="get" color="green1">
+ <component x="20" y="220" name="get6" title="get" color="green1">
<implementation.cpp path="lib/http" library="libhttpget"/>
<service name="get"/>
<reference target="text6" name="url"/>
@@ -73,12 +61,42 @@
<service name="get"/>
<reference target="text7" name="url"/>
</component>
+ <component name="item2" title="{propval} :" color="orange1">
+ <implementation.python script="nuvem/item.py"/>
+ <service name="item"/>
+ <reference target="user" name="value"/>
+ <property>user</property>
+ </component>
+ <component name="item3" title="{propval} :" color="orange1">
+ <implementation.python script="nuvem/item.py"/>
+ <service name="item"/>
+ <reference target="params" name="value"/>
+ <property>name</property>
+ </component>
<component name="join" title="join" color="magenta1">
<implementation.python script="nuvem/join.py"/>
<service name="join"/>
<reference target="text9" name="separator"/>
<reference target="first" name="list"/>
</component>
+ <component x="570" y="350" name="keychain1" title="keychain" color="green1">
+ <implementation.python script="nuvem/keychain.py"/>
+ <service name="keychain"/>
+ <reference target="name1" name="name"/>
+ <reference name="account" target="Accounts" visible="false"/>
+ </component>
+ <component x="710" y="290" name="keychain2" title="keychain" color="green1">
+ <implementation.python script="nuvem/keychain.py"/>
+ <service name="keychain"/>
+ <reference target="name2" name="name"/>
+ <reference name="account" target="Accounts" visible="false"/>
+ </component>
+ <component x="720" y="310" name="keychain3" title="keychain" color="green1">
+ <implementation.python script="nuvem/keychain.py"/>
+ <service name="keychain"/>
+ <reference target="name3" name="name"/>
+ <reference name="account" target="Accounts" visible="false"/>
+ </component>
<component name="list" title="list" color="yellow1">
<implementation.python script="nuvem/list_.py"/>
<service name="list"/>
@@ -86,40 +104,64 @@
<reference target="item3" name="item" clonable="true"/>
<reference name="item" clonable="true"/>
</component>
+ <component name="list1" title="list" color="yellow1">
+ <implementation.python script="nuvem/list_.py"/>
+ <service name="list"/>
+ <reference target="keychain1" name="item" clonable="true"/>
+ <reference target="text10" name="item" clonable="true"/>
+ <reference name="item" clonable="true"/>
+ </component>
+ <component name="list2" title="list" color="yellow1">
+ <implementation.python script="nuvem/list_.py"/>
+ <service name="list"/>
+ <reference target="text11" name="item" clonable="true"/>
+ <reference target="text13" name="item" clonable="true"/>
+ <reference target="text14" name="item" clonable="true"/>
+ <reference name="item" clonable="true"/>
+ </component>
+ <component name="list4" title="list" color="yellow1">
+ <implementation.python script="nuvem/list_.py"/>
+ <service name="list"/>
+ <reference target="format1" name="item" clonable="true"/>
+ <reference name="item" clonable="true"/>
+ </component>
<component name="name" title="" color="orange1">
<implementation.python script="nuvem/name.py"/>
<service name="name"/>
<property>https://testhttp.sca-store.com:8453/components/property</property>
</component>
+ <component x="630" y="350" name="name1" title="" color="orange1">
+ <implementation.python script="nuvem/name.py"/>
+ <service name="name"/>
+ <property>twilliosid</property>
+ </component>
+ <component x="780" y="280" name="name2" title="" color="orange1">
+ <implementation.python script="nuvem/name.py"/>
+ <service name="name"/>
+ <property>twilliosid</property>
+ </component>
+ <component x="780" y="320" name="name3" title="" color="orange1">
+ <implementation.python script="nuvem/name.py"/>
+ <service name="name"/>
+ <property>twilliotoken</property>
+ </component>
<component name="params" title="?params" color="green1">
<implementation.python script="nuvem/params.py"/>
<service name="params"/>
<property name="query" visible="false"/>
</component>
- <component x="597" y="167" name="parse" title="parse" color="magenta1">
+ <component x="410" y="290" name="parse" title="parse" color="magenta1">
<implementation.python script="nuvem/parse.py"/>
<service name="parse"/>
<reference target="text8" name="regexp"/>
<reference target="join" name="string"/>
</component>
- <component x="74" y="343" name="property" title="{propval} :" color="orange1">
+ <component x="20" y="370" name="property" title="{propval} :" color="orange1">
<implementation.python script="nuvem/item.py"/>
<service name="item"/>
<reference target="list" name="value"/>
<property>test</property>
</component>
- <component name="item2" title="{propval} :" color="orange1">
- <implementation.python script="nuvem/item.py"/>
- <service name="item"/>
- <reference target="user" name="value"/>
- <property>user</property>
- </component>
- <component name="item3" title="{propval} :" color="orange1">
- <implementation.python script="nuvem/item.py"/>
- <service name="item"/>
- <reference target="params" name="value"/>
- <property>name</property>
- </component>
<component name="rest" title="rest" color="yellow1">
<implementation.python script="nuvem/rest.py"/>
<service name="rest"/>
@@ -130,6 +172,41 @@
<service name="text"/>
<property>http://www.google.com</property>
</component>
+ <component x="530" y="320" name="text1" title=" '{propval}'" color="orange1">
+ <implementation.python script="nuvem/text.py"/>
+ <service name="text"/>
+ <property>https://api.twilio.com/2010-04-01/Accounts/</property>
+ </component>
+ <component x="570" y="390" name="text10" title=" '{propval}'" color="orange1">
+ <implementation.python script="nuvem/text.py"/>
+ <service name="text"/>
+ <property>SMS/Messages</property>
+ </component>
+ <component x="580" y="480" name="text11" title=" '{propval}'" color="orange1">
+ <implementation.python script="nuvem/text.py"/>
+ <service name="text"/>
+ <property>+14155992671</property>
+ </component>
+ <component x="550" y="450" name="text12" title=" '{propval}'" color="orange1">
+ <implementation.python script="nuvem/text.py"/>
+ <service name="text"/>
+ <property>From={0}&amp;To={1}&amp;Body={2}</property>
+ </component>
+ <component x="700" y="340" name="text13" title=" '{propval}'" color="orange1">
+ <implementation.python script="nuvem/text.py"/>
+ <service name="text"/>
+ <property>+16508632924</property>
+ </component>
+ <component x="690" y="370" name="text14" title=" '{propval}'" color="orange1">
+ <implementation.python script="nuvem/text.py"/>
+ <service name="text"/>
+ <property>This is a text message</property>
+ </component>
+ <component x="710" y="290" name="text15" title=" '{propval}'" color="orange1">
+ <implementation.python script="nuvem/text.py"/>
+ <service name="text"/>
+ <property>application/x-www-form-urlencoded</property>
+ </component>
<component name="text2" title=" '{propval}'" color="orange1">
<implementation.python script="nuvem/text.py"/>
<service name="text"/>
diff --git a/sca-cpp/trunk/modules/edit/palettes/http/palette.composite b/sca-cpp/trunk/modules/edit/palettes/http/palette.composite
index 6b518f0a35..6dcfb9eb43 100644
--- a/sca-cpp/trunk/modules/edit/palettes/http/palette.composite
+++ b/sca-cpp/trunk/modules/edit/palettes/http/palette.composite
@@ -7,22 +7,23 @@
<service name="keychain" promote="keychain"/>
<service name="param" promote="param"/>
<service name="params" promote="params"/>
+ <service name="patch" promote="patch"/>
<service name="path" promote="path"/>
<service name="post" promote="post"/>
<service name="put" promote="put"/>
<service name="url" promote="url"/>
<service name="user" promote="user"/>
- <component x="180" y="230" name="delete" title="delete" color="green1">
+ <component x="90" y="240" name="delete" title="delete" color="green1">
<implementation.cpp path="lib/http" library="libhttpdelete"/>
<service name="delete"/>
<reference name="url"/>
</component>
- <component x="160" y="110" name="email" title="email" color="green1">
+ <component x="250" y="60" name="email" title="email" color="green1">
<implementation.python script="nuvem/email.py"/>
<service name="email"/>
<property name="email" visible="false"/>
</component>
- <component x="150" y="160" name="get" title="get" color="green1">
+ <component x="90" y="170" name="get" title="get" color="green1">
<implementation.cpp path="lib/http" library="libhttpget"/>
<service name="get"/>
<reference name="url"/>
@@ -32,47 +33,53 @@
<service name="host"/>
<property name="host" visible="false"/>
</component>
- <component x="230" y="110" name="keychain" title="keychain" color="green1">
+ <component x="90" y="110" name="keychain" title="keychain" color="green1">
<implementation.python script="nuvem/keychain.py"/>
<service name="keychain"/>
<reference name="name"/>
<reference name="account" target="Accounts" visible="false"/>
</component>
- <component x="180" y="60" name="param" title="?param" color="green1">
+ <component x="90" y="60" name="param" title="?param" color="green1">
<implementation.python script="nuvem/param.py"/>
<service name="param"/>
<property>x</property>
<property name="query" visible="false"/>
</component>
- <component x="90" y="60" name="params" title="?params" color="green1">
+ <component x="230" y="10" name="params" title="?params" color="green1">
<implementation.python script="nuvem/params.py"/>
<service name="params"/>
<property name="query" visible="false"/>
</component>
+ <component x="170" y="240" name="patch" title="patch" color="green1">
+ <implementation.cpp path="lib/http" library="libhttppatch"/>
+ <service name="patch"/>
+ <reference name="url"/>
+ <reference name="content"/>
+ </component>
<component x="160" y="10" name="path" title="path" color="green1">
<implementation.python script="nuvem/path.py"/>
<service name="path"/>
<property name="path" visible="false"/>
</component>
- <component x="90" y="230" name="post" title="post **" color="green1">
+ <component x="230" y="170" name="post" title="post" color="green1">
<implementation.cpp path="lib/http" library="libhttppost"/>
<service name="post"/>
<reference name="url"/>
<reference name="content"/>
</component>
- <component x="220" y="160" name="put" title="put **" color="green1">
+ <component x="160" y="170" name="put" title="put" color="green1">
<implementation.cpp path="lib/http" library="libhttpput"/>
<service name="put"/>
<reference name="url"/>
<reference name="content"/>
</component>
- <component x="90" y="160" name="url" title="url" color="green1">
+ <component x="190" y="110" name="url" title="url" color="green1">
<implementation.python script="nuvem/url.py"/>
<service name="url"/>
<reference name="address"/>
<reference name="args"/>
</component>
- <component x="90" y="110" name="user" title="user" color="green1">
+ <component x="180" y="60" name="user" title="user" color="green1">
<implementation.python script="nuvem/user.py"/>
<service name="user"/>
<property name="user" visible="false"/>
diff --git a/sca-cpp/trunk/modules/http/http.hpp b/sca-cpp/trunk/modules/http/http.hpp
index d4159add29..5cf174f4e1 100644
--- a/sca-cpp/trunk/modules/http/http.hpp
+++ b/sca-cpp/trunk/modules/http/http.hpp
@@ -44,6 +44,7 @@
#include "element.hpp"
#include "monad.hpp"
#include "parallel.hpp"
+#include "../scheme/io.hpp"
#include "../atom/atom.hpp"
#include "../rss/rss.hpp"
#include "../json/json.hpp"
@@ -187,6 +188,13 @@ const string escapeArg(const string& arg) {
}
/**
+ * Return true if a URI is absolute.
+ */
+const bool isAbsolute(const string& uri) {
+ return contains(uri, "://");
+}
+
+/**
* Parse a URI and return its host name.
*/
const string hostName(const string& uri, const gc_pool& p) {
@@ -200,6 +208,19 @@ const string hostName(const string& uri, const gc_pool& p) {
}
/**
+ * Parse a URI and return its scheme.
+ */
+const string scheme(const string& uri, const gc_pool& p) {
+ apr_uri_t u;
+ const apr_status_t rc = apr_uri_parse(pool(p), c_str(uri), &u);
+ if (rc != APR_SUCCESS)
+ return "";
+ if (u.scheme == NULL)
+ return "";
+ return u.scheme;
+}
+
+/**
* Return the first subdomain name in a host name.
*/
const string subDomain(const string& host) {
@@ -223,6 +244,9 @@ const failable<CURL*> setup(const string& url, const CURLSession& cs) {
CURL* ch = handle(cs);
curl_easy_reset(ch);
curl_easy_setopt(ch, CURLOPT_USERAGENT, "libcurl/1.0");
+#ifdef WANT_MAINTAINER_MODE
+ curl_easy_setopt(ch, CURLOPT_VERBOSE, true);
+#endif
// Setup protocol options
curl_easy_setopt(ch, CURLOPT_TCP_NODELAY, true);
@@ -252,6 +276,29 @@ const failable<CURL*> setup(const string& url, const CURLSession& cs) {
curl_easy_setopt(ch, CURLOPT_COOKIE, c_str(cs.cookie));
}
+ // Set up HTTP basic auth if requested
+ apr_uri_t u;
+ apr_pool_t* p = gc_current_pool();
+ const apr_status_t prc = apr_uri_parse(p, c_str(url), &u);
+ if (prc == APR_SUCCESS) {
+ if (u.user != NULL) {
+ debug(u.user, "http::setup::user");
+ curl_easy_setopt(ch, CURLOPT_USERNAME, u.user);
+ }
+ if (u.password != NULL) {
+ debug(u.password, "http::setup::pass");
+ curl_easy_setopt(ch, CURLOPT_PASSWORD, u.password);
+ }
+ if (u.user != NULL || u.password != NULL) {
+ curl_easy_setopt(ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
+
+ // Set target URL, omitting the user:password part
+ curl_easy_setopt(ch, CURLOPT_URL, c_str(escapeURI(apr_uri_unparse(p, &u, APR_URI_UNP_OMITUSERINFO))));
+
+ return ch;
+ }
+ }
+
// Set target URL
curl_easy_setopt(ch, CURLOPT_URL, c_str(escapeURI(url)));
@@ -353,6 +400,10 @@ template<typename R> const failable<list<R> > apply(const list<list<string> >& h
} else if (verb == "PUT") {
curl_easy_setopt(ch, CURLOPT_UPLOAD, true);
curl_easy_setopt(ch, CURLOPT_INFILESIZE, sz);
+ } else if (verb == "PATCH") {
+ curl_easy_setopt(ch, CURLOPT_UPLOAD, true);
+ curl_easy_setopt(ch, CURLOPT_CUSTOMREQUEST, "PATCH");
+ 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);
@@ -424,9 +475,9 @@ const failable<string> location(const list<string>& h) {
/**
* Convert a location to an entry id.
*/
-const failable<value> entryId(const failable<string> l) {
+const value entryId(const failable<string> l) {
if (!hasContent(l))
- return mkfailure<value>(reason(l));
+ return list<value>();
const string ls(content(l));
return value(mklist<value>(string(substr(ls, find_last(ls, '/') + 1))));
}
@@ -536,26 +587,107 @@ const failable<value> get(const string& url, const CURLSession& cs) {
}
/**
+ * Form an HTTP content request.
+ */
+const failable<list<list<string> > > writeRequest(const failable<list<string> >& ls, const string& ct) {
+ if (!hasContent(ls))
+ return mkfailure<list<list<string> > >(reason(ls));
+ const list<list<string> > req = mklist<list<string> >(mklist<string>(string("Content-Type: ") + ct), content(ls));
+ debug(req, "http::writeRequest::req");
+ return req;
+}
+
+/**
+ * Convert a value to an HTTP content request.
+ */
+const failable<list<list<string> > > contentRequest(const value& c, unused const string& url) {
+
+ // Check if the client requested a specific format
+ //TODO derive that from given URL
+ const list<value> fmt = assoc<value>("format", list<value>());
+
+ // Write as a scheme value if requested by the client
+ if (!isNil(fmt) && cadr(fmt) == "scheme")
+ return writeRequest(mklist<string>(scheme::writeValue(c)), "text/plain; charset=utf-8");
+
+ // Write a simple value as a JSON value
+ if (!isList(c)) {
+ js::JSContext cx;
+ if (isSymbol(c)) {
+ const list<value> lc = mklist<value>(mklist<value>("name", value(string(c))));
+ debug(lc, "http::contentRequest::symbol");
+ return writeRequest(json::writeJSON(valuesToElements(lc), cx), "application/json; charset=utf-8");
+ }
+ const list<value> lc = mklist<value>(mklist<value>("value", c));
+ debug(lc, "http::contentRequest::value");
+ return writeRequest(json::writeJSON(valuesToElements(lc), cx), "application/json; charset=utf-8");
+ }
+
+ // Write an empty list as a JSON empty value
+ if (isNil((list<value>)c)) {
+ js::JSContext cx;
+ debug(list<value>(), "http::contentRequest::empty");
+ return writeRequest(json::writeJSON(list<value>(), cx), "application/json; charset=utf-8");
+ }
+
+ // Write content-type / content-list pair
+ if (isString(car<value>(c)) && !isNil(cdr<value>(c)) && isList(cadr<value>(c)))
+ return writeRequest(convertValues<string>(cadr<value>(c)), car<value>(c));
+
+ // Write an assoc value as a JSON result
+ if (isSymbol(car<value>(c)) && !isNil(cdr<value>(c))) {
+ js::JSContext cx;
+ const list<value> lc = mklist<value>(c);
+ debug(lc, "http::contentRequest::assoc");
+ debug(valuesToElements(lc), "http::contentRequest::assoc::element");
+ return writeRequest(json::writeJSON(valuesToElements(lc), cx), "application/json; charset=utf-8");
+ }
+
+ // Write value as JSON if requested by the client
+ if (!isNil(fmt) && cadr(fmt) == "json") {
+ js::JSContext cx;
+ return writeRequest(json::writeJSON(valuesToElements(c), cx), "application/json; charset=utf-8");
+ }
+
+ // Convert list of values to element values
+ const list<value> e = valuesToElements(c);
+ debug(e, "http::contentRequest::elements");
+
+ // Write an ATOM feed or entry
+ if (isList(car<value>(e)) && !isNil(car<value>(e))) {
+ const list<value> el = car<value>(e);
+ if (isSymbol(car<value>(el)) && car<value>(el) == element && !isNil(cdr<value>(el)) && isSymbol(cadr<value>(el)) && elementHasChildren(el) && !elementHasValue(el)) {
+ if (cadr<value>(el) == atom::feed)
+ return writeRequest(atom::writeATOMFeed(e), "application/atom+xml; charset=utf-8");
+ if (cadr<value>(el) == atom::entry)
+ return writeRequest(atom::writeATOMEntry(e), "application/atom+xml; charset=utf-8");
+ }
+ }
+
+ // Write any other compound value as a JSON value
+ js::JSContext cx;
+ return writeRequest(json::writeJSON(e, cx), "application/json; charset=utf-8");
+}
+
+/**
* HTTP POST.
*/
const failable<value> post(const value& val, const string& url, const CURLSession& cs) {
-
- // Convert value to an ATOM entry
- const failable<list<string> > entry = atom::writeATOMEntry(valuesToElements(val));
- if (!hasContent(entry))
- return mkfailure<value>(reason(entry));
debug(url, "http::post::url");
- debug(content(entry), "http::post::input");
+
+ // Convert value to a content request
+ const failable<list<list<string> > > req = contentRequest(val, url);
+ if (!hasContent(req))
+ return mkfailure<value>(reason(req));
+ debug(content(req), "http::post::input");
// POST it to the URL
- const list<string> h = mklist<string>("Content-Type: application/atom+xml");
- const list<list<string> > req = mklist<list<string> >(h, content(entry));
- const failable<list<list<string> > > res = apply<list<string>>(req, rcons<string>, list<string>(), url, "POST", cs);
+ const failable<list<list<string> > > res = apply<list<string>>(content(req), rcons<string>, list<string>(), url, "POST", cs);
if (!hasContent(res))
return mkfailure<value>(reason(res));
- // Return the new entry id from the HTTP location header
- const failable<value> eid(entryId(location(car(content(res)))));
+ // Return the new entry id from the HTTP location header, if any
+ const value eid(entryId(location(car(content(res)))));
debug(eid, "http::post::result");
return eid;
}
@@ -564,18 +696,16 @@ const failable<value> post(const value& val, const string& url, const CURLSessio
* HTTP PUT.
*/
const failable<value> put(const value& val, const string& url, const CURLSession& cs) {
-
- // Convert value to an ATOM entry
- const failable<list<string> > entry = atom::writeATOMEntry(valuesToElements(val));
- if (!hasContent(entry))
- return mkfailure<value>(reason(entry));
debug(url, "http::put::url");
- debug(content(entry), "http::put::input");
+
+ // Convert value to a content request
+ const failable<list<list<string> > > req = contentRequest(val, url);
+ if (!hasContent(req))
+ return mkfailure<value>(reason(req));
+ debug(content(req), "http::put::input");
// PUT it to the URL
- const list<string> h = mklist<string>("Content-Type: application/atom+xml");
- const list<list<string> > req = mklist<list<string> >(h, content(entry));
- const failable<list<list<string> > > res = apply<list<string> >(req, rcons<string>, list<string>(), url, "PUT", cs);
+ const failable<list<list<string> > > res = apply<list<string> >(content(req), rcons<string>, list<string>(), url, "PUT", cs);
if (!hasContent(res))
return mkfailure<value>(reason(res));
@@ -584,6 +714,27 @@ const failable<value> put(const value& val, const string& url, const CURLSession
}
/**
+ * HTTP PATCH.
+ */
+const failable<value> patch(const value& val, const string& url, const CURLSession& cs) {
+ debug(url, "http::put::patch");
+
+ // Convert value to a content request
+ const failable<list<list<string> > > req = contentRequest(val, url);
+ if (!hasContent(req))
+ return mkfailure<value>(reason(req));
+ debug(content(req), "http::patch::input");
+
+ // PATCH it to the URL
+ const failable<list<list<string> > > res = apply<list<string> >(content(req), rcons<string>, list<string>(), url, "PATCH", cs);
+ if (!hasContent(res))
+ return mkfailure<value>(reason(res));
+
+ debug(true, "http::patch::result");
+ return value(true);
+}
+
+/**
* HTTP DELETE.
*/
const failable<value, string> del(const string& url, const CURLSession& cs) {
@@ -778,6 +929,10 @@ struct proxy {
const failable<value> val = put(caddr(args), uri + path(cadr(args)), cs);
return content(val);
}
+ if (fun == "patch") {
+ const failable<value> val = patch(caddr(args), uri + path(cadr(args)), cs);
+ return content(val);
+ }
if (fun == "delete") {
const failable<value> val = del(uri + path(cadr(args)), cs);
return content(val);
diff --git a/sca-cpp/trunk/modules/http/httpd.hpp b/sca-cpp/trunk/modules/http/httpd.hpp
index 534fd78503..9440fe343e 100644
--- a/sca-cpp/trunk/modules/http/httpd.hpp
+++ b/sca-cpp/trunk/modules/http/httpd.hpp
@@ -152,13 +152,6 @@ const bool isVirtualHostRequest(const server_rec* s, request_rec* r) {
}
/**
- * Return true if a URI is absolute.
- */
-const bool isAbsolute(const string& uri) {
- return contains(uri, "://");
-}
-
-/**
* Return the protocol scheme for a server.
*/
const string scheme(const server_rec* s, const string& def = "http") {
@@ -173,6 +166,20 @@ const string scheme(request_rec* r, const string& def = "http") {
}
/**
+ * Return the port number for a server.
+ */
+const int port(const server_rec* s, const int def = 80) {
+ return s->port != 0? s->port : def;
+}
+
+/**
+ * Return the port number from an HTTP request.
+ */
+const int port(request_rec* r, const int def = 80) {
+ return r->server->port != 0? r->server->port : def;
+}
+
+/**
* Return the content type of a request.
*/
const string contentType(const request_rec* r) {
diff --git a/sca-cpp/trunk/modules/server/mod-eval.hpp b/sca-cpp/trunk/modules/server/mod-eval.hpp
index 30d45baac4..b9b11e06dc 100644
--- a/sca-cpp/trunk/modules/server/mod-eval.hpp
+++ b/sca-cpp/trunk/modules/server/mod-eval.hpp
@@ -389,12 +389,22 @@ public:
debug(uri, "modeval::implProxy::wiredByImpl::uri");
debug(aparams, "modeval::implProxy::wiredByImpl::input");
- // Use an HTTP proxy if the target is an absolute http:// target
- if (httpd::isAbsolute(uri)) {
+ // Use an HTTP proxy if the target is an absolute :// target
+ if (http::isAbsolute(uri)) {
gc_pool p(currentRequest->pool);
+
+ // Interpret a uri in the form app://appname, convert it using the scheme,
+ // top level domain and port number from the current request
+ if (http::scheme(uri, p) == "app") {
+ ostringstream appuri;
+ appuri << httpd::scheme(currentRequest) << "://" << substr(uri, 6) << "." << http::topDomain(httpd::hostName(currentRequest)) << ":" << httpd::port(currentRequest) << "/";
+ debug(str(appuri), "modeval::implProxy::httpproxy::appuri");
+ const lambda<value(const list<value>&)> px = lambda<value(const list<value>&)>(http::proxy(str(appuri), sc.ca, sc.cert, sc.key, httpd::cookie(currentRequest), p));
+ return px(aparams);
+ }
// Pass our certificate and the cookie from the current request
- // if the target is in the same domain
+ // only if the target is in the same top level domain
if (http::topDomain(http::hostName(uri, p)) == http::topDomain(httpd::hostName(currentRequest))) {
debug(uri, "modeval::implProxy::httpproxy::samedomain");
const lambda<value(const list<value>&)> px = lambda<value(const list<value>&)>(http::proxy(uri, sc.ca, sc.cert, sc.key, httpd::cookie(currentRequest), p));
@@ -474,7 +484,7 @@ const value mkrefProxy(const ServerConf& sc, const value& ref, unused const stri
return mkimplProxy(sc, value());
if (isNil(target))
return mkunwiredProxy(scdl::name(ref));
- if (httpd::isAbsolute(target))
+ if (http::isAbsolute(target))
return mkhttpProxy(sc, target);
return mkimplProxy(sc, car(pathValues(target)));
}
diff --git a/sca-cpp/trunk/modules/server/mod-wiring.cpp b/sca-cpp/trunk/modules/server/mod-wiring.cpp
index b1a8047525..e56cee1d8c 100644
--- a/sca-cpp/trunk/modules/server/mod-wiring.cpp
+++ b/sca-cpp/trunk/modules/server/mod-wiring.cpp
@@ -106,11 +106,10 @@ int translateReference(const ServerConf& sc, request_rec *r) {
// Route to an absolute target URI using mod_proxy or an HTTP client redirect
const list<value> pathInfo = cdddr(rpath);
- if (httpd::isAbsolute(target)) {
+ if (http::isAbsolute(target)) {
if (useModProxy) {
// Build proxy URI
- // current request's protocol scheme, reference target uri and request path info
- string turi = httpd::scheme(r) + substr(target, find(target, "://")) + path(pathInfo) + (r->args != NULL? string("?") + r->args : string(""));
+ string turi = target + path(pathInfo) + (r->args != NULL? string("?") + r->args : string(""));
r->filename = apr_pstrdup(r->pool, c_str(string("proxy:") + turi));
debug(r->filename, "modwiring::translateReference::filename");
r->proxyreq = PROXYREQ_REVERSE;