blob: 06385cddf21a33c9e270c1e7958f9861120aa78f [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2017-2018 Marc Jakobi, github.com/MrcJkb, fortiss GmbH
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Marc Jakobi - initial implementation
* Jose Cabral - Use CIEC_STRING instead of const char* and cleanning of functions
********************************************************************************/
#include "httpparser.h"
#include <stdio.h>
#include <string.h>
#include "devlog.h"
using namespace forte::com_infra;
void CHttpParser::createGetRequest(CIEC_STRING& paDest, const CIEC_STRING& paHost, const CIEC_STRING& paPath) {
CHttpParser::addCommonHeader(paDest, paHost, paPath, CHttpComLayer::e_GET);
CHttpParser::addHeaderEnding(paDest);
}
void CHttpParser::createPutPostRequest(CIEC_STRING& paDest, const CIEC_STRING& paHost, const CIEC_STRING& paPath, const CIEC_STRING& paData,
const CIEC_STRING& paContentType, CHttpComLayer::ERequestType paType) {
CHttpParser::addCommonHeader(paDest, paHost, paPath, paType);
paDest.append("\r\nContent-type: ");
paDest.append(paContentType.getValue());
paDest.append("\r\nContent-length: ");
changePutPostData(paDest, paData);
}
bool CHttpParser::changePutPostData(CIEC_STRING& paDest, const CIEC_STRING& paData) {
char* helperChar = strstr(paDest.getValue(), "length: ");
if(0 != helperChar) {
helperChar += sizeof("length: ") - 1;
*helperChar = '\0';
paDest = paDest.getValue(); //will shrink the length of the string to the new ending
char contentLength[scmMaxLengthOfContent];
snprintf(contentLength, sizeof(contentLength), "%zu", strlen(paData.getValue()));
paDest.append(contentLength);
CHttpParser::addHeaderEnding(paDest);
paDest.append(paData.getValue());
return true;
} else { // wrong request?
DEVLOG_ERROR("[HTTP Parser] PUT/POST request was wrongly created\n");
return false;
}
}
bool CHttpParser::parseResponse(CIEC_STRING& paBody, CIEC_STRING& paResponseCode, char* paSrc) {
if(CHttpParser::getHttpResponseCode(paResponseCode, paSrc)) {
char* helperChar = strstr(paSrc, "\r\n\r\n"); // Extract data from HTTP response char
if(0 != helperChar) {
helperChar += sizeof("\r\n\r\n") - 1;
paBody = helperChar;
} else { // Empty response received
DEVLOG_INFO("[HTTP Parser] Empty content response received\n");
paBody = "";
}
return true;
}
return false;
}
bool forte::com_infra::CHttpParser::parseGetRequest(CIEC_STRING& paPath, CSinglyLinkedList<CIEC_STRING>& paParameterNames,
CSinglyLinkedList<CIEC_STRING>& paParameterValues, char* paData) {
if(0 == strncmp(paData, "GET ", 4)) {
paData = paData + 4;
char* endOfPath = strstr(paData, " ");
if(endOfPath != 0) {
*endOfPath = '\0';
char* startOfParameters = strstr(paData + 1, "?");
if(startOfParameters != 0) {
*startOfParameters = '\0';
startOfParameters++;
parseGETParameters(startOfParameters, paParameterNames, paParameterValues);
}
paPath = paData;
} else {
DEVLOG_ERROR("[HTTP Parser] Invalid HTTP Get request. No GET string found\n");
return false;
}
} else {
DEVLOG_ERROR("[HTTP Parser] Invalid HTTP Get request. No space after path found\n");
return false;
}
return true;
}
bool forte::com_infra::CHttpParser::parsePutPostRequest(CIEC_STRING& paPath, CIEC_STRING &paContent, char* paData) {
if(0 == strncmp(paData, "PUT ", 4)) {
paData += sizeof("PUT ") - 1;
} else if(0 == strncmp(paData, "POST ", 5)) {
paData += sizeof("POST ") - 1;
} else {
DEVLOG_ERROR("[HTTP Parser] Invalid HTTP PUT/POST request. No PUT/POST string found\n");
return false;
}
char* endOfPath = strstr(paData, " ");
if(endOfPath != 0) {
*endOfPath = '\0';
paPath = paData;
paData = strstr(endOfPath + 1, "\r\n\r\n");
if(paData != 0) {
paData += sizeof("\r\n\r\n") - 1;
paContent = paData;
} else {
DEVLOG_ERROR("[HTTP Parser] Invalid HTTP PUT/POST request. No content was found\n");
return false;
}
} else {
DEVLOG_ERROR("[HTTP Parser] Invalid HTTP PUT/POST request. No space after path found\n");
return false;
}
return true;
}
CHttpComLayer::ERequestType forte::com_infra::CHttpParser::getTypeOfRequest(const char* paRequest) {
if(0 == strncmp(paRequest, "GET ", 4)) {
return CHttpComLayer::ERequestType::e_GET;
} else if(0 == strncmp(paRequest, "PUT ", 4)) {
return CHttpComLayer::ERequestType::e_PUT;
} else if(0 == strncmp(paRequest, "POST ", 5)) {
return CHttpComLayer::ERequestType::e_POST;
} else {
DEVLOG_ERROR("[HTTP Parser] Invalid HTTP request\n");
return CHttpComLayer::ERequestType::e_NOTSET;
}
}
void forte::com_infra::CHttpParser::createResponse(CIEC_STRING& paDest, const CIEC_STRING& paResult, const CIEC_STRING& paContentType,
const CIEC_STRING& paData) {
paDest = paResult;
if(paData.empty()) {
paDest.append("\r\n");
} else {
paDest.append("\r\nContent-type: ");
paDest.append(paContentType.getValue());
paDest.append("\r\nContent-length: ");
changePutPostData(paDest, paData);
}
}
void CHttpParser::addCommonHeader(CIEC_STRING& paDest, const CIEC_STRING& paHost, const CIEC_STRING& paPath, CHttpComLayer::ERequestType paType) {
switch(paType){
case CHttpComLayer::e_GET:
paDest = "GET ";
break;
case CHttpComLayer::e_PUT:
paDest = "PUT ";
break;
case CHttpComLayer::e_POST:
paDest = "POST ";
break;
default:
DEVLOG_ERROR("[HTTP Parser] Unexpected HTTP Type when adding header\n");
break;
}
paDest.append(paPath.getValue());
paDest.append(" HTTP/1.1\r\n");
paDest.append("Host: ");
paDest.append(paHost.getValue());
}
void CHttpParser::addHeaderEnding(CIEC_STRING& paDest) {
paDest.append("\r\n\r\n");
}
bool CHttpParser::getHttpResponseCode(CIEC_STRING& paDest, char* paSrc) {
//HTTP-Version SP Status-Code SP Reason-Phrase CRLF (SP = space)
char* helperChar = strstr(paSrc, "\r\n");
if(helperChar != 0) {
*helperChar = '\0';
CParameterParser parser(paSrc, ' ');
if(3 <= parser.parseParameters()) { //Reason-Phrase can contain spaces in it
paDest = parser[1];
} else {
DEVLOG_ERROR("[HTTP Parser] Invalid HTTP response. The status line is not well defined\n");
return false;
}
*helperChar = '\r';
return true;
} else {
DEVLOG_ERROR("[HTTP Parser] Invalid HTTP response. No line ending was found.\n");
return false;
}
}
unsigned int forte::com_infra::CHttpParser::parseGETParameters(char* paParameters, CSinglyLinkedList<CIEC_STRING>& paParameterNames,
CSinglyLinkedList<CIEC_STRING>& paParameterValues) {
paParameterNames.clearAll();
paParameterValues.clearAll();
unsigned int retVal = 0;
char* startOfName = paParameters;
bool endOfParameters = false;
while('\0' != *startOfName && !endOfParameters) {
char* startOfValue = strstr(startOfName, "=");
if(0 != startOfValue) {
*startOfValue = '\0';
startOfValue++;
} else {
paParameterNames.clearAll();
paParameterValues.clearAll();
retVal = 0;
break;
}
char* nextName = strstr(startOfValue, "&");
endOfParameters = false;
if(0 != nextName) {
*nextName = '\0';
} else {
endOfParameters = true;
}
paParameterNames.pushBack(startOfName);
paParameterValues.pushBack(startOfValue);
retVal++;
if(!endOfParameters) {
startOfName = ++nextName;
}
}
return retVal;
}