Using HTTPLib
In this chapter we will learn how to use the HTTPLib library.
Introduction
This extension provides support for the httplib library
Server Class Methods
route(cType,cURL,cCode)
setContent(cContent,cType)
setHTMLPage(oPage)
shareFolder(cFolder)
setCookie(cStr)
cookies() -> aList
getFileContent(cFile) -> cString
getFileName(cFile) -> cString
request().body() -> cString
setStatus(nStatusCode)
getStatus() -> nStatusCode
Example
load "httplib.ring"
oServer = new Server {
? "Try localhost:8080/hi"
route(:Get,"/hi",:mytest)
? "Listen to port 8080"
listen("0.0.0.0", 8080)
}
func mytest
oServer.setContent("Hello World!", "text/plain")
Samples
The samples exist in ring/samples/UsingHTTPLib folder
Printing Constants
The next example print the constants defined by the extension
load "httplib.ring"
? "Constants:"
? HTTPLIB_KEEPALIVE_TIMEOUT_SECOND
? HTTPLIB_KEEPALIVE_MAX_COUNT
? HTTPLIB_CONNECTION_TIMEOUT_SECOND
? HTTPLIB_CONNECTION_TIMEOUT_USECOND
? HTTPLIB_READ_TIMEOUT_SECOND
? HTTPLIB_READ_TIMEOUT_USECOND
? HTTPLIB_WRITE_TIMEOUT_SECOND
? HTTPLIB_WRITE_TIMEOUT_USECOND
? HTTPLIB_IDLE_INTERVAL_SECOND
? HTTPLIB_IDLE_INTERVAL_USECOND
? HTTPLIB_REQUEST_URI_MAX_LENGTH
? HTTPLIB_REDIRECT_MAX_COUNT
? HTTPLIB_PAYLOAD_MAX_LENGTH
? HTTPLIB_TCP_NODELAY
? HTTPLIB_COMPRESSION_BUFSIZ
? HTTPLIB_THREAD_POOL_COUNT
? HTTPLIB_RECV_FLAGS
? HTTPLIB_LISTEN_BACKLOG
Using HTTP GET
Example(1):
load "httplib.ring"
? "Start the server..."
oServer = new Server
? "Try localhost:8080/hi"
oServer.route(:Get,"/hi",:mytest)
? "Listen to port 8080"
oServer.listen("0.0.0.0", 8080)
func mytest
oServer.setContent("Hello World!", "text/plain")
Example(2):
load "httplib.ring"
? "Start the server..."
oServer = new Server
? "Try localhost:8080/one"
oServer.route(:Get,"/one",:one)
? "Try localhost:8080/two"
oServer.route(:Get,"/two",:two)
? "Listen to port 8080"
oServer.listen("0.0.0.0", 8080)
func one
oServer.setContent("one", "text/plain")
func two
oServer.setContent("two", "text/plain")
Example(3):
In this example we will use anonymous function
load "httplib.ring"
? "Try localhost:8080/hello"
oServer = new Server {
route(:Get,"/hello",func {
oServer.setContent("Hello, World!", "text/plain")
})
listen("0.0.0.0", 8080)
}
Example(4):
load "httplib.ring"
? "Try localhost:8080/hi - See output in console at Server-Side"
? "Try localhost:8080/hello - See output in web browser at Client-Side"
oServer = new Server {
route(:Get,"/hi",'? "Wow, I love Ring programming!"')
route(:Get,"/hello",'oServer.setContent("Hello, World!", "text/plain")')
listen("0.0.0.0", 8080)
}
Example(5):
load "httplib.ring"
new Client("localhost:8080") {
? download("/one")
? download("/two")
}
Tip
Using the Download() method in the InternetLib is faster
Using WebLib
Example(1):
load "httplib.ring"
load "weblib.ring"
import System.Web
? "Start the server..."
oServer = new Server
? "Try localhost:8080/report"
oServer.route(:Get,"/report",:report)
? "Listen to port 8080"
oServer.listen("0.0.0.0", 8080)
func report
oPage = New HTMLPage
{
nRowsCount = 10
title = "Report"
h1 { text("Customers Report") }
Table
{
style = stylewidth("100%") + stylegradient(4)
TR
{
TD { WIDTH="10%" text("Customers Count : " ) }
TD { text (nRowsCount) }
}
}
Table
{
style = stylewidth("100%") + stylegradient(26)
TR
{
style = stylewidth("100%") + stylegradient(24)
TD { text("Name " ) }
TD { text("Age" ) }
TD { text("Country" ) }
TD { text("Job" ) }
TD { text("Company" ) }
}
for x = 1 to nRowsCount
TR
{
TD { text("Test" ) }
TD { text("30" ) }
TD { text("Egypt" ) }
TD { text("Sales" ) }
TD { text("Future" ) }
}
next
}
}
oServer.setHTMLPage(oPage)
Using HTTP Post
Example(1):
load "httplib.ring"
load "weblib.ring"
import System.Web
? "Start the server..."
oServer = new Server
? "Try localhost:8080/form"
oServer.route(:Get,"/form",:form)
oServer.route(:Post,"/formresponse",:formresponse)
? "Listen to port 8080"
oServer.listen("0.0.0.0", 8080)
func form
oPage = New HTMLPageFunctions
{
boxstart()
text( "Post Test")
newline()
boxend()
divstart([:style=StyleFloatLeft()+StyleWidth("100px") ])
newline()
text( "Number1 : " ) newline() newline()
text( "Number2 : " ) newline() newline()
divend()
formpost("formresponse")
divstart([ :style = styleFloatLeft()+StyleWidth("200px") ])
newline()
textbox([ :name = "Number1" ]) newline() newline()
textbox([ :name = "Number2" ]) newline() newline()
submit([ :value = "Send" ] )
divend()
formend()
}
oServer.setHTMLPage(oPage)
func formresponse
oPage = New HTMLPageFunctions
{
boxstart()
text( "Post Result" )
newline()
boxend()
divstart([ :style = styleFloatLeft()+styleWidth("200px") ])
newline()
text( "Number1 : " + oServer["Number1"] )
newline() newline()
text( "Number2 : " + oServer["Number2"] )
newline() newline()
text( "Sum : " + (0 + oServer["Number1"] +
oServer["Number2"] ) )
newline()
divend()
}
oServer.setHTMLPage(oPage)
Example(2):
load "httplib.ring"
Load "openssllib.ring"
load "weblib.ring"
import System.Web
? "Start the server..."
oServer = new Server
? "Try localhost:8080/hash"
oServer.route(:Get,"/hash",:hash)
oServer.route(:Post,"/hashresponse",:hashresponse)
? "Listen to port 8080"
oServer.listen("0.0.0.0", 8080)
func hash
oPage = New HTMLPageFunctions
{
boxstart()
text( "Hash Test")
newline()
boxend()
divstart([:style = StyleFloatLeft() + StyleWidth("100px") ])
newline()
text( "Value : " )
newline() newline()
divend()
formpost("/hashresponse")
divstart([:style = StyleFloatLeft() + StyleWidth("300px") ])
newline()
textbox([ :name = "Value" ])
newline() newline()
submit([ :value = "Send" ])
divend()
formend()
}
oServer.setHTMLPage(oPage)
func hashresponse
oPage = New HTMLPageFunctions
{
boxstart()
text( "Hash Result" )
newline()
boxend()
divstart([:style = styleFloatLeft() + styleWidth("100%") ])
newline()
text( "Value : " + oServer["Value"] )
newline()
text( "MD5 : " + md5(oServer["Value"]) )
newline()
text( "SHA1 : " + SHA1(oServer["Value"]) )
newline()
text( "SHA256 : " + SHA256(oServer["Value"]) )
newline()
text( "SHA224 : " + SHA224(oServer["Value"]) )
newline()
text( "SHA384 : " + SHA384(oServer["Value"]) )
newline()
text( "SHA512 : " + SHA512(oServer["Value"]) )
newline()
divend()
}
oServer.setHTMLPage(oPage)
Getting the Request Body
When building APIs, it’s common to receive data, like JSON, in the raw body of a POST request. The oServer[“key”] syntax is for form-data, not raw bodies. To get the raw body, use the body() method on the request object.
Example: Receiving JSON Data
load "httplib.ring"
load "jsonlib.ring"
? "Start the server..."
oServer = new Server
? `Try: curl -X POST -H "Content-Type: application/json" -d '{"name": "Ring"}' http://localhost:8080/data`
oServer.route(:Post, "/data", :process_data)
? "Listen to port 8080"
oServer.listen("0.0.0.0", 8080)
func process_data
// Get the request object
oRequest = oServer.request()
// Get the raw body as a string
cBody = oRequest.body()
? "Received raw body: " + cBody
// Now you can parse it (e.g., as JSON)
aJson = json2list(cBody)
cName = aJson["name"]
oServer.setContent("Hello, " + cName + "!", "text/plain")
Using HTTP PUT
Example(1):
load "httplib.ring"
? "Start the server..."
oServer = new Server
? "Try localhost:8080/update"
? `Try: curl -X PUT -d "test data" http://localhost:8080/update`
oServer.route(:Put,"/update",:puttest)
? "Listen to port 8080"
oServer.listen("0.0.0.0", 8080)
func puttest
cBody = oServer.request().body()
oServer.setContent("PUT Data received: " + cBody, "text/plain")
Example(2):
load "httplib.ring"
load "jsonlib.ring"
? "Start the server..."
oServer = new Server
? "Try localhost:8080/items"
? `Try: curl -X PUT -H "Content-Type: application/json" -d '{"name": "Item 1"}' http://localhost:8080/items`
oServer.route(:Put,"/items",:updateitem)
? "Listen to port 8080"
oServer.listen("0.0.0.0", 8080)
func updateitem
cBody = oServer.request().body()
aJson = json2list(cBody)
cName = aJson[:name]
oServer.setContent("Updated item: " + cName, "text/plain")
Using HTTP PATCH
Example:
load "httplib.ring"
? "Start the server..."
oServer = new Server
? "Try localhost:8080/patch"
? `Try: curl -X PATCH -H "Content-Type: application/json" -d '{"name": "Partially Updated"}' http://localhost:8080/patch`
oServer.route(:Patch,"/patch",:patchtest)
? "Listen to port 8080"
oServer.listen("0.0.0.0", 8080)
func patchtest
cBody = oServer.request().body()
oServer.setContent("PATCH Data received: " + cBody, "text/plain")
Using HTTP DELETE
Example(1):
load "httplib.ring"
? "Start the server..."
oServer = new Server
? "Try localhost:8080/delete"
? `Try: curl -X DELETE http://localhost:8080/delete`
oServer.route(:Delete,"/delete",:deletetest)
? "Listen to port 8080"
oServer.listen("0.0.0.0", 8080)
func deletetest
oServer.setContent("Item deleted", "text/plain")
Example(2):
load "httplib.ring"
? "Start the server..."
oServer = new Server
? "Try localhost:8080/items/5"
? "Example: localhost:8080/items/123"
? `Try: curl -X DELETE http://localhost:8080/items/5`
oServer.route(:Delete,"/items/(\d+)",:deleteitem)
? "Listen to port 8080"
oServer.listen("0.0.0.0", 8080)
func deleteitem
cItemId = oServer.Match(1)
oServer.setContent("Deleted item: " + cItemId, "text/plain")
Using HTTP OPTIONS
Example:
load "httplib.ring"
? "Start the server..."
oServer = new Server
? "Try localhost:8080/options"
? `Try: curl -v -X OPTIONS http://localhost:8080/options`
oServer.route(:Options,"/options",:optionstest)
? "Listen to port 8080"
oServer.listen("0.0.0.0", 8080)
func optionstest
oServer.setContent("Allowed: GET, POST, PUT, PATCH, DELETE", "text/plain")
oServer.response().set_header("Access-Control-Allow-Methods", "GET, POST, PUT, PATCH, DELETE, OPTIONS")
REST API Authentication
A common way to secure a REST API is by requiring an API key sent in an HTTP header. The setStatus() method is useful here to send the correct HTTP status codes, like 401 Unauthorized for failed authentication or 200 OK for success.
Example:
load "httplib.ring"
load "jsonlib.ring"
# A simple list of valid API keys.
aValidApiKeys = [
"secret-key-123",
"power-user-456",
"limited-access-789"
]
# Create the main server object
oServer = new Server {
# Define the protected API route
route(:Get, "/api/data", func() {
# Authentication check
if isAuthenticated(oServer.request())
# Success: Client is Authenticated
? "Request received with valid API Key. Sending data."
# Prepare the JSON data response
aJsonData = [
:status = "success",
:message = "Welcome, authenticated user!",
:data = [
:user_id = 12345,
:permissions = ["read_data", "view_reports"]
]
]
cJsonResponse = list2json(aJsonData)
# Send the 200 OK response with the JSON data
oServer.setStatus(200)
? "Status for '/api/data' route is: " + oServer.getStatus()
oServer.setContent(cJsonResponse, "application/json")
else
# Failure: Client is Not Authenticated
? "Request received with missing or invalid API Key. Denying access."
# Prepare the JSON error response
aErrorData = [
:error = "Unauthorized",
:message = "A valid 'X-API-KEY' header is required to access this resource."
]
cErrorResponse = list2json(aErrorData)
# Send the 401 Unauthorized response
oServer.setStatus(401)
? "Status for '/api/data' route is: " + oServer.getStatus()
oServer.setContent(cErrorResponse, "application/json")
ok
})
? "REST API Server listening at http://localhost:8080"
? "Try accessing the protected route '/api/data' with and without an API key."
? "Try: curl -H 'X-API-KEY: secret-key-123' http://localhost:8080/api/data"
? "Or without a key: curl -v http://localhost:8080/api/data"
listen("0.0.0.0", 8080)
}
# Helper function to check for a valid API key in the request headers
func isAuthenticated oRequest
# Check if the 'X-API-KEY' header is present
if not oRequest.has_header("X-API-KEY")
return false
ok
# Get the key provided by the client
cClientKey = oRequest.get_header_value("X-API-KEY")
# Check if the client's key exists in our list of valid keys
if find(aValidApiKeys, cClientKey)
return true
else
return false
ok
More Samples
Using Gradients:
load "httplib.ring"
load "weblib.ring"
import System.Web
? "Start the server..."
oServer = new Server
? "Try localhost:8080/gradient"
oServer.route(:Get,"/gradient",:gradient)
? "Listen to port 8080"
oServer.listen("0.0.0.0", 8080)
func gradient
oPage = New HTMLPageFunctions
{
boxstart()
text("StyleGradient() Function")
boxend()
for x = 1 to 60
divstart([ :id = x , :align = "center" ,
:style = stylefloatleft() +
stylesize(string(100/60*6)+"%","50px") +
stylegradient(x) ])
h3(x)
divend()
next
}
oServer.setHTMLPage(oPage)
Using Lists:
load "httplib.ring"
load "weblib.ring"
import System.Web
? "Start the server..."
oServer = new Server
? "Try localhost:8080/lists"
oServer.route(:Get,"/lists",:lists)
? "Listen to port 8080"
oServer.listen("0.0.0.0", 8080)
func lists
oPage = New HTMLPageFunctions
{
ulstart([])
for x = 1 to 10
listart([])
text(x)
liend()
next
ulend()
list2ul(["one","two","three","four","five"])
ulstart([])
for x = 1 to 10
listart([])
cFuncName = "btn"+x+"()"
button([ :onclick = cFuncName , :value = x])
script(scriptfuncalert(cFuncName,string(x)))
liend()
next
ulend()
}
oServer.setHTMLPage(oPage)
Using Tables:
load "httplib.ring"
load "weblib.ring"
import System.Web
? "Start the server..."
oServer = new Server
? "Try localhost:8080/table"
oServer.route(:Get,"/table",:table)
? "Listen to port 8080"
oServer.listen("0.0.0.0", 8080)
func table
oPage = New HTMLPageFunctions
{
divstart([ :style = styledivcenter("400px","500px") ] )
style(styletable() + styletablerows("t01"))
tablestart([ :id = :t01 , :style = stylewidth("100%") ])
rowstart([])
headerstart([]) text("Number") headerend()
headerstart([]) text("square") headerend()
rowend()
for x = 1 to 10
rowstart([])
cellstart([]) text(x) cellend()
cellstart([]) text(x*x) cellend()
rowend()
next
tableend()
divend()
}
oServer.setHTMLPage(oPage)
Play Video:
load "httplib.ring"
load "weblib.ring"
import System.Web
? "Start the server..."
oServer = new Server
? "Try localhost:8080/play"
oServer.route(:Get,"/play",:play)
? "We support files in the res folder like res/horse.ogg and res/movie.mp4"
oServer.shareFolder("res")
? "Listen to port 8080"
oServer.listen("0.0.0.0", 8080)
func play
oPage = New HTMLPage
{
Title = "Welcome"
h1 { text("Play sound and video!") }
div
{
audio
{
src = "res/horse.ogg"
type = "audio/ogg"
}
video
{
width = 320
height = 240
src = "res/movie.mp4"
type = "video/mp4"
}
}
}
oServer.setHTMLPage(oPage)
Uploading Files
Example:
load "httplib.ring"
load "weblib.ring"
import System.Web
? "Start the server..."
oServer = new Server
cUploadFolder = "upload/"
oServer.shareFolder(cUploadFolder)
? "Try localhost:8080/upload"
oServer.route(:Get,"/upload",:upload)
oServer.route(:Post,"/uploadresponse",:uploadresponse)
? "Listen to port 8080"
oServer.listen("0.0.0.0", 8080)
func upload
oPage = New HTMLPageFunctions
{
boxstart()
text( "Upload File" )
newline()
boxend()
for x = 1 to 3 newline() next
formupload("/uploadresponse")
text( "Customer Name : " )
textbox([ :name = "custname" ])
newline() newline()
divstart([ :style = styleFloatLeft() + styleWidth("90px") ])
uploadfile("file1") newline() newline()
uploadfile("file2") newline() newline()
submit([ :value = "Send" ])
divend()
formend()
}
oServer.setHTMLPage(oPage)
func uploadresponse
oPage = New HTMLPageFunctions
{
boxstart()
text( "Upload Result" )
newline()
boxend()
newline()
divstart([ :style= styleFloatLeft() + styleWidth("100px") ])
text( "Name : " + oServer["custname"] )
newline()
divend()
getuploadedfile(self,"file1")
getuploadedfile(self,"file2")
}
oServer.setHTMLPage(oPage)
Func getUploadedFile oObj,cFile
cNewFileName = oServer.getfilename(cFile)
if cNewFileName = NULL return ok
cNewFileContent = oServer.getFileContent(cFile)
/*
Here we use object.property instead of object { }
To avoid executing braceend() method
*/
cFileName = cUploadFolder + cNewFileName
write(cFileName,cNewFileContent)
if isLinux()
system("chmod a+x "+cFileName)
ok
oObj.newline()
oObj.text( "File "+cFileName+ " Uploaded ..." )
oObj.newline()
imageURL = cFileName
oObj.link([ :url = imageURL, :title = "Download" ])
oObj.newline()
oObj.image( [ :url = imageURL , :alt = :image ] )
oObj.newline()
Using Templates
Example:
load "httplib.ring"
load "weblib.ring"
import System.Web
? "Start the server..."
oServer = new Server
? "Try localhost:8080/template"
oServer.route(:Get,"/template"," new numbersController { start() } ")
? "Listen to port 8080"
oServer.listen("0.0.0.0", 8080)
class numbersController
MyHeader aNumbers
func Start
MyHeader = New Header
{
cColumn1 = "Number" cColumn2 = "Square"
}
aNumbers = list(20)
for x = 1 to len(aNumbers)
aNumbers[x] = new number
{
nValue = x nSquare = x*x
}
next
cTemp = Template("templates/mynumbers.html",self)
oPage = new HTMLPageFunctions
{
boxstart()
text( "Test Templates" )
newline()
boxend()
html(cTemp)
}
oServer.setHTMLPage(oPage)
Class Header cColumn1 cColumn2
Class Number nValue nSquare
Regular Expressions
Example:
load "httplib.ring"
? "Start the server..."
oServer = new Server
? "Try localhost:8080/numbers/<number>"
? "Example: localhost:8080/numbers/123"
oServer.route(:Get,"(/numbers/(\d+))",:mytest)
? "Listen to port 8080"
oServer.listen("0.0.0.0", 8080)
func mytest
cOutput = "Match(1): " + oServer.Match(1) + nl
cOutput += "Match(2): " + oServer.Match(2) + nl
oServer.setContent(cOutput, "text/plain")
Stop the Server
Example:
load "httplib.ring"
? "Start the server..."
oServer = new Server
? "Try localhost:8080/time"
? "Try localhost:8080/stop"
oServer.route(:Get,"/time",:gettime)
oServer.route(:Get,"/stop",:stop)
? "Listen to port 8080"
oServer.listen("0.0.0.0", 8080)
func gettime
oServer.setContent("Time: " + time(), "text/plain")
func stop
oServer.stop()