Game Development Community

dev|Pro Game Development Curriculum

TorqueScript Replacement to HTTPObject

by David Higgins · 02/06/2007 (6:19 pm) · 38 comments

UPDATE: httpPage now supports basic "POST" using either a URL-Encoded Query String style or a SimSet that contains SimObjects with "key" and "value" properties.

-- ORIGINAL POST (with updated code for POST support) --

This is pretty simple, you just instantiate a copy of it like so:

new TCPObject(httpPage) { };
httpPage.get("http://gearworxprod.com/index.php");
echo(httpPage.getResult());

Now, of course, the 'getResult' has to be called after the page has actually been retrieved -- I've left this up to the developer using the code, I put a skeleton 'onDisconnect' method for TCPObject in the code, just fill in the blanks :)

Here's the code:

$MAX_HTTP_QUERY_STRING = 255;

[b]
function httpPage::init(%this, %url) {
   %host = "";
   %page = "";

   if(strpos(%url, "http://") == 0)
   {
      %host = getSubStr(%url, 7, strpos(%url, "/", 8) - 7);
      %page = getSubStr(%url, strpos(%url, "/", 8), $MAX_HTTP_QUERY_STRING);
   }
   else
   {
      %host = getSubStr(%url, 0, strpos(%url, "/", 8));
      %page = getSubStr(%url, strpos(%url, "/"));
   }

   if(strpos(%host, ":") < 0) %host = %host @ ":" @ "80";
   %this.Address = %host;
   %this.Page = %page;
}
[/b]

function httpPage::get(%this, %url)
{
   %this.Buffer = "";
   %this.doBuffer = false;


   [b]%this.init(%url);[/b]
   warn("Connecting to: " @ %this.Address @ %this.Page);
   [b]%this.Method = "GET";[/b]
   %this.connect(%this.Address);
}

[b]
function httpPage::post(%this, %url, %data)
{
  %this.Data = "";
  if(isObject(%data)) {
    warn("Data is Object: true");
    for(%x = 0; %x < %data.getCount(); %x++) {
      %datum = %data.getObject(%x);
      if(strlen(%postData) > 0) %postData = %postData @ "&";
      %this.Data = %datum.key @ "=" @ %datum.value;
    }
  } else {
    warn("Data is Object: false");
    %this.Data = %data;
  }


  error("Data: " @ %this.Data);
  error("%data: " @ %data);

  %this.init(%url);
  warn("Connecting to: " @ %this.Address @ %this.Page);
  %this.Method = "POST";
  %this.connect(%this.Address);
}
[/b]

function httpPage::onConnected(%this)
{
   warn("Connected ...");
[b]
   %query = %this.Method @ " " @ %this.page @ " HTTP/1.0\nHost: " @ %this.Address;
   if(%this.Method $= "POST") {
     %query = %query @ "\n" @ "Content-Type: application/x-www-form-urlencoded\n";
     %query = %query @ "Content-Length: " @ strlen(%this.Data) @ "\n\n";
     %query = %query @ %this.Data @ "\n";
   } else {
     %query = %query @ "\n\n";
   }
   warn("QUERY: " @ %query);
[/b]
   %this.send(%query);
}

function httpPage::onLine(%this, %line)
{
   warn("LINE: " @ %line);
   if(!%this.doBuffer && %line $= "") { %this.doBuffer = true; return; }
   if(%this.doBuffer)
   {
      error("BUFFER: " @ %line);
      if(%this.Buffer !$= "") %this.Buffer = %this.Buffer @ "\n";
      %this.Buffer = %this.Buffer @ %line;
   }
}

function httpPage::getResult(%this)
{
   return %this.Buffer;
}

function httpPage::onDisconnect(%this)
{
  warn("Disconnected: " @ %this.Address);
}

function httpPage::onConnectFailed(%this)
{
   error("Connection Failed: " @ %this.Address);
}

UPDATE: To pass parameters to your page, you can just build out a 'Query String' like you normally would for a regular browser call ("http://site.com/page.php?var1=val1&var2=val2"). One thing to note, is that 'httpPage' uses a TCP Socket to communicate with the server, and is not intelligent enough to make 'improper' url's 'proper' -- what does this mean? It means ... make sure "spaces" are "%20" and other things ... you can refer to the HTTP RFC for what is a valid request and what is not, 'httpPage' conforms to 'HTTP/1.0' requests (you can make it 1.1 if you want, by changing 'HTTP/1.0' to 'HTTP/1.1' in the 'onConnected' function, if you for some reason need extra features ... You can also, if you want ... extend it to support more then just 'get' by just, again, reading the HTTP RFC and implementing the POST or PUT, etc actions on your own. I may flesh this out to support POST eventually (maybe soon, ;p).


