echo example

Xbase++ 2.0 Build 554 or later
Message
Author
Zdeno Bielik
Posts: 147
Joined: Thu Jan 28, 2010 9:24 am
Location: Nitra, Slovakia
Contact:

echo example

#1 Post by Zdeno Bielik »

Hi Roger,

I want in your test Test:Echo example save and then show e.g. all previously passed strings and time when it was called.

But when I call this modified example in webbrowser,
next error is saved in error.log file:

= Donnay Software Web Server Error ==========
Date: 20150421, Time: 08:16:16
App: c:\exp20\Samples\Web20\CxpHttpServer.EXE, Thread: 4
ERROR BASE/2266
Receiver of message is not an object.: Form
Called from WEBHANDLER:EXECUTE(149)
Called from CUSTOMWEBHANDLER:EXECUTE(157)
Called from WEBHANDLERPROCESSOR:RUN(260)
Called from ABSTRACTHOSTADAPTOR:RUN(204)
Called from WEBJOB:EXECUTEWEBREQUEST(562)
Called from WEBJOB:EXECUTEHTTP(505)
Called from WEBJOB:EXECUTE(461)


Where is problem? Please, can you correct my code?

TIA & Regards
Zdeno

Code: Select all

#INCLUDE "dcdialog.CH"

#define CRLF Chr(13)+Chr(10)


CLASS Test FROM CustomWebHandler

  EXPORTED:
    VAR aTime
    VAR aString
    VAR nCount

    METHOD /*init,*/ echo, getTime, echoVars, crash

* --------
INLINE METHOD init
* METHOD Test:init
/*
LOCAL aVars, i
aVars := ::classdescribe()[3]
FOR i := 1 TO Len(aVars)
  ::&(aVars[i,1]) := ''
NEXT
*/
::aTime   := {}
::aString := {}
::nCount  := 0
*
RETURN self
* ---------

ENDCLASS

*

METHOD Test:echo( cParamName1 )

LOCAL cHtml := ""

::setErrorBlock()

AAdd( ::aTime, Time() )
::nCount ++

cHtml += "<html><title>Donnay:echo() was called</title><body>"
cHtml += '<pre>'
* cHtml += "Parameter cParamName1 is " + cParamName1 + CRLF
cHtml += 'Pcount is: ' + Alltrim(Str(Pcount())) + CRLF
cHtml += 'Query String is: ' + ::httpRequest:getQueryString() + CRLF
*
cHtml += 'aTime: ' + Var2Char( ::nCount ) + ' - '  + Var2Char( ::aTime ) + ' - ' + Var2Char( ::httpRequest:getQueryString() ) + '</br>'+ CRLF
*
cHtml += "</pre></body></html>"

RETURN cHtml

User avatar
rdonnay
Site Admin
Posts: 4729
Joined: Wed Jan 27, 2010 6:58 pm
Location: Boise, Idaho USA
Contact:

Re: echo example

#2 Post by rdonnay »

It took me some time to figure this out but there were several problems in your code.

1. It appears that classes which inherit from CustomWebHandler cannot have an INIT method. This will cause the WebHandler to not respond at all. I tried several ways to make this work including adding a call to ::webHandler:init() but this is a no go.

2. You need to put correct error recovery in your methods to make sure that errors get sent to the browser.

Unfortunately, the HttpRequest() class is not well documented and all the instance variables are hidden so WTF and DC_InspectObject()
cannot show the values and perform the drill down. I am working on a way to get around this in eXpress++.

Here is some code that works:

Code: Select all

#INCLUDE "dcdialog.CH"

#define CRLF Chr(13)+Chr(10)

CLASS Test FROM CustomWebHandler

  EXPORTED:
    CLASS VAR aTime
    CLASS VAR aString
    CLASS VAR nCount

  EXPORTED:
    METHOD echo, getTime, echoVars, crash, echoHtml, initVars

ENDCLASS

* --------

METHOD Test:InitVars()

DEFAULT ::aTime   := {}
DEFAULT ::aString := {}
DEFAULT ::nCount  := 0

RETURN self

* --------

METHOD Test:echoVars()

LOCAL cResponse, aVars, i, oForm

::setErrorBlock()
BEGIN SEQUENCE

oForm := ::httpRequest:getForm()
aVars := oForm:getCollection()
cResponse := '<H1> Echoing all Vars </H1>'
cResponse += '<pre>' + CRLF

FOR i := 1 TO Len(aVars)
  cResponse += aVars[i,1] + ' : ' + aVars[i,2] + CRLF
