Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

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.
base fork: indutny/couchdb
...
head fork: indutny/couchdb
Checking mergeability… Don't worry, you can still create the pull request.
  • 9 commits
  • 16 files changed
  • 0 commit comments
  • 4 contributors
Commits on Dec 14, 2010
@kocolosk kocolosk Fix OOME when compacting doc w/ lots of conflicts, COUCHDB-888
git-svn-id: https://svn.apache.org/repos/asf/couchdb/trunk@1049247 13f79535-47bb-0310-9956-ffa450edef68
a18429b
Commits on Dec 16, 2010
@fdmanana fdmanana Removed duplicated function
An equivalent exists (couch_util:to_integer/1), but for this particular case it's not needed.


git-svn-id: https://svn.apache.org/repos/asf/couchdb/trunk@1050163 13f79535-47bb-0310-9956-ffa450edef68
bd59f60
Commits on Dec 18, 2010
@fdmanana fdmanana Use safer JSON.parse()
Thanks Jason Smith for bringing this into attention.


git-svn-id: https://svn.apache.org/repos/asf/couchdb/trunk@1050621 13f79535-47bb-0310-9956-ffa450edef68
15d91b3
@fdmanana fdmanana Upgrade ibrowse to version 2.1.1
This ibrowse release includes a few important fixes.
See https://github.com/cmullaparthi/ibrowse for the list of fixes.



git-svn-id: https://svn.apache.org/repos/asf/couchdb/trunk@1050633 13f79535-47bb-0310-9956-ffa450edef68
07f3b09
Commits on Dec 20, 2010
@fdmanana fdmanana Upgraded ibrowse to version 2.1.2 (released today)
This version fixes a blocking issue (which rarely happens) when using the same connection
(with ot without pipelining) for multiple requests using the option {stream_to, {pid(), once}}.



git-svn-id: https://svn.apache.org/repos/asf/couchdb/trunk@1051082 13f79535-47bb-0310-9956-ffa450edef68
5d3bcf5
Commits on Dec 21, 2010
@jchris jchris update benchbulk script for json content-type
git-svn-id: https://svn.apache.org/repos/asf/couchdb/trunk@1051325 13f79535-47bb-0310-9956-ffa450edef68
81b2ff9
@davisp davisp Fix 180-http-proxy tests after Mochiweb upgrade.
Content-Length and the HTTP body are returned as 0 and an empty binary
instead of undefined.



git-svn-id: https://svn.apache.org/repos/asf/couchdb/trunk@1051612 13f79535-47bb-0310-9956-ffa450edef68
41007ee
Commits on Dec 22, 2010
@fdmanana fdmanana Make sure attachments get compressed when their MIME type lists param…
…eters

Closes COUCHDB-996.


git-svn-id: https://svn.apache.org/repos/asf/couchdb/trunk@1052031 13f79535-47bb-0310-9956-ffa450edef68
15941e6
@fdmanana fdmanana Allow a multipart/mixed document GET to send the attachments in encod…
…ed (compressed) form

Currently this API is not used internally but it's a very important one for the new replicator.



