Examples - Web client

Top  Previous  Next

//-----------------------------------------------------------------------------
// web client.vpl, created 2018-03-16 09:58
//
// This is a VERY simple web client. It connects to a web server and downloads
// a web page, which is stored on the internal drive.
// The example shows how to establish a TCP/IP connection, how to enable
// a secure connection, and how to dictate which network interface is used.
//-----------------------------------------------------------------------------
INCLUDE rtcu.inc
 
//  These are the global variables of the program
VAR
  buf      : ARRAY [1..1024] OF SINT;
  timer    : TON;
END_VAR;
 
//////////////////////////////////////////////////////////////////////////////
// Opens a connection to the server
//
// Input:
//    host      - The server domain name
//    port      - The server port number
//    iface     - The network interface to use
//    https     - Use HTTP (FALSE) or HTTPS (TRUE) to get the page
//
// Returns:
//    0         - Success
//    -1        - Socket error
//    -2        - Could not connect to server
FUNCTION server_open : INT;
VAR_INPUT
  host     : STRING;
  port     : DINT;
  iface    : SINT;
  https    : BOOL;
  handle   : ACCESS SYSHANDLE;
END_VAR;
VAR
  address  : STRING;
  rc       : INT;
END_VAR;
 
  // Open socket
  rc := soCreate(socket := handle);
  IF rc < 1 THEN
    // Socket error
    DebugFmt(message := "soCreate = \1", v1 := rc);
    server_open := -1;
    RETURN;
  END_IF;
 
  // Bind to network interface
  IF iface > 0 THEN
    // Get the address of the network interface
    address := soAddrFromInterface(iface := iface);
 
    // Bind the socket
    rc := soBind(socket := handle, address := address);
    IF rc < 1 THEN
        // Socket error
        DebugFmt(message := "soBind = \1", v1 := rc);
        soClose(socket := handle);
        server_open := -1;
        RETURN;
    END_IF;
  END_IF;
 
  // Enable secure connection
  IF https THEN
    rc := soConfigTLS(
                      socket := handle,
                      enable := TRUE
                      );
    IF rc < 1 THEN
        // Socket error
        DebugFmt(message := "soConfigTLS = \1", v1 := rc);
        soClose(socket := handle);
        server_open := -1;
        RETURN;
    END_IF;
  END_IF;
 
  // Generate socket address of destination
  rc := soAddrInetSet(address := address, host := host, port := port);
  IF rc < 1 THEN
    // Not a valid address
    DebugFmt(message := "soAddrInetSet = \1", v1 := rc);
    soClose(socket := handle);
    server_open := -2;
    RETURN;
  END_IF;
 
  // Connect to server
  rc := soConnect(socket := handle, address := address);
  IF rc < 1 THEN
    // Not a valid address
    DebugFmt(message := "soConnect = \1", v1 := rc);
    soClose(socket := handle);
    server_open := -2;
    RETURN;
  END_IF;
 
  // Switch to non-blocking, as some servers leave the socket open
  rc := soConfigNonblock(socket := handle, enable := TRUE);
  IF rc < 1 THEN
    // Not a valid address
    DebugFmt(message := "soConfigNonblock = \1", v1 := rc);
    soClose(socket := handle);
    server_open := -1;
    RETURN;
  END_IF;
END_FUNCTION;
//////////////////////////////////////////////////////////////////////////////
 
//////////////////////////////////////////////////////////////////////////////
// Read response header from server
//
// Input:
//    handle    - The socket connection
//
// Returns:
//    HTTP response code
FUNCTION read_line : INT;
VAR_INPUT
  handle   : SYSHANDLE;
  line     : ACCESS STRING;
END_VAR;
VAR
  ch       : SINT;
  size     : DINT;
  state    : INT;
  rc       : INT;