NEXT

cResponse += '</pre>'

RECOVER USING cResponse
END SEQUENCE

RETURN cResponse

* --------

METHOD Test:echoHtml( cHtml )

DEFAULT cHtml := 'No Html exists'

RETURN MemoRead(cHtml)

//
// The method :get() handles the RESTful path CurrentTime/get
//
METHOD Test:getTime()

LOCAL cThreadId
LOCAL cResponse := ""

cThreadId := AllTrim(Str(ThreadId()))

::setErrorBlock()

BEGIN SEQUENCE

cResponse += "<!DOCTYPE html>"
cResponse += "<html>"
cResponse +=   "<head><title>"+ProcName()+"</title></head>"
cResponse +=   "<body>"
cResponse +=     "<h1>Current Time:</h1>"
cResponse +=     "<p>"+Time()+" (HH:MM:SS)</p>"
cResponse +=     "<small>By thread: "+cThreadId+"</small>"
cResponse +=   "</body>"
cResponse += "</html>"

RECOVER USING cResponse

END SEQUENCE

RETURN cResponse

* -----------

METHOD Test:crash()

LOCAL cResponse

::setErrorBlock()

BEGIN SEQUENCE

&('CrashThisProgram()')

RECOVER USING cResponse

END SEQUENCE

RETURN cResponse

* -----------

METHOD Test:echo( cParamName1 )

LOCAL cHtml := ""

::setErrorBlock()

BEGIN SEQUENCE

::initVars()

AAdd( ::aTime, Time() )
::nCount ++

cHtml += "<html><title>Test:echo() was called</title><body>"
cHtml += '<pre>'
cHtml += "Parameter cParamName1 is " + ::httpRequest:form:getCollection()[1,1] + CRLF
cHtml += DC_Array2String(::httpRequest:form:getCollection()) + CRLF
cHtml += 'Pcount is: ' + Alltrim(Str(Pcount())) + CRLF
cHtml += 'Query String is: ' + ::httpRequest:getQueryString() + CRLF
cHtml += 'aTime: ' + Var2Char( ::nCount ) + ' - '  + Var2Char( ::aTime ) + ' - ' + Var2Char( ::httpRequest:getQueryString() ) + '</br>'+ CRLF
cHtml += "</pre></body></html>"

RECOVER USING cHtml

END SEQUENCE

RETURN cHtml
The eXpress train is coming - and it has more cars.

Zdeno Bielik
Posts: 147
Joined: Thu Jan 28, 2010 9:24 am
Location: Nitra, Slovakia
Contact:

Re: echo example

#3 Post by Zdeno Bielik »

super, many thanks!
I will try it.

Zdeno Bielik
Posts: 147
Joined: Thu Jan 28, 2010 9:24 am
Location: Nitra, Slovakia
Contact:

Re: echo example

#4 Post by Zdeno Bielik »

Roger,

below are little modified your examples.
I defined some next variables and added it in some next methods.
There are some problems or some for me weird behaviors with visibility
and results of each types of defined variables.

If you run echo, getTime or echoVars,
variables are inicialized and result is displayed – ok.

Now try refresh web page, e.g. with press F5 or again from main menu call this or any other method
and you will see new results:
- in contents for variables defined with clause CLASS are added new values
and in variables without that clause is still new only last one value – OK......
For this variants I see cases/options where it will be used – OK...


BUT if you at once time open this same index.html file / examples on web-browser’s next tab-page
or in new standalone instance on the same pc/notebook
or from other pc/notebook,
it works in the same way...
- when you run it, in class variables will be total count from all opened instances
and not only number, how many times it was called from that concrete web-browser window...

Please, how must be defined variables or methods, if it should start fill array or sum/add number from begin
in every new open web-brower window on the same or from other workstation?

TIA & Regards

Zdeno



Code: Select all

#INCLUDE "dcdialog.CH"

#define CRLF Chr(13)+Chr(10)

CLASS Test FROM CustomWebHandler

  HIDDEN:
  CLASS VAR chaTime          // ClassHidden aTime
  CLASS VAR chaString
  CLASS VAR chnCount

  VAR haTime                 // Hidden aTime
  VAR haString
  VAR hnCount


  PROTECTED:
  CLASS VAR cpaTime          // ClassProtected aTime
  CLASS VAR cpaString
  CLASS VAR cpnCount

  VAR paTime                 // Protected aTime
  VAR paString
  VAR pnCount


  EXPORTED:
    CLASS VAR ceaTime        // ClassExported aTime
    CLASS VAR ceaString
    CLASS VAR cenCount

    VAR eaTime               // Exported aTime
    VAR eaString
    VAR enCount



  EXPORTED:
    METHOD echo, getTime, echoVars, crash, echoHtml, initVars

