B4J Question Reply to HTTP

Declan

Well-Known Member
Licensed User
Longtime User
I have a the following code on my server that receives a HTTP message from a remote Arduino device.
The sequence of events is as follows:
Message received from remote device - I receive the message correctly in "Sub AStream_NewData (Buffer() As Byte)"
I then query the MySQL database table for a value in the table field - This works correctly and I get the value from "Sub getTracking(myDeviceID As String"
I must now send the value back to the remote device - This is where I am stumped.

Code:
B4X:
#Region Project Attributes
    #MainFormWidth: 600
    #MainFormHeight: 400
#End Region

Sub Process_Globals
    'the ports should be opened on firewall for both incoming & outgoing ports
    'port of the broker
    Private PORT As Int = 5000
    'port for rdc
    Private RDC_PORT As Int = 4001
    'server IP addres
    Private SERVER_IP As String = "XXX.XXX.129.17"
    Type DBResult (Tag As Object, Columns As Map, Rows As List)
    Type DBCommand (Name As String, Parameters() As Object)
    Private const rdcLink As String = $"http://${SERVER_IP}:${RDC_PORT}/rdc"$
        
    Private fx As JFX
    Private MainForm As Form
    Private client As Socket
    Private SERVER As ServerSocket
    Private astream As AsyncStreams
    Private txtLogs As TextArea
    
End Sub

Sub AppStart (Form1 As Form, Args() As String)
    MainForm = Form1
    MainForm.RootPane.LoadLayout("1") 'Load the layout file.
    MainForm.Show
    Form1.Title = $"HTTP Test - ${PORT}/${RDC_PORT}"$
    txtLogs.Text = ""
    ListenForClients
    '**********************************************
End Sub

Sub ListenForClients
    Log("ListenForClients started...")
    SERVER.Initialize(PORT, "server")
    Log("Server Initialized: " & SERVER.GetMyIP & "-" & PORT)
    Do While True
        SERVER.Listen
        Log("Server Listening for connections...")
        Wait For Server_NewConnection (Successful As Boolean, NewSocket As Socket)
        If Successful Then
            NewConnection(NewSocket)
        End If
        Sleep(1000) 'prevent a busy loop if there is an error
    Loop
End Sub

Sub NewConnection (NewSocket As Socket)
    CloseExistingConnection
    client = NewSocket
    astream.Initialize(client.InputStream, client.OutputStream, "astream")
    Log("Data Received...")
End Sub

Sub CloseExistingConnection
    If astream.IsInitialized Then
        astream.Close
    End If
    If client.IsInitialized And client.Connected Then
        client.Close
    End If
End Sub

Sub astream_Terminated
    astream_Error
End Sub

Sub astream_Error
End Sub

Sub AStream_NewData (Buffer() As Byte)

        Dim myDeviceID As String
        
        Dim MyStr As String
        Dim OAOD As String = Chr(13) & Chr(10)
        Dim resp As String = "HTTP/1.1 200 OK" & OAOD  & "Content-Length: 0" & OAOD & OAOD
        '
        MyStr = BytesToString(Buffer,0,Buffer.Length,"UTF-8")
        MyStr = MyStr.trim

        astream.Write(resp.GetBytes("UTF8"))
        astream.SendAllAndClose
        
        Log("Got: " & MyStr)
        Log("Len Received: " & MyStr.Length)
        
        '***************************
        If MyStr.IndexOf("TRACK") > -1 Then
            Log("Got Track")
            myDeviceID = MyStr.SubString(MyStr.Length - 5)
            Log ("Tracking ID: " & myDeviceID)
            getTracking(myDeviceID)
            
        End If
        '***************************
End Sub

Sub getTracking(myDeviceID As String)
    'sql.get_tracking=SELECT tracking FROM devices WHERE deviceid=?
    Dim myTracking As String
    Dim req As DBRequestManager = CreateRequest
    Dim cmd As DBCommand = CreateCommand("get_tracking", Array(myDeviceID))
    Wait For (req.ExecuteQuery(cmd, 0, Null)) JobDone(j As HttpJob)
    If j.Success Then
        req.HandleJobAsync(j, "req")
        Wait For (req) req_Result(res As DBResult)
        For Each row() As Object In res.Rows
            myTracking = row(res.Columns.Get("tracking"))
            Log("Tracking: " & myTracking)
        Next
    Else
        Log("ERROR: " & j.ErrorMessage)
    End If
    j.Release
    
