/** @file JCHttpHeader.cpp @brief @author guo @version 1.0 @date 2016_09_03 */ #include "JCHttpHeader.h" #include "../util/Log.h" #include "../util/JCCommonMethod.h" #include "../util/JCCrypto.h" #include #define peek(buf, c1) *(buf+1) != '\0' && *(buf+1) == c1 #define peek2(buf, c1, c2) \ *(buf+1) != '\0' && *(buf+1) == c1 \ *(buf+2) != '\0' && *(buf+2) == c2 #define iscrlf(p) (*p == '\r' && *(p + 1) == '\n') #define notcrlf(p) (*p != '\r' && *(p + 1) != '\n') #define notend(p) (*p != '\0') #define end(p) (*p == '\0') #define H3_DEFAULT_HTTP_VERSION "HTTP/1.0" namespace laya{ /** * This function returns a char pointer, which is the end of the request line. * * Return NULL if parse failed. */ const char * JCHttpHeader::request_line_parse(RequestHeader *header, const char *body, int bodyLength) { // Parse the request-line // http://tools.ietf.org/html/rfc2616#section-5.1 // Request-Line = Method SP Request-URI SP HTTP-Version CRLF const char * p = body; header->RequestLineStart = body; while (notend(p) && !isspace(*p)) p++; if (end(p) || iscrlf(p)) { // set error return NULL; } header->RequestMethod = body; header->RequestMethodLen = p - body; // Skip space // parse RequestURI while (isspace(*p) && notcrlf(p) && notend(p)) p++; header->RequestURI = p; while (!isspace(*p) && notcrlf(p) && notend(p)) p++; header->RequestURILen = p - header->RequestURI; // Skip space and parse HTTP-Version if (iscrlf(p) || end(p)) { header->HTTPVersion = H3_DEFAULT_HTTP_VERSION; } else { while (isspace(*p) && notcrlf(p)) p++; header->HTTPVersion = p; while (!isspace(*p) && notcrlf(p)) p++; header->HTTPVersionLen = p - header->HTTPVersion; } return p; } /** * Parse header body */ int JCHttpHeader::request_header_parse(RequestHeader *header, const char *body, int bodyLength) { const char *p = request_line_parse(header, body, bodyLength); if (p == NULL) { return ERR_REQUEST_LINE_PARSE_FAIL; } // should be ended with CR-LF if (end(p)) return -1; // skip CR-LF iscrlf(p); p += 2; if (end(p)) return 0; header->HeaderSize = 0; // Parse Header Fields Here do { HeaderField *field = &header->Fields[header->HeaderSize++]; // HeaderField *field = h3_header_field_new(); field->FieldName = p; // start of a header field name while (notend(p) && *p != ':') p++; field->FieldNameLen = p - field->FieldName; p++; // skip ':' // CRLF is not allowed here if (end(p) || iscrlf(p)) return -1; while (notend(p) && isspace(*p)) p++; // skip space // CRLF is not allowed here if (end(p) || iscrlf(p)) return -1; field->Value = p; while (notend(p) && notcrlf(p)) p++; field->ValueLen = p - field->Value + 1; iscrlf(p); p += 2; #ifdef _DEBUG printf("==> %.*s ==> %.*s\n", field->FieldNameLen, field->FieldName, field->ValueLen, field->Value); #endif // end of header if (iscrlf(p)) return 0; } while (notend(p) && header->HeaderSize < MAX_FILED_NUM); return 0; } constexpr int _First_Len(char v1, int len) { return (((int)v1) << 8) | len; } JCHttpHeader::JCHttpHeader(const char* pHeader) { m_bCacheContrl = false; request_header_parse(this, pHeader, strlen(pHeader)); for (int i = 0; i < (int)HeaderSize; i++) { HeaderField& cfield = Fields[i]; int id = (cfield.FieldName[0] << 8)|(cfield.FieldNameLen & 0xff); switch (id) { case _First_Len('C',12)://Content-Type if (memcmp(cfield.FieldName + 1, "ontent-Type",11) == 0) { int a = 0; } break; case _First_Len('C',13)://Cache-Control if (memcmp(cfield.FieldName + 1, "ache-Control",12) == 0) { parseCacheControl((char*)cfield.Value, cfield.ValueLen); } break; case _First_Len('C',14)://Content-Length if (memcmp(cfield.FieldName + 1, "ontent-Length", 13) == 0) { int a = 0; } break; case _First_Len('E',4): break; case _First_Len('E',7): break; } } } int _getTokeHash(const char*& pDt) { const char* pst = pDt; while (*pDt != ',' && *pDt != ' '&& *pDt!='=' && *pDt != 0) { *pDt++; } return JCBKDRHash::hashMem((const unsigned char*)pst, pDt-pst); } void _getSplit(const char*& pDt) { while (*pDt == ',' || *pDt == ' ' ||*pDt=='=') pDt++; } int _getNumber(const char*& pDt) { const char* pSt = pDt; char number[16] = { 0 }; while (*pDt >= '0' && *pDt <= '9') { pDt++; } if (pDt - pSt > sizeof(number)) { LOGE("parseCacheControl error"); *(int*)0 = 1;//报错 } memcpy(number, pSt, pDt - pSt); return atoi(number); } bool JCHttpHeader::parseCacheControl(char* p_pStr, int p_len) { char* cp = new char[p_len + 1]; memcpy(cp, p_pStr, p_len); cp[p_len] = 0; //最后用0比较容易处理。 const char* pDt = cp; while (*pDt != 0) {// int hash = _getTokeHash(pDt); switch (hash){ case 0x7dbba7b2://max-age pDt++; m_CacheControl.maxage = _getNumber(pDt); break; case 0x4fbcb749://no-store m_CacheControl.nostore = true; _getSplit(pDt); break; case 0x3452662e://no-cache m_CacheControl.nocache = true; _getSplit(pDt); break; case 0x19fb0881://must-revalidate _getSplit(pDt); break; default: _getSplit(pDt); break; } } delete[] cp; return true; } const char* JCHttpHeader::getLastModifyed() {//"Last-Modified" return ""; } const char* JCHttpHeader::getPragma() {//"Pragma" return ""; } const char* JCHttpHeader::getContentType() {//"Content-Type" return ""; } const char* JCHttpHeader::getETag() {//"ETag" return ""; } int64_t JCHttpHeader::getExpires() { static const char* strExpires = "Expires"; if (m_tmExpires != 0) return m_tmExpires; for (int i = 0; i < (int)HeaderSize; i++) { HeaderField& cfield = Fields[i]; if (memcmp(cfield.FieldName, strExpires, strlen(strExpires)) == 0) { //return } } return 0; } }