Skip to content

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also compare across forks.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also compare across forks.
...
  • 4 commits
  • 2 files changed
  • 0 commit comments
  • 1 contributor
Commits on Jun 11, 2011
Robert Newson couchdb 1.1 compatibility. 6c4cc35
Commits on Jun 14, 2011
Robert Newson pass ?z= when creating databases. ef59495
Commits on Jun 16, 2011
Robert Newson support Range header for attachments. f0788c3
Robert Newson pass stale=update_after 529a9d0
Showing with 96 additions and 18 deletions.
  1. +90 −17 src/chttpd_db.erl
  2. +6 −1 src/chttpd_view.erl
View
107 src/chttpd_db.erl
@@ -167,7 +167,8 @@ create_db_req(#httpd{}=Req, DbName) ->
couch_httpd:verify_is_server_admin(Req),
N = couch_httpd:qs_value(Req, "n", couch_config:get("cluster", "n", "3")),
Q = couch_httpd:qs_value(Req, "q", couch_config:get("cluster", "q", "8")),
- case fabric:create_db(DbName, [{n,N}, {q,Q}]) of
+ Z = couch_httpd:qs_value(Req, "z", couch_config:get("cluster", "z", "3")),
+ case fabric:create_db(DbName, [{n,N}, {q,Q}, {z,Z}]) of
ok ->
DocUrl = absolute_uri(Req, "/" ++ couch_util:url_encode(DbName)),
send_json(Req, 201, [{"Location", DocUrl}], {[{ok, true}]});
@@ -569,10 +570,12 @@ db_doc_req(#httpd{method='PUT', user_ctx=Ctx}=Req, Db, DocId) ->
RespHeaders = [{"Location", Loc}],
case couch_util:to_list(couch_httpd:header_value(Req, "Content-Type")) of
("multipart/related;" ++ _) = ContentType ->
- {ok, Doc0} = couch_doc:doc_from_multi_part_stream(ContentType,
+ {ok, Doc0, WaitFun} = couch_doc:doc_from_multi_part_stream(ContentType,
fun() -> receive_request_data(Req) end),
Doc = couch_doc_from_req(Req, DocId, Doc0),
- update_doc(Req, Db, DocId, Doc, RespHeaders, UpdateType);
+ Result = update_doc(Req, Db, DocId, Doc, RespHeaders, UpdateType),
+ WaitFun(),
+ Result;
_Else ->
case couch_httpd:qs_value(Req, "batch") of
"ok" ->
@@ -777,7 +780,7 @@ couch_doc_open(Db, DocId, Rev, Options) ->
% Attachment request handlers
-db_attachment_req(#httpd{method='GET'}=Req, Db, DocId, FileNameParts) ->
+db_attachment_req(#httpd{method='GET',mochi_req=MochiReq}=Req, Db, DocId, FileNameParts) ->
FileName = list_to_binary(mochiweb_util:join(lists:map(fun binary_to_list/1,
FileNameParts),"/")),
#doc_query_args{
@@ -796,16 +799,6 @@ db_attachment_req(#httpd{method='GET'}=Req, Db, DocId, FileNameParts) ->
atom_to_list(Enc),
couch_httpd:accepted_encodings(Req)
),
- Headers = [
- {"ETag", Etag},
- {"Cache-Control", "must-revalidate"},
- {"Content-Type", binary_to_list(Type)}
- ] ++ case ReqAcceptsAttEnc of
- true ->
- [{"Content-Encoding", atom_to_list(Enc)}];
- _ ->
- []
- end,
Len = case {Enc, ReqAcceptsAttEnc} of
{identity, _} ->
% stored and served in identity form
@@ -825,6 +818,23 @@ db_attachment_req(#httpd{method='GET'}=Req, Db, DocId, FileNameParts) ->
% header we'll fall back to a chunked response.
undefined
end,
+ Headers = [
+ {"ETag", Etag},
+ {"Cache-Control", "must-revalidate"},
+ {"Content-Type", binary_to_list(Type)}
+ ] ++ case ReqAcceptsAttEnc of
+ true when Enc =/= identity ->
+ % RFC 2616 says that the 'identify' encoding should not be used in
+ % the Content-Encoding header
+ [{"Content-Encoding", atom_to_list(Enc)}];
+ _ ->
+ []
+ end ++ case Enc of
+ identity ->
+ [{"Accept-Ranges", "bytes"}];
+ _ ->
+ [{"Accept-Ranges", "none"}]
+ end,
AttFun = case ReqAcceptsAttEnc of
false ->
fun couch_doc:att_foldl_decode/3;
@@ -838,11 +848,29 @@ db_attachment_req(#httpd{method='GET'}=Req, Db, DocId, FileNameParts) ->
case Len of
undefined ->
{ok, Resp} = start_chunked_response(Req, 200, Headers),
- AttFun(Att, fun(Seg, _) -> send_chunk(Resp, Seg) end, ok),
+ AttFun(Att, fun(Seg, _) -> send_chunk(Resp, Seg) end, {ok, Resp}),
couch_httpd:last_chunk(Resp);
_ ->
- {ok, Resp} = start_response_length(Req, 200, Headers, Len),
- AttFun(Att, fun(Seg, _) -> send(Resp, Seg) end, ok)
+ Ranges = parse_ranges(MochiReq:get(range), Len),
+ case {Enc, Ranges} of
+ {identity, [{From, To}]} ->
+ Headers1 = [{<<"Content-Range">>, make_content_range(From, To, Len)}]
+ ++ Headers,
+ {ok, Resp} = start_response_length(Req, 206, Headers1, To - From + 1),
+ couch_doc:range_att_foldl(Att, From, To + 1,
+ fun(Seg, _) -> send(Resp, Seg) end, {ok, Resp});
+ {identity, Ranges} when is_list(Ranges) ->
+ send_ranges_multipart(Req, Type, Len, Att, Ranges);
+ _ ->
+ Headers1 = Headers ++
+ if Enc =:= identity orelse ReqAcceptsAttEnc =:= true ->
+ [{"Content-MD5", base64:encode(Att#att.md5)}];
+ true ->
+ []
+ end,
+ {ok, Resp} = start_response_length(Req, 200, Headers1, Len),
+ AttFun(Att, fun(Seg, _) -> send(Resp, Seg) end, {ok, Resp})
+ end
end
end
)
@@ -931,6 +959,51 @@ db_attachment_req(#httpd{method=Method, user_ctx=Ctx}=Req, Db, DocId, FileNamePa
db_attachment_req(Req, _Db, _DocId, _FileNameParts) ->
send_method_not_allowed(Req, "DELETE,GET,HEAD,PUT").
+send_ranges_multipart(Req, ContentType, Len, Att, Ranges) ->
+ Boundary = couch_uuids:random(),
+ CType = {"Content-Type",
+ "multipart/byteranges; boundary=\"" ++ ?b2l(Boundary) ++ "\""},
+ {ok, Resp} = start_chunked_response(Req, 206, [CType]),
+ couch_httpd:send_chunk(Resp, <<"--", Boundary/binary>>),
+ lists:foreach(fun({From, To}) ->
+ ContentRange = make_content_range(From, To, Len),
+ couch_httpd:send_chunk(Resp,
+ <<"\r\nContent-Type: ", ContentType/binary, "\r\n",
+ "Content-Range: ", ContentRange/binary, "\r\n",
+ "\r\n">>),
+ couch_doc:range_att_foldl(Att, From, To + 1,
+ fun(Seg, _) -> send_chunk(Resp, Seg) end, {ok, Resp}),
+ couch_httpd:send_chunk(Resp, <<"\r\n--", Boundary/binary>>)
+ end, Ranges),
+ couch_httpd:send_chunk(Resp, <<"--">>),
+ couch_httpd:last_chunk(Resp),
+ {ok, Resp}.
+
+parse_ranges(undefined, _Len) ->
+ undefined;
+parse_ranges(fail, _Len) ->
+ undefined;
+parse_ranges(Ranges, Len) ->
+ parse_ranges(Ranges, Len, []).
+
+parse_ranges([], _Len, Acc) ->
+ lists:reverse(Acc);
+parse_ranges([{From, To}|_], _Len, _Acc)
+ when is_integer(From) andalso is_integer(To) andalso To < From ->
+ throw(requested_range_not_satisfiable);
+parse_ranges([{From, To}|Rest], Len, Acc)
+ when is_integer(To) andalso To >= Len ->
+ parse_ranges([{From, Len-1}] ++ Rest, Len, Acc);
+parse_ranges([{none, To}|Rest], Len, Acc) ->
+ parse_ranges([{Len - To, Len - 1}] ++ Rest, Len, Acc);
+parse_ranges([{From, none}|Rest], Len, Acc) ->
+ parse_ranges([{From, Len - 1}] ++ Rest, Len, Acc);
+parse_ranges([{From,To}|Rest], Len, Acc) ->
+ parse_ranges(Rest, Len, [{From, To}] ++ Acc).
+
+make_content_range(From, To, Len) ->
+ ?l2b(io_lib:format("bytes ~B-~B/~B", [From, To, Len])).
+
get_md5_header(Req) ->
ContentMD5 = couch_httpd:header_value(Req, "Content-MD5"),
Length = couch_httpd:body_length(Req),
View
7 src/chttpd_view.erl
@@ -187,6 +187,8 @@ parse_json_view_param({<<"limit">>, V}) when is_integer(V), V > 0 ->
[{limit, V}];
parse_json_view_param({<<"stale">>, <<"ok">>}) ->
[{stale, ok}];
+parse_json_view_param({<<"stale">>, <<"update_after">>}) ->
+ [{stale, update_after}];
parse_json_view_param({<<"descending">>, V}) when is_boolean(V) ->
[{descending, V}];
parse_json_view_param({<<"skip">>, V}) when is_integer(V) ->
@@ -229,8 +231,11 @@ parse_view_param("count", _Value) ->
throw({query_parse_error, <<"Query parameter 'count' is now 'limit'.">>});
parse_view_param("stale", "ok") ->
[{stale, ok}];
+parse_view_param("stale", "update_after") ->
+ [{stale, update_after}];
parse_view_param("stale", _Value) ->
- throw({query_parse_error, <<"stale only available as stale=ok">>});
+ throw({query_parse_error,
+ <<"stale only available as stale=ok or as stale=update_after">>});
parse_view_param("update", _Value) ->
throw({query_parse_error, <<"update=false is now stale=ok">>});
parse_view_param("descending", Value) ->

No commit comments for this range

Something went wrong with that request. Please try again.