http(n)
โจ http(n) โจ โ Tcl Bundled Packages ๐ฆ
okay bestie, sit down, hydrate, because we're about to become the absolute ๐
it girl of HTTP clients. this is the http manual page, and it lives in Tcl Bundled Packages. no cap.
๐ NAME ๐
http โ client-side implementation of the HTTP/1.1 protocol ๐
๐ SYNOPSIS ๐
package require http ?2.10?
::http::config ?-option value ...?
::http::geturl url ?-option value ...?
::http::formatQuery key value ?key value ...?
::http::quoteString value
::http::reset token ?why?
::http::wait token
::http::status token
::http::size token
::http::error token
::http::postError token
::http::cleanup token
::http::requestLine token
::http::requestHeaders token ?headerName?
::http::requestHeaderValue token headerName
::http::responseLine token
::http::responseCode token
::http::reasonPhrase code
::http::responseHeaders token ?headerName?
::http::responseHeaderValue token headerName
::http::responseInfo token
::http::responseBody token
::http::register proto port command ?socketCmdVarName? ?useSockThread? ?endToEndProxy?
::http::registerError sock ?message?
::http::unregister proto
::http::code token
::http::data token
::http::meta token ?headerName?
::http::metaValue token headerName
::http::ncode token
๐ EXPORTED COMMANDS ๐
the http namespace is generous and exports these:
configformatQuerygeturlpostErrorquoteStringreasonPhraseregisterregisterErrorrequestHeadersrequestHeaderValuerequestLineresponseBodyresponseCoderesponseHeadersresponseHeaderValueresponseInforesponseLineresetunregisterwait
and it does NOT export these, so don't come for them:
cleanupcodedataerrormetametaValuencodesizestatus
๐ธ DESCRIPTION ๐ธ
bestie, the http package is the client side of HTTP/1.1 โ as defined in RFC 9110 to 9112, which supersede RFC 7230 to RFC 7235, which in turn supersede RFC 2616. (yes we're three layers deep in RFC lore, it's giving academic ancestry ๐ง)
it implements the GET, POST, and HEAD operations of HTTP/1.1. it lets you configure a proxy host so you can sneak through firewalls. and it's compatible with the Safesock security policy, so even untrusted applets can use it to fetch URLs from a restricted set of hosts โ safe and slay ๐
this package can also be extended to support extra HTTP transport protocols, like HTTPS, by handing it a custom socket command via ::http::register. periodt.
::http::geturl does a HTTP transaction. its options decide whether you're doing a GET, POST, or HEAD. what you get back from ::http::geturl is a token for that transaction. that token is your little VIP wristband ๐ โ you hand it to other commands to manage the transaction and peek at the results.
if you pass the -command option, the HTTP operation happens in the background. ::http::geturl returns immediately after generating the HTTP request, and the -command callback fires when the transaction completes. for this to work, the Tcl event loop has to be active. in Tk applications that's always true. in pure-Tcl applications, you can call ::http::wait after ::http::geturl to start the event loop.
โ ๏ธ note bestie: the event queue is used even without the-commandoption. so as a side effect, arbitrary commands may be processed whilehttp::geturlis running. just so you know. ๐
when the HTTP server has replied, call ::http::responseInfo โ it hands you a dict of metadata that's essential for figuring out whether the transaction succeeded and for actually using the response. see the METADATA section for details. the response body itself comes from ::http::responseBody, unless it got redirected to a file by the -channel option of ::http::geturl.
๐ซถ COMMANDS ๐ซถ
::http::config ?options?
the ::http::config command sets and queries the proxy server name and port, plus the User-Agent name used in HTTP requests. lowkey the control room of the whole vibe.
- no options? โ you get the current configuration back.
- one single argument? โ it should be one of the flags below, and you get that setting's current value.
- otherwise? โ the options should be a set of flags and values defining the configuration. here they are:
-accept mimetypesโ theAcceptheader of the request. default is*/*, which means all types of documents are accepted (we're not picky, we're โจopenโจ). otherwise you can supply a comma-separated list of mime type patterns you're willing to receive. for example,"image/gif, image/jpeg, text/*".-cookiejar commandโ the cookie store for the package to use to manage HTTP cookies.commandis a command prefix list. if the empty list is used (that's the default value), no cookies will be sent by requests or stored from responses. the command indicated bycommand, if you supply one, must obey the COOKIE JAR PROTOCOL described below.-pipeline booleanโ specifies whether HTTP/1.1 transactions on a persistent socket will be pipelined. see the PERSISTENT SOCKETS section for details. the default is1.-postfresh booleanโ specifies whether requests using the POST method will always use a fresh socket, overriding the-keepaliveoption of commandhttp::geturl. see the PERSISTENT SOCKETS section for details. the default is0.-proxyauth stringโ if non-empty, the string is supplied to the proxy server as the value of the request headerProxy-Authorization. this option can be used for HTTP Basic Authentication. if the proxy server requires authentication by another technique โ e.g. Digest Authentication โ the-proxyauthoption is not useful. in that case the caller must expect a407response from the proxy, compute the authentication value to be supplied, and use the-headersoption to supply it as the value of theProxy-Authorizationheader. (DIY era ๐ญ)-proxyfilter commandโcommandis a callback made during::http::geturlto determine if a proxy is required for a given host. one argument, a host name, is added tocommandwhen it's invoked. if a proxy is required, the callback should return a two-element list containing the proxy server and proxy port. otherwise the filter command should return an empty list.
the default value of -proxyfilter is http::ProxyRequired, and this command returns the values of the -proxyhost and -proxyport settings if they are non-empty. the options -proxyhost, -proxyport, and -proxynot are used only by http::ProxyRequired, and nowhere else in ::http::geturl. a user-supplied -proxyfilter command may use these options, or it may grab values from elsewhere in the calling script. in that latter case, any values provided for -proxyhost, -proxyport, and -proxynot are unused.
the ::http::geturl command runs the -proxyfilter callback inside a catch command. therefore an error in the callback command does not call the bgerror handler. see the ERRORS section for details.
-proxyhost hostnameโ the host name or IP address of the proxy server, if any. if this value is the empty string, the URL host is contacted directly. see-proxyfilterfor how the value is used.-proxynot listโ a Tcl list of domain names and IP addresses that should be accessed directly, not through the proxy server. the target hostname is compared with each list element using a case-insensitive string match. it's often convenient to use the wildcard"*"at the start of a domain name (e.g.*.example.com) or at the end of an IP address (e.g.192.168.0.*). see-proxyfilterfor how the value is used.-proxyport numberโ the port number of the proxy server. see-proxyfilterfor how the value is used.-repost booleanโ specifies what to do if a POST request over a persistent connection fails because the server has half-closed the connection. if boolean true, the request will be automatically retried; if boolean false it will not, and the application usinghttp::geturlis expected to seek user confirmation before retrying the POST. the value true should be used only under certain conditions. see the PERSISTENT SOCKETS section for details. the default is0.-threadlevel levelโ specifies whether and how to use the Thread package. possible values oflevelare0,1or2.0โ (the default) do not use Thread.1โ use Thread if it's available, don't use it if it's unavailable.2โ use Thread if it's available, raise an error if it's unavailable.
the Tcl socket -async command can block in adverse cases (e.g. a slow DNS lookup). using the Thread package works around this problem, for both HTTP and HTTPS transactions. values of level other than 0 are available only to the main interpreter in each thread. see section THREADS for more info.
-urlencoding encodingโ the encoding used for creating the x-url-encoded URLs with::http::formatQueryand::http::quoteString. the default isutf-8, as specified by RFC 2718. ๐-useragent stringโ the value that goes in theUser-Agentheader of the HTTP request, bestie. In an unsafe interpreter, the default depends on the operating system plus the version numbers of http and Tcl, and it looks something likeโMozilla/5.0 (Windows; U; Windows NT 10.0) http/2.10.0 Tcl/9.0.0โ. But a safe interpreter literally cannot figure out what OS it's on, so in that case the default just uses a Windows 10 value with the current version numbers of http and Tcl. no cap ๐-zip booleanโ okay so if this is boolean true, then by default requests send the headerโAccept-Encoding: gzip,deflateโ. If it's boolean false, then by default requests sendโAccept-Encoding: identityโ. Either way, you can override the default for one individual request by handing in a customAccept-Encodingheader via the-headersoption ofhttp::geturl. The default value is1. โจ
๐ธ ::http::geturl url ?options? ๐ธ
The ::http::geturl command is the main character of the whole package, fr fr. The -query or -querychannel option makes it do a POST operation, and the -validate option makes it do a HEAD operation; otherwise it performs a GET. ::http::geturl returns a token value that you can pass as an argument to other commands to get info about the transaction โ peep the METADATA and ERRORS sections for the details.
And like, ::http::geturl blocks until the operation completes... unless the -command option specifies a callback that gets invoked when the HTTP transaction finishes. Then it just dips and lets the callback handle it. ๐ฆ
::http::geturl takes several options:
-binary booleanโ specifies whether to force interpreting the URL data as binary. Normally this is auto-detected (anything not beginning with a text content type, or whose content encoding isgzipordeflate, counts as binary data). it's giving self-aware ๐-blocksize sizeโ the block size used when reading the URL. At most size bytes are read at once. After each block, a call to the-progresscallback is made (if that option is specified). slay, incremental reads ๐-channel nameโ copy the URL contents to channel name instead of saving it in a Tcl variable for retrieval by::http::responseBody. stream it straight to the channel, periodt โจ-command callbackโ the presence of this option makes::http::geturlreturn immediately. After the HTTP transaction completes, the value of callback is expanded, an additional argument is added, and the resulting command is evaluated. That additional argument is the token returned from::http::geturl. This token is the name of an array that's described in the STATE ARRAY section. Here's a template for the callback:
proc httpCallback {token} {
upvar 0 $token state
# Access state as a Tcl array defined in this proc
...
return
}
The ::http::geturl command runs the -command callback inside a catch command. So an error inside the callback command does not call the bgerror handler. Check the ERRORS section for the details. ๐ญ
-guesstype booleanโ attempt to guess theContent-Typeand character set when a misconfigured server gives you no information at all. The default value isfalse(do nothing). If boolean true, then if the server doesn't send aContent-Typeheader, or if it sends the value"application/octet-stream",http::geturlwill attempt to guess appropriate values. This is not intended to become a general-purpose tool, and currently it's limited to detecting XML documents that begin with an XML declaration. In that case theContent-Typeis changed to"application/xml", the binary flagstate(binary)is changed to0, and the character set is changed to the one specified by the"encoding"tag of the XML line, or toutf-8if no encoding is specified. Not used if a-channelis specified. lowkey niche but iconic ๐ง-handler callbackโ if this option is absent,http::geturlprocesses incoming data itself, either appending it to thestate(body)variable or writing it to the-channel. But if the-handleroption is present,http::geturldoes not do this processing and instead calls callback. Whenever HTTP data is available, the value of callback is expanded, an additional two arguments are added, and the resulting command is evaluated. The two additional arguments are: the socket for the HTTP data, and the token returned from::http::geturl. The token is the name of a global array that's described in the STATE ARRAY section. The procedure is expected to return the number of bytes read from the socket. Here's a template for the callback:
proc httpHandlerCallback {socket token} {
upvar 0 $token state
# Access socket, and state as a Tcl array defined in this proc
# For example...
...
set data [read $socket 1000]
set nbytes [string length $data]
...
return $nbytes
}
heads up bestie ๐ thehttp::geturlcode for the-handleroption is not compatible with either compression or chunked transfer-encoding. If-handleris specified, then to work around these issueshttp::geturlwill reduce the HTTP protocol to1.0, and override the-zipoption (i.e. it will send the headerAccept-Encoding: identityinstead ofAccept-Encoding: gzip,deflate).
If options -handler and -channel are used together, the handler is responsible for copying the data from the HTTP socket to the specified channel. The name of the channel is available to the handler as element -channel of the token array. ๐ซถ
The ::http::geturl command runs the -handler callback inside a catch command. So an error in the callback command does not call the bgerror handler. See the ERRORS section for details.
-headers keyvaluelistโ this one is for adding headers that::http::confighasn't already specified to the HTTP request. The keyvaluelist argument must be a list with an even number of elements that alternate between keys and values. The keys become header field names. Newlines are stripped from the values so the header cannot be corrupted. For example, if keyvaluelist isPragma no-cachethen the following header is included in the HTTP request:
Pragma: no-cache
-keepalive booleanโ if boolean true, attempt to keep the connection open for servicing multiple requests. Default is0. not me wanting to keep the convo going ๐ญ-method typeโ force the HTTP request method to type.::http::geturlwill auto-selectGET,POSTorHEADbased on other options, but this option overrides that selection and enables choices likePUTandDELETEfor WebDAV support. main character energy fr ๐ฅ
It is the caller's responsibility to ensure that the headers and request body (if any) conform to the requirements of the request method. For example, if using -method POST to send a POST with an empty request body, the caller must also supply the option:
-headers {Content-Length 0}
-myaddr addressโ pass an specific local address to the underlying socket call in case multiple interfaces are available. ๐-progress callbackโ if the-progressoption is present, then the callback is made after each transfer of data from the URL. The value of callback is expanded, an additional three arguments are added, and the resulting command is evaluated. Those three additional arguments are: the token returned from::http::geturl, the expected total size of the contents from theContent-Lengthresponse header, and the current number of bytes transferred so far. The token is the name of a global array that's described in the STATE ARRAY section. The expected total size may be unknown, in which case0is passed to the callback. Here's a template for the progress callback:
proc httpProgress {token total current} {
upvar 0 $token state
# Access state as a Tcl array defined in this proc
...
return
}
-protocol versionโ select the HTTP protocol version to use. This should be1.0or1.1(the default). Should only be necessary for servers that do not understand or otherwise complain about HTTP/1.1. like, ok grandpa ๐-query queryโ this flag (if the value is non-empty) causes::http::geturlto do a POST request that passes the string query verbatim to the server as the request payload. The content format (and encoding) of query is announced by the request headerContent-Typewhich is set by the option-type. Any value of-typeis permitted, and it is the responsibility of the caller to supply query in the correct format. no hand-holding here, you got this โจ
If -type isn't specified, it lowkey defaults to application/x-www-form-urlencoded, which means query has to be an x-url-encoding formatted query-string โ that's the whole -type + query combo you get from a POST submitted out of an html form, bestie. And the ::http::formatQuery procedure is literally right there to do that formatting for you. ๐
-queryblocksizesize โ the block size used when posting query data to the URL. At most size bytes get written at once. After each block, a call to the-queryprogresscallback happens (if you actually specified that option, bestie โ otherwise it's just vibing). โจ-querychannelchannelID โ this flag makes::http::geturldo a POST request that passes the data sitting in channelID to the server. The data in channelID must be an x-url-encoding formatted query, unless you use the-typeoption. If aContent-Lengthheader isn't specified via the-headersoptions,::http::geturltries to figure out the size of the post data so it can create that header. And if it can't figure out the size? It returns an error. No cap. ๐ญ-queryprogresscallback โ if the-queryprogressoption is present, then callback gets made after each transfer of data to the URL in a POST request (so, a call to::http::geturlwith option-queryor-querychannel) and it acts exactly like the-progressoption โ the callback format is the same. Iconic twin energy. ๐-strictboolean โ if true, then the command will test that the URL complies with RFC 3986, i.e. that it has no characters that should be "x-url-encoded" (like, a space should be encoded to"%20"). Default value is1. ๐ฆ-timeoutmilliseconds โ if milliseconds is non-zero, then::http::geturlsets up a timeout to occur after the specified number of milliseconds. A timeout results in a call to::http::resetand to the-commandcallback, if specified. The return value of::http::status(and the value of thestatuskey in the dictionary returned by::http::responseInfo) istimeoutafter a timeout has occurred. โฐ-typemime-type โ use mime-type as theContent-Typevalue, instead of the default value (application/x-www-form-urlencoded) during a POST operation.-validateboolean โ if boolean is non-zero, then::http::geturldoes an HTTP HEAD request. This server returns the same status line and response headers as it would for a HTTP GET request, but omits the response entity (the URL "contents"). The response headers are available after the transaction using command::http::responseHeadersor, for selected information,::http::responseInfo. ๐
๐ง ::http::formatQuery key value ?key value ...?
This procedure does x-url-encoding of query data. It takes an even number of arguments that are the keys and values of the query. It encodes the keys and the values, and generates one string that has the proper & and = separators. The result is suitable for the -query value you pass to ::http::geturl. Slay. ๐
๐ธ ::http::quoteString value
This procedure does x-url-encoding of a string. It takes a single argument and encodes it. Short, sweet, done. ๐
๐ ::http::reset token ?why?
This command resets the HTTP transaction identified by token, if any. This sets the state(status) value to why, which defaults to reset, and then calls the registered -command callback. โจ
๐ฆ ::http::wait token
This command blocks and waits for the transaction to complete. Heads up though โ this only works in trusted code because it uses vwait. Also, it's not useful for the case where ::http::geturl is called without the -command option, because in that case the ::http::geturl call doesn't return until the HTTP transaction is complete, and thus there is literally nothing to wait for. Bestie is already done. ๐
๐ ::http::status token
This command returns a description of the status of the HTTP transaction. The return value is the empty string until the HTTP transaction is completed; after completion it has one of the values ok, eof, error, timeout, and reset. The meaning of these values is described in the section ERRORS (below). ๐
Not me needing to clarify: the name "status" is not related to the terms "status line" and "status code" that are defined for a HTTP response. Different things, same word, classic. ๐
โญ ::http::size token
This command returns the number of bytes received so far from the URL in the ::http::geturl call.
๐ ::http::error token
This command returns the error information if the HTTP transaction failed, or the empty string if there was no error. The information is a Tcl list of the error message, stack trace, and error code.
๐ ::http::postError token
A POST request is a call to ::http::geturl with either the -query or -querychannel option. The ::http::postError command returns the error information generated when a HTTP POST request sends its request-body to the server; or the empty string if there was no error. The information is a Tcl list of the error message, stack trace, and error code. When this type of error occurs, the ::http::geturl command continues the transaction and attempts to receive a response from the server. Resilient queen behavior. ๐
๐ ::http::cleanup token
This procedure cleans up the state associated with the connection identified by token. After this call, procedures like ::http::responseBody cannot be used to get information about the operation. It is strongly recommended that you call this function after you are done with a given HTTP request. Not doing so will result in memory not being freed, and if your app calls ::http::geturl enough times, the memory leak could cause a performance hit...or worse. ๐ญ
Fr fr, call ::http::cleanup. The "or worse" is doing a lot of heavy lifting in that sentence and I need you to respect it. ๐
๐ซถ ::http::requestLine token
This command returns the "request line" sent to the server. The "request line" is the first line of a HTTP client request, and has three elements separated by spaces: the HTTP method, the URL relative to the server, and the HTTP version. Examples:
GET / HTTP/1.1
GET /introduction.html?subject=plumbing HTTP/1.1
POST /forms/order.html HTTP/1.1
โจ ::http::requestHeaders token ?headerName?
This command returns the HTTP request header names and values, in the order that they were sent to the server, as a Tcl list of the form ?name value ...?. Header names are case-insensitive and are converted to lower case. The return value is not a dict because some header names may occur more than once. If one argument is supplied, all request headers are returned. If two arguments are supplied, the second provides the value of a header name. Only headers with the requested name (converted to lower case) are returned. If no such headers are found, an empty list is returned. ๐ฆ
๐ธ ::http::requestHeaderValue token headerName
This command returns the value of the HTTP request header named headerName. Header names are case-insensitive and are converted to lower case. If no such header exists, the return value is the empty string. If there are multiple headers named headerName, the result is obtained by joining the individual values with the string ", " (comma and space), preserving their order. ๐
๐ฅ ::http::responseLine token
This command returns the first line of the server response: the HTTP "status line". The "status line" has three elements separated by spaces: the HTTP version, a three-digit numerical "status code", and a "reason phrase". Only the reason phrase may contain spaces. Examples:
HTTP/1.1 200 OK
HTTP/1.0 404 Not Found
The "status code" is a three-digit number in the range 100 to 599. A value of 200 is the normal return from a GET request, and its matching "reason phrase" is "OK". Codes beginning with 4 or 5 indicate errors. Codes beginning with 3 are redirection errors. In this case the Location response header specifies a new URL that contains the requested information. ๐
The "reason phrase" is a textual description of the "status code": it may vary from server to server, and can be changed without affecting the HTTP protocol. The recommended values (RFC 7231 and IANA assignments) for each code are provided by the command ::http::reasonPhrase.
โญ ::http::responseCode token
This command returns the "status code" (200, 404, etc.) of the server "status line". If a three-digit code cannot be found, the full status line is returned. See command ::http::responseLine for more information on the "status line". ๐
๐ ::http::reasonPhrase code
This command returns the IANA recommended "reason phrase" for a particular "status code" returned by a HTTP server. The argument code is a valid status code, and therefore is an integer in the range 100 to 599 inclusive. For numbers in this range with no assigned meaning, the command returns the value "Unassigned". Several status codes are used only in response to the methods defined by HTTP extensions such as WebDAV, and not in response to a HEAD, GET, or POST request method. ๐ง
The "reason phrase" returned by a HTTP server may differ from the recommended value, without affecting the HTTP protocol. The value returned by ::http::geturl can be obtained by calling either command ::http::responseLine (which returns the full status line) or command ::http::responseInfo (which returns a dictionary, with the "reason phrase" stored in key reasonPhrase).
A registry of valid status codes is maintained at https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml โ go peek if you're the kind of bestie who reads RFCs for fun, and honestly? Respect. ๐โจ
::http::responseHeaders token ?headerName?โ okay so when an HTTP server answers you, it doesn't just yeet the body at you, it also sends metadata headers describing the response body AND the transaction itself ๐. This command hands you back the HTTP response header names and values, in the exact order they were received from the server, as a Tcl list shaped like?name value ...?. Header names are case-insensitive and get converted to lower case. And no, the return value is not a dict โ because some header names can show up more than once, notablySet-Cookie๐ค. If you don't pass the second argument, you get all the response headers. If you do pass it, it provides the value of a header name, and only headers with the requested name (converted to lower case) come back. If no such headers are found, you get an empty list, bestie. See section METADATA for more info ๐.::http::responseHeaderValue token headerNameโ this one returns the value of the HTTP response header named _headerName_. Header names are case-insensitive and get converted to lower case ๐ฆ. If no such header exists, the return value is the empty string. If there are multiple headers named _headerName_, the result is made by joining the individual values with the string", "(that's comma and space, not a vibe) while preserving their order โจ. Multiple headers with the same name may be processed this way โ exceptSet-Cookie, which does not conform to the comma-separated-list syntax and cannot be combined into a single value. EachSet-Cookieheader must be treated individually, e.g. by processing the return value of::http::responseHeaders token Set-Cookie๐ช.::http::responseInfo tokenโ returns a dict of selected response metadata that are essential for identifying a successful transaction and making use of the response, along with other metadata that are just informational ๐ . The keys of the dict arestage,status,responseCode,reasonPhrase,contentType,binary,redirection,upgrade,error,postError,method,charset,compression,httpRequest,httpResponse,url,connectionRequest,connectionResponse,connectionActual,transferEncoding,totalPost,currentPost,totalSize, andcurrentSize๐. The meaning of these keys is described in the section METADATA below.
It is always worth checking the value of binary after a HTTP transaction, to determine whether a misconfigured server has caused http to interpret a text resource as a binary, or vice versa ๐ญ.
After a POST transaction, check the value of postError to verify that the request body was uploaded without error ๐ซถ.
::http::responseBody tokenโ returns the entity sent by the HTTP server (unless-channelwas used, in which case the entity was delivered to the channel, and the command returns the empty string) ๐ธ.
Other terms for "entity", with varying precision, include "representation of resource", "resource", "response body after decoding", "payload", "message body after decoding", "content(s)", and "file" โจ.
::http::register proto port command ?socketCmdVarName? ?useSockThread? ?endToEndProxy?โ this procedure lets you provide custom HTTP transport types such as HTTPS, by registering a prefix, the default port, and the command to execute to create the Tcl channel ๐ฅ. The optional arguments configure how http uses the custom transport, and they have default values that are compatible with older versions of http in which::http::registerhas no optional arguments ๐.
Argument _socketCmdVarName_ is the name of a variable provided by the transport, whose value is the command used by the transport to open a socket. Its default value is set by the transport and is "::socket", but if the name of the variable is supplied to ::http::register, then http will set a new value in order to make optional facilities available ๐. These facilities are enabled by the optional arguments _useSockThread_, _endToEndProxy_, which take boolean values with default value false.
Iff argument _useSockThread_ is supplied and is boolean true, then iff permitted by the value [http::config -threadlevel] and by the availability of package Thread, sockets created for the transport will be opened in a different thread so that a slow DNS lookup will not cause the script to block ๐ค.
Iff argument _endToEndProxy_ is supplied and is boolean true, then when http::geturl accesses a server via a proxy, it will open a channel by sending a CONNECT request to the proxy, and it will then make its request over this channel ๐ฆ. This allows end-to-end encryption for HTTPS requests made through a proxy ๐.
For example,
package require http
package require tls
::http::register https 443 ::tls::socket ::tls::socketCmd 1 1
set token [::http::geturl https://my.secure.site/]
::http::registerError sock ?message?โ this procedure allows a registered protocol handler to deliver an error message for use by http ๐ซถ. Calling this command does not raise an error. The command is useful when a registered protocol detects a problem (for example, an invalid TLS certificate) that will cause an error to propagate to http. The command allows http to provide a precise error message rather than a general one, which is honestly so iconic of it ๐ . The command returns the value provided by the last call with argument _message_, or the empty string if no such call has been made โจ.::http::unregister protoโ this procedure unregisters a protocol handler that was previously registered via::http::register, returning a six-item list of the values that were previously supplied to::http::registerif there was such a handler, and an error if there was no such handler ๐ญ.::http::code tokenโ an alternative name for the command::http::responseLine๐::http::data tokenโ an alternative name for the command::http::responseBody๐::http::meta token ?headerName?โ an alternative name for the command::http::responseHeaders๐::http::ncode tokenโ an alternative name for the command::http::responseCode๐
๐ฉน ERRORS ๐ฉน
The ::http::geturl procedure will raise errors in the following cases: invalid command line options, or an invalid URL ๐จ. These errors mean that it cannot even start the network transaction, like it never even got out the door. For synchronous ::http::geturl calls (where -command is not specified), it will raise an error if the URL is on a non-existent host or at a bad port on an existing host ๐ญ. It will also raise an error for any I/O errors while writing out the HTTP request line and headers, or reading the HTTP reply headers or data. Because ::http::geturl does not return a token in these cases, it does all the required cleanup and there is no issue of your app having to call ::http::cleanup ๐ซถ.
For asynchronous ::http::geturl calls, all of the above error situations apply, except that if there is any error while reading the HTTP reply headers or data, no exception is thrown. This is because after writing the HTTP headers, ::http::geturl returns, and the rest of the HTTP transaction occurs in the background ๐ป. The command callback can check if any error occurred during the read by calling ::http::responseInfo to check the transaction status.
Alternatively, if the main program flow reaches a point where it needs to know the result of the asynchronous HTTP request, it can call ::http::wait and then check status and error, just as the synchronous call does ๐
.
The ::http::geturl command runs the -command, -handler, and -proxyfilter callbacks inside a catch command ๐ชค. Therefore an error in the callback command does not call the bgerror handler. When debugging one of these callbacks, it may be convenient to report errors by using a catch command within the callback command itself, e.g. to write an error message to stdout ๐
In any case, you must still call ::http::cleanup to delete the state array when you are done ๐งน.
There are other possible results of the HTTP transaction determined by examining the status from ::http::status (or the value of the status key in the dictionary returned by ::http::responseInfo) โจ. These are described below.
okโ If the HTTP transaction completes entirely, then status will beok๐. However, you should still check the::http::responseLinevalue to get the HTTP status. The::http::responseCodeprocedure provides just the numeric error (e.g.,200,404or500) while the::http::responseLineprocedure returns a value like โHTTP 404 File not foundโ.eofโ If the server closes the socket without replying, then no error is raised, but the status of the transaction will beeof๐ถ.errorโ The error message, stack trace, and error code are accessible via::http::error๐จ. The error message is also provided by the value of theerrorkey in the dictionary returned by::http::responseInfo.timeoutโ A timeout occurred before the transaction could complete โฐ.resetโ The user has called::http::reset๐.""โ (empty string) The transaction has not yet finished ๐.
Another error possibility is that ::http::geturl failed to write the whole of the POST request body (-query or -querychannel data) to the server ๐ญ. ::http::geturl stores the error message for later retrieval by the ::http::postError or ::http::responseInfo commands, and then attempts to complete the transaction. If it can read the server's response the status will be ok, but it is important to call ::http::postError or ::http::responseInfo after every POST to check that the data was sent in full ๐ฅ. If the server has closed the connection the status will be eof.
๐ฎ METADATA ๐ฎ
โญ MOST USEFUL METADATA โญ
When a HTTP server responds to a request, it supplies not only the entity requested, but also metadata โจ. This is provided by the first line (the "status line") of the response, and by a number of HTTP headers ๐ธ. Further metadata relates to how ::http::geturl has processed the response from the server ๐.
The most important metadata can be accessed with the command ::http::responseInfo ๐
. This command returns a dict of metadata that are essential for identifying a successful transaction and making use of the response, along with other metadata that are informational. The keys of the dict are:
===== Essential Values =====
stageโ set by::http::geturl, and it's literally the vibe check of where your transaction is at. The values, in transaction-lifecycle order, are:"created","connecting","header","body", and"complete". Bestie listen: the other dict keys are not available untilstageis either"body"or"complete". And the keycurrentSizeonly has its final value oncestageis"complete". ๐statusโ also set by::http::geturl. It's"ok"for a successful transaction;"eof","error","timeout", or"reset"for an unsuccessful one; or""if the transaction is still in progress. Same value as what::http::statusreturns. What those values actually mean is described in the ERRORS section (above).responseCodeโ the "HTTP status code" the server sent in the first line (the "status line") of the response. If it can't be extracted from the status line, you get the full status line back instead. ๐reasonPhraseโ the "reason phrase" the server sent describing the HTTP status code. Same deal: if it can't be extracted from the status line, the full status line is what you get.contentTypeโ the value of theContent-Typeresponse header, or, if the server didn't supply that header, the default value"application/octet-stream".binaryโ a boolean set by::http::geturlthat says how the command interpreted the entity the server returned (after decoding any compression named by theContent-Encodingresponse header). That decoded entity is what you get as the return value of::http::responseBody.
It's true when http has interpreted the decoded entity as binary โ in which case ::http::responseBody gives you a Tcl binary string, which is the right format for image data, zip files, etc. ::http::geturl picks this value when you've asked for a binary interpretation by passing the -binary option, or when the server supplied a binary content type in a Content-Type response header, or when the server sent no Content-Type header at all.
It's false in other cases, meaning http interpreted the decoded entity as text. That text has been converted from the character set the server told you about into Tcl's internal Unicode format, so ::http::responseBody gives you an ordinary Tcl string. โจ
It is always worth checking the value of "binary" after a HTTP transaction, to work out whether a misconfigured server made http treat a text resource as binary, or the other way round. ๐
redirectionโ the URL that's the redirection target. Its value is that of theLocationresponse header. That header gets sent when a response has status code 3XX (redirection).upgradeโ if it's not empty, the value tells you the protocol(s) the server will switch to after this transaction finishes, all while continuing to use the same connection. When the server means to switch protocols, it also sends the value"101"as the status code (theresponseCodekey), the word"upgrade"as an element of theConnectionresponse header (theconnectionResponsekey), and it will not send a response body. More on this in the PROTOCOL UPGRADES section.errorโ the error message, if there is one. More info, including a stack trace and error code, is available from::http::error.postErrorโ the error message (if any) generated when a HTTP POST request sends its request-body to the server. More info, including a stack trace and error code, is available from::http::postError. A POST transaction can look complete according to the keysstage,status, andresponseCode, but it is important to check thispostErrorkey in case an error happened while uploading the request-body. ๐ซถ
๐ซ Informational Values ๐ซ
methodโ the HTTP method used in the request.charsetโ the value of the charset attribute of theContent-Typeresponse header. The charset value is used only for a text resource. If the server didn't specify a charset, the value defaults to that of the variable::http::defaultCharset, which โ unless you deliberately changed it โ isiso8859-1. Incoming text data is automatically converted from the character set defined bycharsetinto Tcl's internal Unicode representation, i.e. into a Tcl string. ๐ธcompressionโ a copy of theContent-Encodingresponse-header value.httpRequestโ the version of HTTP specified in the request (i.e. sent in the request line). Its value is whatever the-protocoloption passed to::http::geturlwas (default value"1.1"), unless the command reduced it to"1.0"because it was passed the-handleroption.httpResponseโ the version of HTTP the server used (taken from the response "status line"). The server uses this version of HTTP in its response, but makes sure the response is compatible with the HTTP version named in the client's request. If the value can't be extracted from the status line, the full status line is returned.urlโ the requested URL, typically the URL you handed to::http::geturlbut with its "fragment" stripped off (the fragment is the final part of the URL that begins with"#").connectionRequestโ the value, if any, sent to the server inConnectionrequest header(s).connectionResponseโ the value, if any, received from the server inConnectionresponse header(s).connectionActualโ set by::http::geturl, it reports whether the connection was closed after the transaction (value"close") or left open (value"keep-alive").transferEncodingโ the value of theTransfer-Encodingresponse header, if it's present. The value is either"chunked"(meaning HTTP/1.1 "chunked encoding") or the empty string.totalPostโ the total length of the request body in a POST request.currentPostโ the number of bytes of the POST request body sent to the server so far. Same value as what::http::sizereturns.totalSizeโ a copy of theContent-Lengthresponse-header value, i.e. the number of bytes specified in aContent-Lengthheader, if one was sent. If none was sent, the value is0. A correctly configured server omits this header when the transfer-encoding is"chunked", or (for older servers) when the server closes the connection upon reaching the end of the resource.currentSizeโ the number of bytes fetched from the server so far.
๐ฆ MORE METADATA ๐ฆ
The dictionary returned by ::http::responseInfo is the most useful subset of the metadata that's available. Other metadata includes:
- The full "status line" of the response, available as the return value of
::http::responseLine. - The full response headers, available as the return value of
::http::responseHeaders. This return value is a list of the response-header names and values, in the order they were received from the server.
The return value is not a dict, because some header names may occur more than once โ notably Set-Cookie. If you read the value into a dict or into an array (using array set), only the last header with each name will survive. ๐ญ
Some of the header names (metadata keys) are listed below, but the HTTP standard defines several more, and servers are free to add their own. When a dictionary key is mentioned below, this refers to the dict value returned by ::http::responseInfo.
Content-Typeโ the content type of the URL contents. Examples includetext/html,image/gif,application/postscriptandapplication/x-tcl. Text values typically specify a character set, e.g.text/html; charset=UTF-8. Dictionary keycontentType.Content-Lengthโ the advertised size in bytes of the contents, available as dictionary keytotalSize. The actual number of bytes read by::http::geturlso far is available as dictionary keycurrentSize.Content-Encodingโ the compression algorithm used for the contents. Examples includegzip,deflate. Dictionary keycontent.Locationโ this header is sent when a response has status code 3XX (redirection). It provides the URL that is the redirection target. Dictionary keyredirection.Set-Cookieโ this header is sent to offer a cookie to the client. Cookie management is done by the::http::configoption-cookiejar, so user scripts don't need to parse theSet-Cookieheaders themselves. See section COOKIE JAR PROTOCOL.Connectionโ the value can be supplied as a comma-separated list, or by multiple headers. The list often has only one element, either"close"or"keep-alive". The value"upgrade"indicates a successful upgrade request and is typically combined with the status code 101, anUpgraderesponse header, and no response body. Dictionary keyconnectionResponse.Upgradeโ the value indicates the protocol(s) the server will switch to immediately after the empty line that terminates the 101 response headers. Dictionary keyupgrade.
๐ EVEN MORE METADATA ๐
- Details of the HTTP request. The request is determined by the options supplied to
::http::geturland::http::config. But sometimes it's genuinely helpful to peek at what::http::geturlactually sent to the server, and that info is available through::http::requestHeadersand::http::requestLine. ๐ - The state array: the internal variables of
::http::geturl. It may sometimes be helpful to examine this array. Details are given in the next section.
๐ STATE ARRAY ๐
okay bestie, so when you call ::http::geturl, it hands you a token. That token is literally your backstage pass to all the other ::http::* commands โ they use it to examine and manage the state of the HTTP transaction. For most things, those commands are all you need, no cap. But the token also lets you access the internal state of the transaction, which is stored in a Tcl array. This is super useful when you're writing callback commands for the options -command, -handler, -progress, or -queryprogress. To make an easy-to-use array state as a local variable inside your proc, just run this command inside the proc:
upvar 0 $token state
Once you're done with the data for that URL, you should unset the state array to free up storage. The ::http::cleanup procedure is literally there for that purpose. ๐
Here are all the supported elements of the array, and they're the origin of the values returned by the commands we described below. When we mention a dictionary key, it means the dict value returned by the command ::http::responseInfo. โจ
binaryโ For dictionary keybinary. ๐bodyโ For command::http::responseBody. ๐ฆcharsetโ For dictionary keycharset. ๐คcodingโ For dictionary keycompression. ๐๏ธconnectionโ For dictionary keyconnectionActual. ๐currentsizeโ For command::http::size; and for dictionary keycurrentSize. ๐errorโ For command::http::error;partis used in dictionary keyerror. ๐จhttpโ For command::http::responseLine. ๐httpResponseโ For dictionary keyhttpResponse. ๐ฌmetaโ For command::http::responseHeaders. Further discussion above in the section MORE METADATA. ๐methodโ For dictionary keymethod. ๐ ๏ธposterrorโ For dictionary keypostError. ๐ตpostErrorFullโ For command::http::postError. ๐ฅ-protocolโ For dictionary keyhttpRequest. ๐กquerylengthโ For dictionary keytotalPost. ๐queryoffsetโ For dictionary keycurrentPost. ๐reasonPhraseโ For dictionary keyreasonPhrase. ๐ฌrequestHeadersโ For command::http::requestHeaders. ๐จrequestLineโ For command::http::requestLine. ๐responseCodeโ For dictionary keyresponseCode. ๐ขstateโ For dictionary keystage. ๐ญstatusโ For command::http::status; and for dictionary keystatus. โtotalsizeโ For dictionary keytotalSize. ๐ฆtransferโ For dictionary keytransferEncoding. ๐typeโ For dictionary keycontentType. ๐upgradeโ For dictionary keyupgrade. ๐urlโ For dictionary keyurl. ๐
๐ PERSISTENT CONNECTIONS ๐
โจ BASICS โจ
Okay so first, the receipts: see RFC 7230 Sec 6, which supersedes RFC 2616 Sec 8.1. ๐
A persistent connection lets multiple HTTP/1.1 transactions ride the same TCP connection. Pipelining means a client can fire off a bunch of requests over one persistent connection without waiting for each response to come back first โ and the server sends its responses back in the same order the requests came in. ๐ฏ
Now, if a POST request fails to complete, typically you need user confirmation before sending it again. And bestie, the user might wanna check whether the server actually got modified by that failed POST before firing off the same request a second time. ๐
A HTTP request will use a persistent socket if the call to http::geturl has the option -keepalive true. It'll use pipelining where permitted if the http::config option -pipeline is boolean true (which is its default value, slay). โจ
The http package maintains no more than one persistent connection to each server โ i.e. each value of โdomain:portโ. If http::geturl gets called to make a request over a persistent connection while that connection is busy with another request, the new request just chills in a queue until the connection is free. ๐ซถ
And just so we're clear: the http package does not support HTTP/1.0 persistent connections controlled by the Keep-Alive header. Not a thing. ๐
๐ธ SPECIAL CASES ๐ธ
This bit is all about what happens when the server closes the persistent connection, automatic retry of failed requests, the special treatment POST requests need, and the options for dealing with all of it. ๐
In accordance with RFC 7230, http::geturl does not pipeline requests that use the POST method. So if a POST uses a persistent connection and it's not the first request on that connection, http::geturl waits until it's received the response for the previous request; or โ if the http::config option -postfresh is boolean true โ it uses a brand new connection for each POST. ๐
If the server is processing a bunch of pipelined requests and sends a response header Connection: close with one of the responses (any one other than the last), then the subsequent responses are left unfulfilled. ๐ญ When that happens, http::geturl will send those unfulfilled requests again over a new connection. ๐ฆ
Now here's where it gets spicy. ๐ถ๏ธ A difficulty pops up when a HTTP client sends a request over a persistent connection that's been idle for a while. The HTTP server may half-close an apparently idle connection while the client is sending a request, but before that request actually arrives at the server โ in this case (an โasynchronous close eventโ) the request will fail. The whole difficulty is that the client can't be certain whether the POST modified the state of the server. ๐ตโ๐ซ
For HEAD or GET requests, http::geturl just opens another connection and retransmits the failed request. But if the request was a POST, RFC 7230 forbids automatic retry by default, and instead suggests either user confirmation, or confirmation by user-agent software that has semantic understanding of the application. The http::config option -repost allows for either possibility. ๐
Asynchronous close events can only happen in a short interval of time, so it's giving rare but dramatic. The http package monitors each persistent connection for closure by the server. The moment it detects it, the connection is also closed at the client end, and subsequent requests will use a fresh connection. ๐ง
If the http::geturl command is called with option -keepalive true, then it will both try to use an existing persistent connection (if one is available), and send the server a Connection: keep-alive request header asking to keep the connection open for future requests. ๐
The http::config options -pipeline, -postfresh, and -repost are the ones that relate to persistent connections. Here's the breakdown:
-pipelineโ if boolean true, will pipeline GET and HEAD requests made over a persistent connection. POST requests will not be pipelined โ if the POST is not the first transaction on the connection, its request will not be sent until the previous response has finished. GET and HEAD requests made after a POST will not be sent until the POST response has been delivered, and will not be sent at all if the POST fails. ๐-postfreshโ if boolean true, will override thehttp::geturloption-keepalive, and always open a fresh connection for a POST request. ๐ฅ-repostโ if true, permits automatic retry of a POST request that fails because it uses a persistent connection that the server has half-closed (an โasynchronous close eventโ). Subsequent GET and HEAD requests in a failed pipeline will also be retried. โ ๏ธ Use-repostonly if the application understands that the retry is appropriate โ specifically, the application must know that if the failed POST successfully modified the state of the server, a repeat POST would have no adverse effect. No cap, read that twice. ๐ซก
๐ COOKIE JAR PROTOCOL ๐
Cookies are short key-value pairs used to implement sessions within the otherwise-stateless HTTP protocol. (See RFC 6265 for details; Tcl does not implement the Cookie2 protocol as that is rarely seen in the wild. ๐)
Cookie storage management commands โ โcookie jarsโ โ must support these subcommands, which together form the HTTP cookie storage management protocol. And ngl, note that cookieJar below doesn't have to be a command name; it's properly a command prefix (a Tcl list of words that will be expanded in place) and admits many possible implementations. ๐
Though it's not formally part of the protocol, it is expected that particular values of cookieJar will correspond to sessions; it's up to the caller of ::http::config to decide what session applies and to manage the deletion of said sessions when they're no longer desired (which should be when they not configured as the current cookie jar). ๐
cookieJar getCookies protocol host requestPathโ this command asks the cookie jar what cookies should be supplied for a particular request. It should take the protocol (typicallyhttporhttps), host name and requestPath (parsed from the url argument to::http::geturl) and return a list of cookie keys and values that describe the cookies to supply to the remote host. The list must have an even number of elements. โจ
There should only ever be at most one cookie with a particular key for any request (typically the one with the most specific host/domain match and most specific requestPath/path match), but there may be many cookies with different names in any request. ๐ช
cookieJar storeCookie cookieDictionaryโ this command asks the cookie jar to store a particular cookie that was returned by a request; the result of this command is ignored. The cookie (which will have been parsed by the http package) is described by a dictionary,cookieDictionary, that may have the following keys:domainโ this is always present. Its value describes the domain hostname or prefix that the cookie should be returned for. The checking of the domain against the origin (below) should be careful, since sites that issue cookies should only do so for domains related to themselves. Cookies that do not obey a relevant origin matching rule should be ignored. ๐ซexpiresโ this is optional. If present, the cookie is intended to be a persistent cookie and the value of the option is the Tcl timestamp (in seconds from the same base asclock seconds) of when the cookie expires (which may be in the past, which should result in the cookie being deleted immediately). If absent, the cookie is intended to be a session cookie that should be not persisted beyond the lifetime of the cookie jar. โณhostonlyโ this is always present. Its value is a boolean that describes whether the cookie is a single host cookie (true) or a domain-level cookie (false). ๐ฏhttponlyโ this is always present. Its value is a boolean that is true when the site wishes the cookie to only ever be used with HTTP (or HTTPS) traffic. ๐keyโ this is always present. Its value is the key of the cookie, which is part of the information that must be returned when sending this cookie back in a future request. ๐originโ this is always present. Its value describes where the http package believes it received the cookie from, which may be useful for checking whether the cookie's domain is valid. ๐งญpathโ this is always present. Its value describes the path prefix of requests to the cookie domain where the cookie should be returned. ๐ธsecureโ this is always present. Its value is a boolean that is true when the cookie should only be used on requests sent over secure channels (typically HTTPS). ๐ก๏ธvalueโ this is always present. Its value is the value of the cookie, which is part of the information that must be returned when sending this cookie back in a future request. ๐
Other keys may always be ignored; they have no meaning in this protocol. ๐ โโ๏ธ
๐ PROTOCOL UPGRADES
Okay bestie, this is the โจglow-upโจ section. The HTTP/1.1 Connection and Upgrade request headers are literally the server being told "hey girl, the client wants to switch up the protocol on this existing connection" (RFC 7230). This mechanism can be used to request a WebSocket (RFC 6455), a higher version of the HTTP protocol (HTTP 2), or TLS encryption. And if the server accepts the upgrade request? Its response code will be 101. Iconic.
To request a protocol upgrade when calling http::geturl, the -headers option must supply appropriate values for Connection and Upgrade, and the -command option must supply a command that implements the requested protocol and can also handle the server response if the server refuses the protocol upgrade. For upgrade requests http::geturl ignores the value of option -keepalive, and always uses the value 0 so that the upgrade request is not made over a connection that is intended for multiple HTTP requests. No cap, it just does that. ๐
The Tcllib library websocket implements WebSockets, and makes the necessary calls to commands in the http package.
There is currently no native Tcl client library for HTTP/2. Sad but true, bestie. ๐ญ
The Upgrade mechanism is not used to request TLS in web browsers, because http and https are served over different ports. It is used by protocols such as Internet Printing Protocol (IPP) that are built on top of http(s) and use the same TCP port number for both secure and insecure traffic.
In browsers, opportunistic encryption is instead implemented by the Upgrade-Insecure-Requests client header. If a secure service is available, the server response code is a 307 redirect, and the response header Location specifies the target URL. The browser must call http::geturl again in order to fetch this URL. See https://w3c.github.io/webappsec-upgrade-insecure-requests/
๐งต THREADS
๐ฏ PURPOSE
Command ::http::geturl uses the Tcl ::socket command with the -async option to connect to a remote server, but the return from this command can be delayed in adverse cases (e.g. a slow DNS lookup), preventing the event loop from processing other events. That delay is avoided if the ::socket command is evaluated in another thread. The Thread package is not part of Tcl but is provided in "Batteries Included" distributions. Instead of the ::socket command, the http package uses ::http::socket which makes connections in the manner specified by the value of -threadlevel and the availability of package Thread.
๐ WITH TLS (HTTPS)
The same -threadlevel configuration applies to both HTTP and HTTPS connections. HTTPS is enabled by using the http::register command, typically by specifying the ::tls::socket command of the tls package to handle TLS cryptography. The ::tls::socket command connects to the remote server by using the command specified by the value of variable ::tls::socketCmd, and this value defaults to "::socket". If http::geturl finds that ::tls::socketCmd has this value, it replaces it with the value "::http::socket". If ::tls::socketCmd has a value other than "::socket", i.e. if the script or the Tcl installation has replaced the value "::socket" with the name of a different command, then http does not change the value. The script or installation that modified ::tls::socketCmd is responsible for integrating ::http::socket into its own replacement command.
๐ถ WITH A CHILD INTERPRETER
The peer thread can transfer the socket only to the main interpreter of the script's thread. Therefore the thread-based ::http::socket works with non-zero -threadlevel values only if the script runs in the main interpreter. A child interpreter must use -threadlevel 0 unless the parent interpreter has provided alternative facilities. The main parent interpreter may grant full -threadlevel facilities to a child interpreter, for example by aliasing, to ::http::socket in the child, a command that runs http::socket in the parent, and then transfers the socket to the child.
๐ EXAMPLE
This example creates a procedure to copy a URL to a file while printing a progress meter, and prints the response headers associated with the URL. So slay of it.
proc httpcopy { url file {chunk 4096} } {
set out [open $file w]
set token [::http::geturl $url -channel $out \
-progress httpCopyProgress -blocksize $chunk]
close $out
# This ends the line started by httpCopyProgress
puts stderr ""
upvar 0 $token state
set max 0
foreach {name value} $state(meta) {
if {[string length $name] > $max} {
set max [string length $name]
}
if {[regexp -nocase ^location$ $name]} {
# Handle URL redirects
puts stderr "Location:$value"
return [httpcopy [string trim $value] $file $chunk]
}
}
incr max
foreach {name value} $state(meta) {
puts [format "%-*s %s" $max $name: $value]
}
return $token
}
proc httpCopyProgress {args} {
puts -nonewline stderr .
flush stderr
}
๐ SEE ALSO
๐ KEYWORDS
internet, security policy, socket, www
And that's a wrap on the whole http(n) page, bestie!! ๐โจ The footer at the bottom is from the http manual, version 2.10, section http(n) โ so go forth, make those requests, and stay hydrated while your sockets do their thing. Periodt. ๐๐ฆ
๐ฆ where this page comes from
- fedora 43: tcl-doc 1:9.0.2-1.fc43 ยท TCL AND GPL-3.0-or-later WITH Bison-exception-2.2 AND BSD-3-Clause
- fedora 44: tcl-doc 1:9.0.2-1.fc44 ยท TCL AND GPL-3.0-or-later WITH Bison-exception-2.2 AND BSD-3-Clause
- Copyright (c) 1995-1997 Sun Microsystems, Inc
- Copyright (c) 1998-2000 Ajuba Solutions
- Copyright (c) 2004 ActiveState Corporation
- ๐ girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms