Skip to content
This repository
  • 15 commits
  • 5 files changed
  • 0 comments
  • 5 contributors
Nov 14, 2012
Robert Newson rnewson Support placement param when creating db's
Conflicts:
	src/chttpd_db.erl
26bc7f7
Robert Newson rnewson Don't send errors midstream
This patch ensures that an error occuring midstream cleanly aborts the
response (the client will see a truncated response, rather than a
malformed one) and we log the error cleanly.

BugzID: 14764
0c4b403
Paul J. Davis davisp Account for returning a #delayed_resp{}
After the patch last week we're holding onto #delayed_resp{} records
longer. This unwraps the #mochiweb_response{} if we get one back from
the request handler.
6abe8e6
Adam Kocoloski kocolosk Improve validity check for reduce qs
Allowed values are "true" and "false"; anything else is a 400.

BugzID: 14775
dbb432d
Robert Newson rnewson Fix module name ec72102
Robert Newson rnewson add empty jsonstack to send_method_not_allowed 0d01e99
Adam Kocoloski kocolosk Remove unused function da8b347
Robert Newson rnewson Send attachment encoding information
BugzID: 13778
0bcf175
Adam Kocoloski kocolosk Revert "Port workaround for COUCHDB-902"
This reverts commit c484b5b. An
upstream patch for the core issue was applied to dbcore as 88ae45.

BugzID: 11551
BugzID: 11366
40308d8
Bob Dionne Expose latest=true option in calls to retrieve revisions
When a call is made to retrieve a specific revision, latest=true will
retrieve any descendent leaves instead. This enables the replicator to
better keep up with edits that occur whilst it's retrieving revisions

