first request wip, headers crate

This commit is contained in:
Brandon Thomas committed 2025-12-16 22:11:44 -05:00
1 parent 0bcfb774c0
commit 53cd4618eb
34 files changed
+26885 -6

No files matched your search

Generated
+9
View File
@@ -5151,6 +5151,14 @@ dependencies = [
"tokio-util",
]
[[package]]
name = "http_headers"
version = "0.0.1"
dependencies = [
"errors",
"workspace-build-acceleration",
]
[[package]]
name = "http_server_common"
version = "0.0.1"
@@ -14182,6 +14190,7 @@ dependencies = [
"errors",
"filesys",
"futures 0.3.31",
"http_headers",
"log",
"log4rs",
"once_cell",
+2
View File
@@ -68,6 +68,7 @@ members = [
"crates/lib/files/media",
"crates/lib/files/mimetypes",
"crates/lib/files/videos",
"crates/lib/http_headers",
"crates/lib/idempotency",
"crates/lib/image_algorithms",
"crates/lib/jobs_common",
@@ -200,6 +201,7 @@ fal_client = { path = "crates/api_clients/fal_client" }
file_to_base64_url = { path = "crates/lib/files/file_to_base64_url" }
filesys = { path = "crates/lib/files/filesys" }
grok_client = { path = "crates/api_clients/grok_client" }
http_headers = { path = "crates/lib/http_headers" }
idempotency = { path = "crates/lib/idempotency" }
images = { path = "crates/lib/files/images" }
jwt_light = { path = "crates/lib/jwt_light" }
@@ -20,6 +20,7 @@ cloudflare_errors.workspace = true
cloudflare_mitigation.workspace = true
cookie_store.workspace = true
errors.workspace = true
http_headers.workspace = true
# External
anyhow.workspace = true
@@ -1 +1,2 @@
pub mod world_labs_bearer_token;
pub mod world_labs_cookies;
@@ -0,0 +1,35 @@
#[derive(Clone)]
pub struct WorldLabsBearerToken {
/// The bearer token
/// Without "Authorization:" header name and without "Bearer(space)" prefix.
bearer_token: String,
}
impl WorldLabsBearerToken {
pub fn new(mut bearer_token: String) -> Self {
if bearer_token.starts_with("Bearer") {
bearer_token = bearer_token
.trim_start_matches("Bearer")
.trim()
.to_string();
}
Self { bearer_token }
}
pub fn as_raw_str(&self) -> &str {
&self.bearer_token
}
pub fn as_raw_bytes(&self) -> &[u8] {
self.bearer_token.as_bytes()
}
pub fn to_raw_string(&self) -> String {
self.bearer_token.clone()
}
pub fn to_bearer_token_string(&self) -> String {
format!("Bearer {}", self.bearer_token)
}
}
@@ -0,0 +1 @@
{"error":{"code":7,"message":"Request rejected by anti-bot rules.","details":[]}}
@@ -0,0 +1,37 @@
use crate::error::world_labs_error::WorldLabsError;
use crate::error::world_labs_generic_api_error::WorldLabsGenericApiError;
use crate::error::world_labs_specific_api_error::WorldLabsSpecificApiError;
use cloudflare_errors::filter_cloudflare_errors::filter_cloudflare_errors;
use wreq::StatusCode;
/// We know the response is an error at this point.
/// We're just turning it into the right error.
pub fn categorize_grok_http_error(status_code: StatusCode, maybe_body: Option<&str>) -> WorldLabsError {
if let Some(body) = maybe_body {
if let Err(err) = filter_cloudflare_errors(status_code.as_u16(), body) {
return WorldLabsGenericApiError::CloudflareError(err).into();
}
let body = body.to_lowercase();
let anti_bot = body.contains("anti-bot") || body.contains("rejected");
if anti_bot {
return WorldLabsSpecificApiError::AutomationBlocked.into();
}
match status_code {
StatusCode::TOO_MANY_REQUESTS => {
return WorldLabsSpecificApiError::TooManyVideos.into();
}
_ => {},
}
return WorldLabsGenericApiError::UncategorizedBadResponseWithStatusAndBody {
status_code,
body: body.to_string(),
}.into();
}
WorldLabsGenericApiError::UncategorizedBadResponseWithStatus(status_code).into()
}
@@ -0,0 +1,5 @@
pub mod world_labs_client_error;
pub mod world_labs_error;
pub mod world_labs_generic_api_error;
pub mod world_labs_specific_api_error;
pub (crate) mod categorize_world_labs_http_error;
@@ -0,0 +1,154 @@
use base64::DecodeError;
use std::error::Error;
use std::fmt::{Display, Formatter};
#[derive(Debug)]
pub enum WorldLabsClientError {
/// An error was encountered in building the Wreq client
WreqClientError(wreq::Error),
/// Error serializing a message to send the websocket
WebsocketRequestSerializationError(serde_json::Error),
/// Error locking the websocket for sending/receiving
WebsocketLockError,
/// Error reading from a websocket.
WebsocketReadError(wreq::Error),
/// Error sending to an open websocket.
WebsocketSendError(wreq::Error),
/// Can't open a local file for uploading.
CannotOpenLocalFileForUpload(std::io::Error),
/// Can't read a local file for uploading.
CannotReadLocalFileForUpload(std::io::Error),
/// Couldn't write to the file
CannotOpenLocalFileForWriting(std::io::Error),
/// Couldn't write to the file
CannotWriteLocalFile(std::io::Error),
/// File for upload has an invalid path.
FileForUploadHasInvalidPath,
/// Error parsing HTML
HtmlParsingError,
/// Error decoding verification token
FailedToDecodeVerificationToken(DecodeError),
/// Verification token bytes are invalid
InvalidVerificationTokenBytes,
/// Our script logic is out of date
ScriptLogicOutOfDate,
/// Our script logic is out of date (SVG)
ScriptSvgLogicOutOfDate,
/// Our script logic is out of date (script 1)
Script1LogicOutOfDate,
/// Our script logic is out of date (script 2)
Script2LogicOutOfDate,
/// Our script logic is out of date (xsid script)
ScriptXsidLogicOutOfDate,
/// Signature algorithm isn't working with inputs
BadSignatureInputs,
/// Something is broken with timeout math
TimeoutMathBroken,
/// Can't make request because cookies aren't present
NoCookiesPresent,
/// Unknown error generating video
ErrorGeneratingVideo,
// /// An error reading the file for upload.
// FileForUploadReadError(std::io::Error),
//
// /// The file path provided for upload is invalid.
// FileForUploadHasInvalidPath,
//
// /// Something is wrong with the JWT bearer token.
// /// This error originates on our end as we try to parse the JWT.
// LocalJwtClaimsParseError(String),
//
// /// There was an error constructing the form-multipart request.
// MultipartFormError(wreq::Error),
//
// /// We haven't received a bearer token yet
// /// This is our own internal application state error, not something Sora returns.
// /// We know our client can't make the request, so we preemptively fail it.
// NoBearerTokenForRequest,
//
// /// A sentinel token is not present in the client, which is required for some requests.
// NoSentinelTokenForRequest,
//
// /// Issue with using the SoraCredentialBuilder.
// SoraCredentialBuilderError(&'static str),
//
// /// Error parsing a request URL.
// UrlParseError(url::ParseError),
//
// /// Error serializing the sentinel store token to JSON (typically for persistent storage).
// CouldNotSerializeSentinelTokenStore(serde_json::Error),
//
// /// Error deserializing the sentinel token from JSON (typically for persistent storage).
// CouldNotDeserializeSentinelTokenStore {
// error: serde_json::Error,
// raw_json: String
// },
}
impl Error for WorldLabsClientError {}
impl Display for WorldLabsClientError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Self::WreqClientError(err) => write!(f, "Wreq client error (during client creation): {}", err),
Self::WebsocketRequestSerializationError(err) => write!(f, "Websocket request serialization error: {}", err),
Self::WebsocketLockError => write!(f, "Websocket lock error"),
Self::WebsocketReadError(err) => write!(f, "Websocket read error: {}", err),
Self::WebsocketSendError(err) => write!(f, "Websocket send error: {}", err),
Self::CannotOpenLocalFileForUpload(err) => write!(f, "Cannot open local file for upload: {}", err),
Self::CannotReadLocalFileForUpload(err) => write!(f, "Cannot read local file for upload: {}", err),
Self::CannotOpenLocalFileForWriting(err) => write!(f, "Cannot open local file for writing: {}", err),
Self::CannotWriteLocalFile(err) => write!(f, "Cannot write local file: {}", err),
Self::FileForUploadHasInvalidPath => write!(f, "File for upload has invalid path"),
Self::HtmlParsingError => write!(f, "Html parsing error"),
Self::FailedToDecodeVerificationToken(err) => write!(f, "Failed to decode verification token: {}", err),
Self::InvalidVerificationTokenBytes => write!(f, "Invalid verification token bytes"),
Self::ScriptLogicOutOfDate => write!(f, "Script logic out of date"),
Self::ScriptSvgLogicOutOfDate => write!(f, "Script logic out of date (SVG parsing)"),
Self::Script1LogicOutOfDate => write!(f, "Script logic out of date (script 1)"),
Self::Script2LogicOutOfDate => write!(f, "Script logic out of date (script 2)"),
Self::ScriptXsidLogicOutOfDate => write!(f, "Script logic out of date (xsid script)"),
Self::BadSignatureInputs => write!(f, "Bad signature inputs"),
Self::TimeoutMathBroken => write!(f, "Timeout math is broken"),
Self::NoCookiesPresent => write!(f, "No cookies present"),
Self::ErrorGeneratingVideo => write!(f, "Error generating video"),
//Self::FileForUploadReadError(err) => write!(f, "Error reading file for upload: {}", err),
//Self::FileForUploadHasInvalidPath => write!(f, "The file path provided for upload is invalid."),
//Self::LocalJwtClaimsParseError(msg) => write!(f, "Local JWT claims parse error: {}", msg),
//Self::MultipartFormError(err) => write!(f, "Multipart form error: {}", err),
//Self::NoBearerTokenForRequest => write!(f, "No bearer token available. The client needs a bearer token to make the request."),
//Self::NoSentinelTokenForRequest => write!(f, "No sentinel token available. The client needs a sentinel token to make the request."),
//Self::SoraCredentialBuilderError(msg) => write!(f, "Sora Credential Builder error: {}", msg),
//Self::UrlParseError(err) => write!(f, "URL parse error: {}", err),
//Self::CouldNotSerializeSentinelToken(err) => write!(f, "Could not serialize sentinel token to JSON: {}", err),
//Self::CouldNotSerializeSentinelTokenStore(err) => write!(f, "Could not serialize sentinel token store to JSON: {}", err),
//Self::CouldNotDeserializeSentinelTokenStore { error, raw_json} => {
// write!(f, "Could not deserialize sentinel token store from JSON: {:?}, raw JSON: {}", error, raw_json)
//},
}
}
}
@@ -0,0 +1,53 @@
use crate::error::world_labs_client_error::WorldLabsClientError;
use crate::error::world_labs_generic_api_error::WorldLabsGenericApiError;
use crate::error::world_labs_specific_api_error::WorldLabsSpecificApiError;
use cloudflare_errors::cloudflare_error::CloudflareError;
use std::error::Error;
#[derive(Debug)]
pub enum WorldLabsError {
Client(WorldLabsClientError),
ApiSpecific(WorldLabsSpecificApiError),
ApiGeneric(WorldLabsGenericApiError),
}
impl WorldLabsError {
pub fn is_world_labs_having_downtime_issues(&self) -> bool {
match self {
Self::ApiGeneric(WorldLabsGenericApiError::CloudflareError(CloudflareError::BadGateway502)) => true,
Self::ApiGeneric(WorldLabsGenericApiError::CloudflareError(CloudflareError::GatewayTimeout504)) => true,
Self::ApiGeneric(WorldLabsGenericApiError::CloudflareError(CloudflareError::TimeoutOccurred524)) => true,
_ => false,
}
}
}
impl Error for WorldLabsError {}
impl std::fmt::Display for WorldLabsError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Client(e) => write!(f, "WorldLabsClientError: {:?}", e),
Self::ApiSpecific(e) => write!(f, "WorldLabsSpecificApiError: {:?}", e),
Self::ApiGeneric(e) => write!(f, "WorldLabsGenericApiError: {:?}", e),
}
}
}
impl From<WorldLabsClientError> for WorldLabsError {
fn from(error: WorldLabsClientError) -> Self {
Self::Client(error)
}
}
impl From<WorldLabsSpecificApiError> for WorldLabsError {
fn from(error: WorldLabsSpecificApiError) -> Self {
Self::ApiSpecific(error)
}
}
impl From<WorldLabsGenericApiError> for WorldLabsError {
fn from(error: WorldLabsGenericApiError) -> Self {
Self::ApiGeneric(error)
}
}
@@ -0,0 +1,82 @@
use cloudflare_errors::cloudflare_error::CloudflareError;
use std::error::Error;
use std::fmt::{Display, Formatter};
use wreq::StatusCode;
#[derive(Debug)]
pub enum WorldLabsGenericApiError {
/// Specific Cloudflare errors.
CloudflareError(CloudflareError),
/// The downloaded index.html did not include the expected data.
/// This is necessary for handshakes between client and server.
IndexHtmlDidNotIncludeExpectedData {
message: String,
},
/// The downloaded javascript did not include the expected data.
/// This is necessary for handshakes between client and server.
ScriptBodyDidNotIncludeExpectedData {
message: String,
},
/// Error creating the websocket due to 401 Unauthorized, likely an authentication error with Grok.
/// Unfortunately we can't read the response body text to diagnose more because wreq has some API design issues.
LikelyWebsocketAuthentication401,
/// We received a 403 on websocket upgrade, likely from Cloudflare.
/// Unfortunately we can't read the response body text to diagnose more because wreq has some API design issues.
LikelyWebsocketCloudflare403,
/// Unknown status code on websocket upgrade.
UnexpectedWebsocketUpgradeStatusCode(StatusCode),
/// serde_json::Error, likely from JSON deserialization schema mismatch.
/// Includes the original body.
SerdeResponseParseErrorWithBody(serde_json::Error, String),
/// serde_json::Error, likely from JSON deserialization schema mismatch.
/// Includes the original body.
/// Specifically, this is for non-200 (error) responses.
SerdeParseErrorWithBodyOnNon200(serde_json::Error, String),
/// An uncategorized bad HTTP response from Grok.
UncategorizedBadResponseWithStatus(StatusCode),
/// An uncategorized bad HTTP response from Grok.
UncategorizedBadResponseWithStatusAndBody {
status_code: StatusCode,
body: String,
},
/// Our previous upload failed
UploadFailed,
/// An error upgrading the connection to a websocket.
WreqWebsocketUpgradeError(wreq::Error),
/// An uncaught error from the API client.
WreqError(wreq::Error),
}
impl Error for WorldLabsGenericApiError {}
impl Display for WorldLabsGenericApiError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Self::CloudflareError(err) => write!(f, "Cloudflare error: {}", err),
Self::IndexHtmlDidNotIncludeExpectedData { message } => write!(f, "IndexHtmlDidNotIncludeExpectedData ( message: {} )", message),
Self::ScriptBodyDidNotIncludeExpectedData { message } => write!(f, "ScriptBodyDidNotIncludeExpectedData ( message: {} )", message),
Self::LikelyWebsocketAuthentication401 => write!(f, "Likely websocket authentication 401 error (probably Grok authentication issue with cookies)"),
Self::LikelyWebsocketCloudflare403 => write!(f, "Likely Cloudflare 403 error on websocket upgrade"),
Self::UnexpectedWebsocketUpgradeStatusCode(status) => write!(f, "Unexpected websocket upgrade status code: {}", status),
Self::SerdeResponseParseErrorWithBody(err, body) => write!(f, "Failed to parse response body: {:?}. Body: {}", err, body),
Self::SerdeParseErrorWithBodyOnNon200(err, body) => write!(f, "Failed to parse non-200 response body: {:?}. Body: {}", err, body),
Self::UncategorizedBadResponseWithStatus(status) => write!(f, "Uncategorized with status code: {}", status),
Self::UncategorizedBadResponseWithStatusAndBody { status_code, body } => write!(f, "Uncategorized bad response: status code {}, body: {}", status_code, body),
Self::UploadFailed => write!(f, "Upload failed"),
Self::WreqWebsocketUpgradeError(err) => write!(f, "Websocket upgrade error: {}", err),
Self::WreqError(err) => write!(f, "Wreq client error: {}", err),
}
}
}
@@ -0,0 +1,78 @@
use std::error::Error;
use std::fmt::{Display, Formatter};
#[derive(Debug)]
pub enum WorldLabsSpecificApiError {
/// Grok is detecting and blocking our bot, likely due to new secrets use.
AutomationBlocked,
/// Too many videos are being generated.
TooManyVideos,
// /// Failed to generate a bearer token despite the `200 OK` response.
// /// This has been generated by sending old/expired session cookies or empty cookies.
// FailedToGenerateBearerToken,
//
// /// The endpoint told us we used an invalid JWT.
// InvalidJwt,
//
// /// Sora has responded with "sentinel block".
// /// This usually means we need to refresh our sentinel token.
// SentinelBlockError,
//
// /// Sora requires users to set up an account username before they can generate images.
// /// This requires the user to set up that username.
// SoraUsernameNotYetCreated,
//
// /// Sora has responded with "token expired".
// /// This (probably?) means we need to refresh our JWT bearer token.
// TokenExpiredError,
//
// /// We're sending too many tasks to Sora.
// /// Sora is telling us to cool it.
// TooManyConcurrentTasks,
//
// // TODO: Try to make concrete error types for these distinct cases.
// /// Unauthorized, cookie and/or bearer token expired. We'll need to ask for a refreshed login.
// ///
// /// Example message, e.g. from the upload endpoint:
// /// {
// /// "error": {
// /// "message": "Your authentication token has expired. Please try signing in again.",
// /// "type": "invalid_request_error",
// /// "param": null,
// /// "code": "token_expired"
// /// }
// /// }
// UnauthorizedCookieOrBearerExpired,
//
// /// The Sora sentinel token response is missing fields
// SentinelResponseIsMissingFields {
// missing_token: bool,
// missing_turnstile: bool,
// raw_response: String,
// },
}
impl Error for WorldLabsSpecificApiError {}
impl Display for WorldLabsSpecificApiError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Self::AutomationBlocked => write!(f, "Automation blocked"),
Self::TooManyVideos => write!(f, "Too many videos are being generated"),
//Self::FailedToGenerateBearerToken => write!(f, "Failed to generate bearer token. Probably invalid or expired session cookies."),
//Self::InvalidJwt => write!(f, "Invalid JWT token. Please refresh the token."),
//Self::SentinelBlockError => write!(f, "Request blocked by Sentinel Token Block. Please refresh the sentinel token."),
//Self::SoraUsernameNotYetCreated => write!(f, "Sora username not yet created. Please create a username in the Sora app or website."),
//Self::TokenExpiredError => write!(f, "JWT token has expired. Please refresh the token."),
//Self::TooManyConcurrentTasks => write!(f, "Too many concurrent tasks. Please wait."),
//Self::UnauthorizedCookieOrBearerExpired => write!(f, "Unauthorized: cookie and/or bearer token expired"),
//Self::SentinelResponseIsMissingFields { missing_token: token_missing, missing_turnstile: turnstile_missing, raw_response } => {
// write!(f, "Sentinel response is missing fields. token_missing: {}, turnstile_missing: {}. Raw response: {}", token_missing, turnstile_missing, raw_response)
//},
}
}
}
@@ -1,6 +1,8 @@
#[cfg(test)]
pub (crate) mod test_utils;
mod client;
mod credentials;
mod requests;
pub mod client;
pub mod credentials;
pub mod error;
pub mod requests;
@@ -0,0 +1,6 @@
/// Referer header value
pub(crate) const REFERER_VALUE: &str = "https://marble.worldlabs.ai/";
/// Origin header value
pub(crate) const ORIGIN_VALUE: &str = "https://marble.worldlabs.ai/";
@@ -0,0 +1 @@
pub (crate) mod common_header_values;
@@ -0,0 +1,2 @@
pub mod objects;
pub (crate) mod common;
@@ -0,0 +1 @@
pub mod objects_create_initial;
@@ -0,0 +1,76 @@
use crate::credentials::world_labs_bearer_token::WorldLabsBearerToken;
use crate::credentials::world_labs_cookies::WorldLabsCookies;
use crate::error::world_labs_client_error::WorldLabsClientError;
use crate::error::world_labs_error::WorldLabsError;
use crate::requests::common::common_header_values::{ORIGIN_VALUE, REFERER_VALUE};
use http_headers::names::{PRIORITY, SEC_FETCH_DEST, SEC_FETCH_MODE, SEC_FETCH_SITE, SEC_GPC};
use http_headers::values::accept::ACCEPT_ALL;
use http_headers::values::cache_control::CACHE_CONTROL_NO_CACHE;
use http_headers::values::connection::CONNECTION_KEEP_ALIVE;
use http_headers::values::content_type::CONTENT_TYPE_APPLICATION_JSON;
use http_headers::values::pragma::PRAGMA_NO_CACHE;
use http_headers::values::priority::PRIORITY_HIGHEST;
use http_headers::values::sec::{SEC_FETCH_DEST_EMPTY, SEC_FETCH_MODE_CORS, SEC_FETCH_SITE_CROSS_SITE};
use http_headers::values::te::TE_TRAILERS;
use wreq::header::{ACCEPT, ACCEPT_ENCODING, ACCEPT_LANGUAGE, AUTHORIZATION, CACHE_CONTROL, CONNECTION, CONTENT_TYPE, ORIGIN, PRAGMA, REFERER, TE};
use wreq::Client;
use wreq_util::Emulation;
const URL : &str = "https://marble2-kgw-prod-iac1.wlt-ai.art/api/v1/objects";
pub struct ObjectsCreateInitialArgs<'a> {
pub cookies: &'a WorldLabsCookies,
pub bearer_token: &'a WorldLabsBearerToken,
}
pub async fn objects_create_initial(args: ObjectsCreateInitialArgs<'_>) -> Result<(), WorldLabsError> {
let client = Client::builder()
.emulation(Emulation::Firefox143)
.build()
.map_err(|err| WorldLabsClientError::WreqClientError(err))?;
/*
-H 'User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:145.0) Gecko/20100101 Firefox/145.0' \
-H 'Accept: * / *' \
-H 'Accept-Language: en-US,en;q=0.5' \
-H 'Accept-Encoding: gzip, deflate, br, zstd' \
-H 'Referer: https://marble.worldlabs.ai/' \
-H 'content-type: application/json' \
-H 'Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6Ijk1MTg5MTkxMTA3NjA1NDM0NGUxNWUyNTY0MjViYjQyNWVlYjNhNWMiLCJ0eXAiOiJKV1QifQ.eyJuYW1lIjoiVmljdG9yIFZvaWNlIiwicGljdHVyZSI6Imh0dHBzOi8vbGgzLmdvb2dsZXVzZXJjb250ZW50LmNvbS9hL0FDZzhvY0t3MGFhWEtZU3F5LVhra242OXlkSUpGR25KQTM3UzEzWHZqTlk0ZDl4Q3pVektVdz1zOTYtYyIsImlzcyI6Imh0dHBzOi8vc2VjdXJldG9rZW4uZ29vZ2xlLmNvbS93bHQtdHJhaW5pbmctZ3NjIiwiYXVkIjoid2x0LXRyYWluaW5nLWdzYyIsImF1dGhfdGltZSI6MTc2NTMyNTM3NCwidXNlcl9pZCI6InNWZ0FRRHlIM0JmWU9CZkY2ZXBXVGpMSHlnWDIiLCJzdWIiOiJzVmdBUUR5SDNCZllPQmZGNmVwV1RqTEh5Z1gyIiwiaWF0IjoxNzY1MzI1Mzc0LCJleHAiOjE3NjUzMjg5NzQsImVtYWlsIjoidmljdG9ydm9pY2U4OUBnbWFpbC5jb20iLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwiZmlyZWJhc2UiOnsiaWRlbnRpdGllcyI6eyJnb29nbGUuY29tIjpbIjExNjQ1OTMwMDE5ODYxOTg4ODgzNiJdLCJlbWFpbCI6WyJ2aWN0b3J2b2ljZTg5QGdtYWlsLmNvbSJdfSwic2lnbl9pbl9wcm92aWRlciI6Imdvb2dsZS5jb20ifX0.U_mp4iAw14poJz11UFKg3QVO6TSfE8Gps6VqLhg4GLLumjilgnuyD8mnKFznRFX0R-OyBf8mugVk59zfy4BugeZ0cXRkm1FWHqR9BVJZ9BQoEobXw4RqHkUvtgSe2qnx8a4fLUVp5zxNPpXfEjLAdcG4YnNwdZ7-TxyCDvO3U_WHkG3aAJup1q5KOqURDeYoQ7m7THeobyM3pnmDO03GIMdYgLV3Lnpvk6FTprtsV8SxWr8ZJ-Cy1g_I9wtKcB4B5bIFLjfV4r8W_-uq_g5ddbri-Yy28Ah97jxFIFOVChbPSbQpS-TRshW6M2cM5Y2EhGudpgVQBM-Tekco9fGSRw' \
-H 'Origin: https://marble.worldlabs.ai' \
-H 'Sec-GPC: 1' \
-H 'Connection: keep-alive' \
-H 'Sec-Fetch-Dest: empty' \
-H 'Sec-Fetch-Mode: cors' \
-H 'Sec-Fetch-Site: cross-site' \
-H 'Priority: u=0' \
-H 'Pragma: no-cache' \
-H 'Cache-Control: no-cache' \
-H 'TE: trailers' \
--data-raw '{"metadata":{"version":"0.0.1","createdAt":1765325556528,"updatedAt":1765325556528,"useAdvancedEditing":false,"draftMode":false,"nodes":{}},"mime_type":"application/run+json"}'
*/
let mut request_builder = client.post(URL)
.header(ACCEPT, ACCEPT_ALL)
.header(ACCEPT_LANGUAGE, "en-US,en;q=0.5")
.header(ACCEPT_ENCODING, "gzip, deflate, br, zstd")
.header(REFERER, REFERER_VALUE)
.header(CONTENT_TYPE, CONTENT_TYPE_APPLICATION_JSON)
.header(AUTHORIZATION, args.bearer_token.to_bearer_token_string())
.header(ORIGIN, ORIGIN_VALUE)
.header(SEC_GPC, "1")
.header(CONNECTION, CONNECTION_KEEP_ALIVE)
.header(SEC_FETCH_DEST, SEC_FETCH_DEST_EMPTY)
.header(SEC_FETCH_MODE, SEC_FETCH_MODE_CORS)
.header(SEC_FETCH_SITE, SEC_FETCH_SITE_CROSS_SITE)
.header(PRIORITY, PRIORITY_HIGHEST)
.header(PRAGMA, PRAGMA_NO_CACHE)
.header(CACHE_CONTROL, CACHE_CONTROL_NO_CACHE)
.header(TE, TE_TRAILERS);
Ok(())
}
@@ -0,0 +1,16 @@
use crate::credentials::world_labs_bearer_token::WorldLabsBearerToken;
use errors::AnyhowResult;
use std::fs::read_to_string;
#[cfg(test)]
pub (crate) fn get_test_bearer_token_raw() -> AnyhowResult<String> {
let cookies = read_to_string("/home/bt/Artcraft/credentials/world_labs_bearer.txt")?;
let cookies = cookies.trim().to_string();
Ok(cookies)
}
#[cfg(test)]
pub (crate) fn get_test_bearer_token() -> AnyhowResult<WorldLabsBearerToken> {
let cookies = get_test_bearer_token_raw()?;
Ok(WorldLabsBearerToken::new(cookies))
}
@@ -3,13 +3,14 @@ use errors::AnyhowResult;
use std::fs::read_to_string;
#[cfg(test)]
pub fn get_test_cookies() -> AnyhowResult<String> {
pub (crate) fn get_test_cookies() -> AnyhowResult<String> {
let cookies = read_to_string("/home/bt/Artcraft/credentials/world_lab_cookies.txt")?;
let cookies = cookies.trim().to_string();
Ok(cookies)
}
pub fn get_typed_test_cookies() -> AnyhowResult<WorldLabsCookies> {
#[cfg(test)]
pub (crate) fn get_typed_test_cookies() -> AnyhowResult<WorldLabsCookies> {
let cookies = get_test_cookies()?;
Ok(WorldLabsCookies::new(cookies))
}
@@ -1 +1,2 @@
pub mod get_test_cookies;
pub mod get_test_cookies;
mod get_test_bearer_token;
+26
View File
@@ -0,0 +1,26 @@
[package]
name = "http_headers"
edition = "2024"
version = "0.0.1"
publish = false
[package.metadata.cargo-udeps.ignore]
normal = ["workspace-build-acceleration"]
[lib]
# NB: public `http_headers` crate appears to be abandoned, so this name seems fine. Wish we had namespacing, though.
name = "http_headers"
path = "src/lib.rs"
[dependencies]
# Workspace hack (faster compile times)
workspace-build-acceleration.workspace = true
# Internal
errors.workspace = true
# External
# (None yet)
[dev-dependencies]
# (None yet)
+23
View File
@@ -0,0 +1,23 @@
//! http_headers
//!
//! The purpose of this library is to centralize the definition of common header names
//! and header values that are not already represented by common libraries.
//!
// Never allow these
#![forbid(private_bounds)]
#![forbid(private_interfaces)]
#![forbid(unused_must_use)] // NB: It's unsafe to not close/check some things
// Okay to toggle
#![forbid(unreachable_patterns)]
#![forbid(unused_imports)]
#![forbid(unused_mut)]
#![forbid(unused_variables)]
// Always allow
#![allow(dead_code)]
#![allow(non_snake_case)]
pub mod names;
pub mod values;
+20
View File
@@ -0,0 +1,20 @@
/// `Priority` header name
/// https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Priority
pub const PRIORITY: &str = "Priority";
/// `Sec-Fetch-Dest` header name
/// https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Sec-Fetch-Dest
pub const SEC_FETCH_DEST : &str = "Sec-Fetch-Dest";
/// `Sec-Fetch-Mode` header name
/// https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Sec-Fetch-Mode
pub const SEC_FETCH_MODE: &str = "Sec-Fetch-Mode";
/// `Sec-Fetch-Site` header name
/// https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Sec-Fetch-Site
pub const SEC_FETCH_SITE : &str = "Sec-Fetch-Site";
/// `Sec-GPC` header name
/// https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Sec-GPC
pub const SEC_GPC : &str = "Sec-GPC";
@@ -0,0 +1,2 @@
pub const ACCEPT_ALL : &str = "*/*";
@@ -0,0 +1,3 @@
/// eg. -H 'Cache-Control: no-cache'
pub const CACHE_CONTROL_NO_CACHE: &str = "no-cache";
@@ -0,0 +1,2 @@
pub const CONNECTION_KEEP_ALIVE : &str = "keep-alive";
@@ -0,0 +1,2 @@
pub const CONTENT_TYPE_APPLICATION_JSON: &str = "application/json";
@@ -0,0 +1,8 @@
pub mod accept;
pub mod cache_control;
pub mod connection;
pub mod content_type;
pub mod pragma;
pub mod priority;
pub mod sec;
pub mod te;
@@ -0,0 +1,3 @@
/// eg. -H 'Pragma: no-cache'
pub const PRAGMA_NO_CACHE: &str = "no-cache";
@@ -0,0 +1,2 @@
/// eg. -H 'Priority: u=0'
pub const PRIORITY_HIGHEST : &str = "u=0";
@@ -0,0 +1,9 @@
/// eg. -H 'Sec-Fetch-Dest: empty'
pub const SEC_FETCH_DEST_EMPTY : &str = "empty";
/// eg. -H 'Sec-Fetch-Mode: cors'
pub const SEC_FETCH_MODE_CORS : &str = "cors";
/// eg. -H 'Sec-Fetch-Site: cross-site'
pub const SEC_FETCH_SITE_CROSS_SITE : &str = "cross-site";
+3
View File
@@ -0,0 +1,3 @@
/// eg. -H 'TE: trailers'
pub const TE_TRAILERS : &str = "trailers";