1use crate::git::repo;
46use crate::paths;
47use crate::publish::{create_index_line, write_to_index};
48use cargo_util::Sha256;
49use cargo_util::paths::append;
50use flate2::Compression;
51use flate2::write::GzEncoder;
52use pasetors::keys::{AsymmetricPublicKey, AsymmetricSecretKey};
53use pasetors::paserk::FormatAsPaserk;
54use pasetors::token::UntrustedToken;
55use std::collections::{BTreeMap, HashMap};
56use std::fmt;
57use std::fs::{self, File};
58use std::io::{BufRead, BufReader, Read, Write};
59use std::net::{SocketAddr, TcpListener, TcpStream};
60use std::path::{Path, PathBuf};
61use std::thread::{self, JoinHandle};
62use tar::{Builder, Header};
63use time::format_description::well_known::Rfc3339;
64use time::{Duration, OffsetDateTime};
65use url::Url;
66
67pub fn registry_path() -> PathBuf {
75 generate_path("registry")
76}
77
78pub fn api_path() -> PathBuf {
85 generate_path("api")
86}
87
88pub fn dl_path() -> PathBuf {
96 generate_path("dl")
97}
98
99pub fn alt_registry_path() -> PathBuf {
103 generate_path("alternative-registry")
104}
105
106fn alt_registry_url() -> Url {
108 generate_url("alternative-registry")
109}
110
111pub fn alt_dl_path() -> PathBuf {
115 generate_path("alternative-dl")
116}
117
118pub fn alt_api_path() -> PathBuf {
122 generate_path("alternative-api")
123}
124
125fn generate_path(name: &str) -> PathBuf {
126 paths::root().join(name)
127}
128fn generate_url(name: &str) -> Url {
129 Url::from_file_path(generate_path(name)).ok().unwrap()
130}
131
132#[derive(Clone)]
134pub enum Token {
135 Plaintext(String),
136 Keys(String, Option<String>),
137}
138
139impl Token {
140 pub fn rfc_key() -> Token {
144 Token::Keys(
145 "k3.secret.fNYVuMvBgOlljt9TDohnaYLblghqaHoQquVZwgR6X12cBFHZLFsaU3q7X3k1Zn36"
146 .to_string(),
147 Some("sub".to_string()),
148 )
149 }
150}
151
152type RequestCallback = Box<dyn Send + Fn(&Request, &HttpServer) -> Response>;
153
154pub struct RegistryBuilder {
158 alternative: Option<String>,
160 token: Option<Token>,
162 auth_required: bool,
164 http_index: bool,
166 http_api: bool,
168 api: bool,
170 configure_token: bool,
172 configure_registry: bool,
174 custom_responders: HashMap<String, RequestCallback>,
176 not_found_handler: RequestCallback,
178 delayed_index_update: usize,
180 credential_provider: Option<String>,
182}
183
184pub struct TestRegistry {
188 server: Option<HttpServerHandle>,
189 index_url: Url,
190 path: PathBuf,
191 api_url: Url,
192 dl_url: Url,
193 token: Token,
194}
195
196impl TestRegistry {
197 pub fn index_url(&self) -> &Url {
198 &self.index_url
199 }
200
201 pub fn api_url(&self) -> &Url {
202 &self.api_url
203 }
204
205 pub fn token(&self) -> &str {
206 match &self.token {
207 Token::Plaintext(s) => s,
208 Token::Keys(_, _) => panic!("registry was not configured with a plaintext token"),
209 }
210 }
211
212 pub fn key(&self) -> &str {
213 match &self.token {
214 Token::Plaintext(_) => panic!("registry was not configured with a secret key"),
215 Token::Keys(s, _) => s,
216 }
217 }
218
219 pub fn join(self) {
223 if let Some(mut server) = self.server {
224 server.stop();
225 let handle = server.handle.take().unwrap();
226 handle.join().unwrap();
227 }
228 }
229}
230
231impl RegistryBuilder {
232 #[must_use]
233 pub fn new() -> RegistryBuilder {
234 let not_found = |_req: &Request, _server: &HttpServer| -> Response {
235 Response {
236 code: 404,
237 headers: vec![],
238 body: b"not found".to_vec(),
239 }
240 };
241 RegistryBuilder {
242 alternative: None,
243 token: None,
244 auth_required: false,
245 http_api: false,
246 http_index: false,
247 api: true,
248 configure_registry: true,
249 configure_token: true,
250 custom_responders: HashMap::new(),
251 not_found_handler: Box::new(not_found),
252 delayed_index_update: 0,
253 credential_provider: None,
254 }
255 }
256
257 #[must_use]
259 pub fn add_responder<R: 'static + Send + Fn(&Request, &HttpServer) -> Response>(
260 mut self,
261 url: impl Into<String>,
262 responder: R,
263 ) -> Self {
264 self.custom_responders
265 .insert(url.into(), Box::new(responder));
266 self
267 }
268
269 #[must_use]
270 pub fn not_found_handler<R: 'static + Send + Fn(&Request, &HttpServer) -> Response>(
271 mut self,
272 responder: R,
273 ) -> Self {
274 self.not_found_handler = Box::new(responder);
275 self
276 }
277
278 #[must_use]
280 pub fn delayed_index_update(mut self, delay: usize) -> Self {
281 self.delayed_index_update = delay;
282 self
283 }
284
285 #[must_use]
287 pub fn alternative_named(mut self, alt: &str) -> Self {
288 self.alternative = Some(alt.to_string());
289 self
290 }
291
292 #[must_use]
294 pub fn alternative(self) -> Self {
295 self.alternative_named("alternative")
296 }
297
298 #[must_use]
300 pub fn no_configure_token(mut self) -> Self {
301 self.configure_token = false;
302 self
303 }
304
305 #[must_use]
307 pub fn no_configure_registry(mut self) -> Self {
308 self.configure_registry = false;
309 self
310 }
311
312 #[must_use]
314 pub fn token(mut self, token: Token) -> Self {
315 self.token = Some(token);
316 self
317 }
318
319 #[must_use]
322 pub fn auth_required(mut self) -> Self {
323 self.auth_required = true;
324 self
325 }
326
327 #[must_use]
329 pub fn http_index(mut self) -> Self {
330 self.http_index = true;
331 self
332 }
333
334 #[must_use]
336 pub fn http_api(mut self) -> Self {
337 self.http_api = true;
338 self
339 }
340
341 #[must_use]
343 pub fn no_api(mut self) -> Self {
344 self.api = false;
345 self
346 }
347
348 #[must_use]
350 pub fn credential_provider(mut self, provider: &[&str]) -> Self {
351 self.credential_provider = Some(format!("['{}']", provider.join("','")));
352 self
353 }
354
355 #[must_use]
357 pub fn build(self) -> TestRegistry {
358 let config_path = paths::cargo_home().join("config.toml");
359 t!(fs::create_dir_all(config_path.parent().unwrap()));
360 let prefix = if let Some(alternative) = &self.alternative {
361 format!("{alternative}-")
362 } else {
363 String::new()
364 };
365 let registry_path = generate_path(&format!("{prefix}registry"));
366 let index_url = generate_url(&format!("{prefix}registry"));
367 let api_url = generate_url(&format!("{prefix}api"));
368 let dl_url = generate_url(&format!("{prefix}dl"));
369 let dl_path = generate_path(&format!("{prefix}dl"));
370 let api_path = generate_path(&format!("{prefix}api"));
371 let token = self
372 .token
373 .unwrap_or_else(|| Token::Plaintext(format!("{prefix}sekrit")));
374
375 let (server, index_url, api_url, dl_url) = if !self.http_index && !self.http_api {
376 (None, index_url, api_url, dl_url)
378 } else {
379 let server = HttpServer::new(
380 registry_path.clone(),
381 dl_path,
382 api_path.clone(),
383 token.clone(),
384 self.auth_required,
385 self.custom_responders,
386 self.not_found_handler,
387 self.delayed_index_update,
388 );
389 let index_url = if self.http_index {
390 server.index_url()
391 } else {
392 index_url
393 };
394 let api_url = if self.http_api {
395 server.api_url()
396 } else {
397 api_url
398 };
399 let dl_url = server.dl_url();
400 (Some(server), index_url, api_url, dl_url)
401 };
402
403 let registry = TestRegistry {
404 api_url,
405 index_url,
406 server,
407 dl_url,
408 path: registry_path,
409 token,
410 };
411
412 if self.configure_registry {
413 if let Some(alternative) = &self.alternative {
414 append(
415 &config_path,
416 format!(
417 "
418 [registries.{alternative}]
419 index = '{}'",
420 registry.index_url
421 )
422 .as_bytes(),
423 )
424 .unwrap();
425 if let Some(p) = &self.credential_provider {
426 append(
427 &config_path,
428 &format!(
429 "
430 credential-provider = {p}
431 "
432 )
433 .as_bytes(),
434 )
435 .unwrap()
436 }
437 } else {
438 append(
439 &config_path,
440 format!(
441 "
442 [source.crates-io]
443 replace-with = 'dummy-registry'
444
445 [registries.dummy-registry]
446 index = '{}'",
447 registry.index_url
448 )
449 .as_bytes(),
450 )
451 .unwrap();
452
453 if let Some(p) = &self.credential_provider {
454 append(
455 &config_path,
456 &format!(
457 "
458 [registry]
459 credential-provider = {p}
460 "
461 )
462 .as_bytes(),
463 )
464 .unwrap()
465 }
466 }
467 }
468
469 if self.configure_token {
470 let credentials = paths::cargo_home().join("credentials.toml");
471 match ®istry.token {
472 Token::Plaintext(token) => {
473 if let Some(alternative) = &self.alternative {
474 append(
475 &credentials,
476 format!(
477 r#"
478 [registries.{alternative}]
479 token = "{token}"
480 "#
481 )
482 .as_bytes(),
483 )
484 .unwrap();
485 } else {
486 append(
487 &credentials,
488 format!(
489 r#"
490 [registry]
491 token = "{token}"
492 "#
493 )
494 .as_bytes(),
495 )
496 .unwrap();
497 }
498 }
499 Token::Keys(key, subject) => {
500 let mut out = if let Some(alternative) = &self.alternative {
501 format!("\n[registries.{alternative}]\n")
502 } else {
503 format!("\n[registry]\n")
504 };
505 out += &format!("secret-key = \"{key}\"\n");
506 if let Some(subject) = subject {
507 out += &format!("secret-key-subject = \"{subject}\"\n");
508 }
509
510 append(&credentials, out.as_bytes()).unwrap();
511 }
512 }
513 }
514
515 let auth = if self.auth_required {
516 r#","auth-required":true"#
517 } else {
518 ""
519 };
520 let api = if self.api {
521 format!(r#","api":"{}""#, registry.api_url)
522 } else {
523 String::new()
524 };
525 repo(®istry.path)
527 .file(
528 "config.json",
529 &format!(r#"{{"dl":"{}"{api}{auth}}}"#, registry.dl_url),
530 )
531 .build();
532 fs::create_dir_all(api_path.join("api/v1/crates")).unwrap();
533
534 registry
535 }
536}
537
538#[must_use]
564pub struct Package {
565 name: String,
566 vers: String,
567 deps: Vec<Dependency>,
568 files: Vec<PackageFile>,
569 yanked: bool,
570 features: FeatureMap,
571 local: bool,
572 alternative: bool,
573 invalid_index_line: bool,
574 index_line: Option<String>,
575 edition: Option<String>,
576 resolver: Option<String>,
577 proc_macro: bool,
578 links: Option<String>,
579 rust_version: Option<String>,
580 cargo_features: Vec<String>,
581 pubtime: Option<String>,
582 v: Option<u32>,
583}
584
585pub(crate) type FeatureMap = BTreeMap<String, Vec<String>>;
586
587#[derive(Clone)]
589pub struct Dependency {
590 name: String,
591 vers: String,
592 kind: String,
593 artifact: Option<String>,
594 bindep_target: Option<String>,
595 lib: bool,
596 target: Option<String>,
597 features: Vec<String>,
598 registry: Option<String>,
599 package: Option<String>,
600 optional: bool,
601 default_features: bool,
602 public: bool,
603}
604
605#[non_exhaustive]
607enum EntryData {
608 Regular(String),
609 Symlink(PathBuf),
610 Directory,
611}
612
613struct PackageFile {
615 path: String,
616 contents: EntryData,
617 mode: u32,
620 extra: bool,
623}
624
625const DEFAULT_MODE: u32 = 0o644;
626
627pub fn init() -> TestRegistry {
633 RegistryBuilder::new().build()
634}
635
636pub fn alt_init() -> TestRegistry {
640 init();
641 RegistryBuilder::new().alternative().build()
642}
643
644pub struct HttpServerHandle {
645 addr: SocketAddr,
646 handle: Option<JoinHandle<()>>,
647}
648
649impl HttpServerHandle {
650 pub fn index_url(&self) -> Url {
651 Url::parse(&format!("sparse+http://{}/index/", self.addr)).unwrap()
652 }
653
654 pub fn api_url(&self) -> Url {
655 Url::parse(&format!("http://{}/", self.addr)).unwrap()
656 }
657
658 pub fn dl_url(&self) -> Url {
659 Url::parse(&format!("http://{}/dl", self.addr)).unwrap()
660 }
661
662 fn stop(&self) {
663 if let Ok(mut stream) = TcpStream::connect(self.addr) {
664 let _ = stream.write_all(b"stop");
666 let _ = stream.flush();
667 }
668 }
669}
670
671impl Drop for HttpServerHandle {
672 fn drop(&mut self) {
673 self.stop();
674 }
675}
676
677#[derive(Clone)]
679pub struct Request {
680 pub url: Url,
681 pub method: String,
682 pub body: Option<Vec<u8>>,
683 pub authorization: Option<String>,
684 pub if_modified_since: Option<String>,
685 pub if_none_match: Option<String>,
686}
687
688impl fmt::Debug for Request {
689 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
690 f.debug_struct("Request")
692 .field("url", &self.url)
693 .field("method", &self.method)
694 .field("authorization", &self.authorization)
695 .field("if_modified_since", &self.if_modified_since)
696 .field("if_none_match", &self.if_none_match)
697 .finish()
698 }
699}
700
701pub struct Response {
703 pub code: u32,
704 pub headers: Vec<String>,
705 pub body: Vec<u8>,
706}
707
708pub struct HttpServer {
709 listener: TcpListener,
710 registry_path: PathBuf,
711 dl_path: PathBuf,
712 api_path: PathBuf,
713 addr: SocketAddr,
714 token: Token,
715 auth_required: bool,
716 custom_responders: HashMap<String, RequestCallback>,
717 not_found_handler: RequestCallback,
718 delayed_index_update: usize,
719}
720
721struct Mutation<'a> {
724 mutation: &'a str,
725 name: Option<&'a str>,
726 vers: Option<&'a str>,
727 cksum: Option<&'a str>,
728}
729
730impl HttpServer {
731 pub fn new(
732 registry_path: PathBuf,
733 dl_path: PathBuf,
734 api_path: PathBuf,
735 token: Token,
736 auth_required: bool,
737 custom_responders: HashMap<String, RequestCallback>,
738 not_found_handler: RequestCallback,
739 delayed_index_update: usize,
740 ) -> HttpServerHandle {
741 let listener = TcpListener::bind("127.0.0.1:0").unwrap();
742 let addr = listener.local_addr().unwrap();
743 let server = HttpServer {
744 listener,
745 registry_path,
746 dl_path,
747 api_path,
748 addr,
749 token,
750 auth_required,
751 custom_responders,
752 not_found_handler,
753 delayed_index_update,
754 };
755 let handle = Some(thread::spawn(move || server.start()));
756 HttpServerHandle { addr, handle }
757 }
758
759 fn start(&self) {
760 let mut line = String::new();
761 'server: loop {
762 let (socket, _) = self.listener.accept().unwrap();
763 let mut buf = BufReader::new(socket);
764 line.clear();
765 if buf.read_line(&mut line).unwrap() == 0 {
766 continue;
768 }
769 let mut parts = line.split_ascii_whitespace();
771 let method = parts.next().unwrap().to_ascii_lowercase();
772 if method == "stop" {
773 return;
775 }
776 let addr = self.listener.local_addr().unwrap();
777 let url = format!(
778 "http://{}/{}",
779 addr,
780 parts.next().unwrap().trim_start_matches('/')
781 );
782 let url = Url::parse(&url).unwrap();
783
784 let mut if_modified_since = None;
786 let mut if_none_match = None;
787 let mut authorization = None;
788 let mut content_len = None;
789 loop {
790 line.clear();
791 if buf.read_line(&mut line).unwrap() == 0 {
792 continue 'server;
793 }
794 if line == "\r\n" {
795 line.clear();
797 break;
798 }
799 let (name, value) = line.split_once(':').unwrap();
800 let name = name.trim().to_ascii_lowercase();
801 let value = value.trim().to_string();
802 match name.as_str() {
803 "if-modified-since" => if_modified_since = Some(value),
804 "if-none-match" => if_none_match = Some(value),
805 "authorization" => authorization = Some(value),
806 "content-length" => content_len = Some(value),
807 _ => {}
808 }
809 }
810
811 let mut body = None;
812 if let Some(con_len) = content_len {
813 let len = con_len.parse::<u64>().unwrap();
814 let mut content = vec![0u8; len as usize];
815 buf.read_exact(&mut content).unwrap();
816 body = Some(content)
817 }
818
819 let req = Request {
820 authorization,
821 if_modified_since,
822 if_none_match,
823 method,
824 url,
825 body,
826 };
827 println!("req: {:#?}", req);
828 let response = self.route(&req);
829 let buf = buf.get_mut();
830 write!(buf, "HTTP/1.1 {}\r\n", response.code).unwrap();
831 write!(buf, "Content-Length: {}\r\n", response.body.len()).unwrap();
832 write!(buf, "Connection: close\r\n").unwrap();
833 for header in response.headers {
834 write!(buf, "{}\r\n", header).unwrap();
835 }
836 write!(buf, "\r\n").unwrap();
837 buf.write_all(&response.body).unwrap();
838 buf.flush().unwrap();
839 }
840 }
841
842 fn check_authorized(&self, req: &Request, mutation: Option<Mutation<'_>>) -> bool {
843 let (private_key, private_key_subject) = if mutation.is_some() || self.auth_required {
844 match &self.token {
845 Token::Plaintext(token) => return Some(token) == req.authorization.as_ref(),
846 Token::Keys(private_key, private_key_subject) => {
847 (private_key.as_str(), private_key_subject)
848 }
849 }
850 } else {
851 assert!(req.authorization.is_none(), "unexpected token");
852 return true;
853 };
854
855 macro_rules! t {
856 ($e:expr) => {
857 match $e {
858 Some(e) => e,
859 None => return false,
860 }
861 };
862 }
863
864 let secret: AsymmetricSecretKey<pasetors::version3::V3> = private_key.try_into().unwrap();
865 let public: AsymmetricPublicKey<pasetors::version3::V3> = (&secret).try_into().unwrap();
866 let pub_key_id: pasetors::paserk::Id = (&public).into();
867 let mut paserk_pub_key_id = String::new();
868 FormatAsPaserk::fmt(&pub_key_id, &mut paserk_pub_key_id).unwrap();
869 let authorization = t!(&req.authorization);
873 let untrusted_token = t!(
874 UntrustedToken::<pasetors::Public, pasetors::version3::V3>::try_from(authorization)
875 .ok()
876 );
877
878 #[derive(serde::Deserialize, Debug)]
880 struct Footer<'a> {
881 url: &'a str,
882 kip: &'a str,
883 }
884 let footer: Footer<'_> =
885 t!(serde_json::from_slice(untrusted_token.untrusted_footer()).ok());
886 if footer.kip != paserk_pub_key_id {
887 return false;
888 }
889 let trusted_token =
890 t!(
891 pasetors::version3::PublicToken::verify(&public, &untrusted_token, None, None,)
892 .ok()
893 );
894
895 if footer.url != "https://github.com/rust-lang/crates.io-index"
897 && footer.url != &format!("sparse+http://{}/index/", self.addr)
898 {
899 return false;
900 }
901
902 #[derive(serde::Deserialize)]
904 struct Message<'a> {
905 iat: &'a str,
906 sub: Option<&'a str>,
907 mutation: Option<&'a str>,
908 name: Option<&'a str>,
909 vers: Option<&'a str>,
910 cksum: Option<&'a str>,
911 _challenge: Option<&'a str>, v: Option<u8>,
913 }
914 let message: Message<'_> = t!(serde_json::from_str(trusted_token.payload()).ok());
915 let token_time = t!(OffsetDateTime::parse(message.iat, &Rfc3339).ok());
916 let now = OffsetDateTime::now_utc();
917 if (now - token_time) > Duration::MINUTE {
918 return false;
919 }
920 if private_key_subject.as_deref() != message.sub {
921 return false;
922 }
923 if let Some(v) = message.v {
925 if v != 1 {
926 return false;
927 }
928 }
929 if let Some(mutation) = mutation {
933 if message.mutation != Some(mutation.mutation) {
935 return false;
936 }
937 if message.name != mutation.name {
939 return false;
940 }
941 if message.vers != mutation.vers {
942 return false;
943 }
944 if mutation.mutation == "publish" {
946 if message.cksum != mutation.cksum {
947 return false;
948 }
949 }
950 } else {
951 if message.mutation.is_some()
953 || message.name.is_some()
954 || message.vers.is_some()
955 || message.cksum.is_some()
956 {
957 return false;
958 }
959 }
960 true
961 }
962
963 fn route(&self, req: &Request) -> Response {
965 if let Some(responder) = self.custom_responders.get(req.url.path()) {
967 return responder(&req, self);
968 }
969 let path: Vec<_> = req.url.path()[1..].split('/').collect();
970 match (req.method.as_str(), path.as_slice()) {
971 ("get", ["index", ..]) => {
972 if !self.check_authorized(req, None) {
973 self.unauthorized(req)
974 } else {
975 self.index(&req)
976 }
977 }
978 ("get", ["dl", ..]) => {
979 if !self.check_authorized(req, None) {
980 self.unauthorized(req)
981 } else {
982 self.dl(&req)
983 }
984 }
985 ("put", ["api", "v1", "crates", "new"]) => self.check_authorized_publish(req),
987 ("delete" | "put", ["api", "v1", "crates", crate_name, version, mutation]) => {
994 if !self.check_authorized(
995 req,
996 Some(Mutation {
997 mutation,
998 name: Some(crate_name),
999 vers: Some(version),
1000 cksum: None,
1001 }),
1002 ) {
1003 self.unauthorized(req)
1004 } else {
1005 self.ok(&req)
1006 }
1007 }
1008 ("get" | "put" | "delete", ["api", "v1", "crates", crate_name, "owners"]) => {
1010 if !self.check_authorized(
1011 req,
1012 Some(Mutation {
1013 mutation: "owners",
1014 name: Some(crate_name),
1015 vers: None,
1016 cksum: None,
1017 }),
1018 ) {
1019 self.unauthorized(req)
1020 } else {
1021 self.ok(&req)
1022 }
1023 }
1024 _ => self.not_found(&req),
1025 }
1026 }
1027
1028 pub fn unauthorized(&self, _req: &Request) -> Response {
1030 Response {
1031 code: 401,
1032 headers: vec![
1033 r#"WWW-Authenticate: Cargo login_url="https://test-registry-login/me""#.to_string(),
1034 ],
1035 body: b"Unauthorized message from server.".to_vec(),
1036 }
1037 }
1038
1039 pub fn not_found(&self, req: &Request) -> Response {
1041 (self.not_found_handler)(req, self)
1042 }
1043
1044 pub fn ok(&self, _req: &Request) -> Response {
1046 Response {
1047 code: 200,
1048 headers: vec![],
1049 body: br#"{"ok": true, "msg": "completed!"}"#.to_vec(),
1050 }
1051 }
1052
1053 pub fn internal_server_error(&self, _req: &Request) -> Response {
1055 Response {
1056 code: 500,
1057 headers: vec![],
1058 body: br#"internal server error"#.to_vec(),
1059 }
1060 }
1061
1062 pub fn too_many_requests(&self, _req: &Request, delay: std::time::Duration) -> Response {
1064 Response {
1065 code: 429,
1066 headers: vec![format!("Retry-After: {}", delay.as_secs())],
1067 body: format!(
1068 "too many requests, try again in {} seconds",
1069 delay.as_secs()
1070 )
1071 .into_bytes(),
1072 }
1073 }
1074
1075 pub fn dl(&self, req: &Request) -> Response {
1077 let file = self
1078 .dl_path
1079 .join(req.url.path().strip_prefix("/dl/").unwrap());
1080 println!("{}", file.display());
1081 if !file.exists() {
1082 return self.not_found(req);
1083 }
1084 return Response {
1085 body: fs::read(&file).unwrap(),
1086 code: 200,
1087 headers: vec![],
1088 };
1089 }
1090
1091 pub fn index(&self, req: &Request) -> Response {
1093 let file = self
1094 .registry_path
1095 .join(req.url.path().strip_prefix("/index/").unwrap());
1096 if !file.exists() {
1097 return self.not_found(req);
1098 } else {
1099 let data = fs::read(&file).unwrap();
1101 let etag = Sha256::new().update(&data).finish_hex();
1102 let last_modified = format!("{:?}", file.metadata().unwrap().modified().unwrap());
1103
1104 let mut any_match = false;
1106 let mut all_match = true;
1107 if let Some(expected) = &req.if_none_match {
1108 if &etag != expected {
1109 all_match = false;
1110 } else {
1111 any_match = true;
1112 }
1113 }
1114 if let Some(expected) = &req.if_modified_since {
1115 if &last_modified != expected {
1117 all_match = false;
1118 } else {
1119 any_match = true;
1120 }
1121 }
1122
1123 if any_match && all_match {
1124 return Response {
1125 body: Vec::new(),
1126 code: 304,
1127 headers: vec![],
1128 };
1129 } else {
1130 return Response {
1131 body: data,
1132 code: 200,
1133 headers: vec![
1134 format!("ETag: \"{}\"", etag),
1135 format!("Last-Modified: {}", last_modified),
1136 ],
1137 };
1138 }
1139 }
1140 }
1141
1142 pub fn check_authorized_publish(&self, req: &Request) -> Response {
1143 if let Some(body) = &req.body {
1144 let path = self.api_path.join("api/v1/crates/new");
1147 t!(fs::create_dir_all(path.parent().unwrap()));
1148 t!(fs::write(&path, body));
1149
1150 let (len, remaining) = body.split_at(4);
1152 let json_len = u32::from_le_bytes(len.try_into().unwrap());
1153 let (json, remaining) = remaining.split_at(json_len as usize);
1154 let new_crate = serde_json::from_slice::<crates_io::NewCrate>(json).unwrap();
1155 let (len, remaining) = remaining.split_at(4);
1157 let file_len = u32::from_le_bytes(len.try_into().unwrap());
1158 let (file, _remaining) = remaining.split_at(file_len as usize);
1159 let file_cksum = cksum(&file);
1160
1161 if !self.check_authorized(
1162 req,
1163 Some(Mutation {
1164 mutation: "publish",
1165 name: Some(&new_crate.name),
1166 vers: Some(&new_crate.vers),
1167 cksum: Some(&file_cksum),
1168 }),
1169 ) {
1170 return self.unauthorized(req);
1171 }
1172
1173 let dst = self
1174 .dl_path
1175 .join(&new_crate.name)
1176 .join(&new_crate.vers)
1177 .join("download");
1178
1179 if self.delayed_index_update == 0 {
1180 save_new_crate(dst, new_crate, file, file_cksum, &self.registry_path);
1181 } else {
1182 let delayed_index_update = self.delayed_index_update;
1183 let registry_path = self.registry_path.clone();
1184 let file = Vec::from(file);
1185 thread::spawn(move || {
1186 thread::sleep(std::time::Duration::new(delayed_index_update as u64, 0));
1187 save_new_crate(dst, new_crate, &file, file_cksum, ®istry_path);
1188 });
1189 }
1190
1191 self.ok(&req)
1192 } else {
1193 Response {
1194 code: 400,
1195 headers: vec![],
1196 body: b"The request was missing a body".to_vec(),
1197 }
1198 }
1199 }
1200}
1201
1202fn save_new_crate(
1203 dst: PathBuf,
1204 new_crate: crates_io::NewCrate,
1205 file: &[u8],
1206 file_cksum: String,
1207 registry_path: &Path,
1208) {
1209 t!(fs::create_dir_all(dst.parent().unwrap()));
1211 t!(fs::write(&dst, file));
1212
1213 let deps = new_crate
1214 .deps
1215 .iter()
1216 .map(|dep| {
1217 let (name, package) = match &dep.explicit_name_in_toml {
1218 Some(explicit) => (explicit.to_string(), Some(dep.name.to_string())),
1219 None => (dep.name.to_string(), None),
1220 };
1221 serde_json::json!({
1222 "name": name,
1223 "req": dep.version_req,
1224 "features": dep.features,
1225 "default_features": dep.default_features,
1226 "target": dep.target,
1227 "optional": dep.optional,
1228 "kind": dep.kind,
1229 "registry": dep.registry,
1230 "package": package,
1231 "artifact": dep.artifact,
1232 "bindep_target": dep.bindep_target,
1233 "lib": dep.lib,
1234 })
1235 })
1236 .collect::<Vec<_>>();
1237
1238 let line = create_index_line(
1239 serde_json::json!(new_crate.name),
1240 &new_crate.vers,
1241 deps,
1242 &file_cksum,
1243 new_crate.features,
1244 false,
1245 new_crate.links,
1246 new_crate.rust_version.as_deref(),
1247 None,
1248 None,
1249 );
1250
1251 write_to_index(registry_path, &new_crate.name, line, false);
1252}
1253
1254impl Package {
1255 pub fn new(name: &str, vers: &str) -> Package {
1258 let config = paths::cargo_home().join("config.toml");
1259 if !config.exists() {
1260 init();
1261 }
1262 Package {
1263 name: name.to_string(),
1264 vers: vers.to_string(),
1265 deps: Vec::new(),
1266 files: Vec::new(),
1267 yanked: false,
1268 features: BTreeMap::new(),
1269 local: false,
1270 alternative: false,
1271 invalid_index_line: false,
1272 index_line: None,
1273 edition: None,
1274 resolver: None,
1275 proc_macro: false,
1276 links: None,
1277 rust_version: None,
1278 cargo_features: Vec::new(),
1279 pubtime: None,
1280 v: None,
1281 }
1282 }
1283
1284 pub fn local(&mut self, local: bool) -> &mut Package {
1290 self.local = local;
1291 self
1292 }
1293
1294 pub fn alternative(&mut self, alternative: bool) -> &mut Package {
1304 self.alternative = alternative;
1305 self
1306 }
1307
1308 pub fn file(&mut self, name: &str, contents: &str) -> &mut Package {
1310 self.file_with_mode(name, DEFAULT_MODE, contents)
1311 }
1312
1313 pub fn file_with_mode(&mut self, path: &str, mode: u32, contents: &str) -> &mut Package {
1315 self.files.push(PackageFile {
1316 path: path.to_string(),
1317 contents: EntryData::Regular(contents.into()),
1318 mode,
1319 extra: false,
1320 });
1321 self
1322 }
1323
1324 pub fn symlink(&mut self, dst: &str, src: &str) -> &mut Package {
1326 self.files.push(PackageFile {
1327 path: dst.to_string(),
1328 contents: EntryData::Symlink(src.into()),
1329 mode: DEFAULT_MODE,
1330 extra: false,
1331 });
1332 self
1333 }
1334
1335 pub fn directory(&mut self, path: &str) -> &mut Package {
1337 self.files.push(PackageFile {
1338 path: path.to_string(),
1339 contents: EntryData::Directory,
1340 mode: DEFAULT_MODE,
1341 extra: false,
1342 });
1343 self
1344 }
1345
1346 pub fn extra_file(&mut self, path: &str, contents: &str) -> &mut Package {
1352 self.files.push(PackageFile {
1353 path: path.to_string(),
1354 contents: EntryData::Regular(contents.to_string()),
1355 mode: DEFAULT_MODE,
1356 extra: true,
1357 });
1358 self
1359 }
1360
1361 pub fn dep(&mut self, name: &str, vers: &str) -> &mut Package {
1367 self.add_dep(&Dependency::new(name, vers))
1368 }
1369
1370 pub fn feature_dep(&mut self, name: &str, vers: &str, features: &[&str]) -> &mut Package {
1376 self.add_dep(Dependency::new(name, vers).enable_features(features))
1377 }
1378
1379 pub fn target_dep(&mut self, name: &str, vers: &str, target: &str) -> &mut Package {
1385 self.add_dep(Dependency::new(name, vers).target(target))
1386 }
1387
1388 pub fn registry_dep(&mut self, name: &str, vers: &str) -> &mut Package {
1390 self.add_dep(Dependency::new(name, vers).registry("alternative"))
1391 }
1392
1393 pub fn dev_dep(&mut self, name: &str, vers: &str) -> &mut Package {
1399 self.add_dep(Dependency::new(name, vers).dev())
1400 }
1401
1402 pub fn build_dep(&mut self, name: &str, vers: &str) -> &mut Package {
1408 self.add_dep(Dependency::new(name, vers).build())
1409 }
1410
1411 pub fn add_dep(&mut self, dep: &Dependency) -> &mut Package {
1412 self.deps.push(dep.clone());
1413 self
1414 }
1415
1416 pub fn yanked(&mut self, yanked: bool) -> &mut Package {
1418 self.yanked = yanked;
1419 self
1420 }
1421
1422 pub fn edition(&mut self, edition: &str) -> &mut Package {
1424 self.edition = Some(edition.to_owned());
1425 self
1426 }
1427
1428 pub fn resolver(&mut self, resolver: &str) -> &mut Package {
1430 self.resolver = Some(resolver.to_owned());
1431 self
1432 }
1433
1434 pub fn proc_macro(&mut self, proc_macro: bool) -> &mut Package {
1436 self.proc_macro = proc_macro;
1437 self
1438 }
1439
1440 pub fn feature(&mut self, name: &str, deps: &[&str]) -> &mut Package {
1442 let deps = deps.iter().map(|s| s.to_string()).collect();
1443 self.features.insert(name.to_string(), deps);
1444 self
1445 }
1446
1447 pub fn rust_version(&mut self, rust_version: &str) -> &mut Package {
1449 self.rust_version = Some(rust_version.into());
1450 self
1451 }
1452
1453 pub fn invalid_index_line(&mut self, invalid: bool) -> &mut Package {
1456 self.invalid_index_line = invalid;
1457 self
1458 }
1459
1460 pub fn index_line(&mut self, line: &str) -> &mut Package {
1464 self.index_line = Some(line.to_owned());
1465 self
1466 }
1467
1468 pub fn links(&mut self, links: &str) -> &mut Package {
1469 self.links = Some(links.to_string());
1470 self
1471 }
1472
1473 pub fn cargo_feature(&mut self, feature: &str) -> &mut Package {
1474 self.cargo_features.push(feature.to_owned());
1475 self
1476 }
1477
1478 pub fn pubtime(&mut self, time: &str) -> &mut Package {
1480 self.pubtime = Some(time.to_owned());
1481 self
1482 }
1483
1484 pub fn schema_version(&mut self, version: u32) -> &mut Package {
1488 self.v = Some(version);
1489 self
1490 }
1491
1492 pub fn publish(&self) -> String {
1499 self.make_archive();
1500
1501 let deps = self
1503 .deps
1504 .iter()
1505 .map(|dep| {
1506 let registry_url = match (self.alternative, dep.registry.as_deref()) {
1509 (false, None) => None,
1510 (false, Some("alternative")) => Some(alt_registry_url().to_string()),
1511 (true, None) => {
1512 Some("https://github.com/rust-lang/crates.io-index".to_string())
1513 }
1514 (true, Some("alternative")) => None,
1515 _ => panic!("registry_dep currently only supports `alternative`"),
1516 };
1517 let artifact = if let Some(artifact) = &dep.artifact {
1518 serde_json::json!([artifact])
1519 } else {
1520 serde_json::json!(null)
1521 };
1522 serde_json::json!({
1523 "name": dep.name,
1524 "req": dep.vers,
1525 "features": dep.features,
1526 "default_features": dep.default_features,
1527 "target": dep.target,
1528 "artifact": artifact,
1529 "bindep_target": dep.bindep_target,
1530 "lib": dep.lib,
1531 "optional": dep.optional,
1532 "kind": dep.kind,
1533 "registry": registry_url,
1534 "package": dep.package,
1535 "public": dep.public,
1536 })
1537 })
1538 .collect::<Vec<_>>();
1539 let cksum = {
1540 let c = t!(fs::read(&self.archive_dst()));
1541 cksum(&c)
1542 };
1543 let line = if let Some(line) = self.index_line.clone() {
1544 line
1545 } else {
1546 let name = if self.invalid_index_line {
1547 serde_json::json!(1)
1548 } else {
1549 serde_json::json!(self.name)
1550 };
1551 create_index_line(
1552 name,
1553 &self.vers,
1554 deps,
1555 &cksum,
1556 self.features.clone(),
1557 self.yanked,
1558 self.links.clone(),
1559 self.rust_version.as_deref(),
1560 self.pubtime.as_deref(),
1561 self.v,
1562 )
1563 };
1564
1565 let registry_path = if self.alternative {
1566 alt_registry_path()
1567 } else {
1568 registry_path()
1569 };
1570
1571 write_to_index(®istry_path, &self.name, line, self.local);
1572
1573 cksum
1574 }
1575
1576 fn make_archive(&self) {
1577 let dst = self.archive_dst();
1578 t!(fs::create_dir_all(dst.parent().unwrap()));
1579 let f = t!(File::create(&dst));
1580 let mut a = Builder::new(GzEncoder::new(f, Compression::none()));
1581 a.sparse(false);
1582
1583 if !self
1584 .files
1585 .iter()
1586 .any(|PackageFile { path, .. }| path == "Cargo.toml")
1587 {
1588 self.append_manifest(&mut a);
1589 }
1590 if self.files.is_empty() {
1591 self.append(
1592 &mut a,
1593 "src/lib.rs",
1594 DEFAULT_MODE,
1595 &EntryData::Regular("".into()),
1596 );
1597 } else {
1598 for PackageFile {
1599 path,
1600 contents,
1601 mode,
1602 extra,
1603 } in &self.files
1604 {
1605 if *extra {
1606 self.append_raw(&mut a, path, *mode, contents);
1607 } else {
1608 self.append(&mut a, path, *mode, contents);
1609 }
1610 }
1611 }
1612 }
1613
1614 fn append_manifest<W: Write>(&self, ar: &mut Builder<W>) {
1615 let mut manifest = String::new();
1616
1617 if !self.cargo_features.is_empty() {
1618 let mut features = String::new();
1619 serde::Serialize::serialize(
1620 &self.cargo_features,
1621 toml::ser::ValueSerializer::new(&mut features),
1622 )
1623 .unwrap();
1624 manifest.push_str(&format!("cargo-features = {}\n\n", features));
1625 }
1626
1627 manifest.push_str(&format!(
1628 r#"
1629 [package]
1630 name = "{}"
1631 version = "{}"
1632 authors = []
1633 "#,
1634 self.name, self.vers
1635 ));
1636
1637 if let Some(version) = &self.rust_version {
1638 manifest.push_str(&format!("rust-version = \"{}\"\n", version));
1639 }
1640
1641 if let Some(edition) = &self.edition {
1642 manifest.push_str(&format!("edition = \"{}\"\n", edition));
1643 }
1644
1645 if let Some(resolver) = &self.resolver {
1646 manifest.push_str(&format!("resolver = \"{}\"\n", resolver));
1647 }
1648
1649 if !self.features.is_empty() {
1650 let features: Vec<String> = self
1651 .features
1652 .iter()
1653 .map(|(feature, features)| {
1654 if features.is_empty() {
1655 format!("{} = []", feature)
1656 } else {
1657 format!(
1658 "{} = [{}]",
1659 feature,
1660 features
1661 .iter()
1662 .map(|s| format!("\"{}\"", s))
1663 .collect::<Vec<_>>()
1664 .join(", ")
1665 )
1666 }
1667 })
1668 .collect();
1669
1670 manifest.push_str(&format!("\n[features]\n{}", features.join("\n")));
1671 }
1672
1673 for dep in self.deps.iter() {
1674 let target = match dep.target {
1675 None => String::new(),
1676 Some(ref s) => format!("target.'{}'.", s),
1677 };
1678 let kind = match &dep.kind[..] {
1679 "build" => "build-",
1680 "dev" => "dev-",
1681 _ => "",
1682 };
1683 manifest.push_str(&format!(
1684 r#"
1685 [{}{}dependencies.{}]
1686 version = "{}"
1687 "#,
1688 target, kind, dep.name, dep.vers
1689 ));
1690 if dep.optional {
1691 manifest.push_str("optional = true\n");
1692 }
1693 if let Some(artifact) = &dep.artifact {
1694 manifest.push_str(&format!("artifact = \"{}\"\n", artifact));
1695 }
1696 if let Some(target) = &dep.bindep_target {
1697 manifest.push_str(&format!("target = \"{}\"\n", target));
1698 }
1699 if dep.lib {
1700 manifest.push_str("lib = true\n");
1701 }
1702 if let Some(registry) = &dep.registry {
1703 assert_eq!(registry, "alternative");
1704 manifest.push_str(&format!("registry-index = \"{}\"", alt_registry_url()));
1705 }
1706 if !dep.default_features {
1707 manifest.push_str("default-features = false\n");
1708 }
1709 if !dep.features.is_empty() {
1710 let mut features = String::new();
1711 serde::Serialize::serialize(
1712 &dep.features,
1713 toml::ser::ValueSerializer::new(&mut features),
1714 )
1715 .unwrap();
1716 manifest.push_str(&format!("features = {}\n", features));
1717 }
1718 if let Some(package) = &dep.package {
1719 manifest.push_str(&format!("package = \"{}\"\n", package));
1720 }
1721 }
1722 if self.proc_macro {
1723 manifest.push_str("[lib]\nproc-macro = true\n");
1724 }
1725
1726 self.append(
1727 ar,
1728 "Cargo.toml",
1729 DEFAULT_MODE,
1730 &EntryData::Regular(manifest.into()),
1731 );
1732 }
1733
1734 fn append<W: Write>(&self, ar: &mut Builder<W>, file: &str, mode: u32, contents: &EntryData) {
1735 self.append_raw(
1736 ar,
1737 &format!("{}-{}/{}", self.name, self.vers, file),
1738 mode,
1739 contents,
1740 );
1741 }
1742
1743 fn append_raw<W: Write>(
1744 &self,
1745 ar: &mut Builder<W>,
1746 path: &str,
1747 mode: u32,
1748 contents: &EntryData,
1749 ) {
1750 let mut header = Header::new_ustar();
1755 let contents = match contents {
1756 EntryData::Regular(contents) => contents.as_str(),
1757 EntryData::Symlink(src) => {
1758 header.set_entry_type(tar::EntryType::Symlink);
1759 t!(header.set_link_name(src));
1760 "" }
1762 EntryData::Directory => {
1763 header.set_entry_type(tar::EntryType::Directory);
1764 ""
1765 }
1766 };
1767 header.set_size(contents.len() as u64);
1768 t!(header.set_path(path));
1769 header.set_mode(mode);
1770 header.set_cksum();
1771 t!(ar.append(&header, contents.as_bytes()));
1772 }
1773
1774 pub fn archive_dst(&self) -> PathBuf {
1776 if self.local {
1777 let path = if self.alternative {
1778 alt_registry_path()
1779 } else {
1780 registry_path()
1781 };
1782 path.join(format!("{}-{}.crate", self.name, self.vers))
1783 } else if self.alternative {
1784 alt_dl_path()
1785 .join(&self.name)
1786 .join(&self.vers)
1787 .join("download")
1788 } else {
1789 dl_path().join(&self.name).join(&self.vers).join("download")
1790 }
1791 }
1792}
1793
1794pub fn cksum(s: &[u8]) -> String {
1796 Sha256::new().update(s).finish_hex()
1797}
1798
1799impl Dependency {
1800 pub fn new(name: &str, vers: &str) -> Dependency {
1801 Dependency {
1802 name: name.to_string(),
1803 vers: vers.to_string(),
1804 kind: "normal".to_string(),
1805 artifact: None,
1806 bindep_target: None,
1807 lib: false,
1808 target: None,
1809 features: Vec::new(),
1810 package: None,
1811 optional: false,
1812 registry: None,
1813 default_features: true,
1814 public: false,
1815 }
1816 }
1817
1818 pub fn build(&mut self) -> &mut Self {
1820 self.kind = "build".to_string();
1821 self
1822 }
1823
1824 pub fn dev(&mut self) -> &mut Self {
1826 self.kind = "dev".to_string();
1827 self
1828 }
1829
1830 pub fn target(&mut self, target: &str) -> &mut Self {
1832 self.target = Some(target.to_string());
1833 self
1834 }
1835
1836 pub fn artifact(&mut self, kind: &str, target: Option<String>) -> &mut Self {
1839 self.artifact = Some(kind.to_string());
1840 self.bindep_target = target;
1841 self
1842 }
1843
1844 pub fn registry(&mut self, registry: &str) -> &mut Self {
1846 self.registry = Some(registry.to_string());
1847 self
1848 }
1849
1850 pub fn enable_features(&mut self, features: &[&str]) -> &mut Self {
1852 self.features.extend(features.iter().map(|s| s.to_string()));
1853 self
1854 }
1855
1856 pub fn package(&mut self, pkg: &str) -> &mut Self {
1858 self.package = Some(pkg.to_string());
1859 self
1860 }
1861
1862 pub fn optional(&mut self, optional: bool) -> &mut Self {
1864 self.optional = optional;
1865 self
1866 }
1867
1868 pub fn public(&mut self, public: bool) -> &mut Self {
1870 self.public = public;
1871 self
1872 }
1873
1874 pub fn default_features(&mut self, default_features: bool) -> &mut Self {
1876 self.default_features = default_features;
1877 self
1878 }
1879}