END_VAR;
 
  // Default
  line  := "";
  state := 0;
  timer(pt := 10000, trig := ON);
 
  // Iterate incoming data
  WHILE TRUE DO
    // Read character
    rc := soRecv(
                  socket  := handle,
                  data    := ADDR(ch),
                  maxsize := 1,
                  size    := size
                 );
    IF rc < 1 AND rc <> -4 THEN
        DebugFmt(message := "soRecv=\1", v1 := rc);
        RETURN;
    END_IF;
 
    // Handle character
    IF rc <> -4 THEN
        IF ch = 16#0A OR ch = 16#0D THEN
          state := state + 1;
        ELSE
          line  := line + strFromMemory(src := ADDR(ch), len := 1);
        END_IF;
        timer(trig := OFF);
    END_IF;
 
    timer(trig := ON);
    IF timer.q THEN
        // Timeout waiting for data
        RETURN;
    END_IF;
    IF state = 2 THEN
        EXIT;
    END_IF;
  END_WHILE;
 
  // Completed
  read_line := 1;
END_FUNCTION;
//////////////////////////////////////////////////////////////////////////////
 
//////////////////////////////////////////////////////////////////////////////
// Read response header from server
//
// Input:
//    handle    - The socket connection
//
// Returns:
//    HTTP response code
FUNCTION reply_header : INT;
VAR_INPUT
  handle   : SYSHANDLE;
END_VAR;
VAR
  line     : STRING;
  http     : INT;
  rc       : INT;
END_VAR;
 
// Iterate response
WHILE TRUE DO
  // Read line
  rc := read_line(handle := handle, line := line);
  IF rc < 1 THEN
    RETURN;
  END_IF;
 
  // Check for header end
  IF strLen(str := line) = 0 THEN
    // The header is received
    EXIT;
  END_IF;
 
  // Check for HTTP reply
  IF strCompare(str1 := strLeft(str := line, length := 5), str2 := "http/") = 0 THEN
    http := strToInt(str := strMid(str := line, start := 10));
  END_IF;
END_WHILE;
 
  // Completed
  reply_header := http;
END_FUNCTION;
//////////////////////////////////////////////////////////////////////////////
 
//////////////////////////////////////////////////////////////////////////////
// Read response body from server
//
// Input:
//    handle    - The socket connection
//
// Returns:
//    HTTP response code
FUNCTION reply_body : INT;
VAR_INPUT
  handle   : SYSHANDLE;
  filename : STRING;
END_VAR;
VAR
  rc       : INT;
  fd       : FILE;
  sent     : DINT;
END_VAR;
 
  // Open file
  fd := fsFileCreate(name := filename);
 
  // Restart receive timer
  timer(trig := OFF);
  timer(pt := 10000, trig := ON);
 
  // Loop until no more data
  WHILE TRUE DO
    rc := soRecv(socket := handle, data := ADDR(buf), maxsize := SIZEOF(buf), size := sent);
    IF rc < 1 AND rc <> -3 AND rc <> -4 THEN
        // Communication error
        DebugFmt(message := "soRecv=\1", v1 := rc);
        fsFileClose(fd := fd);
        RETURN;
    END_IF;
    IF rc = -3 OR sent = 0 THEN
        EXIT;
    END_IF;
 
    // Write to file
    IF rc <> -4 THEN
        fsFileWrite(fd := fd, buffer := ADDR(buf), length := INT(sent));
        timer(trig := OFF);
    END_IF;
 
    // Timer
    timer(trig := ON);
    IF timer.q THEN
        // Timeout waiting for data
        EXIT;
    END_IF;
  END_WHILE;
 
  // Close file
  fsFileClose(fd := fd);
 
  // Completed
  reply_body := 1;

END_FUNCTION;
//////////////////////////////////////////////////////////////////////////////
 
//////////////////////////////////////////////////////////////////////////////
// Builds the HTTP GET command
//
// Input:
//    host      - The server domain name
//    page      - The path to the page on the server
//
// Returns:
//    String containing the HTTP GET command
FUNCTION build_http_get : STRING;
VAR_INPUT
  host     : STRING;
  page     : STRING;
END_VAR;
VAR
  cmd      : STRING;