git-svn-id: https://svn.apache.org/repos/asf/couchdb/trunk@1052047 13f79535-47bb-0310-9956-ffa450edef68
124c5b3
View
2  share/server/loop.js
@@ -122,7 +122,7 @@ var Loop = function() {
}
};
while (line = readline()) {
- cmd = eval('('+line+')');
+ cmd = JSON.parse(line);
State.line_length = line.length;
try {
cmdkey = cmd.shift();
View
3  share/server/render.js
@@ -175,8 +175,7 @@ var Render = (function() {
} else {
blowChunks();
}
- var line = readline();
- var json = eval('('+line+')');
+ var json = JSON.parse(readline());
if (json[0] == "list_end") {
lastRow = true;
return null;
View
128 share/www/script/test/attachments_multipart.js
@@ -278,5 +278,131 @@ couchTests.attachments_multipart= function(debug) {
T(doc._attachments['bar.txt'].follows == true);
T(sections[1].body == "this is 18 chars l");
-
+
+
+ // check that with the document multipart/mixed API it's possible to receive
+ // attachments in compressed form (if they're stored in compressed form)
+
+ var server_config = [
+ {
+ section: "attachments",
+ key: "compression_level",
+ value: "8"
+ },
+ {
+ section: "attachments",
+ key: "compressible_types",
+ value: "text/plain"
+ }
+ ];
+
+ function testMultipartAttCompression() {
+ var doc = { _id: "foobar" };
+ var lorem =
+ CouchDB.request("GET", "/_utils/script/test/lorem.txt").responseText;
+ var helloData = "hello world";
+
+ TEquals(true, db.save(doc).ok);
+
+ var firstRev = doc._rev;
+ var xhr = CouchDB.request(
+ "PUT",
+ "/" + db.name + "/" + doc._id + "/data.bin?rev=" + firstRev,
+ {
+ body: helloData,
+ headers: {"Content-Type": "application/binary"}
+ }
+ );
+ TEquals(201, xhr.status);
+
+ var secondRev = db.open(doc._id)._rev;
+ xhr = CouchDB.request(
+ "PUT",
+ "/" + db.name + "/" + doc._id + "/lorem.txt?rev=" + secondRev,
+ {
+ body: lorem,
+ headers: {"Content-Type": "text/plain"}
+ }
+ );
+ TEquals(201, xhr.status);
+
+ var thirdRev = db.open(doc._id)._rev;
+
+ xhr = CouchDB.request(
+ "GET",
+ '/' + db.name + '/' + doc._id + '?open_revs=["' + thirdRev + '"]',
+ {
+ headers: {
+ "Accept": "multipart/mixed",
+ "X-CouchDB-Send-Encoded-Atts": "true"
+ }
+ }
+ );
+ TEquals(200, xhr.status);
+
+ var sections = parseMultipart(xhr);
+ // 1 section, with a multipart/related Content-Type
+ TEquals(1, sections.length);
+ TEquals(0,
+ sections[0].headers['Content-Type'].indexOf('multipart/related;'));
+
+ var innerSections = parseMultipart(sections[0]);
+ // 3 inner sections: a document body section plus 2 attachment data sections
+ TEquals(3, innerSections.length);
+ TEquals('application/json', innerSections[0].headers['content-type']);
+
+ doc = JSON.parse(innerSections[0].body);
+
+ TEquals(true, doc._attachments['lorem.txt'].follows);
+ TEquals("gzip", doc._attachments['lorem.txt'].encoding);
+ TEquals(true, doc._attachments['data.bin'].follows);
+ T(doc._attachments['data.bin'] !== "gzip");
+
+ if (innerSections[1].body === helloData) {
+ T(innerSections[2].body !== lorem);
+ } else if (innerSections[2].body === helloData) {
+ T(innerSections[1].body !== lorem);
+ } else {
+ T(false, "Could not found data.bin attachment data");
+ }
+
+ // now test that it works together with the atts_since parameter
+
+ xhr = CouchDB.request(
+ "GET",
+ '/' + db.name + '/' + doc._id + '?open_revs=["' + thirdRev + '"]' +
+ '&atts_since=["' + secondRev + '"]',
+ {
+ headers: {
+ "Accept": "multipart/mixed",
+ "X-CouchDB-Send-Encoded-Atts": "true"
+ }
+ }
+ );
+ TEquals(200, xhr.status);
+
+ sections = parseMultipart(xhr);
+ // 1 section, with a multipart/related Content-Type
+ TEquals(1, sections.length);
+ TEquals(0,
+ sections[0].headers['Content-Type'].indexOf('multipart/related;'));
+
+ innerSections = parseMultipart(sections[0]);
+ // 2 inner sections: a document body section plus 1 attachment data section
+ TEquals(2, innerSections.length);
+ TEquals('application/json', innerSections[0].headers['content-type']);
+
+ doc = JSON.parse(innerSections[0].body);
+
+ TEquals(true, doc._attachments['lorem.txt'].follows);
+ TEquals("gzip", doc._attachments['lorem.txt'].encoding);
+ TEquals("undefined", typeof doc._attachments['data.bin'].follows);
+ TEquals(true, doc._attachments['data.bin'].stub);
+ T(innerSections[1].body !== lorem);
+ }
+
+ run_on_modified_server(server_config, testMultipartAttCompression);
+
+ // cleanup
+ db.deleteDb();
};
View
31 src/couchdb/couch_db_updater.erl
@@ -744,16 +744,6 @@ copy_doc_attachments(#db{updater_fd = SrcFd} = SrcDb, SrcSp, DestFd) ->
end, BinInfos),
{BodyData, NewBinInfos}.
-copy_rev_tree_attachments(SrcDb, DestFd, Tree) ->
- couch_key_tree:map(
- fun(_Rev, {IsDel, Sp, Seq}, leaf) ->
- DocBody = copy_doc_attachments(SrcDb, Sp, DestFd),
- {IsDel, DocBody, Seq};
- (_, _, branch) ->
- ?REV_MISSING
- end, Tree).
-
-
copy_docs(Db, #db{updater_fd = DestFd} = NewDb, InfoBySeq0, Retry) ->
% COUCHDB-968, make sure we prune duplicates during compaction
InfoBySeq = lists:usort(fun(#doc_info{id=A}, #doc_info{id=B}) -> A =< B end,
@@ -761,22 +751,17 @@ copy_docs(Db, #db{updater_fd = DestFd} = NewDb, InfoBySeq0, Retry) ->
Ids = [Id || #doc_info{id=Id} <- InfoBySeq],
LookupResults = couch_btree:lookup(Db#db.fulldocinfo_by_id_btree, Ids),
- % write out the attachments
- NewFullDocInfos0 = lists:map(
- fun({ok, #full_doc_info{rev_tree=RevTree}=Info}) ->
- Info#full_doc_info{rev_tree=copy_rev_tree_attachments(Db, DestFd, RevTree)}
- end, LookupResults),
- % write out the docs
- % we do this in 2 stages so the docs are written out contiguously, making
- % view indexing and replication faster.
NewFullDocInfos1 = lists:map(
- fun(#full_doc_info{rev_tree=RevTree}=Info) ->
- Info#full_doc_info{rev_tree=couch_key_tree:map_leafs(
- fun(_Key, {IsDel, DocBody, Seq}) ->
+ fun({ok, #full_doc_info{rev_tree=RevTree}=Info}) ->
+ Info#full_doc_info{rev_tree=couch_key_tree:map(
+ fun(_Rev, {IsDel, Sp, Seq}, leaf) ->
+ DocBody = copy_doc_attachments(Db, Sp, DestFd),
{ok, Pos} = couch_file:append_term_md5(DestFd, DocBody),
- {IsDel, Pos, Seq}
+ {IsDel, Pos, Seq};
+ (_, _, branch) ->
+ ?REV_MISSING
end, RevTree)}
- end, NewFullDocInfos0),
+ end, LookupResults),
NewFullDocInfos = stem_full_doc_infos(Db, NewFullDocInfos1),
NewDocInfos = [couch_doc:to_doc_info(Info) || Info <- NewFullDocInfos],
View
10 src/couchdb/couch_httpd_auth.erl
@@ -184,7 +184,8 @@ cookie_authentication_handler(#httpd{mochi_req=MochiReq}=Req) ->
FullSecret = <<Secret/binary, UserSalt/binary>>,
ExpectedHash = crypto:sha_mac(FullSecret, User ++ ":" ++ TimeStr),
Hash = ?l2b(string:join(HashParts, ":")),
- Timeout = to_int(couch_config:get("couch_httpd_auth", "timeout", 600)),
+ Timeout = list_to_integer(
+ couch_config:get("couch_httpd_auth", "timeout", "600")),
?LOG_DEBUG("timeout ~p", [Timeout]),
case (catch erlang:list_to_integer(TimeStr, 16)) of
TimeStamp when CurrentTime < TimeStamp + Timeout ->
@@ -341,13 +342,6 @@ auth_name(String) when is_list(String) ->
[_,_,_,_,_,Name|_] = re:split(String, "[\\W_]", [{return, list}]),
?l2b(Name).
-to_int(Value) when is_binary(Value) ->
- to_int(?b2l(Value));
-to_int(Value) when is_list(Value) ->
- list_to_integer(Value);
-to_int(Value) when is_integer(Value) ->
- Value.
-
make_cookie_time() ->
{NowMS, NowS, _} = erlang:now(),
NowMS * 1000000 + NowS.
View
16 src/couchdb/couch_httpd_db.erl
@@ -747,24 +747,30 @@ send_doc_efficiently(#httpd{mochi_req = MochiReq} = Req,
send_json(Req, 200, Headers, couch_doc:to_json_obj(Doc, Options))
end.
-send_docs_multipart(Req, Results, Options) ->
+send_docs_multipart(Req, Results, Options1) ->
OuterBoundary = couch_uuids:random(),
InnerBoundary = couch_uuids:random(),
+ {Options, CompressedAtts} =
+ case couch_httpd:header_value(Req, "X-CouchDB-Send-Encoded-Atts") of
+ "true" ->
+ {[attachments, follows, att_encoding_info | Options1], true};
+ _ ->
+ {[attachments, follows | Options1], false}
+ end,
CType = {"Content-Type",
"multipart/mixed; boundary=\"" ++ ?b2l(OuterBoundary) ++ "\""},
{ok, Resp} = start_chunked_response(Req, 200, [CType]),
couch_httpd:send_chunk(Resp, <<"--", OuterBoundary/binary>>),
lists:foreach(
fun({ok, #doc{atts=Atts}=Doc}) ->
- JsonBytes = ?JSON_ENCODE(couch_doc:to_json_obj(Doc,
- [attachments,follows|Options])),
+ JsonBytes = ?JSON_ENCODE(couch_doc:to_json_obj(Doc, Options)),
{ContentType, _Len} = couch_doc:len_doc_to_multi_part_stream(
- InnerBoundary, JsonBytes, Atts, false),
+ InnerBoundary, JsonBytes, Atts, CompressedAtts),
couch_httpd:send_chunk(Resp, <<"\r\nContent-Type: ",
ContentType/binary, "\r\n\r\n">>),
couch_doc:doc_to_multi_part_stream(InnerBoundary, JsonBytes, Atts,
fun(Data) -> couch_httpd:send_chunk(Resp, Data)
- end, false),
+ end, CompressedAtts),
couch_httpd:send_chunk(Resp, <<"\r\n--", OuterBoundary/binary>>);
({{not_found, missing}, RevId}) ->
RevStr = couch_doc:rev_to_str(RevId),
View
3  src/couchdb/couch_util.erl
@@ -398,7 +398,8 @@ compressible_att_type(MimeType) ->
),
lists:any(
fun(TypeExp) ->
- Regexp = ["^\\s*", re:replace(TypeExp, "\\*", ".*"), "\\s*$"],
+ Regexp = ["^\\s*", re:replace(TypeExp, "\\*", ".*"),
+ "(?:\\s*;.*?)?\\s*", $$],
re:run(MimeType, Regexp, [caseless]) =/= nomatch
end,
[T || T <- TypeExpList, T /= []]
View
2  src/ibrowse/Makefile.am
@@ -10,7 +10,7 @@
## License for the specific language governing permissions and limitations under
## the License.
-ibrowseebindir = $(localerlanglibdir)/ibrowse-2.1.0/ebin
+ibrowseebindir = $(localerlanglibdir)/ibrowse-2.1.2/ebin
ibrowse_file_collection = \
ibrowse.app.in \
View
2  src/ibrowse/ibrowse.app.in
@@ -1,6 +1,6 @@
{application, ibrowse,
[{description, "HTTP client application"},
- {vsn, "2.1.0"},
+ {vsn, "2.1.2"},
{modules, [ ibrowse,
ibrowse_http_client,
ibrowse_app,
View
2  src/ibrowse/ibrowse.erl
@@ -7,7 +7,7 @@
%%%-------------------------------------------------------------------
%% @author Chandrashekhar Mullaparthi <chandrashekhar dot mullaparthi at gmail dot com>
%% @copyright 2005-2010 Chandrashekhar Mullaparthi
-%% @version 2.1.0
+%% @version 2.1.2
%% @doc The ibrowse application implements an HTTP 1.1 client in erlang. This
%% module implements the API of the HTTP client. There is one named
%% process called 'ibrowse' which assists in load balancing and maintaining configuration. There is one load balancing process per unique webserver. There is
View
80 src/ibrowse/ibrowse_http_client.erl
@@ -69,7 +69,7 @@
]).
-define(DEFAULT_STREAM_CHUNK_SIZE, 1024*1024).
-
+-define(dec2hex(X), erlang:integer_to_list(X, 16)).
%%====================================================================
%% External functions
%%====================================================================
@@ -191,13 +191,21 @@ handle_info({stream_next, Req_id}, #state{socket = Socket,
{noreply, State};
handle_info({stream_next, _Req_id}, State) ->
+ _Cur_req_id = case State#state.cur_req of
+ #request{req_id = Cur} ->
+ Cur;
+ _ ->
+ undefined
+ end,
+%% io:format("Ignoring stream_next as ~1000.p is not cur req (~1000.p)~n",
+%% [_Req_id, _Cur_req_id]),
{noreply, State};
handle_info({stream_close, _Req_id}, State) ->
shutting_down(State),
do_close(State),
do_error_reply(State, closing_on_request),
- {stop, normal, ok, State};
+ {stop, normal, State};
handle_info({tcp_closed, _Sock}, State) ->
do_trace("TCP connection closed by peer!~n", []),
@@ -369,15 +377,6 @@ accumulate_response(Data, #state{cur_req = #request{save_response_to_file = Srtf
{error, Reason} ->
{error, {file_write_error, Reason}}
end;
-%% accumulate_response(<<>>, #state{cur_req = #request{caller_controls_socket = Ccs},
-%% socket = Socket} = State) ->
-%% case Ccs of
-%% true ->
-%% do_setopts(Socket, [{active, once}], State);
-%% false ->
-%% ok
-%% end,
-%% State;
accumulate_response(Data, #state{reply_buffer = RepBuf,
rep_buf_size = RepBufSize,
streamed_size = Streamed_size,
@@ -544,7 +543,7 @@ do_send_body1(Source, Resp, State, TE) ->
maybe_chunked_encode(Data, false) ->
Data;
maybe_chunked_encode(Data, true) ->
- [ibrowse_lib:dec2hex(byte_size(to_binary(Data))), "\r\n", Data, "\r\n"].
+ [?dec2hex(size(to_binary(Data))), "\r\n", Data, "\r\n"].
do_close(#state{socket = undefined}) -> ok;
do_close(#state{socket = Sock,
@@ -634,7 +633,7 @@ send_req_1(From,
Path = [Server_host, $:, integer_to_list(Server_port)],
{Req, Body_1} = make_request(connect, Pxy_auth_headers,
Path, Path,
- [], Options, State_1),
+ [], Options, State_1, undefined),
TE = is_chunked_encoding_specified(Options),
trace_request(Req),
case do_send(Req, State) of
@@ -683,8 +682,7 @@ send_req_1(From,
path = RelPath} = Url,
Headers, Method, Body, Options, Timeout,
#state{status = Status,
- socket = Socket,
- is_ssl = Is_ssl} = State) ->
+ socket = Socket} = State) ->
ReqId = make_req_id(),
Resp_format = get_value(response_format, Options, list),
Caller_socket_options = get_value(socket_options, Options, []),
@@ -721,9 +719,10 @@ send_req_1(From,
Headers_1 = maybe_modify_headers(Url, Method, Options, Headers, State_1),
{Req, Body_1} = make_request(Method,
Headers_1,
- AbsPath, RelPath, Body, Options, State_1),
+ AbsPath, RelPath, Body, Options, State_1,
+ ReqId),
trace_request(Req),
- do_setopts(Socket, Caller_socket_options, Is_ssl),
+ do_setopts(Socket, Caller_socket_options, State_1),
TE = is_chunked_encoding_specified(Options),
case do_send(Req, State_1) of
ok ->
@@ -821,7 +820,7 @@ http_auth_digest(Username, Password) ->
ibrowse_lib:encode_base64(Username ++ [$: | Password]).
make_request(Method, Headers, AbsPath, RelPath, Body, Options,
- #state{use_proxy = UseProxy, is_ssl = Is_ssl}) ->
+ #state{use_proxy = UseProxy, is_ssl = Is_ssl}, ReqId) ->
HttpVsn = http_vsn_string(get_value(http_vsn, Options, {1,1})),
Fun1 = fun({X, Y}) when is_atom(X) ->
{to_lower(atom_to_list(X)), X, Y};
@@ -831,17 +830,14 @@ make_request(Method, Headers, AbsPath, RelPath, Body, Options,
Headers_0 = [Fun1(X) || X <- Headers],
Headers_1 =
case lists:keysearch("content-length", 1, Headers_0) of
- false when (Body == []) orelse
- (Body == <<>>) orelse
- is_tuple(Body) orelse
- is_function(Body) ->
- Headers_0;
- false when is_binary(Body) ->
- [{"content-length", "content-length", integer_to_list(size(Body))} | Headers_0];
- false when is_list(Body) ->
- [{"content-length", "content-length", integer_to_list(length(Body))} | Headers_0];
+ false when (Body =:= [] orelse Body =:= <<>>) andalso
+ (Method =:= post orelse Method =:= put) ->
+ [{"content-length", "Content-Length", "0"} | Headers_0];
+ false when is_binary(Body) orelse is_list(Body) ->
+ [{"content-length", "Content-Length", integer_to_list(iolist_size(Body))} | Headers_0];
_ ->
- %% Content-Length is already specified
+ %% Content-Length is already specified or Body is a
+ %% function or function/state pair
Headers_0
end,
{Headers_2, Body_1} =
@@ -860,7 +856,13 @@ make_request(Method, Headers, AbsPath, RelPath, Body, Options,
[{"Transfer-Encoding", "chunked"}],
chunk_request_body(Body, Chunk_size_1)}
end,
- Headers_3 = cons_headers(Headers_2),
+ Headers_3 = case lists:member({include_ibrowse_req_id, true}, Options) of
+ true ->
+ [{"x-ibrowse-request-id", io_lib:format("~1000.p",[ReqId])} | Headers_2];
+ false ->
+ Headers_2
+ end,
+ Headers_4 = cons_headers(Headers_3),
Uri = case get_value(use_absolute_uri, Options, false) or UseProxy of
true ->
case Is_ssl of
@@ -872,7 +874,7 @@ make_request(Method, Headers, AbsPath, RelPath, Body, Options,
false ->
RelPath
end,
- {[method(Method), " ", Uri, " ", HttpVsn, crnl(), Headers_3, crnl()], Body_1}.
+ {[method(Method), " ", Uri, " ", HttpVsn, crnl(), Headers_4, crnl()], Body_1}.
is_chunked_encoding_specified(Options) ->
case get_value(transfer_encoding, Options, false) of
@@ -927,23 +929,23 @@ chunk_request_body(Body, _ChunkSize, Acc) when Body == <<>>; Body == [] ->
chunk_request_body(Body, ChunkSize, Acc) when is_binary(Body),
size(Body) >= ChunkSize ->
<<ChunkBody:ChunkSize/binary, Rest/binary>> = Body,
- Chunk = [ibrowse_lib:dec2hex(ChunkSize),"\r\n",
+ Chunk = [?dec2hex(ChunkSize),"\r\n",
ChunkBody, "\r\n"],
chunk_request_body(Rest, ChunkSize, [Chunk | Acc]);
chunk_request_body(Body, _ChunkSize, Acc) when is_binary(Body) ->
BodySize = size(Body),
- Chunk = [ibrowse_lib:dec2hex(BodySize),"\r\n",
+ Chunk = [?dec2hex(BodySize),"\r\n",
Body, "\r\n"],
LastChunk = "0\r\n",
lists:reverse(["\r\n", LastChunk, Chunk | Acc]);
chunk_request_body(Body, ChunkSize, Acc) when length(Body) >= ChunkSize ->
{ChunkBody, Rest} = split_list_at(Body, ChunkSize),
- Chunk = [ibrowse_lib:dec2hex(ChunkSize),"\r\n",
+ Chunk = [?dec2hex(ChunkSize),"\r\n",
ChunkBody, "\r\n"],
chunk_request_body(Rest, ChunkSize, [Chunk | Acc]);
chunk_request_body(Body, _ChunkSize, Acc) when is_list(Body) ->
BodySize = length(Body),
- Chunk = [ibrowse_lib:dec2hex(BodySize),"\r\n",
+ Chunk = [?dec2hex(BodySize),"\r\n",
Body, "\r\n"],
LastChunk = "0\r\n",
lists:reverse(["\r\n", LastChunk, Chunk | Acc]).
@@ -1316,11 +1318,17 @@ reset_state(State) ->
transfer_encoding = undefined
}.
-set_cur_request(#state{reqs = Reqs} = State) ->
+set_cur_request(#state{reqs = Reqs, socket = Socket} = State) ->
case queue:to_list(Reqs) of
[] ->
State#state{cur_req = undefined};
- [NextReq | _] ->
+ [#request{caller_controls_socket = Ccs} = NextReq | _] ->
+ case Ccs of
+ true ->
+ do_setopts(Socket, [{active, once}], State);
+ _ ->
+ ok
+ end,
State#state{cur_req = NextReq}
end.
View
19 src/ibrowse/ibrowse_lib.erl
@@ -19,9 +19,6 @@
url_encode/1,
decode_rfc822_date/1,
status_code/1,
- dec2hex/1,
- drv_ue/1,
- drv_ue/2,
encode_base64/1,
decode_base64/1,
get_value/2,
@@ -33,17 +30,6 @@
get_trace_status(Host, Port) ->
ibrowse:get_config_value({trace, Host, Port}, false).
-drv_ue(Str) ->
- [{port, Port}| _] = ets:lookup(ibrowse_table, port),
- drv_ue(Str, Port).
-drv_ue(Str, Port) ->
- case erlang:port_control(Port, 1, Str) of
- [] ->
- Str;
- Res ->
- Res
- end.
-
%% @doc URL-encodes a string based on RFC 1738. Returns a flat list.
%% @spec url_encode(Str) -> UrlEncodedStr
%% Str = string()
@@ -163,11 +149,6 @@ status_code(507) -> insufficient_storage;
status_code(X) when is_list(X) -> status_code(list_to_integer(X));
status_code(_) -> unknown_status_code.
-%% @doc Returns a string with the hexadecimal representation of a given decimal.
-%% N = integer() -- the number to represent as hex
-%% @spec dec2hex(N::integer()) -> string()
-dec2hex(N) -> lists:flatten(io_lib:format("~.16B", [N])).
-
%% @doc Implements the base64 encoding algorithm. The output data type matches in the input data type.
%% @spec encode_base64(In) -> Out
%% In = string() | binary()
View
93 src/ibrowse/ibrowse_test.erl
@@ -20,7 +20,8 @@
test_chunked_streaming_once/0,
i_do_async_req_list/4,
test_stream_once/3,
- test_stream_once/4
+ test_stream_once/4,
+ test_20122010/0
]).
test_stream_once(Url, Method, Options) ->
@@ -218,7 +219,8 @@ dump_errors(Key, Iod) ->
{"http://jigsaw.w3.org/HTTP/Basic/", get, [{basic_auth, {"guest", "guest"}}]},
{"http://jigsaw.w3.org/HTTP/CL/", get},
{"http://www.httpwatch.com/httpgallery/chunked/", get},
- {"https://github.com", get, [{ssl_options, [{depth, 2}]}]}
+ {"https://github.com", get, [{ssl_options, [{depth, 2}]}]},
+ {local_test_fun, test_20122010, []}
]).
unit_tests() ->
@@ -228,6 +230,7 @@ unit_tests(Options) ->
application:start(crypto),
application:start(public_key),
application:start(ssl),
+ (catch ibrowse_test_server:start_server(8181, tcp)),
ibrowse:start(),
Options_1 = Options ++ [{connect_timeout, 5000}],
{Pid, Ref} = erlang:spawn_monitor(?MODULE, unit_tests_1, [self(), Options_1]),
@@ -242,7 +245,9 @@ unit_tests(Options) ->
end.
unit_tests_1(Parent, Options) ->
- lists:foreach(fun({Url, Method}) ->
+ lists:foreach(fun({local_test_fun, Fun_name, Args}) ->
+ execute_req(local_test_fun, Fun_name, Args);
+ ({Url, Method}) ->
execute_req(Url, Method, Options);
({Url, Method, X_Opts}) ->
execute_req(Url, Method, X_Opts ++ Options)
@@ -394,6 +399,10 @@ maybe_stream_next(Req_id, Options) ->
ok
end.
+execute_req(local_test_fun, Method, Args) ->
+ io:format(" ~-54.54w: ", [Method]),
+ Result = (catch apply(?MODULE, Method, Args)),
+ io:format("~p~n", [Result]);
execute_req(Url, Method, Options) ->
io:format("~7.7w, ~50.50s: ", [Method, Url]),
Result = (catch ibrowse:send_req(Url, [], Method, [], Options)),
@@ -430,3 +439,81 @@ ue_test(Data) ->
log_msg(Fmt, Args) ->
io:format("~s -- " ++ Fmt,
[ibrowse_lib:printable_date() | Args]).
+
+%%------------------------------------------------------------------------------
+%%
+%%------------------------------------------------------------------------------
+
+test_20122010() ->
+ {ok, Pid} = ibrowse:spawn_worker_process("http://localhost:8181"),
+ Expected_resp = <<"1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23-24-25-26-27-28-29-30-31-32-33-34-35-36-37-38-39-40-41-42-43-44-45-46-47-48-49-50-51-52-53-54-55-56-57-58-59-60-61-62-63-64-65-66-67-68-69-70-71-72-73-74-75-76-77-78-79-80-81-82-83-84-85-86-87-88-89-90-91-92-93-94-95-96-97-98-99-100">>,
+ Test_parent = self(),
+ Fun = fun() ->
+ do_test_20122010(Pid, Expected_resp, Test_parent)
+ end,
+ Pids = [erlang:spawn_monitor(Fun) || _ <- lists:seq(1,10)],
+ wait_for_workers(Pids).
+
+wait_for_workers([{Pid, _Ref} | Pids]) ->
+ receive
+ {Pid, success} ->
+ wait_for_workers(Pids)
+ after 5000 ->
+ test_failed
+ end;
+wait_for_workers([]) ->
+ success.
+
+do_test_20122010(Pid, Expected_resp, Test_parent) ->
+ {ibrowse_req_id, Req_id} = ibrowse:send_req_direct(
+ Pid,
+ "http://localhost:8181/ibrowse_stream_once_chunk_pipeline_test",
+ [], get, [],
+ [{stream_to, {self(), once}},
+ {include_ibrowse_req_id, true}]),
+ do_trace("~p -- sent request ~1000.p~n", [self(), Req_id]),
+ Req_id_str = lists:flatten(io_lib:format("~1000.p",[Req_id])),
+ receive
+ {ibrowse_async_headers, Req_id, "200", Headers} ->
+ case lists:keysearch("x-ibrowse-request-id", 1, Headers) of
+ {value, {_, Req_id_str}} ->
+ ok;
+ {value, {_, Req_id_1}} ->
+ do_trace("~p -- Sent req-id: ~1000.p. Recvd: ~1000.p~n",
+ [self(), Req_id, Req_id_1]),
+ exit(req_id_mismatch)
+ end
+ after 5000 ->
+ do_trace("~p -- response headers not received~n", [self()]),
+ exit({timeout, test_failed})
+ end,
+ do_trace("~p -- response headers received~n", [self()]),
+ ok = ibrowse:stream_next(Req_id),
+ case do_test_20122010_1(Expected_resp, Req_id, []) of
+ true ->
+ Test_parent ! {self(), success};
+ false ->
+ Test_parent ! {self(), failed}
+ end.
+
+do_test_20122010_1(Expected_resp, Req_id, Acc) ->
+ receive
+ {ibrowse_async_response, Req_id, Body_part} ->
+ ok = ibrowse:stream_next(Req_id),
+ do_test_20122010_1(Expected_resp, Req_id, [Body_part | Acc]);
+ {ibrowse_async_response_end, Req_id} ->
+ Acc_1 = list_to_binary(lists:reverse(Acc)),
+ Result = Acc_1 == Expected_resp,
+ do_trace("~p -- End of response. Result: ~p~n", [self(), Result]),
+ Result
+ after 1000 ->
+ exit({timeout, test_failed})
+ end.
+
+do_trace(Fmt, Args) ->
+ do_trace(get(my_trace_flag), Fmt, Args).
+
+do_trace(true, Fmt, Args) ->
+ io:format("~s -- " ++ Fmt, [ibrowse_lib:printable_date() | Args]);
+do_trace(_, _, _) ->
+ ok.
View
2  test/bench/benchbulk.sh
@@ -57,7 +57,7 @@ while [ $RUN -lt $ROUNDS ]; do
echo "startkey $STARTKEY bulksize $BULKSIZE"
DOCS=$(make_bulk_docs $BULKSIZE $STARTKEY $DOCSIZE)
# echo $DOCS
- echo $DOCS | curl -T - -X POST $POSTURL -w%{http_code}\ %{time_total}\ sec\\n >/dev/null 2>&1 &
+ echo $DOCS | curl -T - -H Content-Type:application/json -X POST $POSTURL -w%{http_code}\ %{time_total}\ sec\\n >/dev/null 2>&1 &
let POSTS=POSTS+1
done
View
54 test/etap/140-attachment-comp.t
@@ -22,7 +22,7 @@ test_db_name() ->
main(_) ->
test_util:init_code_path(),
- etap:plan(78),
+ etap:plan(86),
case (catch test()) of
ok ->
etap:end_tests();
@@ -75,6 +75,8 @@ test() ->
"compress"
),
+ test_compressible_type_with_parameters(),
+
timer:sleep(3000), % to avoid mochiweb socket closed exceptions
couch_server:delete(test_db_name(), []),
couch_server_sup:stop(),
@@ -702,6 +704,56 @@ test_create_already_compressed_att_with_invalid_content_encoding(
),
ok.
+test_compressible_type_with_parameters() ->
+ {ok, {{_, Code, _}, _Headers, _Body}} = http:request(
+ put,
+ {db_url() ++ "/testdoc5/readme.txt", [],
+ "text/plain; charset=UTF-8", test_text_data()},
+ [],
+ [{sync, true}]),
+ etap:is(Code, 201, "Created text attachment with MIME type "
+ "'text/plain; charset=UTF-8' using the standalone api"),
+ {ok, {{_, Code2, _}, Headers2, Body}} = http:request(
+ get,
+ {db_url() ++ "/testdoc5/readme.txt", [{"Accept-Encoding", "gzip"}]},
+ [],
+ [{sync, true}]),
+ etap:is(Code2, 200, "HTTP response code is 200"),
+ Gziped = lists:member({"content-encoding", "gzip"}, Headers2),
+ etap:is(Gziped, true, "received body is gziped"),
+ Uncompressed = binary_to_list(zlib:gunzip(list_to_binary(Body))),
+ etap:is(Uncompressed, test_text_data(), "received data is gzipped"),
+ {ok, {{_, Code3, _}, _Headers3, Body3}} = http:request(
+ get,
+ {db_url() ++ "/testdoc5?att_encoding_info=true", []},
+ [],
+ [{sync, true}]),
+ etap:is(Code3, 200, "HTTP response code is 200"),
+ Json = couch_util:json_decode(Body3),
+ {TextAttJson} = couch_util:get_nested_json_value(
+ Json,
+ [<<"_attachments">>, <<"readme.txt">>]
+ ),
+ TextAttLength = couch_util:get_value(<<"length">>, TextAttJson),
+ etap:is(
+ TextAttLength,
+ length(test_text_data()),
+ "text attachment stub length matches the uncompressed length"
+ ),
+ TextAttEncoding = couch_util:get_value(<<"encoding">>, TextAttJson),
+ etap:is(
+ TextAttEncoding,
+ <<"gzip">>,
+ "text attachment stub has the encoding field set to gzip"
+ ),
+ TextAttEncLength = couch_util:get_value(<<"encoded_length">>, TextAttJson),
+ etap:is(
+ TextAttEncLength,
+ iolist_size(zlib:gzip(test_text_data())),
+ "text attachment stub encoded_length matches the compressed length"
+ ),
+ ok.
+
test_png_data() ->
{ok, Data} = file:read_file(
test_util:source_file("share/www/image/logo.png")
View
4 test/etap/180-http-proxy.t
@@ -93,8 +93,8 @@ test_basic() ->
Remote = fun(Req) ->
'GET' = Req:get(method),
"/" = Req:get(path),
- undefined = Req:get(body_length),
- undefined = Req:recv_body(),
+ 0 = Req:get(body_length),
+ <<>> = Req:recv_body(),
{ok, {200, [{"Content-Type", "text/plain"}], "ok"}}
end,
Local = fun({ok, "200", _, "ok"}) -> true; (_) -> false end,

No commit comments for this range

Something went wrong with that request. Please try again.