         
         // add queued write
         let preparedWrite = PreparedWrite(handle: pdu.handle, value: pdu.partValue, offset: pdu.offset)
-        
         preparedWrites.append(preparedWrite)
-        
         let response = ATTPrepareWriteResponse(handle: pdu.handle, offset: pdu.offset, partValue: pdu.partValue)
-        
-        respond(response)
-    }
-    
-    private func executeWriteRequest(_ pdu: ATTExecuteWriteRequest) {
-        
+        await respond(response)
+    }
+    
+    private func executeWriteRequest(_ pdu: ATTExecuteWriteRequest) async {
         let opcode = type(of: pdu).attributeOpcode
-        
         log?("Execute Write Request (\(pdu))")
-        
         let preparedWrites = self.preparedWrites
         self.preparedWrites = []
-        
         var newValues = [UInt16: Data]()
-        
         switch pdu {
-            
         case .cancel:
-            
             break // queue always cleared
-            
         case .write:
-            
             // validate
             for write in preparedWrites {
-                
                 let previousValue = newValues[write.handle] ?? Data()
-                
                 let newValue = previousValue + write.value
-                
                 // validate offset?
                 newValues[write.handle] = newValue
             }
-            
             // validate new values
             for (handle, newValue) in newValues {
-                
                 let attribute = database[handle: handle]
-                
                 // validate application errors with write callback
                 if let error = willWrite?(attribute.uuid, handle, attribute.value, newValue) {
-                    
-                    errorResponse(opcode, error, handle)
+                    await errorResponse(opcode, error, handle)
                     return
                 }
             }
-            
             // write new values
             for (handle, newValue) in newValues {
-                
                 database.write(newValue, forAttribute: handle)
             }
         }
         
-        respond(ATTExecuteWriteResponse())
-        
+        await respond(ATTExecuteWriteResponse())
         for handle in newValues.keys {
-            
-            didWriteAttribute(handle)
+            await didWriteAttribute(handle)
         }
     }
 }
 
 // MARK: - Supporting Types
 
