Skip to content

Calling HTTP ​

Package for HTTP call processing

example
sql
DECLARE
    v_req utl_http.req;
    v_text CLOB;
BEGIN
    BEGIN
        pck_api_http.request(v_req, 'GET', 'https://api.chucknorris.io/jokes/random');
        pck_api_http.response_text(v_req, v_text);
        DBMS_OUTPUT.PUT_LINE(JSON_VALUE(v_text, '$.value'));
    EXCEPTION
        WHEN OTHERS THEN
            DECLARE
                v_error_stack VARCHAR2(4000) := DBMS_UTILITY.FORMAT_ERROR_STACK();
            BEGIN
                IF v_error_stack LIKE '%ORA-24247%' THEN
                    DBMS_OUTPUT.PUT_LINE('ERROR (ORA-24247): Network access denied by Access Control List (ACL).');
                    DBMS_OUTPUT.PUT_LINE('ACL must be enabled first using pck_api_admin.acl_append_host.');
                    DBMS_OUTPUT.PUT_LINE('exec admin.pck_api_admin.acl_append_host(''ODBVUE'', ''api.chucknorris.io'', 443, 443, ''http'');');
                ELSE
                    RAISE;
                END IF;
            END;
    END;
END;
/
specification
sql
CREATE OR REPLACE PACKAGE odbvue.pck_api_http AS -- Package for HTTP call processing

    FUNCTION mime_type ( -- Function returns mime type from file extention, e.g. mp3->audio/mpeg
        p_ext VARCHAR2 --  File extention
    ) RETURN VARCHAR2; -- Mime type

    PROCEDURE request ( -- Function initiates HTTP request
        p_req     IN OUT utl_http.req, -- HTTP request
        p_method  VARCHAR2, -- Method (GET, POST, PUT, DELETE, ..)
        p_url     VARCHAR2, -- Url
        p_version VARCHAR2 DEFAULT 'HTTP/1.1' --  Version
    );

    PROCEDURE request_auth_basic ( -- Procedure authenticates user with username and password
        p_req      IN OUT utl_http.req, -- HTTP request
        p_username VARCHAR2, -- User name
        p_password VARCHAR2 -- Password
    );

    PROCEDURE request_auth_token ( -- Procedure adds Bearer token to the request
        p_req   IN OUT utl_http.req, -- HTTP request
        p_token VARCHAR2 -- Token
    );

    PROCEDURE request_auth_wallet ( -- Procedure adds Oracle Wallet to HTTP connection (must be called before starting request)
        p_wallet_path     VARCHAR2, -- Path to Oracle Wallet (without "file" prefix)
        p_wallet_password VARCHAR2 -- Wallet password
    );

    PROCEDURE request_content_type ( -- Procedure adds content type header to the HTTP request
        p_req          IN OUT utl_http.req, -- HTTP request
        p_content_type VARCHAR2 -- Content type
    );

    PROCEDURE request_charset ( -- Procedure adds charset header to the HTTP request
        p_req          IN OUT utl_http.req, -- HTTP request
        p_body_charset VARCHAR2 -- Charset
    );

    PROCEDURE request_header ( -- Procedure adds custom header to the HTTP request
        p_req   IN OUT utl_http.req, -- HTTP request
        p_name  VARCHAR2, -- Header name
        p_value VARCHAR2 -- Header value
    );

    PROCEDURE request_json ( -- Procedure adds JSON payload to the HTTP request
        p_req  IN OUT utl_http.req, -- HTTP request
        p_json CLOB -- JSON data
    );

    PROCEDURE request_multipart_start ( -- Procedure starts multipart form data request
        p_req     IN OUT utl_http.req, -- HTTP request
        p_charset VARCHAR2 DEFAULT 'UTF-8' -- Charset
    );

    PROCEDURE request_multipart_varchar2 ( -- Procedure adds Varchar2 data to multipart form data 
        p_req     IN OUT utl_http.req, -- HTTP request
        p_name    VARCHAR2, -- Name
        p_value   VARCHAR2, -- Value
        p_charset VARCHAR2 DEFAULT 'UTF-8' -- Charset
    );

    PROCEDURE request_multipart_blob ( -- Procedure add file to multipart form data
        p_req      IN OUT utl_http.req, -- HTTP request
        p_name     VARCHAR2, -- Name
        p_filename VARCHAR2, -- File name  
        p_blob     BLOB -- File content
    );

    PROCEDURE request_multipart_end ( -- Procedure closes multipart data
        p_req IN OUT utl_http.req -- HTTP request
    );

    PROCEDURE response_text ( -- Function returns text data from HTTP request
        p_req  IN OUT utl_http.req, -- HTTP request
        r_clob OUT CLOB -- Response data
    );

    PROCEDURE response_binary ( -- Function returns binary data from HTTP request
        p_req  IN OUT utl_http.req, -- HTTP request
        r_blob OUT BLOB -- Response data
    );

