I am trying to make the example in the documentation work, but I can't.
use http::{Request, Response};
let mut request = Request::builder();
request.uri("https://www.rust-lang.org/")
.header("User-Agent", "my-awesome-agent/1.0");
if needs_awesome_header() {
request.header("Awesome", "yes");
}
let response = send(request.body(()).unwrap());
fn send(req: Request<()>) -> Response<()> {
// ...
}
The question is, how can I print the response in a string to save it? It seems it is not in the response.
Related
When I run the following code:
use exitfailure::ExitFailure;
use reqwest::Url;
use serde_derive::{Deserialize, Serialize};
use std::env;
#[derive(Serialize, Deserialize, Debug)]
struct CompanyInfo {
country: String,
currency: String,
exchange: String,
ipo: String,
marketCapitalization: u128,
name: String,
phone: String,
shareOutstanding: f64,
ticker: String,
weburl: String,
logo: String,
finnhubIndustry: String,
}
impl CompanyInfo {
async fn get(symbol: &String, api_key: &String) -> Result<Self, ExitFailure> {
let url = format!(
"https://finnhub.io/api/v1/stock/profile2?symbol={}&token={}",
symbol, api_key
);
let url = Url::parse(&*url)?;
let res = reqwest::get(url).await?.json::<CompanyInfo>().await?;
Ok(res)
}
}
#[tokio::main]
async fn main() -> Result<(), ExitFailure> {
let api_key = "MY API KEY".to_string();
let args: Vec<String> = env::args().collect();
let mut symbol: String = "AAPL".to_string();
if args.len() < 2 {
println!("Since you didn't specify a company symbol, it has defaulted to AAPL.");
} else {
symbol = args[1].clone();
}
let res = CompanyInfo::get(&symbol, &api_key).await;
println!("{:?}", res);
Ok(())
}
I get an error: Err(error decoding response body: expected ',' or '}' at line 1 column 235). For another API, this code with a similar structure worked. How do you solve this issue with reqwest?
Typically, the error decoding response body means that you tried to deserialize an HTTP response that is in a given format, but the response body wasn't valid for that format. In your case, you are trying to deserialize JSON, so the error means that the thing you are trying to deserialize probably isn't valid JSON, or perhaps it is valid JSON but your expected JSON structure doesn't match the structure returned from the server. The server might have goofed in creating it's JSON, or perhaps it is returning a response body that isn't actually JSON for a specific reason (for example, some APIs will not return JSON if they are returning a 500 response).
In order to debug and fix this, you need to know exactly what the response body looks like that you are attempting to parse. One way to do this is to split the parsing of the code into two parts: one that gets the text, and another that tries to parse. For example, for debugging purposes you can print out the response that you have received by doing something like the following:
// Split up the JSON decoding into two steps.
// 1.) Get the text of the body.
let response_body = reqwest::get(url).await?.text().await?;
println!("Response Body: {}", response_body);
// 2.) Parse the results as JSON.
let res: CompanyInfo = serde_json::from_str(&response_body)?;
This code should will probably fail just as before, but now you'll have the response body that failed printed out. At that point, you'll have to analyze the response body, at which point it will hopefully become obvious why it doesn't work.
I am making some http requests in kotlin with fuel library. I want to test that code using mockk library. I figured out how to mock http requests. Below is the code for that.
val client = mockk<Client>()
every { client.executeRequest(any()).statusCode } returns 200
every { client.executeRequest(any()).responseMessage } returns "test"
every { client.executeRequest(any()).data } returns "abc".toByteArray()
FuelManager.instance.client = client
assertEquals("abc" , testHttpRequest())
I do not like that any() here. I want to be specific about the http method and the url. I would like to return specific responses based on the url being called and the http method being used.
I figured may be I could do following
val req = Request(Method.POST, "my/test", URL("https://testRequest.com"), timeoutInMillisecond = 3000, timeoutReadInMillisecond = 3000)
every { client.executeRequest(req).statusCode } returns 200
every { client.executeRequest(req).responseMessage } returns "OK"
every { client.executeRequest(req).data } returns "abc".toByteArray()
FuelManager.instance.client = client
But I am getting following error.
io.mockk.MockKException: no answer found for: Client(#1).executeRequest(-->
https://testRequest.com/my/test
"Body : abc"
"Headers : (3)"
Accept-Encoding : compress;q=0.5, gzip;q=1.0
Content-Type : application/json
Authorization : Basic xxx)
What am I missing here?
To all those people that ended up here trying to find a solution to this, I've found something that solves the problem for my use case (but there are likely many use cases it's not appropriate for and I accept that it may not be the nicest...).
Provided you always have the calls to different endpoints in the same order every time you can do -
every { client.executeRequest(any()).data} returnsMany listOf(responseBody1, responseBody2, ... , responseBodyN)
Which will return the next response body for every subsequent call to the Fuel client.
The full code would look like (using OP's example) -
val response1 = "This is response one"
val response2 = "This is response two"
val client = mockk<Client>()
every { client.executeRequest(any()).statusCode } returns 200
every { client.executeRequest(any()).responseMessage } returns "test"
every { client.executeRequest(any()).data } returnsMany listOf(response1.toByteArray(), response2.toByteArray())
FuelManager.instance.client = client
assertEquals("This is response one", testHttpRequest())
assertEquals("This is response two", testHttpRequest())
I suspect the correct way to do this is with a 'CustomMatcher' extension function on the MockKMatcherScope as detailed here. I could only get the mock to response with the last item that'd been mocked when doing that, rather than the correct item but YMMV...
Try to use following:
every { client.executeRequest(req) } returns <mock object>
You could try these lines. Regarding how to intercept fuel request.
fun interceptFuel(method: Method, url: String) {
val interceptor = { next: (Request) -> Request ->
{ req: Request ->
if (req.method == method && req.url.toString() == url) {
val client = mockk<Client>()
/*mock fuel into whatever you like*/
FuelManager.instance.client = client
}
next(req)
}
}
FuelManager.instance.addRequestInterceptor(interceptor)
}
then, use it like this
interceptFuel(Method.GET, "https://google.com")
BTW, this code not fully tested. Use at your own risk
I am working on an echo server which takes data from TCP and applies some logic to that data. For example, if the client data comes in as hello I want to respond it ashello from server.
I am able to forward the input data using the copy function, but this is not useful in my case.
Here is the starting code that I am working on:
extern crate futures;
extern crate tokio_core;
extern crate tokio_io;
use futures::stream::Stream;
use futures::Future;
use std::net::SocketAddr;
use tokio_core::net::TcpListener;
use tokio_core::reactor::Core;
use tokio_io::io::copy;
use tokio_io::AsyncRead;
fn main() {
let addr = "127.0.0.1:15000".parse::<SocketAddr>().unwrap();
let mut core = Core::new().unwrap();
let handle = core.handle();
let socket = TcpListener::bind(&addr, &handle).unwrap();
println!("Listening on: {}", addr);
let done = socket.incoming().for_each(move |(socket, addr)| {
let (reader, writer) = socket.split();
let amt = copy(reader, writer);
let msg = amt.then(move |result| {
match result {
Ok((amt, _, _)) => println!("wrote {} bytes to {}", amt, addr),
Err(e) => println!("error on {}: {}", addr, e),
}
Ok(())
});
handle.spawn(msg);
Ok(())
});
core.run(done).unwrap();
}
I know that I need to add some logic instead of this copy function but how?
let amt = copy(reader, writer);
An echo server is kind of special in a sense, that exactly one "request" from a client is followed by exactly one response from the server. A very nice example for such a use-case is tokio's TinyDB example.
One thing that should be considered, however, is that while UDP is based on packets, that hit the other side in the exact form that you sent them with, TCP is not. TCP is a stream protocol - it has strong guarantees relating that a packet was received by the other side and that the data sent is received in exactly the order it was sent in. However, what is not guaranteed is, that one call to "send" on the one side leads to exactly one "receive" call on the other side, returning the exact same chunk of data that was sent. This is especially of interest when sending very long chunks of data, where one send maps to multiple receives. Thus you should settle for a delimiter that the server can wait for before trying to send a response to the client. In Telnet, that delimiter would be "\r\n".
That is where tokio's Decoder/Encoder infrastructure comes to play. An example implementation of such a codec is LinesCodec. If you want to have
Telnet, this does exactly what you want. It will give you exactly one message at a time and allow you to send exactly one such message at a time as response:
extern crate tokio;
use tokio::codec::Decoder;
use tokio::net::TcpListener;
use tokio::prelude::*;
use tokio::codec::LinesCodec;
use std::net::SocketAddr;
fn main() {
let addr = "127.0.0.1:15000".parse::<SocketAddr>().unwrap();
let socket = TcpListener::bind(&addr).unwrap();
println!("Listening on: {}", addr);
let done = socket.incoming()
.map_err(|e| println!("failed to accept socket; error = {:?}", e))
.for_each(move |socket| {
// Fit the line-based codec on top of the socket. This will take on the task of
// parsing incomming messages, as well as formatting outgoing ones (appending \r\n).
let (lines_tx, lines_rx) = LinesCodec::new().framed(socket).split();
// This takes every incomming message and allows to create one outgoing message for it,
// essentially generating a stream of responses.
let responses = lines_rx.map(|incomming_message| {
// Implement whatever transform rules here
if incomming_message == "hello" {
return String::from("hello from server");
}
return incomming_message;
});
// At this point `responses` is a stream of `Response` types which we
// now want to write back out to the client. To do that we use
// `Stream::fold` to perform a loop here, serializing each response and
// then writing it out to the client.
let writes = responses.fold(lines_tx, |writer, response| {
//Return the future that handles to send the response to the socket
writer.send(response)
});
// Run this request/response loop until the client closes the connection
// Then return Ok(()), ignoring all eventual errors.
tokio::spawn(
writes.then(move |_| Ok(()))
);
return Ok(());
});
tokio::run(done);
}
I am trying to get the depth data associated with an image in the PhotoLibrary.
I can get the image, and the URL, but I can't seem to get the aux data associated with it. The call to CGImageSourceCreateWithURL returns a source, but the call to CGImageSourceCopyAuxiliaryDataInfoAtIndex returns nil for both kCGImageAuxiliaryDataTypeDisparity and kCGImageAuxiliaryDataTypeDepth.
Is there something I am missing here?
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
let image = info[UIImagePickerControllerOriginalImage]
let url = info[UIImagePickerControllerImageURL]
print("url=",url)
guard let source = CGImageSourceCreateWithURL(url as! CFURL, nil) else {
return
}
guard let auxDataInfo = CGImageSourceCopyAuxiliaryDataInfoAtIndex(source, 0, kCGImageAuxiliaryDataTypeDisparity) as? [AnyHashable : Any] else {
return
}
}
I struggled with this for a whole day! I finally figured it out, though, after watching the first half of the WWDC Video titled "Editing Images With Depth."
My problem was using a URL for the image that was not from the PHAsset.
Here is the link:
LINK TO WWDC VIDEO
If you don't feel like watching it, check out this function that I wrote that does pretty much exactly what is done in the video.
You have to provide the function the [info] that is returned from the DID_FINISH_PICKING_IMAGE_WITH_INFO function from the UIImagePickerDelegate.
Before using this function - note that is actually doesn't work! It is great to look at though, because it shows the steps clearly. But due to asynchronous behavior, the function will always return nil before it has a chance to set the local depth variable to the AVDepthData.
My solution was to break the function apart and use Grand Central Dispatch to create a Dispatch Group, enter it, retrieve the imageURL from the PHAsset, and then leave the Dispatch Group. Upon leaving the Dispatch Group, the DispatchGroup.NOTIFIED function then proceeded with the rest of the process.
I hope this helps!!!
func returndepthdata(usingimageinfo: [UIImagePickerController.InfoKey : Any]) -> AVDepthData? {
var depthdata: AVDepthData! = nil
if let photoasset = usingimageinfo[.phAsset] as? PHAsset {
let input = photoasset.requestContentEditingInput(with: nil, completionHandler: { (input, info) in
if let imageurl = input?.fullSizeImageURL {
if let source = CGImageSourceCreateWithURL(imageurl as CFURL, nil) {
if let imageproperties = CGImageSourceCopyProperties(source, nil) {
if let disparityinfo = CGImageSourceCopyAuxiliaryDataInfoAtIndex(source, 0, kCGImageAuxiliaryDataTypeDisparity) {
if let truedepthdata = try? AVDepthData(fromDictionaryRepresentation: disparityinfo as! [AnyHashable : Any]) {
depthdata = truedepthdata
}
}
}
}
}
})
}
return depthdata
}
The image URL supplied by UIImagePickerController does not include any of the metadata associated with depth. To get this information, you must access the PHAsset using the PhotoBook API.
First, import the API:
import Photos
Before you display your image picker, request user access to the photo book. You'll need to add an info dictionary key for Photo Library Usage for this to work:
switch PHPhotoLibrary.authorizationStatus() {
case .notDetermined:
PHPhotoLibrary.requestAuthorization { (status) in
if status == .authorized {
DispatchQueue.main.async {
// Display image picker here
}
}
}
case .authorized: // Display image picker here
case .denied, .restricted: // Display appropriate error here
}
Now, in your image picker delegate you can do this:
if let asset = info[.phAsset] as? PHAsset {
PHImageManager.default().requestImageData(for: asset, options: nil) { (imageData, dataType, orientation, info) in
let url = info?["PHImageFileURLKey"] as? URL
// Pass this URL to your existing code.
}
}
Note that the file may contain depth or disparity information. You can convert between these easily enough, but you may need to check which one you have using CGImageSourceCopyProperties(). Also look out for the new supplementary depth data, kCGImageAuxiliaryDataTypePortraitEffectsMatte, which gives a much higher resolution mask for just the subject person in portrait images and is great for doing greenscreen-style effects.
I am writing a function that makes a GET request to a website and returns the response cookie:
extern crate futures;
extern crate hyper;
extern crate tokio_core;
use tokio_core::reactor::Core;
use hyper::Client;
use std::error::Error;
use hyper::header::Cookie;
use futures::future::Future;
fn get_new_cookie() -> Result<String, Box<Error>> {
println!("Getting cookie...");
let core = Core::new()?;
let client = Client::new(&core.handle());
println!("Created client");
let uri = "http://www.cnn.com".parse().expect("Cannot parse url");
println!("Parsed url");
let response = client.get(uri).wait().expect("Cannot get url.");
println!("Got response");
let cookie = response
.headers()
.get::<Cookie>()
.expect("Cannot get cookie");
println!("Cookie: {}", cookie);
Ok(cookie)
}
fn main() {
println!("{:?}", get_new_cookie());
}
This doesn't work; it is stuck on the client.get(...) string. The output I'm getting is:
Getting cookie...
Created client
Parsed url
and after that nothing happens.
What am I doing wrong and how I can change it so it'd work?
As Stefan points out, by calling wait, you are putting the thread to sleep until the future has completed. However, that thread needs to run the event loop, so you've just caused a deadlock. Using Core::run is more correct.
As Francis Gagné points out, the "Cookie" header is used to send a cookie to the server. SetCookie is used to send a cookie to the client. It also returns a vector of all the cookies together:
fn get_new_cookie() -> Result<String, Box<Error>> {
println!("Getting cookie...");
let mut core = Core::new()?;
let client = Client::new(&core.handle());
println!("Created client");
let uri = "http://www.cnn.com".parse().expect("Cannot parse url");
println!("Parsed url");
let response = core.run(client.get(uri)).expect("Cannot get url.");
println!("Got response");
let cookie = response
.headers()
.get::<SetCookie>()
.expect("Cannot get cookie");
println!("Cookie: {:?}", cookie);
Ok(cookie.join(","))
}
However, if you only want a synchronous API, use Reqwest instead. It is built on top of Hyper:
extern crate reqwest;
use std::error::Error;
use reqwest::header::SetCookie;
fn get_new_cookie() -> Result<String, Box<Error>> {
let response = reqwest::get("http://www.cnn.com")?;
let cookies = match response.headers().get::<SetCookie>() {
Some(cookies) => cookies.join(","),
None => String::new(),
};
Ok(cookies)
}
fn main() {
println!("{:?}", get_new_cookie());
}
See the documentation for the wait method:
Note: This method is not appropriate to call on event loops or similar
I/O situations because it will prevent the event loop from making
progress (this blocks the thread). This method should only be called
when it's guaranteed that the blocking work associated with this
future will be completed by another thread.
Future::wait is already deprecated in the tokio-reform branch.
I'd recommend to design the full application to deal with async concepts (i.e. get_new_cookie should take a Handle and return a Future, not allocating its own event loop).
You could run the request with Core::run like this:
let response = core.run(client.get(uri)).expect("Cannot get url.");
reqwest 0.11 (and perhaps earlier) update
In the get_new_cookie function, I believe the code snippet to retrieve the cookies from a reqwest::Response goes something like:
// returns Option<&HeaderValue>
response.headers().get(http::header::SET_COOKIE)