]> git.ipfire.org Git - thirdparty/snort3.git/commitdiff
URI parsing
authorTom Peters <thopeter@cisco.com>
Fri, 13 Jun 2014 20:53:28 +0000 (16:53 -0400)
committerTom Peters <thopeter@cisco.com>
Fri, 13 Jun 2014 20:53:28 +0000 (16:53 -0400)
src/service_inspectors/nhttp_inspect/nhttp_enum.h
src/service_inspectors/nhttp_inspect/nhttp_flow_data.cc
src/service_inspectors/nhttp_inspect/nhttp_flow_data.h
src/service_inspectors/nhttp_inspect/nhttp_msg_head_shared.cc
src/service_inspectors/nhttp_inspect/nhttp_msg_request.cc
src/service_inspectors/nhttp_inspect/nhttp_msg_request.h
src/service_inspectors/nhttp_inspect/nhttp_msg_section.cc
src/service_inspectors/nhttp_inspect/nhttp_msg_section.h
src/service_inspectors/nhttp_inspect/nhttp_msg_status.cc
src/service_inspectors/nhttp_inspect/nhttp_tables.cc
src/service_inspectors/nhttp_inspect/nhttp_test_msgs.txt

index 7f571bb4c39058b4892dc8a0b0f1b8f8b90fe1dc..d17225981b5e41888f25faa7f6fa4d3a2c7f733b 100644 (file)
@@ -51,13 +51,19 @@ typedef enum { SEC__NOTCOMPUTE=-4, SEC__NOTPRESENT=-1, SEC_REQUEST = 2, SEC_STAT
 typedef enum { VERS__NOTCOMPUTE=-4, VERS__PROBLEMATIC=-2, VERS__NOTPRESENT=-1, VERS__OTHER=1, VERS_1_0, VERS_1_1, VERS_2_0 } VersionId;
 
 // Every request method we have ever heard of
-typedef enum { METH__NOTCOMPUTE=-4, METH__INSUFMEMORY=-3, METH__PROBLEMATIC=-2, METH__NOTPRESENT=-1, METH__OTHER=1, METH_GET, METH_HEAD, METH_POST, METH_PUT, METH_DELETE, METH_TRACE,
-   METH_CONNECT, METH_PROPFIND,
+typedef enum { METH__NOTCOMPUTE=-4, METH__INSUFMEMORY=-3, METH__PROBLEMATIC=-2, METH__NOTPRESENT=-1, METH__OTHER=1, METH_OPTIONS, METH_GET, METH_HEAD, METH_POST, METH_PUT,
+   METH_DELETE, METH_TRACE, METH_CONNECT, METH_PROPFIND,
    METH_PROPPATCH, METH_MKCOL, METH_COPY, METH_MOVE, METH_LOCK, METH_UNLOCK, METH_VERSION_CONTROL, METH_REPORT, METH_CHECKOUT, METH_CHECKIN, METH_UNCHECKOUT,
    METH_MKWORKSPACE, METH_UPDATE, METH_LABEL, METH_MERGE, METH_BASELINE_CONTROL, METH_MKACTIVITY, METH_ORDERPATCH, METH_ACL, METH_PATCH, METH_SEARCH, METH_BCOPY,
    METH_BDELETE, METH_BMOVE, METH_BPROPFIND, METH_BPROPPATCH, METH_NOTIFY, METH_POLL, METH_SUBSCRIBE, METH_UNSUBSCRIBE, METH_X_MS_ENUMATTS, METH_BIND, METH_LINK,
    METH_MKCALENDAR, METH_MKREDIRECTREF, METH_REBIND, METH_UNBIND, METH_UNLINK, METH_UPDATEREDIRECTREF } MethodId;
 
+// URI formats
+typedef enum { URI__NOTCOMPUTE=-4, URI__PROBLEMATIC=-2, URI__NOTPRESENT=-1, URI_ASTERISK = 2, URI_AUTHORITY, URI_ABSPATH, URI_ABSOLUTE } UriType;
+
+// URI schemes
+typedef enum { SCH__NOTCOMPUTE=-4, SCH__INSUFMEMORY=-3, SCH__NOTPRESENT=-1, SCH_OTHER = 1, SCH_HTTP, SCH_HTTPS, SCH_FTP, SCH_GOPHER, SCH_FILE } SchemeId;
+
 // Every header we have ever heard of
 typedef enum { HEAD__NOTCOMPUTE=-4, HEAD__INSUFMEMORY=-3, HEAD__PROBLEMATIC=-2, HEAD__NOTPRESENT=-1, HEAD__OTHER=1, HEAD_CACHE_CONTROL, HEAD_CONNECTION, HEAD_DATE,
    HEAD_PRAGMA, HEAD_TRAILER, HEAD_COOKIE, HEAD_SET_COOKIE,
@@ -70,7 +76,7 @@ typedef enum { HEAD__NOTCOMPUTE=-4, HEAD__INSUFMEMORY=-3, HEAD__PROBLEMATIC=-2,
 // All the infractions we might find while parsing and analyzing a message
 typedef enum { INF_TRUNCATED=0x1, INF_HEADTOOLONG=0x2, /*INF_STARTTOOSHORT=0x4,*/ INF_BADREQLINE=0x8, INF_BADSTATLINE=0x10, INF_TOOMANYHEADERS=0x20,
    INF_BADHEADER=0x40, INF_BADSTATCODE=0x80, INF_UNKNOWNVERSION=0x100, INF_BADVERSION=0x200, INF_NOSCRATCH=0x400, INF_BADHEADERREPS=0x800, INF_BADHEADERDATA=0x1000,
-   INF_BROKENCHUNK=0x2000, INF_BADCHUNKSIZE=0x4000, INF_BADPHRASE= 0x8000 } Infraction;
+   INF_BROKENCHUNK=0x2000, INF_BADCHUNKSIZE=0x4000, INF_BADPHRASE=0x8000, INF_BADURI=0x10000, INF_BADPORT=0x20000  } Infraction;
 
 // Formats for output from a header normalization function
 typedef enum { NORM_NULL, NORM_FIELD, NORM_INT64, NORM_ENUM64, NORM_ENUM64LIST } NormFormat;
@@ -81,11 +87,13 @@ typedef enum { TRANSCODE__OTHER=1, TRANSCODE_CHUNKED, TRANSCODE_IDENTITY, TRANSC
 } // end namespace NHttpEnums
 
 // Individual pieces of the message found during parsing
-// Set length to -1 when field has no value
-typedef struct {
-    const uint8_t *start = nullptr;
-    int32_t length = -1;
-} field;
+// Length values <= 0 are StatusCode values and imply that the start pointer is meaningless.
+// Never use the start pointer without verifying that length > 0.
+struct field {
+public:
+    const uint8_t *start;
+    int32_t length;
+};
 
 typedef enum 
 {
index 162584bc4ea15a3611ecefa50ae2e044652c8ea8..01b0f5a0e5325d053721ee746ec4966cc3bc0071 100644 (file)
@@ -50,8 +50,9 @@ void NHttpFlowData::halfReset(SourceId sourceId) {
     chunkOctets[sourceId] = STAT_NOTPRESENT;
 
     versionId[sourceId] = VERS__NOTPRESENT;
-    if (sourceId == SRC_CLIENT) methodId = METH__NOTPRESENT;
-    else statusCodeNum = STAT_NOTPRESENT;
+    methodId[sourceId] = METH__NOTPRESENT;
+    schemeId[sourceId] = SCH__NOTPRESENT;
+    statusCodeNum[sourceId] = STAT_NOTPRESENT;
 }
 
 
index 3c9b79accefdf12b85e49d8feac7318f90b64b23..a11ad7713822297579ed73f0b992b51c85b70af9 100644 (file)
@@ -75,16 +75,18 @@ private:
     int64_t octetsExpected[2] = { NHttpEnums::STAT_NOTPRESENT, NHttpEnums::STAT_NOTPRESENT };    // expected size of the upcoming body or chunk body section      
 
     // Inspector's internal data about the current message
+    // Some items don't apply in both directions. Have two copies anyway just to simplify code and minimize hard-to-find bugs
+    NHttpEnums::VersionId versionId[2] = { NHttpEnums::VERS__NOTPRESENT, NHttpEnums::VERS__NOTPRESENT };
+    NHttpEnums::MethodId methodId[2] = { NHttpEnums::METH__NOTPRESENT, NHttpEnums::METH__NOTPRESENT };
+    NHttpEnums::SchemeId schemeId[2] = { NHttpEnums::SCH__NOTPRESENT, NHttpEnums::SCH__NOTPRESENT };
+    int32_t statusCodeNum[2] = { NHttpEnums::STAT_NOTPRESENT, NHttpEnums::STAT_NOTPRESENT };
+
     int64_t dataLength[2] = { NHttpEnums::STAT_NOTPRESENT, NHttpEnums::STAT_NOTPRESENT };        // length of the data from Content-Length field or chunk header.      
     int64_t bodySections[2] = { NHttpEnums::STAT_NOTPRESENT, NHttpEnums::STAT_NOTPRESENT };      // number of body sections seen so far including chunk headers
     int64_t bodyOctets[2] = { NHttpEnums::STAT_NOTPRESENT, NHttpEnums::STAT_NOTPRESENT };        // number of user data octets seen so far (either regular body or chunks)
     int64_t numChunks[2] = { NHttpEnums::STAT_NOTPRESENT, NHttpEnums::STAT_NOTPRESENT };         // number of chunks seen so far
     int64_t chunkSections[2] = { NHttpEnums::STAT_NOTPRESENT, NHttpEnums::STAT_NOTPRESENT };     // number of sections seen so far in the current chunk
     int64_t chunkOctets[2] = { NHttpEnums::STAT_NOTPRESENT, NHttpEnums::STAT_NOTPRESENT };       // number of user data octets seen so far in the current chunk including terminating CRLF
-
-    NHttpEnums::VersionId versionId[2] = { NHttpEnums::VERS__NOTPRESENT, NHttpEnums::VERS__NOTPRESENT };
-    NHttpEnums::MethodId methodId = NHttpEnums::METH__NOTPRESENT;
-    int32_t statusCodeNum = NHttpEnums::STAT_NOTPRESENT;
 };
 
 #endif
index 87bc568d92c5ac1690b78d91c32c28a170af76b1..77b846a6bc27b19638c6d01133ec9bf38a69b071 100644 (file)
@@ -144,7 +144,7 @@ void NHttpMsgHeadShared::parseHeaderLines() {
 }
 
 void NHttpMsgHeadShared::deriveHeaderNameId(int index) {
-     if (headerName[index].length <= 0) return;
+    if (headerName[index].length <= 0) return;
     // Normalize header field name to lower case for matching purposes
     uint8_t *lowerName;
     if ((lowerName = scratchPad.request(headerName[index].length)) == nullptr) {
@@ -152,8 +152,8 @@ void NHttpMsgHeadShared::deriveHeaderNameId(int index) {
         headerNameId[index] = HEAD__INSUFMEMORY;
         return;
     }
-    int32_t lowerLength = norm2Lower(headerName[index].start, headerName[index].length, lowerName, infractions, nullptr);
-    headerNameId[index] = (HeaderId) strToCode(lowerName, lowerLength, headerList);
+    norm2Lower(headerName[index].start, headerName[index].length, lowerName, infractions, nullptr);
+    headerNameId[index] = (HeaderId) strToCode(lowerName, headerName[index].length, headerList);
 }
 
 void NHttpMsgHeadShared::genEvents() {
index 6527565c63cb41c76030f193dfeccddc053b7224..c55d3b040f448eaf99b0e0b819abcd69e0a3b9e4 100644 (file)
@@ -34,6 +34,7 @@
 
 #include "snort.h"
 #include "nhttp_enum.h"
+#include "nhttp_head_norm.h"
 #include "nhttp_msg_request.h"
 
 using namespace NHttpEnums;
@@ -43,12 +44,31 @@ void NHttpMsgRequest::initSection() {
     NHttpMsgStart::initSection();
     method.length = STAT_NOTCOMPUTE;
     uri.length = STAT_NOTCOMPUTE;
+    uriLegacyNorm.length = STAT_NOTCOMPUTE;
+    uriType = URI__NOTCOMPUTE;
+    scheme.length = STAT_NOTCOMPUTE;
+    schemeId = SCH__NOTCOMPUTE;
+    host.length = STAT_NOTCOMPUTE;
+    hostNorm.length = STAT_NOTCOMPUTE;
+    port.length = STAT_NOTCOMPUTE;
+    portValue = STAT_NOTCOMPUTE;
+    path.length = STAT_NOTCOMPUTE;
+    pathNorm.length = STAT_NOTCOMPUTE;
+    query.length = STAT_NOTCOMPUTE;
+    queryNorm.length = STAT_NOTCOMPUTE;
+    fragment.length = STAT_NOTCOMPUTE;
+    fragmentNorm.length = STAT_NOTCOMPUTE;
 }
 
 // All the processing that is done for every message (i.e. not just-in-time) is done here.
 void NHttpMsgRequest::analyze() {
     NHttpMsgStart::analyze();
     deriveMethodId();
+    parseUri();
+    deriveSchemeId();
+    parseAuthority();
+    derivePortValue();
+    parseAbsPath();
 }
 
 void NHttpMsgRequest::parseStartLine() {
@@ -84,6 +104,128 @@ void NHttpMsgRequest::deriveMethodId() {
     methodId = (MethodId) strToCode(method.start, method.length, methodList);
 }
 
+void NHttpMsgRequest::parseUri() {
+    if (uriType != URI__NOTCOMPUTE) return;
+    if (uri.length <= 0) {
+        uriType = URI__NOTPRESENT;
+        return;
+    }
+
+    // Four basic types of HTTP URI
+    // "*" means request does not apply to any specific resource
+    if ((uri.length == 1) && (uri.start[0] == '*')) {
+        uriType = URI_ASTERISK;
+        scheme.length = STAT_NOTPRESENT;
+        authority.length = STAT_NOTPRESENT;
+        absPath.length = STAT_NOTPRESENT;
+    }
+    // CONNECT method uses an authority
+    else if (methodId == METH_CONNECT) {
+        uriType = URI_AUTHORITY;
+        scheme.length = STAT_NOTPRESENT;
+        authority.length = uri.length;
+        authority.start = uri.start;
+        absPath.length = STAT_NOTPRESENT;
+    }
+    // Absolute path is a path but no scheme or authority
+    else if (uri.start[0] == '/') {
+        uriType = URI_ABSPATH;
+        scheme.length = STAT_NOTPRESENT;
+        authority.length = STAT_NOTPRESENT;
+        absPath.length = uri.length;
+        absPath.start = uri.start;
+    }
+    // Absolute URI includes scheme, authority, and path
+    else {
+        // Find the "://" and then the "/"
+        int j;
+        int k;
+        for (j = 0; (uri.start[j] != ':') && (j < uri.length); j++);
+        for (k = j+3; (uri.start[k] != '/') && (k < uri.length); k++);
+        if ((k < uri.length) && (uri.start[j+1] == '/') && (uri.start[j+2] == '/')) {
+            uriType = URI_ABSOLUTE;
+            scheme.length = j;
+            scheme.start = uri.start;
+            authority.length = k - j - 3;
+            authority.start = uri.start + j + 3;
+            absPath.length = uri.length - k;
+            absPath.start = uri.start + k;
+        }
+        else {
+            infractions |= INF_BADURI;
+            uriType = URI__PROBLEMATIC;
+            scheme.length = STAT_PROBLEMATIC;
+            authority.length = STAT_PROBLEMATIC;
+            absPath.length = STAT_PROBLEMATIC;
+        }
+    }
+}
+
+void NHttpMsgRequest::deriveSchemeId() {
+    if (schemeId != SCH__NOTCOMPUTE) return;
+    if (scheme.length <= 0) return;
+
+    // Normalize scheme name to lower case for matching purposes
+    uint8_t *lowerScheme;
+    if ((lowerScheme = scratchPad.request(scheme.length)) == nullptr) {
+        infractions |= INF_NOSCRATCH;
+        schemeId = SCH__INSUFMEMORY;
+        return;
+    }
+    norm2Lower(scheme.start, scheme.length, lowerScheme, infractions, nullptr);
+    schemeId = (SchemeId) strToCode(lowerScheme, scheme.length, schemeList);
+}
+
+void NHttpMsgRequest::parseAuthority() {
+    if (host.length != STAT_NOTCOMPUTE) return;
+    if (authority.length <= 0) return;
+    host.start = authority.start;
+    for (host.length = 0; (authority.start[host.length] != ':') && (host.length < authority.length); host.length++);
+    if (host.length < authority.length) {
+        port.length = authority.length - host.length - 1;
+        port.start = authority.start + host.length + 1;
+    }
+    else port.length = STAT_NOTPRESENT;
+}
+
+void NHttpMsgRequest::derivePortValue() {
+    if (portValue != SCH__NOTCOMPUTE) return;
+    if (port.length <= 0) return;
+    portValue = 0;
+    for (int k = 0; k < port.length; k++) {
+        portValue = portValue * 10 + (port.start[k] - '0');
+        if ((port.start[k] < '0') || (port.start[k] > '9') || (portValue > 65535))
+        {
+            infractions |= INF_BADURI;
+            portValue = STAT_PROBLEMATIC;
+            break;
+        }
+    }
+}
+
+void NHttpMsgRequest::parseAbsPath() {
+    if (path.length != STAT_NOTCOMPUTE) return;
+    if (absPath.length <= 0) return;
+    path.start = absPath.start;
+    for (path.length = 0; (absPath.start[path.length] != '?') && (absPath.start[path.length] != '#') && (path.length < absPath.length); path.length++);
+    if (path.length == absPath.length) {
+        query.length = STAT_NOTPRESENT;
+        fragment.length = STAT_NOTPRESENT;
+        return;
+    }
+    if (absPath.start[path.length] == '?') {
+        query.start = absPath.start + path.length + 1;
+        for (query.length = 0; (query.start[query.length] != '#') && (query.length < absPath.length - path.length - 1); query.length++);
+        fragment.start = query.start + query.length + 1;
+        fragment.length = absPath.length - path.length - 1 - query.length - 1;
+    }
+    else {
+        query.length = STAT_NOTPRESENT;
+        fragment.start = absPath.start + path.length + 1;
+        fragment.length = absPath.length - path.length - 1;
+    }
+}
+
 void NHttpMsgRequest::genEvents() {
     if (infractions != 0) SnortEventqAdd(NHTTP_GID, EVENT_ASCII); // I'm just an example event
 }
@@ -93,6 +235,21 @@ void NHttpMsgRequest::printSection(FILE *output) const {
     if (versionId != VERS__NOTCOMPUTE) fprintf(output, "Version Id: %d\n", versionId);
     if (methodId != METH__NOTCOMPUTE) fprintf(output, "Method Id: %d\n", methodId);
     printInterval(output, "URI", uri.start, uri.length);
+    if (uriType != URI__NOTCOMPUTE) fprintf(output, "URI Type: %d\n", uriType);
+    printInterval(output, "Scheme", scheme.start, scheme.length);
+    if (schemeId != SCH__NOTCOMPUTE) fprintf(output, "Scheme Id: %d\n", schemeId);
+    printInterval(output, "Authority", authority.start, authority.length);
+    printInterval(output, "Host Name", host.start, host.length);
+    printInterval(output, "Normalized Host Name", hostNorm.start, hostNorm.length);
+    printInterval(output, "Port", port.start, port.length);
+    if (portValue != STAT_NOTCOMPUTE) fprintf(output, "Port Value: %d\n", portValue);
+    printInterval(output, "Absolute Path", absPath.start, absPath.length);
+    printInterval(output, "Path", path.start, path.length);
+    printInterval(output, "Normalized Path", pathNorm.start, pathNorm.length);
+    printInterval(output, "Query", query.start, query.length);
+    printInterval(output, "Normalized Query", queryNorm.start, queryNorm.length);
+    printInterval(output, "Fragment", fragment.start, fragment.length);
+    printInterval(output, "Normalized Fragment", fragmentNorm.start, fragmentNorm.length);
     NHttpMsgSection::printMessageWrapup(output);
 }
 
@@ -111,7 +268,8 @@ void NHttpMsgRequest::updateFlow() const {
     else {
         sessionData->typeExpected[sourceId] = SEC_HEADER;
         sessionData->versionId[sourceId] = versionId;
-        sessionData->methodId = methodId;
+        sessionData->methodId[sourceId] = methodId;
+        sessionData->schemeId[sourceId] = schemeId;
     }
 }
 
@@ -119,7 +277,7 @@ void NHttpMsgRequest::updateFlow() const {
 void NHttpMsgRequest::legacyClients() const {
     if (method.length > 0) SetHttpBuffer(HTTP_BUFFER_METHOD, method.start, (unsigned)method.length);
     if (uri.length > 0) SetHttpBuffer(HTTP_BUFFER_RAW_URI, uri.start, (unsigned)uri.length);
-    if (uri.length > 0) SetHttpBuffer(HTTP_BUFFER_URI, uri.start, (unsigned)uri.length);
+    if (uriLegacyNorm.length > 0) SetHttpBuffer(HTTP_BUFFER_URI, uriLegacyNorm.start, (unsigned)uriLegacyNorm.length);
 }
 
 
index 1e416ccca0f25f52c218732df93f9277ff9d6a06..06376499861d7b2cc5e3f5cf329d28197d5fc814 100644 (file)
@@ -49,17 +49,40 @@ public:
 private:
     // Code conversion tables are for turning token strings into enums.
     static const StrCode methodList[];
+    static const StrCode schemeList[];
 
     // "Parse" methods cut things into pieces. "Derive" methods convert things into a new format such as an integer or enum token. "Normalize" methods convert
     // things into a standard form without changing the underlying format.
     void parseStartLine();
     void deriveMethodId();
+    void parseUri();
+    void deriveSchemeId();
+    void parseAuthority();
+    void derivePortValue();
+    void parseAbsPath();
 
     // This is where all the derived values, extracted message parts, and normalized values are.
-    // Note that this is all scalars, buffer pointers, and buffer sizes. The actual buffers are in the message buffer (raw pieces) or the
+    // Note that these are all scalars, buffer pointers, and buffer sizes. The actual buffers are in the message buffer (raw pieces) or the
     // scratchPad (normalized pieces).
     field method;
+
+    // URI stuff
     field uri;
+    field uriLegacyNorm;
+    NHttpEnums::UriType uriType;
+    field scheme;
+    field authority;
+    field host;
+    field hostNorm;
+    field port;
+    int32_t portValue;
+    field absPath;
+    field path;
+    field pathNorm;
+    field query;
+    field queryNorm;
+    field fragment;
+    field fragmentNorm;
 };
 
 #endif
index 2b2f0b56d8b2401918ce9cbad9d55c3a84a4f33c..6945c0df6ded1d08497c680d76f3ef3883ea6d1b 100644 (file)
@@ -59,8 +59,9 @@ void NHttpMsgSection::loadSection(const uint8_t *buffer, const uint16_t bufsize,
     sourceId = sessionData->sourceId;
     tcpClose = sessionData->tcpClose;
     versionId = sessionData->versionId[sourceId];
-    methodId = sessionData->methodId;
-    statusCodeNum = sessionData->statusCodeNum;
+    methodId = sessionData->methodId[sourceId];
+    schemeId = sessionData->schemeId[sourceId];
+    statusCodeNum = sessionData->statusCodeNum[sourceId];
 
     scratchPad.reinit();
 }
index d487e6c6dad1f72ca5c500600877c9e7da37646d..2e44e2f6f97f4e222b1eaa24fe795953455fbe13 100644 (file)
@@ -77,6 +77,7 @@ protected:
     NHttpEnums::SourceId sourceId;
     NHttpEnums::VersionId versionId;
     NHttpEnums::MethodId methodId;
+    NHttpEnums::SchemeId schemeId;
     int32_t statusCodeNum;
 };
 
index f4b0ac4eee67a2ec77f44ee9d554de116f74a159..4e4ff95827050eaaede890cdffa07e49275f359c 100644 (file)
@@ -118,7 +118,7 @@ void NHttpMsgStatus::updateFlow() const {
     else {
         sessionData->typeExpected[sourceId] = SEC_HEADER;
         sessionData->versionId[sourceId] = versionId;
-        sessionData->statusCodeNum = statusCodeNum;
+        sessionData->statusCodeNum[sourceId] = statusCodeNum;
     }
 }
 
index 8607c98fa176aace81b39d1eb4729d948ac581b6..84014c25910f5457253644e9cf6cb4560db7b1de 100644 (file)
@@ -45,7 +45,8 @@
 using namespace NHttpEnums;
 
 const StrCode NHttpMsgRequest::methodList[] =
-   {{ METH_GET,                "GET"},
+   {{ METH_OPTIONS,            "OPTIONS"},
+    { METH_GET,                "GET"},
     { METH_HEAD,               "HEAD"},
     { METH_POST,               "POST"},
     { METH_PUT,                "PUT"},
@@ -94,6 +95,14 @@ const StrCode NHttpMsgRequest::methodList[] =
     { METH_UPDATEREDIRECTREF,  "UPDATEREDIRECTREF"},
     { 0,                       nullptr} };
 
+const StrCode NHttpMsgRequest::schemeList[] =
+   {{ SCH_HTTP,                "http"},
+    { SCH_HTTPS,               "https"},
+    { SCH_FTP,                 "ftp"},
+    { SCH_GOPHER,              "gopher"},
+    { SCH_FILE,                "file"},
+    { 0,                       nullptr} };
+
 const StrCode NHttpMsgHeadShared::headerList[] =
    {{ HEAD_CACHE_CONTROL,        "cache-control"},
     { HEAD_CONNECTION,           "connection"},
index cbf066a35b8163667dd5fc2ce0994c80103c327c..df1e9da3f97e94a284b6b3c255841834e5ad0f6d 100644 (file)
@@ -251,14 +251,97 @@ MKREDIRECTREF / HTTP/2.0\r\n\r\n
 @3002
 @break
 @request
-BIND 1234567890?abcdef HTTP/1.1\r\n\r\n
+BIND /1234567890?abcdef HTTP/1.1\r\n\r\n
 
 @3003
 @break
 @request
 GET /test/hi-there.txt HTTP/1.0\r\n\r\n
 
+@3004
+@break
+@request
+GET /URI/Absolute/Path/Example HTTP/1.1\r\n\r\n
 
+@3005
+@break
+@request
+GET /uri/aBSOLUTE/pATH/eXAMPLE?with-query HTTP/1.1\r\n\r\n
+
+@3006
+@break
+@request
+MKWORKSPACE /URI/Absolute/Path/Example#with-fragment HTTP/1.1\r\n\r\n
+
+@3007
+@break
+@request
+BPROPFIND /URI/Absolute/Path/Example?with_QUERY#AndAFragmentToo HTTP/1.1\r\n\r\n
+
+@3008
+@break
+@request
+GET /?with-query HTTP/1.1\r\n\r\n
+
+@3009
+@break
+@request
+ORDERPATCH /#with-fragment HTTP/1.1\r\n\r\n
+
+@3010
+@break
+@request
+UPDATEREDIRECTREF /?with_QUERY#AndAFragmentToo HTTP/1.1\r\n\r\n
+
+@3011
+@break
+@request
+VERYLONGUNKNOWNMETHODTEST /?# HTTP/1.1\r\n\r\n
+
+@3012
+@break
+@request
+LABEL /stuff?# HTTP/1.1\r\n\r\n
+
+@3013
+@break
+@request
+MKCALENDAR /*stufflotsandlotslotsandlotsandlotsandlotsandlotsandlotsandlotsandlotsandlotsandlotsandlotsandlotsandlotsandlotsandlotsandlotsandlotsandlotsofstuff?1# HTTP/1.1\r\n\r\n
+
+@3014
+@break
+@request
+VERSION-CONTROL /*stufflotsandlotslotsandlotsandlotsandlotsandlotsandlotsandlotsandlotsandlotsandlotsandlotsandlotsandlotsandlotsandlotsandlotsandlotsandlotsofstuff?#A HTTP/1.1\r\n\r\n
+
+@3015
+@break
+@request
+OPTIONS * HTTP/1.1\r\n\r\n
+
+@3016
+@break
+@request
+OPTIONS /*options/may/have?a*resource HTTP/1.1\r\n\r\n
+
+@3017
+@break
+@request
+CONNECT this.is.an.authority.com HTTP/1.1\r\n\r\n
+
+@3018
+@break
+@request
+CONNECT this.is.an.authority.with.a.port.com:8739 HTTP/1.1\r\n\r\n
+
+@3019
+@break
+@request
+GET http://iamahost.com/simple/example/of/a/path/ HTTP/1.1\r\n\r\n
+
+@3020
+@break
+@request
+GET HtTpS://1.2.3.4.5.a:6/abcdef/ghijklmnop/qrstuvwxyz/?thequery?fieldcontinues?until#afragme#arrives#1234 HTTP/1.1\r\n\r\n
 
 # ***********************************************************************************************
 # Invalid request start lines