Server that executes RPC commands through HTTP.

Dependencies:   EthernetInterface mbed-rpc mbed-rtos mbed

Import programHTTP-Server

Server that executes RPC commands through HTTP.

Overview

This program is a small HTTP Server that can execute RPC commands sent through HTTP and sends back a reply. This reply can be either a line of text or an HTML page.

HTTP Request

The server will only read the first line of the HTTP header. It does not check that the header is correct and it does not attempt to read the body. Hence, the server is only interested in the type of the request and the path. Instead of requesting a file, the path contains the RPC command.

RPC command encoding

Information

The RPC command must be encoded in a special way because no spaces are allowed in the path.

Thus, the RPC command is encoded in this way :

/<obj name or type>/<func name>?arg1_name=arg1_val&arg2_name=arg2_val

or, if the RPC command does not have any arguments :

/<obj name or type>/<func name>

So, a complete URL might be :

http://10.2.200.116/led2/write?val=1

The name of the arguments does not appear in the final RPC command. So these 3 urls will do exactly the same thing :

http://10.2.200.116/led2/write?val=1
http://10.2.200.116/led2/write?test=1
http://10.2.200.116/led2/write?blabla=1

Also, the order of the arguments are preserved.

Request handlers

To process requests, the server relies on RequestHandler. Each RequestHandler is assigned to a request type. Each type of request is assigned to a certain role :

  • PUT requests to create new objects
  • DELETE requests to delete objects
  • GET requests to call a function of an object

However, there is a RequestHandler that accepts only GET requests but it can create/delete/call a function. This was necessary to provide an interactive web page that allows creation and deletion of objects.

Reply

The reply depends on the formatter. Currently, three formatters are available :

  • The most basic one does not modify the RPC reply. Hence, if you consider sending request from python scripts, this formatter is the most appropriate one.
  • A simple HTML formatter will allow the user to view the RPC reply and a list of RPC objects currently alive from a browser.
  • Finally, a more complex HTML formatter creates an entire web page where the user can create and delete an object as well as calling functions on these objects.

Configure the server

The configuration of the server consists on choosing the formatter and adding one or more request handlers to the server. The main function initializes the server in order to produce HTML code and to receive data only using GET requests. If you want to use a simpler and different version of the server you can change the content of the main function (located in main.cpp) by this code :

main

    RPCType::instance().register_types();    

    EthernetInterface eth;
    eth.init();
    eth.connect();
    printf("IP Address is %s\n", eth.getIPAddress());
    
    HTTPServer srv = create_simple_server();

    if(!srv.init(SERVER_PORT))
    {
        printf("Error while initializing the server\n");
        eth.disconnect();
        return -1;
    }

    srv.run();

    return 0;

However, this configuration will not work with the following examples.

Examples

I assume that the server is using the InteractiveHTMLFormatter (which should be the case if you did not make any changes).

Using a browser

