我仍然是新的异步/线程。我想在后台启动一个小型HTTP服务器,并收听响应。到目前为止,这是我想到的:
spinner_newline("Opening your browser...".to_owned());
let x = open::that(build_github_oauth_url());
match x {
Ok(_) => {
// This should be a new thread
setup_oauth_callback_server(|code| {
spinner_newline("Getting your GitHub information...".to_owned());
process_signup_github(code).await; // This should be an async calll
});
}
Err(e) => {
return Err(anyhow!("Failed to open browser: {e}"));
}
}
/*
try_join!(x, async {
setup_oauth_callback_server(&|code| {
spinner_newline("Getting your GitHub information...".to_owned());
process_signup_github(code)
})
});
*/
这段代码无法编译。我想执行process_signup_github时,我从闭包获得的代码。指针吗?我用tokio
这是服务器的设置,对Rust食谱做了一点修改:
fn setup_oauth_callback_server(closure: &impl Fn(String)) {
let listener = TcpListener::bind("127.0.0.1:8808").unwrap();
for stream in listener.incoming() {
let stream = stream.unwrap();
handle_connection(stream, closure);
}
}
fn handle_connection(mut stream: TcpStream, closure: impl Fn(String)) {
let buf_reader = BufReader::new(&stream);
let request_line = &buf_reader.lines().next().unwrap().unwrap();
debug!("Request: {:#?}", request_line);
let code_regex = Regex::new(r"code=([^&]*)").unwrap();
let (status_line, filename) = match code_regex.captures(&request_line) {
Some(group) => {
let code = group.get(1).unwrap().as_str();
debug!("Code: {:#?}", code);
spinner_newline("Authorization complete...".to_owned());
closure(code.to_owned());
("HTTP/1.1 200 OK", "./src/account/hello.html")
}
None => {
debug!("Code not found.");
("HTTP/1.1 404 NOT FOUND", "./src/account/404.html")
}
};
let contents = fs::read_to_string(filename).unwrap();
let length = contents.len();
let response = format!("{status_line}rnContent-Length: {length}rnrn{contents}");
stream.write_all(response.as_bytes()).unwrap();
stream.flush().unwrap();
}
我最终使用了一个通道来解决这个问题。这对我来说更直接。下面是代码的样子:
pub async fn signup_with_github() -> Result<CommandResult> {
// Spin up a simple localhost server to listen for the GitHub OAuth callback
// setup_oauth_callback_server();
// Open the GitHub OAuth URL in the user's browser
spinner_newline("Getting your GitHub information...".to_owned());
let rx = match open::that(build_github_oauth_url()) {
Ok(_) => {
let (tx, rx): (Sender<String>, Receiver<String>) = mpsc::channel();
debug!(
"Setting up OAuth callback server... (tx: {:#?}, rx: {:#?})",
&tx, &rx
);
tokio::spawn(async move {
setup_oauth_callback_server(tx);
});
rx
}
Err(_) => {
let error = anyhow!("Failed to open a browser.");
return Err(error);
}
};
debug!("Waiting for code from channel...");
match rx.recv() {
Ok(code) => {
debug!("Got code from channel: {:#?}", &code);
process_signup_github(code).await
}
Err(e) => {
let error = anyhow!("Failed to get code from channel: {e}");
return Err(error);
}
}
}
fn setup_oauth_callback_server(tx: Sender<String>) {
let listener = TcpListener::bind("127.0.0.1:8808").unwrap();
for stream in listener.incoming() {
let stream = stream.unwrap();
handle_connection(stream, tx.clone());
}
}
fn handle_connection(mut stream: TcpStream, tx: Sender<String>) {
let buf_reader = BufReader::new(&stream);
let request_line = &buf_reader.lines().next().unwrap().unwrap();
debug!("Request: {:#?}", request_line);
let code_regex = Regex::new(r"code=([^&]*)").unwrap();
let (status_line, filename) = match code_regex.captures(&request_line) {
Some(group) => {
let code = group.get(1).unwrap().as_str();
debug!("Code: {:#?}", code);
debug!("Sending code to channel...");
debug!("Channel: {:#?}", &tx);
match tx.send(code.to_string()) {
Ok(_) => {
debug!("Code sent to channel.");
}
Err(e) => {
debug!("Failed to send code to channel: {e}", e);
}
}
("HTTP/1.1 200 OK", "./src/account/hello.html")
}
None => {
debug!("Code not found.");
("HTTP/1.1 404 NOT FOUND", "./src/account/404.html")
}
};
let contents = fs::read_to_string(filename).unwrap();
let length = contents.len();
let response = format!("{status_line}rnContent-Length: {length}rnrn{contents}");
stream.write_all(response.as_bytes()).unwrap();
stream.flush().unwrap();
}