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.