Fixed logging issue and other minor tweaks

• Fixed a bug where the libSession logs wouldn't all come through correctly
• Added logic to handle response headers from libSession
• Minor optimisation to closed groups poller setup
• Minor logging tweaks
pull/1018/head
Morgan Pretty 7 months ago
parent a87a547886
commit d8294aac0d

@ -1 +1 @@
Subproject commit 1d20f5e392ba55847a30431f196d638834a4feb0
Subproject commit 6a1ab5168ff2cd2f967d804ea6e1130a939f2189

@ -7706,7 +7706,7 @@
CLANG_WARN__ARC_BRIDGE_CAST_NONARC = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "iPhone Developer";
CURRENT_PROJECT_VERSION = 484;
CURRENT_PROJECT_VERSION = 485;
ENABLE_BITCODE = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
@ -7784,7 +7784,7 @@
CLANG_WARN__ARC_BRIDGE_CAST_NONARC = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "iPhone Distribution";
CURRENT_PROJECT_VERSION = 484;
CURRENT_PROJECT_VERSION = 485;
ENABLE_BITCODE = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_NO_COMMON_BLOCKS = YES;

@ -819,7 +819,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
public func stopPollers(shouldStopUserPoller: Bool = true) {
if shouldStopUserPoller {
poller.stopAllPollers()
poller.stop()
}
ClosedGroupPoller.shared.stopAllPollers()

@ -267,8 +267,8 @@ public extension Crypto.Generator {
id: "messageServerHash",
args: [swarmPubkey, namespace, data]
) {
var cSwarmPubkey: [CChar] = try swarmPubkey.cString(using: .utf8) ?? { throw LibSessionError.invalidCConversion }()
var cData: [CChar] = try data.base64EncodedString().cString(using: .utf8) ?? { throw LibSessionError.invalidCConversion }()
let cSwarmPubkey: [CChar] = try swarmPubkey.cString(using: .utf8) ?? { throw LibSessionError.invalidCConversion }()
let cData: [CChar] = try data.base64EncodedString().cString(using: .utf8) ?? { throw LibSessionError.invalidCConversion }()
var cHash: [CChar] = [CChar](repeating: 0, count: 65)
guard session_compute_message_hash(cSwarmPubkey, Int16(namespace.rawValue), cData, &cHash) else {

@ -30,7 +30,7 @@ public final class ClosedGroupPoller: Poller {
// Fetch all closed groups (excluding any don't contain the current user as a
// GroupMemeber as the user is no longer a member of those)
dependencies.storage
.read { db in
.read { db -> Set<String> in
try ClosedGroup
.select(.threadId)
.joining(
@ -38,7 +38,7 @@ public final class ClosedGroupPoller: Poller {
.filter(GroupMember.Columns.profileId == getUserHexEncodedPublicKey(db, using: dependencies))
)
.asRequest(of: String.self)
.fetchAll(db)
.fetchSet(db)
}
.defaulting(to: [])
.forEach { [weak self] publicKey in

@ -253,14 +253,14 @@ extension OpenGroupAPI {
.handleEvents(
receiveOutput: { pollFailureCount, prunedIds in
let pollEndTime: TimeInterval = dependencies.dateNow.timeIntervalSince1970
Log.info("Open group polling to \(server) failed in \(.seconds(pollEndTime - pollStartTime), unit: .s) due to error: \(error). Setting failure count to \(pollFailureCount + 1).")
Log.error("Open group polling to \(server) failed in \(.seconds(pollEndTime - pollStartTime), unit: .s) due to error: \(error). Setting failure count to \(pollFailureCount + 1).")
// Add a note to the logs that this happened
if !prunedIds.isEmpty {
let rooms: String = prunedIds
.compactMap { $0.components(separatedBy: server).last }
.joined(separator: ", ")
Log.info("Hidden open group failure count surpassed \(Poller.maxHiddenRoomFailureCount), removed hidden rooms \(rooms).")
Log.error("Hidden open group failure count surpassed \(Poller.maxHiddenRoomFailureCount), removed hidden rooms \(rooms).")
}
}
)

@ -36,13 +36,11 @@ public final class NotificationServiceExtension: UNNotificationServiceExtension
// Abort if the main app is running
guard !(UserDefaults.sharedLokiProject?[.isMainAppActive]).defaulting(to: false) else {
Log.info("didReceive called while main app running.")
return self.completeSilenty(handledNotification: false, isMainAppAndActive: true)
}
guard let notificationContent = request.content.mutableCopy() as? UNMutableNotificationContent else {
Log.info("didReceive called with no content.")
return self.completeSilenty(handledNotification: false)
return self.completeSilenty(handledNotification: false, noContent: true)
}
Log.info("didReceive called.")
@ -366,7 +364,7 @@ public final class NotificationServiceExtension: UNNotificationServiceExtension
completeSilenty(handledNotification: false)
}
private func completeSilenty(handledNotification: Bool, isMainAppAndActive: Bool = false) {
private func completeSilenty(handledNotification: Bool, isMainAppAndActive: Bool = false, noContent: Bool = false) {
// Ensure we only run this once
guard
hasCompleted.mutate({ hasCompleted in
@ -387,7 +385,15 @@ public final class NotificationServiceExtension: UNNotificationServiceExtension
}
let duration: CFTimeInterval = (CACurrentMediaTime() - startTime)
Log.info(handledNotification ? "Completed after handling notification in \(.seconds(duration), unit: .ms)." : "Completed silently after \(.seconds(duration), unit: .ms).")
let logMessage: String = {
switch (isMainAppAndActive, handledNotification, noContent) {
case (true, _, _): return "Called while main app running, ignoring after \(.seconds(duration), unit: .ms)."
case (_, _, true): return "Called with no content, ignoring after \(.seconds(duration), unit: .ms)."
case (_, true, _): return "Completed after handling notification in \(.seconds(duration), unit: .ms)."
default: return "Completed silently after \(.seconds(duration), unit: .ms)."
}
}()
Log.info(logMessage)
Log.flush()
self.contentHandler!(silentContent)

@ -234,7 +234,7 @@ public extension LibSession {
requestAndPathBuildTimeout: TimeInterval?,
using dependencies: Dependencies
) -> AnyPublisher<(ResponseInfoType, Data?), Error> {
typealias Output = (success: Bool, timeout: Bool, statusCode: Int, data: Data?)
typealias Output = (success: Bool, timeout: Bool, statusCode: Int, headers: [String: String], data: Data?)
return getOrCreateNetwork()
.tryFlatMap { network in
@ -272,9 +272,11 @@ public extension LibSession {
cSwarmPublicKey,
Int64(floor(requestTimeout * 1000)),
Int64(floor((requestAndPathBuildTimeout ?? 0) * 1000)),
{ success, timeout, statusCode, dataPtr, dataLen, ctx in
{ success, timeout, statusCode, cHeaders, cHeaderVals, headerLen, dataPtr, dataLen, ctx in
let headers: [String: String] = CallbackWrapper<Output>
.headers(cHeaders, cHeaderVals, headerLen)
let data: Data? = dataPtr.map { Data(bytes: $0, count: dataLen) }
CallbackWrapper<Output>.run(ctx, (success, timeout, Int(statusCode), data))
CallbackWrapper<Output>.run(ctx, (success, timeout, Int(statusCode), headers, data))
},
wrapper.unsafePointer()
)
@ -287,17 +289,19 @@ public extension LibSession {
cPayloadBytes.count,
Int64(floor(requestTimeout * 1000)),
Int64(floor((requestAndPathBuildTimeout ?? 0) * 1000)),
{ success, timeout, statusCode, dataPtr, dataLen, ctx in
{ success, timeout, statusCode, cHeaders, cHeaderVals, headerLen, dataPtr, dataLen, ctx in
let headers: [String: String] = CallbackWrapper<Output>
.headers(cHeaders, cHeaderVals, headerLen)
let data: Data? = dataPtr.map { Data(bytes: $0, count: dataLen) }
CallbackWrapper<Output>.run(ctx, (success, timeout, Int(statusCode), data))
CallbackWrapper<Output>.run(ctx, (success, timeout, Int(statusCode), headers, data))
},
wrapper.unsafePointer()
)
}
}
.tryMap { success, timeout, statusCode, data -> (any ResponseInfoType, Data?) in
try throwErrorIfNeeded(success, timeout, statusCode, data)
return (Network.ResponseInfo(code: statusCode), data)
.tryMap { success, timeout, statusCode, headers, data -> (any ResponseInfoType, Data?) in
try throwErrorIfNeeded(success, timeout, statusCode, headers, data)
return (Network.ResponseInfo(code: statusCode, headers: headers), data)
}
}
.eraseToAnyPublisher()
@ -309,7 +313,7 @@ public extension LibSession {
fileName: String?,
using dependencies: Dependencies = Dependencies()
) -> AnyPublisher<(ResponseInfoType, FileUploadResponse), Error> {
typealias Output = (success: Bool, timeout: Bool, statusCode: Int, data: Data?)
typealias Output = (success: Bool, timeout: Bool, statusCode: Int, headers: [String: String], data: Data?)
return getOrCreateNetwork()
.tryFlatMap { network in
@ -323,20 +327,22 @@ public extension LibSession {
fileName?.cString(using: .utf8),
Int64(floor(Network.fileUploadTimeout * 1000)),
0,
{ success, timeout, statusCode, dataPtr, dataLen, ctx in
{ success, timeout, statusCode, cHeaders, cHeaderVals, headerLen, dataPtr, dataLen, ctx in
let headers: [String: String] = CallbackWrapper<Output>
.headers(cHeaders, cHeaderVals, headerLen)
let data: Data? = dataPtr.map { Data(bytes: $0, count: dataLen) }
CallbackWrapper<Output>.run(ctx, (success, timeout, Int(statusCode), data))
CallbackWrapper<Output>.run(ctx, (success, timeout, Int(statusCode), headers, data))
},
wrapper.unsafePointer()
)
}
.tryMap { success, timeout, statusCode, maybeData -> (any ResponseInfoType, FileUploadResponse) in
try throwErrorIfNeeded(success, timeout, statusCode, maybeData)
.tryMap { success, timeout, statusCode, headers, maybeData -> (any ResponseInfoType, FileUploadResponse) in
try throwErrorIfNeeded(success, timeout, statusCode, headers, maybeData)
guard let data: Data = maybeData else { throw NetworkError.parsingFailed }
return (
Network.ResponseInfo(code: statusCode),
Network.ResponseInfo(code: statusCode, headers: headers),
try FileUploadResponse.decoded(from: data, using: dependencies)
)
}
@ -344,7 +350,7 @@ public extension LibSession {
}
static func downloadFile(from server: Network.Destination) -> AnyPublisher<(ResponseInfoType, Data), Error> {
typealias Output = (success: Bool, timeout: Bool, statusCode: Int, data: Data?)
typealias Output = (success: Bool, timeout: Bool, statusCode: Int, headers: [String: String], data: Data?)
return getOrCreateNetwork()
.tryFlatMap { network in
@ -355,20 +361,22 @@ public extension LibSession {
try wrapper.cServerDestination(server),
Int64(floor(Network.fileDownloadTimeout * 1000)),
0,
{ success, timeout, statusCode, dataPtr, dataLen, ctx in
{ success, timeout, statusCode, cHeaders, cHeaderVals, headerLen, dataPtr, dataLen, ctx in
let headers: [String: String] = CallbackWrapper<Output>
.headers(cHeaders, cHeaderVals, headerLen)
let data: Data? = dataPtr.map { Data(bytes: $0, count: dataLen) }
CallbackWrapper<Output>.run(ctx, (success, timeout, Int(statusCode), data))
CallbackWrapper<Output>.run(ctx, (success, timeout, Int(statusCode), headers, data))
},
wrapper.unsafePointer()
)
}
.tryMap { success, timeout, statusCode, maybeData -> (any ResponseInfoType, Data) in
try throwErrorIfNeeded(success, timeout, statusCode, maybeData)
.tryMap { success, timeout, statusCode, headers, maybeData -> (any ResponseInfoType, Data) in
try throwErrorIfNeeded(success, timeout, statusCode, headers, maybeData)
guard let data: Data = maybeData else { throw NetworkError.parsingFailed }
return (
Network.ResponseInfo(code: statusCode),
Network.ResponseInfo(code: statusCode, headers: headers),
data
)
}
@ -379,7 +387,7 @@ public extension LibSession {
ed25519SecretKey: [UInt8],
using dependencies: Dependencies = Dependencies()
) -> AnyPublisher<(ResponseInfoType, AppVersionResponse), Error> {
typealias Output = (success: Bool, timeout: Bool, statusCode: Int, data: Data?)
typealias Output = (success: Bool, timeout: Bool, statusCode: Int, headers: [String: String], data: Data?)
return getOrCreateNetwork()
.tryFlatMap { network in
@ -393,20 +401,22 @@ public extension LibSession {
&cEd25519SecretKey,
Int64(floor(Network.fileDownloadTimeout * 1000)),
0,
{ success, timeout, statusCode, dataPtr, dataLen, ctx in
{ success, timeout, statusCode, cHeaders, cHeaderVals, headerLen, dataPtr, dataLen, ctx in
let headers: [String: String] = CallbackWrapper<Output>
.headers(cHeaders, cHeaderVals, headerLen)
let data: Data? = dataPtr.map { Data(bytes: $0, count: dataLen) }
CallbackWrapper<Output>.run(ctx, (success, timeout, Int(statusCode), data))
CallbackWrapper<Output>.run(ctx, (success, timeout, Int(statusCode), headers, data))
},
wrapper.unsafePointer()
)
}
.tryMap { success, timeout, statusCode, maybeData -> (any ResponseInfoType, AppVersionResponse) in
try throwErrorIfNeeded(success, timeout, statusCode, maybeData)
.tryMap { success, timeout, statusCode, headers, maybeData -> (any ResponseInfoType, AppVersionResponse) in
try throwErrorIfNeeded(success, timeout, statusCode, headers, maybeData)
guard let data: Data = maybeData else { throw NetworkError.parsingFailed }
return (
Network.ResponseInfo(code: statusCode),
Network.ResponseInfo(code: statusCode, headers: headers),
try AppVersionResponse.decoded(from: data, using: dependencies)
)
}
@ -544,10 +554,16 @@ public extension LibSession {
_ success: Bool,
_ timeout: Bool,
_ statusCode: Int,
_ headers: [String: String],
_ data: Data?
) throws {
guard !success || statusCode < 200 || statusCode > 299 else { return }
guard !timeout else { throw NetworkError.timeout }
guard !timeout else {
switch data.map({ String(data: $0, encoding: .ascii) }) {
case .none: throw NetworkError.timeout(error: "\(NetworkError.unknown)", rawData: data)
case .some(let responseString): throw NetworkError.timeout(error: responseString, rawData: data)
}
}
/// Handle status codes with specific meanings
switch (statusCode, data.map { String(data: $0, encoding: .ascii) }) {
@ -675,6 +691,17 @@ public extension Network.Destination {
}
internal extension LibSession.CallbackWrapper {
static func headers(
_ cHeaders: UnsafeMutablePointer<UnsafePointer<CChar>?>?,
_ cHeaderVals: UnsafeMutablePointer<UnsafePointer<CChar>?>?,
_ count: Int
) -> [String: String] {
let headers: [String] = [String](pointer: cHeaders, count: count, defaultValue: [])
let headerVals: [String] = [String](pointer: cHeaderVals, count: count, defaultValue: [])
return zip(headers, headerVals)
.reduce(into: [:]) { result, next in result[next.0] = next.1 }
}
func cServerDestination(_ destination: Network.Destination) throws -> network_server_destination {
guard
case .server(let url, let method, let headers, let x25519PublicKey) = destination,

@ -425,7 +425,11 @@ public class PagedDatabaseObserver<ObservedTable, T>: TransactionObserver where
let updatedItems: [T] = {
do { return try dataQuery(targetRowIds).fetchAll(db) }
catch {
SNLog("[PagedDatabaseObserver] Error fetching data during change: \(error)")
// If the database is suspended then don't bother logging (as we already know why)
if !Storage.shared.isSuspended {
Log.error("[PagedDatabaseObserver] Error fetching data during change: \(error)")
}
return []
}
}()

@ -670,7 +670,7 @@ public struct AllLoggingCategories {
/// **Note:** We only want to use the `rawValue` to distinguish between logging categories
/// as the `defaultLevel` can change via the dev settings and any additional metadata could
/// be file/class specific
category.rawValue != cat.rawValue
category.rawValue == cat.rawValue
})
else { return }

@ -39,24 +39,27 @@ extension LibSession {
let cat: String = String(pointer: catPtr, length: catLen, encoding: .utf8)
else { return }
/// Logs from libSession come through in the format:
/// `[yyyy-MM-dd hh:mm:ss] [+{lifetime}s] [{cat}:{lvl}|log.hpp:{line}] {message}`
/// We want to remove the extra data because it doesn't help the logs
let processedMessage: String = {
let logParts: [String] = msg.components(separatedBy: "] ")
/// Dispatch to another thread so we don't block thread triggering the log
DispatchQueue.global(qos: .background).async {
/// Logs from libSession come through in the format:
/// `[yyyy-MM-dd hh:mm:ss] [+{lifetime}s] [{cat}:{lvl}|log.hpp:{line}] {message}`
/// We want to remove the extra data because it doesn't help the logs
let processedMessage: String = {
let logParts: [String] = msg.components(separatedBy: "] ")
guard logParts.count == 4 else { return msg.trimmingCharacters(in: .whitespacesAndNewlines) }
let message: String = String(logParts[3]).trimmingCharacters(in: .whitespacesAndNewlines)
return "\(logParts[1])] \(message)"
}()
guard logParts.count == 4 else { return msg.trimmingCharacters(in: .whitespacesAndNewlines) }
let message: String = String(logParts[3]).trimmingCharacters(in: .whitespacesAndNewlines)
return "\(logParts[1])] \(message)"
}()
Log.custom(
Log.Level(lvl),
[Log.Category(rawValue: cat, customPrefix: "libSession:")],
processedMessage
)
Log.custom(
Log.Level(lvl),
[Log.Category(rawValue: cat, customPrefix: "libSession:")],
processedMessage
)
}
})
}

@ -22,11 +22,38 @@ public extension String {
// MARK: - Array
public extension Array where Element == String {
init?(
init?(pointer: UnsafeMutablePointer<UnsafeMutablePointer<CChar>?>?, count: Int?) {
self.init(pointee: pointer.map { $0.pointee.map { UnsafePointer($0) } }, count: count)
}
init?(pointer: UnsafeMutablePointer<UnsafePointer<CChar>?>?, count: Int?) {
self.init(pointee: pointer.map { $0.pointee }, count: count)
}
init(
pointer: UnsafeMutablePointer<UnsafeMutablePointer<CChar>?>?,
count: Int?,
defaultValue: [String]
) {
self = ([String](pointer: pointer, count: count) ?? defaultValue)
}
init(
pointer: UnsafeMutablePointer<UnsafePointer<CChar>?>?,
count: Int?,
defaultValue: [String]
) {
self = ([String](pointer: pointer, count: count) ?? defaultValue)
}
init?(
pointee: UnsafePointer<CChar>?,
count: Int?
) {
guard let count: Int = count else { return nil }
guard
let count: Int = count,
let pointee: UnsafePointer<CChar> = pointee
else { return nil }
// If we were given a count but it's 0 then trying to access the pointer could
// crash (as it could be bad memory) so just return an empty array
@ -34,27 +61,17 @@ public extension Array where Element == String {
self = []
return
}
guard let pointee: UnsafeMutablePointer<CChar> = pointer?.pointee else { return nil }
self = (0..<count)
.reduce(into: []) { result, index in
/// We need to calculate the start position of each of the hashes in memory which will
/// be at the end of the previous hash plus one (due to the null termination character
/// which isn't included in Swift strings so isn't included in `count`)
let prevLength: Int = (result.isEmpty ? 0 :
result.map { ($0.count + 1) }.reduce(0, +)
)
result.append(String(cString: pointee.advanced(by: prevLength)))
}
}
init(
pointer: UnsafeMutablePointer<UnsafeMutablePointer<CChar>?>?,
count: Int?,
defaultValue: [String]
) {
self = ([String](pointer: pointer, count: count) ?? defaultValue)
self = (0..<count).reduce(into: []) { result, index in
/// We need to calculate the start position of each of the hashes in memory which will
/// be at the end of the previous hash plus one (due to the null termination character
/// which isn't included in Swift strings so isn't included in `count`)
let prevLength: Int = (result.isEmpty ? 0 :
result.map { ($0.count + 1) }.reduce(0, +)
)
result.append(String(cString: pointee.advanced(by: prevLength)))
}
}
}

@ -18,7 +18,7 @@ public enum NetworkError: Error, Equatable, CustomStringConvertible {
case gatewayTimeout
case badRequest(error: String, rawData: Data?)
case requestFailed(error: String, rawData: Data?)
case timeout
case timeout(error: String, rawData: Data?)
case suspended
case unknown
@ -36,7 +36,7 @@ public enum NetworkError: Error, Equatable, CustomStringConvertible {
case .serviceUnavailable: return "Service unavailable (NetworkError.serviceUnavailable)."
case .gatewayTimeout: return "Gateway timeout (NetworkError.gatewayTimeout)."
case .badRequest(let error, _), .requestFailed(let error, _): return error
case .timeout: return "The request timed out (NetworkError.timeout)."
case .timeout(let error, _): return "The request timed out with error: \(error) (NetworkError.timeout)."
case .suspended: return "Network requests are suspended (NetworkError.suspended)."
case .unknown: return "An unknown error occurred (NetworkError.unknown)."
}

@ -310,7 +310,7 @@ class TypeConversionUtilitiesSpec: QuickSpec {
"Test3AndExtra".cString(using: .utf8)!
)
let result = test.withUnsafeMutableBufferPointer { ptr in
var mutablePtr = UnsafeMutablePointer(ptr.baseAddress)
var mutablePtr = UnsafePointer(ptr.baseAddress)
return [String](pointer: &mutablePtr, count: 3)
}
@ -322,7 +322,7 @@ class TypeConversionUtilitiesSpec: QuickSpec {
it("returns an empty array if given one") {
var test = [CChar]()
let result = test.withUnsafeMutableBufferPointer { ptr in
var mutablePtr = UnsafeMutablePointer(ptr.baseAddress)
var mutablePtr = UnsafePointer(ptr.baseAddress)
return [String](pointer: &mutablePtr, count: 0)
}
@ -338,7 +338,7 @@ class TypeConversionUtilitiesSpec: QuickSpec {
"Test2".cString(using: .utf8)!
)
let result = test.withUnsafeMutableBufferPointer { ptr in
var mutablePtr = UnsafeMutablePointer(ptr.baseAddress)
var mutablePtr = UnsafePointer(ptr.baseAddress)
return [String](pointer: &mutablePtr, count: 3)
}
@ -348,7 +348,7 @@ class TypeConversionUtilitiesSpec: QuickSpec {
// MARK: ---- returns null when given a null pointer
it("returns null when given a null pointer") {
expect([String](pointer: nil, count: 5)).to(beNil())
expect([String](pointee: nil, count: 5)).to(beNil())
}
// MARK: ---- returns null when given a null count
@ -365,7 +365,8 @@ class TypeConversionUtilitiesSpec: QuickSpec {
// MARK: ---- returns the default value if given null values
it("returns the default value if given null values") {
expect([String](pointer: nil, count: 5, defaultValue: ["Test"]))
let ptr: UnsafeMutablePointer<UnsafePointer<CChar>?>? = nil
expect([String](pointer: ptr, count: 5, defaultValue: ["Test"]))
.to(equal(["Test"]))
}
}

Loading…
Cancel
Save