httpc(3erl)
π NAME π
httpc β an HTTP/1.1 client. Yep, that's the whole vibe. (Also, for the record: this is an Erlang Module Definition, bestie β we're in the Erlang docs, not some random man page.)
β¨ DESCRIPTION β¨
Okay so like, this module gives you the API to an HTTP/1.1 compatible client built according to RFC 2616. Caching? Not supported. We don't do that here. π
ββοΈ
π Note, bestie, read this one: When you start the Inets application, a manager process for the default profile gets started. Every function in this API that doesn't explicitly name a profile just... goes and talks to the default profile. And a profile is basically the little purse π where the client keeps track of proxy options, cookies, and all the other options that can apply to more than one request.
If the scheme is https, you must start the SSL application. And when https links need to go through a proxy, the CONNECT method β that's the extension to HTTP-1.1 β is used to establish a tunnel, and then the connection gets upgraded to TLS. But heads up: "TLS upgrade" according to RFC 2817 is not supported. So don't come for her with that. π
Pipelining? Only used if the pipeline time-out is set. Otherwise you get persistent connections without pipelining β meaning the client always waits for the previous response before it sends the next request. Very "wait for your turn in the group chat" energy. π
Some examples are provided in the Inets User's Guide, if you wanna go look. π
π HTTP CLIENT SERVICE START/STOP π
An HTTP client can be configured to start when you start the Inets application, or it can be started dynamically at runtime by calling the Inets application API:
inets:start(httpc, ServiceConfig)
or
inets:start(httpc, ServiceConfig, How)
β see inets(3erl). π«Ά
The configuration options are as follows:
{profile, Profile :: atom() | pid()}β the name of the profile. This option is mandatory. No profile, no service, no vibes. π{data_dir, Path :: string()}β the directory where the profile can save persistent data. If you omit it, all cookies get treated as session cookies (so they ghost you when the session ends πͺπ»).Pathrepresents a file path or directory path.
And to stop the client, you use inets:stop(httpc, Pid) or inets:stop(httpc, Profile). Slay, cleanup. β¨
β οΈ Warning β actually important, don't skip: Please note thathttpcnormalizes input URIs before internal processing, and you need to take special care when the URI has percent ("%") characters. A percent serves as the indicator for percent-encoded octets β and it must be percent-encoded as"%25"for that octet to be used as data within the URI. For example, in order to send an HTTP GET request with the URIhttp://localhost/foo%25bar, the percent character must be percent-encoded when creating the request:httpc:request("http://localhost/foo%2525bar").Like yes it looks cursed. That's the point. π
π₯ EXPORTS π₯
πΈ cancel_request(RequestId) -> ok
πΈ cancel_request(RequestId, Profile) -> ok
Types:
RequestId = any()
A unique identifier as returned by request/4
Profile = atom() | pid()
When started stand_alone only the pid can be used.
RequestIdβ a unique identifier, as returned byrequest/4.Profileβ anatom()or apid(). When startedstand_alone, only the pid can be used.
Cancels an asynchronous HTTP request. But listen β this does not guarantee that the request response won't be delivered. Because it's asynchronous, the request can already have been completed by the time the cancellation arrives. It's giving "you tried to leave the group chat but the message already sent". π
π¦ cookie_header(Url) -> HttpHeader | {error, Reason}
π¦ cookie_header(Url, ProfileOrOpts) -> HttpHeader | {error, Reason}
Types:
Url = uri_string:uri_string()
HttpHeader = {Field :: [byte()], Value :: binary() | iolist()}
ProfileOrOpts = Profile | Opts
Profile = atom() | pid()
When started stand_alone only the pid can be used.
Opts = [CookieHeaderOpt]
CookieHeaderOpt = {ipv6_host_with_brackets, boolean()}
Reason = term()
Urlβ auri_string:uri_string().HttpHeaderβ a tuple of{Field :: [byte()], Value :: binary() | iolist()}.ProfileOrOptsβ either aProfileorOpts, your call, main character. β¨Profileβ anatom()or apid(). When startedstand_alone, only the pid can be used.Optsβ a list ofCookieHeaderOpt.CookieHeaderOptβ{ipv6_host_with_brackets, boolean()}.Reasonβ aterm().
Returns the cookie header that would have been sent when making a request to Url using profile Profile. If no profile is specified, the default profile is used. It's like a little preview of what your cookies are about to say. πͺ
Option ipv6_host_with_bracket deals with how to parse IPv6 addresses. For details, see argument Options of request/[4,5]. (Yes, the docs said it singular there and plural in the type. We don't editorialize, we just report. π)
π¦ cookie_header(Url, Opts, Profile) -> HttpHeader | {error, Reason}
Types:
Url = uri_string:uri_string()
HttpHeader = {Field :: [byte()], Value :: binary() | iolist()}
Profile = atom() | pid()
When started stand_alone only the pid can be used.
Opts = [CookieHeaderOpt]
CookieHeaderOpt = {ipv6_host_with_brackets, boolean()}
Reason = term()
Urlβ auri_string:uri_string().HttpHeaderβ a tuple of{Field :: [byte()], Value :: binary() | iolist()}.Profileβ anatom()or apid(). When startedstand_alone, only the pid can be used.Optsβ a list ofCookieHeaderOpt.CookieHeaderOptβ{ipv6_host_with_brackets, boolean()}.Reasonβ aterm().
Returns the cookie header that would have been sent when making a request to Url using profile Profile. If no profile is specified, the default profile is used.
Option ipv6_host_with_bracket deals with how to parse IPv6 addresses. For details, see argument Options of request/[4,5]. Same deal as above, icon. π
π§ get_options(OptionItems) -> {ok, Values} | {error, Reason}
π§ get_options(OptionItems, Profile) -> {ok, Values} | {error, Reason}
Types:
OptionItems = all | [OptionItem]
OptionItem =
proxy | https_proxy | max_sessions | keep_alive_timeout |
max_keep_alive_length | pipeline_timeout |
max_pipeline_length | cookies | ipfamily | ip | port |
socket_opts | verbose | unix_socket
Values = [{OptionItem, term()}]
Profile = atom() | pid()
When started stand_alone only the pid can be used.
Reason = term()
OptionItemsβ eitherallor a list ofOptionItem.OptionItemβ one of:proxy,https_proxy,max_sessions,keep_alive_timeout,max_keep_alive_length,pipeline_timeout,max_pipeline_length,cookies,ipfamily,ip,port,socket_opts,verbose,unix_socket.Valuesβ a list of{OptionItem, term()}.Profileβ anatom()or apid(). When startedstand_alone, only the pid can be used.Reasonβ aterm().
Retrieves the options currently used by the client. Peak "what are we even doing right now" introspection. π
β info() -> list() | {error, Reason}
β info(Profile) -> list() | {error, Reason}
Types:
Reason = term()
Profile = atom() | pid()
When started stand_alone only the pid can be used.
Reasonβ aterm().Profileβ anatom()or apid(). When startedstand_alone, only the pid can be used.
Produces a list of miscellaneous information. Intended for debugging. If no profile is specified, the default profile is used. It's literally just a little gossip dump about the client. π«Ά
π reset_cookies() -> Void
π reset_cookies(Profile) -> Void
Types:
Profile = atom() | pid()
When started stand_alone only the pid can be used.
Void = term()
Profileβ anatom()or apid(). When startedstand_alone, only the pid can be used.Voidβ aterm().
Resets (clears) the cookie database for the specified Profile. If no profile is specified, the default profile is used. Fresh start, clean slate, unbothered, moisturized. β¨π§ββοΈ
π request(Url :: uri_string:uri_string()) -> {ok, Result} | {error, term()}
π request(Url, Profile) -> {ok, Result} | {error, term()}
Types:
Url = uri_string:uri_string()
Profile = atom() | pid()
When started stand_alone only the pid can be used.
Result =
{StatusLine, [HttpHeader], HttpBodyResult} |
{StatusCode, HttpBodyResult} |
RequestId | saved_to_file
HttpHeader = {Field :: [byte()], Value :: binary() | iolist()}
HttpBodyResult = uri_string:uri_string() | binary()
StatusLine = {HttpVersion, StatusCode, string()}
HttpVersion = uri_string:uri_string()
StatusCode = integer() >= 0
RequestId = any()
Urlβ auri_string:uri_string().Profileβ anatom()or apid(). When startedstand_alone, only the pid can be used.Resultβ either{StatusLine, [HttpHeader], HttpBodyResult}, or{StatusCode, HttpBodyResult}, or aRequestId, orsaved_to_file.HttpHeaderβ{Field :: [byte()], Value :: binary() | iolist()}.HttpBodyResultβ auri_string:uri_string()or abinary().StatusLineβ{HttpVersion, StatusCode, string()}.HttpVersionβ auri_string:uri_string().StatusCodeβ aninteger() >= 0.RequestIdβany().
Equivalent to httpc:request(get, {Url, []}, [], []). She's the shortcut, the low-effort bop, the "just GET it" of the family. π
π₯ request(Method, Request, HttpOptions, Options) -> {ok, Result} | {error, term()}
π₯ request(Method, Request, HttpOptions, Options, Profile) -> {ok, Result} | {error, term()}
Types:
Method =
head | get | put | patch | post | trace | options | delete
Request =
{uri_string:uri_string(), [HttpHeader]} |
{uri_string:uri_string(),
[HttpHeader],
ContentType :: uri_string:uri_string(),
HttpBody}
HttpBody =
iolist() |
binary() |
{fun((Accumulator :: term()) ->
eof | {ok, iolist(), Accumulator :: term()}),
Accumulator :: term()} |
{chunkify,
fun((Accumulator :: term()) ->
eof | {ok, iolist(), Accumulator :: term()}),
Accumulator :: term()}
HttpHeader = {Field :: [byte()], Value :: binary() | iolist()}
HttpOptions = [HttpOption]
HttpOption =
{timeout, timeout()} |
{connect_timeout, timeout()} |
{ssl, [ssl:tls_option()]} |
{autoredirect, boolean()} |
{proxy_auth, {string(), string()}} |
{version, HttpVersion} |
{relaxed, boolean()}
Options = [OptionRequest]
OptionRequest =
{sync, boolean()} |
{stream, StreamTo} |
{body_format, BodyFormat} |
{full_result, boolean()} |
{headers_as_is, boolean()} |
{socket_opts, [SocketOpt]} |
{receiver, Receiver} |
{ipv6_host_with_brackets, boolean()} |
{max_header_size, MaxSize} |
{max_body_size, MaxSize}
StreamTo = none | self | {self, once} | file:name_all()
BodyFormat = string() | binary() | atom()
SocketOpt = term()
Receiver =
pid() |
fun((term()) -> term()) |
{ReceiverModule :: atom(),
ReceiverFunction :: atom(),
ReceiverArgs :: list()}
MaxSize = integer() >= 0 | nolimit
Profile = atom() | pid()
When Profile is stand_alone only the pid can be used.
HttpVersion = uri_string:uri_string()
Result =
{StatusLine, [HttpHeader], HttpBodyResult} |
{StatusCode, HttpBodyResult} |
RequestId | saved_to_file
StatusLine = {HttpVersion, StatusCode, string()}
StatusCode = integer() >= 0
HttpBodyResult = uri_string:uri_string() | binary()
RequestId = any()
Methodβhead,get,put,patch,post,trace,options, ordelete. Pick your fighter. π₯Requestβ either{uri_string:uri_string(), [HttpHeader]}, or{uri_string:uri_string(), [HttpHeader], ContentType :: uri_string:uri_string(), HttpBody}.HttpBodyβ aniolist(), abinary(), a{fun/1, Accumulator}pair, or a{chunkify, fun/1, Accumulator}triple. Lots of shapes, all valid, she's flexible. π¦HttpHeaderβ{Field :: [byte()], Value :: binary() | iolist()}.HttpOptionsβ a list ofHttpOption.HttpOptionβ one of{timeout, timeout()},{connect_timeout, timeout()},{ssl, [ssl:tls_option()]},{autoredirect, boolean()},{proxy_auth, {string(), string()}},{version, HttpVersion},{relaxed, boolean()}.Optionsβ a list ofOptionRequest.OptionRequestβ one of{sync, boolean()},{stream, StreamTo},{body_format, BodyFormat},{full_result, boolean()},{headers_as_is, boolean()},{socket_opts, [SocketOpt]},{receiver, Receiver},{ipv6_host_with_brackets, boolean()},{max_header_size, MaxSize},{max_body_size, MaxSize}.StreamToβnone,self,{self, once}, or afile:name_all().BodyFormatβ astring(), abinary(), or anatom().SocketOptβ aterm().Receiverβ apid(), afun((term()) -> term()), or{ReceiverModule :: atom(), ReceiverFunction :: atom(), ReceiverArgs :: list()}.MaxSizeβ aninteger() >= 0ornolimit.Profileβ anatom()or apid(). WhenProfileisstand_alone, only the pid can be used.HttpVersionβ auri_string:uri_string().Resultβ{StatusLine, [HttpHeader], HttpBodyResult}, or{StatusCode, HttpBodyResult}, or aRequestId, orsaved_to_file.StatusLineβ{HttpVersion, StatusCode, string()}.StatusCodeβ aninteger() >= 0.HttpBodyResultβ auri_string:uri_string()or abinary().RequestIdβany().
Sends an HTTP request. π The function can be both synchronous and asynchronous β she contains multitudes. In the asynchronous case, the function returns {ok, RequestId} and then the information is delivered to the receiver, depending on that value.
When Profile is stand_alone, only the pid can be used. Not the atom. The pid. Only the pid. Okay? π€
π HTTP options π
timeoutβ the time-out time for the request.- The clock starts ticking when the request is sent. β°
- Time is in milliseconds.
- Default is
infinity.
connect_timeoutβ the connection time-out time, used during the initial request, when the client is connecting to the server. (That's the "hey are you there" phase. π«Ά)- Time is in milliseconds.
- Default is the value of option
timeout.
sslβ this is the SSL/TLS connecting configuration option.- Defaults to
[]. Seessl:connect/[2,3,4]for available options.
- Defaults to
autoredirectβ the client goes and fetches the thing from the new URI itself and hands you that as the result, instead of giving you back a 30X-result code. π« But heads up: for some 30X-result codes, automatic redirect is not allowed, and in those cases the 30X-result is always returned. No sneaky workarounds. Default istrue.proxy_authβ a proxy-authorization header, given as a tuple where the first element is the username and the second element of the tuple is the password, added to the request. πversionβ lets you make the client act as an HTTP/1.0 client. By default this is an HTTP/1.1 client. When using HTTP/1.0, persistent connections are not used (sad, but that's the rules). Default is the string"HTTP/1.1".relaxedβ if set totrue, workarounds for known server deviations from the HTTP-standard are enabled. Basically giving the server some grace π . Default isfalse.
Options details π
syncβ option for the request to be synchronous or asynchronous. Default istrue.streamβ streams the body of a200or206response to the calling process or to a file. π¦ When streaming to the calling process using optionself, the following stream messages are sent to that process:
{http, {RequestId, stream_start, Headers}}
{http, {RequestId, stream, BinBodyPart}}
{http, {RequestId, stream_end, Headers}}
When streaming to the calling processes using option {self, once}, the first message has an extra element, that is, {http, {RequestId, stream_start, Headers, Pid}}. This is the process id to be used as an argument to httpc:stream_next/1 to trigger the next message to be sent to the calling process. π
Notice that chunked encoding can add headers so that there are more headers in the stream_end message than in stream_start. When streaming to a file and the request is asynchronous, the message {http, {RequestId, saved_to_file}} is sent. π
Default is none.
body_formatβ defines if the body is to be delivered as astringorbinary. This option is only valid for the synchronous request, fyi. Default isstring.full_resultβ defines if a "full result" is to be returned to the caller (that is, the body, the headers, and the entire status line) or not (the body and the status code). Default istrue.headers_as_isβ defines if the headers provided by the user are to be made lower case or to be regarded as case sensitive.
The HTTP standard requires them to be case insensitive. Use this feature only if there is no other way to communicate with the server or for testing purpose. When this option is used, no headers are automatically added. All necessary headers must be provided by the user, bestie. π
Default is false.
socket_optsβ socket options to be used for this request.
See the options used by gen_tcp(3erl) and ssl(3erl)
Overrides any value set by function set_options.
The validity of the options is not checked by the HTTP client β they are assumed to be correct and passed on to the ssl application and inet driver, which may reject them if they are not correct. π
Note: persistent connections are not supported when setting thesocket_optsoption. Whensocket_optsis not set, the current implementation assumes the requests to the same host, port combination will use the same socket options.
By default the socket options set by function set_options/[1,2] are used when establishing a connection.
receiverβ defines how the client delivers the result of an asynchronous request (synchas the valuefalse). β¨pid()β messages are sent to this process in the format{http, ReplyInfo}.function/1β information is delivered to the receiver through calls to the provided funReceiver(ReplyInfo).{Module, Function, Args}β information is delivered to the receiver through calls to the callback functionapply(Module, Function, [ReplyInfo | Args]).
In all of these cases, ReplyInfo has the following structure:
{RequestId, saved_to_file}
{RequestId, {error, Reason}}
{RequestId, Result}
{RequestId, stream_start, Headers}
{RequestId, stream_start, Headers, HandlerPid}
{RequestId, stream, BinBodyPart}
{RequestId, stream_end, Headers}
Default is the pid of the process calling the request function (self()).
ipv6_host_with_bracketsβ defines when parsing the Host-Port part of an URI with an IPv6 address with brackets, if those brackets are to be retained (true) or stripped (false). Default isfalse.
set_options(Options) -> ok | {error, Reason}
set_options(Options, Profile) -> ok | {error, Reason}
Types:
Options = [Option]
Option =
{proxy, {Proxy, NoProxy}} |
{https_proxy, {Proxy, NoProxy}} |
{max_sessions, MaxSessions} |
{max_keep_alive_length, MaxKeepAlive} |
{keep_alive_timeout, KeepAliveTimeout} |
{max_pipeline_length, MaxPipeline} |
{pipeline_timeout, PipelineTimeout} |
{cookies, CookieMode} |
{ipfamily, IpFamily} |
{ip, IpAddress} |
{port, Port} |
{socket_opts, [SocketOpt]} |
{verbose, VerboseMode} |
{unix_socket, UnixSocket}
Profile = atom() | pid()
SocketOpt = term()
Proxy = {HostName, Port}
Port = integer() >= 0
NoProxy = [DomainDesc | HostName | IpAddressDesc]
MaxSessions = MaxKeepAlive = KeepAliveTimeout = MaxPipeline =
PipelineTimeout = integer()
CookieMode = enabled | disabled | verify
IpFamily = inet | inet6 | local | inet6fb4
IpAddressDesc = uri_string:uri_string()
IpAddress = inet:ip_address()
VerboseMode = false | verbose | debug | trace
UnixSocket = string()
Reason = term()
DomainDesc = string()
HostName = uri_string:uri_string()
Sets options to be used for subsequent requests. Like, "remember this for next time" vibes. π«Ά
HostNameβ example:"localhost"or"foo.bar.se"DomainDescβ example"*.Domain"or"*.ericsson.se"IpAddressDescβ example:"134.138"or"[FEDC:BA98"(all IP addresses starting with134.138orFEDC:BA98),"66.35.250.150"or"[2010:836B:4179::836B:4179]"(a complete IP address).proxydefaults to{undefined, []}, that is, no proxy is configured, andhttps_proxydefaults to the value ofproxy.MaxSessionsβ MaxSessions maximum number of persistent connections to a host. Default is2.MaxKeepAliveβ MaxKeepAlive maximum number of outstanding requests on the same connection to a host. Default is5.KeepAliveTimeoutβ KeepAliveTimeout: if a persistent connection is idle longer than thekeep_alive_timeoutin milliseconds, the client closes the connection. The server can also have such a time-out, but do not take that for granted. Default is120000(= 2 min). β°MaxPipelineβ MaxPipeline maximum number of outstanding requests on a pipelined connection to a host. Default is2.PipelineTimeoutβ PipelineTimeout: if a persistent connection is idle longer than thepipeline_timeoutin milliseconds, the client closes the connection. Default is0, which results in pipelining not being used.CookieModeβ if cookies are enabled, all valid cookies are automatically saved in the cookie database of the client manager. If optionverifyis used, functionstore_cookies/2has to be called for the cookies to be saved. Default isdisabled. πͺIpFamilyβ default isinet. Withinet6fb4option, IPv6 will be preferred but if connection fails, an IPv4 fallback connection attempt will be made. best of both worlds fr fr πIpAddressβ if the host has several network interfaces, this option specifies which one to use. Seegen_tcp:connect/3,4for details.Portβ example:8080. Local port number to use. Seegen_tcp:connect/3,4for details.SocketOptsβ the options are appended to the socket options used by the client. These are the default values when a new request handler is started (for the initial connect). They are passed directly to the underlying transport (gen_tcpor SSL) without verification.
See the options used by gen_tcp(3erl) and ssl(3erl)
VerboseModeβ default isfalse. This option is used to switch on (or off) different levels of Erlang trace on the client. It is a debug feature. πProfileβ when startedstand_aloneonly the pid can be used.UnixSocketβ experimental option for sending HTTP requests over a unix domain socket. The value ofunix_socketshall be the full path to a unix domain socket file with read/write permissions for the erlang process. Default isundefined.
Note: if possible, the client keeps its connections alive and uses persistent connections with or without pipeline depending on configuration and current circumstances. The HTTP/1.1 specification does not provide a guideline for how many requests that are ideal to be sent on a persistent connection. This depends much on the application. π
Real talk about max_sessions, bestie: a long queue of requests can cause a user-perceived delay, because earlier requests can take a long time to complete. The HTTP/1.1 specification suggests a limit of two persistent connections per server β and that is literally the default value of option max_sessions. Also, the current implementation assumes that requests to the same host, port combination will use the same socket options. β¨
π ssl_verify_host_options(WildcardHostName) -> list()
Types:
WildcardHostName = boolean()
This one returns ssl options you can use to verify the host. It uses public_key:cacerts_get() to read CA certificates, and if WildcardHostName is true, it adds the hostname check from public_key:public_key:pkix_verify_hostname_match_fun(https) to the options. Very βtrust but verifyβ of it. π
π store_cookies(SetCookieHeaders, Url) -> ok | {error, Reason} / store_cookies(SetCookieHeaders, Url, Profile) -> ok | {error, Reason}
Types:
SetCookieHeaders = [HttpHeader]
Where field = "set-cookie"
HttpHeader = {Field :: [byte()], Value :: binary() | iolist()}
Url = term()
Profile = atom() | pid()
When started stand_alone only the pid can be used.
Reason = term()
Saves the cookies defined in SetCookieHeaders in the client profile cookie database. Call this function if option cookies is set to verify. If no profile is specified, the default profile is used. Cookie storage era, no cap. πͺπ
β stream_next(Pid) -> ok
Types:
Pid = pid()
As received in the stream_start message
Triggers the next message to be streamed β that is, the same behavior as active ones for sockets. Itβs literally the βnext bestie pleaseβ button. π
π which_cookies() -> [CookieStores] / which_cookies(Profile) -> [CookieStores]
Types:
Profile = atom() | pid()
When started stand_alone only the pid can be used.
CookieStores = {cookies, Cookies} | {session_cookies, Cookies}
Cookies = [term()]
Produces a list of the entire cookie database. Intended for debugging/testing purposes. If no profile is specified, the default profile is used. Detective mode: activated. ππ«Ά
π which_sessions() -> SessionInfo / which_sessions(Profile) -> SessionInfo
Types:
Profile = atom() | pid()
When started stand_alone only the pid can be used.
SessionInfo = {GoodSession, BadSessions, NonSessions}
GoodSession = [Session]
BadSessions = NonSessions = [term()]
Session = term()
Internal representation of a session.
This function is intended for debugging only. It produces a slightly processed dump of the session database. The first list of the session information tuple will contain session information on an internal format. The last two lists of the session information tuple should always be empty if the code is working as intended. If no profile is specified, the default profile is used. Vibe check on the session DB, fr fr. π§π
π SEE ALSO π
Footer sign-off, with source/version/date energy: Ericsson AB Β· inets 8.3.1.2 Β· httpc(3erl) πβ¨
π¦ where this page comes from
- ubuntu noble: erlang-manpages 1:25.3.2.8+dfsg-1ubuntu4.8 Β· Apache-2.0
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash-1017 on : a language model's rewrite of the original, under its terms