You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by da...@apache.org on 2009/05/13 05:33:35 UTC

svn commit: r774180 - in /couchdb/trunk: share/Makefile.am share/www/script/couch_tests.js share/www/script/test/jsonp.js src/couchdb/couch_db.hrl src/couchdb/couch_httpd.erl src/couchdb/couch_httpd_view.erl

Author: davisp
Date: Wed May 13 03:33:31 2009
New Revision: 774180

URL: http://svn.apache.org/viewvc?rev=774180&view=rev
Log:
Closes COUCHDB-334 - Add JSONP support to CouchDB

Use JSONP by providing a ?callback=function_name URL paramter for any URL
returning JSON data.


Added:
    couchdb/trunk/share/www/script/test/jsonp.js
Modified:
    couchdb/trunk/share/Makefile.am
    couchdb/trunk/share/www/script/couch_tests.js
    couchdb/trunk/src/couchdb/couch_db.hrl
    couchdb/trunk/src/couchdb/couch_httpd.erl
    couchdb/trunk/src/couchdb/couch_httpd_view.erl

Modified: couchdb/trunk/share/Makefile.am
URL: http://svn.apache.org/viewvc/couchdb/trunk/share/Makefile.am?rev=774180&r1=774179&r2=774180&view=diff
==============================================================================
--- couchdb/trunk/share/Makefile.am (original)
+++ couchdb/trunk/share/Makefile.am Wed May 13 03:33:31 2009
@@ -107,6 +107,7 @@
     www/script/test/multiple_rows.js \
     www/script/test/large_docs.js \
     www/script/test/utf8.js \
+    www/script/test/jsonp.js \
     www/script/test/attachments.js \
     www/script/test/attachment_paths.js \
     www/script/test/attachment_views.js \

Modified: couchdb/trunk/share/www/script/couch_tests.js
URL: http://svn.apache.org/viewvc/couchdb/trunk/share/www/script/couch_tests.js?rev=774180&r1=774179&r2=774180&view=diff
==============================================================================
--- couchdb/trunk/share/www/script/couch_tests.js [utf-8] (original)
+++ couchdb/trunk/share/www/script/couch_tests.js [utf-8] Wed May 13 03:33:31 2009
@@ -42,6 +42,7 @@
 loadTest("multiple_rows.js");
 loadTest("large_docs.js");
 loadTest("utf8.js");
+loadTest("jsonp.js");
 loadTest("attachments.js");
 loadTest("attachment_names.js");
 loadTest("attachment_paths.js");

Added: couchdb/trunk/share/www/script/test/jsonp.js
URL: http://svn.apache.org/viewvc/couchdb/trunk/share/www/script/test/jsonp.js?rev=774180&view=auto
==============================================================================
--- couchdb/trunk/share/www/script/test/jsonp.js (added)
+++ couchdb/trunk/share/www/script/test/jsonp.js Wed May 13 03:33:31 2009
@@ -0,0 +1,69 @@
+// Licensed 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.
+
+// Verify callbacks ran
+var jsonp_flag = 0;
+
+// Callbacks
+function jsonp_no_chunk(doc) {
+  T(jsonp_flag == 0);
+  T(doc._id == "0");
+  jsonp_flag = 1;
+}
+
+function jsonp_chunk(doc) {
+  T(jsonp_flag == 0);
+  T(doc.total_rows == 1);
+  jsonp_flag = 1;
+}
+
+// Do some jsonp tests.
+couchTests.jsonp = function(debug) {
+  var db = new CouchDB("test_suite_db");
+  db.deleteDb();
+  db.createDb();
+  if (debug) debugger;
+
+  var doc = {_id:"0",a:0,b:0};
+  T(db.save(doc).ok);
+
+  // Test unchunked callbacks.
+  var xhr = CouchDB.request("GET", "/test_suite_db/0?callback=jsonp_no_chunk");
+  T(xhr.status == 200);
+  jsonp_flag = 0;
+  eval(xhr.responseText);
+  T(jsonp_flag == 1);
+  xhr = CouchDB.request("GET", "/test_suite_db/0?callback=foo\"");
+  T(xhr.status == 400);
+
+  // Test chunked responses
+  var doc = {_id:"1",a:1,b:1};
+  T(db.save(doc).ok);
+
+  var designDoc = {
+    _id:"_design/test",
+    language: "javascript",
+    views: {
+      all_docs: {map: "function(doc) {if(doc.a) emit(null, doc.a);}"}
+    }
+  }
+  T(db.save(designDoc).ok);
+
+  var url = "/test_suite_db/_design/test/_view/all_docs?callback=jsonp_chunk";
+  xhr = CouchDB.request("GET", url);
+  T(xhr.status == 200);
+  jsonp_flag = 0;
+  eval(xhr.responseText);
+  T(jsonp_flag == 1);
+  xhr = CouchDB.request("GET", url + "\'");
+  T(xhr.status == 400);
+};

