VirtualBox

Changeset 87041 in vbox for trunk


Ignore:
Timestamp:
Dec 4, 2020 9:38:16 AM (4 years ago)
Author:
vboxsync
Message:

Shared Clipboard/Transfers: Added initial supported for connection keep-alive handling; made request parsing a bit more robust. bugref:9874

Location:
trunk
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/include/iprt/http-server.h

    r87032 r87041  
    9191typedef struct RTHTTPSERVERCLIENTSTATE
    9292{
    93     uint32_t         fUnused;
     93    /** If non-zero, the time (in ms) to keep a client connection alive.
     94     *  Requested via client header, but set and controlled by the server in the end. */
     95    RTMSINTERVAL msKeepAlive;
    9496} RTHTTPSERVERCLIENTSTATE;
    9597/** Pointer to a FTP server client state. */
     
    196198/** Maximum length (in bytes) a single client request can have. */
    197199#define RTHTTPSERVER_MAX_REQ_LEN        _8K
     200/** EOL string according to the HTTP 1.1 specs.
     201 *  See https://tools.ietf.org/html/rfc2616#section-2.2 */
     202#define RTHTTPSERVER_HTTP11_EOL_STR     "\r\n"
    198203
    199204/**
  • trunk/src/VBox/Runtime/r3/http-server.cpp

    r87037 r87041  
    451451    char **ppapszStrings;
    452452    size_t cStrings;
    453     int rc2 = RTStrSplit(pszData, strlen(pszData), "\r\n", &ppapszStrings, &cStrings);
     453    int rc2 = RTStrSplit(pszData, strlen(pszData), RTHTTPSERVER_HTTP11_EOL_STR, &ppapszStrings, &cStrings);
    454454    if (RT_SUCCESS(rc2))
    455455    {
     
    541541        rc = RTStrAAppend(&pszHdr, pszEntry);
    542542        AssertRCBreak(rc);
    543         rc = RTStrAAppend(&pszHdr, "\r\n");
     543        rc = RTStrAAppend(&pszHdr, RTHTTPSERVER_HTTP11_EOL_STR);
    544544        AssertRCBreak(rc);
    545545    }
     
    553553            rc = RTStrAAppend(&pszHdr, pszEntry);
    554554            AssertRCBreak(rc);
    555             rc = RTStrAAppend(&pszHdr, "\r\n");
     555            rc = RTStrAAppend(&pszHdr, RTHTTPSERVER_HTTP11_EOL_STR);
    556556            AssertRCBreak(rc);
    557557        }
     
    561561    {
    562562        /* Append trailing EOL. */
    563         rc = RTStrAAppend(&pszHdr, "\r\n");
     563        rc = RTStrAAppend(&pszHdr, RTHTTPSERVER_HTTP11_EOL_STR);
    564564        if (RT_SUCCESS(rc))
    565565            rc = rtHttpServerWriteProto(pClient, pszHdr);
     
    721721            }
    722722            AssertRCReturn(rc, rc);
     723
     724            if (pClient->State.msKeepAlive)
     725            {
     726                /* If the client requested to keep alive the connection,
     727                 * always override this with 30s and report this back to the client. */
     728                pClient->State.msKeepAlive = RT_MS_30SEC; /** @todo Make this configurable. */
     729#ifdef DEBUG_andy
     730                pClient->State.msKeepAlive = 5000;
     731#endif
     732                cch = RTStrPrintf2(szVal, sizeof(szVal), "timeout=%RU64", pClient->State.msKeepAlive / RT_MS_1SEC); /** @todo No pipelining support here yet. */
     733                AssertBreakStmt(cch, VERR_BUFFER_OVERFLOW);
     734                rc = RTHttpHeaderListAdd(HdrLst, "Keep-Alive", szVal, strlen(szVal), RTHTTPHEADERLISTADD_F_BACK);
     735                AssertRCReturn(rc, rc);
     736            }
    723737
    724738            rc = rtHttpServerSendResponseEx(pClient, RTHTTPSTATUS_OK, &HdrLst);
     
    904918                if (RT_FAILURE(rc))
    905919                    break;
    906                 rtHttpServerLogProto(pClient, true /* fWrite */, (const char *)pvBuf);
     920                //rtHttpServerLogProto(pClient, true /* fWrite */, (const char *)pvBuf);
    907921                rc = rtHttpServerSendResponseBody(pClient, pvBuf, cbRead, &cbWritten);
    908922                AssertBreak(cbToRead >= cbWritten);
     
    971985
    972986/**
    973  * Parses headers and fills them into a given header list.
     987 * Parses headers and sets (replaces) a given header list.
    974988 *
    975989 * @returns VBox status code.
    976990 * @param   hList               Header list to fill parsed headers in.
    977  * @param   pszReq              Request string with headers to parse.
    978  * @param   cbReq               Size (in bytes) of request string to parse.
    979  */
    980 static int rtHttpServerParseHeaders(RTHTTPHEADERLIST hList, char *pszReq, size_t cbReq)
     991 * @param   cStrings            Number of strings to parse for \a papszStrings.
     992 * @param   papszStrings        Array of strings to parse.
     993 */
     994static int rtHttpServerParseHeaders(RTHTTPHEADERLIST hList, size_t cStrings, char **papszStrings)
    981995{
    982996    /* Nothing to parse left? Bail out early. */
    983     if (   !pszReq
    984         || !cbReq)
     997    if (   !cStrings
     998        || !papszStrings)
    985999        return VINF_SUCCESS;
    9861000
    987     /* Nothing to do here yet. */
    988     RT_NOREF(hList);
    989 
    990     return VINF_SUCCESS;
     1001#ifdef LOG_ENABLED
     1002    for (size_t i = 0; i < cStrings; i++)
     1003        LogFlowFunc(("Header: %s\n", papszStrings[i]));
     1004#endif
     1005
     1006    int rc = RTHttpHeaderListSet(hList, cStrings, papszStrings);
     1007
     1008    LogFlowFunc(("rc=%Rrc, cHeaders=%zu\n", rc, cStrings));
     1009    return rc;
    9911010}
    9921011
    9931012/**
    9941013 * Main function for parsing and allocating a client request.
     1014 *
     1015 * See: https://tools.ietf.org/html/rfc2616#section-2.2
    9951016 *
    9961017 * @returns VBox status code.
     
    10121033    AssertReturn(RTStrIsValidEncoding(pszReq), VERR_INVALID_PARAMETER);
    10131034
    1014     /** Advances pszReq to the next string in the request.
     1035    char **ppapszStrings = NULL;
     1036    size_t cStrings      = 0;
     1037    int rc = RTStrSplit(pszReq, cbReq, RTHTTPSERVER_HTTP11_EOL_STR, &ppapszStrings, &cStrings);
     1038    if (RT_FAILURE(rc))
     1039        return rc;
     1040
     1041    if (!cStrings)
     1042        return VERR_INVALID_PARAMETER;
     1043
     1044    /** Advances pszReq to the next string in the current line.
    10151045     ** @todo Can we do better here? */
    10161046#define REQ_GET_NEXT_STRING \
     
    10241054    } \
    10251055    else /* Be strict for now. */ \
    1026         AssertFailedReturn(VERR_INVALID_PARAMETER);
    1027 
    1028     char *psz = pszReq;
    1029 
    1030     /* Make sure to terminate the string in any case. */
    1031     psz[RT_MIN(RTHTTPSERVER_MAX_REQ_LEN - 1, cbReq)] = '\0';
    1032 
    1033     /* A tiny bit of sanitation. */
    1034     RTStrStrip(psz);
    1035 
    1036     PRTHTTPSERVERREQ pReq = rtHttpServerReqAlloc();
    1037     AssertPtrReturn(pReq, VERR_NO_MEMORY);
    1038 
    1039     REQ_GET_NEXT_STRING
    1040 
    1041     /* Note: Method names are case sensitive. */
    1042     if      (!RTStrCmp(pszReq, "GET"))      pReq->enmMethod = RTHTTPMETHOD_GET;
    1043     else if (!RTStrCmp(pszReq, "HEAD"))     pReq->enmMethod = RTHTTPMETHOD_HEAD;
     1056        AssertFailedBreakStmt(rc = VERR_INVALID_PARAMETER);
     1057
     1058    PRTHTTPSERVERREQ pReq = NULL;
     1059
     1060    for (;;) /* To use break. */
     1061    {
     1062        /* Start with the first line. */
     1063        char *psz = ppapszStrings[0];
     1064        AssertPtrBreakStmt(psz, rc = VERR_INVALID_PARAMETER);
     1065
     1066        /* A tiny bit of sanitation. */
     1067        RTStrStrip(psz);
     1068
     1069        pReq = rtHttpServerReqAlloc();
     1070        AssertPtrBreakStmt(pReq, rc = VERR_NO_MEMORY);
     1071
     1072        REQ_GET_NEXT_STRING
     1073
     1074        /*
     1075         * Parse method to use. Method names are case sensitive.
     1076         */
     1077        if      (!RTStrCmp(pszReq, "GET"))      pReq->enmMethod = RTHTTPMETHOD_GET;
     1078        else if (!RTStrCmp(pszReq, "HEAD"))     pReq->enmMethod = RTHTTPMETHOD_HEAD;
    10441079#ifdef IPRT_HTTP_WITH_WEBDAV
    1045     else if (!RTStrCmp(pszReq, "OPTIONS"))  pReq->enmMethod = RTHTTPMETHOD_OPTIONS;
    1046     else if (!RTStrCmp(pszReq, "PROPFIND")) pReq->enmMethod = RTHTTPMETHOD_PROPFIND;
     1080        else if (!RTStrCmp(pszReq, "OPTIONS"))  pReq->enmMethod = RTHTTPMETHOD_OPTIONS;
     1081        else if (!RTStrCmp(pszReq, "PROPFIND")) pReq->enmMethod = RTHTTPMETHOD_PROPFIND;
    10471082#endif
     1083        else
     1084        {
     1085            rc = VERR_NOT_SUPPORTED;
     1086            break;
     1087        }
     1088
     1089        REQ_GET_NEXT_STRING
     1090
     1091        /** @todo Do URL unescaping here. */
     1092
     1093        if (!rtHttpServerPathIsValid(pszReq, false /* fIsAbsolute */))
     1094        {
     1095            rc = VERR_PATH_NOT_FOUND;
     1096            break;
     1097        }
     1098
     1099        pReq->pszUrl = RTStrDup(pszReq);
     1100
     1101        REQ_GET_NEXT_STRING
     1102
     1103        /* We're picky heree: Only HTTP 1.1 is supported by now. */
     1104        if (RTStrCmp(pszReq, RTHTTPVER_1_1_STR)) /** @todo Use RTStrVersionCompare. Later. */
     1105        {
     1106            rc = VERR_NOT_SUPPORTED;
     1107            break;
     1108        }
     1109
     1110        /** @todo Anything else needed for the first line here? */
     1111
     1112        /*
     1113         * Process headers, if any.
     1114         */
     1115        if (cStrings > 1)
     1116        {
     1117            rc = rtHttpServerParseHeaders(pReq->hHdrLst, cStrings - 1, &ppapszStrings[1]);
     1118            if (RT_SUCCESS(rc))
     1119            {
     1120                if (RTHttpHeaderListGet(pReq->hHdrLst, "Connection", RTSTR_MAX))
     1121                    pClient->State.msKeepAlive = RT_MS_30SEC; /** @todo Insert the real value here. */
     1122            }
     1123        }
     1124        break;
     1125    } /* for (;;) */
     1126
     1127    /*
     1128     * Cleanup.
     1129     */
     1130    for (size_t i = 0; i < cStrings; i++)
     1131        RTStrFree(ppapszStrings[i]);
     1132    RTMemFree(ppapszStrings);
     1133
     1134    if (RT_FAILURE(rc))
     1135    {
     1136        rtHttpServerReqFree(pReq);
     1137        pReq = NULL;
     1138    }
    10481139    else
    1049         return VERR_NOT_SUPPORTED;
    1050 
    1051     REQ_GET_NEXT_STRING
    1052 
    1053     pReq->pszUrl = RTStrDup(pszReq);
    1054 
    1055     if (!rtHttpServerPathIsValid(pReq->pszUrl, false /* fIsAbsolute */))
    1056         return VERR_PATH_NOT_FOUND;
    1057 
    1058     REQ_GET_NEXT_STRING
    1059 
    1060     /* Only HTTP 1.1 is supported by now. */
    1061     if (RTStrCmp(pszReq, RTHTTPVER_1_1_STR)) /** @todo Use RTStrVersionCompare. Later. */
    1062         return VERR_NOT_SUPPORTED;
    1063 
    1064     int rc = rtHttpServerParseHeaders(pReq->hHdrLst, pszReq, cbReq);
    1065     if (RT_SUCCESS(rc))
    10661140        *ppReq = pReq;
    10671141
     
    11361210    LogFlowFunc(("Client connected\n"));
    11371211
    1138     rc = RTTcpSelectOne(pClient->hSocket, RT_INDEFINITE_WAIT);
    1139     if (RT_SUCCESS(rc))
    1140     {
     1212    /* Initialize client state. */
     1213    pClient->State.msKeepAlive = 0;
     1214
     1215    RTMSINTERVAL cWaitMs      = RT_INDEFINITE_WAIT; /* The first wait always waits indefinitely. */
     1216    uint64_t     tsLastReadMs = 0;
     1217
     1218    for (;;)
     1219    {
     1220        rc = RTTcpSelectOne(pClient->hSocket, cWaitMs);
     1221        if (RT_FAILURE(rc))
     1222        {
     1223            LogFlowFunc(("RTTcpSelectOne=%Rrc (cWaitMs=%RU64)\n", rc, cWaitMs));
     1224            if (rc == VERR_TIMEOUT)
     1225            {
     1226                if (pClient->State.msKeepAlive) /* Keep alive handling needed? */
     1227                {
     1228                    if (!tsLastReadMs)
     1229                        tsLastReadMs = RTTimeMilliTS();
     1230                    const uint64_t tsDeltaMs = pClient->State.msKeepAlive - (RTTimeMilliTS() - tsLastReadMs);
     1231                    LogFlowFunc(("tsLastReadMs=%RU64, tsDeltaMs=%RU64\n", tsLastReadMs, tsDeltaMs));
     1232                    Log3Func(("Keep alive active (%RU32ms): %RU64ms remaining\n", pClient->State.msKeepAlive, tsDeltaMs));
     1233                    if (   tsDeltaMs > cWaitMs
     1234                        && tsDeltaMs < pClient->State.msKeepAlive)
     1235                        continue;
     1236
     1237                    LogFunc(("Keep alive active: Client did not respond within %RU32ms, closing\n", pClient->State.msKeepAlive));
     1238                    rc = VINF_SUCCESS;
     1239                    break;
     1240                }
     1241            }
     1242
     1243            break;
     1244        }
     1245
     1246        LogFlowFunc(("Reading client request ...\n"));
     1247
     1248        tsLastReadMs = RTTimeMilliTS();
     1249        cWaitMs      = 200;  /* All consequtive waits do busy waiting for now. */
     1250
    11411251        char  *pszReq      = szReq;
    11421252        size_t cbRead;
     
    11831293            rc = rtHttpServerProcessRequest(pClient, szReq, cbReadTotal);
    11841294        }
     1295        else
     1296            break;
     1297
     1298    } /* for */
     1299
     1300    if (RT_FAILURE(rc))
     1301    {
     1302        switch (rc)
     1303        {
     1304            case VERR_NET_CONNECTION_RESET_BY_PEER:
     1305            {
     1306                LogFunc(("Client closed the connection\n"));
     1307                rc = VINF_SUCCESS;
     1308                break;
     1309            }
     1310
     1311            default:
     1312                LogFunc(("Client processing failed with %Rrc\n", rc));
     1313                break;
     1314        }
    11851315    }
    11861316
Note: See TracChangeset for help on using the changeset viewer.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette