-
Notifications
You must be signed in to change notification settings - Fork 46
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
DataReader gives empty result. #18
Comments
Connection is left open until the request has been completely read.
I found the reason of this bug. The problem is located in Embassy. Solutions: One but in my opinion not a good possibility is to add a sleep as suggested here. Then the request body, which was read in 1 second is initially received with the header. However, when you receive a big request, the time does not suffice in order to read the whole body. Then you will only get the first part. A better solution is to leave the connection open until the whole request has been read. |
@tranquvis I think this is expected behavior Please reference to https://github.com/envoy/Embassy#sendbody As you see, we end the responding by And for And the
In this way, of course the response is ended immediately. The constructor you are using is meant for returning simple data without any async operation, that's why it won't wait for reading POST data body or any other HTTP body data. To solve the problem, you should use another constructor that passes async send body method into the handler.
So instead, you should write something like this mockServer.router["/test"] = JSONResponse() { (requestInfo, sendJSON) -> Any in
let bodyStream = requestInfo["swsgi.input"] as! SWSGIInput
DataReader.read(bodyStream) { data in
let text = String(data: data, encoding: .utf8)
print(text)
sendJSON([])
}
print("return response")
} as you call |
Hi @fangpenlin, I thought that this is expected behaviour. It would be great if you could provide an explanation in your Maybe you like my implementation: import Embassy
import EnvoyAmbassador
struct RequestEvaluator: WebApp {
public static var active: RequestEvaluator?
public let requestHandler: (Data) -> WebApp
private let semaphore = DispatchSemaphore(value: 0)
public init(_ requestHandler: @escaping (Data) -> WebApp) {
self.requestHandler = requestHandler
RequestEvaluator.active = self
}
public init<T>(jsonHandler: @escaping (T?) -> WebApp) {
self.init() { data in
let jsonObject = try? JSONSerialization.jsonObject(
with: data,
options: .allowFragments
)
return jsonHandler(jsonObject as? T)
}
}
public func app(
_ environ: [String: Any],
startResponse: @escaping ((String, [(String, String)]) -> Void),
sendBody: @escaping ((Data) -> Void)
) {
let input = environ["swsgi.input"] as! SWSGIInput
var isBodyReceived = false
DataReader.read(input) { data in
guard !isBodyReceived else { return }
isBodyReceived = true
let nextApp = self.requestHandler(data)
nextApp.app(environ, startResponse: startResponse, sendBody: sendBody)
self.semaphore.signal()
}
}
public func wait(timeout: TimeInterval = 10) -> Bool {
return semaphore.wait(timeout: .now() + timeout) == .success
}
} Usage example
mockServer.router["/addTask"] = RequestEvaluator(jsonHandler: { (requestDict: NSDictionary!) in
XCTAssert(requestDict["name"] as! String == "test")
return JSONResponse() { _ in return [:] }
})
app.tap() // Interact with the app and trigger request.
XCTAssert(RequestEvaluator.active!.wait(timeout: 2))
Problem with
|
I'm experiencing the same issue as @tranquvis. A minified version of my handler is this: JSONResponse { env, respond in
let input = env["swsgi.input"] as! SWSGIInput
JSONReader.read(input) { data in
// do something with data
respond(someValueDepeingOnData)
}
} The block passed to I can also confirm that moving the |
@tranquvis @ahti thanks for the feedback. For waiting async operation in the testing, we usually don't use ref: https://nshipster.com/xctestcase/ So in our real-life UI test case, we will do things like this let postEntryExp = expectation(description: "entry posted")
router[DefaultRouter.postEntriesPath] = DelayResponse(JSONResponse(handler: ({
environ, sendJSON in
XCTAssertEqual(environ["REQUEST_METHOD"] as? String, "POST")
XCTAssertEqual(environ["HTTP_AUTHORIZATION"] as? String, "Bearer MOCK_TOKEN")
JSONReader.read(environ["swsgi.input"] as! SWSGIInput) { json in
let json = json as? [String: Any]
let data = json?["data"] as? [String: Any]
let attrs = data?["attributes"] as? [String: Any]
XCTAssertEqual(attrs?["full-name"] as? String, fullName)
sendJSON(["data": ["attributes": ["approval-status": ["status": "blocked"]]]])
postEntryExp.fulfill()
}
})))
waitForExpectations(timeout: 15, handler: nil) As this is pretty much the recommended way of doing async stuff with iOS UI test, I think it's better to keep it the same way. And for async, it's actually pretty common in our use case, we may like to delay, make it timeout, or many other thing we could do with async operations. But however, yes, the API design is a little bit confusing. Maybe I shouldn't make response class to provide both async and sync operation in the same time. I think it's better to make them two different classes with obvious name what they are doing instead. I saw many issues opened not because of bug in the code, it's more misunderstanding about the async nature of Embassy and sometime mix with sync stuff. So I will take this into account, we may change the API in the future in the newer version to provide a more clear class design which is easier to understand. Also maybe trying to make common use case less confusing and error-prone. |
For those that are facing the issue with the input callback being called twice. We are facing the same issue - however we fixed it by wrapping our ie....
becomes.....
|
I run a http server for my ui test. In order to check the app's request, I defined a route and tried to read the request body with
DataReader
as described.Everything works fine except reading the body. Most time the body is empty.
The following should demonstrate the problem:
Most time the read text is empty and I get the following output:
Sometimes the text is correct and I get the following output:
As you can see, the data is read after the response was returned here.
Please help me to solve this problem. Without this problem the library would be great.
I think it's also interesting that it works this way, without a router:
The text was updated successfully, but these errors were encountered: