Table of contents
3. 3) Especificação Webservice[edit]
4. 4) Projeto do Desenvolvimento[edit]
5. 4.1) Diagrama de Classes[edit]
6. 5) Diagramas de Sequencia[edit]
9. 6.2) Http Request Parser[edit]
10. 7) Implementacao (andamento)[edit]
18. 8) Testes de Unidade[edit]
19. 8.1) Testes Componentes JSON (json_test.cc)[edit]
20. 8.1.1) Descricao dos Testes[edit]
21. 8.1.2) Implementacao dos Testes[edit]
22. 8.2) Testes Componentes HTTP (http_test.cc)[edit]
23. 8.2.1) Descricao dos Testes[edit]
24. 8.2.2) Implementacao dos Testes[edit]
26. 9.1) Descricao do Teste[edit]
27. 9.2) Implementacao do Teste[edit]
Projeto Lógico: Webservice RESTful com JSON no Epos
Atualmente, na internet, a técnica de comunicação entre sistemas distribuídos que mais ganha força é a REST(Representational State Transfer). REST é a descrição de uma interface web que utiliza o HTTP e um formato de encapsulamento de dados como JSON ou XML para troca de mensagens sem a necessidade da especificação de um outro protocolo.
Abaixo estão os princípios de um sistema RESTful retirado do wikipedia:
Para encapsular os dados, o formato mais utilizado com REST para comunicação com dispositivos móveis é o JSON, pois possui um menor nível de complexidade para interpretação dos dados.
O OpenEpos não possui suporte por padrão para uma arquitetura RESTful de aplicação web e, dessa forma, proporciona uma oportunidade de trabalho nessa área. Com o desenvolvimento de alguns componentes e aproveitando a já implementação do TCP, pode-se adicionar o suporte a um servidor web com características RESTful ao Epos.
O objetivo do trabalho é desenvolver um webservice RESTful no Epos. Para comprovação do resultado do trabalho, é planejado o desenvolvimento de um sistema que irá informar a temperatura que o mesmo tem armazenado e permitir que o mesmo receba uma temperatura de um dispositivo qualquer externo. Essas características do sistema irão permitir que testes simples possam ser efetivos.
Abaixo estão as características do sistema:
- Servidor HTTP 1.1.
- Verificar os pedidos e determinar as respostas de acordo com a especificação do webservice RESTful.
- JSON Parser para ler dados de pedidos http e JSON Writter para escrever dados no formato JSON para respostas http.
1- Pedido por temperatura
- Pedido:
- Resposta:
2- Recebimento de temperatura
- Pedido:
- Resposta:
O projeto será desenvolvido para o OpenEPOS e será feito em cima da implementação do TCP existente. O seguintes componentes foram identificados como necessários:
- HttpServer
- HttpServerHandler
- PathParser
- HttpRequest
- HttpResponse
- HttpRequestParser
- HttpResponseWriter
- JSONParser
- JSONWriter
- JSONNode
Os dois diagramas são responsáveis pelo fluxo principal da aplicação e representam os dois casos de uso.
Responsável pelas estruturas que encapsulam resposta e pedido HTTP, escritor e parser de HTTP, e o HTTPServer, bem como o HTTPServerHandler.
#ifndef HTTP_H_
#define HTTP_H_
#include<utility/list.h>
#include<utility/path_parser.h>
#include<tcp.h>
#include<utility/string.h>
#include<utility/vector.h>
__BEGIN_SYS
structstring {
char *_string;
};
structrequestLine {
char *_method;
char *_requestUri;
char *_httpVersion;
};
structheader {
char *_name;
char *_value;
};
structstatusLine {
char *_status;
char *_httpVersion;
};
typedefList_Elements::Doubly_Linked<header> HeaderElement;
typedefList_Elements::Doubly_Linked<string> CharElement;
structhttpRequest {
requestLine_requestLine;
List<header, HeaderElement> _headers;
char* _body;
};
structhttpResponse {
statusLine_statusLine;
List<header, HeaderElement> _headers;
char* _body;
};
classHTTPRequestParser {
public:
httpRequest_request;
bool_parsed;
HTTPRequestParser(constchar * input) {
_input = strcpy(_input, input);
_parsed = false;
parse();
}
httpRequestgetRequest() {return_request;}
booldidParse() {return_parsed;}
private :
char * _input;
boolparseRequestLine(char* requestLine);
boolparseHeaders(char * oneHeader);
voidparse();
};
classHTTPResponseWriter {
public:
HTTPResponseWriter(httpResponse * httpResponse) {
_response = newchar[80];
_httpResponse = httpResponse;
write();
}
char* getResponse();
private:
httpResponse * _httpResponse;
char* _response;
voidwrite();
};
classHTTPServer : publicTCP::ServerSocket {
public:
classHTTPServerHandler {
public :
char * path;
HTTPServerHandler(constchar * path) {
strcpy(this->path, path);
}
~HTTPServerHandler () {};
virtualhttpResponse * onRequest(httpRequestreq, List<char, PathParser::ArgsElement> * args);
};
classJsonRestResource {
public:
char * path;
JsonRestResource(constchar * path) {
strcpy(this->path , path);
}
~JsonRestResource() {}
virtualchar * onRequest(List<char, PathParser::ArgsElement> * args);
};
classHTTPRestHandler : publicHTTPServerHandler {
public:
structrestHandler {
JsonRestResource * _handler;
char* _path;
};
typedefList_Elements::Doubly_Linked<restHandler> JsonResourceElement;
List<restHandler, JsonResourceElement> restResources;
HTTPRestHandler(constchar * path) : HTTPServerHandler(path) {}
httpResponse * onRequest(httpRequestreq, List<char, PathParser::ArgsElement> * args) {
JsonRestResource * selected_handler;
PathParser * parsed_path;
JsonResourceElement *h = restResources.head();
db<TCP>(TRC) << "HTTPRestHandler attempting to get resource for request on path=" << req._requestLine._requestUri <<"\n";
while(h != 0) {
//get path parser to verify if path is compatible
PathParser * parser = newPathParser(req._requestLine._requestUri, h->object()->_path);
if(parser->didMatch()) {
selected_handler = h->object()->_handler;
parsed_path = parser;
db<TCP>(TRC) << "HTTPRestHandler found resource for request on path=" << req._requestLine._requestUri <<"\n";
break;
}
h = h->next();
}
httpResponse * response;
if(h) {
db<TCP>(TRC) << "HTTPRestHandler found resource for request on path=" << req._requestLine._requestUri <<"\n";
char * jsonResponse = h->object()->_handler->onRequest(&parsed_path->args);
db<TCP>(TRC) << "HTTPRestHandler BUILDING RESPONSE\n";
//build response for Rest Json web service
response = newhttpResponse();
response->_statusLine._httpVersion = newchar[strlen("HTTP/1.1")];
strcpy(response->_statusLine._httpVersion, "HTTP/1.1");
db<TCP>(TRC) << "HTTPRestHandler setting httpVersion\n";
response->_statusLine._status = newchar[strlen("200 OK")];
strcpy(response->_statusLine._status, "200 OK");
db<TCP>(TRC) << "HTTPRestHandler setting statusCode\n";
response->_body = newchar[strlen(jsonResponse)];
strcpy(response->_body, jsonResponse);
db<TCP>(TRC) << "HTTPRestHandler setting body\n";
//headers
//content len
header * contentLenghHeader = new header();
contentLenghHeader->_name = newchar[strlen("Content-Length")];
strcpy(contentLenghHeader->_name,"Content-Length" );
intlenInt = strlen(jsonResponse);
char * c = new (&lenInt) char[sizeof(lenInt)];
contentLenghHeader->_value = newchar[strlen( c)];
strcpy(contentLenghHeader->_value, c);
response->_headers.insert(newHeaderElement(contentLenghHeader));
db<TCP>(TRC) << "HTTPRestHandler setting contentLen\n";
//content type
header * contentTypeHeader = new header();
contentTypeHeader->_name = newchar[strlen("Content-Type")];
strcpy(contentTypeHeader->_name,"Content-Type" );
contentTypeHeader->_value = newchar[strlen("application/json")];
strcpy(contentTypeHeader->_value, "application/json");
response->_headers.insert(newHeaderElement(contentTypeHeader));
db<TCP>(TRC) << "HTTPRestHandler did build response on path=" << req._requestLine._requestUri <<"\n";
return response;
} else {
db<TCP>(TRC) << "HTTPRestHandler resource not found for path=" << req._requestLine._requestUri <<"\n";
response = newhttpResponse();
response->_statusLine._httpVersion = newchar[strlen("HTTP/1.1")];
response->_statusLine._status = newchar[strlen("404 not found")];
strcpy(response->_statusLine._httpVersion,"HTTP/1.1");
strcpy(response->_statusLine._status,"404 not found");
return response;
}
}
booladdHandler(JsonRestResource * h) {
//verify if path is already mapped
bool possible = true;
if(restResources.size() > 0) {
JsonResourceElement * element = restResources.head();
while(element) {
if(strcmp(element->object()->_path, h->path) == 0) {
possible = true;
}
element = element->next();
}
}
if(possible) {
restHandler * ha = newrestHandler();
ha->_handler = h;
ha->_path = newchar[strlen(h->path)];
strcpy(ha->_path, h->path);
restResources.insert_tail(newJsonResourceElement(ha));
db<TCP>(TRC) << "\nHTTP REST adding handler path=" << ha->_path << " \n";
}
return possible;
}
~HTTPRestHandler() {
//do nothing
}
};
HTTPServer(int port) : TCP::ServerSocket(TCP::Address(tcp()->ip()->address(),port)) {}
structhandler {
HTTPServerHandler * _handler;
char* _path;
};
typedefList_Elements::Doubly_Linked<handler> HandlerElement;
List<handler, HandlerElement> handlers;
booladdHandler(HTTPServerHandler * h) {
//verify if path is already mapped
bool possible = true;
if(handlers.size() > 0) {
HandlerElement * element = handlers.head();
while(element) {
if(strcmp(element->object()->_path, h->path) == 0) {
possible = true;
}
element = element->next();
}
}
if(possible) {
handler * ha = new handler();
ha->_handler = h;
ha->_path = newchar[strlen(h->path)];
strcpy(ha->_path, h->path);
db<TCP>(TRC) << "\nHTTP adding handler path=" << ha->_path << " \n";
handlers.insert_tail(newHandlerElement(ha));
}
return possible;
}
char * receivedLocal(constchar *data,u16 size) {
received(data, size);
returnresponse;
}
private:
char* response;
httpResponse * resourceNotFoundRe;
TCP::Socket* incoming(constTCP::Address& from) {
//TODO: strategy to accept multiple connections
returnthis;
}
voidconnected() {}
voidclosed() {
listen();
}
voiderror(short err) {}
voidsent(u16 size) {}
voidreceived(constchar *data,u16 size) {
//get req from parser
HTTPRequestParser * parser = newHTTPRequestParser(data);
if(!parser->_parsed) {
return;
}
httpRequestreq = parser->_request;
HTTPServerHandler * selected_handler;
PathParser * parsed_path;
HandlerElement *h = handlers.head();
while(h != 0) {
//get path parser to verify if path is compatible
db<TCP>(TRC) << "\nHTTP testing handler path=" << h->object()->_path << " for path="<< req._requestLine._requestUri << " \n";
PathParser * parser = newPathParser(req._requestLine._requestUri, h->object()->_path);
if(parser->didMatch()) {
selected_handler = h->object()->_handler;
parsed_path = parser;
db<TCP>(TRC) << "HTTPServer handler found for request on path =" <<req._requestLine._requestUri <<"\n";
break;
}
h = h->next();
}
httpResponse * res;
if(selected_handler != 0) {
res = selected_handler->onRequest(req, &parsed_path->args);
db<TCP>(TRC) << "HTTPServer handler informed response for request on path =" <<req._requestLine._requestUri <<"\n";
} else {
db<TCP>(TRC) << "\nHTTP could not find handler\n";
res = resourceNotFoundResponse();
}
//get writter to convert response to
HTTPResponseWriter * wrt = newHTTPResponseWriter(res);
response = wrt->getResponse();
db<TCP>(TRC) << "HTTPServer handler responsed=" << response << " for request on path =" <<req._requestLine._requestUri <<"\n";
send(response, strlen(response));
close();
}
httpResponse * resourceNotFoundResponse() {
if(resourceNotFoundRe == 0) {
resourceNotFoundRe = newhttpResponse();
resourceNotFoundRe->_statusLine._httpVersion = newchar[strlen("HTTP/1.1")];
resourceNotFoundRe->_statusLine._status = newchar[strlen("404 not found")];
strcpy(resourceNotFoundRe->_statusLine._httpVersion,"HTTP/1.1");
strcpy(resourceNotFoundRe->_statusLine._status,"404 not found");
}
returnresourceNotFoundRe;
}
};
__END_SYS
#endif/* HTTP_H_ */
#include<http.h>
__BEGIN_SYS
boolisspace(char * c);
boolisspace(char c);
char *trimwhitespace(char *str);
CharElement * createElement(char * p);
char* HTTPResponseWriter::getResponse() {
return_response;
}
voidHTTPResponseWriter::write() {
//status line
strcat(_response, _httpResponse->_statusLine._httpVersion);
db<TCP>(TRC) << "\nHTTP Response Writer response=" << _response << " \n";
strcat(_response, " ");
db<TCP>(TRC) << "\nHTTP Response Writer response=" << _response << " \n";
strcat(_response, _httpResponse->_statusLine._status);
db<TCP>(TRC) << "\nHTTP Response Writer response=" << _response << " \n";
strcat(_response,"\r\n");
db<TCP>(TRC) << "\nHTTP Response Writer response=" << _response << " \n";
//headers, if any
if(!_httpResponse->_headers.empty()) {
HeaderElement * h = _httpResponse->_headers.head();
while(h) {
strcat(_response,h->object()->_name);
strcat(_response,":");
strcat(_response,h->object()->_value);
strcat(_response,"\r\n");
h = h->next();
db<TCP>(TRC) << "\nHTTP Response Writer response=" << _response << " \n";
}
}
//CRLF
strcat(_response,"\r\n");
if(_httpResponse->_body) {
strcat(_response, _httpResponse->_body);
}
db<TCP>(TRC) << "\nHTTP Response Writer response=" << _response << " \n";
}
List<string, CharElement> * split(char * input, constchar * pttr) {
// char str[] ="This\r\n a \r\nsample\r\n string\r\n";
// char * pch;
// pch = strtok(str," ,.-");
// while (pch != 0)
// {
// db<TCP>(TRC) << "\nSPLIT TEST worked =" <<pch<< " \n ";
//
// pch = strtok(0, "\r\n");
// }
List<string, CharElement> * v = newList<string, CharElement>;
// char pattern[strlen(pttr)];
// strcpy(pattern, pttr);
char *p;
char inp[strlen(input)];
strcpy(inp, input);
p = strtok(inp, pttr);
if(p != 0) {
while (p != 0) {
if(!isspace(p)) {
db<TCP>(TRC) << "\nHTTP did split=" << p << " \n";
v->insert_tail(createElement(p));
}
p = strtok(0, pttr);
}
} else {
db<TCP>(TRC) << "\nHTTPcouldnot SPLIT!!\n";
}
return v;
}
CharElement * createElement(char * p){
string * s = newstring();
s->_string = newchar[strlen(p)];
strcpy(s->_string, p);
CharElement * element = newCharElement(s);
returnelement;
}
boolisspace(char * content) {
int len1 = strlen(content);
if(len1 == 0) {
returntrue;
}
char c[len1];
strcpy(c, content);
intlen = strlen(c);
intcount = 0;
for(int i= 0; i < len; i++) {
if(isspace(c[i])) {
count++;
}
}
if(count == len) {
returntrue;
} else {
returnfalse;
}
}
boolisspace(char c){
if(c == 0 || c == 0x20 || c == 0x09 || c == 0x0a || c == 0x0b || c == 0x0c || c == 0x0d) {
returntrue;
}
returnfalse;
}
char *trimwhitespace(char *str)
{
size_tlen = 0;
char *frontp = str - 1;
char *endp = 0;
if( str == 0 )
return 0;
if( str[0] == '\0' )
returnstr;
len = strlen(str);
endp = str + len;
/* Move the front andback pointers toaddress
* thefirst non-whitespacecharactersfrom
* each end.
*/
while( isspace(*(++frontp)) );
while( isspace(*(--endp)) && endp != frontp );
if( str + len - 1 != endp )
*(endp + 1) = '\0';
elseif( frontp != str && endp == frontp )
*str = '\0';
/* Shift thestringsothat it starts atstrso
* thatifit'sdynamicallyallocated, wecan
* still free it onthereturned pointer. Note
* the reuse ofendptomeanthe front ofthe
* string buffer now.
*/
endp = str;
if( frontp != str )
{
while( *frontp ) *endp++ = *frontp++;
*endp = '\0';
}
returnstr;
}
//char * trimwhitespace(char *str)
//{
// char *end;
//
// // Trimleadingspace
// while(isspace(str)) str++;
//
// if(*str == 0)// Allspaces?
// returnstr;
//
// // Trimtrailingspace
// end = str + strlen(str) - 1;
// while(end > str && isspace(end)) end--;
//
// // Write new nullterminator
// *(end+1) = 0;
//
// returnstr;
//}
voidHTTPRequestParser::parse() {
List<string, CharElement> * allContents = split(_input, "\n");
if(allContents->size() > 0) {
string * requestLine = allContents->head()->object();
//ifcant parse requestline, abort
if(!parseRequestLine(requestLine->_string)) {
db<TCP>(TRC) << "\nHTTP:Nao conseguiu fazer o parser do requestline: "<< _input << " \n";
return;
}
//Verifica qntosheaders tem fazendo a comparacao com a linha em branco que antecede o body ou o final.
CharElement * possibleHeader = allContents->head()->next();
while(possibleHeader != 0) {
db<TCP>(TRC) << "\nHTTP: Processingpossible header <<" << possibleHeader->object()->_string << " \n";
string * header = possibleHeader->object();
boolparsed = parseHeaders(header->_string);
if(!parsed && !possibleHeader->next()) {
db<TCP>(TRC) << "\nHTTP: Processingbody <<" << possibleHeader->object()->_string << " \n";
strcpy(_request._body,possibleHeader->object()->_string);
} elseif(!parsed) {
db<TCP>(TRC) << "\nHTTP: Processing header failed= <<" << possibleHeader->object()->_string << " \n";
return;
}
possibleHeader = possibleHeader->next();
}
_parsed = true;
}
}
boolHTTPRequestParser::parseRequestLine(char* requestLine) {
db<TCP>(TRC) << "\nHTTP: ParsingRequestLine: "<< requestLine << " \n";
List<string, CharElement> * requestLineContents = split(requestLine, " ");
db<TCP>(TRC) << "\nHTTP:Numero de elementos da requestLineContents: "<< requestLineContents->size() << " \n";
if(requestLineContents->size() == 3) {
CharElement * method = requestLineContents->head();
char * methodValue = trimwhitespace(method->object()->_string);
_request._requestLine._method = newchar[strlen(methodValue)];
strcpy(_request._requestLine._method, methodValue);
db<TCP>(TRC) << "\nHTTP: Parsed httpMethod: "<< _request._requestLine._method << " \n";
char * requestUriValue = trimwhitespace(method->next()->object()->_string);
_request._requestLine._requestUri = newchar[strlen(requestUriValue)];
strcpy(_request._requestLine._requestUri, requestUriValue);
db<TCP>(TRC) << "\nHTTP: Parsed requestUri: "<< _request._requestLine._requestUri << " \n";
char * httpVersionValue = trimwhitespace(method->next()->next()->object()->_string);
_request._requestLine._httpVersion = newchar[strlen(httpVersionValue)];
strcpy(_request._requestLine._httpVersion, httpVersionValue);
db<TCP>(TRC) << "\nHTTP: Parsed httpVersion: "<< _request._requestLine._httpVersion << " \n";
returntrue;
} else {
db<TCP>(TRC) << "\nHTTP:FAILED - > Expected 3 But it found requestLineContents: "<< requestLineContents->size() << " \n";
returnfalse;
}
}
boolHTTPRequestParser::parseHeaders(char * oneHeader) {
List<string, CharElement> * headerContent = split(oneHeader, ":");
if(headerContent->size() == 2) {
header * _header = new header();
char * headerName = trimwhitespace(headerContent->head()->object()->_string);
_header->_name = newchar[strlen(headerName)];
strcpy(_header->_name, headerName);
db<TCP>(TRC) << "\nHTTP: Parsed headername=: "<<_header->_name << " \n";
char* headerValue = trimwhitespace(headerContent->head()->next()->object()->_string);
_header->_value = newchar[strlen(headerValue)];
strcpy(_header->_value,headerValue );
db<TCP>(TRC) << "\nHTTP: Parsed headervalue=: "<<headerValue<< " \n";
_request._headers.insert(newHeaderElement(_header));
db<TCP>(TRC) << "\nHTTP: Processed header <<" << oneHeader<< " \n";
returntrue;
} else {
db<TCP>(TRC) << "\nHTTP: Processed header failed <<" << oneHeader<< " \n";
returnfalse;
}
}
__END_SYS
#ifndef JSONPARSER_H_
#define JSONPARSER_H_
#include<utility/list.h>
#include<utility/string.h>
#include<tcp.h>
__BEGIN_SYS
structjsonNode {
char * key;
char * value;
};
classJsonParser {
private:
char * string;
voidparse();
intreadSeparators(int * index, char string[]);
public:
boolparsed;
typedefList_Elements::Doubly_Linked<jsonNode> JsonElement;
List<jsonNode, JsonElement> nodes;
JsonParser(constchar * input) {
string = newchar[strlen(input)];
strcpy(string,input);
parsed = false;
parse();
}
booldidParse();
List<jsonNode, JsonElement> getNodes();
};
classJsonWriter {
public:
char * json;
boolhasValues;
JsonWriter() {
strcpy(json, "{");
hasValues = false;}
voidwrite(constchar * key, constchar * value);
char* getJson() {
char temp2[2];
strcpy(temp2, json);
returnstrcat(temp2, "}");
}
};
__END_SYS
#endif/* PATHPARSER_H_ */
#include<utility/json.h>
__BEGIN_SYS
voidincreaseArrayIfNeeded(int index, int size, char * keyTemp){
if(index >= size){
char * temp;
temp = newchar[size+1];
for(int i =0; i < size; i++){
temp[i] = keyTemp[i];
}
delete[] keyTemp;
keyTemp = temp;
}
}
voidincreaseArraySize(int extraSize, int size, char * keyTemp){
char * temp;
temp = newchar[size + extraSize];
for(int i =0; i < size; i++){
temp[i] = keyTemp[i];
}
delete[] keyTemp;
keyTemp = temp;
}
voidJsonWriter::write(constchar *key,constchar * value) {
constchar * DB_QUOTES = "\"";
constchar * TWO_POINTS = " : ";
constchar * SEPARATOR = ",";
db<TCP>(TRC) << "JsonWriter:: "<< json << "\n";
if(hasValues) {
strcat(json,SEPARATOR);
db<TCP>(TRC) << "JsonWriter:: "<< json << "\n";
} else {
hasValues = true;
}
strcat(json, DB_QUOTES);
db<TCP>(TRC) << "JsonWriter:: "<< json << "\n";
strcat(json, key);
db<TCP>(TRC) << "JsonWriter:: "<< json << "\n";
strcat(json, DB_QUOTES);
db<TCP>(TRC) << "JsonWriter:: "<< json << "\n";
strcat(json, TWO_POINTS);
db<TCP>(TRC) << "JsonWriter:: "<< json << "\n";
strcat(json, DB_QUOTES);
db<TCP>(TRC) << "JsonWriter:: "<< json << "\n";
strcat(json, value);
db<TCP>(TRC) << "JsonWriter:: "<< json << "\n";
strcat(json, DB_QUOTES);
db<TCP>(TRC) << "JsonWriter:: "<< json << "\n";
}
boolJsonParser::didParse() {
returnparsed;
}
intJsonParser::readSeparators(int * idx, char string[]) {
int index = *idx;
while(string[index] == ' ' || string[index] == '\n') {
index++;
}
return index;
}
voidJsonParser::parse() {
db<TCP>(TRC) << "JsonParser::trying to parse "<< string << "\n";
if (string[0] != '{') {
db<TCP>(TRC) << "JsonParser::could not parse "<< string << " because json has to start with {" << "\n";
return;
}
bool continuar = true;
int index = 1;
while (continuar) {
index = readSeparators(&index, string);
char * keyTemp;
keyTemp = newchar[2];
char * valueTemp;
valueTemp = newchar[2];
db<TCP>(TRC) << "JsonParser:: linha 46= " << index << "=" << string[index] << " \n";
if (string[index] == '\"') {
index++;
db<TCP>(TRC) << "JsonParser:: linha 49= " << index << "=" << string[index] <<" \n";
while (string[index] != '\"') {
char * temp;
temp = newchar[1];
temp[0] = string[index];
strcat(keyTemp, temp);
db<TCP>(TRC) << "JsonParser:: linha 52= " << index << "=" << string[index] << " keyTemp=" << keyTemp <<" \n";
index++;
}
index++;
index = readSeparators(&index, string);
if (string[index] != ':') {
db<TCP>(TRC) << "JsonParser::could not parse "<< "=" << string[index] << " because was expecting :" << "\n";
return;
}
index++;
index = readSeparators(&index, string);
if (string[index] != '\"') {
db<TCP>(TRC) << "JsonParser::could not parse "<< "=" << string[index] <<" because was expecting double quotes"<< "\n";
return;
}
index++;
db<TCP>(TRC) << "JsonParser:: linha 69= " << index << "=" << string[index] <<" \n";
while (string[index] != '\"') {
char * temp;
temp = newchar[1];
temp[0] = string[index];
strcat(valueTemp, temp);
db<TCP>(TRC) << "JsonParser:: linha 71= " << index << "=" << string[index] << " valueTemp=" << valueTemp <<" \n";
index++;
}
index++;
jsonNode * node = new jsonNode();
node->value = newchar[strlen(valueTemp)];
strcpy(node->value, valueTemp);
node->key = newchar[strlen(keyTemp)];
strcpy(node->key, keyTemp);
delete[] keyTemp;
delete[] valueTemp;
db<TCP>(TRC) << "JsonParser:: found key=" << node->key << "\n";
db<TCP>(TRC) << "JsonParser:: found value=" << node->value << "\n";
nodes.insert(new JsonElement(node));
index = readSeparators(&index, string);
if (string[index] == '}') {
continuar = false;
} else {
if (string[index] == ',') {
//keyTemp = {0};
//valueTemp = {0};
index++;
} else {
db<TCP>(TRC) << "JsonParser::error was expecting , but got=" << string[index] << "\n";
return;
}
}
} else {
db<TCP>(TRC) << "JsonParser::could not parse "<< string << " because was expecting double quotes"<< "\n";
return;
}
}
parsed = true;
}
__END_SYS
#ifndef PATHPARSER_H_
#define PATHPARSER_H_
#include<utility/list.h>
#include<utility/string.h>
#include<radio.h>
__BEGIN_SYS
// class should receive a pattern like /temperatura/*, where * means its an argument
// and should receive a path like /temperatura/ or /temperatura/20F
classPathParser {
public:
typedef List_Elements::Doubly_Linked<char> ArgsElement;
List<char*, ArgsElement> args;
PathParser(char * path, char * pattern) {
this->pattern = newchar[strlen(pattern)];
strcpy(this->pattern, pattern);
this->path = newchar[strlen(path)];
strcpy(this->path, path);
db<TCP>(TRC) << "\nPathParser:: parsing pattern=" << this->pattern << " for path=" << path << "\n";
matched = false;
parse();
}
booldidMatch();
private:
char * path;
char * pattern;
boolmatched;
voidparse();
};
__END_SYS
#endif/* PATHPARSER_H_ */
#include<utility/path_parser.h>
__BEGIN_SYS
boolPathParser::didMatch() {
returnmatched;
}
voidPathParser::parse() {
intpathLen = strlen(path);
intpatternLen = strlen(pattern);
if(pathLen >= patternLen && patternLen > 0) {
intpathIndex = 0;
intpatternIndex = 0;
if(pattern[patternIndex] == '/') {
patternIndex++;
if(path[pathIndex] == '/') {
pathIndex++;
} else {
return;
}
} else {
return;
}
while(patternIndex < patternLen) {
//if its not the beginning, verify if its an argument or a path name
if(pattern[patternIndex] != '*') { // its a name, we have to match char by char
while(pattern[patternIndex] != '/' && patternIndex < patternLen) {
if(pathIndex < pathLen && pattern[patternIndex] == path[pathIndex]) {
pathIndex++;
patternIndex++;
} else {
return;
}
}
} else { // its an argument
if (pathIndex >= pathLen)
return;
while(pattern[patternIndex] != '/' && patternIndex < patternLen ) {
char * argument;
int argIndex = 0;
while(path[pathIndex] != '/' && pathIndex < pathLen) {
argument[argIndex] = path[pathIndex];
argIndex++;
pathIndex++;
}
argument[argIndex] = '\0';
patternIndex++;
//store the argument
if(strlen(argument) > 0) {
args.insert(new ArgsElement(argument));
}
}
}
// skip the next '/'
if (pattern[patternIndex] == '/' && patternIndex < patternLen) {
if (path[pathIndex] == '/') {
pathIndex++;
patternIndex++;
} else {
return;
}
}
}
if (patternIndex >= patternLen && pathIndex < pathLen)
return;
matched = true;
}
}
__END_SYS
// EPOS String Utility Declarations
// This work is licensed under the EPOS Software License v1.0.
// A copy of this license is available at the EPOS system source tree root.
// A copy of this license is also available online at:
// http://epos.lisha.ufsc.br/EPOS+Software+License+v1.0
// Note that EPOS Software License applies to both source code and executables.
#ifndef __string_h
#define __string_h
extern"C" {
intmemcmp(constvoid * m1, constvoid * m2, size_t n);
void * memcpy(void * d, constvoid * s, size_t n);
void * memset(void * m, int c, size_t n);
void * memchr(constvoid * m, int c, size_t n);
intstrcmp(constchar * s1, constchar * s2);
intstrncmp(constchar * s1, constchar * s2, size_t n);
char * strcpy(char * d, constchar * s);
char * strncpy(char * d, constchar * s, size_t n);
char * strcat(char * d, constchar * s);
char * strchr(constchar * s, int c);
char * strrchr(constchar * s, int c);
size_tstrlen(constchar * s);
longatol(constchar *s);
char *itoa(int v, char *s);
intutoa(unsignedlong v, char * d);
char * strtok(char *s, constchar *delim);
char * strtok_r(char *s, constchar *delim, char **last);
}
#endif
7.6) path_parser.cc
-- Componente: JSONWriter
-- Resultados Esperados:
-- JSONWriter.getJson() == {"temperatura": "104F"}
-- Componente: JSONParser
-- JSONParser.getNodes().size == 1
-- JSONParser.getNodes().contains(temperatura)
-- JSONParser.getNodes().get(temperatura).value == "104F"
-- Componente: JSONWriter
-- JSONWriter.getJson() == {"temperatura": "104F", "action": "teste"}
-- Componente: JSONParser
-- JSONParser.getNodes().size == 2
-- JSONParser.getNodes().contains(temperatura)
-- JSONParser.getNodes().get(temperatura).value == "104F"
-- JSONParser.getNodes().contains(action)
-- JSONParser.getNodes().get(temperatura).value == "teste"
#include <utility/ostream.h>
#include <utility/malloc.h>
#include <utility/json.h>
__USING_SYS
;
voidtest_write_and_parse();
voidexpectedWriterOutput22(char expectedWriterOutput2[]);
voidexpectedWriterOutput1(char expectedWriterOutput[]);
OStream cout;
intmain() {
cout << "JSON Utility Test\n";
test_write_and_parse();
cout << "\nDone!\n";
return 0;
}
voidexpectedWriterOutput1(char expectedWriterOutput[]) {
strcat(expectedWriterOutput, "{");
strcat(expectedWriterOutput, "\"");
strcat(expectedWriterOutput, "temperatura");
strcat(expectedWriterOutput, "\"");
strcat(expectedWriterOutput, " : ");
strcat(expectedWriterOutput, "\"");
strcat(expectedWriterOutput, "104F");
strcat(expectedWriterOutput, "\"");
strcat(expectedWriterOutput, "}");
}
voidexpectedWriterOutput22(char expectedWriterOutput2[]) {
strcat(expectedWriterOutput2, "{");
strcat(expectedWriterOutput2, "\"");
strcat(expectedWriterOutput2, "temperatura");
strcat(expectedWriterOutput2, "\"");
strcat(expectedWriterOutput2, " : ");
strcat(expectedWriterOutput2, "\"");
strcat(expectedWriterOutput2, "104F");
strcat(expectedWriterOutput2, "\"");
strcat(expectedWriterOutput2, ",");
strcat(expectedWriterOutput2, "\"");
strcat(expectedWriterOutput2, "action");
strcat(expectedWriterOutput2, "\"");
strcat(expectedWriterOutput2, " : ");
strcat(expectedWriterOutput2, "\"");
strcat(expectedWriterOutput2, "teste");
strcat(expectedWriterOutput2, "\"");
strcat(expectedWriterOutput2, "}");
}
voidtest_write_and_parse() {
//test temperatura=104F
constchar * tempkey = "temperatura";
constchar * tempvalue = "104F";
cout << "\nTest1!\n";
//write
JsonWriter * writer = new JsonWriter();
writer->write(tempkey, tempvalue);
constchar * writerOutput = writer->getJson();
cout << "Wrote=" << writerOutput << "\n";
//build json to compare and read
char expectedWriterOutput[80];
expectedWriterOutput1(expectedWriterOutput);
cout << "Expects=" << expectedWriterOutput << "\n";
if (strcmp(writerOutput, expectedWriterOutput) != 0) {
cout << "\nDid not Match\n";
} else {
cout << "passou 1" << "\n";
}
//read
JsonParser * parser = new JsonParser(writerOutput);
if (parser->didParse()) {
if (parser->nodes.size() == 1) {
cout << "passou 2 \n";
jsonNode * node = parser->nodes.head()->object();
if (strcmp(node->key, tempkey) != 0
|| strcmp(node->value, tempvalue) != 0) {
cout << "key=" << node->key << "\n" ;
cout << "value=" << node->value << "\n" ;
} else {
cout << "passou 3 \n";
}
} else {
cout << "\nNode expected:" << tempkey << ":" << tempvalue
<< " but none was given \n";
}
} else {
cout << "\nCould not parse\n";
}
cout << "\nTest2!\n";
//test temperatura=104F && action=teste
//write action
constchar * actionkey = "action";
constchar * actionvalue = "teste";
writer->write(actionkey, actionvalue);
constchar * writerOutput2 = writer->getJson();
cout << "Wrote=" << writerOutput2 << "\n";
//build json to compare and read
char expectedWriterOutput2[80];
expectedWriterOutput22(expectedWriterOutput2);
cout << "Expects=" << expectedWriterOutput2 << "\n";
if (strcmp(writerOutput2, expectedWriterOutput2) != 0) {
cout << "\nDid not match\n";
} else {
cout << "passou 4\n";
}
//parse
JsonParser * parser2 = new JsonParser(writerOutput2);
if (parser2->didParse()) {
if (parser2->nodes.size() == 2) {
cout << "passou 5 \n";
JsonParser::JsonElement * e = parser2->nodes.head();
while (e) {
jsonNode * node = e->object();
cout << "will parse next node \n";
cout << "parsed key=" << node->key << "\n";
cout << "parsed value=" << node->value << "\n";
cout << "parsed=" << node->key << ":" << node->value << "\n";
e = e->next();
cout << "trying to parse next node \n";
}
cout << "finished testing nodes \n";
} else {
cout << "\nDid not parse the two values\n";
}
} else {
cout << "\nCould not parse\n";
}
}
-- Componente: HTTPRequestParser
-- Resultados Esperados:
--- httpRequest.requestLine.httpVersion == HTTP/1.1
--- httpRequest.requestLine.method == GET
--- httpRequest.headers.size == 1
--- httpRequest.headers.contains(Host)
--- httpRequest.headers.get(Host).value == www.google.com
GET / HTTP/1.1 |
Host: www.google.com |
|
Testando |
|
-- Componente HTTPRequestParser
-- Resultados Esperados:
--- httpRequest.requestLine.httpVersion == HTTP/1.1
--- httpRequest.requestLine.method == POST
--- httpRequest.requestLine.requestUri == /enlighten/rest
--- httpRequest.headers.size == 2
--- httpRequest.headers.contains(Host)
--- httpRequest.headers.get(Host).value == api.opencalais.com
--- httpRequest.headers.contains(Content-Length)
--- httpRequest.headers.get(Content-Length).value == length
--- httpRequest.body == licenseID=string&content=string&/paramsXML=string
|
|
|
|
|
-- Componente HTTPResponseWriter
-- Resultados Esperados:
--- HTTPResponseWriter.getResponse() == HTTP/1.1 200 OK\r\n\r\nHello world!\r\n
HTTP/1.1 200 OK |
Hello world! |
-- Componente HTTPResponseWriter
-- Resultados Esperados:
--- HTTPResponseWriter.getResponse() == HTTP/1.1 200 OK\r\nContent-Type: text/xml; charset=utf-8\r\nContent-Length: 8\r\n\r\nTestando\r\n
HTTP/1.1 200 OK |
Content-Type: text/xml; charset=utf-8 |
Content-Length: 8 |
Testando |
#include<utility/ostream.h>
#include<utility/malloc.h>
#include<http.h>
__USING_SYS
;
voidtest_parser();
voidtest_parser2();
voidtest_writer();
voidtest_writer2();
OStream cout;
intmain() {
cout << "HTTP Utilities Test\n";
cout << "\nTesting Parser!\n";
test_parser();
cout << "\nDone test parse 1!\n";
test_parser2();
cout << "\nDone test parse 2!\n";
cout << "\nTesting Writer!\n";
test_writer();
cout << "\nDone test writer 1!\n";
test_writer2();
cout << "\nDone test writer 2!\n";
return 0;
}
voidtest_parser() {
char request_msg[80] = "GET / HTTP/1.1";
cout << "\nMsg=" << request_msg;
strcat(request_msg, "\r\n");
strcat(request_msg, "Host: www.google.com");
strcat(request_msg, "\r\n");
strcat(request_msg, "\r\n");
cout << "\nTesting\n ";
cout << request_msg;
HTTPRequestParser * parser = new HTTPRequestParser(request_msg);
if (parser->didParse()) {
httpRequest req = parser->getRequest();
if (strcmp(req._requestLine._httpVersion, "HTTP/1.1") != 0) {
cout << "\nCould not parse version expected=" << "HTTP/1.1"
<< " but got=" << req._requestLine._httpVersion << "\n ";
} else {
cout << "\nPassou httpVersion\n";
}
if (strcmp(req._requestLine._method, "GET") != 0) {
cout << "\nCould not parse version expected=" << "GET"
<< " but got=" << req._requestLine._method << "\n ";
} else {
cout << "\nPassou method\n";
}
if (strcmp(req._requestLine._requestUri, "/") != 0) {
cout << "\nCould not parse version expected=" << "/" << " but got="
<< req._requestLine._requestUri << "\n ";
} else {
cout << "\nPassou requestUri\n";
}
if (req._headers.size() == 1) {
HeaderElement * h = req._headers.head();
if (strcmp(h->object()->_name, "Host") != 0) {
cout << "\nCould not parse header expected=" << "Host"
<< " but got=" << h->object()->_name << "\n ";
} else {
cout << "\nPassou header Host\n";
}
if (strcmp(h->object()->_value, "www.google.com") != 0) {
cout << "\nCould not parse header expected="
<< " www.google.com" << " but got="
<< h->object()->_value << " eol\n ";
} else {
cout << "\nPassou header Host valor\n";
}
} else {
cout << "\nCould not parse headers expected=" << 1 << " but got="
<< req._headers.size() << "\n ";
}
} else {
cout << "\nCould not parse msg" << "\n ";
}
}
voidtest_parser2() {
char request_msg[80] = "POST /enlighten/calais.asmx/Enlighten HTTP/1.1";
cout << "\nMsg=" << request_msg;
strcat(request_msg, "\r\n");
strcat(request_msg, "Host: api.opencalais.com");
strcat(request_msg, "\r\n");
strcat(request_msg, "Content-Type: application/x-www-form-urlencoded");
strcat(request_msg, "\r\n");
strcat(request_msg, "Content-Length: 34");
strcat(request_msg, "\r\n");
strcat(request_msg, "\r\n");
strcat(request_msg, "licenseID=string&content=string¶msXML=string");
strcat(request_msg, "\r\n");
cout << "\nTesting2" << request_msg;
HTTPRequestParser * parser = new HTTPRequestParser(request_msg);
if (parser->didParse()) {
httpRequest req = parser->getRequest();
if (strcmp(req._requestLine._httpVersion, "HTTP/1.1") != 0) {
cout << "\nCould not parse version expected=" << "HTTP/1.1"
<< " but got=" << req._requestLine._httpVersion;
}
if (strcmp(req._requestLine._method, "POST") != 0) {
cout << "\nCould not parse version expected=" << "POST"
<< " but got=" << req._requestLine._method;
}
if (strcmp(req._requestLine._requestUri, "/enlighten/calais.asmx/Enlighten") != 0) {
cout << "\nCould not parse version expected="
<< "/enlighten/calais.asmx/Enlighten" << " but got="
<< req._requestLine._requestUri;
}
if (req._headers.size() == 3) {
HeaderElement * he = req._headers.head();
header * h = he->object();
if (strcmp(h->_name , "Host")) {
cout << "\nCould not parse header expected=" << "Host"
<< " but got=" << h->_name;
}
if (strcmp(h->_value, "api.opencalais.com") != 0) {
cout << "\nCould not parse header expected="
<< "api.opencalais.com" << " but got=" << h->_value;
}
he = he->next();
h = he->object();
if (strcmp(h->_name, "Content-Type") != 0) {
cout << "\nCould not parse header expected=" << "Content-Type"
<< " but got=" << h->_name;
}
if (strcmp(h->_value, "application/x-www-form-urlencoded") != 0) {
cout << "\nCould not parse header expected="
<< "application/x-www-form-urlencoded" << " but got="
<< h->_value;
}
he = he->next();
h = he->object();
if (strcmp(h->_name, "Content-Length") != 0) {
cout << "\nCould not parse header expected=" << "Content-Length"
<< " but got=" << h->_name;
}
if (strcmp(h->_value, "34") != 0) {
cout << "\nCould not parse header expected=" << "34"
<< " butgot=" << h->_value;
}
} else {
cout << "\nCould not parse headers expected=" << 1 << " but got="
<< req._headers.size();
}
//if (strcmp(req._body, "licenseID=string&content=string¶msXML=string") != 0) {
// cout << "\nCould not read body expected="
// << "licenseID=string&content=string¶msXML=string";
// cout << "\n but got=" << req._body;
//}
} else {
cout << "\nCould not parse msg=" << request_msg;
}
}
voidtest_writer() {
constchar * response_msg = "HTTP/1.1 200 OK\r\n\r\nHello world!\r\n";
cout << "\nTesting" << response_msg;
httpResponse * res = new httpResponse();
cout << "\nCriou o response";
strcpy(res->_body, "Hello world!");
strcpy(res->_statusLine._httpVersion, "HTTP/1.1");
strcpy(res->_statusLine._status, "200");
cout << "\nTerminou de montar o response";
HTTPResponseWriter * writer = newHTTPResponseWriter(res);
cout << "\nCriou o writer";
constchar * writer_response = writer->getResponse();
if (strcmp(writer_response, response_msg) != 0) {
cout << "\nWriter Ok=" << writer_response;
} else {
cout << "\nCould not write msg=" << response_msg << " got="
<< writer_response;
}
cout << "\nTerminou de montar o writer";
}
voidtest_writer2() {
char response_msg[80] = "HTTP/1.1 200 OK";
strcat(response_msg, "\r\n");
strcat(response_msg, "Content-Type: text/xml; charset=utf-8");
strcat(response_msg, "\r\n");
strcat(response_msg, "Content-Length: 8");
strcat(response_msg, "\r\n");
strcat(response_msg, "\r\n");
strcat(response_msg, "testando");
strcat(response_msg, "\r\n");
cout << "\nTesting" << response_msg;
httpResponse * res = new httpResponse();
strcpy(res->_body, "testando");
strcpy(res->_statusLine._httpVersion, "HTTP/1.1");
strcpy(res->_statusLine._status, "200");
List<header, HeaderElement> * headers = newList<header, HeaderElement>();
header * type = new header();
strcpy(type->_name, "Content-Type");
strcpy(type->_value, "text/xml; charset=utf-8");
headers->insert(newHeaderElement(type));
header * length = new header();
strcpy(type->_name, "Content-Length");
strcpy(type->_value, "8");
headers->insert(newHeaderElement(length));
res->_headers = *headers;
strcpy(res->_body, "testando");
HTTPResponseWriter * writer = new HTTPResponseWriter(res);
constchar * writer_response = writer->getResponse();
if (strcmp(writer_response, response_msg) != 0) {
cout << "\nWriter Ok=" << writer_response;
} else {
cout << "\nCould not write msg=" << response_msg << " got="
<< writer_response;
}
}
Nesse teste o objetivo é utilizar todos os componentes criados. A ideia é fazer um web service para atender a pedidos de temperatura.
- Teste : Fazer pedido para /temperatura e receber uma resposta com um JSON:
- Componentes Utilizados:
--- HTTPRequestParser (ler o pedido HTTP)
--- HTTPResponseWriter (escrever o pedido HTTP)
--- HTTPServer (Receber o pedido e mandar para um HTTPHandler, se o pedido for válido e existir um Handler para o "PATH" requisitado)
--- HTTPRestHandler (responsavel por mapear todos os recursos para o seu "PATH", irá montar a resposta baseado na resposta do Recurso)
--- TemperaturaJSONResource (recurso responsável por responder o valor em JSON para o pedido)
--- PathParser (irá verificar se o PATH informado está compatível com o PADRÃO esperado)
- Resultados Esperados:
-- Espera-se receber uma resposta HTTP da seguinte forma:
--- HTTP OK
--- Header: Content-Type=application/json
--- Header: Content-Lenght
--- Body={"temperatura":"104F"}
#include<utility/ostream.h>
#include<utility/malloc.h>
#include<http.h>
#include<utility/json.h>
__USING_SYS
OStream cout;
HTTPServer * server;
classTemperaturaJsonRestResource: publicHTTPServer::JsonRestResource {
public:
TemperaturaJsonRestResource(constchar * path) :
JsonRestResource(path) {
}
char * onRequest(List<char, PathParser::ArgsElement> * args) {
JsonWriter * writer = new JsonWriter();
constchar * tempKey = "temperatura";
constchar * tempValue = "104F";
writer->write(tempKey, tempValue);
char * response = writer->getJson();
cout << "Temperatura Resource Response=" << response << "\n";
return response;
}
};
intmain() {
cout << "HTTP Integration Test\n";
server = new HTTPServer(80);
constchar * path = "/temperatura";
HTTPServer::HTTPRestHandler * handler = newHTTPServer::HTTPRestHandler(
path);
TemperaturaJsonRestResource * resource = new TemperaturaJsonRestResource(
path);
handler->addHandler(resource);
server->addHandler(handler);
constchar * request_msg = "GET /temperatura HTTP/1.1";
char * response = server->receivedLocal(request_msg, strlen(request_msg));
cout << response << "\n ";
return 0;
}
http://www.gliffy.com/pubdoc/3601660/L.png
HTTP
http://www.w3.org/Protocols/rfc2616/rfc2616.html
JSON
http://www.ietf.org/rfc/rfc4627.txt
REST