ENDCLASS


* --------


METHOD Test:InitVars()

DEFAULT ::chaTime   := {}
DEFAULT ::chaString := {}
DEFAULT ::chnCount  := 0

DEFAULT ::haTime   := {}
DEFAULT ::haString := {}
DEFAULT ::hnCount  := 0


DEFAULT ::cpaTime   := {}
DEFAULT ::cpaString := {}
DEFAULT ::cpnCount  := 0

DEFAULT ::paTime   := {}
DEFAULT ::paString := {}
DEFAULT ::pnCount  := 0


DEFAULT ::ceaTime   := {}
DEFAULT ::ceaString := {}
DEFAULT ::cenCount  := 0

DEFAULT ::eaTime   := {}
DEFAULT ::eaString := {}
DEFAULT ::enCount  := 0

RETURN self


*

METHOD Test:echo( cParamName1 )

LOCAL cHtml := ""

::setErrorBlock()

BEGIN SEQUENCE

::initVars()

AAdd( ::chaTime, Time() )
::chnCount ++

AAdd( ::haTime, Time() )
::hnCount ++

*

AAdd( ::cpaTime, Time() )
::cpnCount ++

AAdd( ::paTime, Time() )
::pnCount ++

*

AAdd( ::ceaTime, Time() )
::cenCount ++
AAdd( ::eaTime, Time() )
::enCount ++

*

cHtml += "<html><title>Test:echo() was called</title><body>"
cHtml += '<pre>'
cHtml += "Parameter cParamName1 is " + ::httpRequest:form:getCollection()[1,1] + CRLF
cHtml += DC_Array2String(::httpRequest:form:getCollection()) + CRLF
cHtml += 'Pcount is: ' + Alltrim(Str(Pcount())) + CRLF
cHtml += 'Query String is: ' + ::httpRequest:getQueryString() + '</br>' + CRLF

cHtml += '</br>'+ CRLF

cHtml += 'chaTime: ' + Var2Char( ::chnCount ) + ' - '  + Var2Char( ::chaTime ) + '</br>'+ CRLF
cHtml += 'haTime : ' + Var2Char( ::hnCount ) + ' - '  + Var2Char( ::haTime ) + '</br>'+ CRLF
cHtml += '</br>'+ CRLF

cHtml += 'cpaTime: ' + Var2Char( ::cpnCount ) + ' - '  + Var2Char( ::cpaTime ) + '</br>'+ CRLF
cHtml += 'paTime : ' + Var2Char( ::pnCount ) + ' - '  + Var2Char( ::paTime ) + '</br>'+ CRLF
cHtml += '</br>'+ CRLF

cHtml += 'ceaTime: ' + Var2Char( ::cenCount ) + ' - '  + Var2Char( ::ceaTime ) + '</br>'+ CRLF
cHtml += 'eaTime : ' + Var2Char( ::enCount ) + ' - '  + Var2Char( ::eaTime ) + '</br>'+ CRLF
cHtml += '</br>'+ CRLF

cHtml += "</pre></body></html>"

RECOVER USING cHtml

END SEQUENCE

RETURN cHtml

*

METHOD Test:echoVars()

LOCAL cResponse, aVars, i, oForm

::setErrorBlock()
BEGIN SEQUENCE

::initVars()

AAdd( ::chaTime, Time() )
::chnCount ++

AAdd( ::haTime, Time() )
::hnCount ++

*

AAdd( ::cpaTime, Time() )
::cpnCount ++

AAdd( ::paTime, Time() )
::pnCount ++

*

AAdd( ::ceaTime, Time() )
::cenCount ++
AAdd( ::eaTime, Time() )
::enCount ++

*

oForm := ::httpRequest:getForm()
aVars := oForm:getCollection()
cResponse := '<H1> Echoing all Vars </H1>'
cResponse += '<pre>' + CRLF

FOR i := 1 TO Len(aVars)
  cResponse += aVars[i,1] + ' : ' + aVars[i,2] + CRLF
NEXT

cResponse += '</br>'+ CRLF

cResponse += 'chaTime: ' + Var2Char( ::chnCount ) + ' - '  + Var2Char( ::chaTime ) + '</br>'+ CRLF
cResponse += 'haTime : ' + Var2Char( ::hnCount ) + ' - '  + Var2Char( ::haTime ) + '</br>'+ CRLF
cResponse += '</br>'+ CRLF