END;
/


-- sqlcl_snapshot {"hash":"2973206e4cdfd0291f2daffe7637be80f844cce4","type":"PACKAGE_SPEC","name":"PCK_API_HTTP","schemaName":"ODBVUE","sxml":""}
implementation
sql
CREATE OR REPLACE PACKAGE BODY odbvue.pck_api_http AS

    SUBTYPE boolean IS PLS_INTEGER RANGE 0..1;
    c_debug    CONSTANT BOOLEAN := 0;
    c_boundary CONSTANT VARCHAR2(30 CHAR) := 'gc0p4Jq0M2Yt08jU534c0p';

    FUNCTION mime_type (
        p_ext VARCHAR2
    ) RETURN VARCHAR2 AS
    BEGIN
        RETURN
            CASE TRIM(lower(p_ext))
                WHEN 'aac'    THEN
                    'audio/aac'
                WHEN 'abw'    THEN
                    'application/x-abiword'
                WHEN 'apng'   THEN
                    'image/apng'
                WHEN 'arc'    THEN
                    'application/x-freearc'
                WHEN 'avif'   THEN
                    'image/avif'
                WHEN 'avi'    THEN
                    'video/x-msvideo'
                WHEN 'azw'    THEN
                    'application/vnd.amazon.ebook'
                WHEN 'bin'    THEN
                    'application/octet-stream'
                WHEN 'bmp'    THEN
                    'image/bmp'
                WHEN 'bz'     THEN
                    'application/x-bzip'
                WHEN 'bz2'    THEN
                    'application/x-bzip2'
                WHEN 'cda'    THEN
                    'application/x-cdf'
                WHEN 'csh'    THEN
                    'application/x-csh'
                WHEN 'css'    THEN
                    'text/css'
                WHEN 'csv'    THEN
                    'text/csv'
                WHEN 'doc'    THEN
                    'application/msword'
                WHEN 'docx'   THEN
                    'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
                WHEN 'eot'    THEN
                    'application/vnd.ms-fontobject'
                WHEN 'epub'   THEN
                    'application/epub+zip'
                WHEN 'gz'     THEN
                    'application/gzip'
                WHEN 'gif'    THEN
                    'image/gif'
                WHEN 'htm'    THEN
                    'text/html'
                WHEN 'html'   THEN
                    'text/html'
                WHEN 'ico'    THEN
                    'image/vnd.microsoft.icon'
                WHEN 'ics'    THEN
                    'text/calendar'
                WHEN 'jar'    THEN
                    'application/java-archive'
                WHEN 'jpeg'   THEN
                    'image/jpeg'
                WHEN 'jpg'    THEN
                    'image/jpeg'
                WHEN 'js'     THEN
                    'text/javascript'
                WHEN 'json'   THEN
                    'application/json'
                WHEN 'jsonld' THEN
                    'application/ld+json'
                WHEN 'mid'    THEN
                    'audio/midi, audio/x-midi'
                WHEN 'midi'   THEN
                    'audio/midi, audio/x-midi'
                WHEN 'mjs'    THEN
                    'text/javascript'
                WHEN 'mp3'    THEN
                    'audio/mpeg'
                WHEN 'mp4'    THEN
                    'video/mp4'
                WHEN 'mpeg'   THEN
                    'video/mpeg'
                WHEN 'mpkg'   THEN
                    'application/vnd.apple.installer+xml'
                WHEN 'odp'    THEN
                    'application/vnd.oasis.opendocument.presentation'
                WHEN 'ods'    THEN
                    'application/vnd.oasis.opendocument.spreadsheet'
                WHEN 'odt'    THEN
                    'application/vnd.oasis.opendocument.text'
                WHEN 'oga'    THEN
                    'audio/ogg'
                WHEN 'ogv'    THEN
                    'video/ogg'
                WHEN 'ogx'    THEN
                    'application/ogg'
                WHEN 'opus'   THEN
                    'audio/opus'
                WHEN 'otf'    THEN
                    'font/otf'
                WHEN 'png'    THEN
                    'image/png'
                WHEN 'pdf'    THEN
                    'application/pdf'
                WHEN 'php'    THEN
                    'application/x-httpd-php'
                WHEN 'ppt'    THEN
                    'application/vnd.ms-powerpoint'
                WHEN 'pptx'   THEN
                    'application/vnd.openxmlformats-officedocument.presentationml.presentation'
                WHEN 'rar'    THEN
                    'application/vnd.rar'
                WHEN 'rtf'    THEN
                    'application/rtf'
                WHEN 'sh'     THEN
                    'application/x-sh'
                WHEN 'svg'    THEN
                    'image/svg+xml'
                WHEN 'tar'    THEN
                    'application/x-tar'
                WHEN 'tif'    THEN
                    'image/tiff'
                WHEN 'tiff'   THEN
                    'image/tiff'
                WHEN 'ts'     THEN
                    'video/mp2t'
                WHEN 'ttf'    THEN
                    'font/ttf'
                WHEN 'txt'    THEN
                    'text/plain'
                WHEN 'vsd'    THEN
                    'application/vnd.visio'
                WHEN 'wav'    THEN
                    'audio/wav'
                WHEN 'weba'   THEN
                    'audio/webm'
                WHEN 'webm'   THEN
                    'video/webm'
                WHEN 'webp'   THEN
                    'image/webp'
                WHEN 'woff'   THEN
                    'font/woff'
                WHEN 'woff2'  THEN
                    'font/woff2'
                WHEN 'xhtml'  THEN
                    'application/xhtml+xml'
                WHEN 'xls'    THEN
                    'application/vnd.ms-excel'
                WHEN 'xlsx'   THEN
                    'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
                WHEN 'xml'    THEN
                    'application/xml'
                WHEN 'xul'    THEN
                    'application/vnd.mozilla.xul+xml'
                WHEN 'zip'    THEN
                    'application/zip'
                WHEN '7z'     THEN
                    'application/x-7z-compressed'
                ELSE
                    'application/octet-stream'
            END;
    END;

    PROCEDURE request (
        p_req     IN OUT utl_http.req,
        p_method  VARCHAR2,
        p_url     VARCHAR2,
        p_version VARCHAR2 DEFAULT 'HTTP/1.1'
    ) AS
    BEGIN
        IF c_debug = 1 THEN
            dbms_output.put_line(p_method
                                 || ' ' || p_url);
        END IF;

        p_req := utl_http.begin_request(p_url, p_method, p_version);
    END;

    PROCEDURE request_auth_basic (
        p_req      IN OUT utl_http.req,
        p_username VARCHAR2,
        p_password VARCHAR2
    ) AS
    BEGIN
        IF c_debug = 1 THEN
            dbms_output.put_line('Authorization: Basic '
                                 || p_username
                                 || ':' || regexp_replace(p_password, '-\w', '*'));
        END IF;

        utl_http.set_authentication(p_req, p_username, p_password);
    END;

    PROCEDURE request_auth_token (
        p_req   IN OUT utl_http.req,
        p_token VARCHAR2
    ) AS
    BEGIN
        IF c_debug = 1 THEN
            dbms_output.put_line('Authorization: Bearer ' || regexp_replace(p_token, '\w', '*'));
        END IF;

        utl_http.set_header(p_req, 'Authorization', 'Bearer ' || p_token);
    END;

    PROCEDURE request_auth_wallet ( -- https://oracle-base.com/articles/misc/utl_http-and-ssl
        p_wallet_path     VARCHAR2,
        p_wallet_password VARCHAR2
    ) AS
    BEGIN
        IF c_debug = 1 THEN
            dbms_output.put_line('Authorization: Wallet file:'
                                 || p_wallet_path
                                 || ' : ' || regexp_replace(p_wallet_password, '\w', '*'));
        END IF;

        utl_http.set_wallet('file:' || p_wallet_path, p_wallet_password);
    END;

    PROCEDURE request_content_type (
        p_req          IN OUT utl_http.req,
        p_content_type VARCHAR2
    ) AS
    BEGIN
        IF p_content_type IS NOT NULL THEN
            IF c_debug = 1 THEN
                dbms_output.put_line('Header content-type: ' || p_content_type);
            END IF;
            utl_http.set_header(p_req, 'content-type', p_content_type);
        END IF;
    END;

    PROCEDURE request_charset (
        p_req          IN OUT utl_http.req,
        p_body_charset VARCHAR2
    ) AS
    BEGIN
        IF p_body_charset IS NOT NULL THEN
            IF c_debug = 1 THEN
                dbms_output.put_line('Charset: ' || p_body_charset);
            END IF;
            utl_http.set_body_charset(p_body_charset);
        END IF;
    END;

    PROCEDURE request_header (
        p_req   IN OUT utl_http.req,
        p_name  VARCHAR2,
        p_value VARCHAR2
    ) AS
    BEGIN
        IF c_debug = 1 THEN
            dbms_output.put_line(p_name
                                 || ': ' || p_value);
        END IF;

        utl_http.set_header(p_req, p_name, p_value);
    END;

    PROCEDURE request_json (
        p_req  IN OUT utl_http.req,
        p_json CLOB
    ) AS

        v_payload_length PLS_INTEGER := dbms_lob.getlength(p_json);
        v_amount         PLS_INTEGER := 2000;
        v_offset         PLS_INTEGER := 1;
        v_buffer         VARCHAR2(6000);
    BEGIN
        IF c_debug = 1 THEN
            dbms_output.put_line('Transfer-Encoding: chunked');
        END IF;
        utl_http.set_header(p_req, 'Transfer-Encoding', 'chunked');
        WHILE ( v_offset < v_payload_length ) LOOP
            dbms_lob.read(p_json, v_amount, v_offset, v_buffer);
            IF c_debug = 1 THEN
                dbms_output.put_line(v_buffer);
            END IF;
            utl_http.write_raw(p_req,
                               utl_raw.cast_to_raw(v_buffer));
            v_offset := v_offset + v_amount;
        END LOOP;

    END;

    PROCEDURE request_multipart_start (
        p_req     IN OUT utl_http.req,
        p_charset VARCHAR2 DEFAULT 'UTF-8'
    ) AS
    BEGIN
        IF c_debug = 1 THEN
            dbms_output.put_line('Content-Type: multipart/form-data; boundary="'
                                 || c_boundary
                                 || '"' ||
                CASE
                    WHEN p_charset IS NOT NULL THEN
                        '; charset=UTF-8'
                    ELSE
                        ''
                END
            );

            dbms_output.put_line('Transfer-Encoding: chunked');
        END IF;

        utl_http.set_header(p_req, 'Content-Type', 'multipart/form-data; boundary="'
                                                   || c_boundary
                                                   || '"'
                                                   ||
            CASE
                WHEN p_charset IS NOT NULL THEN
                    '; charset=UTF-8'
                ELSE
                    ''
            END
        );

        utl_http.set_header(p_req, 'Transfer-Encoding', 'chunked');
    END;

    PROCEDURE request_multipart_varchar2 (
        p_req     IN OUT utl_http.req,
        p_name    VARCHAR2,
        p_value   VARCHAR2,
        p_charset VARCHAR2 DEFAULT 'UTF-8'
    ) AS

        v_chunk CLOB := ''
                        || '--'
                        || c_boundary
                        || utl_tcp.crlf
                        || 'Content-Disposition: form-data; name="'
                        || p_name
                        || '"'
                        || utl_tcp.crlf
                        || 'Content-Type: "application/text'
                        ||
            CASE
                WHEN p_charset IS NOT NULL THEN
                    '; charset=utf-8"'
                ELSE
                    '"'
            END
                        || utl_tcp.crlf
                        || utl_tcp.crlf
                        || utl_url.escape(p_value)
                        || utl_tcp.crlf;
    BEGIN
        IF c_debug = 1 THEN
            dbms_output.put_line(v_chunk);
        END IF;
        utl_http.write_text(p_req, v_chunk);
    END;

    PROCEDURE request_multipart_blob (
        p_req      IN OUT utl_http.req,
        p_name     VARCHAR2,
        p_filename VARCHAR2,
        p_blob     BLOB
    ) AS

        v_chunk  CLOB := ''
                        || '--'
                        || c_boundary
                        || utl_tcp.crlf
                        || 'Content-Disposition: form-data; name="'
                        || p_name
                        || '"; filename="'
                        || p_filename
                        || '"'
                        || utl_tcp.crlf
                        || 'Content-Type: '
                        || mime_type(substr(p_filename,
                                            nullif(instr(p_filename, '.', -1) + 1,
                                                   1)))
                        || utl_tcp.crlf
                        || utl_tcp.crlf;
        v_offset NUMBER := 1;
        v_amount NUMBER := 2000;
        v_buffer RAW(32767);
    BEGIN
        IF c_debug = 1 THEN
            dbms_output.put_line(v_chunk);
        END IF;
        utl_http.write_text(p_req, v_chunk);
        IF c_debug = 1 THEN
            dbms_output.put_line('Writing out '
                                 || dbms_lob.getlength(p_blob) || ' bytes..');
        END IF;

        WHILE ( v_offset < dbms_lob.getlength(p_blob) ) LOOP
            dbms_lob.read(p_blob, v_amount, v_offset, v_buffer);
            utl_http.write_raw(p_req, v_buffer);
            v_offset := v_offset + v_amount;
        END LOOP;

    END;

    PROCEDURE request_multipart_end (
        p_req IN OUT utl_http.req
    ) AS
    BEGIN
        IF c_debug = 1 THEN
            dbms_output.put_line(utl_tcp.crlf
                                 || '--'
                                 || c_boundary || '--');
        END IF;

        utl_http.write_text(p_req, utl_tcp.crlf
                                   || '--'
                                   || c_boundary
                                   || '--');

    END;

    PROCEDURE response_text (
        p_req  IN OUT utl_http.req,
        r_clob OUT CLOB
    ) AS
        v_res    utl_http.resp;
        v_chunk  VARCHAR2(4000);
        v_result CLOB;
    BEGIN
        v_res := utl_http.get_response(p_req);
        dbms_lob.createtemporary(v_result, TRUE, dbms_lob.call);
        BEGIN
            LOOP
                utl_http.read_line(v_res, v_chunk);
                IF c_debug = 1 THEN
                    dbms_output.put_line(length(v_chunk) || ' bytes..');
                END IF;

                dbms_lob.writeappend(v_result,
                                     length(v_chunk),
                                     v_chunk);
            END LOOP;

            utl_http.end_response(v_res);
        EXCEPTION
            WHEN utl_http.end_of_body THEN
                utl_http.end_response(v_res);
        END;

        r_clob := v_result;
    EXCEPTION
        WHEN OTHERS THEN
            utl_http.end_response(v_res);
            RAISE;
    END;

    PROCEDURE response_binary (
        p_req  IN OUT utl_http.req,
        r_blob OUT BLOB
    ) AS
        v_res    utl_http.resp;
        v_chunk  RAW(32767);
        v_result BLOB;
    BEGIN
        v_res := utl_http.get_response(p_req);
        dbms_lob.createtemporary(v_result, TRUE, dbms_lob.call);
        BEGIN
            LOOP
                utl_http.read_raw(v_res, v_chunk);
                IF c_debug = 1 THEN
                    dbms_output.put_line(utl_raw.length(v_chunk) || ' bytes..');
                END IF;

                dbms_lob.writeappend(v_result,
                                     utl_raw.length(v_chunk),
                                     v_chunk);
            END LOOP;

            utl_http.end_response(v_res);
        EXCEPTION
            WHEN utl_http.end_of_body THEN
                utl_http.end_response(v_res);
        END;

        r_blob := v_result;
    EXCEPTION
        WHEN OTHERS THEN
            utl_http.end_response(v_res);
            RAISE;
    END;

