util::http::post_payload (public)

 util::http::post_payload [ -url url ] [ -files files ] [ -base64 ] \
    [ -formvars formvars ] [ -formvars_list formvars_list ] \
    [ -body body ] [ -max_body_size max_body_size ] \
    [ -headers headers ] [ -multipart ]

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

Build the payload for a POST request

Switches:
-url (optional)
does not affect the payload directly, but is used to check that variables specified via the URL do not conflict with those coming from other parameters. In such case, an error is returned.
-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 (optional, boolean)
-formvars (optional)
These are additional form variables already in URLencoded format, for instance, by using 'export_vars -url'. They will be translated for the proper type of form (URLencoded or multipart) depending on the presence of 'files' or the 'multipart' flag. Variables specified this way will be appended to those supplied via the 'formvars_list' parameter.
-formvars_list (optional)
These are additional form variables in list format. They will be translated for the proper type of form (URLencoded or multipart) depending on the presence of files or the multipart flag. The payload will be made by the sum of data coming from 'formvars', 'formvars_list' and 'files' arguments. Default behavior is to build payload as an 'application/x-www-form-urlencoded' payload if no files are specified, and 'multipart/form-data' otherwise. If '-multipart' flag is set, format will be forced to multipart.
-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 (optional, defaults to "25000000")
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)
Processing the payload might set some request headers. Provide yours to either override the default behavior, or to merge your headers with those from the payload. The resulting headers will be returned in the dict.
-multipart (optional, boolean)
Returns:
a dict with fields 'payload', 'payload_file' and 'headers'

Partial Call Graph (max 5 caller/called nodes):
%3 test_postman_echo postman_echo (test acs-tcl) util::http::post_payload util::http::post_payload test_postman_echo->util::http::post_payload test_template_widget_file template_widget_file (test acs-templating) test_template_widget_file->util::http::post_payload test_util_http_json_encoding util_http_json_encoding (test acs-tcl) test_util_http_json_encoding->util::http::post_payload test_util_http_post_vars util_http_post_vars (test acs-tcl) test_util_http_post_vars->util::http::post_payload ad_file ad_file (public) util::http::post_payload->ad_file ad_urlencode_query ad_urlencode_query (public) util::http::post_payload->ad_urlencode_query util::http::append_to_payload util::http::append_to_payload (private) util::http::post_payload->util::http::append_to_payload util::http::get_channel_settings util::http::get_channel_settings (private) util::http::post_payload->util::http::get_channel_settings acs::test::form_reply acs::test::form_reply (public) acs::test::form_reply->util::http::post_payload file_storage::test::add_file_to_folder file_storage::test::add_file_to_folder (private) file_storage::test::add_file_to_folder->util::http::post_payload util::http::post util::http::post (public) util::http::post->util::http::post_payload

Testcases:
util_http_json_encoding, postman_echo, util_http_post_vars, template_widget_file
Source code:
    set this_proc [lindex [info level 0] 0]

    # Retrieve variables sent by the URL...
    set parsed [ns_parseurl $url]
    if {[dict exists $parsed query]} {
        array set urlvars [ns_set array [ns_parsequery [dict get $parsed query]]]
    }

    if {[llength $formvars_list] % 2 == 1} {
        error "'formvars_list' must have an even number of elements"
    }

    if {$formvars ne ""} {
        foreach {key val} [ns_set array [ns_parsequery $formvars]] {
            lappend formvars_list $key $val
        }
    }

    # Check whether we don't have multiple variable definition in url
    # and payload.
    foreach {key value} $formvars_list {
        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 ||
        [llength $files] != 0 ||
        [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 f $files {
            if {![dict exists $f data]} {
                if {![dict exists $f file]} {
                    return -code error "${this_proc}:  No file specified"
                }
                set file [dict get $f file]
                if {![ad_file exists $file]} {
                    return -code error "${this_proc}:  Error reading file: $file not found"
                }
                if {![ad_file readable $file]} {
                    return -code error "${this_proc}:  Error reading file: $file permission denied"
                }

                dict set f filename [expr {[dict exists $f filename] ?
                                            [dict get $f filename] :
                                            [ad_file tail $file]}]
            }

            # Filename and fieldname must be in the file dict at this
            # point
            foreach key {filename fieldname} {
                if {![dict exists $f $key]} {
                    return -code error "${this_proc}:  '$key' missing for file POST"
                }
                set $key [dict get $f $key]
            }

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

            if {![dict exists $f mime_type]} {
                set mime_type [ns_guesstype $filename]
                if {$mime_type in {"*/*" ""}} {
                    set mime_type "application/octet-stream"
                }
            } else {
                set mime_type [dict get $f mime_type]
            }

            set transfer_encoding [expr {$base64_p ? "base64" : "binary"}]

            set content [list --$boundary  \r\n  "Content-Disposition: form-data; "  "name=\"$fieldname\"; filename=\"$filename\""  \r\n  "Content-Type: $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 {[dict exists $f data]} {
                set app [append_to_payload  -content [dict get $f data]  $enc  $max_body_size  $payload  $payload_file  $payload_file_fd]
            } else {
                set app [append_to_payload  -file $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 {key val} $formvars_list {
            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 {}
        foreach {key val} $formvars_list {
            lappend payload [ad_urlencode_query $key]=[ad_urlencode_query $val]
        }
        set payload [join $payload &]
    }

    # 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 [list  payload $payload  payload_file $payload_file  headers $headers]
XQL Not present:
Generic, PostgreSQL, Oracle
[ hide source ] | [ make this the default ]
Show another procedure: