aqhome: completed adapting to msgio2 interface.
This commit is contained in:
302
aqhome/http/endpoint_http.c
Normal file
302
aqhome/http/endpoint_http.c
Normal file
@@ -0,0 +1,302 @@
|
||||
#if 0
|
||||
|
||||
/****************************************************************************
|
||||
* This file is part of the project AqHome.
|
||||
* AqHome (c) by 2023 Martin Preuss, all rights reserved.
|
||||
*
|
||||
* The license for this file can be found in the file COPYING which you
|
||||
* should have received along with this file.
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
|
||||
#include "aqhome/http/endpoint_http_p.h"
|
||||
|
||||
|
||||
#include <gwenhywfar/endpoint_tcpc.h>
|
||||
#include <gwenhywfar/debug.h>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
|
||||
|
||||
#define AQH_ENDPOINT_HTTP_NAME "http"
|
||||
#define AQH_ENDPOINT_HTTP_BUFFERSIZE 1024
|
||||
|
||||
#define AQH_ENDPOINT_HTTP_BOOKMARK_HEADER 0
|
||||
#define AQH_ENDPOINT_HTTP_BOOKMARK_BODY 1
|
||||
|
||||
|
||||
|
||||
GWEN_INHERIT(GWEN_MSG_ENDPOINT, AQH_ENDPOINT_HTTP)
|
||||
|
||||
|
||||
|
||||
static void GWENHYWFAR_CB _freeData(void *bp, void *p);
|
||||
static int _handleReadable(GWEN_MSG_ENDPOINT *ep, GWEN_UNUSED GWEN_MSG_ENDPOINT_MGR *emgr);
|
||||
static int _readCommand(GWEN_MSG_ENDPOINT *ep, const uint8_t **pPtr, int *pLen);
|
||||
static int _readHeader(GWEN_MSG_ENDPOINT *ep, const uint8_t **pPtr, int *pLen);
|
||||
static int _readBody(GWEN_MSG_ENDPOINT *ep, const uint8_t **pPtr, int *pLen);
|
||||
static int _readLineCheckEmpty(GWEN_MSG_ENDPOINT *ep, const uint8_t **pPtr, int *pLen);
|
||||
static int _calcCurrentLineLength(const GWEN_MSG_ENDPOINT *ep);
|
||||
static int _parseCommandLineCheckIfHeaderNeeded(char *buffer, GWEN_DB_NODE *dbCurrentReadCommand);
|
||||
|
||||
|
||||
|
||||
|
||||
GWEN_MSG_ENDPOINT *AQH_HttpEndpoint_new(const char *host, int port, const char *name, int groupId)
|
||||
{
|
||||
GWEN_MSG_ENDPOINT *ep;
|
||||
AQH_ENDPOINT_HTTP *xep;
|
||||
|
||||
ep=GWEN_TcpcEndpoint_new(host, port, name?name:AQH_ENDPOINT_HTTP_NAME, groupId);
|
||||
if (ep==NULL) {
|
||||
DBG_INFO(AQH_LOGDOMAIN, "here");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
GWEN_NEW_OBJECT(AQH_ENDPOINT_HTTP, xep);
|
||||
GWEN_INHERIT_SETDATA(GWEN_MSG_ENDPOINT, AQH_ENDPOINT_HTTP, ep, xep, _freeData);
|
||||
|
||||
xep->currentReadBuffer=GWEN_Buffer_new(0, 256, 0, 1);
|
||||
xep->currentReadHeader=GWEN_DB_Group_new("header");
|
||||
xep->currentReadCommand=GWEN_DB_Group_new("cmd");
|
||||
|
||||
GWEN_MsgEndpoint_SetDefaultBufferSize(ep, AQH_ENDPOINT_HTTP_BUFFERSIZE);
|
||||
// GWEN_MsgEndpoint_SetIsMsgCompleteFn(ep, _isMsgComplete);
|
||||
GWEN_MsgEndpoint_SetHandleReadableFn(ep, _handleReadable);
|
||||
|
||||
return ep;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void _freeData(void *bp, void *p)
|
||||
{
|
||||
AQH_ENDPOINT_HTTP *xep;
|
||||
|
||||
xep=(AQH_ENDPOINT_HTTP*) p;
|
||||
|
||||
GWEN_DB_Group_free(xep->currentReadHeader);
|
||||
GWEN_DB_Group_free(xep->currentReadCommand);
|
||||
GWEN_Buffer_free(xep->currentReadBuffer);
|
||||
|
||||
|
||||
GWEN_FREE_OBJECT(xep);
|
||||
}
|
||||
|
||||
|
||||
|
||||
int _handleReadable(GWEN_MSG_ENDPOINT *ep, GWEN_UNUSED GWEN_MSG_ENDPOINT_MGR *emgr)
|
||||
{
|
||||
AQH_ENDPOINT_HTTP *xep;
|
||||
int rv;
|
||||
uint8_t buffer[AQH_ENDPOINT_HTTP_BUFFERSIZE];
|
||||
const uint8_t *ptr;
|
||||
int len;
|
||||
|
||||
DBG_DEBUG(GWEN_LOGDOMAIN, "Reading from endpoint %s", GWEN_MsgEndpoint_GetName(ep));
|
||||
xep=GWEN_INHERIT_GETDATA(GWEN_MSG_ENDPOINT, AQH_ENDPOINT_HTTP, ep);
|
||||
do {
|
||||
rv=read(GWEN_MsgEndpoint_GetFd(ep), buffer, sizeof(buffer));
|
||||
} while( (rv<0) && errno==EINTR);
|
||||
if (rv<0) {
|
||||
if (errno==EAGAIN || errno==EWOULDBLOCK)
|
||||
return GWEN_ERROR_TRY_AGAIN;
|
||||
DBG_INFO(GWEN_LOGDOMAIN, "Error on read(): %s (%d)", strerror(errno), errno);
|
||||
return GWEN_ERROR_IO;
|
||||
}
|
||||
else if (rv==0) {
|
||||
DBG_INFO(GWEN_LOGDOMAIN, "EOF met on read()");
|
||||
return GWEN_ERROR_IO;
|
||||
}
|
||||
len=rv;
|
||||
ptr=buffer;
|
||||
|
||||
/* len=number of bytes read into buffer */
|
||||
while(len) {
|
||||
switch(xep->readMode) {
|
||||
case AQH_EndpointHttpd_ReadMode_Command:
|
||||
rv=_readCommand(ep, &ptr, &len);
|
||||
break;
|
||||
case AQH_EndpointHttpd_ReadMode_Headers:
|
||||
rv=_readHeader(ep, &ptr, &len);
|
||||
break;
|
||||
case AQH_EndpointHttpd_ReadMode_Body:
|
||||
rv=_readBody(ep, &ptr, &len);
|
||||
break;
|
||||
default:
|
||||
DBG_ERROR(AQH_LOGDOMAIN, "Unexpected read mode %d", xep->readMode);
|
||||
return GWEN_ERROR_INTERNAL;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* rv: 1 if packet complete, <0 on error, 0 if packet not complete */
|
||||
int _readCommand(GWEN_MSG_ENDPOINT *ep, const uint8_t **pPtr, int *pLen)
|
||||
{
|
||||
AQH_ENDPOINT_HTTP *xep;
|
||||
int rv;
|
||||
|
||||
xep=GWEN_INHERIT_GETDATA(GWEN_MSG_ENDPOINT, AQH_ENDPOINT_HTTP, ep);
|
||||
rv=_readLineCheckEmpty(ep, pPtr, pLen);
|
||||
if (rv>=0) {
|
||||
if (rv==1) {
|
||||
DBG_ERROR(AQH_LOGDOMAIN, "Empty command line received");
|
||||
return GWEN_ERROR_BAD_DATA;
|
||||
}
|
||||
|
||||
/* line complete, parse command */
|
||||
rv=_parseCommandLineCheckIfHeaderNeeded(GWEN_Buffer_GetStart(xep->currentReadBuffer), xep->currentReadCommand);
|
||||
if (rv==0) {
|
||||
/* no header follows, stay in readMode AQH_EndpointHttpd_ReadMode_Command for next command */
|
||||
DBG_INFO(AQH_LOGDOMAIN, "No header expected, packet finished");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* line complete, set bookmark 0=pos of header begin, advance readMode */
|
||||
GWEN_Buffer_SetBookmark(xep->currentReadBuffer, AQH_ENDPOINT_HTTP_BOOKMARK_HEADER, GWEN_Buffer_GetPos(xep->currentReadBuffer));
|
||||
xep->readMode=AQH_EndpointHttpd_ReadMode_Headers;
|
||||
DBG_INFO(AQH_LOGDOMAIN, "Start reading headers");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int _readHeader(GWEN_MSG_ENDPOINT *ep, const uint8_t **pPtr, int *pLen)
|
||||
{
|
||||
AQH_ENDPOINT_HTTP *xep;
|
||||
int rv;
|
||||
|
||||
xep=GWEN_INHERIT_GETDATA(GWEN_MSG_ENDPOINT, AQH_ENDPOINT_HTTP, ep);
|
||||
rv=_readLineCheckEmpty(ep, pPtr, pLen);
|
||||
if (rv==1) {
|
||||
/* line complete and empty */
|
||||
// TODO: parse header
|
||||
GWEN_Buffer_SetBookmark(xep->currentReadBuffer, AQH_ENDPOINT_HTTP_BOOKMARK_BODY, GWEN_Buffer_GetPos(xep->currentReadBuffer));
|
||||
xep->readMode=AQH_EndpointHttpd_ReadMode_Body;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int _readBody(GWEN_MSG_ENDPOINT *ep, const uint8_t **pPtr, int *pLen)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* ret: -1 if line not complete, 1 if line complete and empty, 0 if line complete and not empty */
|
||||
int _readLineCheckEmpty(GWEN_MSG_ENDPOINT *ep, const uint8_t **pPtr, int *pLen)
|
||||
{
|
||||
AQH_ENDPOINT_HTTP *xep;
|
||||
const uint8_t *ptr=*pPtr;
|
||||
int len=*pLen;
|
||||
const uint8_t *p2;
|
||||
int rv;
|
||||
|
||||
xep=GWEN_INHERIT_GETDATA(GWEN_MSG_ENDPOINT, AQH_ENDPOINT_HTTP, ep);
|
||||
|
||||
p2=memchr(ptr, 10, len);
|
||||
if (p2) {
|
||||
int l2;
|
||||
uint32_t pos;
|
||||
int currentLineLength;
|
||||
|
||||
l2=p2-ptr+1; /* also count the LF byte itself */
|
||||
GWEN_Buffer_AppendBytes(xep->currentReadBuffer, (const char*) ptr, l2);
|
||||
currentLineLength=_calcCurrentLineLength(ep);
|
||||
pos=GWEN_Buffer_GetPos(xep->currentReadBuffer);
|
||||
/* set bookmark 0: pos of header begin */
|
||||
GWEN_Buffer_SetBookmark(xep->currentReadBuffer, 0, pos);
|
||||
xep->lastLineStartPos=pos;
|
||||
ptr+=l2;
|
||||
len-=l2;
|
||||
rv=(currentLineLength==0)?1:0;
|
||||
}
|
||||
else {
|
||||
GWEN_Buffer_AppendBytes(xep->currentReadBuffer, (const char*) ptr, len);
|
||||
ptr+=len;
|
||||
len=0;
|
||||
rv=-1;
|
||||
}
|
||||
|
||||
*pPtr=ptr;
|
||||
*pLen=len;
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int _calcCurrentLineLength(const GWEN_MSG_ENDPOINT *ep)
|
||||
{
|
||||
AQH_ENDPOINT_HTTP *xep;
|
||||
const uint8_t *ptr;
|
||||
int len=0;
|
||||
|
||||
xep=GWEN_INHERIT_GETDATA(GWEN_MSG_ENDPOINT, AQH_ENDPOINT_HTTP, ep);
|
||||
ptr=(const uint8_t*) (GWEN_Buffer_GetStart(xep->currentReadBuffer)+xep->lastLineStartPos);
|
||||
while(*ptr && *ptr!=10 && *ptr!=13)
|
||||
len++;
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int _parseCommandLineCheckIfHeaderNeeded(char *buffer, GWEN_DB_NODE *dbCurrentReadCommand)
|
||||
{
|
||||
char *p;
|
||||
char *s;
|
||||
|
||||
s=buffer;
|
||||
|
||||
/* read command */
|
||||
p=strchr(s, ' ');
|
||||
if (!p) {
|
||||
DBG_ERROR(AQH_LOGDOMAIN, "Bad format of HTTP request (%s)", buffer);
|
||||
return GWEN_ERROR_INVALID;
|
||||
}
|
||||
*p=0;
|
||||
GWEN_DB_SetCharValue(dbCurrentReadCommand, GWEN_DB_FLAGS_OVERWRITE_VARS, "command", s);
|
||||
*p=' '; /* restore buffer */
|
||||
p++;
|
||||
s=p;
|
||||
|
||||
/* read URL */
|
||||
p=strchr(s, ' ');
|
||||
if (!p) {
|
||||
DBG_ERROR(AQH_LOGDOMAIN, "Bad format of HTTP request (%s)", buffer);
|
||||
return GWEN_ERROR_INVALID;
|
||||
}
|
||||
*p=0;
|
||||
GWEN_DB_SetCharValue(dbCurrentReadCommand, GWEN_DB_FLAGS_OVERWRITE_VARS, "url", s);
|
||||
*p=' '; /* restore buffer */
|
||||
p++;
|
||||
s=p;
|
||||
|
||||
if (*s==0) {
|
||||
/* no protocol information follows, so we assume HTTP/0.9 */
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
GWEN_DB_SetCharValue(dbCurrentReadCommand, GWEN_DB_FLAGS_OVERWRITE_VARS, "protocol", s);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user