END;
/


-- sqlcl_snapshot {"hash":"28189b5ce7a304220f732ce794cbf08a92628533","type":"PACKAGE_BODY","name":"PCK_API_HTTP","schemaName":"ODBVUE","sxml":""}

MIME_TYPE ​

Function returns mime type from file extention, e.g. mp3->audio/mpeg

Argument nameIn OutData typeDefault valueDescription
OUTVARCHAR2Mime type
P_EXTINVARCHAR2File extention

REQUEST ​

Function initiates HTTP request

Argument nameIn OutData typeDefault valueDescription
P_REQIN/OUTPL/SQL RECORDHTTP request
P_METHODINVARCHAR2Method (GET, POST, PUT, DELETE, ..)
P_URLINVARCHAR2Url
P_VERSIONINVARCHAR2'HTTP/1.1'Version

REQUEST_AUTH_BASIC ​

Procedure authenticates user with username and password

Argument nameIn OutData typeDefault valueDescription
P_REQIN/OUTPL/SQL RECORDHTTP request
P_USERNAMEINVARCHAR2User name
P_PASSWORDINVARCHAR2Password

REQUEST_AUTH_TOKEN ​

Procedure adds Bearer token to the request

Argument nameIn OutData typeDefault valueDescription
P_REQIN/OUTPL/SQL RECORDHTTP request
P_TOKENINVARCHAR2Token

