At the suggestion of our developer-in-chief, Monsieur Laboureur, I had adapted the same example to utilise the newer HTTPRequest() network function, which is a fully-integrated networking method that handles all the conventional networking operations in a single call, including:
1. opening, configuring, and negotiating HTTP & HTTPS connections
2. formatting and sending queries and headers
3. configuring, receiving, and formatting the responses
I am re-posting it here, in a separate thread, under a more descriptive title, for the benefit of yielding better search results for this function:
Code: Select all
;==========================================================
;
; pbHTTP is a modification of pbPost, which implements
; the PureBasic HTTPRequest() function added in v5.70
; instead of the older networking functions in consuming
; online REST APIs - it also supports the HTTPS protocol
;
; this is the forum thread for the orignal pbPost code:
; www.purebasic.fr/english/viewtopic.php?p=625568#p625568
;
; tested & working on:
; 1. Windows 8.1 w/ PureBasic 5.73 LTS (x64)
; 2. Windows 10 w/ PureBasic 6.11 LTS (x64)
; 3. macOS High Sierra w/ PureBasic 5.70 LTS (x64)
; 4. macOS Catalina w/ PureBasic 5.73 LTS (x64)
; 5. macOS Sonoma w/ PureBasic 6.11 LTS (arm64)
; 6. Ubuntu Linux 18.04.2 w/ PureBasic 6.11 LTS (x64)
;
; by TI-994A - free to use, improve, share...
;
; 9th August 2024 - Singapore
;
;==========================================================
EnableExplicit
; the InitNetwork() call is required for
; earlier versions of PureBasic (below v6.0)
CompilerIf #PB_Compiler_Version < 600
InitNetwork()
CompilerEndIf
; the http protocol transfers data in plain text
; the https protocol transfers data in encrypted format
#http = "http://"
#https = "https://"
; http servers typically respond with status codes
; prior to sending the actual requested datasets.
; more information on these codes can be found here:
; https://en.wikipedia.org/wiki/List_of_HTTP_status_codes
#httpSuccess = 200
; these are the URLs to the test server and API script
#host = "server.syed.sg"
#apiURL = "/pbPost.php"
Enumeration
#window
#frame
#editor
#buttonPost
#buttonRetrieve
#textNewTitle
#textOldTitle
#textName
#textCountry
#stringName
#stringCountry
EndEnumeration
Define event, appQuit
; HTTP calls must be preceded by standard headers to
; notify the server of the required transmission protocols.
; more information on the specifications can be found here:
; https://en.wikipedia.org/wiki/List_of_HTTP_header_fields.
; these are the minimum required headers notifying the server
; of the API script (pbPost.php), the request method (POST),
; the host (server.syed.sg), the client (this app), the type
; of connection (close - meaning close after transmission), and
; the format of the content being transmitted (application/x...).
NewMap headers.s()
headers("Host") = #host
headers("Connection") = "close"
headers("User-Agent") = "PureBasic Network Client"
headers("Content-Type") = "application/x-www-form-urlencoded"
; this is the main function which processes
; the requests and responses to/from the server.
Procedure post(query.s)
Shared headers()
Define.s queryLen, serverData, apiURL, httpRequest.i
queryLen = Str(StringByteLength(query, #PB_UTF8))
headers("Content-Length") = queryLen
apiUrl = #https + #host + #apiURL
; HTTPRequest() is an integrated function that performs all the networking
; actions, from opening, configuring, and negotiating HTTP/HTTPS connections,
; formatting and sending queries and headers, to configuring, receiving, and
; formatting the responses which could then be read by the HTTPInfo() function.
httpRequest = HTTPRequest(#PB_HTTP_Post, apiURL, query, #Null, headers())
If httpRequest
If Val(HTTPInfo(httpRequest, #PB_HTTP_StatusCode)) = #httpSuccess
serverData = HTTPInfo(httpRequest, #PB_HTTP_Response)
; an end-of-header delimiter was added by the API to
; conveniently display the response without the headers.
serverData = StringField(serverData, 2, "end_of_header")
SetGadgetText(#editor, serverData)
SetGadgetText(#stringName, "")
SetGadgetText(#stringCountry, "")
EndIf
EndIf
If Trim(serverData) = ""
SetGadgetText(#editor, "Network connection error!")
Debug HTTPInfo(httpRequest, #PB_HTTP_Response)
EndIf
FinishHTTP(httpRequest)
EndProcedure
; this function requests the API to UPDATE (action)
; the given NAME and COUNTRY values into the server
Procedure postEntry()
Define.s name, country, query
name = GetGadgetText(#stringName)
country = GetGadgetText(#stringCountry)
query = URLEncoder("action=update&" + "name=" + name + "&country=" + country)
post(query)
EndProcedure
; this function requests the API to RETRIEVE (action) and
; return the existing server data - the additional parameter
; values are not relevant for this action and will be ignored
Procedure retrieveEntries()
Define.s query = URLEncoder("action=retrieve&name=&country=")
post(query)
EndProcedure
OpenWindow(#window, #PB_Ignore, #PB_Ignore, 640, 480, "REST API w/HTTPRequest",
#PB_Window_SystemMenu | #PB_Window_ScreenCentered)
FrameGadget(#frame, 20, 40, 480, 110, "")
EditorGadget(#editor, 20, 210, 600, 210)
ButtonGadget(#buttonPost, 510, 40, 110, 110, "POST")
ButtonGadget(#buttonRetrieve, 490, 430, 130, 30, "RETRIEVE")
TextGadget(#textNewTitle, 20, 10, 170, 25, "Participant's Information:")
TextGadget(#textOldTitle, 20, 180, 150, 25, "Previous Participants:")
TextGadget(#textName, 35, 60, 70, 25, "Name:")
TextGadget(#textCountry, 35, 100, 70, 25, "Country:")
StringGadget(#stringName, 110, 60, 350, 25, "")
StringGadget(#stringCountry, 110, 100, 350, 25, "")
Repeat
event = WaitWindowEvent()
Select event
Case #PB_Event_CloseWindow
appQuit = #True
Case #PB_Event_Gadget
Select EventGadget()
Case #buttonPost
postEntry()
Case #buttonRetrieve
retrieveEntries()
EndSelect
EndSelect
Until appQuit
The PHP script for the API remains mostly unchanged:
Code: Select all
<?php
// =============================================================
//
// A simple API written in PHP to process 2 request types:
// 1. receive and save POSTed data to the server
// 2. read and return the data from the server
//
// This API will work with any client that complies with
// the query format and is able to process the responses.
//
// by TI-994A - free to use, improve, share...
//
// 20th July 2024 - Singapore
//
// =============================================================
// PHP stores query values in system-designated variables:
// POST data is stored in a variable array named $_POST[]
// GET data is stored in a variable array named $_GET[]
// similarly, the array variable $_SERVER[] stores system
// data pertaining to the client, host, protocols, etc.
// the PureBasic app POSTs [action=Action&name=nameValue&country=countryValue]
// here the named POST query values are being extracted into local variables
$action = trim($_POST['action']);
$name = trim($_POST['name']);
$country = trim($_POST['country']);
// if the requester is the PureBasic application - an end-of-header delimiter
// will be added before the response data to facilitate the removal of headers.
if (trim($_SERVER['HTTP_USER_AGENT']) == "PureBasic Network Client")
echo "end_of_header";
// the server will identify itself with its HTTP protocol
$protocol = (isset($_SERVER['HTTPS'])) ? "https" : "http";
echo $protocol . "://" . $_SERVER['HTTP_HOST'] . " says: " . PHP_EOL;
// server data is usually stored in some SQL database for speed & efficiency -
// however, for simplicity and clarity, a simple file is being used here.
// if the action value is [retrieve] - read and return the server data
if ($action == "retrieve") {
$index = 0;
$records = file("datafile.txt");
if ($records) {
$records = array_reverse($records); // list newest entries first
foreach ($records as $record) {
$index++;
$recordSet .= $index . ". " . $record;
if ($index > 99) // limit response to 100 newest records only
break;
}
echo "Here are the records of the previous participants:" . PHP_EOL;
echo $recordSet;
} else {
echo "Server file error! Records not retrieved.";
}
// if the action value is [update] - store the data into the server
} else if ($action == "update" && $name != "" && $country != "") {
echo "Hello, " . $name . ". ";
$writeSuccess = true;
$dataFile = fopen("datafile.txt", "a");
if ($dataFile) {
// formats the record with the date and the requester's IP address
$newData = $name . " from " . $country . " visited on " .
date("d M Y") . " (" . $protocol . " > IP: " .
$_SERVER['REMOTE_ADDR'] . ")" . PHP_EOL;
if (fwrite($dataFile, $newData)) {
echo "Your records have been updated successfully!" . PHP_EOL;
echo "Thank you for your participation. " . PHP_EOL;
echo "Goodbye!" . PHP_EOL;
} else {
$writeSuccess = false;
}
fclose($dataFile);
} else {
$writeSuccess = false;
}
if (!$writeSuccess) echo "Server file error! Records not updated.";
} else {
// a friendly message for stragglers
echo "You have reached a test site." . PHP_EOL;
}
?>