+2
Waiting for user's reply

Anyone here with a ready made HTTP-Server (TCP_Custom_Server) script?

Martin Lang 8 years ago in iRidium Script updated by a pivovarov 7 years ago 17

Is anyone here having a ready made script for a TCP_CUSTOM_SERVER that acts like a small HTTP-Server?


I thought i ask before i start writing my own...


Thx!


Martin

+1

here's a snippet i just wrote in a few minutes (not good coding), it is working you see the requested file/command in the log and you get a message back to the browser. the only thing i couldn't figured out was how to force the HTTPServer to send out the data to the client without disconnecting and reconnecting them. whats the way to disconnect a client that is connected to a CUSTOM_TCP_SERVER? 


var HTTPServer = IR.CreateDevice(IR.DEVICE_CUSTOM_SERVER_TCP, "HTTPServer",
   {Port: 80, MaxClients: 3});
var httpRequestMessage = "";
IR.AddListener(IR.EVENT_ONLINE,HTTPServer,function()
{
IR.Log("*** HTTP Server online");
});
IR.AddListener(IR.EVENT_OFFLINE,HTTPServer,function()
{
IR.Log("*** HTTP Server offline");
});
IR.AddListener(IR.EVENT_RECEIVE_TEXT, HTTPServer, function(text)
{
  
  httpRequestMessage = httpRequestMessage + text;
  
  var httpRequestEND = httpRequestMessage.indexOf('\r\n\r\n');
  
  var GETstartpos = httpRequestMessage.indexOf('GET ');
  
  if ((httpRequestEND != -1) && (GETstartpos != -1)) {
    
   httpRequestMessage = httpRequestMessage.slice(GETstartpos + 5);
   
   var GETendpos = httpRequestMessage.indexOf(' ');
   
   var httpRequest = decodeURIComponent(httpRequestMessage.slice(0,GETendpos));
   
   IR.Log("*** HTTP Request: " + httpRequest);
   
   httpRequestMessage = "";
   
   switch (httpRequest) {
   
      case 'favicon.ico' :
         HTTPServer.Send(['HTTP/1.1 302 Found\n']);
         HTTPServer.Send(['Location: http://www.iridiummobile.net/favicon.ico\n\n']);
      break;
      default:
         HTTPServer.Send(['HTTP/1.0 200 OK\n']);
         HTTPServer.Send(['Content-Type: text/plain\n\n']);
         HTTPServer.Send(['OK you requested: ' + httpRequest]);
      break;
      } 
   
   IR.SetTimeout(200,function()
   {
   HTTPServer.Disconnect();
   });
   
   IR.SetTimeout(400,function()
   {
   HTTPServer.Connect();
   });
   
   }
 
 });



So maybe the iRidium Team can help out with the Sending flush to the client!?


Ciao

 Martin

Thank you Martin! How to use it? What can we send to it and what to do then?

hi,


you see the switch-part? in that part you can do anything you want like:


   switch (httpRequest) {
   
      case 'favicon.ico':
         HTTPServer.Send(['HTTP/1.1 302 Found\n']);
         HTTPServer.Send(['Location: http://www.iridiummobile.net/favicon.ico\n\n']);
      break;
      case 'command1':
         IR.GetDevice("HDL-BUS Pro Network (UDP)").Set("Curtain1:Curtain Up/Stop/Down",1);
         IR.GetDevice("KNX Router (KNXnet/IP)").Set(1,"red"); 
         IR.GetDevice("BAOS").Set("BAOS Command",100);
         IR.GetDevice("Global Cache").Set("POWER ON","");
         IR.GetDevice("Sonos").Set("Play","");
         HTTPServer.Send(['HTTP/1.0 200 OK\n']);
         HTTPServer.Send(['Content-Type: text/plain\n\n']);
         HTTPServer.Send(['Your command was: ' + httpRequest]);
      break;
      default:
         HTTPServer.Send(['HTTP/1.0 200 OK\n']);
         HTTPServer.Send(['Content-Type: text/plain\n\n']);
         HTTPServer.Send(['OK you requested: ' + httpRequest]);
      break;
      } 

using it with a device that can make a http request, like a browser:

http://<IP_OF_IRIDIUMSERVER/command


like


http://192.168.1.100/command1


then you get back a short message "Your command was: command1" and all the action within the 'command1' switch part will be executed. But thats a really simple example, you can do whatever you want within your JS. you can send back html code too by changing the content-type to text/html.


Ciao

 Martin


Great job, it works! I've made an improvement which helps anyone to use it without getting to script source. You just send drivername, channel and value and it works!


//Control part: send http://[server address]/[driver name],[channel name],[value]
//ex. http://127.0.0.1/ModbusTCP,Channel1,1 
   part = httpRequest.split(",");
   IR.Log("payload: " +part[0] +" " +part[1] +" " +part[2]);
   IR.GetDevice(part[0]).Set(part[1], part[2]);


Here is the project example:

http_server_strings_control_example.sirpz


More improvements are welcome!