REQUEST_AUTH_WALLET ​

Procedure adds Oracle Wallet to HTTP connection (must be called before starting request)

Argument nameIn OutData typeDefault valueDescription
P_WALLET_PATHINVARCHAR2Path to Oracle Wallet (without "file" prefix)
P_WALLET_PASSWORDINVARCHAR2Wallet password

REQUEST_CHARSET ​

Procedure adds charset header to the HTTP request

Argument nameIn OutData typeDefault valueDescription
P_REQIN/OUTPL/SQL RECORDHTTP request
P_BODY_CHARSETINVARCHAR2Charset

REQUEST_CONTENT_TYPE ​

Procedure adds content type header to the HTTP request

Argument nameIn OutData typeDefault valueDescription
P_REQIN/OUTPL/SQL RECORDHTTP request
P_CONTENT_TYPEINVARCHAR2Content type

REQUEST_HEADER ​

Procedure adds custom header to the HTTP request

Argument nameIn OutData typeDefault valueDescription
P_REQIN/OUTPL/SQL RECORDHTTP request
P_NAMEINVARCHAR2Header name
P_VALUEINVARCHAR2Header value

REQUEST_JSON ​

Procedure adds JSON payload to the HTTP request

Argument nameIn OutData typeDefault valueDescription
P_REQIN/OUTPL/SQL RECORDHTTP request
P_JSONINCLOBJSON data

