HTTP Download, with: Redirections, Authorization, Proxys, ..

Photoshop Script Snippets - Note: Full Scripts go in the Photoshop Scripts Forum

Moderators: Tom, Kukurykus

Biriba

HTTP Download, with: Redirections, Authorization, Proxys, ..

Post by Biriba »

Hi,

I have written a JS-Class that allows sending http-requests and retrieve / parse the responses.
It uses the socket()-class.

It supports:
- get the Status-Code of a webresource
- fetch the headers of a webresource
- return the data of a webresource as string
- save the data into a local file and return a File()-Object of it
- Basic Authentication of webresources
- Use of Proxy-Server
- Authentication of Proxy-Server
- following Redirections in a controlled manner
- ...

It doesn't support:
- HTTPS
- POST-Requests (only GET / HEAD)


the use is very simple and easy:
Code: Select all// creating the Object
var http = new HN_http_request();

// set the url
http.set_url('http://example.com/path/to/resource.typ');

// set the request_type
http.set_request_type('only_status');

// get the result
var result = http.get_result();
alert( result==false ? http.get_error() : result );

// -------------------------------------------------------

// setup:

   // the different request_types are:
     http.set_request_type('only_status');
     http.set_request_type('only_headers');
     http.set_request_type('data');
     http.set_request_type('data', local_filename);

   // controlling Redirections
     http.set_max_redirections(4);

   // setting the URI is also used to pass data for Non-Standard-Ports, Authorization, Query,
   // Proxy, Proxy-Port, Proxy-Authorization, all at once:
     http.set_url('http://example.com/path/to/resource.typ');
     http.set_url('http://example.com:8080/path/to/resource.typ');
     http.set_url('http://example.com/path/to/resource.typ ... &var2=val2');
     http.set_url('http://user:password@example.com/path/to/resource.typ');
     http.set_url('http://example.com/path/to/resource.typ', 'http://proxyhost.test');
     http.set_url('http://example.com/path/to/resource.typ', 'http://proxyhost.test:3128');
     http.set_url('http://example.com/path/to/resource.typ', 'http://proxyuser:proxypassword@proxyhost.test:3128');

// get result:

   // save webresource to local file
     http.set_request_type('data', local_filename);
     var myFile = http.get_result();

   // check value of specific Header:
     http.get_response_header('status-code');
     http.get_response_header('reason-phrase');
     http.get_response_header('content-type');
     http.get_response_header('content-length');
     http.get_response_header('last-modified');





