aboutsummaryrefslogtreecommitdiffstats
path: root/lib/inets/src/http_lib
diff options
context:
space:
mode:
authorErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
committerErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
commit84adefa331c4159d432d22840663c38f155cd4c1 (patch)
treebff9a9c66adda4df2106dfd0e5c053ab182a12bd /lib/inets/src/http_lib
downloadotp-84adefa331c4159d432d22840663c38f155cd4c1.tar.gz
otp-84adefa331c4159d432d22840663c38f155cd4c1.tar.bz2
otp-84adefa331c4159d432d22840663c38f155cd4c1.zip
The R13B03 release.OTP_R13B03
Diffstat (limited to 'lib/inets/src/http_lib')
-rw-r--r--lib/inets/src/http_lib/Makefile101
-rw-r--r--lib/inets/src/http_lib/http_chunk.erl291
-rw-r--r--lib/inets/src/http_lib/http_internal.hrl108
-rw-r--r--lib/inets/src/http_lib/http_request.erl281
-rw-r--r--lib/inets/src/http_lib/http_response.erl208
-rw-r--r--lib/inets/src/http_lib/http_transport.erl353
-rw-r--r--lib/inets/src/http_lib/http_util.erl148
7 files changed, 1490 insertions, 0 deletions
diff --git a/lib/inets/src/http_lib/Makefile b/lib/inets/src/http_lib/Makefile
new file mode 100644
index 0000000000..27e7ee65c5
--- /dev/null
+++ b/lib/inets/src/http_lib/Makefile
@@ -0,0 +1,101 @@
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2005-2009. All Rights Reserved.
+#
+# 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 online 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.
+#
+# %CopyrightEnd%
+#
+#
+
+include $(ERL_TOP)/make/target.mk
+EBIN = ../../ebin
+include $(ERL_TOP)/make/$(TARGET)/otp.mk
+
+# ----------------------------------------------------
+# Application version
+# ----------------------------------------------------
+include ../../vsn.mk
+
+VSN = $(INETS_VSN)
+
+# ----------------------------------------------------
+# Release directory specification
+# ----------------------------------------------------
+RELSYSDIR = $(RELEASE_PATH)/lib/inets-$(VSN)
+
+# ----------------------------------------------------
+# Target Specs
+# ----------------------------------------------------
+MODULES = \
+ http_chunk \
+ http_transport\
+ http_util \
+ http_request \
+ http_response
+
+HRL_FILES = http_internal.hrl
+
+ERL_FILES = $(MODULES:%=%.erl)
+
+TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR))
+
+# ----------------------------------------------------
+# INETS FLAGS
+# ----------------------------------------------------
+INETS_FLAGS = -D'SERVER_SOFTWARE="inets/$(VSN)"' \
+
+# ----------------------------------------------------
+# FLAGS
+# ----------------------------------------------------
+INETS_ERL_FLAGS += -I ../inets_app
+
+ifeq ($(WARN_UNUSED_WARS),true)
+ERL_COMPILE_FLAGS += +warn_unused_vars
+endif
+
+ERL_COMPILE_FLAGS += $(INETS_ERL_FLAGS) \
+ $(INETS_FLAGS) \
+ +'{parse_transform,sys_pre_attributes}' \
+ +'{attribute,insert,app_vsn,$(APP_VSN)}'
+
+
+# ----------------------------------------------------
+# Targets
+# ----------------------------------------------------
+
+debug opt: $(TARGET_FILES)
+
+clean:
+ rm -f $(TARGET_FILES)
+ rm -f core
+
+docs:
+
+# ----------------------------------------------------
+# Release Target
+# ----------------------------------------------------
+include $(ERL_TOP)/make/otp_release_targets.mk
+
+release_spec: opt
+ $(INSTALL_DIR) $(RELSYSDIR)/src
+ $(INSTALL_DATA) $(HRL_FILES) $(ERL_FILES) $(RELSYSDIR)/src
+ $(INSTALL_DIR) $(RELSYSDIR)/ebin
+ $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin
+
+release_docs_spec:
+
+info:
+ @echo "INETS_DEBUG = $(INETS_DEBUG)"
+ @echo "INETS_FLAGS = $(INETS_FLAGS)"
+ @echo "ERL_COMPILE_FLAGS = $(ERL_COMPILE_FLAGS)"
diff --git a/lib/inets/src/http_lib/http_chunk.erl b/lib/inets/src/http_lib/http_chunk.erl
new file mode 100644
index 0000000000..cd20dce9d5
--- /dev/null
+++ b/lib/inets/src/http_lib/http_chunk.erl
@@ -0,0 +1,291 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2004-2009. All Rights Reserved.
+%%
+%% 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 online 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.
+%%
+%% %CopyrightEnd%
+%%
+%% 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(erlang:iolist_size(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}}.
diff --git a/lib/inets/src/http_lib/http_internal.hrl b/lib/inets/src/http_lib/http_internal.hrl
new file mode 100644
index 0000000000..bb2e831727
--- /dev/null
+++ b/lib/inets/src/http_lib/http_internal.hrl
@@ -0,0 +1,108 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2002-2009. All Rights Reserved.
+%%
+%% 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 online 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.
+%%
+%% %CopyrightEnd%
+%%
+%%
+
+-include("inets_internal.hrl").
+
+-define(HTTP_MAX_BODY_SIZE, nolimit).
+-define(HTTP_MAX_HEADER_SIZE, 10240).
+-define(HTTP_MAX_URI_SIZE, nolimit).
+
+%%% Response headers
+-record(http_response_h,{
+%%% --- Standard "General" headers
+ 'cache-control',
+ connection,
+ date,
+ pragma,
+ trailer,
+ 'transfer-encoding',
+ upgrade,
+ via,
+ warning,
+%%% --- Standard "Response" headers
+ 'accept-ranges',
+ age,
+ etag,
+ location,
+ 'proxy-authenticate',
+ 'retry-after',
+ server,
+ vary,
+ 'www-authenticate',
+%%% --- Standard "Entity" headers
+ allow,
+ 'content-encoding',
+ 'content-language',
+ 'content-length' = "-1",
+ 'content-location',
+ 'content-md5',
+ 'content-range',
+ 'content-type',
+ expires,
+ 'last-modified',
+ other=[] % list() - Key/Value list with other headers
+ }).
+
+
+%%% Request headers
+-record(http_request_h,{
+%%% --- Standard "General" headers
+ 'cache-control',
+ connection = "keep-alive",
+ date,
+ pragma,
+ trailer,
+ 'transfer-encoding',
+ upgrade,
+ via,
+ warning,
+%%% --- Standard "Request" headers
+ accept,
+ 'accept-charset',
+ 'accept-encoding',
+ 'accept-language',
+ authorization,
+ expect,
+ from,
+ host,
+ 'if-match',
+ 'if-modified-since',
+ 'if-none-match',
+ 'if-range',
+ 'if-unmodified-since',
+ 'max-forwards',
+ 'proxy-authorization',
+ range,
+ referer,
+ te,
+ 'user-agent',
+%%% --- Standard "Entity" headers
+ allow,
+ 'content-encoding',
+ 'content-language',
+ 'content-length' = "0",
+ 'content-location',
+ 'content-md5',
+ 'content-range',
+ 'content-type',
+ expires,
+ 'last-modified',
+ other=[] % list() - Key/Value list with other headers
+ }).
diff --git a/lib/inets/src/http_lib/http_request.erl b/lib/inets/src/http_lib/http_request.erl
new file mode 100644
index 0000000000..c214aca4a4
--- /dev/null
+++ b/lib/inets/src/http_lib/http_request.erl
@@ -0,0 +1,281 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2005-2009. All Rights Reserved.
+%%
+%% 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 online 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.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(http_request).
+
+-include("http_internal.hrl").
+
+-export([headers/2, http_headers/1, is_absolut_uri/1]).
+
+%%-------------------------------------------------------------------------
+%% headers(HeaderList, #http_request_h{}) -> #http_request_h{}
+%% HeaderList - ["HeaderField:Value"]
+%% HeaderField - string()
+%% Value - string()
+%%
+%% Description: Creates a http_request_h-record used internally to
+%% handle http-headers.
+%%-------------------------------------------------------------------------
+headers([], Headers) ->
+ Headers;
+headers([Header | Tail], Headers) ->
+ case lists:splitwith(fun($:) -> false; (_) -> true end, Header) of
+ {Key, [$: | Value]} ->
+ headers(Tail, headers(http_util:to_lower(string:strip(Key)),
+ string:strip(Value), Headers));
+ {_, []} ->
+ Report = io_lib:format("Ignored invalid HTTP-header: ~p~n",
+ [Header]),
+ error_logger:error_report(Report),
+ headers(Tail, Headers)
+ end.
+
+%%-------------------------------------------------------------------------
+%% headers(#http_request_h{}) -> HeaderList
+%% HeaderList - ["HeaderField:Value"]
+%% HeaderField - string()
+%% Value - string()
+%%
+%% Description: Creates a HTTP header string.
+%%-------------------------------------------------------------------------
+http_headers(Headers = #http_request_h{other = Other}) ->
+ HeaderFields = record_info(fields, http_request_h) -- [other],
+ HeaderStr = lists:foldl(fun(Key, Acc) ->
+ case key_value_str(Key, Headers) of
+ undefined ->
+ Acc;
+ Str ->
+ [Str | Acc]
+ end
+ end,
+ [], HeaderFields),
+
+ lists:flatten([HeaderStr | headers_other(Other, [])]).
+
+%%-------------------------------------------------------------------------
+%% is_absolut_uri(URI) -> true | false
+%% URI - string()
+%%
+%% Description: Checks if an URI is absolute or relative
+%%-------------------------------------------------------------------------
+is_absolut_uri("http://" ++ _) ->
+ true;
+is_absolut_uri("https://" ++ _) ->
+ true;
+is_absolut_uri(_) ->
+ false.
+
+%%%========================================================================
+%%% Internal functions
+%%%========================================================================
+
+%%% --- Request headers
+headers("accept", Value, Headers) ->
+ Headers#http_request_h{accept = Value};
+headers("accept-charset", Value, Headers) ->
+ Headers#http_request_h{'accept-charset' = Value};
+headers("accept-encoding", Value, Headers) ->
+ Headers#http_request_h{'accept-encoding' = Value};
+headers("accept-language", Value, Headers) ->
+ Headers#http_request_h{'accept-language' = Value};
+headers("authorization", Value, Headers) ->
+ Headers#http_request_h{authorization = Value};
+headers("expect", Value, Headers) ->
+ Headers#http_request_h{expect = Value};
+headers("from", Value, Headers) ->
+ Headers#http_request_h{from = Value};
+headers("host", Value, Headers) ->
+ Headers#http_request_h{host = Value};
+headers("if-match", Value, Headers) ->
+ Headers#http_request_h{'if-match' = Value};
+headers("if-modified-since", Value, Headers) ->
+ Headers#http_request_h{'if-modified-since' = Value};
+headers("if-none-match", Value, Headers) ->
+ Headers#http_request_h{'if-none-match' = Value};
+headers("if-range", Value, Headers) ->
+ Headers#http_request_h{'if-range' = Value};
+headers("if-unmodified-since", Value, Headers) ->
+ Headers#http_request_h{'if-unmodified-since' = Value};
+headers("max-forwards", Value, Headers) ->
+ Headers#http_request_h{'max-forwards' = Value};
+headers("proxy-authorization", Value, Headers) ->
+ Headers#http_request_h{'proxy-authorization' = Value};
+headers("range", Value, Headers) ->
+ Headers#http_request_h{range = Value};
+headers("referer", Value, Headers) ->
+ Headers#http_request_h{referer = Value};
+headers("te", Value, Headers) ->
+ Headers#http_request_h{te = Value};
+headers("user-agent", Value, Headers) ->
+ Headers#http_request_h{'user-agent' = Value};
+
+%% General-Headers
+headers("cache-control", Value, Headers) ->
+ Headers#http_request_h{'cache-control' = Value};
+headers("connection", Value, Headers) ->
+ Headers#http_request_h{connection = Value};
+headers("date", Value, Headers) ->
+ Headers#http_request_h{date = Value};
+headers("pragma", Value, Headers) ->
+ Headers#http_request_h{pragma = Value};
+headers("trailer", Value, Headers) ->
+ Headers#http_request_h{trailer = Value};
+headers("transfer-encoding", Value, Headers) ->
+ Headers#http_request_h{'transfer-encoding' = Value};
+headers("upgrade", Value, Headers) ->
+ Headers#http_request_h{upgrade = Value};
+headers("via", Value, Headers) ->
+ Headers#http_request_h{via = Value};
+headers("warning", Value, Headers) ->
+ Headers#http_request_h{warning = Value};
+
+%% Entity header
+headers("allow", Value, Headers) ->
+ Headers#http_request_h{allow = Value};
+headers("content-encoding", Value, Headers) ->
+ Headers#http_request_h{'content-encoding' = Value};
+headers("content-language", Value, Headers) ->
+ Headers#http_request_h{'content-language' = Value};
+headers("content-length", Value, Headers) ->
+ Headers#http_request_h{'content-length' = Value};
+headers("content-location", Value, Headers) ->
+ Headers#http_request_h{'content-location' = Value};
+headers("content-md5", Value, Headers) ->
+ Headers#http_request_h{'content-md5' = Value};
+headers("content-range", Value, Headers) ->
+ Headers#http_request_h{'content-range' = Value};
+headers("content-type", Value, Headers) ->
+ Headers#http_request_h{'content-type' = Value};
+headers("expires", Value, Headers) ->
+ Headers#http_request_h{expires = Value};
+headers("last-modified", Value, Headers) ->
+ Headers#http_request_h{'last-modified' = Value};
+headers(Key, Value, Headers) ->
+ Headers#http_request_h{other=
+ [{Key, Value} | Headers#http_request_h.other]}.
+
+key_value_str(Key = 'cache-control', Headers) ->
+ key_value_str(atom_to_list(Key), Headers#http_request_h.'cache-control');
+key_value_str(Key = connection, Headers) ->
+ key_value_str(atom_to_list(Key), Headers#http_request_h.connection);
+key_value_str(Key = date, Headers) ->
+ key_value_str(atom_to_list(Key), Headers#http_request_h.date);
+key_value_str(Key = pragma, Headers) ->
+ key_value_str(atom_to_list(Key), Headers#http_request_h.pragma);
+key_value_str(Key = trailer, Headers) ->
+ key_value_str(atom_to_list(Key), Headers#http_request_h.trailer);
+key_value_str(Key = 'transfer-encoding', Headers) ->
+ key_value_str(atom_to_list(Key),
+ Headers#http_request_h.'transfer-encoding');
+key_value_str(Key = upgrade, Headers) ->
+ key_value_str(atom_to_list(Key), Headers#http_request_h.upgrade);
+key_value_str(Key = via, Headers) ->
+ key_value_str(atom_to_list(Key), Headers#http_request_h.via);
+key_value_str(Key = warning, Headers) ->
+ key_value_str(atom_to_list(Key), Headers#http_request_h.warning);
+key_value_str(Key = accept, Headers) ->
+ key_value_str(atom_to_list(Key), Headers#http_request_h.accept);
+key_value_str(Key = 'accept-charset', Headers) ->
+ key_value_str(atom_to_list(Key), Headers#http_request_h.'accept-charset');
+key_value_str(Key = 'accept-encoding', Headers) ->
+ key_value_str(atom_to_list(Key), Headers#http_request_h.'accept-encoding');
+key_value_str(Key = 'accept-language', Headers) ->
+ key_value_str(atom_to_list(Key), Headers#http_request_h.'accept-language');
+key_value_str(Key = authorization, Headers) ->
+ key_value_str(atom_to_list(Key),
+ Headers#http_request_h.authorization);
+key_value_str(Key = expect, Headers) ->
+ key_value_str(atom_to_list(Key), Headers#http_request_h.expect);
+key_value_str(Key = from, Headers) ->
+ key_value_str(atom_to_list(Key), Headers#http_request_h.from);
+key_value_str(Key = host, Headers) ->
+ key_value_str(atom_to_list(Key), Headers#http_request_h.host);
+key_value_str(Key = 'if-match', Headers) ->
+ key_value_str(atom_to_list(Key),
+ Headers#http_request_h.'if-match');
+key_value_str(Key = 'if-modified-since', Headers) ->
+ key_value_str(atom_to_list(Key),
+ Headers#http_request_h.'if-modified-since');
+key_value_str(Key = 'if-none-match', Headers) ->
+ key_value_str(atom_to_list(Key),
+ Headers#http_request_h.'if-none-match');
+key_value_str(Key = 'if-range', Headers) ->
+ key_value_str(atom_to_list(Key),
+ Headers#http_request_h.'if-range');
+key_value_str(Key = 'if-unmodified-since', Headers) ->
+ key_value_str(atom_to_list(Key),
+ Headers#http_request_h.'if-unmodified-since');
+key_value_str(Key = 'max-forwards', Headers) ->
+ key_value_str(atom_to_list(Key),
+ Headers#http_request_h.'max-forwards');
+key_value_str(Key = 'proxy-authorization', Headers) ->
+ key_value_str(atom_to_list(Key),
+ Headers#http_request_h.'proxy-authorization');
+key_value_str(Key = range, Headers) ->
+ key_value_str(atom_to_list(Key),
+ Headers#http_request_h.range);
+key_value_str(Key = referer, Headers) ->
+ key_value_str(atom_to_list(Key),
+ Headers#http_request_h.referer);
+key_value_str(Key = te, Headers) ->
+ key_value_str(atom_to_list(Key),
+ Headers#http_request_h.te);
+key_value_str(Key = 'user-agent', Headers) ->
+ key_value_str(atom_to_list(Key),
+ Headers#http_request_h.'user-agent');
+key_value_str(Key = allow, Headers) ->
+ key_value_str(atom_to_list(Key), Headers#http_request_h.allow);
+key_value_str(Key = 'content-encoding', Headers) ->
+ key_value_str(atom_to_list(Key),
+ Headers#http_request_h.'content-encoding');
+key_value_str(Key = 'content-language', Headers) ->
+ key_value_str(atom_to_list(Key),
+ Headers#http_request_h.'content-language');
+key_value_str(Key = 'content-length', Headers) ->
+ case Headers#http_request_h.'content-length' of
+ "0" ->
+ undefined;
+ _ ->
+ key_value_str(atom_to_list(Key),
+ Headers#http_request_h.'content-length')
+ end;
+key_value_str(Key = 'content-location', Headers) ->
+ key_value_str(atom_to_list(Key),
+ Headers#http_request_h.'content-location');
+key_value_str(Key = 'content-md5', Headers) ->
+ key_value_str(atom_to_list(Key),
+ Headers#http_request_h.'content-md5');
+key_value_str(Key = 'content-range', Headers) ->
+ key_value_str(atom_to_list(Key), Headers#http_request_h.'content-range');
+key_value_str(Key = 'content-type', Headers) ->
+ key_value_str(atom_to_list(Key), Headers#http_request_h.'content-type');
+key_value_str(Key = expires, Headers) ->
+ key_value_str(atom_to_list(Key), Headers#http_request_h.expires);
+key_value_str(Key = 'last-modified', Headers) ->
+ key_value_str(atom_to_list(Key), Headers#http_request_h.'last-modified');
+key_value_str(_, undefined) ->
+ undefined;
+key_value_str(Key, Value) ->
+ Key ++ ": " ++ Value ++ ?CRLF.
+
+headers_other([], Headers) ->
+ Headers;
+headers_other([{Key,Value} | Rest], Headers) ->
+ Header = Key ++ ": " ++ Value ++ ?CRLF,
+ headers_other(Rest, [Header | Headers]).
diff --git a/lib/inets/src/http_lib/http_response.erl b/lib/inets/src/http_lib/http_response.erl
new file mode 100644
index 0000000000..b1e7f1e647
--- /dev/null
+++ b/lib/inets/src/http_lib/http_response.erl
@@ -0,0 +1,208 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2005-2009. All Rights Reserved.
+%%
+%% 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 online 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.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(http_response).
+
+-include("http_internal.hrl").
+
+-export([headers/2, header_list/1]).
+
+%%-------------------------------------------------------------------------
+%% headers(HeaderList, #http_response_h{}) -> #http_response_h{}
+%% HeaderList - ["HeaderField:Value"]
+%% HeaderField - string()
+%% Value - string()
+%%
+%% Description: Creates a http_response_h-record used internally to
+%% handle http-headers.
+%%-------------------------------------------------------------------------
+headers([], Headers) ->
+ Headers;
+
+headers([Header | Tail], Headers) ->
+ {Key, [$: | Value]} =
+ lists:splitwith(fun($:) -> false; (_) -> true end, Header),
+ headers(Tail, headers(http_util:to_lower(string:strip(Key)),
+ string:strip(Value), Headers)).
+
+%%-------------------------------------------------------------------------
+%% headers(#http_response_h{}) -> HeaderList
+%% HeaderList - [{"HeaderField", Value"}]
+%% HeaderField - string()
+%% Value - string()
+%%
+%% Description: Creates a list of key value tuples from the #http_response_h
+%% record, to be returned to the application programmer. We
+%% do not wish to make the application programmer dependent on
+%% our records.
+%%-------------------------------------------------------------------------
+header_list(Headers) ->
+ HeaderFields = record_info(fields, http_response_h) -- [other],
+ HeaderList = lists:foldl(fun(Key, Acc) ->
+ case key_value_tuple(Key, Headers) of
+ undefined ->
+ Acc;
+ Tuple ->
+ [Tuple | Acc]
+ end
+ end,
+ [], HeaderFields),
+ lists:reverse(HeaderList) ++ Headers#http_response_h.other.
+%%%========================================================================
+%%% Internal functions
+%%%========================================================================
+headers("cache-control", Value, Headers) ->
+ Headers#http_response_h{'cache-control'= Value};
+headers("connection", Value, Headers) ->
+ Headers#http_response_h{connection = Value};
+headers("date", Value, Headers) ->
+ Headers#http_response_h{date = Value};
+headers("pragma", Value, Headers) ->
+ Headers#http_response_h{pragma = Value};
+headers("trailer", Value, Headers) ->
+ Headers#http_response_h{trailer = Value};
+headers("transfer-encoding", Value, Headers) ->
+ Headers#http_response_h{'transfer-encoding' = Value};
+headers("upgrade", Value, Headers) ->
+ Headers#http_response_h{upgrade = Value};
+headers("via", Value, Headers) ->
+ Headers#http_response_h{via = Value};
+headers("warning", Value, Headers) ->
+ Headers#http_response_h{warning = Value};
+headers("accept-ranges", Value, Headers) ->
+ Headers#http_response_h{'accept-ranges' = Value};
+headers("age", Value, Headers) ->
+ Headers#http_response_h{age = Value};
+headers("etag", Value, Headers) ->
+ Headers#http_response_h{etag = Value};
+headers("location", Value, Headers) ->
+ Headers#http_response_h{location = Value};
+headers("proxy-authenticate", Value, Headers) ->
+ Headers#http_response_h{'proxy-authenticate' = Value};
+headers("retry-after", Value, Headers) ->
+ Headers#http_response_h{'retry-after' = Value};
+headers("server", Value, Headers) ->
+ Headers#http_response_h{server = Value};
+headers("vary", Value, Headers) ->
+ Headers#http_response_h{vary = Value};
+headers("www-authenticate", Value, Headers) ->
+ Headers#http_response_h{'www-authenticate' = Value};
+headers("allow", Value, Headers) ->
+ Headers#http_response_h{allow = Value};
+headers("content-encoding", Value, Headers) ->
+ Headers#http_response_h{'content-encoding' = Value};
+headers("content-language", Value, Headers) ->
+ Headers#http_response_h{'content-language' = Value};
+headers("content-length", Value, Headers) ->
+ Headers#http_response_h{'content-length' = Value};
+headers("content-location", Value, Headers) ->
+ Headers#http_response_h{'content-location' = Value};
+headers("content-md5", Value, Headers) ->
+ Headers#http_response_h{'content-md5' = Value};
+headers("content-range", Value, Headers) ->
+ Headers#http_response_h{'content-range' = Value};
+headers("content-type", Value, Headers) ->
+ Headers#http_response_h{'content-type' = Value};
+headers("expires", Value, Headers) ->
+ Headers#http_response_h{expires = Value};
+headers("last-modified", Value, Headers) ->
+ Headers#http_response_h{'last-modified' = Value};
+headers(Key, Value, Headers) ->
+ Headers#http_response_h{other=
+ [{Key, Value} | Headers#http_response_h.other]}.
+
+
+key_value_tuple(Key = 'cache-control', Headers) ->
+ key_value_tuple(atom_to_list(Key),
+ Headers#http_response_h.'cache-control');
+key_value_tuple(Key = connection, Headers) ->
+ key_value_tuple(atom_to_list(Key), Headers#http_response_h.connection);
+key_value_tuple(Key = date, Headers) ->
+ key_value_tuple(atom_to_list(Key), Headers#http_response_h.date);
+key_value_tuple(Key = pragma, Headers) ->
+ key_value_tuple(atom_to_list(Key), Headers#http_response_h.pragma);
+key_value_tuple(Key = trailer, Headers) ->
+ key_value_tuple(atom_to_list(Key), Headers#http_response_h.trailer);
+key_value_tuple(Key ='transfer-encoding', Headers) ->
+ key_value_tuple(atom_to_list(Key),
+ Headers#http_response_h.'transfer-encoding');
+key_value_tuple(Key = upgrade, Headers) ->
+ key_value_tuple(atom_to_list(Key), Headers#http_response_h.upgrade);
+key_value_tuple(Key = via, Headers) ->
+ key_value_tuple(atom_to_list(Key), Headers#http_response_h.via);
+key_value_tuple(Key = warning, Headers) ->
+ key_value_tuple(atom_to_list(Key), Headers#http_response_h.warning);
+key_value_tuple(Key = 'accept-ranges', Headers) ->
+ key_value_tuple(atom_to_list(Key),
+ Headers#http_response_h.'accept-ranges');
+key_value_tuple(Key = age, Headers) ->
+ key_value_tuple(atom_to_list(Key), Headers#http_response_h.age);
+key_value_tuple(Key = etag, Headers) ->
+ key_value_tuple(atom_to_list(Key), Headers#http_response_h.etag);
+key_value_tuple(Key = location, Headers) ->
+ key_value_tuple(atom_to_list(Key), Headers#http_response_h.location);
+key_value_tuple(Key = 'proxy-authenticate', Headers) ->
+ key_value_tuple(atom_to_list(Key),
+ Headers#http_response_h.'proxy-authenticate');
+key_value_tuple(Key = 'retry-after', Headers) ->
+ key_value_tuple(atom_to_list(Key), Headers#http_response_h.'retry-after');
+key_value_tuple(Key = server, Headers) ->
+ key_value_tuple(atom_to_list(Key), Headers#http_response_h.server);
+key_value_tuple(Key = vary, Headers) ->
+ key_value_tuple(atom_to_list(Key), Headers#http_response_h.vary);
+key_value_tuple(Key = 'www-authenticate', Headers) ->
+ key_value_tuple(atom_to_list(Key),
+ Headers#http_response_h.'www-authenticate');
+key_value_tuple(Key = allow, Headers) ->
+ key_value_tuple(atom_to_list(Key), Headers#http_response_h.allow);
+key_value_tuple(Key = 'content-encoding', Headers) ->
+ key_value_tuple(atom_to_list(Key),
+ Headers#http_response_h.'content-encoding');
+key_value_tuple(Key = 'content-language', Headers) ->
+ key_value_tuple(atom_to_list(Key),
+ Headers#http_response_h.'content-language');
+key_value_tuple(Key = 'content-length', Headers) ->
+ case Headers#http_response_h.'content-length' of
+ "-1" ->
+ undefined;
+ _ ->
+ key_value_tuple(atom_to_list(Key),
+ Headers#http_response_h.'content-length')
+ end;
+key_value_tuple(Key = 'content-location', Headers) ->
+ key_value_tuple(atom_to_list(Key),
+ Headers#http_response_h.'content-location');
+key_value_tuple(Key = 'content-md5', Headers) ->
+ key_value_tuple(atom_to_list(Key),
+ Headers#http_response_h.'content-md5');
+key_value_tuple(Key = 'content-range', Headers) ->
+ key_value_tuple(atom_to_list(Key),
+ Headers#http_response_h.'content-range');
+key_value_tuple(Key = 'content-type', Headers) ->
+ key_value_tuple(atom_to_list(Key),
+ Headers#http_response_h.'content-type');
+key_value_tuple(Key = expires, Headers) ->
+ key_value_tuple(atom_to_list(Key), Headers#http_response_h.expires);
+key_value_tuple(Key = 'last-modified', Headers) ->
+ key_value_tuple(atom_to_list(Key),
+ Headers#http_response_h.'last-modified');
+key_value_tuple(_, undefined) ->
+ undefined;
+key_value_tuple(Key, Value) ->
+ {Key, Value}.
diff --git a/lib/inets/src/http_lib/http_transport.erl b/lib/inets/src/http_lib/http_transport.erl
new file mode 100644
index 0000000000..8100d7183a
--- /dev/null
+++ b/lib/inets/src/http_lib/http_transport.erl
@@ -0,0 +1,353 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2004-2009. All Rights Reserved.
+%%
+%% 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 online 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.
+%%
+%% %CopyrightEnd%
+%%
+%
+-module(http_transport).
+
+% Internal application API
+-export([start/1, connect/3, connect/4, listen/2, listen/3,
+ accept/2, accept/3, close/2,
+ send/3, controlling_process/3, setopts/3,
+ peername/2, resolve/0]).
+
+-export([negotiate/3]).
+
+
+%%%=========================================================================
+%%% Internal application API
+%%%=========================================================================
+
+%%-------------------------------------------------------------------------
+%% start(SocketType) -> ok | {error, Reason}
+%% SocketType = ip_comm | {ssl, _}
+%%
+%% Description: Makes sure inet_db or ssl is started.
+%%-------------------------------------------------------------------------
+start(ip_comm) ->
+ case inet_db:start() of
+ {ok, _} ->
+ ok;
+ {error, {already_started, _}} ->
+ ok;
+ Error ->
+ Error
+ end;
+start({ssl, _}) ->
+ case ssl:start() of
+ ok ->
+ ok;
+ {error, {already_started,_}} ->
+ ok;
+ Error ->
+ Error
+ end.
+
+
+%%-------------------------------------------------------------------------
+%% connect(SocketType, Address, Options, Timeout) ->
+%% {ok, Socket} | {error, Reason}
+%% SocketType = ip_comm | {ssl, SslConfig}
+%% Address = {Host, Port}
+%% Options = [option()]
+%% Socket = socket()
+%% option() = ipfamily() | {ip, ip_address()} | {port, integer()}
+%% ipfamily() = inet | inet6
+%%
+%% Description: Connects to the Host and Port specified in HTTPRequest.
+%%-------------------------------------------------------------------------
+
+connect(SocketType, Address, Opts) ->
+ connect(SocketType, Address, Opts, infinity).
+
+connect(ip_comm = _SocketType, {Host, Port}, Opts0, Timeout)
+ when is_list(Opts0) ->
+ Opts = [binary, {packet, 0}, {active, false}, {reuseaddr, true} | Opts0],
+ gen_tcp:connect(Host, Port, Opts, Timeout);
+
+connect({ssl, SslConfig}, {Host, Port}, _, Timeout) ->
+ Opts = [binary, {active, false}] ++ SslConfig,
+ ssl:connect(Host, Port, Opts, Timeout);
+
+connect({erl_ssl, SslConfig}, {Host, Port}, _, Timeout) ->
+ Opts = [binary, {active, false}, {ssl_imp, new}] ++ SslConfig,
+ ssl:connect(Host, Port, Opts, Timeout).
+
+
+%%-------------------------------------------------------------------------
+%% listen(SocketType, Port) -> {ok, Socket} | {error, Reason}
+%% SocketType = ip_comm | {ssl, SSLConfig}
+%% Port = integer()
+%% Socket = socket()
+%%
+%% Description: Sets up socket to listen on the port Port on the local
+%% host using either gen_tcp or ssl. In the gen_tcp case the port
+%% might allready have been initiated by a wrapper-program and is
+%% given as an Fd that can be retrieved by init:get_argument. The
+%% reason for this to enable a HTTP-server not running as root to use
+%% port 80.
+%%-------------------------------------------------------------------------
+listen(SocketType, Port) ->
+ listen(SocketType, undefined, Port).
+
+listen(ip_comm, Addr, Port) ->
+ case (catch listen_ip_comm(Addr, Port)) of
+ {'EXIT', Reason} ->
+ {error, {exit, Reason}};
+ Else ->
+ Else
+ end;
+
+listen({ssl, SSLConfig} = Ssl, Addr, Port) ->
+ Opt = sock_opt(Ssl, Addr, SSLConfig),
+ ssl:listen(Port, Opt);
+
+listen({erl_ssl, SSLConfig} = Ssl, Addr, Port) ->
+ Opt = sock_opt(Ssl, Addr, SSLConfig),
+ ssl:listen(Port, [{ssl_imp, new} | Opt]).
+
+
+listen_ip_comm(Addr, Port) ->
+ {NewPort, Opts, IpFamily} = get_socket_info(Addr, Port),
+ case IpFamily of
+ inet6fb4 ->
+ Opts2 = [inet6 | Opts],
+ case (catch gen_tcp:listen(NewPort, Opts2)) of
+ {error, Reason} when ((Reason =:= nxdomain) orelse
+ (Reason =:= eafnosupport)) ->
+ Opts3 = [inet | Opts],
+ gen_tcp:listen(NewPort, Opts3);
+
+ %% This is when a given hostname has resolved to a
+ %% IPv4-address. The inet6-option together with a
+ %% {ip, IPv4} option results in badarg
+ {'EXIT', _} ->
+ Opts3 = [inet | Opts],
+ gen_tcp:listen(NewPort, Opts3);
+
+ Other ->
+ Other
+ end;
+ _ ->
+ Opts2 = [IpFamily | Opts],
+ gen_tcp:listen(NewPort, Opts2)
+ end.
+
+ipfamily_default(Addr, Port) ->
+ httpd_conf:lookup(Addr, Port, ipfamily, inet6fb4).
+
+get_socket_info(Addr, Port) ->
+ Key = list_to_atom("httpd_" ++ integer_to_list(Port)),
+ BaseOpts = [{backlog, 128}, {reuseaddr, true}],
+ IpFamilyDefault = ipfamily_default(Addr, Port),
+ case init:get_argument(Key) of
+ {ok, [[Value]]} ->
+ {Fd, IpFamily} =
+ case string:tokens(Value, [$|]) of
+ [FdStr, IpFamilyStr] ->
+ Fd0 = fd_of(FdStr),
+ IpFamily0 = ip_family_of(IpFamilyStr),
+ {Fd0, IpFamily0};
+ [FdStr] ->
+ {fd_of(FdStr), IpFamilyDefault};
+ _ ->
+ throw({error, {bad_descriptor, Value}})
+ end,
+ {0, sock_opt(ip_comm, Addr, [{fd, Fd} | BaseOpts]), IpFamily};
+ error ->
+ {Port, sock_opt(ip_comm, Addr, BaseOpts), IpFamilyDefault}
+ end.
+
+
+fd_of(FdStr) ->
+ case (catch list_to_integer(FdStr)) of
+ Fd when is_integer(Fd) ->
+ Fd;
+ _ ->
+ throw({error, {bad_descriptor, FdStr}})
+ end.
+
+ip_family_of(IpFamilyStr) ->
+ IpFamily = list_to_atom(IpFamilyStr),
+ case lists:member(IpFamily, [inet, inet6, inet6fb4]) of
+ true ->
+ IpFamily;
+ false ->
+ throw({error, {bad_ipfamily, IpFamilyStr}})
+ end.
+
+
+%%-------------------------------------------------------------------------
+%% accept(SocketType, ListenSocket) -> {ok, Socket} | {error, Reason}
+%% accept(SocketType, ListenSocket, Timeout) -> ok | {error, Reason}
+%% SocketType = ip_comm | {ssl, SSLConfig}
+%% ListenSocket = socket()
+%% Timeout = infinity | integer() >= 0
+%% Socket = socket()
+%%
+%% Description: Accepts an incoming connection request on a listen socket,
+%% using either gen_tcp or ssl.
+%%-------------------------------------------------------------------------
+accept(SocketType, ListenSocket) ->
+ accept(SocketType, ListenSocket, infinity).
+accept(ip_comm, ListenSocket, Timeout) ->
+ gen_tcp:accept(ListenSocket, Timeout);
+accept({ssl,_SSLConfig}, ListenSocket, Timeout) ->
+ ssl:transport_accept(ListenSocket, Timeout).
+
+%%-------------------------------------------------------------------------
+%% controlling_process(SocketType, Socket, NewOwner) -> ok | {error, Reason}
+%% SocketType = ip_comm | {ssl, _}
+%% Socket = socket()
+%% NewOwner = pid()
+%%
+%% Description: Assigns a new controlling process to Socket.
+%%-------------------------------------------------------------------------
+controlling_process(ip_comm, Socket, NewOwner) ->
+ gen_tcp:controlling_process(Socket, NewOwner);
+controlling_process({ssl, _}, Socket, NewOwner) ->
+ ssl:controlling_process(Socket, NewOwner).
+
+%%-------------------------------------------------------------------------
+%% setopts(SocketType, Socket, Options) -> ok | {error, Reason}
+%% SocketType = ip_comm | {ssl, _}
+%% Socket = socket()
+%% Options = list()
+%% Description: Sets one or more options for a socket, using either
+%% gen_tcp or ssl.
+%%-------------------------------------------------------------------------
+setopts(ip_comm, Socket, Options) ->
+ inet:setopts(Socket,Options);
+setopts({ssl, _}, Socket, Options) ->
+ ssl:setopts(Socket, Options).
+
+%%-------------------------------------------------------------------------
+%% send(RequestOrSocketType, Socket, Message) -> ok | {error, Reason}
+%% SocketType = ip_comm | {ssl, _}
+%% Socket = socket()
+%% Message = list() | binary()
+%% Description: Sends a packet on a socket, using either gen_tcp or ssl.
+%%-------------------------------------------------------------------------
+send(ip_comm, Socket, Message) ->
+ gen_tcp:send(Socket, Message);
+send({ssl, _}, Socket, Message) ->
+ ssl:send(Socket, Message).
+
+%%-------------------------------------------------------------------------
+%% close(SocketType, Socket) -> ok | {error, Reason}
+%% SocketType = ip_comm | {ssl, _}
+%% Socket = socket()
+%%
+%% Description: Closes a socket, using either gen_tcp or ssl.
+%%-------------------------------------------------------------------------
+close(ip_comm, Socket) ->
+ gen_tcp:close(Socket);
+close({ssl, _}, Socket) ->
+ ssl:close(Socket).
+
+%%-------------------------------------------------------------------------
+%% peername(SocketType, Socket) -> ok | {error, Reason}
+%% SocketType = ip_comm | {ssl, _}
+%% Socket = socket()
+%%
+%% Description: Returns the address and port for the other end of a
+%% connection, usning either gen_tcp or ssl.
+%%-------------------------------------------------------------------------
+peername(ip_comm, Socket) ->
+ case inet:peername(Socket) of
+ {ok,{{A, B, C, D}, Port}} ->
+ PeerName = integer_to_list(A)++"."++integer_to_list(B)++"."++
+ integer_to_list(C)++"."++integer_to_list(D),
+ {Port, PeerName};
+ {ok,{{A, B, C, D, E, F, G, H}, Port}} ->
+ PeerName = http_util:integer_to_hexlist(A) ++ ":"++
+ http_util:integer_to_hexlist(B) ++ ":" ++
+ http_util:integer_to_hexlist(C) ++ ":" ++
+ http_util:integer_to_hexlist(D) ++ ":" ++
+ http_util:integer_to_hexlist(E) ++ ":" ++
+ http_util:integer_to_hexlist(F) ++ ":" ++
+ http_util:integer_to_hexlist(G) ++":"++
+ http_util:integer_to_hexlist(H),
+ {Port, PeerName};
+ {error, _} ->
+ {-1, "unknown"}
+ end;
+
+peername({ssl, _}, Socket) ->
+ case ssl:peername(Socket) of
+ {ok,{{A, B, C, D}, Port}} ->
+ PeerName = integer_to_list(A)++"."++integer_to_list(B)++"."++
+ integer_to_list(C)++"."++integer_to_list(D),
+ {Port, PeerName};
+ {error, _} ->
+ {-1, "unknown"}
+ end.
+
+%%-------------------------------------------------------------------------
+%% resolve() -> HostName
+%% HostName = string()
+%%
+%% Description: Returns the local hostname.
+%%-------------------------------------------------------------------------
+resolve() ->
+ {ok, Name} = inet:gethostname(),
+ Name.
+
+
+%%%========================================================================
+%%% Internal functions
+%%%========================================================================
+
+%% Address any comes from directive: BindAddress "*"
+sock_opt(ip_comm, any = Addr, Opts) ->
+ sock_opt2([{ip, Addr} | Opts]);
+sock_opt(ip_comm, undefined, Opts) ->
+ sock_opt2(Opts);
+sock_opt(_, any = _Addr, Opts) ->
+ sock_opt2(Opts);
+sock_opt(_, undefined = _Addr, Opts) ->
+ sock_opt2(Opts);
+sock_opt(_, {_,_,_,_} = Addr, Opts) ->
+ sock_opt2([{ip, Addr} | Opts]);
+sock_opt(ip_comm, Addr, Opts) ->
+ sock_opt2([{ip, Addr} | Opts]);
+sock_opt(_, Addr, Opts) ->
+ sock_opt2([{ip, Addr} | Opts]).
+
+sock_opt2(Opts) ->
+ [{packet, 0}, {active, false} | Opts].
+
+negotiate(ip_comm,_,_) ->
+ ok;
+negotiate({ssl,_},Socket,Timeout) ->
+ negotiate(Socket, Timeout);
+negotiate({erl_ssl, _}, Socket, Timeout) ->
+ negotiate(Socket, Timeout).
+
+negotiate(Socket, Timeout) ->
+ case ssl:ssl_accept(Socket, Timeout) of
+ ok ->
+ ok;
+ {error, Error} ->
+ case lists:member(Error,
+ [timeout,econnreset,esslaccept,esslerrssl]) of
+ true ->
+ {error,normal};
+ false ->
+ {error, Error}
+ end
+ end.
diff --git a/lib/inets/src/http_lib/http_util.erl b/lib/inets/src/http_lib/http_util.erl
new file mode 100644
index 0000000000..b03b780cf8
--- /dev/null
+++ b/lib/inets/src/http_lib/http_util.erl
@@ -0,0 +1,148 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2005-2009. All Rights Reserved.
+%%
+%% 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 online 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.
+%%
+%% %CopyrightEnd%
+%%
+%%
+-module(http_util).
+
+-export([to_upper/1, to_lower/1, convert_netscapecookie_date/1,
+ hexlist_to_integer/1, integer_to_hexlist/1,
+ convert_month/1, is_hostname/1]).
+
+%%%=========================================================================
+%%% Internal application API
+%%%=========================================================================
+to_upper(Str) ->
+ string:to_upper(Str).
+
+to_lower(Str) ->
+ string:to_lower(Str).
+
+convert_netscapecookie_date([_D,_A,_Y, $,, _SP,
+ D1,D2,_DA,
+ M,O,N,_DA,
+ Y1,Y2,Y3,Y4,_SP,
+ H1,H2,_Col,
+ M1,M2,_Col,
+ S1,S2|_Rest]) ->
+ Year=list_to_integer([Y1,Y2,Y3,Y4]),
+ Day=list_to_integer([D1,D2]),
+ Month=convert_month([M,O,N]),
+ Hour=list_to_integer([H1,H2]),
+ Min=list_to_integer([M1,M2]),
+ Sec=list_to_integer([S1,S2]),
+ {{Year,Month,Day},{Hour,Min,Sec}};
+
+convert_netscapecookie_date([_D,_A,_Y, _SP,
+ D1,D2,_DA,
+ M,O,N,_DA,
+ Y1,Y2,Y3,Y4,_SP,
+ H1,H2,_Col,
+ M1,M2,_Col,
+ S1,S2|_Rest]) ->
+ Year=list_to_integer([Y1,Y2,Y3,Y4]),
+ Day=list_to_integer([D1,D2]),
+ Month=convert_month([M,O,N]),
+ Hour=list_to_integer([H1,H2]),
+ Min=list_to_integer([M1,M2]),
+ Sec=list_to_integer([S1,S2]),
+ {{Year,Month,Day},{Hour,Min,Sec}}.
+
+hexlist_to_integer([])->
+ empty;
+%%When the string only contains one value its eaasy done.
+%% 0-9
+hexlist_to_integer([Size]) when Size >= 48 , Size =< 57 ->
+ Size - 48;
+%% A-F
+hexlist_to_integer([Size]) when Size >= 65 , Size =< 70 ->
+ Size - 55;
+%% a-f
+hexlist_to_integer([Size]) when Size >= 97 , Size =< 102 ->
+ Size - 87;
+hexlist_to_integer([_Size]) ->
+ not_a_num;
+
+hexlist_to_integer(Size) ->
+ Len = string:span(Size, "1234567890abcdefABCDEF"),
+ hexlist_to_integer2(Size, 16 bsl (4 *(Len-2)),0).
+
+integer_to_hexlist(Num)->
+ integer_to_hexlist(Num, get_size(Num), []).
+
+convert_month("Jan") -> 1;
+convert_month("Feb") -> 2;
+convert_month("Mar") -> 3;
+convert_month("Apr") -> 4;
+convert_month("May") -> 5;
+convert_month("Jun") -> 6;
+convert_month("Jul") -> 7;
+convert_month("Aug") -> 8;
+convert_month("Sep") -> 9;
+convert_month("Oct") -> 10;
+convert_month("Nov") -> 11;
+convert_month("Dec") -> 12.
+
+is_hostname(Dest) ->
+ inet_parse:domain(Dest).
+
+%%%========================================================================
+%%% Internal functions
+%%%========================================================================
+hexlist_to_integer2([],_Pos,Sum)->
+ Sum;
+hexlist_to_integer2([HexVal | HexString], Pos, Sum)
+ when HexVal >= 48, HexVal =< 57 ->
+ hexlist_to_integer2(HexString, Pos bsr 4, Sum + ((HexVal-48) * Pos));
+
+hexlist_to_integer2([HexVal | HexString], Pos, Sum)
+ when HexVal >= 65, HexVal =<70 ->
+ hexlist_to_integer2(HexString, Pos bsr 4, Sum + ((HexVal-55) * Pos));
+
+hexlist_to_integer2([HexVal | HexString], Pos, Sum)
+ when HexVal>=97, HexVal=<102 ->
+ hexlist_to_integer2(HexString, Pos bsr 4, Sum + ((HexVal-87) * Pos));
+
+hexlist_to_integer2(_AfterHexString, _Pos, Sum)->
+ Sum.
+
+integer_to_hexlist(Num, Pot, Res) when Pot<0 ->
+ convert_to_ascii([Num | Res]);
+
+integer_to_hexlist(Num,Pot,Res) ->
+ Position = (16 bsl (Pot*4)),
+ PosVal = Num div Position,
+ integer_to_hexlist(Num - (PosVal*Position), Pot-1, [PosVal | Res]).
+
+get_size(Num)->
+ get_size(Num, 0).
+
+get_size(Num, Pot) when Num < (16 bsl(Pot *4)) ->
+ Pot-1;
+
+get_size(Num, Pot) ->
+ get_size(Num, Pot+1).
+
+convert_to_ascii(RevesedNum) ->
+ convert_to_ascii(RevesedNum, []).
+
+convert_to_ascii([], Num)->
+ Num;
+convert_to_ascii([Num | Reversed], Number) when Num > -1, Num < 10 ->
+ convert_to_ascii(Reversed, [Num + 48 | Number]);
+convert_to_ascii([Num | Reversed], Number) when Num > 9, Num < 16 ->
+ convert_to_ascii(Reversed, [Num + 55 | Number]).