- Timestamp:
- Dec 4, 2020 9:38:16 AM (4 years ago)
- Location:
- trunk
- Files:
-
- 2 edited
-
include/iprt/http-server.h (modified) (2 diffs)
-
src/VBox/Runtime/r3/http-server.cpp (modified) (11 diffs)
Legend:
- Unmodified
- Added
- Removed
-
trunk/include/iprt/http-server.h
r87032 r87041 91 91 typedef struct RTHTTPSERVERCLIENTSTATE 92 92 { 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; 94 96 } RTHTTPSERVERCLIENTSTATE; 95 97 /** Pointer to a FTP server client state. */ … … 196 198 /** Maximum length (in bytes) a single client request can have. */ 197 199 #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" 198 203 199 204 /** -
trunk/src/VBox/Runtime/r3/http-server.cpp
r87037 r87041 451 451 char **ppapszStrings; 452 452 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); 454 454 if (RT_SUCCESS(rc2)) 455 455 { … … 541 541 rc = RTStrAAppend(&pszHdr, pszEntry); 542 542 AssertRCBreak(rc); 543 rc = RTStrAAppend(&pszHdr, "\r\n");543 rc = RTStrAAppend(&pszHdr, RTHTTPSERVER_HTTP11_EOL_STR); 544 544 AssertRCBreak(rc); 545 545 } … … 553 553 rc = RTStrAAppend(&pszHdr, pszEntry); 554 554 AssertRCBreak(rc); 555 rc = RTStrAAppend(&pszHdr, "\r\n");555 rc = RTStrAAppend(&pszHdr, RTHTTPSERVER_HTTP11_EOL_STR); 556 556 AssertRCBreak(rc); 557 557 } … … 561 561 { 562 562 /* Append trailing EOL. */ 563 rc = RTStrAAppend(&pszHdr, "\r\n");563 rc = RTStrAAppend(&pszHdr, RTHTTPSERVER_HTTP11_EOL_STR); 564 564 if (RT_SUCCESS(rc)) 565 565 rc = rtHttpServerWriteProto(pClient, pszHdr); … … 721 721 } 722 722 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 } 723 737 724 738 rc = rtHttpServerSendResponseEx(pClient, RTHTTPSTATUS_OK, &HdrLst); … … 904 918 if (RT_FAILURE(rc)) 905 919 break; 906 rtHttpServerLogProto(pClient, true /* fWrite */, (const char *)pvBuf);920 //rtHttpServerLogProto(pClient, true /* fWrite */, (const char *)pvBuf); 907 921 rc = rtHttpServerSendResponseBody(pClient, pvBuf, cbRead, &cbWritten); 908 922 AssertBreak(cbToRead >= cbWritten); … … 971 985 972 986 /** 973 * Parses headers and fills them intoa given header list.987 * Parses headers and sets (replaces) a given header list. 974 988 * 975 989 * @returns VBox status code. 976 990 * @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 stringto 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 */ 994 static int rtHttpServerParseHeaders(RTHTTPHEADERLIST hList, size_t cStrings, char **papszStrings) 981 995 { 982 996 /* Nothing to parse left? Bail out early. */ 983 if ( ! pszReq984 || ! cbReq)997 if ( !cStrings 998 || !papszStrings) 985 999 return VINF_SUCCESS; 986 1000 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; 991 1010 } 992 1011 993 1012 /** 994 1013 * Main function for parsing and allocating a client request. 1014 * 1015 * See: https://tools.ietf.org/html/rfc2616#section-2.2 995 1016 * 996 1017 * @returns VBox status code. … … 1012 1033 AssertReturn(RTStrIsValidEncoding(pszReq), VERR_INVALID_PARAMETER); 1013 1034 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. 1015 1045 ** @todo Can we do better here? */ 1016 1046 #define REQ_GET_NEXT_STRING \ … … 1024 1054 } \ 1025 1055 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; 1044 1079 #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; 1047 1082 #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 } 1048 1139 else 1049 return VERR_NOT_SUPPORTED;1050 1051 REQ_GET_NEXT_STRING1052 1053 pReq->pszUrl = RTStrDup(pszReq);1054 1055 if (!rtHttpServerPathIsValid(pReq->pszUrl, false /* fIsAbsolute */))1056 return VERR_PATH_NOT_FOUND;1057 1058 REQ_GET_NEXT_STRING1059 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))1066 1140 *ppReq = pReq; 1067 1141 … … 1136 1210 LogFlowFunc(("Client connected\n")); 1137 1211 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 1141 1251 char *pszReq = szReq; 1142 1252 size_t cbRead; … … 1183 1293 rc = rtHttpServerProcessRequest(pClient, szReq, cbReadTotal); 1184 1294 } 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 } 1185 1315 } 1186 1316
Note:
See TracChangeset
for help on using the changeset viewer.