REQUEST_MULTIPART_BLOB ​

Procedure add file to multipart form data

Argument nameIn OutData typeDefault valueDescription
P_REQIN/OUTPL/SQL RECORDHTTP request
P_NAMEINVARCHAR2Name
P_FILENAMEINVARCHAR2File name
P_BLOBINBLOBFile content

REQUEST_MULTIPART_END ​

Procedure closes multipart data

Argument nameIn OutData typeDefault valueDescription
P_REQIN/OUTPL/SQL RECORDHTTP request

REQUEST_MULTIPART_START ​

Procedure starts multipart form data request

Argument nameIn OutData typeDefault valueDescription
P_REQIN/OUTPL/SQL RECORDHTTP request
P_CHARSETINVARCHAR2'UTF-8'Charset

REQUEST_MULTIPART_VARCHAR2 ​

Procedure adds Varchar2 data to multipart form data

Argument nameIn OutData typeDefault valueDescription
P_REQIN/OUTPL/SQL RECORDHTTP request
P_NAMEINVARCHAR2Name
P_VALUEINVARCHAR2Value
P_CHARSETINVARCHAR2'UTF-8'Charset

RESPONSE_BINARY ​

Function returns binary data from HTTP request

Argument nameIn OutData typeDefault valueDescription
P_REQIN/OUTPL/SQL RECORDHTTP request
R_BLOBOUTBLOBResponse data

RESPONSE_TEXT ​

Function returns text data from HTTP request

Argument nameIn OutData typeDefault valueDescription
P_REQIN/OUTPL/SQL RECORDHTTP request
R_CLOBOUTCLOBResponse data