Here is a quick guide how to run this program :

  1. Compiles this program and copies it to the mbed
  2. Open TeraTerm (install it if you don't have it), select serial and choose the port named "mbed Serial Port"
  3. Reset your mbed
  4. The IP address should appear in teraterm. In this example, I will use 10.2.200.116.
  5. Open your browser and go to http://10.2.200.116/.
  6. If everything is ok, you should see a webpage.

Now, let's switch on a led. First, we need to create an object to control a led :

Then, let's write an RPC command to switch on led :


Using python

This program creates and switches on led2.

Sending RPC commands over HTTP with Python

import httplib

SERVER_ADDRESS = '10.2.200.38'

h = httplib.HTTPConnection(SERVER_ADDRESS)
h.request("GET", "/DigitalOut/new?arg=LED2&name=led2")
r = h.getresponse()
print r.read()

h.request("GET", "/led2/write?arg=1")
r = h.getresponse()
print r.read()

h.close()

Of course, you might have to change the server address in order to make it work.

Committer:
feb11
Date:
Thu Jul 18 10:10:14 2013 +0000
Revision:
10:8b4c3d605bf0
Parent:
9:a9bf63017854
Improved javascript code

Who changed what in which revision?

UserRevisionLine numberNew contents of line
feb11 0:9e4bcb10b3e3 1 #include "Formatter.h"
feb11 0:9e4bcb10b3e3 2 #include "mbed.h"
feb11 0:9e4bcb10b3e3 3 #include "RPCObjectManager.h"
feb11 0:9e4bcb10b3e3 4 #include "EthernetInterface.h"
feb11 0:9e4bcb10b3e3 5
feb11 0:9e4bcb10b3e3 6 const char *SIMPLE_HTML_CODE = "\
feb11 0:9e4bcb10b3e3 7 <!DOCTYPE html>\
feb11 0:9e4bcb10b3e3 8 <html>\
feb11 4:624527ebc0fa 9 <head>\
feb11 4:624527ebc0fa 10 <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\
feb11 4:624527ebc0fa 11 <title>TCP Server</title>\
feb11 4:624527ebc0fa 12 </head>\
feb11 4:624527ebc0fa 13 <body>";
feb11 0:9e4bcb10b3e3 14
feb11 0:9e4bcb10b3e3 15
feb11 0:9e4bcb10b3e3 16 const char* INTERACTIVE_HTML_CODE_1 = "\
feb11 4:624527ebc0fa 17 <!DOCTYPE html> \
feb11 0:9e4bcb10b3e3 18 <html>\
feb11 4:624527ebc0fa 19 <head>\
feb11 4:624527ebc0fa 20 <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\
feb11 4:624527ebc0fa 21 <title>TCP Server</title>\
feb11 4:624527ebc0fa 22 <script type=\"text/javascript\">\
feb11 4:624527ebc0fa 23 var ip = \"%s\";\
feb11 4:624527ebc0fa 24 function submitCreateForm()\
feb11 4:624527ebc0fa 25 {\
feb11 4:624527ebc0fa 26 var list = document.getElementById(\"type\");\
feb11 4:624527ebc0fa 27 var type = list.options[list.selectedIndex].value;\
feb11 4:624527ebc0fa 28 var name = document.getElementById(\"name\").value;\
feb11 10:8b4c3d605bf0 29 if(name === \"\") \
feb11 10:8b4c3d605bf0 30 return;\
feb11 4:624527ebc0fa 31 var arg = document.getElementById(\"arg\").value;\
feb11 4:624527ebc0fa 32 var url;\
feb11 6:d03e189ebbfe 33 if(arg === \"\") url = \"http://\" + ip + type + \"new?name=\" + name;\
feb11 6:d03e189ebbfe 34 else url = \"http://\" + ip + type + \"new?arg=\" + arg + \"&name=\" + name;\
feb11 4:624527ebc0fa 35 location.href= url;\
feb11 4:624527ebc0fa 36 }\
feb11 4:624527ebc0fa 37 function submitCallFuncForm()\
feb11 4:624527ebc0fa 38 {\
feb11 4:624527ebc0fa 39 var command = document.getElementById(\"command\").value;\
feb11 10:8b4c3d605bf0 40 if(command === \"\") \
feb11 10:8b4c3d605bf0 41 return; \
feb11 4:624527ebc0fa 42 var tmp = command.split(\' \');\
feb11 4:624527ebc0fa 43 var url = tmp[0];\
feb11 4:624527ebc0fa 44 if(tmp.length > 1)\
feb11 4:624527ebc0fa 45 url += \"?\";\
feb11 4:624527ebc0fa 46 for(var i = 1; i < tmp.length; ++i)\
feb11 4:624527ebc0fa 47 {\
feb11 4:624527ebc0fa 48 url += \"arg\" + i + \"=\" + tmp[i];\
feb11 4:624527ebc0fa 49 if(i+1 < tmp.length)\
feb11 4:624527ebc0fa 50 url += \"&\";\
feb11 4:624527ebc0fa 51 }\
feb11 4:624527ebc0fa 52 location.href = url;\
feb11 4:624527ebc0fa 53 }\
feb11 4:624527ebc0fa 54 </script>\
feb11 4:624527ebc0fa 55 </head> \
feb11 0:9e4bcb10b3e3 56 <body>";
feb11 0:9e4bcb10b3e3 57
feb11 0:9e4bcb10b3e3 58 const char* INTERACTIVE_HTML_CODE_2 = "<h3>Create Object :</h3>\
feb11 9:a9bf63017854 59 <form>\
feb11 4:624527ebc0fa 60 Type: <select id=\"type\">\
feb11 4:624527ebc0fa 61 <option value=\"/DigitalOut/\">DigitalOut</option>\
feb11 4:624527ebc0fa 62 <option value=\"/DigitalIn/\">DigitalIn</option>\
feb11 4:624527ebc0fa 63 <option value=\"/DigitalInOut/\">DigitalInOut</option>\
feb11 4:624527ebc0fa 64 <option value=\"/PwmOut/\">PwmOut</option>\
feb11 4:624527ebc0fa 65 <option value=\"/Timer/\">Timer</option>\
feb11 4:624527ebc0fa 66 </select><br>\
feb11 4:624527ebc0fa 67 name: <input type=\"text\" id=\"name\"><br>\
feb11 4:624527ebc0fa 68 arg(optional): <input type=\"text\" id=\"arg\">\
feb11 4:624527ebc0fa 69 <p><input type=\"button\" value=\"Create\" onclick=\"javascript:submitCreateForm();\"></p>\
feb11 4:624527ebc0fa 70 </form> \
feb11 4:624527ebc0fa 71 \
feb11 4:624527ebc0fa 72 <h3>Call a function :</h3>\
feb11 4:624527ebc0fa 73 <p>Enter an RPC command.</p>\
feb11 9:a9bf63017854 74 <form>\
feb11 10:8b4c3d605bf0 75 Command: <input type= \"text\" id=\"command\" maxlength=127><br>\
feb11 9:a9bf63017854 76 <p><input type=\"button\" value=\"Send\" onclick=\"javascript:submitCallFuncForm();\"></p><br>\
feb11 4:624527ebc0fa 77 </form>\
feb11 4:624527ebc0fa 78 </body> \
feb11 0:9e4bcb10b3e3 79 </html>";
feb11 0:9e4bcb10b3e3 80
feb11 0:9e4bcb10b3e3 81 static char chunk[1024];
feb11 0:9e4bcb10b3e3 82
feb11 0:9e4bcb10b3e3 83 Formatter::Formatter(int nb):
feb11 0:9e4bcb10b3e3 84 currentChunk(0),
feb11 0:9e4bcb10b3e3 85 nbChunk(nb)
feb11 0:9e4bcb10b3e3 86 {
feb11 0:9e4bcb10b3e3 87 }
feb11 0:9e4bcb10b3e3 88
feb11 0:9e4bcb10b3e3 89 char* Formatter::get_page(char *reply)
feb11 0:9e4bcb10b3e3 90 {
feb11 0:9e4bcb10b3e3 91 chunk[0] = '\0';
feb11 0:9e4bcb10b3e3 92
feb11 0:9e4bcb10b3e3 93 if(currentChunk < nbChunk)
feb11 0:9e4bcb10b3e3 94 {
feb11 0:9e4bcb10b3e3 95 get_chunk(currentChunk, reply);
feb11 0:9e4bcb10b3e3 96 currentChunk++;
feb11 0:9e4bcb10b3e3 97 }
feb11 0:9e4bcb10b3e3 98 else
feb11 0:9e4bcb10b3e3 99 currentChunk = 0;
feb11 0:9e4bcb10b3e3 100
feb11 0:9e4bcb10b3e3 101 return chunk;
feb11 0:9e4bcb10b3e3 102 }
feb11 0:9e4bcb10b3e3 103
feb11 0:9e4bcb10b3e3 104 void Formatter::get_chunk(const int c, char *reply)
feb11 0:9e4bcb10b3e3 105 {
feb11 0:9e4bcb10b3e3 106 strcat(chunk, reply);
feb11 0:9e4bcb10b3e3 107 }
feb11 0:9e4bcb10b3e3 108
feb11 0:9e4bcb10b3e3 109 SimpleHTMLFormatter::SimpleHTMLFormatter():
feb11 0:9e4bcb10b3e3 110 Formatter()
feb11 0:9e4bcb10b3e3 111 {
feb11 0:9e4bcb10b3e3 112 }
feb11 0:9e4bcb10b3e3 113
feb11 0:9e4bcb10b3e3 114 void SimpleHTMLFormatter::get_chunk(const int c, char* reply)
feb11 0:9e4bcb10b3e3 115 {
feb11 0:9e4bcb10b3e3 116 strcat(chunk, SIMPLE_HTML_CODE);
feb11 0:9e4bcb10b3e3 117
feb11 0:9e4bcb10b3e3 118 if(reply != NULL && strlen(reply) != 0)
feb11 0:9e4bcb10b3e3 119 {
feb11 0:9e4bcb10b3e3 120 strcat(chunk, "RPC reply : ");
feb11 0:9e4bcb10b3e3 121 strcat(chunk, reply);
feb11 0:9e4bcb10b3e3 122 }
feb11 0:9e4bcb10b3e3 123
feb11 0:9e4bcb10b3e3 124 if(!RPCObjectManager::instance().is_empty())
feb11 0:9e4bcb10b3e3 125 {
feb11 0:9e4bcb10b3e3 126 strcat(chunk, "<ul>");
feb11 0:9e4bcb10b3e3 127 for(std::list<char*>::iterator itor = RPCObjectManager::instance().begin();
feb11 0:9e4bcb10b3e3 128 itor != RPCObjectManager::instance().end();
feb11 0:9e4bcb10b3e3 129 ++itor)
feb11 0:9e4bcb10b3e3 130 {
feb11 0:9e4bcb10b3e3 131 strcat(chunk, "<li>");
feb11 0:9e4bcb10b3e3 132 strcat(chunk, *itor);
feb11 0:9e4bcb10b3e3 133 strcat(chunk, "</li>");
feb11 0:9e4bcb10b3e3 134 }
feb11 0:9e4bcb10b3e3 135 strcat(chunk, "</ul>");
feb11 0:9e4bcb10b3e3 136 }
feb11 0:9e4bcb10b3e3 137
feb11 0:9e4bcb10b3e3 138 strcat(chunk, "</body></html>");
feb11 0:9e4bcb10b3e3 139 }
feb11 0:9e4bcb10b3e3 140
feb11 0:9e4bcb10b3e3 141 InteractiveHTMLFormatter::InteractiveHTMLFormatter():
feb11 0:9e4bcb10b3e3 142 Formatter(3)
feb11 0:9e4bcb10b3e3 143 {
feb11 0:9e4bcb10b3e3 144 }
feb11 0:9e4bcb10b3e3 145
feb11 0:9e4bcb10b3e3 146 void InteractiveHTMLFormatter::get_chunk(const int c, char *reply)
feb11 0:9e4bcb10b3e3 147 {
feb11 0:9e4bcb10b3e3 148 if(c == 0)
feb11 0:9e4bcb10b3e3 149 sprintf(chunk, INTERACTIVE_HTML_CODE_1, EthernetInterface::getIPAddress());
feb11 4:624527ebc0fa 150
feb11 0:9e4bcb10b3e3 151 else if(c == 1)
feb11 0:9e4bcb10b3e3 152 {
feb11 0:9e4bcb10b3e3 153 if(reply != NULL && strlen(reply) != 0)
feb11 0:9e4bcb10b3e3 154 {
feb11 0:9e4bcb10b3e3 155 strcat(chunk, "RPC reply : ");
feb11 0:9e4bcb10b3e3 156 strcat(chunk, reply);
feb11 0:9e4bcb10b3e3 157 }
feb11 0:9e4bcb10b3e3 158 if(!RPCObjectManager::instance().is_empty())
feb11 0:9e4bcb10b3e3 159 {
feb11 0:9e4bcb10b3e3 160 strcat(chunk, "<p>Objects created :</p>");
feb11 0:9e4bcb10b3e3 161
feb11 0:9e4bcb10b3e3 162 strcat(chunk, "<ul>");
feb11 0:9e4bcb10b3e3 163 for(std::list<char*>::iterator itor = RPCObjectManager::instance().begin();
feb11 0:9e4bcb10b3e3 164 itor != RPCObjectManager::instance().end();
feb11 0:9e4bcb10b3e3 165 ++itor)
feb11 0:9e4bcb10b3e3 166 {
feb11 0:9e4bcb10b3e3 167 strcat(chunk, "<li>");
feb11 0:9e4bcb10b3e3 168 strcat(chunk, *itor);
feb11 0:9e4bcb10b3e3 169 strcat(chunk, " (<a href=\"http://");
feb11 0:9e4bcb10b3e3 170 strcat(chunk, EthernetInterface::getIPAddress());
feb11 0:9e4bcb10b3e3 171 strcat(chunk, "/");
feb11 0:9e4bcb10b3e3 172 strcat(chunk, *itor);
feb11 0:9e4bcb10b3e3 173 strcat(chunk, "/delete\">delete</a>)");
feb11 0:9e4bcb10b3e3 174 strcat(chunk, "</li>");
feb11 0:9e4bcb10b3e3 175 }
feb11 0:9e4bcb10b3e3 176 strcat(chunk, "</ul>");
feb11 0:9e4bcb10b3e3 177 }
feb11 0:9e4bcb10b3e3 178 strcat(chunk, " ");
feb11 0:9e4bcb10b3e3 179 }
feb11 0:9e4bcb10b3e3 180 else if(c == 2)
feb11 0:9e4bcb10b3e3 181 strcat(chunk, INTERACTIVE_HTML_CODE_2);
feb11 0:9e4bcb10b3e3 182 }
feb11 0:9e4bcb10b3e3 183
feb11 0:9e4bcb10b3e3 184