I just want to have some time and merge it with http send solution here: http://support.iridiummobile.net/topics/12577-http-command-with-added-dynamic-value-and-no-script/


Then we are going to have a full featured http send-receive example, which people without any script knowledge whould be ableto use

yes, but the iRidium team should please jump in and tell me how to finish the client connection (flushing out the buffered data) without disconnecting and reconnecting the driver itself. it works, but thats a little buggy to me.

Maybe you must join data to one request.

and

HTTP/1.0 defines the octet sequence CR LF as the end-of-line marker for all protocol elements
except the Entity-Body (see Appendix B for tolerant applications). The end-of-line marker
within an Entity-Body is defined by its associated media type, as described in Section 3.6.
HTTPServer.Send(['HTTP/1.0 200 OK\r\nConnection: keep-alive\r\nContent-Type: text/plain\r\n\r\nOK you requested: ' + httpRequest]);

use Wireshark to understand what happens


http://www.cs.columbia.edu/~jae/3157/files/overview-sockets-http.pdf
HTTP 1.1 is better for persist connections

Tried it, doesn't make any changes. Looks like the data is buffered and not sent out directly. When you delete the part:

IR.SetTimeout(200,function()
   {
   HTTPServer.Disconnect();
   });
  
   IR.SetTimeout(400,function()
   {
   HTTPServer.Connect();
   });

You get no answer in the browser until you restart the iridiumserver or upload a new project. On a normal Webserver, the Server closes the connection to the client when the data was sent out. So, how to close a Client connection?

Checked it again with wireshark. The iRidium Server does not send out any data when you call the .Send Method, until you kill the device. Thats a bug i think. iRidium Team?

This topic is discussed by integrators. The Iridium team does not recommend such a solution (it is extremely
unsafe) and requires a good understanding of network interaction, so we do not participate in this discussion.

i understand your side for a http server. you don't have to comment.


but to use the normal tcpserver, why is the .send method not working?

Martin, it works! Try create empty project and send date from web-interface:


var TCPConnection = IR.CreateDevice(IR.DEVICE_CUSTOM_SERVER_TCP, "TCPConnection", "", 55555, 5, IR.BACKGROUND_OFF);
var string = 0;


IR.AddListener(IR.EVENT_RECEIVE_TEXT, TCPConnection, function(text, client_id,  host, port, host_ip, host_port) {
   if(text == "Answer")
      IR.Log("EVENT_RECEIVE_TEXT " + text);
});


function fnStr(in_Type, in_Name, in_Value) {
   IR.Log("Set string to " + in_Value)
   string = in_Value;
}



function fnSend(in_Type, in_Name, in_Value) {
   IR.Log("Send text " + string);
   TCPConnection.Send([string]);
}


+1

@Aleksandr,


I have attempted to get the response to work also, but have the same experience as Martin in the efforts i have made.


For full reference this is the code which I have been testing from, its a ready to run script, just paste and go :). 


You will observe the issues at the end of the Switch Statement in the IR.EVENT_RECEIVE_TEXT anon func.  Any guidance on how to solve this would be appreciated. 


I also understand your security concerns, so this is at my own risk - fully accepted.



//
// Iridium TCPServer Feature
//

// Connction Details 
//    TCP Listener
//        Port - Based on the Port Allocated in the driver
//        Clients - Max Clients based on the count set in the driver

// Feedback
//    Currently the server is sending HTTP feedback, but only after a forced
//    Disconnect and Reconnect, as mentioned in the support thread
//    http://support.iridiummobile.net/topics/12528-anyone-here-with-a-ready-made-http-server-tcp_custom_server-script/#

