This repository has been archived by the owner on Aug 3, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 337
refactor and document wrangler dev #1220
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
Empty file.
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
mod headers; | ||
mod server; | ||
mod setup; | ||
mod watch; | ||
|
||
use server::serve; | ||
use setup::{get_preview_id, get_session_id}; | ||
use watch::watch_for_changes; | ||
|
||
use crate::commands::dev::{socket, ServerConfig}; | ||
use crate::settings::global_user::GlobalUser; | ||
use crate::settings::toml::Target; | ||
|
||
use std::sync::{Arc, Mutex}; | ||
use std::thread; | ||
|
||
use tokio::runtime::Runtime as TokioRuntime; | ||
|
||
/// spin up a local server that routes requests to the preview service | ||
/// that has a Cloudflare Workers runtime without access to zone-specific features | ||
pub fn dev( | ||
target: Target, | ||
user: Option<GlobalUser>, | ||
server_config: ServerConfig, | ||
verbose: bool, | ||
) -> Result<(), failure::Error> { | ||
// setup the session | ||
let session_id = get_session_id()?; | ||
|
||
// upload the initial script | ||
let preview_id = get_preview_id( | ||
target.clone(), | ||
user.clone(), | ||
&server_config, | ||
&session_id, | ||
verbose, | ||
)?; | ||
|
||
// the local server needs the preview ID to properly route | ||
// HTTP requests | ||
// | ||
// the file watcher updates the preview ID when there is a new | ||
// Worker | ||
// | ||
// Since these run on separate threads, we must stuff the | ||
// preview ID into an Arc<Mutex so that the server waits on the | ||
// file watcher to release the lock before routing a request | ||
let preview_id = Arc::new(Mutex::new(preview_id)); | ||
// a new scope is created to satisfy the borrow checker | ||
{ | ||
// we must clone each of these variables in order to | ||
// safely use them in another thread | ||
let session_id = session_id.clone(); | ||
let preview_id = preview_id.clone(); | ||
let server_config = server_config.clone(); | ||
thread::spawn(move || { | ||
watch_for_changes( | ||
target, | ||
user, | ||
&server_config, | ||
Arc::clone(&preview_id), | ||
&session_id, | ||
verbose, | ||
) | ||
}); | ||
} | ||
|
||
// in order to spawn futures we must create a tokio runtime | ||
let mut runtime = TokioRuntime::new()?; | ||
|
||
// and we must block the main thread on the completion of | ||
// said futures | ||
runtime.block_on(async { | ||
let devtools_listener = tokio::spawn(socket::listen(session_id.to_string())); | ||
let server = tokio::spawn(serve(server_config, Arc::clone(&preview_id))); | ||
let res = tokio::try_join!(async { devtools_listener.await? }, async { server.await? }); | ||
|
||
match res { | ||
Ok(_) => Ok(()), | ||
Err(e) => Err(e), | ||
} | ||
}) | ||
EverlastingBugstopper marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
use crate::commands::dev::gcs::headers::{destructure_response, structure_request}; | ||
use crate::commands::dev::ServerConfig; | ||
use crate::terminal::emoji; | ||
|
||
use std::sync::{Arc, Mutex}; | ||
|
||
use chrono::prelude::*; | ||
|
||
use hyper::client::{HttpConnector, ResponseFuture}; | ||
use hyper::header::{HeaderName, HeaderValue}; | ||
use hyper::http::uri::InvalidUri; | ||
use hyper::service::{make_service_fn, service_fn}; | ||
use hyper::{Body, Client as HyperClient, Request, Response, Server, Uri}; | ||
|
||
use hyper_tls::HttpsConnector; | ||
|
||
const PREVIEW_HOST: &str = "rawhttp.cloudflareworkers.com"; | ||
|
||
/// performs all logic that takes an incoming request | ||
/// and routes it to the Workers runtime preview service | ||
pub(super) async fn serve( | ||
server_config: ServerConfig, | ||
preview_id: Arc<Mutex<String>>, | ||
) -> Result<(), failure::Error> { | ||
// set up https client to connect to the preview service | ||
let https = HttpsConnector::new(); | ||
let client = HyperClient::builder().build::<_, Body>(https); | ||
|
||
let listening_address = server_config.listening_address.clone(); | ||
|
||
// create a closure that hyper will use later to handle HTTP requests | ||
EverlastingBugstopper marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// this takes care of sending an incoming request along to | ||
// the uploaded Worker script and returning its response | ||
let make_service = make_service_fn(move |_| { | ||
let client = client.to_owned(); | ||
let server_config = server_config.to_owned(); | ||
let preview_id = preview_id.to_owned(); | ||
async move { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. mebbe not here, but i'd suggest pulling these nested anonymous functions into named functions for clarity. not 100% necessary tho. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I actually spent like an hour trying to figure out how to do this but couldn't because of the |
||
Ok::<_, failure::Error>(service_fn(move |req| { | ||
let client = client.to_owned(); | ||
let server_config = server_config.to_owned(); | ||
let preview_id = preview_id.lock().unwrap().to_owned(); | ||
let version = req.version(); | ||
|
||
// record the time of the request | ||
let now: DateTime<Local> = Local::now(); | ||
|
||
// split the request into parts so we can read | ||
// what it contains and display in logs | ||
let (parts, body) = req.into_parts(); | ||
|
||
let req_method = parts.method.to_string(); | ||
|
||
// parse the path so we can send it to the preview service | ||
// we don't want to send "localhost:8787/path", just "/path" | ||
let path = get_path_as_str(&parts.uri); | ||
|
||
async move { | ||
// send the request to the preview service | ||
let resp = preview_request( | ||
Request::from_parts(parts, body), | ||
client, | ||
preview_id.to_owned(), | ||
) | ||
.await?; | ||
let (mut parts, body) = resp.into_parts(); | ||
|
||
// format the response for the user | ||
destructure_response(&mut parts)?; | ||
let resp = Response::from_parts(parts, body); | ||
|
||
// print information about the response | ||
// [2020-04-20 15:25:54] GET example.com/ HTTP/1.1 200 OK | ||
println!( | ||
"[{}] {} {}{} {:?} {}", | ||
now.format("%Y-%m-%d %H:%M:%S"), | ||
req_method, | ||
server_config.host, | ||
path, | ||
version, | ||
resp.status() | ||
); | ||
Ok::<_, failure::Error>(resp) | ||
} | ||
})) | ||
} | ||
}); | ||
|
||
let server = Server::bind(&listening_address.address).serve(make_service); | ||
println!("{} Listening on http://{}", emoji::EAR, listening_address); | ||
if let Err(e) = server.await { | ||
eprintln!("server error: {}", e); | ||
} | ||
Ok(()) | ||
} | ||
|
||
fn preview_request( | ||
req: Request<Body>, | ||
client: HyperClient<HttpsConnector<HttpConnector>>, | ||
preview_id: String, | ||
) -> ResponseFuture { | ||
let (mut parts, body) = req.into_parts(); | ||
|
||
let path = get_path_as_str(&parts.uri); | ||
let preview_id = &preview_id; | ||
|
||
structure_request(&mut parts); | ||
|
||
parts.headers.insert( | ||
HeaderName::from_static("host"), | ||
HeaderValue::from_static(PREVIEW_HOST), | ||
); | ||
|
||
parts.headers.insert( | ||
HeaderName::from_static("cf-ew-preview"), | ||
HeaderValue::from_str(preview_id).expect("Could not create header for preview id"), | ||
); | ||
|
||
parts.uri = get_preview_url(&path).expect("Could not get preview url"); | ||
|
||
let req = Request::from_parts(parts, body); | ||
|
||
client.request(req) | ||
} | ||
|
||
fn get_preview_url(path_string: &str) -> Result<Uri, InvalidUri> { | ||
format!("https://{}{}", PREVIEW_HOST, path_string).parse() | ||
} | ||
|
||
fn get_path_as_str(uri: &Uri) -> String { | ||
uri.path_and_query() | ||
.map(|x| x.as_str()) | ||
.unwrap_or("") | ||
.to_string() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
use crate::commands::dev::ServerConfig; | ||
use crate::commands::preview::upload; | ||
use crate::settings::global_user::GlobalUser; | ||
use crate::settings::toml::Target; | ||
|
||
use uuid::Uuid; | ||
|
||
/// generate a unique uuid that lasts the entirety of the | ||
/// `wrangler dev` session | ||
pub(super) fn get_session_id() -> Result<String, failure::Error> { | ||
Ok(Uuid::new_v4().to_simple().to_string()) | ||
} | ||
|
||
/// upload the script to the Workers API, and combine its response | ||
/// with the session id to get the preview ID | ||
/// | ||
/// this is used when sending requests to the Workers Runtime | ||
/// so it executes the correct Worker | ||
EverlastingBugstopper marked this conversation as resolved.
Show resolved
Hide resolved
|
||
pub fn get_preview_id( | ||
mut target: Target, | ||
user: Option<GlobalUser>, | ||
server_config: &ServerConfig, | ||
session_id: &str, | ||
verbose: bool, | ||
) -> Result<String, failure::Error> { | ||
// setting sites_preview to `true` would print a message to the terminal | ||
// directing the user to open the browser to view the output | ||
// this message makes sense for `wrangler preview` but not `wrangler dev` | ||
let sites_preview = false; | ||
let script_id = upload(&mut target, user.as_ref(), sites_preview, verbose).map_err(|_| failure::format_err!("Could not upload your script. Check your internet connection or https://www.cloudflarestatus.com/ for rare incidents impacting the Cloudflare Workers API."))?; | ||
EverlastingBugstopper marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Ok(format!( | ||
"{}{}{}{}", | ||
&script_id, | ||
session_id, | ||
server_config.host.is_https() as u8, | ||
server_config.host | ||
)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
v. nice