The function is implemented by setting cache file pointer to fill read_buf. Consequently, when HTTPREQ_ReadFile is called, it will try reading data from cache after read_buf is depleted, before continuing reading from http stream.
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=26570
-- v7: wininet: Partially implement InternetSetFilePointer wininet/tests: Add InternetSetFilePointer tests.
From: Jason Kuo j20001970@gmail.com
--- dlls/wininet/tests/http.c | 173 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 173 insertions(+)
diff --git a/dlls/wininet/tests/http.c b/dlls/wininet/tests/http.c index 8fcb9df293a..c6a0e7ec621 100644 --- a/dlls/wininet/tests/http.c +++ b/dlls/wininet/tests/http.c @@ -581,6 +581,177 @@ static void close_async_handle(HINTERNET handle, int handle_cnt) CHECK_NOTIFIED2(INTERNET_STATUS_HANDLE_CLOSING, handle_cnt); }
+static void InternetSetFilePointer_test(const char *host, const char *path) +{ +#ifndef READ_BUFFER_SIZE +#define READ_BUFFER_SIZE 8192 +#endif + char expect_response[READ_BUFFER_SIZE]; + char buf[READ_BUFFER_SIZE]; + HINTERNET hi = 0, hic = 0, hor = 0; + BOOL res, expected; + DWORD count, size, i, pos, err; + + hi = InternetOpenA("Winetest", INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0); + ok((hi != 0x0), "InternetOpen failed with error %lu\n", GetLastError()); + if(hi == 0x0) goto abort; + + hic = InternetConnectA(hi, host, INTERNET_DEFAULT_HTTP_PORT, + NULL, NULL, INTERNET_SERVICE_HTTP, 0, 0); + ok((hic != 0x0), "InternetConnect failed with error %lu\n", GetLastError()); + if(hic == 0x0) goto abort; + + hor = HttpOpenRequestA(hic, NULL, path, NULL, NULL, NULL, + INTERNET_FLAG_RELOAD, + 0x0); + ok((hor != 0x0), "HttpOpenRequest failed with error %lu\n", GetLastError()); + if(hor == 0x0) goto abort; + + ok(InternetReadFile(hor, expect_response, READ_BUFFER_SIZE, &count), + "InternetReadFile failed\n"); + if(!count) goto abort; + if(hor) InternetCloseHandle(hor); + + + hor = HttpOpenRequestA(hic, NULL, path, NULL, NULL, NULL, + INTERNET_FLAG_RELOAD|INTERNET_FLAG_DONT_CACHE, + 0x0); + ok((hor != 0x0), "HttpOpenRequest failed with error %lu\n", GetLastError()); + if(hor == 0x0) goto abort; + + /* INTERNET_FLAG_DONT_CACHE tests */ + pos = InternetSetFilePointer(hor, 0, NULL, FILE_BEGIN, 0); + err = pos == INVALID_SET_FILE_POINTER ? GetLastError() : NO_ERROR; + expected = pos == INVALID_SET_FILE_POINTER && err == ERROR_INTERNET_INVALID_OPERATION; + ok(expected, "Expected position %#x. Got %#lx. GetLastError() %lu\n", + INVALID_SET_FILE_POINTER, pos, err); + if(hor) InternetCloseHandle(hor); + + hor = HttpOpenRequestA(hic, NULL, path, NULL, NULL, NULL, + INTERNET_FLAG_RELOAD, + 0x0); + ok((hor != 0x0), "HttpOpenRequest failed with error %lu\n", GetLastError()); + if(hor == 0x0) goto abort; + + res = HttpSendRequestW(hor, NULL, 0, NULL, 0); + ok(res, "HttpSendRequest failed with error %lu\n", GetLastError()); + + /* FILE_BEGIN tests */ + i = 0; + while(i < 4) { + pos = InternetSetFilePointer(hor, i, NULL, FILE_BEGIN, 0); + err = pos == INVALID_SET_FILE_POINTER ? GetLastError() : NO_ERROR; + expected = pos == i && err == NO_ERROR; + ok(expected, "Expected position %#lx. Got %#lx. GetLastError() %lu\n", i, pos, err); + res = InternetReadFile(hor, buf, 1024, &count); + err = !res ? GetLastError() : NO_ERROR; + ok(res, "InternetReadFile failed with error %ld\n", err); + ok(!memcmp(expect_response+i, buf, count), "Unexpected result from InternetReadFile\n"); + i = i + 1; + } + while(i > 0) { + pos = InternetSetFilePointer(hor, i, NULL, FILE_BEGIN, 0); + err = pos == INVALID_SET_FILE_POINTER ? GetLastError() : NO_ERROR; + expected = pos == i && err == NO_ERROR; + ok(expected, "Expected position %#lx. Got %#lx. GetLastError() %lu\n", i, pos, err); + res = InternetReadFile(hor, buf, 1024, &count); + err = !res ? GetLastError() : NO_ERROR; + ok(res, "InternetReadFile failed with error %ld\n", err); + ok(!memcmp(expect_response+i, buf, count), "Unexpected result from InternetReadFile\n"); + i = i - 1; + } + + /* FILE_CURRENT tests */ + i = 0; + while(i < 4) { + i = i + 1; + pos = InternetSetFilePointer(hor, 0, NULL, FILE_BEGIN, 0); + err = pos == INVALID_SET_FILE_POINTER ? GetLastError() : NO_ERROR; + expected = pos == 0 && err == NO_ERROR; + ok(expected, "Expected position %#lx. Got %#lx. GetLastError() %lu\n", i, pos, err); + pos = InternetSetFilePointer(hor, i, NULL, FILE_CURRENT, 0); + err = pos == INVALID_SET_FILE_POINTER ? GetLastError() : NO_ERROR; + expected = pos == i && err == NO_ERROR; + todo_wine ok(expected, "Expected position %#lx. Got %#lx. GetLastError() %lu\n", i, pos, err); + res = InternetReadFile(hor, buf, 1024, &count); + err = !res ? GetLastError() : NO_ERROR; + ok(res, "InternetReadFile failed with error %ld\n", err); + todo_wine ok(!memcmp(expect_response+i, buf, count), "Unexpected result from InternetReadFile\n"); + } + + size = InternetSetFilePointer(hor, 0, NULL, FILE_CURRENT, 0); + todo_wine ok(strlen(expect_response) == size, "Unexpected size %ld\n", size); + + /* Out of bound pointer tests */ + pos = InternetSetFilePointer(hor, INT_MAX, NULL, FILE_BEGIN, 0); + err = pos == INVALID_SET_FILE_POINTER ? GetLastError() : NO_ERROR; + expected = pos == INT_MAX && err == NO_ERROR; + ok(expected, "Expected position %#x. Got %#lx. GetLastError() %lu\n", INT_MAX, pos, err); + pos = InternetSetFilePointer(hor, 1, NULL, FILE_CURRENT, 0); + err = pos == INVALID_SET_FILE_POINTER ? GetLastError() : NO_ERROR; + expected = pos == (DWORD)INT_MAX+1 && err == NO_ERROR; + todo_wine ok(expected, "Expected position %#lx. Got %#lx. GetLastError() %lu\n", + (DWORD)INT_MAX+1, pos, err); + pos = InternetSetFilePointer(hor, -1, NULL, FILE_CURRENT, 0); + err = pos == INVALID_SET_FILE_POINTER ? GetLastError() : NO_ERROR; + expected = pos == INVALID_SET_FILE_POINTER && err == ERROR_NEGATIVE_SEEK; + ok(expected, "Expected position %#x. Got %#lx. GetLastError() %lu\n", + INVALID_SET_FILE_POINTER, pos, err); + + /* Negative pointer tests */ + pos = InternetSetFilePointer(hor, 0, NULL, FILE_BEGIN, 0); + err = pos == INVALID_SET_FILE_POINTER ? GetLastError() : NO_ERROR; + expected = pos == 0 && err == NO_ERROR; + ok(expected, "Expected position %#x. Got %#lx. GetLastError() %lu\n", 0, pos, err); + pos = InternetSetFilePointer(hor, -1, NULL, FILE_CURRENT, 0); + err = pos == INVALID_SET_FILE_POINTER ? GetLastError() : NO_ERROR; + expected = pos == -1 && err == NO_ERROR; + todo_wine ok(expected, "Expected position %#x. Got %#lx. GetLastError() %lu\n", -1, pos, err); + pos = InternetSetFilePointer(hor, -1, NULL, FILE_CURRENT, 0); + err = pos == INVALID_SET_FILE_POINTER ? GetLastError() : NO_ERROR; + expected = pos == INVALID_SET_FILE_POINTER && err == ERROR_NEGATIVE_SEEK; + ok(expected, "Expected position %#x. Got %#lx. GetLastError() %lu\n", + INVALID_SET_FILE_POINTER, pos, err); + + /* ERROR_INTERNET_INVALID_OPERATION tests */ + pos = InternetSetFilePointer(hor, size, NULL, FILE_BEGIN, 0); + err = pos == INVALID_SET_FILE_POINTER ? GetLastError() : NO_ERROR; + expected = pos == size && err == NO_ERROR; + todo_wine ok(expected, "Expected position %#lx. Got %#lx. GetLastError() %lu\n", size, pos, err); + res = InternetReadFile(hor, buf, 1024, &count); + err = !res ? GetLastError() : NO_ERROR; + expected = res && !count; + todo_wine ok(expected, "Unexpected result from InternetReadFile. res=%d, count=%ld\n", res, count); + /* ERROR_INTERNET_INVALID_OPERATION starts here */ + pos = InternetSetFilePointer(hor, 0, NULL, FILE_CURRENT, 0); + err = pos == INVALID_SET_FILE_POINTER ? GetLastError() : NO_ERROR; + expected = pos == INVALID_SET_FILE_POINTER && err == ERROR_INTERNET_INVALID_OPERATION; + todo_wine ok(expected, "InternetSetFilePointer unexpectly succeeded\n"); + pos = InternetSetFilePointer(hor, 1, NULL, FILE_CURRENT, 0); + err = pos == INVALID_SET_FILE_POINTER ? GetLastError() : NO_ERROR; + expected = pos == INVALID_SET_FILE_POINTER && err == ERROR_INTERNET_INVALID_OPERATION; + todo_wine ok(expected, "InternetSetFilePointer unexpectly succeeded\n"); + pos = InternetSetFilePointer(hor, -1, NULL, FILE_CURRENT, 0); + err = pos == INVALID_SET_FILE_POINTER ? GetLastError() : NO_ERROR; + expected = pos == INVALID_SET_FILE_POINTER && err == ERROR_INTERNET_INVALID_OPERATION; + todo_wine ok(expected, "InternetSetFilePointer unexpectly succeeded\n"); + pos = InternetSetFilePointer(hor, size, NULL, FILE_BEGIN, 0); + err = pos == INVALID_SET_FILE_POINTER ? GetLastError() : NO_ERROR; + expected = pos == INVALID_SET_FILE_POINTER && err == ERROR_INTERNET_INVALID_OPERATION; + ok(expected, "InternetSetFilePointer unexpectly succeeded\n"); + +abort: + if(hor) + InternetCloseHandle(hor); + if(hic) + InternetCloseHandle(hic); + if(hi) + InternetCloseHandle(hi); +#ifdef READ_BUFFER_SIZE +#undef READ_BUFFER_SIZE +#endif +} + static void InternetReadFile_test(int flags, const test_data_t *test) { char *post_data = NULL; @@ -7946,6 +8117,8 @@ START_TEST(http) InternetReadFile_chunked_test(); HttpSendRequestEx_test(); InternetReadFile_test(INTERNET_FLAG_ASYNC, &test_data[3]); + InternetSetFilePointer_test("test.winehq.org", "/tests/hello.html"); + InternetSetFilePointer_test("test.winehq.org", "/favicon.ico"); test_connection_failure(); test_default_service_port(); test_concurrent_header_access();
From: Jason Kuo j20001970@gmail.com
The function is implemented by manipulating read_pos and read_size or setting cache pointer to fill read_buf if cache handle is available. Consequently, when HTTPREQ_ReadFile is called with valid cache handle, it will try reading data from cache after read_buf is depleted, before continuing reading from http stream.
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=26570 --- dlls/wininet/http.c | 5 +++ dlls/wininet/internet.c | 86 +++++++++++++++++++++++++++++++++++++++-- 2 files changed, 88 insertions(+), 3 deletions(-)
diff --git a/dlls/wininet/http.c b/dlls/wininet/http.c index c9d1c463982..42ead1f8c8e 100644 --- a/dlls/wininet/http.c +++ b/dlls/wininet/http.c @@ -3184,6 +3184,11 @@ static DWORD HTTPREQ_ReadFile(object_header_t *hdr, void *buf, DWORD size, DWORD req->read_pos += read; }
+ if(read < size && req->hCacheFile) { + res = ReadFile(req->hCacheFile, (char*)buf+read, size-read, &cread, NULL); + read += cread; + } + if(read < size && (!read || !(flags & IRF_NO_WAIT)) && !end_of_read_data(req)) { LeaveCriticalSection(&req->read_section); INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0); diff --git a/dlls/wininet/internet.c b/dlls/wininet/internet.c index 292fd44dd8e..1920e582ab6 100644 --- a/dlls/wininet/internet.c +++ b/dlls/wininet/internet.c @@ -2154,14 +2154,94 @@ INTERNET_STATUS_CALLBACK WINAPI InternetSetStatusCallbackW(
/*********************************************************************** * InternetSetFilePointer (WININET.@) + * Sets read position for an open internet file. + * + * RETURNS + * Current position of the file on success + * INVALID_SET_FILE_POINTER on failure */ DWORD WINAPI InternetSetFilePointer(HINTERNET hFile, LONG lDistanceToMove, PVOID pReserved, DWORD dwMoveContext, DWORD_PTR dwContext) { - FIXME("(%p %ld %p %ld %Ix): stub\n", hFile, lDistanceToMove, pReserved, dwMoveContext, dwContext); + DWORD pos, size, read, cread; + DWORD err = ERROR_INTERNET_INVALID_OPERATION, res = INVALID_SET_FILE_POINTER; + http_request_t *request; + + TRACE("(%p %ld %p %ld %Ix)\n", hFile, lDistanceToMove, pReserved, dwMoveContext, dwContext); + + request = (http_request_t*)get_handle_object(hFile); + + if(request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC) { + FIXME("Asynchronous request not implemented\n"); + goto lend; + } + + if(request->hdr.dwFlags & (INTERNET_FLAG_DONT_CACHE|INTERNET_FLAG_NO_CACHE_WRITE)) + goto lend; + + switch (dwMoveContext) { + case FILE_BEGIN: + res = lDistanceToMove; + break; + case FILE_CURRENT: + FIXME("dwMoveContext %ld not implemented\n", dwMoveContext); + err = ERROR_NEGATIVE_SEEK; + goto lend; + case FILE_END: + default: + FIXME("Unhandled dwMoveContext %ld\n", dwMoveContext); + goto lend; + }
- SetLastError(ERROR_INTERNET_INVALID_OPERATION); - return INVALID_SET_FILE_POINTER; + if(request->hCacheFile) { + size = GetFileSize(request->hCacheFile, NULL); + err = size == INVALID_FILE_SIZE ? GetLastError() : NO_ERROR; + if(err != NO_ERROR) { + ERR("GetFileSize failed with error %ld\n", err); + goto lend; + } + if(size < res) { + BYTE buf[1024]; + BOOL ret; + while(size < res) { + ret = InternetReadFile(hFile, buf, 1024, &cread); + if(ret != ERROR_SUCCESS || !cread) + break; + size = GetFileSize(request->hCacheFile, NULL); + } + } + pos = SetFilePointer(request->hCacheFile, lDistanceToMove, NULL, dwMoveContext); + err = pos == INVALID_SET_FILE_POINTER ? GetLastError() : NO_ERROR; + if(err != NO_ERROR) { + ERR("SetFilePointer failed with error %ld\n", err); + goto lend; + } + EnterCriticalSection(&request->read_section); + read = min(READ_BUFFER_SIZE, size-pos); + if(ReadFile(request->hCacheFile, request->read_buf, read, &cread, NULL)) { + request->read_size = cread; + request->read_pos = 0; + } + LeaveCriticalSection(&request->read_section); + } + else { + if(!request->contentLength) + goto lend; + EnterCriticalSection(&request->read_section); + size = request->read_pos + request->read_size; + pos = size - request->contentLength; + request->read_pos = min(size, pos + res); + request->read_size = size - request->read_pos; + LeaveCriticalSection(&request->read_section); + } + +lend: + if(res == INVALID_SET_FILE_POINTER) + SetLastError(err); + TRACE("returning %ld\n", res); + if(request) + WININET_Release( &request->hdr ); + return res; }
/***********************************************************************
Hi,
It looks like your patch introduced the new failures shown below. Please investigate and fix them before resubmitting your patch. If they are not new, fixing them anyway would help a lot. Otherwise please ask for the known failures list to be updated.
The tests also ran into some preexisting test failures. If you know how to fix them that would be helpful. See the TestBot job for the details:
The full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=126078
Your paranoid android.
=== debian11 (32 bit report) ===
wmvcore: wmvcore.c:2997: Test failed: Wait timed out.
On Fri Nov 11 18:32:05 2022 +0000, Jason Kuo wrote:
changed this line in [version 7 of the diff](/wine/wine/-/merge_requests/1268/diffs?diff_id=18222&start_sha=7dd229a99962aaa48f77b9e616ffc5a3acee6d89#a134dbfacd27ba4f966ac878e6a58920cfda52b1_2202_2196)
Haven't figured out how to handle asynchronous requests, so add a check to only handle synchronous requests for now.
On Fri Nov 11 18:32:04 2022 +0000, Jason Kuo wrote:
changed this line in [version 7 of the diff](/wine/wine/-/merge_requests/1268/diffs?diff_id=18222&start_sha=7dd229a99962aaa48f77b9e616ffc5a3acee6d89#5257c8cfe2c57bd3f642ff218718644d33ec40ef_3195_3188)
Fixed.
On Fri Nov 11 19:24:33 2022 +0000, **** wrote:
Marvin replied on the mailing list:
Hi, It looks like your patch introduced the new failures shown below. Please investigate and fix them before resubmitting your patch. If they are not new, fixing them anyway would help a lot. Otherwise please ask for the known failures list to be updated. The tests also ran into some preexisting test failures. If you know how to fix them that would be helpful. See the TestBot job for the details: The full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=126078 Your paranoid android. === debian11 (32 bit report) === wmvcore: wmvcore.c:2997: Test failed: Wait timed out.
The failed wmvcore test doesn't seems related to wininet. Is there anything I need to fix here?