here is the script content with demos / examples:
(you also can download it as plaintext-file here: http://nogajski.de/download/?f=hn_http_request.jsx)

Code: Select all/*
    Photoshop-Script: hn_http_request.jsx

    Allows to fetch HTTP-Resources or their Informations in many ways,
    comfortable and easy. See and run the demos at the end of the file.

    @Author:    Horst Nogajski  <info [AT] nogajski [DOT] de>
    @Version:   1.0
    @Date:      2010/07/04
    @Licence:   MIT

    $Source: /JSX/libs/hn_http_request.jsx,v $
    $Id: hn_http_request.jsx,v 1.2 2010/07/04 22:31:06 horst Exp $

CREDITS:

    // parseUri 1.2.2
    // (c) Steven Levithan <stevenlevithan.com>
    // MIT License

*/


/*  HN_http_request  */
try
{
    /* constructor */
    function HN_http_request()
    {
        try {
            // class-vars for setters
                this.sRequestType = 'data';  // 'only_status' | 'only_headers' | 'data'
                this.sLocalFilename = null;
                this.iMaxRedirections = 4;
            // class-vars for getters
                this.sStatuscode = null;
                this.sReasonPhrase = null;
                this.sRequest = null;
                this.sResponse = null;
                this.sError = '';
            // private other class-vars
                this.sKeptProxyUrl = null;
                this.bUseProxy = false;
                this.sProxyHost = null;
                this.iProxyPort = null;
                this.sProxyAuthHeader = null;
                this.sUriHost = null;
                this.sUriPath = null;
                this.iUriPort = 80;
                this.sAuthorizationHeader = null;
                this.sRefererHeader = null;
                this.sCookieHeader = null;
                this.iRedirections = 0;
                this.oResponse = new Object();
                this.oSocket = new Socket();
        }
        catch(e) { alert("ERROR:\n" + e); }
    }


/* SETTERS */

    /*  set_url(sUrl)  */
    HN_http_request.prototype.set_url = function(url, proxy_url)
    {
        try
        {
            if(!url.match(/:\/\//))
            {
                url = 'http://' + url;
            }
            var URI = parseUri(url);
            if(URI.protocol.toLowerCase() != "http")
            {
                this.sError = "URL-scheme isn't supported:\n" + URI.protocol;
                return false;
            }
            this.sUriHost = URI.host;
            this.iUriPort = (URI.port == '') ? 80 : URI.port;
            this.sUriPath = (URI.path == '') ? '/' : URI.path;
            this.sUriPath += (URI.query == '') ? '' : '?' + URI.query;
            this.sUriPath += (URI.anchor == '') ? '' : '#' + URI.anchor;
            if(URI.user != '' && URI.password != '' && URI.source.match(/.*?:.*?@.*/))
            {
                this.sAuthorizationHeader = 'Authorization: Basic ' + this._base64_encode(URI.user + ':' + URI.password) + "\r\n";
            }
            //alert(URI.host + "\n" + URI.port + "\n" + URI.path + "\n" + URI.query + "\n" + URI.anchor + "\n" + URI.user + "\n" + URI.password);
            //alert(this.sUriHost + "\n" + this.iUriPort + "\n" + this.sUriPath + "\n" + this.sAuthorizationHeader);
            this.sKeptProxyUrl = proxy_url;
            this.bUseProxy = (typeof proxy_url=='undefined' || proxy_url=='' || proxy_url==null) ? false : true;
            if(this.bUseProxy)
            {
                // setup the proxy params
                if(!proxy_url.match(/:\/\//))
                {
                    proxy_url = 'http://' + proxy_url;
                }
                var PROXY = parseUri(proxy_url);
                this.sProxyHost = PROXY.host;
                this.iProxyPort = (PROXY.port == '') ? 80 : PROXY.port;
                if(PROXY.user != '' && PROXY.password != '' && PROXY.source.match(/.*?:.*?@.*/))
                {
                    this.sProxyAuthHeader = 'Proxy-Authorization: Basic ' + this._base64_encode(PROXY.user + ':' + PROXY.password) + "\r\n";
                }
            }
            return true;
        }
        catch(e) { alert("ERROR:\n" + e); }
    }


    /*  set_request_type(sRequestType)  */
    //
    //   valid params:  [ 'only_status' | 'only_headers' | 'data' ] , optional (when using 'data') a local_filename
    //   default:  'data'
    HN_http_request.prototype.set_request_type = function(value, local_filename)
    {
        try
        {
            this.sError = '';
            this.sLocalFilename = null;
            this.sStatuscode = null;
            this.sRequest = null;
            this.sResponse = null;
            this.oResponse = new Object();
            this.oSocket = new Socket();
            if(value.toLowerCase() == 'only_status')
            {
                this.sRequestType = 'only_status';
            }
            else if(value.toLowerCase() == 'only_headers')
            {
                this.sRequestType = 'only_headers';
            }
            else
            {
                if(typeof local_filename == 'undefined' || local_filename == null || local_filename == '')
                {
                    this.sRequestType = 'DATA_STRING';
                }
                else
                {
                    this.sRequestType = 'DATA_FILE';
                    this.sLocalFilename = local_filename;
                }
            }
        }
        catch(e) { alert("ERROR:\n" + e); }
    }


    /*  set_max_redirections(iMaxRedirections)  */
    //
    HN_http_request.prototype.set_max_redirections = function(value)    { this.iMaxRedirections = value; }


/* GETTERS */

    /*  get_statuscode()  */
    //
    HN_http_request.prototype.get_statuscode = function() { return this.sStatuscode; }

    /*  get_request()  */
    //
    HN_http_request.prototype.get_request = function() { return this.sRequest; }

    /*  get_response_allheaders()  */
    //
    HN_http_request.prototype.get_response_allheaders = function() { return this.sResponse; }

    /*  get_response_header(headername)  */
    //
    HN_http_request.prototype.get_response_header = function(headername)
    {
        try {
            for(var key in this.oResponse)
            {
                if(key == headername.toLowerCase())
                {
                    return this.oResponse[key];
                }
            }
            return null;
        }
        catch(e) { alert("ERROR:\n" + e); }
    }

    /*  get_error()  */
    //
    HN_http_request.prototype.get_error = function()
    {
        try {
            var sInfo = "---[HN_http_request]-----------------------------------------------\n";
            if(this.bUseProxy==true)
            {
                sInfo += " - proxy_host: " + this.sProxyHost + "\n";
                sInfo += " - proxy_port: " + this.iProxyPort + "\n";
            }
            sInfo += " - uri_host: " + this.sUriHost + "\n";
            sInfo += " - uri_port: " + this.iUriPort + "\n";
            sInfo += " - uri_path: " + this.sUriPath + "\n";
            sInfo += " - request_type: " + this.sRequestType + "\n";
            sInfo += " - status-code: " + this.sStatuscode + "\n";
            sInfo += " - reason-phrase: " + this.sReasonPhrase + "\n\n";
            sInfo += "---[ERROR:]--------------------------------------------------------\n";
            return sInfo + this.sError;
        }
        catch(e) { alert("ERROR:\n" + e); }
    }

    /*  get_result(bIsRedirected)  */
    //
    HN_http_request.prototype.get_result = function(bIsRedirected)
    {
        try {
            var buffer = '';
            var dump = '';
            var dataoffset = null;
            var a = null;
            var key = null;

            // optional reset some vars
            if(typeof bIsRedirected=='undefined' || bIsRedirected!=true)
            {
                this.iRedirections = 0;
            }

            // build HTTP-Request:
            var myConnection = this.sUriHost + ':' + this.iUriPort;
            var myRequest = this.sRequestType=='only_status' ? "HEAD " : "GET ";
            myRequest += this.sUriPath + " HTTP/1.1\r\n";
            if(this.bUseProxy==true)
            {
                myConnection = this.sProxyHost + ':' + this.iProxyPort;
                myRequest = this.sRequestType=='only_status' ? "HEAD " : "GET ";
                myRequest += 'http://' + this.sUriHost + ':' + this.iUriPort + this.sUriPath + " HTTP/1.1\r\n";
                myRequest += (this.sProxyAuthHeader==null) ? '' : this.sProxyAuthHeader;
                myRequest += "Proxy-Connection: close\r\n";
            }
            myRequest += "Host: " + this.sUriHost + "\r\n";
            myRequest += (this.sAuthorizationHeader==null) ? '' : this.sAuthorizationHeader;
            myRequest += (this.sRefererHeader==null) ? '' : "Referer: " + this.sRefererHeader + "\r\n";
            myRequest += (this.sCookieHeader==null) ? '' : "Cookie: " + this.sCookieHeader + "\r\n";
            myRequest += "User-Agent: ESTK/" + $.version + " (" + $.os + ")\r\n";
            myRequest += "Pragma: no-cache\r\n";
            myRequest += "Cache-Control: no-cache\r\n";
            myRequest += "Cache-Control: max-age=0\r\n";
            myRequest += "Cache-Control: max-stale=0\r\n";
            myRequest += "Connection: close\r\n\r\n";
            this.sRequest = myConnection + "\n\n" + myRequest;

            // connect to host or proxy
            if(!this.oSocket.open(myConnection, "binary"))
            {
                this.sError = "Cannot open Socket:\n" + myConnection;
                return false;
            }

            // send request and read a first chunk on the wire
            this.oSocket.write(myRequest);
            this.sRefererHeader = 'http://' + this.sUriHost + ':' + this.iUriPort + this.sUriPath;
            buffer = this.oSocket.read(1536);

            // parse all headers
            var lines = buffer.split("\r\n");
            dump = '';
            for(var line in lines)
            {
                // get the status-code from HTTP-Headerline
                if(lines[line].substr(0,7)=='HTTP/1.')
                {
                    this.sStatuscode = lines[line].substr(9,3);
                    this.sReasonPhrase = lines[line].substr(13);
                    this.oResponse['status-code'] = this.sStatuscode;
                    this.oResponse['reason-phrase'] = this.sReasonPhrase;
                    // CHECK-OUT for ONLY_STATUS
                    if(this.sRequestType=='only_status')
                    {
                        // early checkout
                        this.oSocket.close();
                        return this.sStatuscode;
                    }
                    dump += lines[line] + "\r\n";
                    continue;
                }
                if(lines[line] == "")
                {
                    // empty header-line = headers end & data starts
                    this.sResponse = dump;
                    this.dataoffset = dump.length + 2;
                    break;
                }
                // alle weiteren headerzeilen werden geparst und in key / value aufgeteilt
                dump += lines[line] + "\r\n";
                a = lines[line].split(': ');
                key = a[0].toLowerCase();
                a.shift();
                this.oResponse[key] = a.join(': ');
                if(key=='set-cookie')
                {
                    this.sCookieHeader = a.join(': ');
                }
            }

            // checking Status-Code for Redirections
            if(this.sStatuscode[0]=='3' && this.iRedirections < this.iMaxRedirections)
            {
                this.oSocket.close();
                this.iRedirections++;
                var current_host = this.sUriHost;
                var new_location = this.get_response_header('location');
                if(new_location==false || new_location==null)
                {
                    this.sError = "StatusCode is [" + this.sStatuscode + "], but we haven't found a Location-Header!\n\nnew_location = " + new_location + "\nRedirections = " + this.sRedirections;
                    return false;
                }
                if(new_location.substr(0,7)!='http://')
                {
                    new_location = 'http://' + current_host + new_location;
                }
                this.set_url(new_location, this.sKeptProxyUrl);
                return this.get_result(true);
            }

            // final Check of Status-Code
            if(this.sStatuscode!='200')
            {
                this.oSocket.close();
                if(this.sStatuscode[0]=='3')
                {
                    this.sError = "To much Redirections!\nRedirections = " + this.iRedirections;
                }
                else
                {
                    this.sError = "Not the right StatusCode: [" + this.sStatuscode + "]\n\nRedirections = " + this.iRedirections;
                }
                return false;
            }

            // CHECK-OUT for HEADERS_ONLY
            if(this.sRequestType=='only_headers')
            {
                this.oSocket.close();
                return this.sRequest + "\n" + this.sResponse;
            }


            // CHECK-OUT for DATA_STRING
            if(this.sRequestType=='DATA_STRING')
            {
                // den Rest des buffers in filecontent zuweisen
                var filecontent = buffer.substr(this.dataoffset);
                while(!this.oSocket.eof)
                {
                        filecontent += this.oSocket.read(4096);
                }
                this.oSocket.close();
                return filecontent;
            }


            // CHECK-OUT for DATA_FILE
            if(this.sRequestType=='DATA_FILE' && this.sLocalFilename!=null)
            {
                var f = new File(this.sLocalFilename);
                f.encoding  = "binary";
                if(!f.open('w'))
                {
                    this.oSocket.close();
                    this.sError = "Cannot open file for writing:\n" + this.sLocalFilename;
                    return false;
                }
                f.write(buffer.substr(this.dataoffset));
                while(!this.oSocket.eof)
                {
                    f.write(this.oSocket.read(4096));
                }
                f.close();
                this.oSocket.close();
                return f;
            }

            // should never be reached, this line, ...
            this.sError = "Seems to be, that there wasn't set a right RequestType!";
            return false;
        }
        catch(e) { alert("ERROR:\n" + e); }
    }


/* HELPERS */

    /*  _base64_encode(sInput)  */
    //
    HN_http_request.prototype._base64_encode = function(sInput)
    {
        try
        {
            var keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
            var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
            var i = 0;
            var output = '';
            while(i < sInput.length)
            {
                chr1 = sInput.charCodeAt(i++);
                chr2 = sInput.charCodeAt(i++);
                chr3 = sInput.charCodeAt(i++);

                enc1 = chr1 >> 2;
                enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
                enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
                enc4 = chr3 & 63;

                if(isNaN(chr2))
                {
                    enc3 = enc4 = 64;
                }
                else if(isNaN(chr3))
                {
                    enc4 = 64;
                }
                output = output + keyStr.charAt(enc1) + keyStr.charAt(enc2) + keyStr.charAt(enc3) + keyStr.charAt(enc4);
            }
            return output;
        }
        catch(e) { alert("ERROR:\n" + e); }
    }


    // parseUri 1.2.2
    // (c) Steven Levithan <stevenlevithan.com>
    // MIT License
    function parseUri (str)
    {
        try
        {
            var    o   = parseUri.options,
                m   = o.parser[o.strictMode ? "strict" : "loose"].exec(str),
                uri = {},
                i   = 14;

            while (i--) uri[o.key] = m || "";

            uri[o.q.name] = {};
            uri[o.key[12]].replace(o.q.parser, function ($0, $1, $2) {
                if ($1) uri[o.q.name][$1] = $2;
            });

            return uri;
        }
        catch(e) { alert("ERROR:\n" + e); }
    }
    parseUri.options = {
        strictMode: false,
        key: ["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"],
        q:   {
            name:   "queryKey",
            parser: /(?:^|&)([^&=]*)=?([^&]*)/g
        },
        parser: {
            strict: /^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/,
            loose:  /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/
        }
    };








// EXAMPLES:
    if(confirm('Do you want to run Demos of the http_request-class?'))
    {
        var result = null;

        // creating the Object
        var http = new HN_http_request();

        // A)
        // a short and simple HEAD-Request, to get the status code of a web resource
        // returns: string = StatusCode, e.g. 200 | 404 | 304 | 501 | ...
        //                or false on failure
        alert("The first Demo try to fetch the Status-Code of a webresource:\n\n    if(!http.set_url('http://nogajski.de/autosoftproof_doc_en/'))\n    {\n        alert(http.get_error());\n    }\n    else\n    {\n        http.set_request_type('only_status');\n        result = http.get_result();\n        alert( result==false ? http.get_error() : result );\n    }\n");
        // set the url
        if(!http.set_url('http://nogajski.de/autosoftproof_doc_en/'))
        {
            alert(http.get_error());
        }
        else
        {
            http.set_request_type('only_status');
            result = http.get_result();
            alert( result==false ? http.get_error() : result );
        }




        // B)
        // fetch all headers as dump and as Object
        if(confirm("The second Demo fetches all Response-Headers:\n\n    if(!http.set_url('http://nogajski.de/autosoftproof_doc_en/'))\n    {\n        alert(http.get_error());\n    }\n    else\n    {\n        http.set_request_type('only_headers');\n        result = http.get_result();\n        alert( result==false ? \"Fetching headers fails: \" + http.get_error() : result );\n\n        result = http.get_response_header('status-code');\n        alert( result==false ? \"Fetching a single header fails: \" + http.get_error() : \"status-code = \" + result );\n\n        result = http.get_response_header('reason-phrase');\n        alert( result==false ? \"Fetching a single header fails: \" + http.get_error() : \"reason-phrase = \" + result );\n\n        result = http.get_response_header('content-length');\n        alert( result==false ? \"Fetching a single header fails: \" + http.get_error() : \"content-length = \" + result );\n    }\n\n\nDo you want to run it now?"))
        {
            // set the url
            if(!http.set_url('http://nogajski.de/autosoftproof_doc_en/'))
            {
                alert(http.get_error());
            }
            else
            {
                http.set_request_type('only_headers');
                result = http.get_result();
                alert( result==false ? "Fetching headers fails:\n\n" + http.get_error() : result );

                // C)
                // looking for specific Headers
                // when the request_type isn't "only_status", you don't need to execute http.get_result() again,
                // because all headers are fetched and kept in the class. You simply check the header:
                result = http.get_response_header('status-code');
                alert( result==false ? "Fetching a single header fails:\n\n" + http.get_error() : "status-code = " + result );

                result = http.get_response_header('reason-phrase');
                alert( result==false ? "Fetching a single header fails:\n\n" + http.get_error() : "reason-phrase = " + result );

                result = http.get_response_header('content-length');
                alert( result==false ? "Fetching a single header fails:\n\n" + http.get_error() : "content-length = " + result );

                result = http.get_response_header('last-modified');
                alert( result==false ? "Fetching a single header fails:\n\n" + http.get_error() : "last-modified = " + result );

                result = http.get_response_header('content-type');
                alert( result==false ? "Fetching a single header fails:\n\n" + http.get_error() : "content-type = " + result );

                result = http.get_response_header('thisheader-shouldnot-exist');
                alert( result==false ? "Fetching a single header fails:\n\n" + http.get_error() : "thisheader-shouldnot-exist = " + result );
            }
        }




        // D)
        // follow Redirections in a controlled manner
        if(confirm("Next part demonstrates the ability to follow Redirections in a controlled manner:\n\nDo you want to run it now?"))
        {
            // set the url
            if(!http.set_url('http://nogajski.de/redir'))
            {
                alert(http.get_error());
            }
            else
            {
                http.set_request_type('only_headers');
                result = http.get_result();
                alert( result==false ? "Fetching headers fails:\n\n" + http.get_error() : result );

                alert("Now, we increase the number of allowed Redirections from the default of 4 to 8, and try again!\n\n    http.set_max_redirections(8);\n");
                http.set_max_redirections(8);
                result = http.get_result();
                alert( result==false ? "Fetching headers fails:\n\n" + http.get_error() : result );
            }
        }




        // E)
        // when passing 'data' as type and a local-filename as second param, it downloads the web resource into a local file
        // returns  a File()-Object
        //               or false on failure
        // F)
        // when passing 'data' as type _without_ the optional second param local-filename, the downloaded content is returned as string:
        // http.set_request_type('data');
        var local_filename = $.getenv('TEMP');
        local_filename = Folder(local_filename).exists ? local_filename : $.getenv('TMP');
        local_filename = Folder(local_filename).exists ? local_filename + '/downloaded.pdf' : null;
        var myQuestion = local_filename==null ? "Last Demo download a Resource and returns it as a string!\nBut you also can save it directly into a local file.\n\n" : "Last Demo try to download a Resource and save it into:\n - [" + local_filename + "] - \n\n";
        if(confirm(myQuestion + "    if(local_filename==null)\n    {\n        http.set_url('http://nogajski.de/autosoftproof_doc_en/')\n        http.set_request_type('data');\n        result = http.get_result();\n        alert( result==false ? \"Fetching data fails: \" + http.get_error() : result );\n    }\n    else\n    {\n        http.set_url('http://nogajski.de/autosoftproof_doc_en/')\n        http.set_request_type('data', local_filename);\n        var myFile = http.get_result();\n        if(myFile instanceof Object)\n        {\n            if(myFile.exists)\n            {\n                myFile.execute();\n            }\n        }\n    }\n\nDo you want to run it now?"))
        {
            if(local_filename==null)
            {
                http.set_url('http://nogajski.de/autosoftproof_doc_en/')
                http.set_request_type('data');
                result = http.get_result();
                alert( result==false ? "Fetching data fails:\n\n" + http.get_error() : result );
            }
            else
            {
                http.set_url('http://nogajski.de/autosoftproof_doc_en/')
                http.set_request_type('data', local_filename);
                var myFile = http.get_result();
                if(myFile instanceof Object)
                {
                    if(myFile.exists)
                    {
                        myFile.execute();
                    }
                }
            }
        }


    }
}
catch(e) { alert("ERROR:\n" + e); }




Best regards,
Biriba