util::http::post (public)

 util::http::post [ -url url ] [ -files files ] [ -base64 ] \
    [ -formvars formvars ] [ -body body ] \
    [ -max_body_size max_body_size ] [ -headers headers ] \
    [ -timeout timeout ] [ -max_depth max_depth ] [ -force_ssl ] \
    [ -multipart ] [ -gzip_request ] [ -gzip_response ] \
    [ -post_redirect ] [ -spool ] [ -preference preference ]

Defined in packages/acs-tcl/tcl/http-client-procs.tcl

Implement client-side HTTP POST request.

Switches:
-url (optional)
-files (optional)
File upload can be specified using actual files on the filesystem or binary strings of data using the -files parameter. -files must be a dict (flat list of key value pairs). Keys of -files parameter are:
  • data: binary data to be sent. If set, has precedence on 'file' key
  • file: path for the actual file on filesystem
  • filename: name the form will receive for this file
  • fieldname: name the field this file will be sent as
  • mime_type: mime_type the form will receive for this file
If 'filename' is missing and an actual file is being sent, it will be set as the same name as the file.
If 'mime_type' is missing, it will be guessed from 'filename'. If result is */* or an empty mime_type, 'application/octet-stream' will be used
If -base64 flag is set, files will be base64 encoded (useful for some kind of form).
-base64 (boolean) (optional)
-formvars (optional)
-body (optional)
is the payload for the request and will be passed as is (useful for many purposes, such as webDav). A convenient way to specify form variables through this argument is passing a string obtained by export_vars -url.
-max_body_size (defaults to "25000000") (optional)
this value in number of characters will tell how big can the whole body payload get before we start spooling its content to a file. This is important in case of big file uploads, when keeping the entire request in memory is just not feasible. The handling of the spooling is taken care of in the api. This value takes into account also the encoding required by the content type, so its value could not reflect the exact length of body's string representation.
-headers (optional)
specifies an ns_set of extra headers to send to the server when doing the request. Some options exist that allow to avoid the need to specify headers manually, but headers will always take precedence over options.
-timeout (defaults to "30") (optional)
Timeout in seconds. The value can be an integer, a floating point number or an ns_time value.
-max_depth (defaults to "10") (optional)
is the maximum number of redirects the proc is allowed to follow. A value of 0 disables redirection. When max depth for redirection has been reached, proc will return response from the last page we were redirected to. This is important if redirection response contains data such as cookies we need to obtain anyway. Be aware that when following redirects, unless it is a code 303 redirect, url and POST urlencoded variables will be sent again to the redirected host. Multipart variables won't be sent again. Sending to the redirected host can be dangerous, if such host is not trusted or uses a lower level of security.
-force_ssl (boolean) (optional)
specifies wether we want to use SSL despite the url being in http:// form. Default behavior is to use SSL on https:// urls only.
-multipart (boolean) (optional)
-gzip_request (boolean) (optional)
informs the server that we are sending data in gzip format. Data will be automatically compressed. Notice that not all servers can treat gzipped requests properly, and in such cases response will likely be an error.
-gzip_response (boolean) (optional)
informs the server that we are capable of receiving gzipped responses. If server complies to our indication, the result will be automatically decompressed.
-post_redirect (boolean) (optional)
decides what happens when we are POSTing and server replies with 301, 302 or 303 redirects. RFC 2616/10.3.2 states that method should not change when 301 or 302 are returned, and that GET should be used on a 303 response, but most HTTP clients fail in respecting this and switch to a GET request independently. This options forces this kinds of redirect to conserve their original method.
-spool (boolean) (optional)
enables file spooling of the request on the file specified. It is useful when we expect large responses from the server. The result is spooled to a temporary file, the name is returned in the file component of the result.
-preference (defaults to "native curl") (optional)
decides which available implementation prefer in respective order. Choice is between 'native', based on ns_ api, available for NaviServer only and giving the best performances and 'curl', which wraps the command line utility (available on every system with curl installed).
Returns:
Returns the data as dict with elements headers, page, file, status, and modified.
Source code:
    set this_proc [lindex [info level 0] 0]

    # Retrieve variables sent by the URL...
    set vars [lindex [split $url ?] 1]
    foreach var [split $vars &] {
        set var [split $var =]
        set key [lindex $var 0]
        set urlvars($key) 1
    }

    # Check wether we don't have multiple variable definition in url and payload
    foreach var [split $formvars &] {
        set var [split $var =]
        set key [lindex $var 0]
        if {[info exists urlvars($key)]} {
            return -code error "${this_proc}:  Variable '$key' already specified as url variable"
        }
    }

    if {$headers eq ""} {
        set headers [ns_set create headers]
    }

    set req_content_type [ns_set iget $headers "content-type"]
    
    set payload {}
    set payload_file {}
    set payload_file_fd {}

    # Request will be multipart if required by the flag, if we have
    # files or if set up manually by the headers
    if {$multipart_p ||
        $files ne {} ||
        [string match -nocase "*multipart/form-data*" $req_content_type]} {

        # delete every manually set content-type header...
        while {[ns_set ifind $headers "Content-type"] >= 0} {
            ns_set idelkey $headers "Content-type"
        }
        # ...replace it with our own...
        set boundary [ns_sha1 [list [clock clicks -milliseconds] [clock seconds]]]
        set req_content_type "multipart/form-data; boundary=$boundary"        
        ns_set put $headers "Content-type" $req_content_type
        # ...and get the proper encoding for the content.
        set enc [util::http::get_channel_settings $req_content_type]

        # Transform files into binaries
        foreach file $files {
            unset -nocomplain f
            array set f $file

            if {![info exists f(data)]} {
                if {![info exists f(file)]} {
                    return -code error "${this_proc}:  No file specified"
                }
                if {![file exists $f(file)]} {
                    return -code error "${this_proc}:  Error reading file: $f(file) not found"
                }
                if {![file readable $f(file)]} {
                    return -code error "${this_proc}:  Error reading file: $f(file) permission denied"
                }

                if {![info exists f(filename)]} {
                    set f(filename) [file tail $f(file)]
                }
            }

            foreach key {filename fieldname} {
                if {![info exists f($key)]} {
                    return -code error "${this_proc}:  '$key' missing for file POST"
                }
            }

            # Check that we don't already have this var specified in the url
            if {[info exists urlvars($f(fieldname))]} {
                return -code error "${this_proc}:  file field '$f(fieldname)' already specified as url variable"
            }
            # Track form variables sent as files
            set filevars($f(fieldname)) 1

            if {![info exists f(mime_type)]} {
                set f(mime_type) [ns_guesstype $f(filename)]
                if {$f(mime_type) in {"*/*" ""}} {
                    set f(mime_type) "application/octet-stream"
                }
            }

            if {$base64_p} {
                set transfer_encoding base64
            } else {
                set transfer_encoding binary
            }

            set content [list --$boundary  \r\n  "Content-Disposition: form-data; "  "name=\"$f(fieldname)\"; filename=\"$f(filename)\""  \r\n  "Content-Type: $f(mime_type)"  \r\n  "Content-transfer-encoding: $transfer_encoding"  \r\n  \r\n]
            set app [append_to_payload  -content [join $content ""]  $enc  $max_body_size  $payload  $payload_file  $payload_file_fd]
            lassign $app payload payload_file payload_file_fd

            if {[info exists f(data)]} {
                set app [append_to_payload  -content $f(data)  $enc  $max_body_size  $payload  $payload_file  $payload_file_fd]
            } else {
                set app [append_to_payload  -file $f(file)  $enc  $max_body_size  $payload  $payload_file  $payload_file_fd]
            }
            lassign $app payload payload_file payload_file_fd

            set app [append_to_payload  -content \r\n  $enc  $max_body_size  $payload  $payload_file  $payload_file_fd]
            lassign $app payload payload_file payload_file_fd

        }

        # Translate urlencoded vars into multipart variables
        foreach formvar [split $formvars &] {
            set formvar [split $formvar  =]
            set key [lindex $formvar 0]
            set val [join [lrange $formvar 1 end] =]

            if {[info exists filevars($key)]} {
                return -code error "${this_proc}:  Variable '$key' already specified as file variable"
            }

            set content [list --$boundary  \r\n  "Content-Disposition: form-data; name=\"$key\""  \r\n  \r\n  $val  \r\n]
            set app [append_to_payload  -content [join $content ""]  $enc  $max_body_size  $payload  $payload_file  $payload_file_fd]
            lassign $app payload payload_file payload_file_fd

        }

        set content "--$boundary--\r\n"
        set app [append_to_payload  -content $content  $enc  $max_body_size  $payload  $payload_file  $payload_file_fd]
        lassign $app payload payload_file payload_file_fd
        
    } else {
        # If people specified a content type we won't overwrite it,
        # otherwise this will be a 'application/x-www-form-urlencoded'
        # payload
        if {$req_content_type eq ""} {
            set req_content_type "application/x-www-form-urlencoded"
            ns_set put $headers "Content-type" $req_content_type
        }
        set enc [util::http::get_channel_settings $req_content_type]
        set payload $formvars
    }

    # Body will be appended as is to the payload
    set app [append_to_payload  -content $body  $enc  $max_body_size  $payload  $payload_file  $payload_file_fd]
    lassign $app payload payload_file payload_file_fd

    if {$payload_file_fd ne ""} {close $payload_file_fd}
    
    return [util::http::request  -method          POST  -body            $payload  -body_file       $payload_file  -delete_body_file  -headers         $headers  -url             $url  -timeout         $timeout  -max_depth       $max_depth  -preference      $preference  -force_ssl=$force_ssl_p  -gzip_request=$gzip_request_p  -gzip_response=$gzip_response_p  -post_redirect=$post_redirect_p  -spool=$spool_p]
XQL Not present:
Generic, PostgreSQL, Oracle
[ hide source ] | [ make this the default ]
Show another procedure: