#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 #endif #include "aqhome/http/endpoint_http_p.h" #include #include #include #include #include #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