You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by jc...@apache.org on 2010/06/24 07:21:31 UTC
svn commit: r957422 - in /couchdb/trunk: share/www/script/
share/www/script/test/ src/couchdb/
Author: jchris
Date: Thu Jun 24 05:21:30 2010
New Revision: 957422
URL: http://svn.apache.org/viewvc?rev=957422&view=rev
Log:
use JSON content type in replicator, require it in the _bulk_docs and other POST apis
Modified:
couchdb/trunk/share/www/script/couch.js
couchdb/trunk/share/www/script/test/basics.js
couchdb/trunk/share/www/script/test/batch_save.js
couchdb/trunk/share/www/script/test/stats.js
couchdb/trunk/src/couchdb/couch_httpd.erl
couchdb/trunk/src/couchdb/couch_httpd_auth.erl
couchdb/trunk/src/couchdb/couch_httpd_db.erl
couchdb/trunk/src/couchdb/couch_httpd_show.erl
couchdb/trunk/src/couchdb/couch_rep.erl
couchdb/trunk/src/couchdb/couch_rep_writer.erl
couchdb/trunk/src/couchdb/couch_util.erl
Modified: couchdb/trunk/share/www/script/couch.js
URL: http://svn.apache.org/viewvc/couchdb/trunk/share/www/script/couch.js?rev=957422&r1=957421&r2=957422&view=diff
==============================================================================
--- couchdb/trunk/share/www/script/couch.js [utf-8] (original)
+++ couchdb/trunk/share/www/script/couch.js [utf-8] Thu Jun 24 05:21:30 2010
@@ -398,6 +398,8 @@ CouchDB.newXhr = function() {
CouchDB.request = function(method, uri, options) {
options = options || {};
+ options.headers = options.headers || {};
+ options.headers["Content-Type"] = options.headers["Content-Type"] || "application/json";
var req = CouchDB.newXhr();
if(uri.substr(0, "http://".length) != "http://") {
uri = CouchDB.urlPrefix + uri
Modified: couchdb/trunk/share/www/script/test/basics.js
URL: http://svn.apache.org/viewvc/couchdb/trunk/share/www/script/test/basics.js?rev=957422&r1=957421&r2=957422&view=diff
==============================================================================
--- couchdb/trunk/share/www/script/test/basics.js (original)
+++ couchdb/trunk/share/www/script/test/basics.js Thu Jun 24 05:21:30 2010
@@ -152,7 +152,8 @@ couchTests.basics = function(debug) {
// test that the POST response has a Location header
var xhr = CouchDB.request("POST", "/test_suite_db", {
- body: JSON.stringify({"foo":"bar"})
+ body: JSON.stringify({"foo":"bar"}),
+ headers: {"Content-Type": "application/json"}
});
var resp = JSON.parse(xhr.responseText);
T(resp.ok);
@@ -164,6 +165,7 @@ couchTests.basics = function(debug) {
// test that that POST's with an _id aren't overriden with a UUID.
var xhr = CouchDB.request("POST", "/test_suite_db", {
+ headers: {"Content-Type": "application/json"},
body: JSON.stringify({"_id": "oppossum", "yar": "matey"})
});
var resp = JSON.parse(xhr.responseText);
@@ -202,7 +204,10 @@ couchTests.basics = function(debug) {
result = JSON.parse(xhr.responseText);
T(result.error == "doc_validation");
- xhr = CouchDB.request("POST", "/test_suite_db/", {body: data});
+ xhr = CouchDB.request("POST", "/test_suite_db/", {
+ headers: {"Content-Type": "application/json"},
+ body: data
+ });
T(xhr.status == 500);
result = JSON.parse(xhr.responseText);
T(result.error == "doc_validation");
Modified: couchdb/trunk/share/www/script/test/batch_save.js
URL: http://svn.apache.org/viewvc/couchdb/trunk/share/www/script/test/batch_save.js?rev=957422&r1=957421&r2=957422&view=diff
==============================================================================
--- couchdb/trunk/share/www/script/test/batch_save.js (original)
+++ couchdb/trunk/share/www/script/test/batch_save.js Thu Jun 24 05:21:30 2010
@@ -36,7 +36,10 @@ couchTests.batch_save = function(debug)
// repeat the tests for POST
for(i=0; i < 100; i++) {
- var resp = db.request("POST", db.uri + "?batch=ok", {body: JSON.stringify({a:1})});
+ var resp = db.request("POST", db.uri + "?batch=ok", {
+ headers: {"Content-Type": "application/json"},
+ body: JSON.stringify({a:1})
+ });
T(JSON.parse(resp.responseText).ok);
}
Modified: couchdb/trunk/share/www/script/test/stats.js
URL: http://svn.apache.org/viewvc/couchdb/trunk/share/www/script/test/stats.js?rev=957422&r1=957421&r2=957422&view=diff
==============================================================================
--- couchdb/trunk/share/www/script/test/stats.js (original)
+++ couchdb/trunk/share/www/script/test/stats.js Thu Jun 24 05:21:30 2010
@@ -160,7 +160,10 @@ couchTests.stats = function(debug) {
runTest("couchdb", "database_writes", {
run: function(db) {
- CouchDB.request("POST", "/test_suite_db", {body: '{"a": "1"}'})
+ CouchDB.request("POST", "/test_suite_db", {
+ headers: {"Content-Type": "application/json"},
+ body: '{"a": "1"}'
+ })
},
test: function(before, after) {
TEquals(before+1, after, "POST'ing new docs increments doc writes.");
Modified: couchdb/trunk/src/couchdb/couch_httpd.erl
URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_httpd.erl?rev=957422&r1=957421&r2=957422&view=diff
==============================================================================
--- couchdb/trunk/src/couchdb/couch_httpd.erl (original)
+++ couchdb/trunk/src/couchdb/couch_httpd.erl Thu Jun 24 05:21:30 2010
@@ -25,7 +25,7 @@
-export([start_json_response/2, start_json_response/3, end_json_response/1]).
-export([send_response/4,send_method_not_allowed/2,send_error/4, send_redirect/2,send_chunked_error/2]).
-export([send_json/2,send_json/3,send_json/4,last_chunk/1,parse_multipart_request/3]).
--export([accepted_encodings/1,handle_request_int/5]).
+-export([accepted_encodings/1,handle_request_int/5,validate_referer/1,validate_ctype/2]).
start_link() ->
% read config and register for configuration changes
@@ -321,6 +321,34 @@ vhost_global(VhostGlobals, MochiReq) ->
end,
[true] == [true||V <- VhostGlobals, V == Front].
+validate_referer(Req) ->
+ Host = host_for_request(Req),
+ Referer = header_value(Req, "Referer", fail),
+ case Referer of
+ fail ->
+ throw({bad_request, <<"Referer header required.">>});
+ Referer ->
+ {_,RefererHost,_,_,_} = mochiweb_util:urlsplit(Referer),
+ if
+ RefererHost =:= Host -> ok;
+ true -> throw({bad_request, <<"Referer header must match host.">>})
+ end
+ end.
+
+validate_ctype(Req, Ctype) ->
+ case couch_httpd:header_value(Req, "Content-Type") of
+ undefined ->
+ throw({bad_ctype, "Content-Type must be "++Ctype});
+ ReqCtype ->
+ % ?LOG_ERROR("Ctype ~p ReqCtype ~p",[Ctype,ReqCtype]),
+ case re:split(ReqCtype, ";", [{return, list}]) of
+ [Ctype] -> ok;
+ [Ctype, _Rest] -> ok;
+ _Else ->
+ throw({bad_ctype, "Content-Type must be "++Ctype})
+ end
+ end.
+
% Utilities
partition(Path) ->
@@ -367,9 +395,9 @@ qs(#httpd{mochi_req=MochiReq}) ->
path(#httpd{mochi_req=MochiReq}) ->
MochiReq:get(path).
-absolute_uri(#httpd{mochi_req=MochiReq}, Path) ->
+host_for_request(#httpd{mochi_req=MochiReq}) ->
XHost = couch_config:get("httpd", "x_forwarded_host", "X-Forwarded-Host"),
- Host = case MochiReq:get_header_value(XHost) of
+ case MochiReq:get_header_value(XHost) of
undefined ->
case MochiReq:get_header_value("Host") of
undefined ->
@@ -379,7 +407,10 @@ absolute_uri(#httpd{mochi_req=MochiReq},
Value1
end;
Value -> Value
- end,
+ end.
+
+absolute_uri(#httpd{mochi_req=MochiReq}=Req, Path) ->
+ Host = host_for_request(Req),
XSsl = couch_config:get("httpd", "x_forwarded_ssl", "X-Forwarded-Ssl"),
Scheme = case MochiReq:get_header_value(XSsl) of
"on" -> "https";
Modified: couchdb/trunk/src/couchdb/couch_httpd_auth.erl
URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_httpd_auth.erl?rev=957422&r1=957421&r2=957422&view=diff
==============================================================================
--- couchdb/trunk/src/couchdb/couch_httpd_auth.erl (original)
+++ couchdb/trunk/src/couchdb/couch_httpd_auth.erl Thu Jun 24 05:21:30 2010
@@ -251,6 +251,7 @@ ensure_cookie_auth_secret() ->
handle_session_req(#httpd{method='POST', mochi_req=MochiReq}=Req) ->
ReqBody = MochiReq:recv_body(),
Form = case MochiReq:get_primary_header_value("content-type") of
+ % content type should be json
"application/x-www-form-urlencoded" ++ _ ->
mochiweb_util:parse_qs(ReqBody);
_ ->
Modified: couchdb/trunk/src/couchdb/couch_httpd_db.erl
URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_httpd_db.erl?rev=957422&r1=957421&r2=957422&view=diff
==============================================================================
--- couchdb/trunk/src/couchdb/couch_httpd_db.erl (original)
+++ couchdb/trunk/src/couchdb/couch_httpd_db.erl Thu Jun 24 05:21:30 2010
@@ -112,6 +112,7 @@ handle_changes_req(#httpd{path_parts=[_,
send_method_not_allowed(Req, "GET,HEAD").
handle_compact_req(#httpd{method='POST',path_parts=[DbName,_,Id|_]}=Req, _Db) ->
+ couch_httpd:validate_ctype(Req, "application/json"),
ok = couch_view_compactor:start_compact(DbName, Id),
send_json(Req, 202, {[{ok, true}]});
@@ -195,6 +196,7 @@ db_req(#httpd{method='GET',path_parts=[_
send_json(Req, {DbInfo});
db_req(#httpd{method='POST',path_parts=[DbName]}=Req, Db) ->
+ couch_httpd:validate_ctype(Req, "application/json"),
Doc = couch_doc:from_json_obj(couch_httpd:json_body(Req)),
Doc2 = case Doc#doc.id of
<<"">> ->
@@ -262,6 +264,7 @@ db_req(#httpd{path_parts=[_,<<"_ensure_f
db_req(#httpd{method='POST',path_parts=[_,<<"_bulk_docs">>]}=Req, Db) ->
couch_stats_collector:increment({httpd, bulk_requests}),
+ couch_httpd:validate_ctype(Req, "application/json"),
{JsonProps} = couch_httpd:json_body_obj(Req),
DocsArray = couch_util:get_value(<<"docs">>, JsonProps),
case couch_httpd:header_value(Req, "X-Couch-Full-Commit") of
@@ -323,6 +326,7 @@ db_req(#httpd{path_parts=[_,<<"_bulk_doc
send_method_not_allowed(Req, "POST");
db_req(#httpd{method='POST',path_parts=[_,<<"_purge">>]}=Req, Db) ->
+ couch_httpd:validate_ctype(Req, "application/json"),
{IdsRevs} = couch_httpd:json_body_obj(Req),
IdsRevs2 = [{Id, couch_doc:parse_revs(Revs)} || {Id, Revs} <- IdsRevs],
@@ -367,7 +371,6 @@ db_req(#httpd{method='POST',path_parts=[
db_req(#httpd{path_parts=[_,<<"_missing_revs">>]}=Req, _Db) ->
send_method_not_allowed(Req, "POST");
-
db_req(#httpd{method='POST',path_parts=[_,<<"_revs_diff">>]}=Req, Db) ->
{JsonDocIdRevs} = couch_httpd:json_body_obj(Req),
JsonDocIdRevs2 =
@@ -586,14 +589,11 @@ db_doc_req(#httpd{method='GET'}=Req, Db,
end
end;
+
db_doc_req(#httpd{method='POST'}=Req, Db, DocId) ->
+ couch_httpd:validate_referer(Req),
couch_doc:validate_docid(DocId),
- case couch_httpd:header_value(Req, "Content-Type") of
- "multipart/form-data" ++ _Rest ->
- ok;
- _Else ->
- throw({bad_ctype, <<"Invalid Content-Type header for form upload">>})
- end,
+ couch_httpd:validate_ctype(Req, "multipart/form-data"),
Form = couch_httpd:parse_form(Req),
case proplists:is_defined("_doc", Form) of
true ->
Modified: couchdb/trunk/src/couchdb/couch_httpd_show.erl
URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_httpd_show.erl?rev=957422&r1=957421&r2=957422&view=diff
==============================================================================
--- couchdb/trunk/src/couchdb/couch_httpd_show.erl (original)
+++ couchdb/trunk/src/couchdb/couch_httpd_show.erl Thu Jun 24 05:21:30 2010
@@ -145,7 +145,7 @@ send_doc_update_response(Req, Db, DDoc,
{200, JsonResp}
end,
- JsonResp2 = json_apply_field({<<"code">>, Code}, JsonResp1),
+ JsonResp2 = couch_util:json_apply_field({<<"code">>, Code}, JsonResp1),
% todo set location field
couch_httpd_external:send_external_response(Req, JsonResp2).
@@ -376,21 +376,6 @@ render_head_for_empty_list(StartListResp
render_head_for_empty_list(StartListRespFun, Req, Etag, CurrentSeq, TotalRows) ->
StartListRespFun(Req, Etag, TotalRows, null, [], CurrentSeq).
-
-% Maybe this is in the proplists API
-% todo move to couch_util
-json_apply_field(H, {L}) ->
- json_apply_field(H, L, []).
-json_apply_field({Key, NewValue}, [{Key, _OldVal} | Headers], Acc) ->
- % drop matching keys
- json_apply_field({Key, NewValue}, Headers, Acc);
-json_apply_field({Key, NewValue}, [{OtherKey, OtherVal} | Headers], Acc) ->
- % something else is next, leave it alone.
- json_apply_field({Key, NewValue}, Headers, [{OtherKey, OtherVal} | Acc]);
-json_apply_field({Key, NewValue}, [], Acc) ->
- % end of list, add ours
- {[{Key, NewValue}|Acc]}.
-
apply_etag({ExternalResponse}, CurrentEtag) ->
% Here we embark on the delicate task of replacing or creating the
% headers on the JsonResponse object. We need to control the Etag and
@@ -404,8 +389,8 @@ apply_etag({ExternalResponse}, CurrentEt
JsonHeaders ->
{[case Field of
{<<"headers">>, JsonHeaders} -> % add our headers
- JsonHeadersEtagged = json_apply_field({<<"Etag">>, CurrentEtag}, JsonHeaders),
- JsonHeadersVaried = json_apply_field({<<"Vary">>, <<"Accept">>}, JsonHeadersEtagged),
+ JsonHeadersEtagged = couch_util:json_apply_field({<<"Etag">>, CurrentEtag}, JsonHeaders),
+ JsonHeadersVaried = couch_util:json_apply_field({<<"Vary">>, <<"Accept">>}, JsonHeadersEtagged),
{<<"headers">>, JsonHeadersVaried};
_ -> % skip non-header fields
Field
Modified: couchdb/trunk/src/couchdb/couch_rep.erl
URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_rep.erl?rev=957422&r1=957421&r2=957422&view=diff
==============================================================================
--- couchdb/trunk/src/couchdb/couch_rep.erl (original)
+++ couchdb/trunk/src/couchdb/couch_rep.erl Thu Jun 24 05:21:30 2010
@@ -653,10 +653,11 @@ commit_to_both(Source, Target, RequiredS
end,
{SourceStartTime, TargetStartTime}.
-ensure_full_commit(#http_db{} = Target) ->
+ensure_full_commit(#http_db{headers = Headers} = Target) ->
Req = Target#http_db{
resource = "_ensure_full_commit",
- method = post
+ method = post,
+ headers = [{"content-type", "application/json"} | Headers]
},
{ResultProps} = couch_rep_httpc:request(Req),
true = couch_util:get_value(<<"ok">>, ResultProps),
@@ -677,11 +678,12 @@ ensure_full_commit(Target) ->
InstanceStartTime
end.
-ensure_full_commit(#http_db{} = Source, RequiredSeq) ->
+ensure_full_commit(#http_db{headers = Headers} = Source, RequiredSeq) ->
Req = Source#http_db{
resource = "_ensure_full_commit",
method = post,
- qs = [{seq, RequiredSeq}]
+ qs = [{seq, RequiredSeq}],
+ headers = [{"content-type", "application/json"} | Headers]
},
{ResultProps} = couch_rep_httpc:request(Req),
case couch_util:get_value(<<"ok">>, ResultProps) of
Modified: couchdb/trunk/src/couchdb/couch_rep_writer.erl
URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_rep_writer.erl?rev=957422&r1=957421&r2=957422&view=diff
==============================================================================
--- couchdb/trunk/src/couchdb/couch_rep_writer.erl (original)
+++ couchdb/trunk/src/couchdb/couch_rep_writer.erl Thu Jun 24 05:21:30 2010
@@ -76,8 +76,9 @@ write_bulk_docs(#http_db{headers = Heade
resource = "_bulk_docs",
method = post,
body = {[{new_edits, false}, {docs, JsonDocs}]},
- headers = [{"x-couch-full-commit", "false"} | Headers]
+ headers = couch_util:proplist_apply_field({"Content-Type", "application/json"}, [{"X-Couch-Full-Commit", "false"} | Headers])
},
+ ?LOG_ERROR("headers ~p",[Request#http_db.headers]),
ErrorsJson = case couch_rep_httpc:request(Request) of
{FailProps} ->
exit({target_error, couch_util:get_value(<<"error">>, FailProps)});
Modified: couchdb/trunk/src/couchdb/couch_util.erl
URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_util.erl?rev=957422&r1=957421&r2=957422&view=diff
==============================================================================
--- couchdb/trunk/src/couchdb/couch_util.erl (original)
+++ couchdb/trunk/src/couchdb/couch_util.erl Thu Jun 24 05:21:30 2010
@@ -19,6 +19,7 @@
-export([encodeBase64Url/1, decodeBase64Url/1]).
-export([to_hex/1, parse_term/1, dict_find/3]).
-export([file_read_size/1, get_nested_json_value/2, json_user_ctx/1]).
+-export([proplist_apply_field/2, json_apply_field/2]).
-export([to_binary/1, to_integer/1, to_list/1, url_encode/1]).
-export([json_encode/1, json_decode/1]).
-export([verify/2,simple_call/2,shutdown_sync/1]).
@@ -143,6 +144,19 @@ get_nested_json_value(Value, []) ->
get_nested_json_value(_NotJSONObj, _) ->
throw({not_found, json_mismatch}).
+proplist_apply_field(H, L) ->
+ {R} = json_apply_field(H, {L}),
+ R.
+
+json_apply_field(H, {L}) ->
+ json_apply_field(H, L, []).
+json_apply_field({Key, NewValue}, [{Key, _OldVal} | Headers], Acc) ->
+ json_apply_field({Key, NewValue}, Headers, Acc);
+json_apply_field({Key, NewValue}, [{OtherKey, OtherVal} | Headers], Acc) ->
+ json_apply_field({Key, NewValue}, Headers, [{OtherKey, OtherVal} | Acc]);
+json_apply_field({Key, NewValue}, [], Acc) ->
+ {[{Key, NewValue}|Acc]}.
+
json_user_ctx(#db{name=DbName, user_ctx=Ctx}) ->
{[{<<"db">>, DbName},
{<<"name">>,Ctx#user_ctx.name},
Re: svn commit: r957422 - in /couchdb/trunk: share/www/script/
share/www/script/test/ src/couchdb/
Posted by Paul Davis <pa...@gmail.com>.
On Thu, Jun 24, 2010 at 1:21 AM, <jc...@apache.org> wrote:
> Author: jchris
> Date: Thu Jun 24 05:21:30 2010
> New Revision: 957422
>
> URL: http://svn.apache.org/viewvc?rev=957422&view=rev
> Log:
> use JSON content type in replicator, require it in the _bulk_docs and other POST apis
>
> Modified:
> couchdb/trunk/share/www/script/couch.js
> couchdb/trunk/share/www/script/test/basics.js
> couchdb/trunk/share/www/script/test/batch_save.js
> couchdb/trunk/share/www/script/test/stats.js
> couchdb/trunk/src/couchdb/couch_httpd.erl
> couchdb/trunk/src/couchdb/couch_httpd_auth.erl
> couchdb/trunk/src/couchdb/couch_httpd_db.erl
> couchdb/trunk/src/couchdb/couch_httpd_show.erl
> couchdb/trunk/src/couchdb/couch_rep.erl
> couchdb/trunk/src/couchdb/couch_rep_writer.erl
> couchdb/trunk/src/couchdb/couch_util.erl
>
> Modified: couchdb/trunk/share/www/script/couch.js
> URL: http://svn.apache.org/viewvc/couchdb/trunk/share/www/script/couch.js?rev=957422&r1=957421&r2=957422&view=diff
> ==============================================================================
> --- couchdb/trunk/share/www/script/couch.js [utf-8] (original)
> +++ couchdb/trunk/share/www/script/couch.js [utf-8] Thu Jun 24 05:21:30 2010
> @@ -398,6 +398,8 @@ CouchDB.newXhr = function() {
>
> CouchDB.request = function(method, uri, options) {
> options = options || {};
> + options.headers = options.headers || {};
> + options.headers["Content-Type"] = options.headers["Content-Type"] || "application/json";
> var req = CouchDB.newXhr();
> if(uri.substr(0, "http://".length) != "http://") {
> uri = CouchDB.urlPrefix + uri
>
> Modified: couchdb/trunk/share/www/script/test/basics.js
> URL: http://svn.apache.org/viewvc/couchdb/trunk/share/www/script/test/basics.js?rev=957422&r1=957421&r2=957422&view=diff
> ==============================================================================
> --- couchdb/trunk/share/www/script/test/basics.js (original)
> +++ couchdb/trunk/share/www/script/test/basics.js Thu Jun 24 05:21:30 2010
> @@ -152,7 +152,8 @@ couchTests.basics = function(debug) {
>
> // test that the POST response has a Location header
> var xhr = CouchDB.request("POST", "/test_suite_db", {
> - body: JSON.stringify({"foo":"bar"})
> + body: JSON.stringify({"foo":"bar"}),
> + headers: {"Content-Type": "application/json"}
> });
> var resp = JSON.parse(xhr.responseText);
> T(resp.ok);
> @@ -164,6 +165,7 @@ couchTests.basics = function(debug) {
>
> // test that that POST's with an _id aren't overriden with a UUID.
> var xhr = CouchDB.request("POST", "/test_suite_db", {
> + headers: {"Content-Type": "application/json"},
> body: JSON.stringify({"_id": "oppossum", "yar": "matey"})
> });
> var resp = JSON.parse(xhr.responseText);
> @@ -202,7 +204,10 @@ couchTests.basics = function(debug) {
> result = JSON.parse(xhr.responseText);
> T(result.error == "doc_validation");
>
> - xhr = CouchDB.request("POST", "/test_suite_db/", {body: data});
> + xhr = CouchDB.request("POST", "/test_suite_db/", {
> + headers: {"Content-Type": "application/json"},
> + body: data
> + });
> T(xhr.status == 500);
> result = JSON.parse(xhr.responseText);
> T(result.error == "doc_validation");
>
> Modified: couchdb/trunk/share/www/script/test/batch_save.js
> URL: http://svn.apache.org/viewvc/couchdb/trunk/share/www/script/test/batch_save.js?rev=957422&r1=957421&r2=957422&view=diff
> ==============================================================================
> --- couchdb/trunk/share/www/script/test/batch_save.js (original)
> +++ couchdb/trunk/share/www/script/test/batch_save.js Thu Jun 24 05:21:30 2010
> @@ -36,7 +36,10 @@ couchTests.batch_save = function(debug)
>
> // repeat the tests for POST
> for(i=0; i < 100; i++) {
> - var resp = db.request("POST", db.uri + "?batch=ok", {body: JSON.stringify({a:1})});
> + var resp = db.request("POST", db.uri + "?batch=ok", {
> + headers: {"Content-Type": "application/json"},
> + body: JSON.stringify({a:1})
> + });
> T(JSON.parse(resp.responseText).ok);
> }
>
>
> Modified: couchdb/trunk/share/www/script/test/stats.js
> URL: http://svn.apache.org/viewvc/couchdb/trunk/share/www/script/test/stats.js?rev=957422&r1=957421&r2=957422&view=diff
> ==============================================================================
> --- couchdb/trunk/share/www/script/test/stats.js (original)
> +++ couchdb/trunk/share/www/script/test/stats.js Thu Jun 24 05:21:30 2010
> @@ -160,7 +160,10 @@ couchTests.stats = function(debug) {
>
> runTest("couchdb", "database_writes", {
> run: function(db) {
> - CouchDB.request("POST", "/test_suite_db", {body: '{"a": "1"}'})
> + CouchDB.request("POST", "/test_suite_db", {
> + headers: {"Content-Type": "application/json"},
> + body: '{"a": "1"}'
> + })
> },
> test: function(before, after) {
> TEquals(before+1, after, "POST'ing new docs increments doc writes.");
>
> Modified: couchdb/trunk/src/couchdb/couch_httpd.erl
> URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_httpd.erl?rev=957422&r1=957421&r2=957422&view=diff
> ==============================================================================
> --- couchdb/trunk/src/couchdb/couch_httpd.erl (original)
> +++ couchdb/trunk/src/couchdb/couch_httpd.erl Thu Jun 24 05:21:30 2010
> @@ -25,7 +25,7 @@
> -export([start_json_response/2, start_json_response/3, end_json_response/1]).
> -export([send_response/4,send_method_not_allowed/2,send_error/4, send_redirect/2,send_chunked_error/2]).
> -export([send_json/2,send_json/3,send_json/4,last_chunk/1,parse_multipart_request/3]).
> --export([accepted_encodings/1,handle_request_int/5]).
> +-export([accepted_encodings/1,handle_request_int/5,validate_referer/1,validate_ctype/2]).
>
> start_link() ->
> % read config and register for configuration changes
> @@ -321,6 +321,34 @@ vhost_global(VhostGlobals, MochiReq) ->
> end,
> [true] == [true||V <- VhostGlobals, V == Front].
>
> +validate_referer(Req) ->
> + Host = host_for_request(Req),
> + Referer = header_value(Req, "Referer", fail),
> + case Referer of
> + fail ->
> + throw({bad_request, <<"Referer header required.">>});
> + Referer ->
> + {_,RefererHost,_,_,_} = mochiweb_util:urlsplit(Referer),
> + if
> + RefererHost =:= Host -> ok;
> + true -> throw({bad_request, <<"Referer header must match host.">>})
> + end
> + end.
> +
> +validate_ctype(Req, Ctype) ->
> + case couch_httpd:header_value(Req, "Content-Type") of
> + undefined ->
> + throw({bad_ctype, "Content-Type must be "++Ctype});
> + ReqCtype ->
> + % ?LOG_ERROR("Ctype ~p ReqCtype ~p",[Ctype,ReqCtype]),
> + case re:split(ReqCtype, ";", [{return, list}]) of
> + [Ctype] -> ok;
> + [Ctype, _Rest] -> ok;
> + _Else ->
> + throw({bad_ctype, "Content-Type must be "++Ctype})
> + end
> + end.
> +
> % Utilities
>
> partition(Path) ->
> @@ -367,9 +395,9 @@ qs(#httpd{mochi_req=MochiReq}) ->
> path(#httpd{mochi_req=MochiReq}) ->
> MochiReq:get(path).
>
> -absolute_uri(#httpd{mochi_req=MochiReq}, Path) ->
> +host_for_request(#httpd{mochi_req=MochiReq}) ->
> XHost = couch_config:get("httpd", "x_forwarded_host", "X-Forwarded-Host"),
> - Host = case MochiReq:get_header_value(XHost) of
> + case MochiReq:get_header_value(XHost) of
> undefined ->
> case MochiReq:get_header_value("Host") of
> undefined ->
> @@ -379,7 +407,10 @@ absolute_uri(#httpd{mochi_req=MochiReq},
> Value1
> end;
> Value -> Value
> - end,
> + end.
> +
> +absolute_uri(#httpd{mochi_req=MochiReq}=Req, Path) ->
> + Host = host_for_request(Req),
> XSsl = couch_config:get("httpd", "x_forwarded_ssl", "X-Forwarded-Ssl"),
> Scheme = case MochiReq:get_header_value(XSsl) of
> "on" -> "https";
>
> Modified: couchdb/trunk/src/couchdb/couch_httpd_auth.erl
> URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_httpd_auth.erl?rev=957422&r1=957421&r2=957422&view=diff
> ==============================================================================
> --- couchdb/trunk/src/couchdb/couch_httpd_auth.erl (original)
> +++ couchdb/trunk/src/couchdb/couch_httpd_auth.erl Thu Jun 24 05:21:30 2010
> @@ -251,6 +251,7 @@ ensure_cookie_auth_secret() ->
> handle_session_req(#httpd{method='POST', mochi_req=MochiReq}=Req) ->
> ReqBody = MochiReq:recv_body(),
> Form = case MochiReq:get_primary_header_value("content-type") of
> + % content type should be json
> "application/x-www-form-urlencoded" ++ _ ->
> mochiweb_util:parse_qs(ReqBody);
> _ ->
>
> Modified: couchdb/trunk/src/couchdb/couch_httpd_db.erl
> URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_httpd_db.erl?rev=957422&r1=957421&r2=957422&view=diff
> ==============================================================================
> --- couchdb/trunk/src/couchdb/couch_httpd_db.erl (original)
> +++ couchdb/trunk/src/couchdb/couch_httpd_db.erl Thu Jun 24 05:21:30 2010
> @@ -112,6 +112,7 @@ handle_changes_req(#httpd{path_parts=[_,
> send_method_not_allowed(Req, "GET,HEAD").
>
> handle_compact_req(#httpd{method='POST',path_parts=[DbName,_,Id|_]}=Req, _Db) ->
> + couch_httpd:validate_ctype(Req, "application/json"),
> ok = couch_view_compactor:start_compact(DbName, Id),
> send_json(Req, 202, {[{ok, true}]});
>
> @@ -195,6 +196,7 @@ db_req(#httpd{method='GET',path_parts=[_
> send_json(Req, {DbInfo});
>
> db_req(#httpd{method='POST',path_parts=[DbName]}=Req, Db) ->
> + couch_httpd:validate_ctype(Req, "application/json"),
> Doc = couch_doc:from_json_obj(couch_httpd:json_body(Req)),
> Doc2 = case Doc#doc.id of
> <<"">> ->
> @@ -262,6 +264,7 @@ db_req(#httpd{path_parts=[_,<<"_ensure_f
>
> db_req(#httpd{method='POST',path_parts=[_,<<"_bulk_docs">>]}=Req, Db) ->
> couch_stats_collector:increment({httpd, bulk_requests}),
> + couch_httpd:validate_ctype(Req, "application/json"),
> {JsonProps} = couch_httpd:json_body_obj(Req),
> DocsArray = couch_util:get_value(<<"docs">>, JsonProps),
> case couch_httpd:header_value(Req, "X-Couch-Full-Commit") of
> @@ -323,6 +326,7 @@ db_req(#httpd{path_parts=[_,<<"_bulk_doc
> send_method_not_allowed(Req, "POST");
>
> db_req(#httpd{method='POST',path_parts=[_,<<"_purge">>]}=Req, Db) ->
> + couch_httpd:validate_ctype(Req, "application/json"),
> {IdsRevs} = couch_httpd:json_body_obj(Req),
> IdsRevs2 = [{Id, couch_doc:parse_revs(Revs)} || {Id, Revs} <- IdsRevs],
>
> @@ -367,7 +371,6 @@ db_req(#httpd{method='POST',path_parts=[
> db_req(#httpd{path_parts=[_,<<"_missing_revs">>]}=Req, _Db) ->
> send_method_not_allowed(Req, "POST");
>
> -
> db_req(#httpd{method='POST',path_parts=[_,<<"_revs_diff">>]}=Req, Db) ->
> {JsonDocIdRevs} = couch_httpd:json_body_obj(Req),
> JsonDocIdRevs2 =
> @@ -586,14 +589,11 @@ db_doc_req(#httpd{method='GET'}=Req, Db,
> end
> end;
>
> +
> db_doc_req(#httpd{method='POST'}=Req, Db, DocId) ->
> + couch_httpd:validate_referer(Req),
> couch_doc:validate_docid(DocId),
> - case couch_httpd:header_value(Req, "Content-Type") of
> - "multipart/form-data" ++ _Rest ->
> - ok;
> - _Else ->
> - throw({bad_ctype, <<"Invalid Content-Type header for form upload">>})
> - end,
> + couch_httpd:validate_ctype(Req, "multipart/form-data"),
> Form = couch_httpd:parse_form(Req),
> case proplists:is_defined("_doc", Form) of
> true ->
>
> Modified: couchdb/trunk/src/couchdb/couch_httpd_show.erl
> URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_httpd_show.erl?rev=957422&r1=957421&r2=957422&view=diff
> ==============================================================================
> --- couchdb/trunk/src/couchdb/couch_httpd_show.erl (original)
> +++ couchdb/trunk/src/couchdb/couch_httpd_show.erl Thu Jun 24 05:21:30 2010
> @@ -145,7 +145,7 @@ send_doc_update_response(Req, Db, DDoc,
> {200, JsonResp}
> end,
>
> - JsonResp2 = json_apply_field({<<"code">>, Code}, JsonResp1),
> + JsonResp2 = couch_util:json_apply_field({<<"code">>, Code}, JsonResp1),
> % todo set location field
> couch_httpd_external:send_external_response(Req, JsonResp2).
>
> @@ -376,21 +376,6 @@ render_head_for_empty_list(StartListResp
> render_head_for_empty_list(StartListRespFun, Req, Etag, CurrentSeq, TotalRows) ->
> StartListRespFun(Req, Etag, TotalRows, null, [], CurrentSeq).
>
> -
> -% Maybe this is in the proplists API
> -% todo move to couch_util
> -json_apply_field(H, {L}) ->
> - json_apply_field(H, L, []).
> -json_apply_field({Key, NewValue}, [{Key, _OldVal} | Headers], Acc) ->
> - % drop matching keys
> - json_apply_field({Key, NewValue}, Headers, Acc);
> -json_apply_field({Key, NewValue}, [{OtherKey, OtherVal} | Headers], Acc) ->
> - % something else is next, leave it alone.
> - json_apply_field({Key, NewValue}, Headers, [{OtherKey, OtherVal} | Acc]);
> -json_apply_field({Key, NewValue}, [], Acc) ->
> - % end of list, add ours
> - {[{Key, NewValue}|Acc]}.
> -
> apply_etag({ExternalResponse}, CurrentEtag) ->
> % Here we embark on the delicate task of replacing or creating the
> % headers on the JsonResponse object. We need to control the Etag and
> @@ -404,8 +389,8 @@ apply_etag({ExternalResponse}, CurrentEt
> JsonHeaders ->
> {[case Field of
> {<<"headers">>, JsonHeaders} -> % add our headers
> - JsonHeadersEtagged = json_apply_field({<<"Etag">>, CurrentEtag}, JsonHeaders),
> - JsonHeadersVaried = json_apply_field({<<"Vary">>, <<"Accept">>}, JsonHeadersEtagged),
> + JsonHeadersEtagged = couch_util:json_apply_field({<<"Etag">>, CurrentEtag}, JsonHeaders),
> + JsonHeadersVaried = couch_util:json_apply_field({<<"Vary">>, <<"Accept">>}, JsonHeadersEtagged),
> {<<"headers">>, JsonHeadersVaried};
> _ -> % skip non-header fields
> Field
>
> Modified: couchdb/trunk/src/couchdb/couch_rep.erl
> URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_rep.erl?rev=957422&r1=957421&r2=957422&view=diff
> ==============================================================================
> --- couchdb/trunk/src/couchdb/couch_rep.erl (original)
> +++ couchdb/trunk/src/couchdb/couch_rep.erl Thu Jun 24 05:21:30 2010
> @@ -653,10 +653,11 @@ commit_to_both(Source, Target, RequiredS
> end,
> {SourceStartTime, TargetStartTime}.
>
> -ensure_full_commit(#http_db{} = Target) ->
> +ensure_full_commit(#http_db{headers = Headers} = Target) ->
> Req = Target#http_db{
> resource = "_ensure_full_commit",
> - method = post
> + method = post,
> + headers = [{"content-type", "application/json"} | Headers]
> },
> {ResultProps} = couch_rep_httpc:request(Req),
> true = couch_util:get_value(<<"ok">>, ResultProps),
> @@ -677,11 +678,12 @@ ensure_full_commit(Target) ->
> InstanceStartTime
> end.
>
> -ensure_full_commit(#http_db{} = Source, RequiredSeq) ->
> +ensure_full_commit(#http_db{headers = Headers} = Source, RequiredSeq) ->
> Req = Source#http_db{
> resource = "_ensure_full_commit",
> method = post,
> - qs = [{seq, RequiredSeq}]
> + qs = [{seq, RequiredSeq}],
> + headers = [{"content-type", "application/json"} | Headers]
> },
> {ResultProps} = couch_rep_httpc:request(Req),
> case couch_util:get_value(<<"ok">>, ResultProps) of
>
> Modified: couchdb/trunk/src/couchdb/couch_rep_writer.erl
> URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_rep_writer.erl?rev=957422&r1=957421&r2=957422&view=diff
> ==============================================================================
> --- couchdb/trunk/src/couchdb/couch_rep_writer.erl (original)
> +++ couchdb/trunk/src/couchdb/couch_rep_writer.erl Thu Jun 24 05:21:30 2010
> @@ -76,8 +76,9 @@ write_bulk_docs(#http_db{headers = Heade
> resource = "_bulk_docs",
> method = post,
> body = {[{new_edits, false}, {docs, JsonDocs}]},
> - headers = [{"x-couch-full-commit", "false"} | Headers]
> + headers = couch_util:proplist_apply_field({"Content-Type", "application/json"}, [{"X-Couch-Full-Commit", "false"} | Headers])
> },
> + ?LOG_ERROR("headers ~p",[Request#http_db.headers]),
> ErrorsJson = case couch_rep_httpc:request(Request) of
> {FailProps} ->
> exit({target_error, couch_util:get_value(<<"error">>, FailProps)});
>
> Modified: couchdb/trunk/src/couchdb/couch_util.erl
> URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_util.erl?rev=957422&r1=957421&r2=957422&view=diff
> ==============================================================================
> --- couchdb/trunk/src/couchdb/couch_util.erl (original)
> +++ couchdb/trunk/src/couchdb/couch_util.erl Thu Jun 24 05:21:30 2010
> @@ -19,6 +19,7 @@
> -export([encodeBase64Url/1, decodeBase64Url/1]).
> -export([to_hex/1, parse_term/1, dict_find/3]).
> -export([file_read_size/1, get_nested_json_value/2, json_user_ctx/1]).
> +-export([proplist_apply_field/2, json_apply_field/2]).
> -export([to_binary/1, to_integer/1, to_list/1, url_encode/1]).
> -export([json_encode/1, json_decode/1]).
> -export([verify/2,simple_call/2,shutdown_sync/1]).
> @@ -143,6 +144,19 @@ get_nested_json_value(Value, []) ->
> get_nested_json_value(_NotJSONObj, _) ->
> throw({not_found, json_mismatch}).
>
> +proplist_apply_field(H, L) ->
> + {R} = json_apply_field(H, {L}),
> + R.
> +
> +json_apply_field(H, {L}) ->
> + json_apply_field(H, L, []).
> +json_apply_field({Key, NewValue}, [{Key, _OldVal} | Headers], Acc) ->
> + json_apply_field({Key, NewValue}, Headers, Acc);
> +json_apply_field({Key, NewValue}, [{OtherKey, OtherVal} | Headers], Acc) ->
> + json_apply_field({Key, NewValue}, Headers, [{OtherKey, OtherVal} | Acc]);
> +json_apply_field({Key, NewValue}, [], Acc) ->
> + {[{Key, NewValue}|Acc]}.
> +
> json_user_ctx(#db{name=DbName, user_ctx=Ctx}) ->
> {[{<<"db">>, DbName},
> {<<"name">>,Ctx#user_ctx.name},
>
>
>
Why the change to requiring the JSON header? I thought we'd always
left that lax for the clients that are stuck in 1980.
Also, are we ok in requiring exactly application/json and not the
non-RFC-compliant things like text/json et al?
Paul