I put this together, using some of the code from my Forum/Blog GUI Kit I started working on a few months back, for a poster in the TGB Private Forums -- as this is TGE, TGB and TGEA friendly, I figured I'd post a .plan and share it for anyone else who may be struggling with HTTP Connectivity -- I leave the idea of "what to do with the http data" up to you ... :)

Page«First 1 2 Next»
#21
03/19/2008 (9:07 pm)
Ok -- I just posted an update to the main post, which updates the httpPage "class".

I used the following code to test this, so please, let me know if you run into any problems:

test.cs
cls();

exec("./httpPage.cs");
new TCPObject(httpPage);


$url[0] = "http://projects.zoulcreations.com/httpPage/get.php";
$url[1] = "http://projects.zoulcreations.com/httpPage/get.php?foo=bar";
$url[2] = "http://projects.zoulcreations.com/httpPage/get.php?foo=message%20text";


function httpPage::onDisconnect(%this)
{
  warn("Disconnected: " @ %this.Address);
  next();
}

$urlNbr = 0;

function next() {
  warn("next(): " @ $urlNbr);
  httpPage.get($url[$urlNbr]);
  $urlNbr = $urlNbr + 1;
}

httpPage.post("http://projects.zoulcreations.com/httpPage/post.php?method=post", "foo=bar&msg=message%20text");

I simply exec("./test.cs") in my game.cs file and checked the console output. As you can tell, I did -NOT- test the SimSet functionality, I'm just "assuming" it works -- :)

The URL's in this are valid, and your welcome to use them for yourself to test this functionality out -- get.php simply spits back the query-string to you, and post.php spits out both the query-string AND the post data.

post.php
<b>POST DATA</b>
<?
foreach($_GET as $k=>$v) {
	echo "G: <b>$k</b>: $v";	
}
foreach($_POST as $k=>$v) { 
	echo "P: <b>$k</b>: $v";
}
?>

get.php
<?
foreach($_GET as $k=>$v) {
	echo "<b>$k</b>: $v";
}
?>

#22
05/08/2008 (1:19 am)
I've copied the code in News.cs and exec it in client/init.cs but it doesn't work for me.
I'm using TGEA 1.7

console:
==>new TCPObject(httpPage) { };

httpPage.get("http://www.garagegames.com/index.php?sec=mg&mod=resource&page=view&qid=12262");

echo(httpPage.getResult());
Connecting to: www.garagegames.com:80/index.php?sec=mg&mod=resource&page=view&qid=12262

Error connecting to www.garagegames.com: No error
Connection Failed: www.garagegames.com:80
#23
05/31/2008 (2:05 pm)
I am also getting errors that look like:

Error connecting to www.garagegames.com: No error
Connection Failed: www.garagegames.com:80

and I also using TGEA 1.7. Has anyone used this resource under 1.7 with success?
#24
05/31/2008 (2:36 pm)
Can anyone confirm that 1.7's TcpObject is the same as previous versions? Perhaps there were some bug fixes, or alterations to the way TcpObject works in 1.7 that are breaking this?

Also, can anyone experiencing issues with 1.7 confirm that it does in fact work with previous versions and 1.7 is the issue?

#25
05/31/2008 (5:43 pm)
I went back to a very old build on a game I left stranded back in early 2007, think it was TGEA 1.01? I am not even sure, lol. Anyway, it was the only older version of the engine I apparently still have laying around that was available to test immediately. I installed httpPage and succesfully connected to my webserver and executed a get() during startup before the client promptly crashed... hehe, don't know or care why it crashed. Anyway, the test you requested was whether this is a 1.7 issue, I can't confiirm it definitively, but this test does eliminate a lot of operator error possibilities on my part and suggests that 1.7 might be the culprit.
#26
06/01/2008 (12:37 am)
thanks for the quick and prompt test -- seems they probably fixed a bug, or introduced one ... in the 1.7 (or slightly previous?) code ... I'll just have to simply say "This is apparently not supported in TGE/A 1.7" :)

#27
09/14/2008 (3:00 pm)
Here is something weird that I hope someone can shed some light on. I am using TGE 1.5.2 and had this resource working great on my Vista box (if you can imagine). Well I got sick of Vista and went back to XP and suddenly the resource no longer works. Basically what I am doing is "GET" to a web page which emitts xml. Works great when I navigate to it manually but when I use this resource the connection terminates before the xml is sent. Here is what I am getting...
LINE: HTTP/1.1 200 OK
LINE: Server: Microsoft-IIS/5.1
LINE: Date: Sun, 14 Sep 2008 21:54:56 GMT
LINE: X-Powered-By: ASP.NET
LINE: X-AspNet-Version: 2.0.50727
LINE: Cache-Control: private
LINE: Content-Type: text/xml
LINE:

Disconnected: mattxp:80