END_VAR;
  // HTTP command
  cmd  := "GET ";
 
  // Add the page
  cmd  := cmd + page;
 
  // Add HTTP
  cmd  := cmd + " HTTP/1.1$N";
 
  // Add IP address
  cmd  := cmd + "Host: ";
  cmd  := cmd + host;
  cmd  := cmd + "$N";
 
  // Terminate command
  cmd  := cmd + "$N";
 
  build_http_get := cmd;
END_FUNCTION;
//////////////////////////////////////////////////////////////////////////////
 
//////////////////////////////////////////////////////////////////////////////
// Get a WEB page and store it as a file
//
// Input:
//    host      - The server domain name
//    port      - The server port number
//    iface     - The network interface to use
//    page      - The path to the page on the server
//    https     - Use HTTP (FALSE) or HTTPS (TRUE) to get the page
//    filename  - The file name where the page is stored
//
// Returns:
//    0         - Success.
//    -1        - Could not connect to server
//    -2        - Failed to send request
//    -3        - Failed to receive web page
FUNCTION GetPage : INT;
VAR_INPUT
  host     : STRING;
  port     : DINT := 80;
  iface    : SINT;
  page     : STRING;
  filename : STRING;
  https    : BOOL;
END_VAR;
VAR
  handle   : SYSHANDLE;
  sent     : DINT;
  len      : INT;
  rc       : INT;
  request  : STRING;
END_VAR;
 
  // Generate HTTP request
  request := build_http_get(host := host, page := page);
 
  // Open a socket to the server
  DebugMsg(message := "Connecting...");
  rc := server_open(host := host, port := port, iface := iface, https := https, handle:= handle);
  IF rc < 0 THEN
    DebugFmt(message := "server_open=\1", v1 := rc);
    GetPage := -1;
    RETURN;
  END_IF;
 
  // Send request
  DebugMsg(message := "Send request...");
  len := strLen(str := request);
  strToMemory(dst := ADDR(buf), str := request, len := len);
  rc := soSend(socket := handle, data := ADDR(buf), size := len, sent := sent);
  IF rc < 1 THEN
    // Communication error
    DebugFmt(message := "soSend=\1", v1 := rc);
    soClose(socket := handle);
    GetPage := -2;
    RETURN;
  END_IF;
 
  // Receive response
  DebugMsg(message := "Wait for reply...");
  rc := reply_header(handle := handle);
  IF rc < 1 THEN
    // Communication error
    DebugFmt(message := "reply_header=\1", v1 := rc);
    soClose(socket := handle);
    GetPage := -3;
    RETURN;
  END_IF;
  DebugFmt(message := "Reply = \1", v1 := rc);
 
  // Receive web page
  rc := reply_body(handle := handle, filename := filename);
  IF rc < 1 THEN
    // Communication error
    DebugFmt(message := "reply_body=\1", v1 := rc);
    soClose(socket := handle);
    GetPage := -3;
    RETURN;
  END_IF;
 
  // Close
  soClose(socket := handle);
END_FUNCTION;
//////////////////////////////////////////////////////////////////////////////
 
PROGRAM web_client;
VAR
  rc : INT;
END_VAR;
 
  DebugMsg(message := "----------------------------------------");
  DebugMsg(message := "Initialize...");
 
  // Open filesystem
  fsMediaOpen(media := 1);
  Sleep(delay := 2000);
  fsDirChange(path := "B:/");
 
  // Open network interface
  netOpen(iface := _NET_IFACE_LAN1);
 
  // Wait for network connection
  WHILE NOT netConnected(iface := _NET_IFACE_LAN1) DO
    DebugMsg(message := "Wait for connection...");
    Sleep(delay := 3000);
  END_WHILE;
 
  DebugMsg(message := "----------------------------------------");
  rc := GetPage(
                host      := "www.google.dk"
                ,https    := ON
                ,port     := 443
                ,iface    := _NET_IFACE_LAN1
                ,page     := "/"
                ,filename := "index.htm"
               );
  DebugFmt(message := "GetPage=\1", v1 := rc);
 
BEGIN
END;
END_PROGRAM;