BugzID: 14241
479964b
Robert Dionne Add support for descending=true argument to search queries. bfa73ab
Robert Newson rnewson Support jsonp in externals
Allow ?callback= for any external that returns json (i.e,
uses "json":{} instead of "data":"data".

BugzID: 12748
f40f349
Robert Newson rnewson fix URL rewriting for key/startkey/endkey qs params
COUCHDB-1074
d7721cf
Adam Kocoloski kocolosk Use extra array instead of extending record d7e9420
Bob Dionne Remove unused variables 5168fe0
53 src/chttpd.erl
@@ -38,7 +38,8 @@
38 38 req,
39 39 code,
40 40 headers,
41   - first_chunk
  41 + first_chunk,
  42 + resp=nil
42 43 }).
43 44
44 45 start_link() ->
@@ -240,6 +241,8 @@ handle_request(MochiReq) ->
240 241
241 242 RequestTime = timer:now_diff(now(), Begin)/1000,
242 243 {Status, Code} = case Result of
  244 + {ok, #delayed_resp{resp=Resp}} ->
  245 + {ok, Resp:get(code)};
243 246 {ok, Resp} ->
244 247 {ok, Resp:get(code)};
245 248 {aborted, Resp, _} ->
@@ -556,7 +559,7 @@ send_response(#httpd{mochi_req=MochiReq}=Req, Code, Headers, Body) ->
556 559
557 560 send_method_not_allowed(Req, Methods) ->
558 561 send_error(Req, 405, [{"Allow", Methods}], <<"method_not_allowed">>,
559   - ?l2b("Only " ++ Methods ++ " allowed")).
  562 + ?l2b("Only " ++ Methods ++ " allowed"), []).
560 563
561 564 send_json(Req, Value) ->
562 565 send_json(Req, 200, Value).
@@ -601,40 +604,47 @@ start_delayed_chunked_response(Req, Code, Headers, FirstChunk) ->
601 604 headers = Headers,
602 605 first_chunk = FirstChunk}}.
603 606
604   -send_delayed_chunk(Resp, Chunk) ->
605   - {ok, Resp1} = start_delayed_response(Resp),
606   - send_chunk(Resp1, Chunk).
  607 +send_delayed_chunk(#delayed_resp{}=DelayedResp, Chunk) ->
  608 + {ok, #delayed_resp{resp=Resp}=DelayedResp1} =
  609 + start_delayed_response(DelayedResp),
  610 + {ok, Resp} = send_chunk(Resp, Chunk),
  611 + {ok, DelayedResp1}.
607 612
608 613 send_delayed_last_chunk(Req) ->
609 614 send_delayed_chunk(Req, []).
610 615
611   -send_delayed_error(#httpd{}=Req, Reason) ->
612   - {Code, ErrorStr, ReasonStr} = error_info(Reason),
613   - send_error(Req, Code, ErrorStr, ReasonStr);
614   -send_delayed_error(#delayed_resp{req=Req}, Reason) ->
  616 +send_delayed_error(#delayed_resp{req=Req,resp=nil}, Reason) ->
615 617 {Code, ErrorStr, ReasonStr} = error_info(Reason),
616 618 send_error(Req, Code, ErrorStr, ReasonStr);
617   -send_delayed_error(Resp, Reason) ->
  619 +send_delayed_error(#delayed_resp{resp=Resp}, Reason) ->
618 620 throw({http_abort, Resp, Reason}).
619 621
620   -end_delayed_json_response(Resp) ->
621   - {ok, Resp1} = start_delayed_response(Resp),
622   - end_json_response(Resp1).
  622 +end_delayed_json_response(#delayed_resp{}=DelayedResp) ->
  623 + {ok, #delayed_resp{resp=Resp}} =
  624 + start_delayed_response(DelayedResp),
  625 + end_json_response(Resp).
623 626
624 627 get_delayed_req(#delayed_resp{req=#httpd{mochi_req=MochiReq}}) ->
625 628 MochiReq;
626 629 get_delayed_req(Resp) ->
627 630 Resp:get(request).
628 631
629   -start_delayed_response(#delayed_resp{start_fun=StartFun, req=Req, code=Code,
630   - headers=Headers, first_chunk=FirstChunk}) ->
  632 +start_delayed_response(#delayed_resp{resp=nil}=DelayedResp) ->
  633 + #delayed_resp{
  634 + start_fun=StartFun,
  635 + req=Req,
  636 + code=Code,
  637 + headers=Headers,
  638 + first_chunk=FirstChunk
  639 + }=DelayedResp,
631 640 {ok, Resp} = StartFun(Req, Code, Headers),
632 641 case FirstChunk of
633   - "" -> {ok, Resp};
634   - _ -> send_chunk(Resp, FirstChunk)
635   - end;
636   -start_delayed_response(Resp) ->
637   - {ok, Resp}.
  642 + "" -> ok;
  643 + _ -> {ok, Resp} = send_chunk(Resp, FirstChunk)
  644 + end,
  645 + {ok, DelayedResp#delayed_resp{resp=Resp}};
  646 +start_delayed_response(#delayed_resp{}=DelayedResp) ->
  647 + {ok, DelayedResp}.
638 648
639 649 error_info({Error, Reason}) when is_list(Reason) ->
640 650 error_info({Error, couch_util:to_binary(Reason)});
@@ -761,9 +771,6 @@ send_error(Req, Error) ->
761 771 send_error(Req, Code, ErrorStr, ReasonStr) ->
762 772 send_error(Req, Code, [], ErrorStr, ReasonStr, []).
763 773
764   -send_error(Req, Code, Headers, ErrorStr, ReasonStr) ->
765   - send_error(Req, Code, Headers, ErrorStr, ReasonStr, []).
766   -
767 774 send_error(Req, Code, Headers, ErrorStr, ReasonStr, Stack) ->
768 775 send_json(Req, Code, Headers,
769 776 {[{<<"error">>, ErrorStr},
12 src/chttpd_db.erl
@@ -171,8 +171,9 @@ create_db_req(#httpd{}=Req, DbName) ->
171 171 couch_httpd:verify_is_server_admin(Req),
172 172 N = couch_httpd:qs_value(Req, "n", couch_config:get("cluster", "n", "3")),
173 173 Q = couch_httpd:qs_value(Req, "q", couch_config:get("cluster", "q", "8")),
  174 + P = couch_httpd:qs_value(Req, "placement", couch_config:get("cluster", "placement")),
174 175 DocUrl = absolute_uri(Req, "/" ++ couch_util:url_encode(DbName)),
175   - case fabric:create_db(DbName, [{n,N}, {q,Q}]) of
  176 + case fabric:create_db(DbName, [{n,N}, {q,Q}, {placement,P}]) of
176 177 ok ->
177 178 send_json(Req, 201, [{"Location", DocUrl}], {[{ok, true}]});
178 179 accepted ->
@@ -704,7 +705,7 @@ send_doc_efficiently(Req, #doc{atts=Atts}=Doc, Headers, Options) ->
704 705 true ->
705 706 Boundary = couch_uuids:random(),
706 707 JsonBytes = ?JSON_ENCODE(couch_doc:to_json_obj(Doc,
707   - [attachments, follows|Options])),
  708 + [attachments, follows, att_encoding_info | Options])),
708 709 {ContentType, Len} = couch_doc:len_doc_to_multi_part_stream(
709 710 Boundary,JsonBytes, Atts, true),
710 711 CType = {<<"Content-Type">>, ContentType},
@@ -1021,10 +1022,8 @@ db_attachment_req(#httpd{method=Method, user_ctx=Ctx}=Req, Db, DocId, FileNamePa
1021 1022 end
1022 1023 end,
1023 1024
1024   - #doc{atts=Atts, revs = {Pos, Revs}} = Doc,
  1025 + #doc{atts=Atts} = Doc,
1025 1026 DocEdited = Doc#doc{
1026   - % prune revision list as a workaround for key tree bug (COUCHDB-902)
1027   - revs = {Pos, case Revs of [] -> []; [Hd|_] -> [Hd] end},
1028 1027 atts = NewAtt ++ [A || A <- Atts, A#att.name /= FileName]
1029 1028 },
1030 1029 case fabric:update_doc(Db, DocEdited, [{user_ctx,Ctx}]) of
@@ -1148,6 +1147,9 @@ parse_doc_query(Req) ->
1148 1147 {"open_revs", RevsJsonStr} ->
1149 1148 JsonArray = ?JSON_DECODE(RevsJsonStr),
1150 1149 Args#doc_query_args{open_revs=[couch_doc:parse_rev(Rev) || Rev <- JsonArray]};
  1150 + {"latest", "true"} ->
  1151 + Options = [latest | Args#doc_query_args.options],
  1152 + Args#doc_query_args{options=Options};
1151 1153 {"atts_since", RevsJsonStr} ->
1152 1154 JsonArray = ?JSON_DECODE(RevsJsonStr),
1153 1155 Args#doc_query_args{atts_since = couch_doc:parse_revs(JsonArray)};
19 src/chttpd_external.erl
@@ -111,19 +111,26 @@ json_query_keys([{<<"endkey">>, Value} | Rest], Acc) ->
111 111 json_query_keys(Rest, [{<<"endkey">>, ?JSON_DECODE(Value)}|Acc]);
112 112 json_query_keys([{<<"key">>, Value} | Rest], Acc) ->
113 113 json_query_keys(Rest, [{<<"key">>, ?JSON_DECODE(Value)}|Acc]);
  114 +json_query_keys([{<<"descending">>, Value} | Rest], Acc) ->
  115 + json_query_keys(Rest, [{<<"descending">>, ?JSON_DECODE(Value)}|Acc]);
114 116 json_query_keys([Term | Rest], Acc) ->
115 117 json_query_keys(Rest, [Term|Acc]).
116 118
117   -send_external_response(#httpd{mochi_req=MochiReq}, Response) ->
  119 +send_external_response(Req, Response) ->
118 120 #extern_resp_args{
119 121 code = Code,
120 122 data = Data,
121 123 ctype = CType,
122   - headers = Headers
  124 + headers = Headers,
  125 + json = Json
123 126 } = parse_external_response(Response),
124   - Resp = MochiReq:respond({Code,
125   - default_or_content_type(CType, Headers ++ chttpd:server_header()), Data}),
126   - {ok, Resp}.
  127 + Headers1 = default_or_content_type(CType, Headers),
  128 + case Json of
  129 + nil ->
  130 + couch_httpd:send_response(Req, Code, Headers1, Data);
  131 + Json ->
  132 + couch_httpd:send_json(Req, Code, Headers1, Json)
  133 + end.
127 134
128 135 parse_external_response({Response}) ->
129 136 lists:foldl(fun({Key,Value}, Args) ->
@@ -136,7 +143,7 @@ parse_external_response({Response}) ->
136 143 Args#extern_resp_args{stop=true};
137 144 {<<"json">>, Value} ->
138 145 Args#extern_resp_args{
139   - data=?JSON_ENCODE(Value),
  146 + json=Value,
140 147 ctype="application/json"};
141 148 {<<"body">>, Value} ->
142 149 Args#extern_resp_args{data=Value, ctype="text/html; charset=utf-8"};
185 src/chttpd_rewrite.erl
@@ -117,8 +117,7 @@ handle_rewrite_req(#httpd{
117 117 % we are in a design handler
118 118 DesignId = <<"_design/", DesignName/binary>>,
119 119 Prefix = <<"/", DbName/binary, "/", DesignId/binary>>,
120   - QueryList = couch_httpd:qs(Req),
121   - QueryList1 = [{to_binding(K), V} || {K, V} <- QueryList],
  120 + QueryList = lists:map(fun decode_query_value/1, couch_httpd:qs(Req)),
122 121
123 122 #doc{body={Props}} = DDoc,
124 123
@@ -133,10 +132,11 @@ handle_rewrite_req(#httpd{
133 132 Rules ->
134 133 % create dispatch list from rules
135 134 DispatchList = [make_rule(Rule) || {Rule} <- Rules],
  135 + Method1 = couch_util:to_binary(Method),
136 136
137 137 %% get raw path by matching url to a rule.
138   - RawPath = case try_bind_path(DispatchList, couch_util:to_binary(Method), PathParts,
139   - QueryList1) of
  138 + RawPath = case try_bind_path(DispatchList, Method1,
  139 + PathParts, QueryList) of
140 140 no_dispatch_path ->
141 141 throw(not_found);
142 142 {NewPathParts, Bindings} ->
@@ -144,13 +144,14 @@ handle_rewrite_req(#httpd{
144 144
145 145 % build new path, reencode query args, eventually convert
146 146 % them to json
147   - Path = lists:append(
148   - string:join(Parts, [?SEPARATOR]),
149   - case Bindings of
150   - [] -> [];
151   - _ -> [$?, encode_query(Bindings)]
152   - end),
153   -
  147 + Bindings1 = maybe_encode_bindings(Bindings),
  148 + Path = binary_to_list(
  149 + iolist_to_binary([
  150 + string:join(Parts, [?SEPARATOR]),
  151 + [["?", mochiweb_util:urlencode(Bindings1)]
  152 + || Bindings1 =/= [] ]
  153 + ])),
  154 +
154 155 % if path is relative detect it and rewrite path
155 156 case mochiweb_util:safe_relative_path(Path) of
156 157 undefined ->
@@ -189,7 +190,7 @@ quote_plus(X) ->
189 190 try_bind_path([], _Method, _PathParts, _QueryList) ->
190 191 no_dispatch_path;
191 192 try_bind_path([Dispatch|Rest], Method, PathParts, QueryList) ->
192   - [{PathParts1, Method1}, RedirectPath, QueryArgs] = Dispatch,
  193 + [{PathParts1, Method1}, RedirectPath, QueryArgs, Formats] = Dispatch,
193 194 case bind_method(Method1, Method) of
194 195 true ->
195 196 case bind_path(PathParts1, PathParts, []) of
@@ -197,7 +198,8 @@ try_bind_path([Dispatch|Rest], Method, PathParts, QueryList) ->
197 198 Bindings1 = Bindings ++ QueryList,
198 199 % we parse query args from the rule and fill
199 200 % it eventually with bindings vars
200   - QueryArgs1 = make_query_list(QueryArgs, Bindings1, []),
  201 + QueryArgs1 = make_query_list(QueryArgs, Bindings1,
  202 + Formats, []),
201 203 % remove params in QueryLists1 that are already in
202 204 % QueryArgs1
203 205 Bindings2 = lists:foldl(fun({K, V}, Acc) ->
@@ -223,56 +225,79 @@ try_bind_path([Dispatch|Rest], Method, PathParts, QueryList) ->
223 225 %% rewriting dynamically the quey list given as query member in
224 226 %% rewrites. Each value is replaced by one binding or an argument
225 227 %% passed in url.
226   -make_query_list([], _Bindings, Acc) ->
  228 +make_query_list([], _Bindings, _Formats, Acc) ->
227 229 Acc;
228   -make_query_list([{Key, {Value}}|Rest], Bindings, Acc) ->
229   - Value1 = to_json({Value}),
230   - make_query_list(Rest, Bindings, [{to_binding(Key), Value1}|Acc]);
231   -make_query_list([{Key, Value}|Rest], Bindings, Acc) when is_binary(Value) ->
232   - Value1 = replace_var(Key, Value, Bindings),
233   - make_query_list(Rest, Bindings, [{to_binding(Key), Value1}|Acc]);
234   -make_query_list([{Key, Value}|Rest], Bindings, Acc) when is_list(Value) ->
235   - Value1 = replace_var(Key, Value, Bindings),
236   - make_query_list(Rest, Bindings, [{to_binding(Key), Value1}|Acc]);
237   -make_query_list([{Key, Value}|Rest], Bindings, Acc) ->
238   - make_query_list(Rest, Bindings, [{to_binding(Key), Value}|Acc]).
239   -
240   -replace_var(Key, Value, Bindings) ->
241   - case Value of
242   - <<":", Var/binary>> ->
243   - get_var(Var, Bindings, Value);
244   - _ when is_list(Value) ->
245   - Value1 = lists:foldr(fun(V, Acc) ->
246   - V1 = case V of
247   - <<":", VName/binary>> ->
248   - case get_var(VName, Bindings, V) of
249   - V2 when is_list(V2) ->
250   - iolist_to_binary(V2);
251   - V2 -> V2
252   - end;
253   - _ ->
254   -
255   - V
256   - end,
257   - [V1|Acc]
258   - end, [], Value),
259   - to_json(Value1);
260   - _ when is_binary(Value) ->
261   - Value;
262   - _ ->
263   - case Key of
264   - <<"key">> -> to_json(Value);
265   - <<"startkey">> -> to_json(Value);
266   - <<"endkey">> -> to_json(Value);
267   - _ ->
268   - lists:flatten(?JSON_ENCODE(Value))
269   - end
  230 +make_query_list([{Key, {Value}}|Rest], Bindings, Formats, Acc) ->
  231 + Value1 = {Value},
  232 + make_query_list(Rest, Bindings, Formats, [{to_binding(Key), Value1}|Acc]);
  233 +make_query_list([{Key, Value}|Rest], Bindings, Formats, Acc) when is_binary(Value) ->
  234 + Value1 = replace_var(Value, Bindings, Formats),
  235 + make_query_list(Rest, Bindings, Formats, [{to_binding(Key), Value1}|Acc]);
  236 +make_query_list([{Key, Value}|Rest], Bindings, Formats, Acc) when is_list(Value) ->
  237 + Value1 = replace_var(Value, Bindings, Formats),
  238 + make_query_list(Rest, Bindings, Formats, [{to_binding(Key), Value1}|Acc]);
  239 +make_query_list([{Key, Value}|Rest], Bindings, Formats, Acc) ->
  240 + make_query_list(Rest, Bindings, Formats, [{to_binding(Key), Value}|Acc]).
  241 +
  242 +replace_var(<<"*">>=Value, Bindings, Formats) ->
  243 + get_var(Value, Bindings, Value, Formats);
  244 +replace_var(<<":", Var/binary>> = Value, Bindings, Formats) ->
  245 + get_var(Var, Bindings, Value, Formats);
  246 +replace_var(Value, _Bindings, _Formats) when is_binary(Value) ->
  247 + Value;
  248 +replace_var(Value, Bindings, Formats) when is_list(Value) ->
  249 + lists:reverse(lists:foldl(fun
  250 + (<<":", Var/binary>>=Value1, Acc) ->
  251 + [get_var(Var, Bindings, Value1, Formats)|Acc];
  252 + (Value1, Acc) ->
  253 + [Value1|Acc]
  254 + end, [], Value));
  255 +replace_var(Value, _Bindings, _Formats) ->
  256 + Value.
  257 +
  258 +maybe_json(Key, Value) ->
  259 + case lists:member(Key, [<<"key">>, <<"startkey">>, <<"start_key">>,
  260 + <<"endkey">>, <<"end_key">>, <<"keys">>]) of
  261 + true ->
  262 + ?JSON_ENCODE(Value);
  263 + false ->
  264 + Value
270 265 end.
271 266
272   -
273   -get_var(VarName, Props, Default) ->
  267 +get_var(VarName, Props, Default, Formats) ->
274 268 VarName1 = to_binding(VarName),
275   - couch_util:get_value(VarName1, Props, Default).
  269 + Val = couch_util:get_value(VarName1, Props, Default),
  270 + maybe_format(VarName, Val, Formats).
  271 +
  272 +maybe_format(VarName, Value, Formats) ->
  273 + case couch_util:get_value(VarName, Formats) of
  274 + undefined ->
  275 + Value;
  276 + Format ->
  277 + format(Format, Value)
  278 + end.
  279 +
  280 +format(<<"int">>, Value) when is_integer(Value) ->
  281 + Value;
  282 +format(<<"int">>, Value) when is_binary(Value) ->
  283 + format(<<"int">>, ?b2l(Value));
  284 +format(<<"int">>, Value) when is_list(Value) ->
  285 + case (catch list_to_integer(Value)) of
  286 + IntVal when is_integer(IntVal) ->
  287 + IntVal;
  288 + _ ->
  289 + Value
  290 + end;
  291 +format(<<"bool">>, Value) when is_binary(Value) ->
  292 + format(<<"bool">>, ?b2l(Value));
  293 +format(<<"bool">>, Value) when is_list(Value) ->
  294 + case string:to_lower(Value) of
  295 + "true" -> true;
  296 + "false" -> false;
  297 + _ -> Value
  298 + end;
  299 +format(_Format, Value) ->
  300 + Value.
276 301
277 302 %% doc: build new patch from bindings. bindings are query args
278 303 %% (+ dynamic query rewritten if needed) and bindings found in
@@ -288,7 +313,8 @@ make_new_path([?MATCH_ALL|_Rest], _Bindings, Remaining, Acc) ->
288 313 make_new_path([{bind, P}|Rest], Bindings, Remaining, Acc) ->
289 314 P2 = case couch_util:get_value({bind, P}, Bindings) of
290 315 undefined -> << "undefined">>;
291   - P1 -> P1
  316 + P1 ->
  317 + iolist_to_binary(P1)
292 318 end,
293 319 make_new_path(Rest, Bindings, Remaining, [P2|Acc]);
294 320 make_new_path([P|Rest], Bindings, Remaining, Acc) ->
@@ -365,7 +391,11 @@ make_rule(Rule) ->
365 391 To ->
366 392 parse_path(To)
367 393 end,
368   - [{FromParts, Method}, ToParts, QueryArgs].
  394 + Formats = case couch_util:get_value(<<"formats">>, Rule) of
  395 + undefined -> [];
  396 + {Fmts} -> Fmts
  397 + end,
  398 + [{FromParts, Method}, ToParts, QueryArgs, Formats].
369 399
370 400 parse_path(Path) ->
371 401 {ok, SlashRE} = re:compile(<<"\\/">>),
@@ -398,17 +428,25 @@ path_to_list([P|R], Acc, DotDotCount) ->
398 428 end,
399 429 path_to_list(R, [P1|Acc], DotDotCount).
400 430
401   -encode_query(Props) ->
402   - Props1 = lists:foldl(fun ({{bind, K}, V}, Acc) ->
403   - V1 = case is_list(V) orelse is_binary(V) of
404   - true -> V;
405   - false ->
406   - % probably it's a number
407   - quote_plus(V)
408   - end,
409   - [{K, V1} | Acc]
410   - end, [], Props),
411   - lists:flatten(mochiweb_util:urlencode(Props1)).
  431 +maybe_encode_bindings([]) ->
  432 + [];
  433 +maybe_encode_bindings(Props) ->
  434 + lists:foldl(fun
  435 + ({{bind, <<"*">>}, _V}, Acc) ->
  436 + Acc;
  437 + ({{bind, K}, V}, Acc) ->
  438 + V1 = iolist_to_binary(maybe_json(K, V)),
  439 + [{K, V1}|Acc]
  440 + end, [], Props).
  441 +
  442 +decode_query_value({K,V}) ->
  443 + case lists:member(K, ["key", "startkey", "start_key",
  444 + "endkey", "end_key", "keys"]) of
  445 + true ->
  446 + {to_binding(K), ?JSON_DECODE(V)};
  447 + false ->
  448 + {to_binding(K), ?l2b(V)}
  449 + end.
412 450
413 451 to_binding({bind, V}) ->
414 452 {bind, V};
@@ -416,6 +454,3 @@ to_binding(V) when is_list(V) ->
416 454 to_binding(?l2b(V));
417 455 to_binding(V) ->
418 456 {bind, V}.
419   -
420   -to_json(V) ->
421   - iolist_to_binary(?JSON_ENCODE(V)).
13 src/chttpd_view.erl
@@ -101,7 +101,7 @@ extract_view_type(ViewName, [View|Rest], IsReduce) ->
101 101
102 102 handle_view_req(#httpd{method='GET',
103 103 path_parts=[_, _, _, _, ViewName]}=Req, Db, DDoc) ->
104   - Keys = couch_httpd:qs_json_value(Req, "keys", nil),
  104 + Keys = chttpd:qs_json_value(Req, "keys", nil),
105 105 design_doc_view(Req, Db, DDoc, ViewName, Keys);
106 106
107 107 handle_view_req(#httpd{method='POST',
@@ -136,7 +136,14 @@ reverse_key_default(?MAX_STR) -> ?MIN_STR;
136 136 reverse_key_default(Key) -> Key.
137 137
138 138 get_reduce_type(Req) ->
139   - list_to_existing_atom(chttpd:qs_value(Req, "reduce", "true")).
  139 + case chttpd:qs_value(Req, "reduce", "true") of
  140 + "true" ->
  141 + true;
  142 + "false" ->
  143 + false;
  144 + _Error ->
  145 + throw({bad_request, "`reduce` qs param must be `true` or `false`"})
  146 + end.
140 147
141 148 parse_view_params(Req, Keys, ViewType) when not is_list(Req) ->
142 149 QueryParams = lists:flatmap(fun({K,V}) -> parse_view_param(K,V) end,
@@ -346,7 +353,7 @@ validate_view_query(conflicts, true, Args) ->
346 353 "is invalid for reduce views.">>,
347 354 throw({query_parse_error, Msg});
348 355 _ ->
349   - Args#view_query_args{conflicts = true}
  356 + Args#view_query_args{extra = [conflicts|Args#view_query_args.extra]}
350 357 end;
351 358 validate_view_query(conflicts, _Value, Args) ->
352 359 Args;

No commit comments for this range

Something went wrong with that request. Please try again.