Modified: couchdb/trunk/src/couchdb/couch_db.hrl
URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_db.hrl?rev=774180&r1=774179&r2=774180&view=diff
==============================================================================
--- couchdb/trunk/src/couchdb/couch_db.hrl (original)
+++ couchdb/trunk/src/couchdb/couch_db.hrl Wed May 13 03:33:31 2009
@@ -167,7 +167,8 @@
     include_docs = false,
     stale = false,
     multi_get = false,
-    ignore = false
+    ignore = false,
+    callback = nil
 }).
 
 -record(view_fold_helper_funs, {

Modified: couchdb/trunk/src/couchdb/couch_httpd.erl
URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_httpd.erl?rev=774180&r1=774179&r2=774180&view=diff
==============================================================================
--- couchdb/trunk/src/couchdb/couch_httpd.erl (original)
+++ couchdb/trunk/src/couchdb/couch_httpd.erl Wed May 13 03:33:31 2009
@@ -386,8 +386,10 @@
         {"Content-Type", negotiate_content_type(Req)},
         {"Cache-Control", "must-revalidate"}
     ],
-    send_response(Req, Code, DefaultHeaders ++ Headers,
-                  list_to_binary([?JSON_ENCODE(Value), $\n])).
+    Body = list_to_binary(
+        [start_jsonp(Req), ?JSON_ENCODE(Value), end_jsonp(), $\n]
+    ),
+    send_response(Req, Code, DefaultHeaders ++ Headers, Body).
 
 start_json_response(Req, Code) ->
     start_json_response(Req, Code, []).
@@ -397,12 +399,66 @@
         {"Content-Type", negotiate_content_type(Req)},
         {"Cache-Control", "must-revalidate"}
     ],
-    start_chunked_response(Req, Code, DefaultHeaders ++ Headers).
+    start_jsonp(Req), % Validate before starting chunked.
+    %start_chunked_response(Req, Code, DefaultHeaders ++ Headers).
+    {ok, Resp} = start_chunked_response(Req, Code, DefaultHeaders ++ Headers),
+    case start_jsonp(Req) of
+        [] -> ok;
+        Start -> send_chunk(Resp, Start)
+    end,
+    {ok, Resp}.
 
 end_json_response(Resp) ->
-    send_chunk(Resp, [$\n]),
+    send_chunk(Resp, end_jsonp() ++ [$\n]),
+    %send_chunk(Resp, [$\n]),
     send_chunk(Resp, []).
 
+start_jsonp(Req) ->
+    case get(jsonp) of
+        undefined -> put(jsonp, qs_value(Req, "callback", no_jsonp));
+        _ -> ok
+    end,
+    case get(jsonp) of
+        no_jsonp -> [];
+        [] -> [];
+        CallBack ->
+            try
+                validate_callback(CallBack),
+                CallBack ++ "("
+            catch
+                Error ->
+                    put(jsonp, no_jsonp),
+                    throw(Error)
+            end
+    end.
+
+end_jsonp() ->
+    Resp = case get(jsonp) of
+        no_jsonp -> [];
+        [] -> [];
+        _ -> ");"
+    end,
+    put(jsonp, undefined),
+    Resp.
+        
+validate_callback(CallBack) when is_binary(CallBack) ->
+    validate_callback(binary_to_list(CallBack));
+validate_callback([]) ->
+    ok;
+validate_callback([Char | Rest]) ->
+    case Char of
+        _ when Char >= $a andalso Char =< $z -> ok;
+        _ when Char >= $A andalso Char =< $Z -> ok;
+        _ when Char >= $0 andalso Char =< $9 -> ok;
+        _ when Char == $. -> ok;
+        _ when Char == $_ -> ok;
+        _ when Char == $[ -> ok;
+        _ when Char == $] -> ok;
+        _ ->
+            throw({bad_request, invalid_callback})
+    end,
+    validate_callback(Rest).
+
 
 error_info({Error, Reason}) when is_list(Reason) ->
     error_info({Error, ?l2b(Reason)});

Modified: couchdb/trunk/src/couchdb/couch_httpd_view.erl
URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_httpd_view.erl?rev=774180&r1=774179&r2=774180&view=diff
==============================================================================
--- couchdb/trunk/src/couchdb/couch_httpd_view.erl (original)
+++ couchdb/trunk/src/couchdb/couch_httpd_view.erl Wed May 13 03:33:31 2009
@@ -270,6 +270,8 @@
     [{reduce, parse_bool_param(Value)}];
 parse_view_param("include_docs", Value) ->
     [{include_docs, parse_bool_param(Value)}];
+parse_view_param("callback", _) ->
+    []; % Verified in the JSON response functions
 parse_view_param(Key, Value) ->
     [{extra, {Key, Value}}].