|
//-----------------------------------------------------------------------------
// 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
// Input variables that can be configured via the configuration dialog (These are global)
VAR_INPUT
END_VAR;
// Output variables that can be configured via the configuration dialog (These are global)
VAR_OUTPUT
END_VAR;
// 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(trig := OFF);
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 filename 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; // The server domain name
port : DINT := 80; // The server port. default: HTTP (80)
iface : SINT; // The network interface to use
page : STRING; // The page to get
filename : STRING; // The filename to store the web page
https : BOOL; // Use https to get the page
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;
|