You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by ja...@apache.org on 2008/06/01 20:25:56 UTC
svn commit: r662261 [3/3] - in /incubator/couchdb/branches/runtimeconfig: ./
bin/ share/server/ share/www/script/ share/www/style/ src/couchdb/
src/fulltext/ src/mochiweb/
Modified: incubator/couchdb/branches/runtimeconfig/src/couchdb/couch_doc.erl
URL: http://svn.apache.org/viewvc/incubator/couchdb/branches/runtimeconfig/src/couchdb/couch_doc.erl?rev=662261&r1=662260&r2=662261&view=diff
==============================================================================
--- incubator/couchdb/branches/runtimeconfig/src/couchdb/couch_doc.erl (original)
+++ incubator/couchdb/branches/runtimeconfig/src/couchdb/couch_doc.erl Sun Jun 1 11:25:56 2008
@@ -51,8 +51,10 @@
true -> % return the full rev list and the binaries as strings.
BinProps = lists:map(
fun({Name, {Type, BinValue}}) ->
- {Name, {obj, [{"content-type", Type},
- {"data", couch_util:encodeBase64(bin_to_binary(BinValue))}]}}
+ {Name, {obj, [
+ {"content_type", Type},
+ {"data", couch_util:encodeBase64(bin_to_binary(BinValue))}
+ ]}}
end,
Doc#doc.attachments),
case BinProps of
@@ -62,8 +64,11 @@
false ->
BinProps = lists:map(
fun({Name, {Type, BinValue}}) ->
- {Name, {obj, [{"stub", true}, {"content-type", Type},
- {"length", bin_size(BinValue)}]}}
+ {Name, {obj, [
+ {"stub", true},
+ {"content_type", Type},
+ {"length", bin_size(BinValue)}
+ ]}}
end,
Doc#doc.attachments),
case BinProps of
@@ -81,7 +86,7 @@
[{Name, stub}];
_ ->
Value = proplists:get_value("data", BinProps),
- Type = proplists:get_value("content-type", BinProps,
+ Type = proplists:get_value("content_type", BinProps,
?DEFAULT_ATTACHMENT_CONTENT_TYPE),
[{Name, {Type, couch_util:decodeBase64(Value)}}]
end
Modified: incubator/couchdb/branches/runtimeconfig/src/couchdb/couch_httpd.erl
URL: http://svn.apache.org/viewvc/incubator/couchdb/branches/runtimeconfig/src/couchdb/couch_httpd.erl?rev=662261&r1=662260&r2=662261&view=diff
==============================================================================
--- incubator/couchdb/branches/runtimeconfig/src/couchdb/couch_httpd.erl (original)
+++ incubator/couchdb/branches/runtimeconfig/src/couchdb/couch_httpd.erl Sun Jun 1 11:25:56 2008
@@ -15,6 +15,9 @@
-export([start_link/0, stop/0, handle_request/2]).
+% Maximum size of document PUT request body (4GB)
+-define(MAX_DOC_SIZE, (4*1024*1024*1024)).
+
-record(doc_query_args, {
options = [],
rev = "",
@@ -52,7 +55,6 @@
mochiweb_http:stop(?MODULE).
handle_request(Req, DocumentRoot) ->
-
% alias HEAD to GET as mochiweb takes care of stripping the body
Method = case Req:get(method) of
'HEAD' -> 'GET';
@@ -63,10 +65,12 @@
% removed, but URL quoting left intact
{Path, _, _} = mochiweb_util:urlsplit_path(Req:get(raw_path)),
- ?LOG_DEBUG("Version: ~p", [Req:get(version)]),
- ?LOG_DEBUG("Method: ~p", [Method]),
- ?LOG_DEBUG("Request URI: ~p", [Path]),
- ?LOG_DEBUG("Headers: ~p", [mochiweb_headers:to_list(Req:get(headers))]),
+ ?LOG_DEBUG("~s ~s ~p~nHeaders: ~p", [
+ atom_to_list(Req:get(method)),
+ Path,
+ Req:get(version),
+ mochiweb_headers:to_list(Req:get(headers))
+ ]),
{ok, Resp} = case catch(handle_request(Req, DocumentRoot, Method, Path)) of
{ok, Resp0} ->
@@ -93,18 +97,25 @@
handle_welcome_request(Req, Method);
"/_all_dbs" ->
handle_all_dbs_request(Req, Method);
- "/favicon.ico" ->
- {ok, Req:serve_file("favicon.ico", DocumentRoot)};
"/_replicate" ->
handle_replicate_request(Req, Method);
+ "/_restart" ->
+ handle_restart_request(Req, Method);
"/_utils" ->
{ok, Req:respond({301, [{"Location", "/_utils/"}], <<>>})};
"/_utils/" ++ PathInfo ->
{ok, Req:serve_file(PathInfo, DocumentRoot)};
+<<<<<<< .working
"/_config/" ++ Config ->
handle_config_request(Req, Method, {config, Config});
"/_" ++ UnknownPrivatePath ->
handle_unkown_private_uri_request(Req, Method, UnknownPrivatePath);
+=======
+ "/_" ++ _Path ->
+ throw({not_found, unknown_private_path});
+ "/favicon.ico" ->
+ {ok, Req:serve_file("favicon.ico", DocumentRoot)};
+>>>>>>> .merge-right.r660315
_Else ->
handle_db_request(Req, Method, {Path})
end.
@@ -138,17 +149,12 @@
handle_replicate_request(_Req, _Method) ->
throw({method_not_allowed, "POST"}).
-handle_unkown_private_uri_request(Req, _Method, UnknownPrivatePath) ->
- KnownPrivatePaths = ["_utils"],
- Msg = {obj,
- [
- {error, "Could not find the private path '_" ++
- mochiweb_util:unquote(UnknownPrivatePath) ++
- "'. Known private path(s): '" ++
- lists:flatten(KnownPrivatePaths) ++ "'"}
- ]
- },
- send_error(Req, 404, Msg).
+handle_restart_request(Req, 'POST') ->
+ couch_server:remote_restart(),
+ send_json(Req, {obj, [{ok, true}]});
+
+handle_restart_request(_Req, _Method) ->
+ throw({method_not_allowed, "POST"}).
% Database request handlers
@@ -179,7 +185,7 @@
handle_db_request(Req, 'DELETE', {DbName, _Db, []}) ->
ok = couch_server:delete(DbName),
- send_json(Req, 202, {obj, [
+ send_json(Req, 200, {obj, [
{ok, true}
]});
@@ -361,7 +367,7 @@
direction = Dir,
start_docid = StartDocId,
end_docid = EndDocId
- } = QueryArgs = parse_view_query(Req),
+ } = QueryArgs = parse_view_query(Req),
case couch_view:get_map_view({DbName, "_design/" ++ DocId, ViewName}) of
{ok, View} ->
{ok, RowCount} = couch_view:get_row_count(View),
@@ -393,6 +399,14 @@
{missing_revs, {obj, JsonResults}}
]});
+handle_db_request(Req, 'POST', {_DbName, Db, ["_increment_update_seq"]}) ->
+ % NOTE, use at own risk. This functionality is experimental
+ % and might go away entirely.
+ {ok, NewSeq} = couch_db:increment_update_seq(Db),
+ send_json(Req, {obj, [{ok, true},
+ {update_seq, NewSeq}
+ ]});
+
handle_db_request(Req, 'POST', {DbName, _Db, ["_temp_view"]}) ->
#view_query_args{
start_key = StartKey,
@@ -466,7 +480,7 @@
throw({bad_request, "Document rev and etag have different values"})
end,
{ok, NewRev} = couch_db:delete_doc(Db, DocId, [RevToDelete]),
- send_json(Req, 202, {obj, [
+ send_json(Req, 200, {obj, [
{ok, true},
{id, DocId},
{rev, NewRev}
@@ -481,31 +495,30 @@
case Revs of
[] ->
case Rev of
- "" ->
- % open most recent rev
+ "" -> % open most recent rev
case couch_db:open_doc(Db, DocId, Options) of
- {ok, #doc{revs=[DocRev|_]}=Doc} ->
- Etag = none_match(Req, DocRev),
- JsonDoc = couch_doc:to_json_obj(Doc, Options),
- AdditionalHeaders =
- case Doc#doc.meta of
- [] -> [{"Etag", Etag}]; % output etag when we have no meta
- _ -> []
- end,
- send_json(Req, 200, AdditionalHeaders, JsonDoc);
- Error ->
- throw(Error)
+ {ok, #doc{revs=[DocRev|_]}=Doc} ->
+ true;
+ Error ->
+ Doc = DocRev = undefined,
+ throw(Error)
end;
- _ ->
- % open a specific rev (deletions come back as stubs)
+ _ -> % open a specific rev (deletions come back as stubs)
case couch_db:open_doc_revs(Db, DocId, [Rev], Options) of
- {ok, [{ok, Doc}]} ->
- send_json(Req, 200, [],
- couch_doc:to_json_obj(Doc, Options));
- {ok, [Else]} ->
- throw(Else)
+ {ok, [{ok, Doc}]} ->
+ DocRev = Rev;
+ {ok, [Else]} ->
+ Doc = DocRev = undefined,
+ throw(Else)
end
- end;
+ end,
+ Etag = none_match(Req, DocRev),
+ AdditionalHeaders = case Doc#doc.meta of
+ [] -> [{"Etag", Etag}]; % output etag when we have no meta
+ _ -> []
+ end,
+ JsonDoc = couch_doc:to_json_obj(Doc, Options),
+ send_json(Req, 200, AdditionalHeaders, JsonDoc);
_ ->
{ok, Results} = couch_db:open_doc_revs(Db, DocId, Revs, Options),
Resp = start_json_response(Req, 200),
@@ -532,7 +545,7 @@
end;
handle_doc_request(Req, 'PUT', _DbName, Db, DocId) ->
- Json = {obj, DocProps} = cjson:decode(Req:recv_body()),
+ Json = {obj, DocProps} = cjson:decode(Req:recv_body(?MAX_DOC_SIZE)),
DocRev = proplists:get_value("_rev", DocProps),
Etag = case Req:get_header_value("If-Match") of
undefined ->
Modified: incubator/couchdb/branches/runtimeconfig/src/couchdb/couch_stream.erl
URL: http://svn.apache.org/viewvc/incubator/couchdb/branches/runtimeconfig/src/couchdb/couch_stream.erl?rev=662261&r1=662260&r2=662261&view=diff
==============================================================================
--- incubator/couchdb/branches/runtimeconfig/src/couchdb/couch_stream.erl (original)
+++ incubator/couchdb/branches/runtimeconfig/src/couchdb/couch_stream.erl Sun Jun 1 11:25:56 2008
@@ -80,6 +80,7 @@
copy_to_new_stream(Src, Sp, Len, DestFd) ->
{ok, Dest} = open(DestFd),
+ ok = set_min_buffer(Dest, 0),
{ok, NewSp} = copy(Src, Sp, Len, Dest),
close(Dest),
{ok, NewSp}.
Added: incubator/couchdb/branches/runtimeconfig/src/couchdb/couchdb_config_writer.erl
URL: http://svn.apache.org/viewvc/incubator/couchdb/branches/runtimeconfig/src/couchdb/couchdb_config_writer.erl?rev=662261&view=auto
==============================================================================
--- incubator/couchdb/branches/runtimeconfig/src/couchdb/couchdb_config_writer.erl (added)
+++ incubator/couchdb/branches/runtimeconfig/src/couchdb/couchdb_config_writer.erl Sun Jun 1 11:25:56 2008
@@ -0,0 +1,87 @@
+-module(couch_ini_writer).
+-export([save_change_to_file/2]).
+
+save_change_to_file(Config, File) ->
+ {ok, Stream} = file:read_file(File),
+ {ok, Lines} = regexp:split(binary_to_list(Stream), "\r\n|\n|\r|\032"),
+
+ NewFileContents = save_loop(Config, Lines, "", "", []),
+
+ save_file(File, NewFileContents),
+ file:close(Stream),
+ ok.
+
+save_loop({{Module, Variable}, Value}, [Line|Rest], OldCurrentModule, Contents, DoneVariables) ->
+
+ % if we find a new [ini section] (Module), save that for reference
+ NewCurrentModule = parse_module(Line, OldCurrentModule),
+
+ % if the current Module is the one we want to change, try to match
+ % each line with the Variable
+ NewContents = case Module of
+ NewCurrentModule ->
+ % see if the current line matches the variable we want to substitute
+ case parse_variable(Line, Variable, Value) of
+ % nope, return original line
+ nomatch ->
+ DoneVariables2 = DoneVariables,
+ Line;
+ % got em! return new line
+ NewLine ->
+ DoneVariables2 = [Variable|DoneVariables],
+ NewLine
+ end;
+ % if the variable we want to change couldn't be replaced, we append it
+ % in the proper module section
+ OldCurrentModule ->
+ case lists:member(Variable, DoneVariables) of
+ false ->
+ DoneVariables2 = [Variable|DoneVariables],
+ Variable ++ "=" ++ Value ++ "\n" ++ Line;
+ true ->
+ DoneVariables2 = DoneVariables,
+ Line
+ end;
+ % otherwise we just print out the original line
+ _ ->
+ DoneVariables2 = DoneVariables,
+ Line
+ end,
+ % clumsy way to only append a newline character
+ % if the line is not empty. We need this to not
+ % avoid haveing a newline inserted at the top
+ % of the target file each time we save it.
+ Contents2 = case Contents of "" -> ""; _ -> Contents ++ "\n" end,
+
+ % go to next line
+ save_loop({{Module, Variable}, Value}, Rest, NewCurrentModule, Contents2 ++ NewContents, DoneVariables2);
+
+save_loop(_Config, [], _OldModule, NewFileContents, _DoneVariable) ->
+ % we're out of new lines, just return the new file's contents
+ NewFileContents.
+
+parse_module(Line, OldModule) ->
+ case regexp:match(Line, "^\\[([a-zA-Z0-9_-]*)\\]$") of
+ nomatch ->
+ OldModule;
+ {error, Error} ->
+ io:format("ini file regex error module: '~s'~n", [Error]),
+ OldModule;
+ {match, Start, Length} ->
+ string:substr(Line, Start, Length)
+ end.
+
+parse_variable(Line, Variable, Value) ->
+ case regexp:match(Line, "^" ++ Variable ++ "=") of
+ nomatch ->
+ nomatch;
+ {error, Error}->
+ io:format("ini file regex error variable: '~s'~n", [Error]),
+ nomatch;
+ {match, _Start, _Length} ->
+ Variable ++ "=" ++ Value
+ end.
+
+
+save_file(File, Contents) ->
+ file:write_file(File, list_to_binary(Contents)).
\ No newline at end of file
Modified: incubator/couchdb/branches/runtimeconfig/src/mochiweb/Makefile.am
URL: http://svn.apache.org/viewvc/incubator/couchdb/branches/runtimeconfig/src/mochiweb/Makefile.am?rev=662261&r1=662260&r2=662261&view=diff
==============================================================================
--- incubator/couchdb/branches/runtimeconfig/src/mochiweb/Makefile.am (original)
+++ incubator/couchdb/branches/runtimeconfig/src/mochiweb/Makefile.am Sun Jun 1 11:25:56 2008
@@ -12,7 +12,7 @@
datarootdir = @prefix@/share
-mochiwebebindir = $(erlanglibdir)/mochiweb-r73/ebin
+mochiwebebindir = $(erlanglibdir)/mochiweb-r76/ebin
mochiweb_file_collection = \
mochifmt.erl \
Modified: incubator/couchdb/branches/runtimeconfig/src/mochiweb/mochiweb.erl
URL: http://svn.apache.org/viewvc/incubator/couchdb/branches/runtimeconfig/src/mochiweb/mochiweb.erl?rev=662261&r1=662260&r2=662261&view=diff
==============================================================================
--- incubator/couchdb/branches/runtimeconfig/src/mochiweb/mochiweb.erl (original)
+++ incubator/couchdb/branches/runtimeconfig/src/mochiweb/mochiweb.erl Sun Jun 1 11:25:56 2008
@@ -77,6 +77,14 @@
Method,
Uri,
Version,
+ mochiweb_headers:make(Headers));
+%% Request-URI is "*"
+%% From http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.2
+new_request({Socket, {Method, '*'=Uri, Version}, Headers}) ->
+ mochiweb_request:new(Socket,
+ Method,
+ Uri,
+ Version,
mochiweb_headers:make(Headers)).
%% @spec new_response({Request, integer(), Headers}) -> MochiWebResponse
Modified: incubator/couchdb/branches/runtimeconfig/src/mochiweb/mochiweb_request.erl
URL: http://svn.apache.org/viewvc/incubator/couchdb/branches/runtimeconfig/src/mochiweb/mochiweb_request.erl?rev=662261&r1=662260&r2=662261&view=diff
==============================================================================
--- incubator/couchdb/branches/runtimeconfig/src/mochiweb/mochiweb_request.erl (original)
+++ incubator/couchdb/branches/runtimeconfig/src/mochiweb/mochiweb_request.erl Sun Jun 1 11:25:56 2008
@@ -325,7 +325,8 @@
andalso get_header_value("connection") =/= "Keep-Alive")
%% unread data left on the socket, can't safely continue
orelse (DidNotRecv
- andalso get_header_value("content-length") =/= undefined).
+ andalso get_header_value("content-length") =/= undefined
+ andalso list_to_integer(get_header_value("content-length")) > 0).
%% @spec cleanup() -> ok
%% @doc Clean up any junk in the process dictionary, required before continuing
@@ -454,15 +455,17 @@
%% @spec serve_file(Path, DocRoot) -> Response
%% @doc Serve a file relative to DocRoot.
serve_file(Path, DocRoot) ->
- FullPath = filename:join([DocRoot, Path]),
- File = case filelib:is_dir(FullPath) of
- true ->
- filename:join([FullPath, "index.html"]);
- false ->
- FullPath
- end,
- case lists:prefix(DocRoot, File) of
- true ->
+ case mochiweb_util:safe_relative_path(Path) of
+ undefined ->
+ not_found();
+ RelPath ->
+ FullPath = filename:join([DocRoot, RelPath]),
+ File = case filelib:is_dir(FullPath) of
+ true ->
+ filename:join([FullPath, "index.html"]);
+ false ->
+ FullPath
+ end,
case file:read_file_info(File) of
{ok, FileInfo} ->
LastModified = httpd_util:rfc1123_date(FileInfo#file_info.mtime),
@@ -482,9 +485,7 @@
end;
{error, _} ->
not_found()
- end;
- false ->
- not_found()
+ end
end.
Modified: incubator/couchdb/branches/runtimeconfig/src/mochiweb/mochiweb_util.erl
URL: http://svn.apache.org/viewvc/incubator/couchdb/branches/runtimeconfig/src/mochiweb/mochiweb_util.erl?rev=662261&r1=662260&r2=662261&view=diff
==============================================================================
--- incubator/couchdb/branches/runtimeconfig/src/mochiweb/mochiweb_util.erl (original)
+++ incubator/couchdb/branches/runtimeconfig/src/mochiweb/mochiweb_util.erl Sun Jun 1 11:25:56 2008
@@ -11,6 +11,7 @@
-export([guess_mime/1, parse_header/1]).
-export([shell_quote/1, cmd/1, cmd_string/1, cmd_port/2]).
-export([record_to_proplist/2, record_to_proplist/3]).
+-export([safe_relative_path/1, partition/2]).
-export([to_lower/1]).
-export([test/0]).
@@ -32,6 +33,69 @@
unhexdigit(C) when C >= $a, C =< $f -> C - $a + 10;
unhexdigit(C) when C >= $A, C =< $F -> C - $A + 10.
+%% @spec partition(String, Sep) -> {String, [], []} | {Prefix, Sep, Postfix}
+%% @doc Inspired by Python 2.5's str.partition:
+%% partition("foo/bar", "/") = {"foo", "/", "bar"},
+%% partition("foo", "/") = {"foo", "", ""}.
+partition(String, Sep) ->
+ case partition(String, Sep, []) of
+ undefined ->
+ {String, "", ""};
+ Result ->
+ Result
+ end.
+
+partition("", _Sep, _Acc) ->
+ undefined;
+partition(S, Sep, Acc) ->
+ case partition2(S, Sep) of
+ undefined ->
+ [C | Rest] = S,
+ partition(Rest, Sep, [C | Acc]);
+ Rest ->
+ {lists:reverse(Acc), Sep, Rest}
+ end.
+
+partition2(Rest, "") ->
+ Rest;
+partition2([C | R1], [C | R2]) ->
+ partition2(R1, R2);
+partition2(_S, _Sep) ->
+ undefined.
+
+
+
+%% @spec safe_relative_path(string()) -> string() | undefined
+%% @doc Return the reduced version of a relative path or undefined if it
+%% is not safe. safe relative paths can be joined with an absolute path
+%% and will result in a subdirectory of the absolute path.
+safe_relative_path("/" ++ _) ->
+ undefined;
+safe_relative_path(P) ->
+ safe_relative_path(P, []).
+
+safe_relative_path("", Acc) ->
+ case Acc of
+ [] ->
+ "";
+ _ ->
+ join(lists:reverse(Acc), "/")
+ end;
+safe_relative_path(P, Acc) ->
+ case partition(P, "/") of
+ {"", "/", _} ->
+ %% /foo or foo//bar
+ undefined;
+ {"..", _, _} when Acc =:= [] ->
+ undefined;
+ {"..", _, Rest} ->
+ safe_relative_path(Rest, tl(Acc));
+ {Part, "/", ""} ->
+ safe_relative_path("", ["", Part | Acc]);
+ {Part, _, Rest} ->
+ safe_relative_path(Rest, [Part | Acc])
+ end.
+
%% @spec shell_quote(string()) -> string()
%% @doc Quote a string according to UNIX shell quoting rules, returns a string
%% surrounded by double quotes.
@@ -400,6 +464,8 @@
test_shell_quote(),
test_cmd(),
test_cmd_string(),
+ test_partition(),
+ test_safe_relative_path(),
ok.
test_shell_quote() ->
@@ -499,3 +565,25 @@
[{"foo", "bar"}, {"baz", "wibble \r\n"}, {"z", "1"}] =
parse_qs("foo=bar&baz=wibble+%0D%0A&z=1"),
ok.
+
+test_partition() ->
+ {"foo", "", ""} = partition("foo", "/"),
+ {"foo", "/", "bar"} = partition("foo/bar", "/"),
+ {"foo", "/", ""} = partition("foo/", "/"),
+ {"", "/", "bar"} = partition("/bar", "/"),
+ {"f", "oo/ba", "r"} = partition("foo/bar", "oo/ba"),
+ ok.
+
+test_safe_relative_path() ->
+ "foo" = safe_relative_path("foo"),
+ "foo/" = safe_relative_path("foo/"),
+ "foo" = safe_relative_path("foo/bar/.."),
+ "bar" = safe_relative_path("foo/../bar"),
+ "bar/" = safe_relative_path("foo/../bar/"),
+ "" = safe_relative_path("foo/.."),
+ "" = safe_relative_path("foo/../"),
+ undefined = safe_relative_path("/foo"),
+ undefined = safe_relative_path("../foo"),
+ undefined = safe_relative_path("foo/../.."),
+ undefined = safe_relative_path("foo//"),
+ ok.