The xml should be coming in after the blank line but instead the server just disconnects. Anyone else seen this behavior?
#28
09/29/2008 (7:15 am)
from the looks of it ... your HTTP response headers are missing the Content-Length ... which, if I remember correctly, means that your response is ended ... so, seems like it's a server-side issue, not related to httpPage ...

The server is just sending over a response header, and then disconnecting ... most likely, because there is no content to send over. Validate this with your page making the same request with HttpWebRequest in .NET or something ...
#29
10/16/2009 (3:28 am)
This resource is awesome.
Thanks for posting it.
I wish I had found this earlier.

TGEA 1.8.1
#30
10/19/2009 (11:21 am)
Hi David,

That's an excellent resource and I found it very useful.
I have a question for you.
You have mentioned that one should only call the httpPage::getResult after knowing that onDisconnect has executed.
To do this, I am using the following code.

$message_received = 0;
function httpPage::getResult(%this)
{
while ($message_received == 0) { }
message_received = 0;
return %this.Buffer;
}

function httpPage::onDisconnect(%this)
{
warn("Disconnected: " @ %this.Address);
$message_received = 1;
}

But when I call httpPage.getResult() method, it goes to infinite loop because of "while". can you give me an idea on how to do this?
#31
10/19/2009 (11:45 am)
Basically, your supposed to call 'getResult' in the onDisconnect.

httpPage::onDisconnect(%this) {
warn("Disconnected: " @ %this.Address);
%results = %this.getResult();
// DO SOMETHING HERE WITH THE RESULTS
}


Check the example above in the post, where I referenced 'test.cs' for my own tests ... 'onDisconnect' is where I decide what to do after the data is retrieved.

TorqueScript is not 'multi-threaded' and so your code would not work because you are calling 'getResult' before 'onDisconnect' is called ... meaning that 'onDisconnect' is never executed (because getResult is in an infinite loop).

There are a number of ways you could handle this ... you could:

make the request in some form of handler code for a user action (ie; button click, etc) or some form of game event (ie; game over, level start, etc) and then

a) setup a timer to check if disconnect has occurred - if so, then call getResults
b) extend the httpPage class and give it an action to perform after onDisconnect -- essentially wiring up your own 'event system'. I did this a while back, but never updated the resource here ... basically, I set it up so that my httpPage could turn around and call an object with a named method and pass it the results ... the called method on the object would then perform any other additional updates, etc to inform the user or the game that something 'happened'.

I haven't done any TorqueScript in quite some time, so I'd be extremely rusty ... but, if necessary, I could probably rewrite the 'event driven' bits again (might take a few days though).
#32
10/20/2009 (5:00 pm)
Thanks David.
As I have less time, I am going with putting my required functionality in the onDisconnect method.

Thanks for the other ideas. I feel they might be useful in my course of work with Torque.
#33
10/20/2009 (5:51 pm)
no prob - let me know if you have any other problems or questions.
#34
11/19/2009 (7:22 pm)
This is pretty great!

I do have a question: how might one go about encoding the binary data of a jpg to send with the request?

I have a notion that I'll build in some functionality that allows beta testers to bring up a text input, type a message, and have that message+game state data+current screenshot go to a webservice that logs the report. I know how to handle everything but the screen shot. Any ideas?
#35
11/19/2009 (7:51 pm)
don't think that's possible ... at least without some form of additional functionality being coded in to allow you to gain access to the binary bytes of the JPEG ... and even then, I don't know if TorqueScript would support that in 'string' format or not ... if not, you'd have to encode the bytes first ... then send them over encoded (takes up more space ...) and then decode them on the other end ... not the greatest of solutions.
#36
05/03/2010 (2:10 pm)
Hey there David,

I'm trying to do this on iT2D so that my application can update to Twitter.
Basically I'm using your two scripts, but I have a field where the user enters his/her password and it passes the post to

http://USERNAME:PASSWORD@twitter.com/statuses/update.xml

which is a hook to the HTTP contained in the twitter API.

This doesn't seem to work..
And to verify, I copy and pasted your code exactly, which also doesn't seem to work.

It returns the following..

Data is Object: false
Data: foo=bar&msg=message%20text
%data: foo=bar&msg=message%20text
Connecting to: projects.zoulcreations.com:80/httpPage/post.php?method=post

at which point nothing further happens.

Any thoughts on what might be causing this?

Thanks
#37
05/04/2010 (6:21 am)
To be honest, I'm not entirely sure whether or not this code is even valid in the latest Torque code bases ... I've not really touched Torque in quite some time, been too busy with other things as of late. Could someone else verify that the code does not work properly in the latest code base ... if not, I'll update it to work.
#38
05/19/2011 (2:48 pm)
Hello,

I was using this code here to download text files, and I am wondering, is it possible to download an image file? I was trying and getting very weird results...

Glenn
Page«First 1 2 Next»