Permalink
Browse files

introduced configurable couch database frontend

All http requests (except _design/** and _all_docs) are performed
through configurable db-frontend module. Default implementation
performs operations normally though couchdb internals. But by choosing
other db-frontend module it's possible to intercept database
operations.

_design/** and _all_docs are already intercept-able via normal couch
configuration.

Change-Id: I5661c25cd959cef9d6e27fbb0befffe6bab51b04
Reviewed-on: http://review.couchbase.org/7600
Tested-by: Aliaksey Kandratsenka <alkondratenko@gmail.com>
Reviewed-by: Filipe David Borba Manana <fdmanana@gmail.com>
  • Loading branch information...
Aliaksey Kandratsenka authored and dustin committed Jun 26, 2011
1 parent d6090ed commit 413ddf8deecc534d41071af215cbc45eb9552f7d
@@ -37,6 +37,7 @@ default_handler = {couch_httpd_db, handle_request}
secure_rewrites = true
vhost_global_handlers = _utils, _uuids, _session, _oauth, _users
allow_jsonp = false
+db_frontend = couch_db_frontend
; Options for the MochiWeb HTTP server.
;server_options = [{backlog, 128}, {acceptor_pool_size, 16}]
; For more socket options, consult Erlang's module 'inet' man page.
View
@@ -48,6 +48,7 @@ source_files = \
couch_external_manager.erl \
couch_external_server.erl \
couch_file.erl \
+ couch_db_frontend.erl \
couch_httpc_pool.erl \
couch_httpd.erl \
couch_httpd_db.erl \
@@ -120,6 +121,7 @@ compiled_files = \
couch_external_manager.beam \
couch_external_server.beam \
couch_file.beam \
+ couch_db_frontend.beam \
couch_httpc_pool.beam \
couch_httpd.beam \
couch_httpd_db.beam \
@@ -105,7 +105,7 @@ os_filter_fun(FilterName, Style, Req, Db) ->
end;
[DName, FName] ->
DesignId = <<"_design/", DName/binary>>,
- DDoc = couch_httpd_db:couch_doc_open(Db, DesignId, nil, [ejson_body]),
+ DDoc = couch_db_frontend:couch_doc_open(Db, DesignId, nil, [ejson_body]),
% validate that the ddoc has the filter fun
#doc{body={Props}} = DDoc,
couch_util:get_nested_json_value({Props}, [<<"filters">>, FName]),
@@ -178,7 +178,7 @@ filter_view(ViewName, Style, Db) ->
throw({bad_request, "Invalid `view` parameter."});
[DName, VName] ->
DesignId = <<"_design/", DName/binary>>,
- DDoc = couch_httpd_db:couch_doc_open(Db, DesignId, nil, [ejson_body]),
+ DDoc = couch_db_frontend:couch_doc_open(Db, DesignId, nil, [ejson_body]),
% validate that the ddoc has the filter fun
#doc{body={Props}} = DDoc,
couch_util:get_nested_json_value({Props}, [<<"views">>, VName]),
View
@@ -74,6 +74,7 @@
method,
requested_path_parts,
path_parts,
+ db_frontend,
db_url_handlers,
user_ctx,
req_body = undefined,
@@ -0,0 +1,181 @@
+% Licensed under the Apache License, Version 2.0 (the "License"); you may not
+% use this file except in compliance with the License. You may obtain a copy of
+% the License at
+%
+% http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+% License for the specific language governing permissions and limitations under
+% the License.
+
+-module(couch_db_frontend).
+
+-include("couch_db.hrl").
+
+-compile(export_all).
+
+do_db_req(#httpd{user_ctx=UserCtx,path_parts=[DbName|_]}=Req, Fun) ->
+ case couch_db:open(DbName, [{user_ctx, UserCtx}]) of
+ {ok, Db} ->
+ try
+ Fun(Req, Db)
+ after
+ catch couch_db:close(Db)
+ end;
+ Error ->
+ throw(Error)
+ end.
+
+get_db_info(Db) ->
+ couch_db:get_db_info(Db).
+
+update_doc(Db, Doc, Options) ->
+ couch_db:update_doc(Db, Doc, Options).
+
+update_doc(Db, Doc, Options, UpdateType) ->
+ couch_db:update_doc(Db, Doc, Options, UpdateType).
+
+-spec ensure_full_commit(any(), integer()) -> {ok, binary()}.
+ensure_full_commit(Db, RequiredSeq) ->
+ UpdateSeq = couch_db:get_update_seq(Db),
+ CommittedSeq = couch_db:get_committed_update_seq(Db),
+ case RequiredSeq of
+ undefined ->
+ couch_db:ensure_full_commit(Db);
+ _ ->
+ if RequiredSeq > UpdateSeq ->
+ throw({bad_request,
+ "can't do a full commit ahead of current update_seq"});
+ RequiredSeq > CommittedSeq ->
+ couch_db:ensure_full_commit(Db);
+ true ->
+ {ok, Db#db.instance_start_time}
+ end
+ end.
+
+check_is_admin(Db) ->
+ couch_db:check_is_admin(Db).
+
+handle_changes(ChangesArgs, Req, Db) ->
+ couch_changes:handle_changes(ChangesArgs, Req, Db).
+
+start_view_compact(DbName, GroupId) ->
+ couch_view_compactor:start_compact(DbName, GroupId).
+
+start_db_compact(Db) ->
+ couch_db:start_compact(Db).
+
+cleanup_view_index_files(Db) ->
+ couch_view:cleanup_index_files(Db).
+
+get_group_info(Db, DesignId) ->
+ couch_view:get_group_info(Db, DesignId).
+
+create_db(DbName, UserCtx) ->
+ case couch_server:create(DbName, [{user_ctx, UserCtx}]) of
+ {ok, Db} ->
+ couch_db:close(Db),
+ ok;
+ Error ->
+ Error
+ end.
+
+delete_db(DbName, UserCtx) ->
+ couch_server:delete(DbName, [{user_ctx, UserCtx}]).
+
+update_docs(Db, Docs, Options) ->
+ couch_db:update_docs(Db, Docs, Options).
+
+update_docs(Db, Docs, Options, Type) ->
+ couch_db:update_docs(Db, Docs, Options, Type).
+
+purge_docs(Db, IdsRevs) ->
+ couch_db:purge_docs(Db, IdsRevs).
+
+get_missing_revs(Db, JsonDocIdRevs) ->
+ couch_db:get_missing_revs(Db, JsonDocIdRevs).
+
+set_security(Db, SecurityObj) ->
+ couch_db:set_security(Db, SecurityObj).
+
+get_security(Db) ->
+ couch_db:get_security(Db).
+
+set_revs_limit(Db, Limit) ->
+ couch_db:set_revs_limit(Db, Limit).
+
+get_revs_limit(Db) ->
+ couch_db:get_revs_limit(Db).
+
+open_doc_revs(Db, DocId, Revs, Options) ->
+ couch_db:open_doc_revs(Db, DocId, Revs, Options).
+
+open_doc(Db, DocId, Options) ->
+ couch_db:open_doc(Db, DocId, Options).
+
+make_attachment_fold(_Att, ReqAcceptsAttEnc) ->
+ case ReqAcceptsAttEnc of
+ false -> fun couch_doc:att_foldl_decode/3;
+ _ -> fun couch_doc:att_foldl/3
+ end.
+
+range_att_foldl(Att, From, To, Fun, Acc) ->
+ couch_doc:range_att_foldl(Att, From, To, Fun, Acc).
+
+all_databases() ->
+ couch_server:all_databases().
+
+task_status_all() ->
+ couch_task_status:all().
+
+restart_core_server() ->
+ couch_server_sup:restart_core_server().
+
+config_all() ->
+ couch_config:all().
+
+config_get(Section) ->
+ couch_config:get(Section).
+
+config_get(Section, Key, Default) ->
+ couch_config:get(Section, Key, Default).
+
+config_set(Section, Key, Value, Persist) ->
+ couch_config:set(Section, Key, Value, Persist).
+
+config_delete(Section, Key, Persist) ->
+ couch_config:delete(Section, Key, Persist).
+
+increment_update_seq(Db) ->
+ couch_db:increment_update_seq(Db).
+
+stats_aggregator_all(Range) ->
+ couch_stats_aggregator:all(Range).
+
+stats_aggregator_get_json(Key, Range) ->
+ couch_stats_aggregator:get_json(Key, Range).
+
+stats_aggregator_collect_sample() ->
+ couch_stats_aggregator:collect_sample().
+
+couch_doc_open(Db, DocId, Rev, Options) ->
+ case Rev of
+ nil -> % open most recent rev
+ case open_doc(Db, DocId, Options) of
+ {ok, Doc} ->
+ Doc;
+ Error ->
+ throw(Error)
+ end;
+ _ -> % open a specific rev (deletions come back as stubs)
+ case open_doc_revs(Db, DocId, [Rev], Options) of
+ {ok, [{ok, Doc}]} ->
+ Doc;
+ {ok, [{{not_found, missing}, Rev}]} ->
+ throw(not_found);
+ {ok, [Else]} ->
+ throw(Else)
+ end
+ end.
@@ -14,7 +14,7 @@
-include("couch_db.hrl").
-export([start_link/0, start_link/1, stop/0, config_change/2,
- handle_request/5]).
+ handle_request/6]).
-export([header_value/2,header_value/3,qs_value/2,qs_value/3,qs/1,qs_json_value/3]).
-export([path/1,absolute_uri/2,body_length/1]).
@@ -28,7 +28,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,validate_referer/1,validate_ctype/2]).
+-export([accepted_encodings/1,handle_request_int/6,validate_referer/1,validate_ctype/2]).
start_link() ->
start_link(http).
@@ -86,6 +86,8 @@ start_link(Name, Options) ->
{ok, SocketOptions} = couch_util:parse_term(
couch_config:get("httpd", "socket_options", "[]")),
+ DbFrontendModule = list_to_atom(couch_config:get("httpd", "db_frontend", "couch_db_frontend")),
+
Loop = fun(Req)->
case SocketOptions of
[] ->
@@ -94,7 +96,7 @@ start_link(Name, Options) ->
ok = mochiweb_socket:setopts(Req:get(socket), SocketOptions)
end,
apply(?MODULE, handle_request, [
- Req, DefaultFun, UrlHandlers, DbUrlHandlers, DesignUrlHandlers
+ Req, DbFrontendModule, DefaultFun, UrlHandlers, DbUrlHandlers, DesignUrlHandlers
])
end,
@@ -167,15 +169,15 @@ make_arity_3_fun(SpecStr) ->
make_fun_spec_strs(SpecStr) ->
re:split(SpecStr, "(?<=})\\s*,\\s*(?={)", [{return, list}]).
-handle_request(MochiReq, DefaultFun, UrlHandlers, DbUrlHandlers,
+handle_request(MochiReq, DbFrontendModule, DefaultFun, UrlHandlers, DbUrlHandlers,
DesignUrlHandlers) ->
MochiReq1 = couch_httpd_vhost:dispatch_host(MochiReq),
-
- handle_request_int(MochiReq1, DefaultFun,
+
+ handle_request_int(MochiReq1, DbFrontendModule, DefaultFun,
UrlHandlers, DbUrlHandlers, DesignUrlHandlers).
-handle_request_int(MochiReq, DefaultFun,
+handle_request_int(MochiReq, DbFrontendModule, DefaultFun,
UrlHandlers, DbUrlHandlers, DesignUrlHandlers) ->
Begin = now(),
AuthenticationSrcs = make_fun_spec_strs(
@@ -248,6 +250,7 @@ handle_request_int(MochiReq, DefaultFun,
requested_path_parts =
[?l2b(unquote(Part)) || Part <- string:tokens(RequestedPath, "/")],
path_parts = [?l2b(unquote(Part)) || Part <- string:tokens(Path, "/")],
+ db_frontend = DbFrontendModule,
db_url_handlers = DbUrlHandlers,
design_url_handlers = DesignUrlHandlers,
default_fun = DefaultFun,
Oops, something went wrong.

0 comments on commit 413ddf8

Please sign in to comment.