cResponse += 'cpaTime: ' + Var2Char( ::cpnCount ) + ' - '  + Var2Char( ::cpaTime ) + '</br>'+ CRLF
cResponse += 'paTime : ' + Var2Char( ::pnCount ) + ' - '  + Var2Char( ::paTime ) + '</br>'+ CRLF
cResponse += '</br>'+ CRLF

cResponse += 'ceaTime: ' + Var2Char( ::cenCount ) + ' - '  + Var2Char( ::ceaTime ) + '</br>'+ CRLF
cResponse += 'eaTime : ' + Var2Char( ::enCount ) + ' - '  + Var2Char( ::eaTime ) + '</br>'+ CRLF
cResponse += '</br>'+ CRLF

cResponse += '</pre>'

RECOVER USING cResponse
END SEQUENCE

RETURN cResponse

* ------------


//
// The method :get() handles the RESTful path CurrentTime/get
//
METHOD Test:getTime()

LOCAL cThreadId
LOCAL cResponse := ""

cThreadId := AllTrim(Str(ThreadId()))

::setErrorBlock()

BEGIN SEQUENCE

::initVars()

AAdd( ::chaTime, Time() )
::chnCount ++

AAdd( ::haTime, Time() )
::hnCount ++

*

AAdd( ::cpaTime, Time() )
::cpnCount ++

AAdd( ::paTime, Time() )
::pnCount ++

*

AAdd( ::ceaTime, Time() )
::cenCount ++
AAdd( ::eaTime, Time() )
::enCount ++

*

cResponse += "<!DOCTYPE html>"
cResponse += "<html>"
cResponse +=   "<head><title>"+ProcName()+"</title></head>"
cResponse +=   "<body>"
cResponse +=     "<h1>Current Time:</h1>"
cResponse +=     "<p>"+Time()+" (HH:MM:SS)</p>"
cResponse +=     "<small>By thread: "+cThreadId+"</small>"

cResponse += '</br>'+ CRLF

cResponse += 'chaTime: ' + Var2Char( ::chnCount ) + ' - '  + Var2Char( ::chaTime ) + '</br>'+ CRLF
cResponse += 'haTime : ' + Var2Char( ::hnCount ) + ' - '  + Var2Char( ::haTime ) + '</br>'+ CRLF
cResponse += '</br>'+ CRLF

cResponse += 'cpaTime: ' + Var2Char( ::cpnCount ) + ' - '  + Var2Char( ::cpaTime ) + '</br>'+ CRLF
cResponse += 'paTime : ' + Var2Char( ::pnCount ) + ' - '  + Var2Char( ::paTime ) + '</br>'+ CRLF
cResponse += '</br>'+ CRLF

cResponse += 'ceaTime: ' + Var2Char( ::cenCount ) + ' - '  + Var2Char( ::ceaTime ) + '</br>'+ CRLF
cResponse += 'eaTime : ' + Var2Char( ::enCount ) + ' - '  + Var2Char( ::eaTime ) + '</br>'+ CRLF
cResponse += '</br>'+ CRLF

cResponse +=   "</body>"
cResponse += "</html>"

RECOVER USING cResponse

END SEQUENCE

RETURN cResponse

* -----------

METHOD Test:crash()

LOCAL cResponse

::setErrorBlock()

BEGIN SEQUENCE

&('CrashThisProgram()')

RECOVER USING cResponse

END SEQUENCE

RETURN cResponse


*


METHOD Test:echoHtml( cHtml )

DEFAULT cHtml := 'No Html exists'

RETURN MemoRead(cHtml)

User avatar
rdonnay
Site Admin
Posts: 4729
Joined: Wed Jan 27, 2010 6:58 pm
Location: Boise, Idaho USA
Contact:

Re: echo example

#5 Post by rdonnay »

I think you will find that each connection runs in it's own thread.

You can try to build an array that holds session information.
The index into the array would be the ThreadID().

I'll try to find time to create a sample.
The eXpress train is coming - and it has more cars.

User avatar
rdonnay
Site Admin
Posts: 4729
Joined: Wed Jan 27, 2010 6:58 pm
Location: Boise, Idaho USA
Contact:

Re: echo example

#6 Post by rdonnay »

I think you will find that each connection runs in it's own thread.
I appear to be wrong about this.
I don't have an answer for this yet.

