/* @file aqp_opalstatus.c * @brief * Implements a simple protocol to GET reactor status using an HTTP/1.1 GET. * It sets the UserAgent as SICS to avoid re-directs because we don't handle them. * If the HTTP status is not OK then it returns the status in the data buffer. */ #include "../made_config.h" #include #include #define HTTP_OK "HTTP/1.1 200 OK" #define HTTP11 " HTTP/1.1\r\n" #if defined SICS_VERSION && defined SICS_REVISION #define USERAGENT "User-Agent: SICS Version = " SICS_VERSION " Revision = " SICS_REVISION "\r\n" #else #define USERAGENT "User-Agent: SICS\r\n" #endif #define HOST "Host: neutron.ansto.gov.au\r\n" enum replystates {START, HEADER, CHKHEADEREND, MSG}; static pAsyncProtocol OPAL_Protocol = NULL; static int OPAL_Tx(pAsyncProtocol p, pAsyncTxn txn) { if (txn == NULL) { return 0; } txn->txn_status = ATX_ACTIVE; if (AsyncUnitWrite(txn->unit, txn->out_buf, txn->out_len) < 0) { return 0; } return 1; } /* The header and message are followed by the character sequences given below, * HEADER cr lf cr lf cr lf * MESSAGE cr lf 0x30 cr lf cr lf * So we assume that the header is terminated by two consecutive crlf char * pairs which are eventually followed by another crlf * The message is assumed to be terminated by a crlf pair eventually followed * by two more crlf pairs of characters * Processing is simplified by discarding cr chars and counting lf chars */ static int OPAL_Rx(pAsyncProtocol p, pAsyncTxn txn, int ch) { int ret = 1; static int nlctr = 0; static enum replystates state = START; if (ch == '\r') { return ret; } switch (state) { case START: if (ch == '\n') { /* check HTTP status */ if ( strncmp(txn->inp_buf, HTTP_OK, strlen(HTTP_OK) ) == 0 ) { txn->inp_idx = 0; nlctr = 0; state = HEADER; } else ret = AQU_POP_CMD; } else if (txn->inp_idx < txn->inp_len) txn->inp_buf[txn->inp_idx++] = ch; else ret = AQU_POP_CMD; break; case HEADER: if (ch == '\n') { nlctr++; state = CHKHEADEREND; } break; case CHKHEADEREND: if (nlctr == 1 && ch == '\n') nlctr++; else if (nlctr == 1 && ch != '\n') { txn->inp_idx = 0; nlctr = 0; state = HEADER; } else if (nlctr == 2 && ch == '\n') { txn->inp_idx = 0; nlctr = 0; state = MSG; } break; case MSG: if (txn->inp_idx >= txn->inp_len) ret = AQU_POP_CMD; if (nlctr == 0 && ch != '\n') txn->inp_buf[txn->inp_idx++] = ch; else if (nlctr == 2 && ch == '\n') ret = AQU_POP_CMD; else nlctr++; break; } if (ret == AQU_POP_CMD) { nlctr = 0; state = START; } return ret; } static int OPAL_Ev(pAsyncProtocol p, pAsyncTxn pTxn, int event) { if (event == AQU_TIMEOUT) { pTxn->txn_status = ATX_TIMEOUT; return AQU_POP_CMD; } return AQU_POP_CMD; } /* Append HTTP/1.1 and Host: line to message */ static int OPAL_PrepareTxn(pAsyncProtocol p, pAsyncTxn txn, const char* cmd, int cmd_len, int rsp_len) { int msglen; msglen = cmd_len + strlen(HTTP11) + strlen(USERAGENT) + strlen(HOST) + 3; txn->out_buf = (char*) malloc(msglen); if (txn->out_buf == NULL) { SICSLogWrite("ERROR: Out of memory in OPAL_PrepareTxn", eError); return 0; } strcpy(txn->out_buf, cmd); strcat(txn->out_buf, HTTP11); strcat(txn->out_buf, USERAGENT); strcat(txn->out_buf, HOST); strcat(txn->out_buf, "\r\n"); txn->out_len = msglen; txn->out_idx = 0; if (txn->inp_buf != NULL) { free(txn->inp_buf); } txn->inp_len = rsp_len; txn->inp_idx = 0; txn->txn_state = 0; txn->txn_status = 0; return 1; } void OpalStatusInitProtocol(SicsInterp *pSics) { if (OPAL_Protocol == NULL) { OPAL_Protocol = AsyncProtocolCreate(pSics, "OPALSTAT", NULL, NULL); OPAL_Protocol->sendCommand = OPAL_Tx; OPAL_Protocol->handleInput = OPAL_Rx; OPAL_Protocol->killPrivate = NULL; OPAL_Protocol->handleEvent = OPAL_Ev; OPAL_Protocol->prepareTxn = OPAL_PrepareTxn; } }