| a | b | |
|---|
| 0 | | - | diff --git a/src/couchdb/couch_db.erl b/src/couchdb/couch_db.erl |
|---|
| 0 | | - | index c35bb91..0a99845 100644 |
|---|
| 0 | | - | --- a/src/couchdb/couch_db.erl |
|---|
| 0 | | - | +++ b/src/couchdb/couch_db.erl |
|---|
| 0 | | - | @@ -24,7 +24,7 @@ |
|---|
| 0 | | - | -export([start_link/3,make_doc/2,set_admins/2,get_admins/1,ensure_full_commit/1]). |
|---|
| 0 | | - | -export([init/1,terminate/2,handle_call/3,handle_cast/2,code_change/3,handle_info/2]). |
|---|
| 0 | | - | |
|---|
| 0 | | - | - |
|---|
| 0 | | - | +-export([write_streamed_attachment/4]). |
|---|
| 0 | | - | -include("couch_db.hrl"). |
|---|
| 0 | | - | |
|---|
| 0 | | - | |
|---|
| 0 | | - | @@ -400,8 +400,14 @@ doc_flush_binaries(Doc, Fd) -> |
|---|
| 0 | | - | % written to a different file |
|---|
| 0 | | - | SizeAcc + Len; |
|---|
| 0 | | - | {_Key, {_Type, Bin}} when is_binary(Bin) -> |
|---|
| 0 | | - | + % we have a new binary to write |
|---|
| 0 | | - | SizeAcc + size(Bin); |
|---|
| 0 | | - | + {_Key, {_Type, {Fun, undefined}}} when is_function(Fun) -> |
|---|
| 0 | | - | + % function without a known length |
|---|
| 0 | | - | + % we'll have to alloc as we go with this one, for now, nothing |
|---|
| 0 | | - | + SizeAcc; |
|---|
| 0 | | - | {_Key, {_Type, {Fun, Len}}} when is_function(Fun) -> |
|---|
| 0 | | - | + % function to yield binary data with known length |
|---|
| 0 | | - | SizeAcc + Len |
|---|
| 0 | | - | end |
|---|
| 0 | | - | end, |
|---|
| 0 | | - | @@ -436,6 +442,16 @@ doc_flush_binaries(Doc, Fd) -> |
|---|
| 0 | | - | Bin when is_binary(Bin) -> |
|---|
| 0 | | - | {ok, StreamPointer} = couch_stream:write(OutputStream, Bin), |
|---|
| 0 | | - | {Fd, StreamPointer, size(Bin)}; |
|---|
| 0 | | - | + {StreamFun, undefined} when is_function(StreamFun) -> |
|---|
| 0 | | - | + % StreamFun(MaxChunkSize, WriterFun) |
|---|
| 0 | | - | + % will call our WriterFun |
|---|
| 0 | | - | + % once for each chunk of the attachment. |
|---|
| 0 | | - | + WriterFun = make_writer_fun(OutputStream), |
|---|
| 0 | | - | + MaxChunkSize = 4294967296, % TODO config |
|---|
| 0 | | - | + % couch_config:get("couchdb", "max_document_size", "4294967296") |
|---|
| 0 | | - | + {ok, {TotalLength, NewStreamPointer}} = |
|---|
| 0 | | - | + StreamFun(MaxChunkSize, WriterFun, {0, nil}), |
|---|
| 0 | | - | + {Fd, NewStreamPointer, TotalLength}; |
|---|
| 0 | | - | {Fun, Len} when is_function(Fun) -> |
|---|
| 0 | | - | {ok, StreamPointer} = |
|---|
| 0 | | - | write_streamed_attachment(OutputStream, Fun, Len, nil), |
|---|
| 0 | | - | @@ -445,8 +461,26 @@ doc_flush_binaries(Doc, Fd) -> |
|---|
| 0 | | - | end, Bins), |
|---|
| 0 | | - | |
|---|
| 0 | | - | {ok, _FinalPos} = couch_stream:close(OutputStream), |
|---|
| 0 | | - | - |
|---|
| 0 | | - | Doc#doc{attachments = NewBins}. |
|---|
| 0 | | - | + |
|---|
| 0 | | - | +% WriterFun({Length, Binary}, State) |
|---|
| 0 | | - | +% WriterFun({0, _Footers}, State) |
|---|
| 0 | | - | +% Called with Length == 0 on the last time. |
|---|
| 0 | | - | +% WriterFun returns NewState. |
|---|
| 0 | | - | +make_writer_fun(Stream) -> |
|---|
| 0 | | - | + fun |
|---|
| 0 | | - | + ({0, _Footers}, {FinalLen, SpFin}) -> |
|---|
| 0 | | - | + % last block, return the final tuple |
|---|
| 0 | | - | + {ok, {FinalLen, SpFin}}; |
|---|
| 0 | | - | + ({Length, Bin}, {Total, nil}) -> |
|---|
| 0 | | - | + % save StreamPointer |
|---|
| 0 | | - | + {ok, StreamPointer} = couch_stream:write(Stream, Bin), |
|---|
| 0 | | - | + {Total+Length, StreamPointer}; |
|---|
| 0 | | - | + ({Length, Bin}, {Total, SpAcc}) -> |
|---|
| 0 | | - | + % write the Bin to disk |
|---|
| 0 | | - | + {ok, _Sp} = couch_stream:write(Stream, Bin), |
|---|
| 0 | | - | + {Total+Length, SpAcc} |
|---|
| 0 | | - | + end. |
|---|
| 0 | | - | |
|---|
| 0 | | - | write_streamed_attachment(_Stream, _F, 0, SpAcc) -> |
|---|
| 0 | | - | {ok, SpAcc}; |
|---|
| 0 | | - | diff --git a/src/couchdb/couch_httpd.erl b/src/couchdb/couch_httpd.erl |
|---|
| 0 | | - | index 6b9079c..3447e22 100644 |
|---|
| 0 | | - | --- a/src/couchdb/couch_httpd.erl |
|---|
| 0 | | - | +++ b/src/couchdb/couch_httpd.erl |
|---|
| 0 | | - | @@ -16,7 +16,7 @@ |
|---|
| 0 | | - | -export([start_link/0, stop/0, handle_request/3]). |
|---|
| 0 | | - | |
|---|
| 0 | | - | -export([header_value/2,header_value/3,qs_value/2,qs_value/3,qs/1,path/1,absolute_uri/2]). |
|---|
| 0 | | - | --export([verify_is_server_admin/1,unquote/1,quote/1,recv/2]). |
|---|
| 0 | | - | +-export([verify_is_server_admin/1,unquote/1,quote/1,recv/2,recv_chunked/4]). |
|---|
| 0 | | - | -export([parse_form/1,json_body/1,body/1,doc_etag/1, make_etag/1, etag_respond/3]). |
|---|
| 0 | | - | -export([primary_header_value/2,partition/1,serve_file/3]). |
|---|
| 0 | | - | -export([start_chunked_response/3,send_chunk/2]). |
|---|
| 0 | | - | @@ -260,6 +260,12 @@ parse_form(#httpd{mochi_req=MochiReq}) -> |
|---|
| 0 | | - | recv(#httpd{mochi_req=MochiReq}, Len) -> |
|---|
| 0 | | - | MochiReq:recv(Len). |
|---|
| 0 | | - | |
|---|
| 0 | | - | +recv_chunked(#httpd{mochi_req=MochiReq}, MaxChunkSize, ChunkFun, InitState) -> |
|---|
| 0 | | - | + % Fun is called once with each chunk |
|---|
| 0 | | - | + % Fun({Length, Binary}) |
|---|
| 0 | | - | + % called with Length == 0 on the last time. |
|---|
| 0 | | - | + MochiReq:recv_body(MaxChunkSize, ChunkFun, InitState). |
|---|
| 0 | | - | + |
|---|
| 0 | | - | body(#httpd{mochi_req=MochiReq}) -> |
|---|
| 0 | | - | % Maximum size of document PUT request body (4GB) |
|---|
| 0 | | - | MaxSize = list_to_integer( |
|---|
| 0 | | - | diff --git a/src/couchdb/couch_httpd_db.erl b/src/couchdb/couch_httpd_db.erl |
|---|
| 0 | | - | index ae92512..df88fe9 100644 |
|---|
| 0 | | - | --- a/src/couchdb/couch_httpd_db.erl |
|---|
| 0 | | - | +++ b/src/couchdb/couch_httpd_db.erl |
|---|
| 0 | | - | @@ -591,10 +591,24 @@ db_attachment_req(#httpd{method=Method}=Req, Db, DocId, FileNameParts) |
|---|
| 0 | | - | []; |
|---|
| 0 | | - | _ -> |
|---|
| 0 | | - | [{FileName, { |
|---|
| 0 | | - | - list_to_binary(couch_httpd:header_value(Req,"Content-Type")), |
|---|
| 0 | | - | + case couch_httpd:header_value(Req,"Content-Type") of |
|---|
| 0 | | - | + undefined -> |
|---|
| 0 | | - | + % we could throw an error here or guess by the name |
|---|
| 0 | | - | + % currently, just giving it a default |
|---|
| 0 | | - | + <<"application/octet-stream">>; |
|---|
| 0 | | - | + CType -> |
|---|
| 0 | | - | + list_to_binary(CType) |
|---|
| 0 | | - | + end, |
|---|
| 0 | | - | case couch_httpd:header_value(Req,"Content-Length") of |
|---|
| 0 | | - | undefined -> |
|---|
| 0 | | - | - throw({bad_request, "Attachment uploads must be fixed length"}); |
|---|
| 0 | | - | + {fun(MaxChunkSize, ChunkFun, InitState) -> |
|---|
| 0 | | - | + % ChunkFun is called once with each chunk |
|---|
| 0 | | - | + % ChunkFun({Length, Binary}, State) |
|---|
| 0 | | - | + % called with Length == 0 on the last time. |
|---|
| 0 | | - | + % returns NewState |
|---|
| 0 | | - | + couch_httpd:recv_chunked(Req, MaxChunkSize, |
|---|
| 0 | | - | + ChunkFun, InitState) |
|---|
| 0 | | - | + end, undefined}; |
|---|
| 0 | | - | Length -> |
|---|
| 0 | | - | {fun() -> couch_httpd:recv(Req, 0) end, list_to_integer(Length)} |
|---|
| 0 | | - | end |
|---|
| 0 | | - | diff --git a/src/couchdb/couch_stream.erl b/src/couchdb/couch_stream.erl |
|---|
| 0 | | - | index d957268..d6f7269 100644 |
|---|
| 0 | | - | --- a/src/couchdb/couch_stream.erl |
|---|
| 0 | | - | +++ b/src/couchdb/couch_stream.erl |
|---|
| 0 | | - | @@ -136,6 +136,7 @@ handle_call(get_state, _From, Stream) -> |
|---|
| 0 | | - | {reply, {Pos, BytesRemaining}, Stream}; |
|---|
| 0 | | - | handle_call({set_min_buffer, MinBuffer}, _From, Stream) -> |
|---|
| 0 | | - | {reply, ok, Stream#write_stream{min_alloc = MinBuffer}}; |
|---|
| 0 | | - | +% set next_alloc if we need more room |
|---|
| 0 | | - | handle_call({ensure_buffer, BufferSizeRequested}, _From, Stream) -> |
|---|
| 0 | | - | #write_stream{bytes_remaining = BytesRemainingInCurrentBuffer} = Stream, |
|---|
| 0 | | - | case BytesRemainingInCurrentBuffer < BufferSizeRequested of |
|---|
| 0 | | - | diff --git a/src/mochiweb/mochiweb_request.erl b/src/mochiweb/mochiweb_request.erl |
|---|
| 0 | | - | index 311ed50..095b251 100644 |
|---|
| 0 | | - | --- a/src/mochiweb/mochiweb_request.erl |
|---|
| 0 | | - | +++ b/src/mochiweb/mochiweb_request.erl |
|---|
| 0 | | - | @@ -12,7 +12,7 @@ |
|---|
| 0 | | - | -define(READ_SIZE, 8192). |
|---|
| 0 | | - | |
|---|
| 0 | | - | -export([get_header_value/1, get_primary_header_value/1, get/1, dump/0]). |
|---|
| 0 | | - | --export([send/1, recv/1, recv/2, recv_body/0, recv_body/1]). |
|---|
| 0 | | - | +-export([send/1, recv/1, recv/2, recv_body/0, recv_body/1, recv_body/3]). |
|---|
| 0 | | - | -export([start_response/1, start_response_length/1, start_raw_response/1]). |
|---|
| 0 | | - | -export([respond/1, ok/1]). |
|---|
| 0 | | - | -export([not_found/0, not_found/1]). |
|---|
| 0 | | - | @@ -171,6 +171,9 @@ recv_body() -> |
|---|
| 0 | | - | %% @doc Receive the body of the HTTP request (defined by Content-Length). |
|---|
| 0 | | - | %% Will receive up to MaxBody bytes. |
|---|
| 0 | | - | recv_body(MaxBody) -> |
|---|
| 0 | | - | + recv_body(MaxBody, nil, nil). |
|---|
| 0 | | - | + |
|---|
| 0 | | - | +recv_body(MaxBody, ChunkFun, ChunkAcc) -> |
|---|
| 0 | | - | case get_header_value("expect") of |
|---|
| 0 | | - | "100-continue" -> |
|---|
| 0 | | - | start_raw_response({100, gb_trees:empty()}); |
|---|
| 0 | | - | @@ -183,7 +186,15 @@ recv_body(MaxBody) -> |
|---|
| 0 | | - | {unknown_transfer_encoding, Unknown} -> |
|---|
| 0 | | - | exit({unknown_transfer_encoding, Unknown}); |
|---|
| 0 | | - | chunked -> |
|---|
| 0 | | - | - read_chunked_body(MaxBody, []); |
|---|
| 0 | | - | + case ChunkFun of |
|---|
| 0 | | - | + nil -> |
|---|
| 0 | | - | + read_chunked_body(MaxBody); |
|---|
| 0 | | - | + _StreamFun -> |
|---|
| 0 | | - | + % In this case the MaxBody is actually used to |
|---|
| 0 | | - | + % determine the maximum allowed size of a single |
|---|
| 0 | | - | + % chunk. |
|---|
| 0 | | - | + stream_chunked_body(MaxBody, ChunkFun, ChunkAcc) |
|---|
| 0 | | - | + end; |
|---|
| 0 | | - | 0 -> |
|---|
| 0 | | - | <<>>; |
|---|
| 0 | | - | Length when is_integer(Length), Length =< MaxBody -> |
|---|
| 0 | | - | @@ -408,15 +419,27 @@ parse_post() -> |
|---|
| 0 | | - | Cached |
|---|
| 0 | | - | end. |
|---|
| 0 | | - | |
|---|
| 0 | | - | -read_chunked_body(Max, Acc) -> |
|---|
| 0 | | - | +read_chunked_body(MaxBufferSize) -> |
|---|
| 0 | | - | + stream_chunked_body(MaxBufferSize, fun |
|---|
| 0 | | - | + ({0, _}, Acc) -> |
|---|
| 0 | | - | + iolist_to_binary(lists:reverse(Acc)); |
|---|
| 0 | | - | + ({Length, Bin}, Acc) -> |
|---|
| 0 | | - | + [Bin | Acc] |
|---|
| 0 | | - | + end, []). |
|---|
| 0 | | - | + |
|---|
| 0 | | - | +% takes a function and the max amount to bite off in each call. |
|---|
| 0 | | - | +% the function gets called with each chunk. |
|---|
| 0 | | - | +% used internally by read_chunked_body/2 |
|---|
| 0 | | - | +stream_chunked_body(MaxChunkSize, Fun, FunState) -> |
|---|
| 0 | | - | + io:format("stream_chunked_body~n",[]), |
|---|
| 0 | | - | case read_chunk_length() of |
|---|
| 0 | | - | 0 -> |
|---|
| 0 | | - | - read_chunk(0), |
|---|
| 0 | | - | - iolist_to_binary(lists:reverse(Acc)); |
|---|
| 0 | | - | - Length when Length > Max -> |
|---|
| 0 | | - | + Result = Fun({0, read_chunk(0)}, FunState); |
|---|
| 0 | | - | + Length when Length > MaxChunkSize -> |
|---|
| 0 | | - | exit({body_too_large, chunked}); |
|---|
| 0 | | - | Length -> |
|---|
| 0 | | - | - read_chunked_body(Max - Length, [read_chunk(Length) | Acc]) |
|---|
| 0 | | - | + NewState = Fun({Length, read_chunk(Length)}, FunState), |
|---|
| 0 | | - | + stream_chunked_body(MaxChunkSize, Fun, NewState) |
|---|
| 0 | | - | end. |
|---|
| 0 | | - | |
|---|
| 0 | | - | %% @spec read_chunk_length() -> integer() |
|---|
| 0 | | - | |
|---|
| 0 | + | SMgVkh <a href="http://qpmwnxhomdjy.com/">qpmwnxhomdjy</a>, [url=http://ncnszkixjkle.com/]ncnszkixjkle[/url], [link=http://ntttbvuwnuln.com/]ntttbvuwnuln[/link], http://qbmcuihlmkxf.com/ |
|---|
| ... | |
|---|