End Sub

Sub CreateCommand(Name As String, Parameters() As Object) As DBCommand
    Dim cmd As DBCommand
    cmd.Initialize
    cmd.Name = Name
    If Parameters <> Null Then cmd.Parameters = Parameters
    Return cmd
End Sub

Sub CreateRequest As DBRequestManager
    Dim req As DBRequestManager
    req.Initialize(Me, rdcLink)
    Return req
End Sub


Private Sub btnClear_Click
    txtLogs.Text = ""
End Sub

Log from above code:
B4X:
ListenForClients started...
Server Initialized: 10.0.17.2-5000
Server Listening for connections...
Data Received...
Got: POST / HTTP/1.1
Host: 197.234.129.17:5000
Accept: */*
Connection: Keep-Alive
Content-Type: application/text
User-Agent: SIMCOM_MODULE
Content-Length: 10
~l00524302:
TRACKIS005
Len Received: 172
Got Track
Tracking ID: IS005
Tracking: 1
Server Listening for connections...

How do I now reply to the remote device with the value I get from the MySQL table?
 

Declan

Well-Known Member
Licensed User
Longtime User
File - New - Server and you have a server running.
Great
I have the basic example running on the server and an able to access it from my local pc.
Code in Main:
B4X:
#Region Project Attributes
    #CommandLineArgs:
    #MergeLibraries: True
#End Region

Sub Process_Globals
    Private srvr As Server
End Sub

Sub AppStart (Args() As String)
    srvr.Initialize("srvr")
    srvr.Port = 3307
    srvr.StaticFilesFolder = File.Combine(File.DirApp, "www")
    srvr.AddHandler("/guessmynumber/guess", "GuessMyNumber", False)
    srvr.AddHandler("/guessmynumber/reset", "ResetMyNumber", False)
    srvr.AddWebSocket("/guessmynumber_ws/ws", "WSGuessMyNumber")
    srvr.Start
    StartMessageLoop
    'open browser and navigate to: http://127.0.0.1:3307/
    
End Sub

  1. Now, how do I have a Listener to listen to incoming messages?
  2. Then I must remove the current Handlers and create a new Handler to access my MySQL database.
  3. Then reply to the device IP that I received the message form.
 
Upvote 0

Declan

Well-Known Member
Licensed User
Longtime User
The handler receives a request and writes the response
Is it possible to give me an example of a Handler that would receive the request on the given port and then writes the response?
Even, at this stage, if it just sends back what it receives.
I have tried to create a Listener, but not working.
 
Upvote 0

Erel

B4X founder
Staff member
Licensed User
Longtime User
I have tried to create a Listener, but not working.
There is no such thing.

See the GuessMyNumber handler in the default project:
B4X:
Sub Class_Globals
End Sub

Public Sub Initialize

End Sub

Sub Handle(req As ServletRequest, resp As ServletResponse)
    Try
        If req.GetSession.HasAttribute("myNumber") = False Then
            req.GetSession.SetAttribute("myNumber", Rnd(0, 101))
        End If
        Dim myNumber As Int = req.GetSession.GetAttribute("myNumber")
        Dim n As String = req.GetParameter("number")
        If IsNumber(n) = False Then
            resp.Write("Please enter a valid number.")
        Else
            If n > myNumber Then
                resp.Write("My number is smaller.")
            Else If n < myNumber Then
                resp.Write("My number is larger.")
            Else
                resp.Write("Well done!!!")
            End If
        End If
    Catch
        resp.SendError(500, "error....")
    End Try
End Sub
It reads the user guess from the "number" parameter and returns a string as a response.
 
Upvote 0

Declan

Well-Known Member
Licensed User
Longtime User
It reads the user guess from the "number" parameter and returns a string as a response.
Many Thanks.
This works when I login to the server from my home pc:
B4X:
'Class module
Sub Class_Globals
End Sub

Public Sub Initialize

End Sub

Sub Handle(req As ServletRequest, resp As ServletResponse)
    Try
        Dim n As String = req.GetParameter("number")
        Log(n)
        resp.Write("Got: " & n)
    Catch
        resp.SendError(500, "error....")
    End Try
End Sub

I get:
"Got: "whatever Text I sent"
Which is the text that I enter into the "Enter Number" box.

How do I automate this so that "req.GetParameter("number")" is the message received on the Port?
 
Upvote 0

Erel

B4X founder
Staff member
Licensed User
Longtime User
Remove the Try / Catch. It isn't needed.

How do I automate this so that "req.GetParameter("number")" is the message received on the Port?
It depends on the type of http request you are making.

If this is a POST request and you are sending raw payloads:
B4X:
Dim input() As Byte = Bit.InputStreamToBytes(req.InputStream)
Dim msg As String = BytesToString(input, 0, input.Length, "utf8")
 
Upvote 0

Declan

Well-Known Member
Licensed User
Longtime User
If this is a POST request and you are sending raw payloads
Yes, I am sending a POST request:
B4X:
URL","http://xxx.xxx.129.17:3307/guessmynumber/"
doPost() - Payload to send : TRACKIS005

However, I receive the following from my device:
B4X:
Receive timeout
readHTTP() - Server timeout
HTTP POST error 408

I do not receive anything on my server?

This is my code:
B4X:
'Class module
Sub Class_Globals
End Sub

Public Sub Initialize

End Sub

Sub Handle(req As ServletRequest, resp As ServletResponse)

    Dim input() As Byte = Bit.InputStreamToBytes(req.InputStream)
    Dim msg As String = BytesToString(input, 0, input.Length, "utf8")

    Log("Got: " & msg)
    resp.Write(msg)
        resp.SendError(500, "error....")
End Sub
 
Upvote 0

Declan

Well-Known Member
Licensed User
Longtime User
Ok, now I receive the HTTP Post on my jServer with the following from my Arduino device:
B4X:
SIM800L : Send "AT+HTTPPARA="URL","http://xxx.xxx.129.17:5000/guessmynumber/""
My jServer Code:
B4X:
'Class module
Sub Class_Globals
End Sub

Public Sub Initialize

End Sub

Sub Handle(req As ServletRequest, resp As ServletResponse)

    Dim input() As Byte = Bit.InputStreamToBytes(req.InputStream)
    Dim msg As String = BytesToString(input, 0, input.Length, "utf8")
    
    Dim myDeviceID As String
    If msg.IndexOf("TRACK") > -1 Then
        Log("Got Track")
        myDeviceID = msg.SubString(msg.Length - 5)
        Log ("Tracking ID: " & myDeviceID)
        
        'Get status from MySQL db
        'getTracking(myDeviceID)
            
    End If

    resp.Write("GOTCHA")
    resp.SendError(500, "error....")
End Sub
Log on Server:
B4X:
Got Track
Tracking ID: IS005
In the code I am using the following as a test back to my Arduino device:
B4X:
    resp.Write("GOTCHA")
    resp.SendError(500, "error....")
But, my Arduino device receives the following and NOT the text "GOTCHA":
B4X:
SIM800L : End of transmission
SIM800L : Receive "
+HTTPACTION: 1,500,492
"
SIM800L : readHTTP() - HTTP status 500
SIM800L : Send "AT+HTTPTERM"
SIM800L : End of transmission
SIM800L : Receive "AT+HTTPTERM

OK

I cannot see what I am doing incorrectly in that I do not receive the " resp.Write("GOTCHA")" on the device that initiated the Post request.
 
Upvote 0

Declan

Well-Known Member
Licensed User
Longtime User
Ok, I commented the following line out:
B4X:
resp.SendError(500, "error....")
I now receive the reply "GOTCHA" from the server

I now need to query the MySQL database.
Can that be done with:
B4X:
Sub getTracking(myDeviceID As String)
    'sql.get_tracking=SELECT tracking FROM devices WHERE deviceid=?
    Dim myTracking As String
    Dim req As DBRequestManager = CreateRequest
    Dim cmd As DBCommand = CreateCommand("get_tracking", Array(myDeviceID))
    Wait For (req.ExecuteQuery(cmd, 0, Null)) JobDone(j As HttpJob)
    If j.Success Then
        req.HandleJobAsync(j, "req")
        Wait For (req) req_Result(res As DBResult)
        For Each row() As Object In res.Rows
            myTracking = row(res.Columns.Get("tracking"))
            Log("Tracking: " & myTracking)
        Next
    Else
        Log("ERROR: " & j.ErrorMessage)
    End If
    j.Release
    
End Sub
 
Upvote 0
Top