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:
7:838d7ea07e18
Improved javascript code

Who changed what in which revision?

UserRevisionLine numberNew contents of line
feb11 0:9e4bcb10b3e3 1 #include "RequestHandler.h"
feb11 0:9e4bcb10b3e3 2 #include "mbed_rpc.h"
feb11 0:9e4bcb10b3e3 3 #include "RPCObjectManager.h"
feb11 0:9e4bcb10b3e3 4 #include "RPCCommand.h"
feb11 0:9e4bcb10b3e3 5
feb11 7:838d7ea07e18 6 const char* INVALID_CMD = "Invalid RPC command";
feb11 7:838d7ea07e18 7 const char* DELETE_ERROR = "You must send a DELETE request to remove an object ";
feb11 7:838d7ea07e18 8 const char* CREATE_ERROR = "You must send a PUT request to create an object";
feb11 7:838d7ea07e18 9 const char* FUNC_CALL_ERROR = "You must send a GET request to call a function";
feb11 7:838d7ea07e18 10
feb11 0:9e4bcb10b3e3 11 void GetRequestHandler::handle(const RPCCommand& cmd, char *reply)
feb11 0:9e4bcb10b3e3 12 {
feb11 0:9e4bcb10b3e3 13 switch(cmd.get_type())
feb11 0:9e4bcb10b3e3 14 {
feb11 0:9e4bcb10b3e3 15 case DELETE:
feb11 7:838d7ea07e18 16 printf("Error: %s\n", DELETE_ERROR);
feb11 7:838d7ea07e18 17 strcat(reply, DELETE_ERROR);
feb11 0:9e4bcb10b3e3 18 break;
feb11 0:9e4bcb10b3e3 19 case FUNCTION_CALL:
feb11 0:9e4bcb10b3e3 20 RPC::call(cmd.get_cmd(), reply);
feb11 0:9e4bcb10b3e3 21 break;
feb11 0:9e4bcb10b3e3 22 case CREATE:
feb11 7:838d7ea07e18 23 printf("Error: %s\n", CREATE_ERROR);
feb11 7:838d7ea07e18 24 strcat(reply, CREATE_ERROR);
feb11 0:9e4bcb10b3e3 25 break;
feb11 0:9e4bcb10b3e3 26 default:
feb11 7:838d7ea07e18 27 printf("Error: %s\n", INVALID_CMD);
feb11 7:838d7ea07e18 28 strcat(reply, INVALID_CMD);
feb11 0:9e4bcb10b3e3 29 break;
feb11 0:9e4bcb10b3e3 30 }
feb11 0:9e4bcb10b3e3 31 }
feb11 0:9e4bcb10b3e3 32
feb11 0:9e4bcb10b3e3 33 void PutRequestHandler::handle(const RPCCommand& cmd, char *reply)
feb11 0:9e4bcb10b3e3 34 {
feb11 0:9e4bcb10b3e3 35 switch(cmd.get_type())
feb11 0:9e4bcb10b3e3 36 {
feb11 0:9e4bcb10b3e3 37 case DELETE:
feb11 7:838d7ea07e18 38 printf("Error: %s\n", DELETE_ERROR);
feb11 7:838d7ea07e18 39 strcat(reply, DELETE_ERROR);
feb11 0:9e4bcb10b3e3 40 break;
feb11 0:9e4bcb10b3e3 41 case FUNCTION_CALL:
feb11 7:838d7ea07e18 42 printf("Error: %s\n", FUNC_CALL_ERROR);
feb11 7:838d7ea07e18 43 strcat(reply, FUNC_CALL_ERROR);
feb11 0:9e4bcb10b3e3 44 break;
feb11 0:9e4bcb10b3e3 45 case CREATE:
feb11 0:9e4bcb10b3e3 46 RPC::call(cmd.get_cmd(), reply);
feb11 0:9e4bcb10b3e3 47 if(strlen(reply) > 0)
feb11 0:9e4bcb10b3e3 48 {
feb11 0:9e4bcb10b3e3 49 RPCObjectManager::instance().store_object(reply);
feb11 0:9e4bcb10b3e3 50 strcat(reply, " has been created");
feb11 0:9e4bcb10b3e3 51 }
feb11 0:9e4bcb10b3e3 52 else
feb11 7:838d7ea07e18 53 {
feb11 7:838d7ea07e18 54 printf("Error while creating object\n");
feb11 0:9e4bcb10b3e3 55 strcat(reply, "Error while creating object.");
feb11 7:838d7ea07e18 56 }
feb11 0:9e4bcb10b3e3 57 break;
feb11 0:9e4bcb10b3e3 58 default:
feb11 7:838d7ea07e18 59 printf("Error: %s\n", INVALID_CMD);
feb11 7:838d7ea07e18 60 strcat(reply, INVALID_CMD);
feb11 0:9e4bcb10b3e3 61 break;
feb11 0:9e4bcb10b3e3 62 }
feb11 0:9e4bcb10b3e3 63 }
feb11 0:9e4bcb10b3e3 64
feb11 0:9e4bcb10b3e3 65 void DeleteRequestHandler::handle(const RPCCommand& cmd, char *reply)
feb11 0:9e4bcb10b3e3 66 {
feb11 0:9e4bcb10b3e3 67 switch(cmd.get_type())
feb11 0:9e4bcb10b3e3 68 {
feb11 0:9e4bcb10b3e3 69 case CREATE:
feb11 7:838d7ea07e18 70 printf("Error: %s\n", CREATE_ERROR);
feb11 7:838d7ea07e18 71 strcat(reply, CREATE_ERROR);
feb11 0:9e4bcb10b3e3 72 break;
feb11 0:9e4bcb10b3e3 73 case FUNCTION_CALL:
feb11 7:838d7ea07e18 74 printf("Error: %s\n", FUNC_CALL_ERROR);
feb11 7:838d7ea07e18 75 strcat(reply, FUNC_CALL_ERROR);
feb11 0:9e4bcb10b3e3 76 break;
feb11 0:9e4bcb10b3e3 77 case DELETE:
feb11 0:9e4bcb10b3e3 78 RPC::call(cmd.get_cmd(), reply);
feb11 0:9e4bcb10b3e3 79 RPCObjectManager::instance().remove_object(cmd.get_obj_name());
feb11 0:9e4bcb10b3e3 80 strcat(reply, "Deleted object ");
feb11 0:9e4bcb10b3e3 81 strcat(reply, cmd.get_obj_name());
feb11 0:9e4bcb10b3e3 82 break;
feb11 0:9e4bcb10b3e3 83 default:
feb11 7:838d7ea07e18 84 printf("Error: %s\n", INVALID_CMD);
feb11 7:838d7ea07e18 85 strcat(reply, INVALID_CMD);
feb11 0:9e4bcb10b3e3 86 break;
feb11 0:9e4bcb10b3e3 87 }
feb11 0:9e4bcb10b3e3 88 }
feb11 0:9e4bcb10b3e3 89
feb11 0:9e4bcb10b3e3 90 void ComplexRequestHandler::handle(const RPCCommand& cmd, char *reply)
feb11 0:9e4bcb10b3e3 91 {
feb11 0:9e4bcb10b3e3 92 switch(cmd.get_type())
feb11 0:9e4bcb10b3e3 93 {
feb11 0:9e4bcb10b3e3 94 case CREATE :
feb11 0:9e4bcb10b3e3 95 putHandler.handle(cmd, reply);
feb11 0:9e4bcb10b3e3 96 break;
feb11 0:9e4bcb10b3e3 97 case DELETE :
feb11 0:9e4bcb10b3e3 98 deleteHandler.handle(cmd, reply);
feb11 0:9e4bcb10b3e3 99 break;
feb11 0:9e4bcb10b3e3 100 case FUNCTION_CALL :
feb11 0:9e4bcb10b3e3 101 getHandler.handle(cmd, reply);
feb11 0:9e4bcb10b3e3 102 break;
feb11 0:9e4bcb10b3e3 103 default :
feb11 7:838d7ea07e18 104 printf("Error: %s\n", INVALID_CMD);
feb11 7:838d7ea07e18 105 strcat(reply, INVALID_CMD);
feb11 0:9e4bcb10b3e3 106 break;
feb11 0:9e4bcb10b3e3 107 }
feb11 0:9e4bcb10b3e3 108 }
feb11 0:9e4bcb10b3e3 109