/** * This is an asynchronous protocol implementation for HTTP. * It includes special features to store binary data coming * from a SINQ http histogram memory in a sinqdata object. * Which has to be specified on initialisation. * * copyright: see file COPYRIGHT * * Mark Koennecke, June 2008 * * Expanded to handle post:url:data style and node:path:data syntax * in preparation for a full second generation HM object. * * Mark Koennecke, May 2009 */ #include #include #include #include #include #include #include "uselect.h" #include "sicshipadaba.h" /*---------------------------------------------------------------------*/ typedef struct { ghttp_request *request; char *userName; char *password; pSICSData binData; pHdb node; } HttpProt, *pHttpProt; /*---------------------------------------------------------------------*/ static int configRequest(Ascon * a) { pHttpProt pHttp = (pHttpProt) a->private; pDynString request; char *uri = NULL; char *data, *pPtr, *path, *dataCopy; pHttp->node = NULL; ghttp_clean(pHttp->request); ghttp_set_header(pHttp->request, "connection", "keep-alive"); request = CreateDynString(64, 64); if (request == NULL) { AsconError(a, "Out of memory", 0); return 0; } DynStringConcat(request, "http://"); DynStringConcat(request, a->hostport); DynStringConcatChar(request, '/'); data = GetCharArray(a->wrBuffer); if(strstr(data,"node:") == data){ dataCopy = strdup(data); path = strchr(dataCopy,':')+1; uri = strchr(path,':'); *uri = '\0'; uri++; pHttp->node = FindHdbNode(NULL,path,pServ->dummyCon); DynStringConcat(request,uri); ghttp_set_type(pHttp->request, ghttp_type_get); free(dataCopy); } else if(strstr(data,"post:") == data){ dataCopy = strdup(data); uri = strchr(dataCopy,':') + 1; pPtr = strchr(uri,':'); *pPtr = '\0'; ghttp_set_type(pHttp->request, ghttp_type_post); DynStringConcat(request,uri); pPtr++; ghttp_set_body(pHttp->request,strdup(pPtr), strlen(pPtr)); free(dataCopy); } else { ghttp_set_type(pHttp->request, ghttp_type_get); DynStringConcat(request, GetCharArray(a->wrBuffer)); } uri = GetCharArray(request); if (ghttp_set_uri(pHttp->request, uri) < 0) { AsconError(a, "Bad URL", 0); return 0; } if (pHttp->userName != NULL && pHttp->password != NULL) { ghttp_set_authinfo(pHttp->request, pHttp->userName, pHttp->password); } ghttp_set_sync(pHttp->request, ghttp_async); ghttp_prepare(pHttp->request); DeleteDynString(request); return 1; } /*---------------------------------------------------------------------*/ static void handleReply(Ascon * a) { char *pPtr = NULL, *pType = NULL, *path = NULL; int len, i, *dataPtr = NULL; HistInt *hmData = NULL; pHttpProt pHttp = (pHttpProt) a->private; pPtr = ghttp_get_body(pHttp->request); len = ghttp_get_body_len(pHttp->request); if (strstr(pPtr, "ERROR") != NULL) { AsconError(a, pPtr, 0); DynStringConcat(a->rdBuffer, pPtr); } else if (strstr(pPtr, "Authentication Error") != NULL) { DynStringConcat(a->rdBuffer, pPtr); AsconError(a, pPtr, 0); } else { pType = (char *) ghttp_get_header(pHttp->request, "Content-Type"); if (strstr(pType, "sinqhm") == NULL) { /* text data */ for (i = 0; i < len; i++) { DynStringConcatChar(a->rdBuffer, pPtr[i]); } } else { hmData = (HistInt *) pPtr; len = len / sizeof(HistInt); if(pHttp->node == NULL){ clearSICSData(pHttp->binData); dataPtr = getSICSDataPointer(pHttp->binData, 0, len); for (i = 0; i < len; i++) { dataPtr[i] = htonl(hmData[i]); } assignSICSType(pHttp->binData, 0, len, INTTYPE); DynStringCopy(a->rdBuffer, "SICSDATA"); } else { if(pHttp->node->value.arrayLength != len){ if(pHttp->node->value.v.intArray != NULL){ free(pHttp->node->value.v.intArray); } pHttp->node->value.v.intArray = malloc(len*sizeof(int)); if(pHttp->node->value.v.intArray == NULL){ AsconError(a,"Out of memory ",0); return; } pHttp->node->value.arrayLength = len; } for(i = 0; i < len; i++){ pHttp->node->value.v.intArray[i] = htonl(hmData[i]); } NotifyHipadabaPar(pHttp->node,NULL); /* path = GetHipadabaPath(pHttp->node); if(path != NULL){ printf("Sinqhttpprot has updated node: %s\n", path); free(path); } */ } } } } /*---------------------------------------------------------------------*/ static int HttpHandler(Ascon * a) { ghttp_status status; pHttpProt pHttp = (pHttpProt) a->private; int socke, selStat; fd_set rmask; struct timeval tmo = { 0, 0 }; char buffer[1024]; ghttp_current_status procStatus; switch (a->state) { case AsconConnectStart: a->state = AsconConnecting; break; case AsconConnecting: a->state = AsconConnectDone; /* success */ break; case AsconWriteStart: if (configRequest(a)) { a->state = AsconWriting; } return 1; break; case AsconWriting: status = ghttp_process(pHttp->request); if (status == ghttp_error) { ghttp_close(pHttp->request); configRequest(a); status = ghttp_process(pHttp->request); } if (status == ghttp_error) { AsconError(a, "Server error", 0); DynStringConcat(a->rdBuffer, "Server error"); a->state = AsconReadDone; } else { procStatus = ghttp_get_status(pHttp->request); if (procStatus.proc == ghttp_proc_response_hdrs || procStatus.proc == ghttp_proc_response) { a->state = AsconWriteDone; } else { return 0; } } a->start = DoubleTime(); DynStringClear(a->rdBuffer); break; case AsconReadStart: socke = ghttp_get_socket(pHttp->request); FD_ZERO(&rmask); FD_SET(socke, &rmask); selStat = uselect(socke + 1, &rmask, NULL, NULL, &tmo); if (selStat > 0 && FD_ISSET(socke, &rmask)) { status = ghttp_process(pHttp->request); a->state = AsconReading; } else { if (DoubleTime() > a->start + a->timeout) { AsconError(a, "read timeout", 0); a->state = AsconTimeout; /* this to clear the line */ ghttp_close(pHttp->request); } } return 1; break; case AsconReading: socke = ghttp_get_socket(pHttp->request); /* FD_ZERO(&rmask); FD_SET(socke, &rmask); selStat = uselect(socke + 1, &rmask, NULL, NULL, &tmo); if (selStat == 0) { return 1; } */ status = ghttp_process(pHttp->request); switch (status) { case ghttp_not_done: if (DoubleTime() > a->start + a->timeout) { AsconError(a, "read timeout", 0); a->state = AsconTimeout; /* this to clear the line */ ghttp_close(pHttp->request); } return 0; case ghttp_done: handleReply(a); a->state = AsconReadDone; break; case ghttp_error: /* * A first error may not be an error but a * reconnect */ ghttp_close(pHttp->request); configRequest(a); status = ghttp_process(pHttp->request); if (status == ghttp_error) { AsconError(a, "Server error", 0); DynStringConcat(a->rdBuffer, "Server error"); a->state = AsconReadDone; } return 1; break; } break; default: return AsconStdHandler(a); } return 1; } /*------------------------------------------------------------------------*/ static void killHttp(void *data) { pHttpProt prot = (pHttpProt) data; if (prot == NULL) { return; } if (prot->request != NULL) { ghttp_close(prot->request); } if (prot->password != NULL) { free(prot->password); } if (prot->userName != NULL) { free(prot->userName); } free(prot); } /*------------------------------------------------------------------------*/ static int HttpProtInit(Ascon * a, SConnection * con, int argc, char *argv[]) { pHttpProt pHttp = NULL; pHttp = calloc(sizeof(HttpProt), 1); if (pHttp == NULL) { SCWrite(con, "ERROR: out of memory in HttpProtInit", eError); return 0; } if (argc < 3) { return 0; } a->hostport = strdup(argv[1]); pHttp->binData = (pSICSData) FindCommandData(pServ->pSics, argv[2], "SICSData"); if (pHttp->binData == NULL) { SCWrite(con, "ERROR: SICSData objct not found", eError); return 0; } if (argc > 3) { a->timeout = atof(argv[3]); } else { a->timeout = 10.; } if (argc > 5) { pHttp->userName = strdup(argv[4]); pHttp->password = strdup(argv[5]); } pHttp->request = ghttp_request_new(); a->private = pHttp; a->killPrivate = killHttp; a->state = AsconConnectStart; return 1; } /*-------------------------------------------------------------------------*/ void AddHttpProtocoll() { AsconProtocol *prot = NULL; prot = calloc(sizeof(AsconProtocol), 1); prot->name = strdup("sinqhttp"); prot->init = HttpProtInit; prot->handler = HttpHandler; AsconInsertProtocol(prot); }