/* @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 or if the driver fails to find a line that * contains the "Power:" field then it returns the unprocessed reply * characters in the data buffer. * * The reply is expected to have a form similar to the following, * HTTP/1.1 200 OK * Date: Thu, 26 Feb 2015 01:27:30 GMT * Server: Oracle-Application-Server-10g/10.1.3.5.0 Oracle-HTTP-Server * Content-Location: /reactor.jsp * Content-Type: text/plain; charset=UTF-8 * Set-Cookie: JSESSIONID=74646fa7cf3cdf53a371694a33ab760cd649631d758f416d916c6d1b496a477b.e34Pc3qNbh4RaO0Ma3yMbxaRaN0Ke0; path=/Bragg * Connection: close * Transfer-Encoding: chunked * * 4f * Power: 19.381588; CNS Out: 25.76; TG123: 1; CG123: 1; TG4: Open; HB1: 0; HB2: 1 * * 0 * * It should have no more than 512 characters excluding the HTTP OK status. * Each line is expected to be terminated with a \r\n. * The reply is expected to begin with "HTTP/1.1 200 OK" * A line is identified as the status message if and only if it includes * "Power:" regardless of case. * The reply is expected to end with 0\r\n\r\n * * Processing lines is simplified by discarding \r characters and then just * checking for \n. * NOTE: This implies that \n should not be used in the status line as a * delimiter. */ #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" /* Assume that the first field in the reactor status starts with "Power" */ #define PWR_FLD "Power" #define PWR_FLD_LEN 5 enum replystates {START, CHK_PWR_FLD, MSG, END_MSG, DRAIN_REPLY}; static pAsyncProtocol OPAL_Protocol = NULL; /* The message is assumed to start with "Power:" and terminated with crlf. * by two more crlf pairs of characters * Processing of line terminators is simplified by discarding cr chars */ static int OPAL_Rx(pAsyncProtocol p, pAsyncTxn txn, int ch) { char *fldptr; int ret = 1; static int char_ctr = 0, newline_ctr = 0; static enum replystates state = START; if (char_ctr++ >= txn->inp_len) { char_ctr = 0; newline_ctr = 0; state = START; return AQU_POP_CMD; } if (ch == '\r') { return ret; } switch (state) { case START: if (ch == '\n') { /* check HTTP status is OK before proceeding */ if ( strncmp(txn->inp_buf, HTTP_OK, strlen(HTTP_OK) ) == 0 ) { txn->inp_idx = 0; state = CHK_PWR_FLD; } else ret = AQU_POP_CMD; } else txn->inp_buf[txn->inp_idx++] = ch; break; case CHK_PWR_FLD: if (ch == '\n') { txn->inp_idx = 0; } else if (ch == ':') { if ( txn->inp_idx >= PWR_FLD_LEN ) { /* Check for "Power" field anywhere in status line */ fldptr = &txn->inp_buf[txn->inp_idx - PWR_FLD_LEN]; if ( strncasecmp(fldptr, PWR_FLD, PWR_FLD_LEN) == 0 ) { state = MSG; } } } txn->inp_buf[txn->inp_idx++] = ch; break; case MSG: if (ch == '\n') { state = END_MSG; } else { txn->inp_buf[txn->inp_idx++] = ch; } break; case END_MSG: if (ch == '0') { state = DRAIN_REPLY; } break; case DRAIN_REPLY: newline_ctr++; if (newline_ctr >= 2) ret = AQU_POP_CMD; break; } if (ret == AQU_POP_CMD) { char_ctr = 0; newline_ctr = 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 = 512; 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 = NULL; OPAL_Protocol->handleInput = OPAL_Rx; OPAL_Protocol->killPrivate = NULL; OPAL_Protocol->handleEvent = OPAL_Ev; OPAL_Protocol->prepareTxn = OPAL_PrepareTxn; } }