-private extension GATTServer {
+internal extension GATTServer {
     
     struct PreparedWrite {
         

    
    // MARK: Callbacks
    
    private func exchangeMTU(_ pdu: ATTMaximumTransmissionUnitRequest) {
        
        let serverMTU = preferredMaximumTransmissionUnit.rawValue
        
        let finalMTU = ATTMaximumTransmissionUnit(server: serverMTU, client: pdu.clientMTU)
        
        // Respond with the server MTU (not final MTU)
        connection.send(ATTMaximumTransmissionUnitResponse(serverMTU: serverMTU))
        
        // Set MTU
        maximumTransmissionUnit = finalMTU
        
        log?("MTU Exchange (\(pdu.clientMTU) -> \(serverMTU))")
    }
    
    private func readByGroupType(_ pdu: ATTReadByGroupTypeRequest) {
        
        typealias AttributeData = ATTReadByGroupTypeResponse.AttributeData
        
        log?("Read by Group Type (\(pdu.startHandle) - \(pdu.endHandle))")
        
        // validate handles
        guard pdu.startHandle != 0 && pdu.endHandle != 0
            else { errorResponse(type(of: pdu).attributeOpcode, .invalidHandle); return }
        
        guard pdu.startHandle <= pdu.endHandle
            else { errorResponse(type(of: pdu).attributeOpcode, .invalidHandle, pdu.startHandle); return }
        
        // GATT defines that only the Primary Service and Secondary Service group types 
        // can be used for the "Read By Group Type" request. Return an error if any other group type is given.
        guard pdu.type == .primaryService
            || pdu.type == .secondaryService
            else { errorResponse(type(of: pdu).attributeOpcode, .unsupportedGroupType, pdu.startHandle); return }
        
        let attributeData = database.readByGroupType(handle: (pdu.startHandle, pdu.endHandle), type: pdu.type)
        
        guard let firstAttribute = attributeData.first
            else { errorResponse(type(of: pdu).attributeOpcode, .attributeNotFound, pdu.startHandle); return }
        
        let mtu = Int(connection.maximumTransmissionUnit.rawValue)
        
        let valueLength = firstAttribute.value.count
        
        let response: ATTReadByGroupTypeResponse
        
        // truncate for MTU if first handle is too large
        if ATTReadByGroupTypeResponse([firstAttribute]).dataLength > mtu {
            
            let maxLength = min(min(mtu - 6, 251), valueLength)
            
            let truncatedAttribute = AttributeData(attributeHandle: firstAttribute.attributeHandle,
                                                   endGroupHandle: firstAttribute.endGroupHandle,
                                                   value: Data(firstAttribute.value.prefix(maxLength)))
            
            response = ATTReadByGroupTypeResponse([truncatedAttribute])
            
        } else {
            
            var count = 1
            
            // respond with results that are the same length
            if attributeData.count > 1 {
                
                for (index, attribute) in attributeData.suffix(from: 1).enumerated() {
                    
                    let newCount = index + 1
                    
                    guard attribute.value.count == valueLength,
                        ATTReadByGroupTypeResponse.dataLength(for: attributeData.prefix(newCount)) <= mtu
                        else { break }
                    
                    count = newCount
                }
            }
            
            let limitedAttributes = Array(attributeData.prefix(count))
            
            response = ATTReadByGroupTypeResponse(limitedAttributes)
        }
        
        assert(response.dataLength <= mtu,
               "Response \(response.dataLength) bytes > MTU (\(mtu))")
        
        respond(response)
    }
    
    private func readByType(_ pdu: ATTReadByTypeRequest) {
        
        typealias AttributeData = ATTReadByTypeResponse.AttributeData
        
        log?("Read by Type (\(pdu.attributeType)) (\(pdu.startHandle) - \(pdu.endHandle))")
        
        guard pdu.startHandle != 0 && pdu.endHandle != 0
            else { errorResponse(type(of: pdu).attributeOpcode, .invalidHandle); return }
        
        guard pdu.startHandle <= pdu.endHandle
            else { errorResponse(type(of: pdu).attributeOpcode, .invalidHandle, pdu.startHandle); return }
        
        let attributeData = database
            .readByType(handle: (pdu.startHandle, pdu.endHandle), type: pdu.attributeType)
            .map { AttributeData(handle: $0.handle, value: $0.value) }
        
        guard let firstAttribute = attributeData.first
            else { errorResponse(type(of: pdu).attributeOpcode, .attributeNotFound, pdu.startHandle); return }
        
        let mtu = Int(connection.maximumTransmissionUnit.rawValue)
        
        let valueLength = firstAttribute.value.count
        

    }
    
    /// Handler for Write Request and Command
    private func handleWriteRequest(opcode: ATTOpcode, handle: UInt16, value: Data, shouldRespond: Bool) async {
        
        /// Conditionally respond
        func doResponse( _ block: @autoclosure () async -> ()) async {
            if shouldRespond {
                await block()
            }
        }
        
        log?("Write \(shouldRespond ? "Request" : "Command") (\(handle)) \(value)")
        
        // no attributes, impossible to write
        guard database.attributes.isEmpty == false else {
            await doResponse(await errorResponse(opcode, .invalidHandle, handle))
            return
        }
        
        // validate handle
        guard database.contains(handle: handle) else {
            await errorResponse(opcode, .invalidHandle, handle)
            return
        }
        
        // get attribute
        let attribute = database[handle: handle]
        
        // validate permissions
        if let error = await checkPermissions([.write, .writeAuthentication, .writeEncrypt], attribute) {
            await doResponse(await errorResponse(opcode, error, handle))
            return
        }
        
        // validate application errors with write callback
        if let error = willWrite?(attribute.uuid, handle, attribute.value, value) {
            await doResponse(await errorResponse(opcode, error, handle))
            return
        }
        
        database.write(value, forAttribute: handle)
        await doResponse(await respond(ATTWriteResponse()))
        await didWriteAttribute(handle)
    }
    
    private func didWriteAttribute(_ attributeHandle: UInt16, isLocalWrite: Bool = false) async {
        
        let (group, attribute) = database.attributeGroup(for: attributeHandle)
        assert(attribute.handle == attributeHandle)