var TCPServer = function(DriverName)
{
   //-------------------------------------------------------
   // Driver Data
   //------------------------------------------------------- 
   this.DriverName = DriverName;
   this.device;
   this.Online = false;
   
   this.Msg = ""; // Variable storing the command for sending to the device 
   this.error = false; // Error flag
 
   var that = this; // Receiving the link to the object for its using inside the function
   
   function initialization() // Initialization method of the class instance
   {
      var port = 8088;
      var max_clients = 10;
      this.device = IR.CreateDevice(IR.DEVICE_CUSTOM_SERVER_TCP, this.DriverName, "", port, max_clients, IR.BACKGROUND_OFF);
      this.device.Connect();

      that = this; // Receiving the link to the object for its using inside the function
  
      //-------------------------------------------------------
      // Device Online
      //-------------------------------------------------------     
      IR.AddListener(IR.EVENT_ONLINE, that.device, function(text)
      { 
         IR.Log("[" + that.DriverName + "] DEVICE is Online"); // Write to the log that the device is Online
         that.Online = true; // Assign the true value to the that.Online variable
      }, that);

      //-------------------------------------------------------
      // Device Offline
      //-------------------------------------------------------
      IR.AddListener(IR.EVENT_OFFLINE, that.device, function(text)
      { 
         IR.Log("[" + that.DriverName + "] DEVICE is Offline"); // Write to the log that the device is Offline
         that.Online = false; // Assign the false value to the that.Online variable
      }, that);  
      
      
      //-------------------------------------------------------
      // Device Registered
      //-------------------------------------------------------
      IR.AddListener (IR.EVENT_ACCEPT, that.device, function (text)   
      {  
          IR.Log("[" + that.DriverName + "] DEVICE is Listening"); // Write to the log that the device is Offline
      }, that);
   
      //-------------------------------------------------------
      // Receive Text
      //-------------------------------------------------------
      IR.AddListener(IR.EVENT_RECEIVE_TEXT, that.device, function(text, client_id, host, port, host_ip, host_port)
      {
         IR.Log("[" + that.DriverName + "] Text Received: " + text);  // Output the received data in the log       
 
         // Parser
         var reqEnd = text.indexOf('\r\n\r\n');
         var reqGET = text.indexOf('GET ');
  
         if ((reqEnd != -1) && (reqGET != -1)) {
            req = text.slice(reqGET + 5);
            var httpRequest = decodeURIComponent(req.slice(0,req.indexOf(' ')));
            IR.Log("[" + that.DriverName + "] HTTP Request: " + httpRequest);  // Output the received data in the log
            //ex. http://127.0.0.1/ModbusTCP,Channel1,1 
            part = httpRequest.split(",");
            IR.Log("[" + that.DriverName + "] HTTP Request Payload: Driver[" + part[0] + "] Channel [" + part[1] +"] Value [" + part[2] + "]");  // Output the received data in the log
            
            
            switch (part[0]) {
               case 'favicon.ico' :
                  that.device.Send(['HTTP/1.1 302 Found\n']);
                  that.device.Send(['Location: http://www.iridiummobile.net/favicon.ico\n\n']);
                  break;
      
               case 'sonos':
                  //IR.GetDevice("Sonos").Set("Play","");
                  IR.GetDevice(part[0]).Set(part[1], part[2]);
                  that.device.Send(['HTTP/1.0 200 OK\n']);
                  that.device.Send(['Content-Type: text/plain\n\n']);
                  that.device.Send(['Your command was: ' + httpRequest]);
                  break;
                  
               case 'notify':
                  // Send Push Notification
                  IR.SendPush("Call from "+part[0], part[0], 1, this, catchError, 1);   
                  IR.Log("[" + that.DriverName + "] Notification Sent: " + part[0]);  // Output the received data in the log
                  that.device.Send(['HTTP/1.0 200 OK\n']);
                  that.device.Send(['Content-Type: text/plain\n\n']);
                  that.device.Send(['OK you requested: ' + httpRequest]);
                  break;
                  
               default:
                  that.device.Send(['HTTP/1.0 200 OK\n']);
                  that.device.Send(['Content-Type: text/plain\n\n']);
                  that.device.Send(['OK you requested: ' + httpRequest]);
                  break;
            }          
   
            IR.SetTimeout(200,function()
            {
               that.device.Disconnect();
            });
   
            IR.SetTimeout(400,function()
            {
               that.device.Connect();
            });
            
         }
         
      }, that);
      

   }
   
    function catchError(object)
    {
      IR.Log("[" +  that.DriverName + "] Error: " + object.Error + ' - ' + object.ErrorDescription);
    }

   //-------------------------------------------------------
   // Public
   //-------------------------------------------------------
   this.Init = initialization; // Make the initialization function public
}


// Create the Instance of the driver with the following sample
var myTCPServer = new TCPServer('Sky Q');

myTCPServer.Init();


I have been working on this a little more, running on the Server (not client), and this TCP connection, even with the demo code is not working as suggested.


Can we have this reopened for investigation, as this does feel like a bug.

thanks

Damian

Waiting for user's reply

So i have spend a couple of hours today with this issue, and also trying to determine is i might have some alternative options. 


The objective is simple, I need to have an external service trigger a variable/tag on the iridium server. I am not locked to a HTTP server implementation; I was looking at the potential to use MQTT as an option - have not had any luck on the back channel.


In any case, my HTTP server code works only when i force close the device; but this upsets the sender as it may not have had a response back with a HTTP 200/400 etc in the session.


I have purchased the Iridium Gate; does this open any additional options, as by design this device sends JSON and accepts JSON from the internet hosted services - exactly what i need!


cheers

Damian

I wrote an http-driver that works like an http module in Node JS. We use it to exchange data between Iridium Clients and Iridium server as the standard mechanism feedback and commands is not always convenient for us. Also we use it to provide data exchange with other systems. So we can generate JSON that can be read by other services.


Example of a server in Iridium:

http.createServer(function (req, res) {
   res.writeHead(200, {'Content-Type' : 'text/plain'});
   res.write('Hello World!');
   res.end();
}).listen(8000);


Example of an http request in Iridium:

var options = {
    port: 8000,
    hostname: '127.0.0.1',
    path: '/',
    method: 'GET'
};
var req = http.request(options, function (res) {
    res.on('data', function (chunk) {
        IR.Log(chunk)
    });
    res.on('end', function () {
       IR.Log('end');
    });
});
req.end();