Index: generators/TableGenerator.m ================================================================== --- generators/TableGenerator.m +++ generators/TableGenerator.m @@ -76,11 +76,15 @@ } - (void)client: (OFHTTPClient *)client didPerformRequest: (OFHTTPRequest *)request response: (OFHTTPResponse *)response + exception: (id)exception { + if (exception != nil) + @throw exception; + [of_stdout writeLine: @" done"]; switch (_state) { case STATE_UNICODE_DATA: [self parseUnicodeData: response]; @@ -89,17 +93,10 @@ [self parseCaseFolding: response]; break; } } -- (void)client: (OFHTTPClient *)client - didFailWithException: (id)exception - request: (OFHTTPRequest *)request -{ - @throw exception; -} - - (void)parseUnicodeData: (OFHTTPResponse *)response { OFString *line; OFHTTPRequest *request; Index: src/OFHTTPClient.h ================================================================== --- src/OFHTTPClient.h +++ src/OFHTTPClient.h @@ -40,27 +40,17 @@ /** * @brief A callback which is called when an OFHTTPClient performed a request. * * @param client The OFHTTPClient which performed the request * @param request The request the OFHTTPClient performed - * @param response The response to the request performed + * @param response The response to the request performed, or nil on error + * @param exception An exception if the request failed, or nil on success */ - (void)client: (OFHTTPClient *)client didPerformRequest: (OFHTTPRequest *)request - response: (OFHTTPResponse *)response; - -/** - * @brief A callback which is called when an OFHTTPClient encountered an - * exception while performing a request. - * - * @param client The client which encountered an exception - * @param exception The exception the client encountered - * @param request The request during which the client encountered the exception - */ -- (void)client: (OFHTTPClient *)client - didFailWithException: (id)exception - request: (OFHTTPRequest *)request; + response: (nullable OFHTTPResponse *)response + exception: (nullable id)exception; @optional /** * @brief A callback which is called when an OFHTTPClient creates a socket. * Index: src/OFHTTPClient.m ================================================================== --- src/OFHTTPClient.m +++ src/OFHTTPClient.m @@ -296,21 +296,23 @@ { [_client close]; _client->_inProgress = false; [_client->_delegate client: _client - didFailWithException: exception - request: _request]; + didPerformRequest: _request + response: nil + exception: exception]; } - (void)createResponseWithSocketOrThrow: (OFTCPSocket *)sock { OFURL *URL = _request.URL; OFHTTPClientResponse *response; OFString *connectionHeader; bool keepAlive; OFString *location; + id exception; response = [[[OFHTTPClientResponse alloc] initWithSocket: sock] autorelease]; response.protocolVersionString = _version; response.statusCode = _status; @@ -420,19 +422,22 @@ } _client->_inProgress = false; if (_status / 100 != 2) - @throw [OFHTTPRequestFailedException + exception = [OFHTTPRequestFailedException exceptionWithRequest: _request response: response]; + else + exception = nil; [_client->_delegate performSelector: @selector(client:didPerformRequest: - response:) + response:exception:) withObject: _client withObject: _request withObject: response + withObject: exception afterDelay: 0]; } - (void)createResponseWithSocket: (OFTCPSocket *)sock { @@ -1128,33 +1133,32 @@ } - (void)client: (OFHTTPClient *)client didPerformRequest: (OFHTTPRequest *)request response: (OFHTTPResponse *)response + exception: (id)exception { + if (exception != nil) { + /* + * Restore the delegate - we're giving up, but not reaching the + * release of the autorelease pool that contains us, so + * resetting it via -[dealloc] might be too late. + */ + _client.delegate = _delegate; + + @throw exception; + } + [[OFRunLoop currentRunLoop] stop]; [_response release]; _response = [response retain]; [_delegate client: client didPerformRequest: request - response: response]; -} - -- (void)client: (OFHTTPClient *)client - didFailWithException: (id)exception - request: (OFHTTPRequest *)request -{ - /* - * Restore the delegate - we're giving up, but not reaching the release - * of the autorelease pool that contains us, so resetting it via - * -[dealloc] might be too late. - */ - _client.delegate = _delegate; - - @throw exception; + response: response + exception: nil]; } - (void)client: (OFHTTPClient *)client didCreateSocket: (OFTCPSocket *)sock request: (OFHTTPRequest *)request Index: tests/OFHTTPClientTests.m ================================================================== --- tests/OFHTTPClientTests.m +++ tests/OFHTTPClientTests.m @@ -99,11 +99,14 @@ } - (void)client: (OFHTTPClient *)client didPerformRequest: (OFHTTPRequest *)request response: (OFHTTPResponse *)response_ + exception: (id)exception { + OF_ENSURE(exception == nil); + response = [response_ retain]; [[OFRunLoop mainRunLoop] stop]; } Index: utils/ofhttp/OFHTTP.m ================================================================== --- utils/ofhttp/OFHTTP.m +++ utils/ofhttp/OFHTTP.m @@ -635,109 +635,10 @@ _length = 0; return true; } -- (void)client: (OFHTTPClient *)client - didFailWithException: (id)e - request: (OFHTTPRequest *)request -{ - if ([e isKindOfClass: [OFResolveHostFailedException class]]) { - if (!_quiet) - [of_stdout writeString: @"\n"]; - - [of_stderr writeLine: - OF_LOCALIZED(@"download_resolve_host_failed", - @"%[prog]: Failed to download <%[url]>!\n" - @" Failed to resolve host: %[exception]", - @"prog", [OFApplication programName], - @"url", request.URL.string, - @"exception", e)]; - } else if ([e isKindOfClass: [OFConnectionFailedException class]]) { - if (!_quiet) - [of_stdout writeString: @"\n"]; - - [of_stderr writeLine: - OF_LOCALIZED(@"download_failed_connection_failed", - @"%[prog]: Failed to download <%[url]>!\n" - @" Connection failed: %[exception]", - @"prog", [OFApplication programName], - @"url", request.URL.string, - @"exception", e)]; - } else if ([e isKindOfClass: [OFInvalidServerReplyException class]]) { - if (!_quiet) - [of_stdout writeString: @"\n"]; - - [of_stderr writeLine: - OF_LOCALIZED(@"download_failed_invalid_server_reply", - @"%[prog]: Failed to download <%[url]>!\n" - @" Invalid server reply!", - @"prog", [OFApplication programName], - @"url", request.URL.string)]; - } else if ([e isKindOfClass: [OFUnsupportedProtocolException class]]) { - if (!_quiet) - [of_stdout writeString: @"\n"]; - - [of_stderr writeLine: OF_LOCALIZED(@"no_ssl_library", - @"%[prog]: No TLS library loaded!\n" - @" In order to download via https, you need to preload an " - @"TLS library for ObjFW\n" - @" such as ObjOpenSSL!", - @"prog", [OFApplication programName])]; - } else if ([e isKindOfClass: [OFReadOrWriteFailedException class]]) { - OFString *error = OF_LOCALIZED( - @"download_failed_read_or_write_failed_any", - @"Read or write failed"); - - if (!_quiet) - [of_stdout writeString: @"\n"]; - - if ([e isKindOfClass: [OFReadFailedException class]]) - error = OF_LOCALIZED( - @"download_failed_read_or_write_failed_read", - @"Read failed"); - else if ([e isKindOfClass: [OFWriteFailedException class]]) - error = OF_LOCALIZED( - @"download_failed_read_or_write_failed_write", - @"Write failed"); - - [of_stderr writeLine: - OF_LOCALIZED(@"download_failed_read_or_write_failed", - @"%[prog]: Failed to download <%[url]>!\n" - @" %[error]: %[exception]", - @"prog", [OFApplication programName], - @"url", request.URL.string, - @"error", error, - @"exception", e)]; - } else if ([e isKindOfClass: [OFHTTPRequestFailedException class]]) { - short statusCode; - OFString *codeString; - - if (_ignoreStatus) { - [self client: client - didPerformRequest: [e request] - response: [e response]]; - return; - } - - statusCode = [[e response] statusCode]; - codeString = [OFString stringWithFormat: @"%hd %@", - statusCode, of_http_status_code_to_string(statusCode)]; - [of_stderr writeLine: OF_LOCALIZED(@"download_failed", - @"%[prog]: Failed to download <%[url]>!\n" - @" HTTP status code: %[code]", - @"prog", [OFApplication programName], - @"url", request.URL.string, - @"code", codeString)]; - } else - @throw e; - - _errorCode = 1; - [self performSelector: @selector(downloadNextURL) - afterDelay: 0]; -} - - (bool)stream: (OFStream *)response didReadIntoBuffer: (void *)buffer length: (size_t)length exception: (id)exception { @@ -895,11 +796,119 @@ } - (void)client: (OFHTTPClient *)client didPerformRequest: (OFHTTPRequest *)request response: (OFHTTPResponse *)response + exception: (id)exception { + if (exception != nil) { + if ([exception isKindOfClass: + [OFResolveHostFailedException class]]) { + if (!_quiet) + [of_stdout writeString: @"\n"]; + + [of_stderr writeLine: + OF_LOCALIZED(@"download_resolve_host_failed", + @"%[prog]: Failed to download <%[url]>!\n" + @" Failed to resolve host: %[exception]", + @"prog", [OFApplication programName], + @"url", request.URL.string, + @"exception", exception)]; + } else if ([exception isKindOfClass: + [OFConnectionFailedException class]]) { + if (!_quiet) + [of_stdout writeString: @"\n"]; + + [of_stderr writeLine: + OF_LOCALIZED(@"download_failed_connection_failed", + @"%[prog]: Failed to download <%[url]>!\n" + @" Connection failed: %[exception]", + @"prog", [OFApplication programName], + @"url", request.URL.string, + @"exception", exception)]; + } else if ([exception isKindOfClass: + [OFInvalidServerReplyException class]]) { + if (!_quiet) + [of_stdout writeString: @"\n"]; + + [of_stderr writeLine: OF_LOCALIZED( + @"download_failed_invalid_server_reply", + @"%[prog]: Failed to download <%[url]>!\n" + @" Invalid server reply!", + @"prog", [OFApplication programName], + @"url", request.URL.string)]; + } else if ([exception isKindOfClass: + [OFUnsupportedProtocolException class]]) { + if (!_quiet) + [of_stdout writeString: @"\n"]; + + [of_stderr writeLine: OF_LOCALIZED(@"no_ssl_library", + @"%[prog]: No TLS library loaded!\n" + @" In order to download via https, you need to " + @"preload an TLS library for ObjFW\n" + @" such as ObjOpenSSL!", + @"prog", [OFApplication programName])]; + } else if ([exception isKindOfClass: + [OFReadOrWriteFailedException class]]) { + OFString *error = OF_LOCALIZED( + @"download_failed_read_or_write_failed_any", + @"Read or write failed"); + + if (!_quiet) + [of_stdout writeString: @"\n"]; + + if ([exception isKindOfClass: + [OFReadFailedException class]]) + error = OF_LOCALIZED( + @"download_failed_read_or_write_failed_" + @"read", + @"Read failed"); + else if ([exception isKindOfClass: + [OFWriteFailedException class]]) + error = OF_LOCALIZED( + @"download_failed_read_or_write_failed_" + @"write", + @"Write failed"); + + [of_stderr writeLine: OF_LOCALIZED( + @"download_failed_read_or_write_failed", + @"%[prog]: Failed to download <%[url]>!\n" + @" %[error]: %[exception]", + @"prog", [OFApplication programName], + @"url", request.URL.string, + @"error", error, + @"exception", exception)]; + } else if ([exception isKindOfClass: + [OFHTTPRequestFailedException class]]) { + short statusCode; + OFString *codeString; + + if (_ignoreStatus) { + exception = nil; + goto after_exception_handling; + } + + statusCode = response.statusCode; + codeString = [OFString stringWithFormat: @"%hd %@", + statusCode, + of_http_status_code_to_string(statusCode)]; + [of_stderr writeLine: OF_LOCALIZED(@"download_failed", + @"%[prog]: Failed to download <%[url]>!\n" + @" HTTP status code: %[code]", + @"prog", [OFApplication programName], + @"url", request.URL.string, + @"code", codeString)]; + } else + @throw exception; + + _errorCode = 1; + [self performSelector: @selector(downloadNextURL) + afterDelay: 0]; + return; + } + +after_exception_handling: if (_method == OF_HTTP_REQUEST_METHOD_HEAD) goto next; if (_detectFileNameRequest) { _currentFileName = [fileNameFromContentDisposition(