Version 1 of File HTTPS Upload

Updated 2013-12-10 23:10:56 by AJG

This is an example of uploading a file from a client to a server using https (SSL) through a squid proxy running on another host. The website is using a self-signed SSL certificate.

Server code in file upload.cgi:

#! /bin/sh
# \
exec tclsh $0 $@

set DIR /server_path_to/upload_directory

package require ncgi ;# in tcllib

# for message back to browser
puts "Content-type: text/html
<HTML>
 <HEAD>
  <TITLE>POST File</TITLE>
 </HEAD>
 <BODY>"

::ncgi::parse
set filedata [::ncgi::value file2upload]
set filedesc [::ncgi::value name]

if {[catch {open "$DIR/$filedesc" "w"} fh]} {
   puts "Failed - $fh"
} else {
   puts -nonewline $fh "$filedata"
   puts "<BR>File uploaded to $filedesc"
   close $fh
}

puts " </BODY>
</HTML>"

Here is an html file, form.htm, for testing upload.cgi from a web browser running on your client:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<HTML>
 <HEAD>
    <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
    <TITLE>Form Test</TITLE>
 </HEAD>

 <BODY>
  <FORM METHOD="post" ACTION="https://www.your-site.org/to_cgi_bin/upload.cgi"
        ENCTYPE="multipart/form-data">
   <INPUT TYPE="text" NAME="name" VALUE="bytesSSL.dat" SIZE="25">
   <INPUT TYPE="file" NAME="file2upload">
   <INPUT TYPE="submit">
  </FORM>
 </BODY>
</HTML>

Once this is working,

Client code, upload.tcl:

#!/bin/sh
# \
exec /usr/bin/tclsh $0 $@

package require http
package require tls
package require base64

set host     www.your-site.org
set cgi      /to_cgi_bin/upload.cgi ;# relative location of script on $host
set cert     /client_path_to/self-signed-certificate.pem
set bound    "--HARD_TO_STUMBLE_ON_0123456789" ;# multipart separator
set file     "junk.dat" ;# client file to be uploaded
set new_name bytesSSL.dat ;# name to give the file on server
set ssl      443 ;# https port
set ssluser  your_ssl_user_name_on_server
set passwd   your_ssl_password_on_server
set up       [::base64::encode "$ssluser:$passwd"] ;# encoded SSL user:password
set phost    proxyhost ;# name or FQDN or numeric
set pport    3128 ;# proxy port

set ifid [open $file "r"]
fconfigure $ifid -translation binary
set data [read $ifid]
close $ifid

append content "--$bound\r\n"
append content "Content-Disposition: form-data; name=\"name\"\r\n"
append content "\r\n"
append content "$new_name\r\n"
append content "--$bound\r\n"
append content "Content-Disposition: form-data; name=\"file2upload\"; "
append content "filename=\"$file\"\r\n" 
append content "Content-Type: application/octet-stream\r\n"
append content "\r\n"
append content "$data\r\n"
append content "--${bound}--\r\n"

set psocket [socket $phost $pport] ;# create socket to proxy
puts $psocket "CONNECT $host:$ssl HTTP/1.1\r\n" ;# tunnel to https://$host
flush $psocket
while {[gets $psocket line] > 0} { ;# look for "200 Connection established"
    ;#puts $line
}
http::register https $ssl \
 [list ::tls::import $psocket -require 1 -cafile $cert]

# if proxy is not used,
# http::register https $ssl [list ::tls::socket -require 1 -cafile $cert]

set token [::http::geturl https://${host}$cgi \
 -type "multipart/form-data; boundary=$bound" -query $content \
 -headers [list Authorization "Basic $up"]]
# Depending on your data and platform, you might also need -binary 1 in the geturl command

# if no SSL needed:
# set token [::http::geturl http://${host}$cgi \
# -type "multipart/form-data; boundary=$bound" -query $content

puts "[::http::data $token]"

Test on Linux:

upload.tcl | lynx -stdin