diff options
Diffstat (limited to 'src/couch_inets/http_chunk.erl')
-rw-r--r-- | src/couch_inets/http_chunk.erl | 289 |
1 files changed, 0 insertions, 289 deletions
diff --git a/src/couch_inets/http_chunk.erl b/src/couch_inets/http_chunk.erl deleted file mode 100644 index 462cfcc5..00000000 --- a/src/couch_inets/http_chunk.erl +++ /dev/null @@ -1,289 +0,0 @@ -%% ``The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved via the world wide web at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. -%% -%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. -%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings -%% AB. All Rights Reserved.'' -%% -%% $Id$ -%% Description: Implements chunked transfer encoding see RFC2616 section -%% 3.6.1 --module(http_chunk). - --include("http_internal.hrl"). - -%% API --export([decode/3, decode/4, encode/1, encode_last/0, handle_headers/2]). -%% Callback API - used for example if the chunkedbody is received a -%% little at a time on a socket. --export([decode_size/1, ignore_extensions/1, decode_data/1, decode_trailer/1]). - -%%%========================================================================= -%%% API -%%%========================================================================= -%%------------------------------------------------------------------------- -%% decode(ChunkedBody, MaxBodySize, MaxHeaderSize, <Stream>) -> -%% {ok, {Headers, Body}} | {Module, Function, Args} -%% -%% Headers = ["Header:Value"] -%% ChunkedBody = binary() -%% MaxBodySize = integer() -%% MaxHeaderSize = integer() -%% Stream = {Code, Request} - if Request#request.stream =/= none -%% and Code == 200 the side effect of sending each decode chunk to the -%% client/file before the whole body is received will take place. -%% -%% Note: decode/4 should only be used from httpc_handler module. -%% Otherwhise use the side effect free decode/3. -%% -%% Description: Decodes a body encoded by the chunked transfer -%% encoding. If the ChunkedBody is not compleate it returns {Module, -%% Function, Args} so that decoding can be continued when more of the -%% data has been received by calling Module:Function([NewData | Args]). -%% -%% Note: In the case of pipelining a call to decode might contain data -%% that belongs to the next request/response and will be returned as -%% part of the body, hence functions calling http_chunk:decode must -%% look at the returned content-length header to make sure that they -%% split the actual body and data that possible should be passed along to -%% the next pass in the loop. -%%------------------------------------------------------------------------- -decode(ChunkedBody, MaxBodySize, MaxHeaderSize) -> - decode(ChunkedBody, MaxBodySize, MaxHeaderSize, false). - -decode(ChunkedBody, MaxBodySize, MaxHeaderSize, Stream) -> - %% Note decode_size will call decode_data. - decode_size([ChunkedBody, <<>>, [], - {MaxBodySize, <<>>, 0, MaxHeaderSize, Stream}]). - -%%------------------------------------------------------------------------- -%% encode(Chunk) -> EncodedChunk -%% -%% Chunked = binary() -%% EncodedChunk = binary() -%% -%% Description: Encodes a body part with the chunked transfer encoding. -%% Chunks are returned as lists or binaries depending on the -%% input format. When sending the data on the both formats -%% are accepted. -%%------------------------------------------------------------------------- -encode(Chunk) when is_binary(Chunk)-> - HEXSize = list_to_binary(http_util:integer_to_hexlist(size(Chunk))), - <<HEXSize/binary, ?CR, ?LF, Chunk/binary, ?CR, ?LF>>; - -encode(Chunk) when is_list(Chunk)-> - HEXSize = http_util:integer_to_hexlist(length(Chunk)), - [HEXSize, ?CR, ?LF, Chunk, ?CR, ?LF]. - -encode_last() -> - <<$0, ?CR, ?LF, ?CR, ?LF >>. - -%%------------------------------------------------------------------------- -%% handle_headers(HeaderRecord, ChunkedHeaders) -> NewHeaderRecord -%% -%% HeaderRecord = NewHeaderRecord = #http_request_h{} | #http_response_h{} -%% ChunkedHeaders = ["Header:Value"] as returnde by http_chunk:decode/3 -%% -%% Description: Removes chunked from the header as we now have decode -%% the body and adds a content-length header and any other headers -%% found in the chunked trail. -%%------------------------------------------------------------------------- -handle_headers(RequestHeaderRecord = #http_request_h{}, ChunkedHeaders) -> - NewHeaders = http_request:headers(ChunkedHeaders, RequestHeaderRecord), - TransferEncoding = - case NewHeaders#http_request_h.'transfer-encoding' -- "chunked" of - "" -> - undefined; - Other -> - Other - end, - NewHeaders#http_request_h{'transfer-encoding' = TransferEncoding}; - -handle_headers(ResponseHeaderRecord = #http_response_h{}, ChunkedHeaders) -> - NewHeaders = http_response:headers(ChunkedHeaders, ResponseHeaderRecord), - TransferEncoding = - case NewHeaders#http_response_h.'transfer-encoding' -- "chunked" of - "" -> - undefined; - Other -> - Other - end, - NewHeaders#http_response_h{'transfer-encoding' = TransferEncoding}. - -%% Functions that may be returned during the decoding process -%% if the input data is incompleate. -decode_size([Bin, Rest, HexList, Info]) -> - decode_size(<<Rest/binary, Bin/binary>>, HexList, Info). - -ignore_extensions([Bin, Rest, NextFunction]) -> - ignore_extensions(<<Rest/binary, Bin/binary>>, NextFunction). - -decode_data([Bin, ChunkSize, TotalChunk, Info]) -> - decode_data(ChunkSize, <<TotalChunk/binary, Bin/binary>>, Info). - -decode_trailer([Bin, Rest, Header, Headers, MaxHeaderSize, Body, - BodyLength]) -> - decode_trailer(<<Rest/binary, Bin/binary>>, - Header, Headers, MaxHeaderSize, Body, BodyLength). - -%%%======================================================================== -%%% Internal functions -%%%======================================================================== -decode_size(<<>>, HexList, Info) -> - {?MODULE, decode_size, [<<>>, HexList, Info]}; -decode_size(Data = <<?CR, ?LF, ChunkRest/binary>>, HexList, - {MaxBodySize, Body, - AccLength, - MaxHeaderSize, Stream}) -> - ChunkSize = http_util:hexlist_to_integer(lists:reverse(HexList)), - case ChunkSize of - 0 -> % Last chunk, there was no data - ignore_extensions(Data, {?MODULE, decode_trailer, - [<<>>, [],[], MaxHeaderSize, - Body, - integer_to_list(AccLength)]}); - _ -> - %% Note decode_data may call decode_size again if there - %% is more than one chunk, hence here is where the last parameter - %% to this function comes in. - decode_data(ChunkSize, ChunkRest, {MaxBodySize, Body, - ChunkSize + AccLength , - MaxHeaderSize, Stream}) - end; -decode_size(<<";", Rest/binary>>, HexList, Info) -> - %% Note ignore_extensions will call decode_size/1 again when - %% it ignored all extensions. - ignore_extensions(Rest, {?MODULE, decode_size, [<<>>, HexList, Info]}); -decode_size(<<?CR>> = Data, HexList, Info) -> - {?MODULE, decode_size, [Data, HexList, Info]}; -decode_size(<<Octet, Rest/binary>>, HexList, Info) -> - decode_size(Rest, [Octet | HexList], Info). - -%% "All applications MUST ignore chunk-extension extensions they -%% do not understand.", see RFC 2616 Section 3.6.1 We don't -%% understand any extension... -ignore_extensions(<<>>, NextFunction) -> - {?MODULE, ignore_extensions, [<<>>, NextFunction]}; -ignore_extensions(Data = <<?CR, ?LF, _ChunkRest/binary>>, - {Module, Function, Args}) -> - Module:Function([Data | Args]); -ignore_extensions(<<?CR>> = Data, NextFunction) -> - {?MODULE, ignore_extensions, [Data, NextFunction]}; -ignore_extensions(<<_Octet, Rest/binary>>, NextFunction) -> - ignore_extensions(Rest, NextFunction). - -decode_data(ChunkSize, TotalChunk, - Info = {MaxBodySize, BodySoFar, AccLength, MaxHeaderSize, Stream}) - when ChunkSize =< size(TotalChunk) -> - case TotalChunk of - %% Potential last chunk - <<_:ChunkSize/binary, ?CR, ?LF, "0">> -> - {?MODULE, decode_data, [ChunkSize, TotalChunk, Info]}; - <<_:ChunkSize/binary, ?CR, ?LF, "0", ?CR>> -> - {?MODULE, decode_data, [ChunkSize, TotalChunk, Info]}; - <<_:ChunkSize/binary, ?CR, ?LF>> -> - {?MODULE, decode_data, [ChunkSize, TotalChunk, Info]}; - %% Last chunk - <<Data:ChunkSize/binary, ?CR, ?LF, "0", ";">> -> - %% Note ignore_extensions will call decode_trailer/1 - %% once it ignored all extensions. - {NewBody, _} = - stream(<<BodySoFar/binary, Data/binary>>, Stream), - {?MODULE, ignore_extensions, - [<<>>, - {?MODULE, decode_trailer, [<<>>, [],[], MaxHeaderSize, - NewBody, - integer_to_list(AccLength)]}]}; - <<Data:ChunkSize/binary, ?CR, ?LF, "0", ";", Rest/binary>> -> - %% Note ignore_extensions will call decode_trailer/1 - %% once it ignored all extensions. - {NewBody, _} = stream(<<BodySoFar/binary, Data/binary>>, Stream), - ignore_extensions(Rest, {?MODULE, decode_trailer, - [<<>>, [],[], MaxHeaderSize, - NewBody, - integer_to_list(AccLength)]}); - <<Data:ChunkSize/binary, ?CR, ?LF, "0", ?CR, ?LF>> -> - {NewBody, _} = stream(<<BodySoFar/binary, Data/binary>>, Stream), - {?MODULE, decode_trailer, [<<?CR, ?LF>>, [],[], MaxHeaderSize, - NewBody, - integer_to_list(AccLength)]}; - <<Data:ChunkSize/binary, ?CR, ?LF, "0", ?CR, ?LF, Rest/binary>> -> - {NewBody,_}= stream(<<BodySoFar/binary, Data/binary>>, Stream), - decode_trailer(<<?CR, ?LF, Rest/binary>>, [],[], MaxHeaderSize, - NewBody, - integer_to_list(AccLength)); - %% There are more chunks, so here we go agin... - <<Data:ChunkSize/binary, ?CR, ?LF, Rest/binary>> - when (AccLength < MaxBodySize) or (MaxBodySize == nolimit) -> - {NewBody, NewStream} = - stream(<<BodySoFar/binary, Data/binary>>, Stream), - decode_size(Rest, [], - {MaxBodySize, NewBody, - AccLength, MaxHeaderSize, NewStream}); - <<_:ChunkSize/binary, ?CR, ?LF, _/binary>> -> - throw({error, body_too_big}); - _ -> - {?MODULE, decode_data, [ChunkSize, TotalChunk, Info]} - end; -decode_data(ChunkSize, TotalChunk, Info) -> - {?MODULE, decode_data, [ChunkSize, TotalChunk, Info]}. - -decode_trailer(<<>>, Header, Headers, MaxHeaderSize, Body, BodyLength) -> - {?MODULE, decode_trailer, [<<>>, Header, Headers, MaxHeaderSize, Body, - BodyLength]}; - -%% Note: If Bin is not empty it is part of a pipelined request/response. -decode_trailer(<<?CR,?LF,?CR,?LF, Bin/binary>>, [], [], _, Body, BodyLength) -> - {ok, {["content-length:" ++ BodyLength], <<Body/binary, Bin/binary>>}}; -decode_trailer(<<?CR,?LF,?CR,?LF, Bin/binary>>, - Header, Headers, MaxHeaderSize, Body, BodyLength) -> - NewHeaders = case Header of - [] -> - Headers; - _ -> - [lists:reverse(Header) | Headers] - end, - Length = length(NewHeaders), - case Length > MaxHeaderSize of - true -> - throw({error, {header_too_long, MaxHeaderSize, - MaxHeaderSize-Length}}); - false -> - {ok, {["content-length:" ++ BodyLength | NewHeaders], - <<Body/binary, Bin/binary>>}} - end; -decode_trailer(<<?CR,?LF,?CR>> = Data, Header, Headers, MaxHeaderSize, - Body, BodyLength) -> - {?MODULE, decode_trailer, [Data, Header, Headers, MaxHeaderSize, Body, - BodyLength]}; -decode_trailer(<<?CR,?LF>> = Data, Header, Headers, MaxHeaderSize, - Body, BodyLength) -> - {?MODULE, decode_trailer, [Data, Header, Headers, MaxHeaderSize, Body, - BodyLength]}; -decode_trailer(<<?CR>> = Data, Header, Headers, MaxHeaderSize, - Body, BodyLength) -> - {?MODULE, decode_trailer, [Data, Header, Headers, MaxHeaderSize, Body, - BodyLength]}; -decode_trailer(<<?CR, ?LF, Rest/binary>>, Header, Headers, - MaxHeaderSize, Body, BodyLength) -> - decode_trailer(Rest, [], [lists:reverse(Header) | Headers], - MaxHeaderSize, Body, BodyLength); - -decode_trailer(<<Octet, Rest/binary>>, Header, Headers, MaxHeaderSize, Body, - BodyLength) -> - decode_trailer(Rest, [Octet | Header], Headers, MaxHeaderSize, - Body, BodyLength). - -stream(BodyPart, false) -> - {BodyPart, false}; -stream(BodyPart, {Code, Request}) -> - {NewBody, NewRequest} = httpc_handler:stream(BodyPart, Request, Code), - {NewBody, {Code, NewRequest}}. |