mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-01 14:58:14 +00:00
Merge pull request #22237 from hrydgard/naett-cancel
http: Make Cancel() actually stop an HTTPS transfer
This commit is contained in:
10 files changed
+209
-26
No files matched your search
@@ -100,3 +100,22 @@ Keep this list up to date - it's what makes it possible to move to a newer upstr
|
||||
- `src/naett_osx.c`: `invalidateAndCancel` returns before the session lets go of its delegate, so
|
||||
the delegate's back pointer to the response is cleared first, and `didReceiveData` checks it
|
||||
(as `didCompleteWithError` already did).
|
||||
- `src/naett_osx.c` / `src/naett_android.c`: both threw away the body writer's return value.
|
||||
Windows and Linux already treat a short write as a failed request - it's how the default
|
||||
writer reports it couldn't grow, and how we cancel a transfer - so on those two a short write
|
||||
silently truncated the body and still looked like a success. Apple also cancels the task, or
|
||||
the data just keeps arriving.
|
||||
- `naett.h`: documented what the body writer's return value means, since three of the four
|
||||
backends' behaviour depends on it.
|
||||
- `src/naett_win.c`: a short write from the body writer marked the request complete and then
|
||||
queued another read anyway, so the transfer carried on and WinHTTP kept writing into a
|
||||
response the caller was by then free to close. It stops there now, and the callback returns
|
||||
early for anything raised after the request is complete.
|
||||
- `src/naett_core.c`: `naettMake` only asserted that the request wasn't NULL, and the request
|
||||
constructors return NULL when the platform can't set one up - a URL it can't parse, say. In a
|
||||
release build that walked into a null dereference. Returns NULL instead.
|
||||
- `src/naett_osx.c`: the delegate callbacks ignore a response that's already complete, so a
|
||||
cancellation we asked for doesn't overwrite the error that caused it.
|
||||
- `src/naett_core.c` / `naett.h`: `naettInit` asserted it was only ever called once, so a caller
|
||||
that can be entered more than again needed a flag purely to guard it. A repeat call is a no-op
|
||||
now, the way `WSAStartup` behaves, and `net::Init` dropped its `g_naettInitialized`.
|
||||
@@ -17,6 +17,10 @@ typedef void* naettInitData;
|
||||
/**
|
||||
* @brief Global init method.
|
||||
* Call to initialize the library.
|
||||
*
|
||||
* PPSSPP: calling this more than once is fine - everything after the first call does nothing,
|
||||
* including the platform setup, so the init data from the first call is the one that sticks.
|
||||
* Not thread safe; call it during startup, before anything else can reach the library.
|
||||
*/
|
||||
void naettInit(naettInitData initThing);
|
||||
|
||||
@@ -41,6 +45,11 @@ naettOption* naettBody(const char* body, int size);
|
||||
// Sets a request body reader.
|
||||
naettOption* naettBodyReader(naettReadFunc reader, void* userData);
|
||||
// Sets a response body writer.
|
||||
//
|
||||
// PPSSPP: the writer is handed each chunk as it arrives, on whichever thread the backend runs
|
||||
// its transfer on, and must return the number of bytes it took. Returning anything else fails
|
||||
// the request with naettReadError and stops the transfer - which is both how the default writer
|
||||
// reports that it couldn't grow its buffer, and how a caller cancels one in progress.
|
||||
naettOption* naettBodyWriter(naettWriteFunc writer, void* userData);
|
||||
// Sets connection timeout in milliseconds.
|
||||
naettOption* naettTimeout(int milliSeconds);
|
||||
|
||||
@@ -297,7 +297,14 @@ static void* processRequest(void* data) {
|
||||
break;
|
||||
} else if (bytesRead > 0) {
|
||||
(*env)->GetByteArrayRegion(env, buffer, 0, bytesRead, (jbyte*) byteBuffer);
|
||||
req->options.bodyWriter(byteBuffer, bytesRead, req->options.bodyWriterData);
|
||||
// PPSSPP: a short write from the body writer fails the request - it's how a caller
|
||||
// aborts, and how the default writer reports that it couldn't grow. Upstream ignored
|
||||
// it, so the body was silently truncated and still reported as a success.
|
||||
int written = req->options.bodyWriter(byteBuffer, bytesRead, req->options.bodyWriterData);
|
||||
if (written != bytesRead) {
|
||||
res->code = naettReadError;
|
||||
goto finally;
|
||||
}
|
||||
res->totalBytesRead += bytesRead;
|
||||
}
|
||||
} while (!res->closeRequested);
|
||||
|
||||
@@ -137,7 +137,12 @@ static void applyOptionParams(InternalRequest* req, InternalOption* option) {
|
||||
// Public API
|
||||
|
||||
void naettInit(naettInitData initData) {
|
||||
assert(!initialized);
|
||||
// PPSSPP: upstream asserted that this was the first call. Callers that can be entered more
|
||||
// than once then need a flag of their own purely to guard this, which is bookkeeping the
|
||||
// library may as well do itself - so a repeat call is a no-op instead.
|
||||
if (initialized) {
|
||||
return;
|
||||
}
|
||||
naettPlatformInit(initData);
|
||||
initialized = 1;
|
||||
}
|
||||
@@ -310,6 +315,12 @@ naettReq* naettRequestWithOptions(const char* url, int numOptions, const naettOp
|
||||
naettRes* naettMake(naettReq* request) {
|
||||
assert(initialized);
|
||||
assert(request != NULL);
|
||||
// PPSSPP: naettRequest* returns NULL when the platform can't set the request up - a URL it
|
||||
// can't parse, most likely - and the assert above is compiled out in release, so this used to
|
||||
// walk straight into a null dereference.
|
||||
if (request == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
InternalRequest* req = (InternalRequest*)request;
|
||||
naettAlloc(InternalResponse, res);
|
||||
|
||||
@@ -100,7 +100,8 @@ void didReceiveData(id self, SEL _sel, id session, id dataTask, id data) {
|
||||
object_getInstanceVariable(self, "response", (void**)&res);
|
||||
// PPSSPP: didComplete already checked this; this one didn't, and the delegate outlives the
|
||||
// response when a session is invalidated.
|
||||
if (res == NULL) {
|
||||
if (res == NULL || res->complete) {
|
||||
// PPSSPP: once complete, the caller may already be closing this - don't touch it further.
|
||||
release(p);
|
||||
return;
|
||||
}
|
||||
@@ -153,7 +154,18 @@ void didReceiveData(id self, SEL _sel, id session, id dataTask, id data) {
|
||||
const void* bytes = objc_msgSend_t(const void*)(data, sel("bytes"));
|
||||
NSUInteger length = objc_msgSend_t(NSUInteger)(data, sel("length"));
|
||||
|
||||
res->request->options.bodyWriter(bytes, (int)length, res->request->options.bodyWriterData);
|
||||
// PPSSPP: a writer that doesn't take everything is failing the request - that's how a caller
|
||||
// aborts a transfer, and how the default writer reports that it couldn't grow. Upstream threw
|
||||
// the result away here, so a short write silently truncated the body and still looked like a
|
||||
// success. Cancel the task too, or the data just keeps coming.
|
||||
int written = res->request->options.bodyWriter(bytes, (int)length, res->request->options.bodyWriterData);
|
||||
if (written != (int)length) {
|
||||
res->code = naettReadError;
|
||||
res->complete = 1;
|
||||
objc_msgSend_void(dataTask, sel("cancel"));
|
||||
release(p);
|
||||
return;
|
||||
}
|
||||
res->totalBytesRead += (int)length;
|
||||
|
||||
release(p);
|
||||
@@ -163,6 +175,11 @@ static void didComplete(id self, SEL _sel, id session, id dataTask, id error) {
|
||||
InternalResponse* res = NULL;
|
||||
object_getInstanceVariable(self, "response", (void**)&res);
|
||||
if (res != NULL) {
|
||||
// PPSSPP: if we already failed the request - a writer that wouldn't take the data, say -
|
||||
// that's the reason we want reported, not the cancellation it caused.
|
||||
if (res->complete) {
|
||||
return;
|
||||
}
|
||||
if (error != nil) {
|
||||
res->code = naettConnectionError;
|
||||
}
|
||||
|
||||
@@ -111,6 +111,13 @@ static void CALLBACK
|
||||
callback(HINTERNET request, DWORD_PTR context, DWORD status, LPVOID statusInformation, DWORD statusInfoLength) {
|
||||
InternalResponse* res = (InternalResponse*)context;
|
||||
|
||||
// PPSSPP: once we've reported the request as complete the caller is free to close it, so
|
||||
// don't start any more I/O against it - anything still in flight would be writing into a
|
||||
// response that's being freed.
|
||||
if (res->complete) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (status) {
|
||||
case WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE: {
|
||||
// PPSSPP: the sizing call only fills in bufSize when it fails with
|
||||
@@ -177,9 +184,17 @@ callback(HINTERNET request, DWORD_PTR context, DWORD status, LPVOID statusInform
|
||||
size_t bytesRead = statusInfoLength;
|
||||
|
||||
InternalRequest* req = res->request;
|
||||
if (req == NULL) {
|
||||
return;
|
||||
}
|
||||
if (req->options.bodyWriter(res->buffer, (int)bytesRead, req->options.bodyWriterData) != bytesRead) {
|
||||
// PPSSPP: the writer failed the request - it couldn't grow, or the caller is
|
||||
// cancelling. Upstream marked it complete and then queued another read anyway,
|
||||
// which both kept the transfer running and left WinHTTP writing into a response
|
||||
// the caller was by then free to close.
|
||||
res->code = naettReadError;
|
||||
res->complete = 1;
|
||||
break;
|
||||
}
|
||||
res->totalBytesRead += (int)bytesRead;
|
||||
// PPSSPP: bytesLeft is unsigned, so a read longer than announced used to wrap it
|
||||
|
||||
Reference in new issue
Block a user