Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Dispatching HTTP POST Requests from Oracle with Triggers and Network ACLs

Tech 2

Row-level database activity can drive outbonud HTTP calls by wiring a trigger to invoke a PL/SQL procedure that performs the request via UTL_HTTP. Network access must be permitted using ACLs, and character-set handling needs attention to prevent data corruption.

Trigger to invoke an HTTP request

CREATE OR REPLACE TRIGGER trg_req_http_notify
AFTER INSERT ON REQUESTHANDLERINFO
FOR EACH ROW
BEGIN
  pkg_http_bridge.send_request(
    p_req_id => :NEW.id,
    p_params => :NEW.params
  );
END trg_req_http_notify;

Procedure to issue a POST and read the response

CREATE OR REPLACE PACKAGE pkg_http_bridge AS
  PROCEDURE send_request(p_req_id IN VARCHAR2, p_params IN VARCHAR2);
END pkg_http_bridge;
/

CREATE OR REPLACE PACKAGE BODY pkg_http_bridge AS
  PROCEDURE send_request(p_req_id IN VARCHAR2, p_params IN VARCHAR2) IS
    l_req       UTL_HTTP.req;
    l_resp      UTL_HTTP.resp;
    l_line      VARCHAR2(32767);
    l_base_url  VARCHAR2(1024) :=
      'http://192.168.0.156:8080/bzjw_platform/f/responseresult/responseResult/invokeOutRequest';

    -- Parameters are placed on the query string to avoid body-encoding issues in non-UTF8 databases
    l_query     VARCHAR2(4000) :=
      'rid=' || UTL_URL.escape(p_req_id, TRUE) ||
      '&params=' || UTL_URL.escape(p_params, TRUE);

    -- Minimal payload for POST semantics
    l_payload   VARCHAR2(200) := 'ping=1';
    l_raw       RAW(32767) := UTL_RAW.cast_to_raw(l_payload);
  BEGIN
    l_req := UTL_HTTP.begin_request(url => l_base_url || '?' || l_query, method => 'POST');

    UTL_HTTP.set_body_charset('UTF-8');
    UTL_HTTP.set_header(l_req, 'Content-Type', 'application/x-www-form-urlencoded');
    UTL_HTTP.set_header(l_req, 'Content-Length', TO_CHAR(UTL_RAW.length(l_raw)));
    UTL_HTTP.set_header(l_req, 'Keep-Alive', 'timeout=1');

    UTL_HTTP.write_raw(l_req, l_raw);

    l_resp := UTL_HTTP.get_response(l_req);
    LOOP
      UTL_HTTP.read_line(l_resp, l_line, TRUE);
      DBMS_OUTPUT.put_line(l_line);
    END LOOP;
  EXCEPTION
    WHEN UTL_HTTP.end_of_body THEN
      UTL_HTTP.end_response(l_resp);
  END send_request;
END pkg_http_bridge;
/

Alternaitve: POST body encoded to UTF-8 (for non-UTF8 database charsets)

When the database character set is not UTF-8 (for example, GBK), POST body content can be corrupted if sent as VARCHAR2. Convert the body to UTF-8 bytes explicit and declare the charset in the Content-Type header.

CREATE OR REPLACE PROCEDURE proc_http_post_utf8(
  p_req_id  IN VARCHAR2,
  p_params  IN VARCHAR2
) AS
  l_req       UTL_HTTP.req;
  l_resp      UTL_HTTP.resp;
  l_line      VARCHAR2(32767);
  l_url       VARCHAR2(1024) :=
    'http://192.168.0.156:8080/bzjw_platform/f/responseresult/responseResult/invokeOutRequest';

  -- Send parameters in the body
  l_form      VARCHAR2(4000) := 'rid=' || p_req_id || '&params=' || p_params;
  l_raw_body  RAW(32767) := UTL_I18N.string_to_raw(l_form, 'AL32UTF8');
BEGIN
  l_req := UTL_HTTP.begin_request(l_url, 'POST');

  UTL_HTTP.set_header(l_req, 'Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
  UTL_HTTP.set_header(l_req, 'Content-Length', TO_CHAR(UTL_RAW.length(l_raw_body)));
  UTL_HTTP.write_raw(l_req, l_raw_body);

  l_resp := UTL_HTTP.get_response(l_req);
  LOOP
    UTL_HTTP.read_line(l_resp, l_line, TRUE);
    DBMS_OUTPUT.put_line(l_line);
  END LOOP;
EXCEPTION
  WHEN UTL_HTTP.end_of_body THEN
    UTL_HTTP.end_response(l_resp);
END proc_http_post_utf8;
/

Network ACL setup

Oracle requires explicit ACL configuration to allow UTL_HTTP outbound access.

Create the ACL:

BEGIN
  DBMS_NETWORK_ACL_ADMIN.create_acl(
    acl         => 'acl_http_req.xml',
    description => 'HTTP access',
    principal   => 'CONNECT',
    is_grant    => TRUE,
    privilege   => 'connect',
    start_date  => NULL,
    end_date    => NULL
  );
END;
/

Grant a specific user privileges in the ACL:

BEGIN
  DBMS_NETWORK_ACL_ADMIN.add_privilege(
    acl        => 'acl_http_req.xml',
    principal  => 'SD_JY',  -- use the exact case of the database username
    is_grant   => TRUE,
    privilege  => 'connect',
    start_date => NULL,
    end_date   => NULL
  );
END;
/

Bind the ACL to the target host and port:

BEGIN
  DBMS_NETWORK_ACL_ADMIN.assign_acl(
    acl        => 'acl_http_req.xml',
    host       => '192.168.0.156',
    lower_port => 8080,
    upper_port => NULL
  );
END;
/

Verify ACL artifacts

List ACL files stored in the data dictionary:

SELECT any_path
FROM   resource_view
WHERE  any_path LIKE '/sys/acls/%.xml';

Inspect granted privileges:

SELECT acl,
       principal,
       privilege,
       is_grant,
       TO_CHAR(start_date, 'DD-MON-YYYY') AS start_date,
       TO_CHAR(end_date,   'DD-MON-YYYY') AS end_date
FROM   dba_network_acl_privileges;

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.