aqhome: completed adapting to msgio2 interface.
This commit is contained in:
76
aqhome/http/0BUILD
Normal file
76
aqhome/http/0BUILD
Normal file
@@ -0,0 +1,76 @@
|
||||
<?xml?>
|
||||
|
||||
<gwbuild>
|
||||
|
||||
<target type="ConvenienceLibrary" name="aqhhttp" >
|
||||
|
||||
<includes type="c" >
|
||||
$(gwenhywfar_cflags)
|
||||
-I$(topsrcdir)
|
||||
-I$(topbuilddir)
|
||||
</includes>
|
||||
|
||||
<includes type="tm2" >
|
||||
--include=$(builddir)
|
||||
--include=$(srcdir)
|
||||
</includes>
|
||||
|
||||
|
||||
<define name="BUILDING_AQHOME" />
|
||||
|
||||
<setVar name="local/cflags">$(visibility_cflags)</setVar>
|
||||
|
||||
|
||||
<setVar name="tm2flags" >
|
||||
--api=AQHOME_API
|
||||
</setVar>
|
||||
|
||||
<setVar name="local/typefiles" >
|
||||
</setVar>
|
||||
|
||||
<setVar name="local/built_sources" >
|
||||
</setVar>
|
||||
|
||||
<setVar name="local/built_headers_pub">
|
||||
</setVar>
|
||||
|
||||
|
||||
<setVar name="local/built_headers_priv" >
|
||||
</setVar>
|
||||
|
||||
|
||||
<headers dist="false" install="$(pkgincludedir)/ipc" >
|
||||
$(local/built_headers_pub)
|
||||
</headers>
|
||||
|
||||
|
||||
<headers dist="true" install="$(pkgincludedir)/http" >
|
||||
endpoint_http.h
|
||||
</headers>
|
||||
|
||||
|
||||
<headers dist="true" >
|
||||
endpoint_http_p.h
|
||||
</headers>
|
||||
|
||||
|
||||
<sources>
|
||||
$(local/typefiles)
|
||||
|
||||
endpoint_http.c
|
||||
</sources>
|
||||
|
||||
|
||||
<extradist>
|
||||
</extradist>
|
||||
|
||||
|
||||
<useTargets>
|
||||
</useTargets>
|
||||
|
||||
<subdirs>
|
||||
</subdirs>
|
||||
|
||||
</target>
|
||||
|
||||
</gwbuild>
|
||||
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
|
||||
22
aqhome/http/endpoint_http.h
Normal file
22
aqhome/http/endpoint_http.h
Normal file
@@ -0,0 +1,22 @@
|
||||
/****************************************************************************
|
||||
* 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.
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef AQH_ENDPOINT_HTTP_H
|
||||
#define AQH_ENDPOINT_HTTP_H
|
||||
|
||||
|
||||
#include <aqhome/api.h>
|
||||
|
||||
#include <gwenhywfar/endpoint.h>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
42
aqhome/http/endpoint_http_p.h
Normal file
42
aqhome/http/endpoint_http_p.h
Normal file
@@ -0,0 +1,42 @@
|
||||
/****************************************************************************
|
||||
* 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.
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef AQH_ENDPOINT_HTTP_P_H
|
||||
#define AQH_ENDPOINT_HTTP_P_H
|
||||
|
||||
|
||||
|
||||
#include "aqhome/http/endpoint_http.h"
|
||||
|
||||
#include <gwenhywfar/db.h>
|
||||
#include <gwenhywfar/buffer.h>
|
||||
|
||||
|
||||
enum {
|
||||
AQH_EndpointHttpd_ReadMode_Command=0,
|
||||
AQH_EndpointHttpd_ReadMode_Headers,
|
||||
AQH_EndpointHttpd_ReadMode_Body
|
||||
};
|
||||
|
||||
|
||||
|
||||
typedef struct AQH_ENDPOINT_HTTP AQH_ENDPOINT_HTTP;
|
||||
struct AQH_ENDPOINT_HTTP {
|
||||
int readMode;
|
||||
GWEN_BUFFER *currentReadBuffer;
|
||||
GWEN_DB_NODE *currentReadCommand;
|
||||
GWEN_DB_NODE *currentReadHeader;
|
||||
int currentBodyPos;
|
||||
int currentBodySize;
|
||||
int lastLineStartPos;
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user