I tried to use cookies for session data but haven't been successful.
The eXpress train is coming - and it has more cars.

Zdeno Bielik
Posts: 147
Joined: Thu Jan 28, 2010 9:24 am
Location: Nitra, Slovakia
Contact:

Re: echo example

#7 Post by Zdeno Bielik »

Roger,

many thanks for now.
I hope you will find any solution/workaround soon,
because now web-app doesn‘t look like „multi-user“,
but like „single-user“ opened/accessed on more computers/browser windows.

Zdeno

Zdeno Bielik
Posts: 147
Joined: Thu Jan 28, 2010 9:24 am
Location: Nitra, Slovakia
Contact:

Re: echo example

#8 Post by Zdeno Bielik »

Hi Roger,

I have solution/workaround!
After next more tests I found that only REMOTE_IP value is unique for each device (computer/notebook...) - maybe I am wrong… but without complete Xbase++ docs I just try some things…

So, my idea was simple:
instead of defining/creating many variables I will just create dynamicaly one array with needed values for each device/session.
So, each device will hold in array e.g. RemoteIP address, Time of LogIn, array of all variables names and their values...

Just try run modified examples Test1 - Get the Time, Test1 - Echo some vars and Test1 – Echo on the main pc and from other pc/nb from network – works great/ok!
As you can see, I can set, get, compare values – just press f5 on the same pc or on other pc and see actual/changed contents of variables.
- variables are iniciaziled for each device in :InitVars() and then :AddIP(), and set/get valueas are done via :MySetValue() / :MyGetValue()
- I just first compare „remote-ip“ and then in array I look or set needed informations


Anyway, I have one little problem – I don’t know, how to define aRemoteIPs and aVariables,
that their contents will be shared between different methods
just now try run that methods from Test2 prg (only one difference against test1 is that CLASS is defined like Test2)
and you will see, that values are inicialized again…
I tried define “super class” MyTestVars and from it inherit that two values
and this use like parent class for classes Test1 and Test2 instead of direct CustomWebHandler,
but it looks like I cannot correctly define/inicialize/call it…

One workaournd may be join all methods/code from different classes in one prg file with one class,
but I don’t want do it in this way – I want have more single prg files.


Please, can you look at it?

From first tests it looks OK for me, because now it is “device-safe”,
so it is ”acceptable” solution for “net/web-using” for accessing from different devices,
because I already found, many other web-app for work in similar ways…
Yes, I know if it will be web-browser or web-browser’s tabpage safe on one device also,
it will be perfect, I can live with this for now.

Btw: what do you think about this my idea for holding needed informations in one big array for each device?
Do you like it? Or will be with this any possible problems?

Regards
Zdeno
Attachments
test08.zip
(5.71 KiB) Downloaded 761 times

User avatar
rdonnay
Site Admin
Posts: 4729
Joined: Wed Jan 27, 2010 6:58 pm
Location: Boise, Idaho USA
Contact:

Re: echo example

#9 Post by rdonnay »

Zdeno -

I am not sure yet that you should go down this path.
Session management should not be tied to an IP address or a device type.

For example, if you make a connection to the server from a browser on 2 different tabs, you will get the same session info.

I had a Skype call with Steffen yesterday. I pleaded with him to give us more documentation on HttpRequest() and HttpResponse().
He has promised to send me preliminary docs next week.

Meanwhile, I am going to use CXPINFO.CXP code to write a special method for CxpHttpServer that will return all this information the way CXPINFO.CXP does. This will help with understanding the environment.

Roger
The eXpress train is coming - and it has more cars.

User avatar
rdonnay
Site Admin
Posts: 4729
Joined: Wed Jan 27, 2010 6:58 pm
Location: Boise, Idaho USA
Contact:

Re: echo example

#10 Post by rdonnay »

Here is an updated CxpHttpServer.Prg.

This has a new method named CustomWebHandler:getInfo()

http://localhost:8080/test2/getInfo will return a page similar to CxpInfo.Cxp except it will not contain any Cxp Info.
It will contain Platform, Http Server Environment, Http Request Environment info only.
But this will tell you a lot.

Eventually, WebHandler will support the same features as CxpAbstractPage and CxpApplication. Steffen said that this is the plan.

For now, I'm still trying to figure out how to use cookies for session management with WebHandler.
I'm waiting for more documentation.

Until then, I use HIDDEN variables in FORMS to handle my session management.
Attachments
cxphttpserver.zip
(4.86 KiB) Downloaded 816 times
The eXpress train is coming - and it has more cars.

Post Reply