1use crate::git::repo;
46use crate::paths;
47use crate::publish::{create_index_line, write_to_index};
48use cargo_util::paths::append;
49use cargo_util::Sha256;
50use flate2::write::GzEncoder;
51use flate2::Compression;
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 v: Option<u32>,
582}
583
584pub(crate) type FeatureMap = BTreeMap<String, Vec<String>>;
585
586#[derive(Clone)]
588pub struct Dependency {
589 name: String,
590 vers: String,
591 kind: String,
592 artifact: Option<String>,
593 bindep_target: Option<String>,
594 lib: bool,
595 target: Option<String>,
596 features: Vec<String>,
597 registry: Option<String>,
598 package: Option<String>,
599 optional: bool,
600 default_features: bool,
601 public: bool,
602}
603
604#[non_exhaustive]
606enum EntryData {
607 Regular(String),
608 Symlink(PathBuf),
609}
610
611struct PackageFile {
613 path: String,
614 contents: EntryData,
615 mode: u32,
618 extra: bool,
621}
622
623const DEFAULT_MODE: u32 = 0o644;
624
625pub fn init() -> TestRegistry {
631 RegistryBuilder::new().build()
632}
633
634pub fn alt_init() -> TestRegistry {
638 init();
639 RegistryBuilder::new().alternative().build()
640}
641
642pub struct HttpServerHandle {
643 addr: SocketAddr,
644 handle: Option<JoinHandle<()>>,
645}
646
647impl HttpServerHandle {
648 pub fn index_url(&self) -> Url {
649 Url::parse(&format!("sparse+http://{}/index/", self.addr.to_string())).unwrap()
650 }
651
652 pub fn api_url(&self) -> Url {
653 Url::parse(&format!("http://{}/", self.addr.to_string())).unwrap()
654 }
655
656 pub fn dl_url(&self) -> Url {
657 Url::parse(&format!("http://{}/dl", self.addr.to_string())).unwrap()
658 }
659
660 fn stop(&self) {
661 if let Ok(mut stream) = TcpStream::connect(self.addr) {
662 let _ = stream.write_all(b"stop");
664 let _ = stream.flush();
665 }
666 }
667}
668
669impl Drop for HttpServerHandle {
670 fn drop(&mut self) {
671 self.stop();
672 }
673}
674
675#[derive(Clone)]
677pub struct Request {
678 pub url: Url,
679 pub method: String,
680 pub body: Option<Vec<u8>>,
681 pub authorization: Option<String>,
682 pub if_modified_since: Option<String>,
683 pub if_none_match: Option<String>,
684}
685
686impl fmt::Debug for Request {
687 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
688 f.debug_struct("Request")
690 .field("url", &self.url)
691 .field("method", &self.method)
692 .field("authorization", &self.authorization)
693 .field("if_modified_since", &self.if_modified_since)
694 .field("if_none_match", &self.if_none_match)
695 .finish()
696 }
697}
698
699pub struct Response {
701 pub code: u32,
702 pub headers: Vec<String>,
703 pub body: Vec<u8>,
704}
705
706pub struct HttpServer {
707 listener: TcpListener,
708 registry_path: PathBuf,
709 dl_path: PathBuf,
710 api_path: PathBuf,
711 addr: SocketAddr,
712 token: Token,
713 auth_required: bool,
714 custom_responders: HashMap<String, RequestCallback>,
715 not_found_handler: RequestCallback,
716 delayed_index_update: usize,
717}
718
719struct Mutation<'a> {
722 mutation: &'a str,
723 name: Option<&'a str>,
724 vers: Option<&'a str>,
725 cksum: Option<&'a str>,
726}
727
728impl HttpServer {
729 pub fn new(
730 registry_path: PathBuf,
731 dl_path: PathBuf,
732 api_path: PathBuf,
733 token: Token,
734 auth_required: bool,
735 custom_responders: HashMap<String, RequestCallback>,
736 not_found_handler: RequestCallback,
737 delayed_index_update: usize,
738 ) -> HttpServerHandle {
739 let listener = TcpListener::bind("127.0.0.1:0").unwrap();
740 let addr = listener.local_addr().unwrap();
741 let server = HttpServer {
742 listener,
743 registry_path,
744 dl_path,
745 api_path,
746 addr,
747 token,
748 auth_required,
749 custom_responders,
750 not_found_handler,
751 delayed_index_update,
752 };
753 let handle = Some(thread::spawn(move || server.start()));
754 HttpServerHandle { addr, handle }
755 }
756
757 fn start(&self) {
758 let mut line = String::new();
759 'server: loop {
760 let (socket, _) = self.listener.accept().unwrap();
761 let mut buf = BufReader::new(socket);
762 line.clear();
763 if buf.read_line(&mut line).unwrap() == 0 {
764 continue;
766 }
767 let mut parts = line.split_ascii_whitespace();
769 let method = parts.next().unwrap().to_ascii_lowercase();
770 if method == "stop" {
771 return;
773 }
774 let addr = self.listener.local_addr().unwrap();
775 let url = format!(
776 "http://{}/{}",
777 addr,
778 parts.next().unwrap().trim_start_matches('/')
779 );
780 let url = Url::parse(&url).unwrap();
781
782 let mut if_modified_since = None;
784 let mut if_none_match = None;
785 let mut authorization = None;
786 let mut content_len = None;
787 loop {
788 line.clear();
789 if buf.read_line(&mut line).unwrap() == 0 {
790 continue 'server;
791 }
792 if line == "\r\n" {
793 line.clear();
795 break;
796 }
797 let (name, value) = line.split_once(':').unwrap();
798 let name = name.trim().to_ascii_lowercase();
799 let value = value.trim().to_string();
800 match name.as_str() {
801 "if-modified-since" => if_modified_since = Some(value),
802 "if-none-match" => if_none_match = Some(value),
803 "authorization" => authorization = Some(value),
804 "content-length" => content_len = Some(value),
805 _ => {}
806 }
807 }
808
809 let mut body = None;
810 if let Some(con_len) = content_len {
811 let len = con_len.parse::<u64>().unwrap();
812 let mut content = vec![0u8; len as usize];
813 buf.read_exact(&mut content).unwrap();
814 body = Some(content)
815 }
816
817 let req = Request {
818 authorization,
819 if_modified_since,
820 if_none_match,
821 method,
822 url,
823 body,
824 };
825 println!("req: {:#?}", req);
826 let response = self.route(&req);
827 let buf = buf.get_mut();
828 write!(buf, "HTTP/1.1 {}\r\n", response.code).unwrap();
829 write!(buf, "Content-Length: {}\r\n", response.body.len()).unwrap();
830 write!(buf, "Connection: close\r\n").unwrap();
831 for header in response.headers {
832 write!(buf, "{}\r\n", header).unwrap();
833 }
834 write!(buf, "\r\n").unwrap();
835 buf.write_all(&response.body).unwrap();
836 buf.flush().unwrap();
837 }
838 }
839
840 fn check_authorized(&self, req: &Request, mutation: Option<Mutation<'_>>) -> bool {
841 let (private_key, private_key_subject) = if mutation.is_some() || self.auth_required {
842 match &self.token {
843 Token::Plaintext(token) => return Some(token) == req.authorization.as_ref(),
844 Token::Keys(private_key, private_key_subject) => {
845 (private_key.as_str(), private_key_subject)
846 }
847 }
848 } else {
849 assert!(req.authorization.is_none(), "unexpected token");
850 return true;
851 };
852
853 macro_rules! t {
854 ($e:expr) => {
855 match $e {
856 Some(e) => e,
857 None => return false,
858 }
859 };
860 }
861
862 let secret: AsymmetricSecretKey<pasetors::version3::V3> = private_key.try_into().unwrap();
863 let public: AsymmetricPublicKey<pasetors::version3::V3> = (&secret).try_into().unwrap();
864 let pub_key_id: pasetors::paserk::Id = (&public).into();
865 let mut paserk_pub_key_id = String::new();
866 FormatAsPaserk::fmt(&pub_key_id, &mut paserk_pub_key_id).unwrap();
867 let authorization = t!(&req.authorization);
871 let untrusted_token = t!(
872 UntrustedToken::<pasetors::Public, pasetors::version3::V3>::try_from(authorization)
873 .ok()
874 );
875
876 #[derive(serde::Deserialize, Debug)]
878 struct Footer<'a> {
879 url: &'a str,
880 kip: &'a str,
881 }
882 let footer: Footer<'_> =
883 t!(serde_json::from_slice(untrusted_token.untrusted_footer()).ok());
884 if footer.kip != paserk_pub_key_id {
885 return false;
886 }
887 let trusted_token =
888 t!(
889 pasetors::version3::PublicToken::verify(&public, &untrusted_token, None, None,)
890 .ok()
891 );
892
893 if footer.url != "https://github.com/rust-lang/crates.io-index"
895 && footer.url != &format!("sparse+http://{}/index/", self.addr.to_string())
896 {
897 return false;
898 }
899
900 #[derive(serde::Deserialize)]
902 struct Message<'a> {
903 iat: &'a str,
904 sub: Option<&'a str>,
905 mutation: Option<&'a str>,
906 name: Option<&'a str>,
907 vers: Option<&'a str>,
908 cksum: Option<&'a str>,
909 _challenge: Option<&'a str>, v: Option<u8>,
911 }
912 let message: Message<'_> = t!(serde_json::from_str(trusted_token.payload()).ok());
913 let token_time = t!(OffsetDateTime::parse(message.iat, &Rfc3339).ok());
914 let now = OffsetDateTime::now_utc();
915 if (now - token_time) > Duration::MINUTE {
916 return false;
917 }
918 if private_key_subject.as_deref() != message.sub {
919 return false;
920 }
921 if let Some(v) = message.v {
923 if v != 1 {
924 return false;
925 }
926 }
927 if let Some(mutation) = mutation {
931 if message.mutation != Some(mutation.mutation) {
933 return false;
934 }
935 if message.name != mutation.name {
937 return false;
938 }
939 if message.vers != mutation.vers {
940 return false;
941 }
942 if mutation.mutation == "publish" {
944 if message.cksum != mutation.cksum {
945 return false;
946 }
947 }
948 } else {
949 if message.mutation.is_some()
951 || message.name.is_some()
952 || message.vers.is_some()
953 || message.cksum.is_some()
954 {
955 return false;
956 }
957 }
958 true
959 }
960
961 fn route(&self, req: &Request) -> Response {
963 if let Some(responder) = self.custom_responders.get(req.url.path()) {
965 return responder(&req, self);
966 }
967 let path: Vec<_> = req.url.path()[1..].split('/').collect();
968 match (req.method.as_str(), path.as_slice()) {
969 ("get", ["index", ..]) => {
970 if !self.check_authorized(req, None) {
971 self.unauthorized(req)
972 } else {
973 self.index(&req)
974 }
975 }
976 ("get", ["dl", ..]) => {
977 if !self.check_authorized(req, None) {
978 self.unauthorized(req)
979 } else {
980 self.dl(&req)
981 }
982 }
983 ("put", ["api", "v1", "crates", "new"]) => self.check_authorized_publish(req),
985 ("delete" | "put", ["api", "v1", "crates", crate_name, version, mutation]) => {
992 if !self.check_authorized(
993 req,
994 Some(Mutation {
995 mutation,
996 name: Some(crate_name),
997 vers: Some(version),
998 cksum: None,
999 }),
1000 ) {
1001 self.unauthorized(req)
1002 } else {
1003 self.ok(&req)
1004 }
1005 }
1006 ("get" | "put" | "delete", ["api", "v1", "crates", crate_name, "owners"]) => {
1008 if !self.check_authorized(
1009 req,
1010 Some(Mutation {
1011 mutation: "owners",
1012 name: Some(crate_name),
1013 vers: None,
1014 cksum: None,
1015 }),
1016 ) {
1017 self.unauthorized(req)
1018 } else {
1019 self.ok(&req)
1020 }
1021 }
1022 _ => self.not_found(&req),
1023 }
1024 }
1025
1026 pub fn unauthorized(&self, _req: &Request) -> Response {
1028 Response {
1029 code: 401,
1030 headers: vec![
1031 r#"WWW-Authenticate: Cargo login_url="https://test-registry-login/me""#.to_string(),
1032 ],
1033 body: b"Unauthorized message from server.".to_vec(),
1034 }
1035 }
1036
1037 pub fn not_found(&self, req: &Request) -> Response {
1039 (self.not_found_handler)(req, self)
1040 }
1041
1042 pub fn ok(&self, _req: &Request) -> Response {
1044 Response {
1045 code: 200,
1046 headers: vec![],
1047 body: br#"{"ok": true, "msg": "completed!"}"#.to_vec(),
1048 }
1049 }
1050
1051 pub fn internal_server_error(&self, _req: &Request) -> Response {
1053 Response {
1054 code: 500,
1055 headers: vec![],
1056 body: br#"internal server error"#.to_vec(),
1057 }
1058 }
1059
1060 pub fn too_many_requests(&self, _req: &Request, delay: std::time::Duration) -> Response {
1062 Response {
1063 code: 429,
1064 headers: vec![format!("Retry-After: {}", delay.as_secs())],
1065 body: format!(
1066 "too many requests, try again in {} seconds",
1067 delay.as_secs()
1068 )
1069 .into_bytes(),
1070 }
1071 }
1072
1073 pub fn dl(&self, req: &Request) -> Response {
1075 let file = self
1076 .dl_path
1077 .join(req.url.path().strip_prefix("/dl/").unwrap());
1078 println!("{}", file.display());
1079 if !file.exists() {
1080 return self.not_found(req);
1081 }
1082 return Response {
1083 body: fs::read(&file).unwrap(),
1084 code: 200,
1085 headers: vec![],
1086 };
1087 }
1088
1089 pub fn index(&self, req: &Request) -> Response {
1091 let file = self
1092 .registry_path
1093 .join(req.url.path().strip_prefix("/index/").unwrap());
1094 if !file.exists() {
1095 return self.not_found(req);
1096 } else {
1097 let data = fs::read(&file).unwrap();
1099 let etag = Sha256::new().update(&data).finish_hex();
1100 let last_modified = format!("{:?}", file.metadata().unwrap().modified().unwrap());
1101
1102 let mut any_match = false;
1104 let mut all_match = true;
1105 if let Some(expected) = &req.if_none_match {
1106 if &etag != expected {
1107 all_match = false;
1108 } else {
1109 any_match = true;
1110 }
1111 }
1112 if let Some(expected) = &req.if_modified_since {
1113 if &last_modified != expected {
1115 all_match = false;
1116 } else {
1117 any_match = true;
1118 }
1119 }
1120
1121 if any_match && all_match {
1122 return Response {
1123 body: Vec::new(),
1124 code: 304,
1125 headers: vec![],
1126 };
1127 } else {
1128 return Response {
1129 body: data,
1130 code: 200,
1131 headers: vec![
1132 format!("ETag: \"{}\"", etag),
1133 format!("Last-Modified: {}", last_modified),
1134 ],
1135 };
1136 }
1137 }
1138 }
1139
1140 pub fn check_authorized_publish(&self, req: &Request) -> Response {
1141 if let Some(body) = &req.body {
1142 let path = self.api_path.join("api/v1/crates/new");
1145 t!(fs::create_dir_all(path.parent().unwrap()));
1146 t!(fs::write(&path, body));
1147
1148 let (len, remaining) = body.split_at(4);
1150 let json_len = u32::from_le_bytes(len.try_into().unwrap());
1151 let (json, remaining) = remaining.split_at(json_len as usize);
1152 let new_crate = serde_json::from_slice::<crates_io::NewCrate>(json).unwrap();
1153 let (len, remaining) = remaining.split_at(4);
1155 let file_len = u32::from_le_bytes(len.try_into().unwrap());
1156 let (file, _remaining) = remaining.split_at(file_len as usize);
1157 let file_cksum = cksum(&file);
1158
1159 if !self.check_authorized(
1160 req,
1161 Some(Mutation {
1162 mutation: "publish",
1163 name: Some(&new_crate.name),
1164 vers: Some(&new_crate.vers),
1165 cksum: Some(&file_cksum),
1166 }),
1167 ) {
1168 return self.unauthorized(req);
1169 }
1170
1171 let dst = self
1172 .dl_path
1173 .join(&new_crate.name)
1174 .join(&new_crate.vers)
1175 .join("download");
1176
1177 if self.delayed_index_update == 0 {
1178 save_new_crate(dst, new_crate, file, file_cksum, &self.registry_path);
1179 } else {
1180 let delayed_index_update = self.delayed_index_update;
1181 let registry_path = self.registry_path.clone();
1182 let file = Vec::from(file);
1183 thread::spawn(move || {
1184 thread::sleep(std::time::Duration::new(delayed_index_update as u64, 0));
1185 save_new_crate(dst, new_crate, &file, file_cksum, ®istry_path);
1186 });
1187 }
1188
1189 self.ok(&req)
1190 } else {
1191 Response {
1192 code: 400,
1193 headers: vec![],
1194 body: b"The request was missing a body".to_vec(),
1195 }
1196 }
1197 }
1198}
1199
1200fn save_new_crate(
1201 dst: PathBuf,
1202 new_crate: crates_io::NewCrate,
1203 file: &[u8],
1204 file_cksum: String,
1205 registry_path: &Path,
1206) {
1207 t!(fs::create_dir_all(dst.parent().unwrap()));
1209 t!(fs::write(&dst, file));
1210
1211 let deps = new_crate
1212 .deps
1213 .iter()
1214 .map(|dep| {
1215 let (name, package) = match &dep.explicit_name_in_toml {
1216 Some(explicit) => (explicit.to_string(), Some(dep.name.to_string())),
1217 None => (dep.name.to_string(), None),
1218 };
1219 serde_json::json!({
1220 "name": name,
1221 "req": dep.version_req,
1222 "features": dep.features,
1223 "default_features": dep.default_features,
1224 "target": dep.target,
1225 "optional": dep.optional,
1226 "kind": dep.kind,
1227 "registry": dep.registry,
1228 "package": package,
1229 "artifact": dep.artifact,
1230 "bindep_target": dep.bindep_target,
1231 "lib": dep.lib,
1232 })
1233 })
1234 .collect::<Vec<_>>();
1235
1236 let line = create_index_line(
1237 serde_json::json!(new_crate.name),
1238 &new_crate.vers,
1239 deps,
1240 &file_cksum,
1241 new_crate.features,
1242 false,
1243 new_crate.links,
1244 new_crate.rust_version.as_deref(),
1245 None,
1246 );
1247
1248 write_to_index(registry_path, &new_crate.name, line, false);
1249}
1250
1251impl Package {
1252 pub fn new(name: &str, vers: &str) -> Package {
1255 let config = paths::cargo_home().join("config.toml");
1256 if !config.exists() {
1257 init();
1258 }
1259 Package {
1260 name: name.to_string(),
1261 vers: vers.to_string(),
1262 deps: Vec::new(),
1263 files: Vec::new(),
1264 yanked: false,
1265 features: BTreeMap::new(),
1266 local: false,
1267 alternative: false,
1268 invalid_index_line: false,
1269 index_line: None,
1270 edition: None,
1271 resolver: None,
1272 proc_macro: false,
1273 links: None,
1274 rust_version: None,
1275 cargo_features: Vec::new(),
1276 v: None,
1277 }
1278 }
1279
1280 pub fn local(&mut self, local: bool) -> &mut Package {
1286 self.local = local;
1287 self
1288 }
1289
1290 pub fn alternative(&mut self, alternative: bool) -> &mut Package {
1300 self.alternative = alternative;
1301 self
1302 }
1303
1304 pub fn file(&mut self, name: &str, contents: &str) -> &mut Package {
1306 self.file_with_mode(name, DEFAULT_MODE, contents)
1307 }
1308
1309 pub fn file_with_mode(&mut self, path: &str, mode: u32, contents: &str) -> &mut Package {
1311 self.files.push(PackageFile {
1312 path: path.to_string(),
1313 contents: EntryData::Regular(contents.into()),
1314 mode,
1315 extra: false,
1316 });
1317 self
1318 }
1319
1320 pub fn symlink(&mut self, dst: &str, src: &str) -> &mut Package {
1322 self.files.push(PackageFile {
1323 path: dst.to_string(),
1324 contents: EntryData::Symlink(src.into()),
1325 mode: DEFAULT_MODE,
1326 extra: false,
1327 });
1328 self
1329 }
1330
1331 pub fn extra_file(&mut self, path: &str, contents: &str) -> &mut Package {
1337 self.files.push(PackageFile {
1338 path: path.to_string(),
1339 contents: EntryData::Regular(contents.to_string()),
1340 mode: DEFAULT_MODE,
1341 extra: true,
1342 });
1343 self
1344 }
1345
1346 pub fn dep(&mut self, name: &str, vers: &str) -> &mut Package {
1352 self.add_dep(&Dependency::new(name, vers))
1353 }
1354
1355 pub fn feature_dep(&mut self, name: &str, vers: &str, features: &[&str]) -> &mut Package {
1361 self.add_dep(Dependency::new(name, vers).enable_features(features))
1362 }
1363
1364 pub fn target_dep(&mut self, name: &str, vers: &str, target: &str) -> &mut Package {
1370 self.add_dep(Dependency::new(name, vers).target(target))
1371 }
1372
1373 pub fn registry_dep(&mut self, name: &str, vers: &str) -> &mut Package {
1375 self.add_dep(Dependency::new(name, vers).registry("alternative"))
1376 }
1377
1378 pub fn dev_dep(&mut self, name: &str, vers: &str) -> &mut Package {
1384 self.add_dep(Dependency::new(name, vers).dev())
1385 }
1386
1387 pub fn build_dep(&mut self, name: &str, vers: &str) -> &mut Package {
1393 self.add_dep(Dependency::new(name, vers).build())
1394 }
1395
1396 pub fn add_dep(&mut self, dep: &Dependency) -> &mut Package {
1397 self.deps.push(dep.clone());
1398 self
1399 }
1400
1401 pub fn yanked(&mut self, yanked: bool) -> &mut Package {
1403 self.yanked = yanked;
1404 self
1405 }
1406
1407 pub fn edition(&mut self, edition: &str) -> &mut Package {
1409 self.edition = Some(edition.to_owned());
1410 self
1411 }
1412
1413 pub fn resolver(&mut self, resolver: &str) -> &mut Package {
1415 self.resolver = Some(resolver.to_owned());
1416 self
1417 }
1418
1419 pub fn proc_macro(&mut self, proc_macro: bool) -> &mut Package {
1421 self.proc_macro = proc_macro;
1422 self
1423 }
1424
1425 pub fn feature(&mut self, name: &str, deps: &[&str]) -> &mut Package {
1427 let deps = deps.iter().map(|s| s.to_string()).collect();
1428 self.features.insert(name.to_string(), deps);
1429 self
1430 }
1431
1432 pub fn rust_version(&mut self, rust_version: &str) -> &mut Package {
1434 self.rust_version = Some(rust_version.into());
1435 self
1436 }
1437
1438 pub fn invalid_index_line(&mut self, invalid: bool) -> &mut Package {
1441 self.invalid_index_line = invalid;
1442 self
1443 }
1444
1445 pub fn index_line(&mut self, line: &str) -> &mut Package {
1449 self.index_line = Some(line.to_owned());
1450 self
1451 }
1452
1453 pub fn links(&mut self, links: &str) -> &mut Package {
1454 self.links = Some(links.to_string());
1455 self
1456 }
1457
1458 pub fn cargo_feature(&mut self, feature: &str) -> &mut Package {
1459 self.cargo_features.push(feature.to_owned());
1460 self
1461 }
1462
1463 pub fn schema_version(&mut self, version: u32) -> &mut Package {
1467 self.v = Some(version);
1468 self
1469 }
1470
1471 pub fn publish(&self) -> String {
1478 self.make_archive();
1479
1480 let deps = self
1482 .deps
1483 .iter()
1484 .map(|dep| {
1485 let registry_url = match (self.alternative, dep.registry.as_deref()) {
1488 (false, None) => None,
1489 (false, Some("alternative")) => Some(alt_registry_url().to_string()),
1490 (true, None) => {
1491 Some("https://github.com/rust-lang/crates.io-index".to_string())
1492 }
1493 (true, Some("alternative")) => None,
1494 _ => panic!("registry_dep currently only supports `alternative`"),
1495 };
1496 let artifact = if let Some(artifact) = &dep.artifact {
1497 serde_json::json!([artifact])
1498 } else {
1499 serde_json::json!(null)
1500 };
1501 serde_json::json!({
1502 "name": dep.name,
1503 "req": dep.vers,
1504 "features": dep.features,
1505 "default_features": dep.default_features,
1506 "target": dep.target,
1507 "artifact": artifact,
1508 "bindep_target": dep.bindep_target,
1509 "lib": dep.lib,
1510 "optional": dep.optional,
1511 "kind": dep.kind,
1512 "registry": registry_url,
1513 "package": dep.package,
1514 "public": dep.public,
1515 })
1516 })
1517 .collect::<Vec<_>>();
1518 let cksum = {
1519 let c = t!(fs::read(&self.archive_dst()));
1520 cksum(&c)
1521 };
1522 let line = if let Some(line) = self.index_line.clone() {
1523 line
1524 } else {
1525 let name = if self.invalid_index_line {
1526 serde_json::json!(1)
1527 } else {
1528 serde_json::json!(self.name)
1529 };
1530 create_index_line(
1531 name,
1532 &self.vers,
1533 deps,
1534 &cksum,
1535 self.features.clone(),
1536 self.yanked,
1537 self.links.clone(),
1538 self.rust_version.as_deref(),
1539 self.v,
1540 )
1541 };
1542
1543 let registry_path = if self.alternative {
1544 alt_registry_path()
1545 } else {
1546 registry_path()
1547 };
1548
1549 write_to_index(®istry_path, &self.name, line, self.local);
1550
1551 cksum
1552 }
1553
1554 fn make_archive(&self) {
1555 let dst = self.archive_dst();
1556 t!(fs::create_dir_all(dst.parent().unwrap()));
1557 let f = t!(File::create(&dst));
1558 let mut a = Builder::new(GzEncoder::new(f, Compression::none()));
1559 a.sparse(false);
1560
1561 if !self
1562 .files
1563 .iter()
1564 .any(|PackageFile { path, .. }| path == "Cargo.toml")
1565 {
1566 self.append_manifest(&mut a);
1567 }
1568 if self.files.is_empty() {
1569 self.append(
1570 &mut a,
1571 "src/lib.rs",
1572 DEFAULT_MODE,
1573 &EntryData::Regular("".into()),
1574 );
1575 } else {
1576 for PackageFile {
1577 path,
1578 contents,
1579 mode,
1580 extra,
1581 } in &self.files
1582 {
1583 if *extra {
1584 self.append_raw(&mut a, path, *mode, contents);
1585 } else {
1586 self.append(&mut a, path, *mode, contents);
1587 }
1588 }
1589 }
1590 }
1591
1592 fn append_manifest<W: Write>(&self, ar: &mut Builder<W>) {
1593 let mut manifest = String::new();
1594
1595 if !self.cargo_features.is_empty() {
1596 let mut features = String::new();
1597 serde::Serialize::serialize(
1598 &self.cargo_features,
1599 toml::ser::ValueSerializer::new(&mut features),
1600 )
1601 .unwrap();
1602 manifest.push_str(&format!("cargo-features = {}\n\n", features));
1603 }
1604
1605 manifest.push_str(&format!(
1606 r#"
1607 [package]
1608 name = "{}"
1609 version = "{}"
1610 authors = []
1611 "#,
1612 self.name, self.vers
1613 ));
1614
1615 if let Some(version) = &self.rust_version {
1616 manifest.push_str(&format!("rust-version = \"{}\"\n", version));
1617 }
1618
1619 if let Some(edition) = &self.edition {
1620 manifest.push_str(&format!("edition = \"{}\"\n", edition));
1621 }
1622
1623 if let Some(resolver) = &self.resolver {
1624 manifest.push_str(&format!("resolver = \"{}\"\n", resolver));
1625 }
1626
1627 if !self.features.is_empty() {
1628 let features: Vec<String> = self
1629 .features
1630 .iter()
1631 .map(|(feature, features)| {
1632 if features.is_empty() {
1633 format!("{} = []", feature)
1634 } else {
1635 format!(
1636 "{} = [{}]",
1637 feature,
1638 features
1639 .iter()
1640 .map(|s| format!("\"{}\"", s))
1641 .collect::<Vec<_>>()
1642 .join(", ")
1643 )
1644 }
1645 })
1646 .collect();
1647
1648 manifest.push_str(&format!("\n[features]\n{}", features.join("\n")));
1649 }
1650
1651 for dep in self.deps.iter() {
1652 let target = match dep.target {
1653 None => String::new(),
1654 Some(ref s) => format!("target.'{}'.", s),
1655 };
1656 let kind = match &dep.kind[..] {
1657 "build" => "build-",
1658 "dev" => "dev-",
1659 _ => "",
1660 };
1661 manifest.push_str(&format!(
1662 r#"
1663 [{}{}dependencies.{}]
1664 version = "{}"
1665 "#,
1666 target, kind, dep.name, dep.vers
1667 ));
1668 if dep.optional {
1669 manifest.push_str("optional = true\n");
1670 }
1671 if let Some(artifact) = &dep.artifact {
1672 manifest.push_str(&format!("artifact = \"{}\"\n", artifact));
1673 }
1674 if let Some(target) = &dep.bindep_target {
1675 manifest.push_str(&format!("target = \"{}\"\n", target));
1676 }
1677 if dep.lib {
1678 manifest.push_str("lib = true\n");
1679 }
1680 if let Some(registry) = &dep.registry {
1681 assert_eq!(registry, "alternative");
1682 manifest.push_str(&format!("registry-index = \"{}\"", alt_registry_url()));
1683 }
1684 if !dep.default_features {
1685 manifest.push_str("default-features = false\n");
1686 }
1687 if !dep.features.is_empty() {
1688 let mut features = String::new();
1689 serde::Serialize::serialize(
1690 &dep.features,
1691 toml::ser::ValueSerializer::new(&mut features),
1692 )
1693 .unwrap();
1694 manifest.push_str(&format!("features = {}\n", features));
1695 }
1696 if let Some(package) = &dep.package {
1697 manifest.push_str(&format!("package = \"{}\"\n", package));
1698 }
1699 }
1700 if self.proc_macro {
1701 manifest.push_str("[lib]\nproc-macro = true\n");
1702 }
1703
1704 self.append(
1705 ar,
1706 "Cargo.toml",
1707 DEFAULT_MODE,
1708 &EntryData::Regular(manifest.into()),
1709 );
1710 }
1711
1712 fn append<W: Write>(&self, ar: &mut Builder<W>, file: &str, mode: u32, contents: &EntryData) {
1713 self.append_raw(
1714 ar,
1715 &format!("{}-{}/{}", self.name, self.vers, file),
1716 mode,
1717 contents,
1718 );
1719 }
1720
1721 fn append_raw<W: Write>(
1722 &self,
1723 ar: &mut Builder<W>,
1724 path: &str,
1725 mode: u32,
1726 contents: &EntryData,
1727 ) {
1728 let mut header = Header::new_ustar();
1729 let contents = match contents {
1730 EntryData::Regular(contents) => contents.as_str(),
1731 EntryData::Symlink(src) => {
1732 header.set_entry_type(tar::EntryType::Symlink);
1733 t!(header.set_link_name(src));
1734 "" }
1736 };
1737 header.set_size(contents.len() as u64);
1738 t!(header.set_path(path));
1739 header.set_mode(mode);
1740 header.set_cksum();
1741 t!(ar.append(&header, contents.as_bytes()));
1742 }
1743
1744 pub fn archive_dst(&self) -> PathBuf {
1746 if self.local {
1747 let path = if self.alternative {
1748 alt_registry_path()
1749 } else {
1750 registry_path()
1751 };
1752 path.join(format!("{}-{}.crate", self.name, self.vers))
1753 } else if self.alternative {
1754 alt_dl_path()
1755 .join(&self.name)
1756 .join(&self.vers)
1757 .join("download")
1758 } else {
1759 dl_path().join(&self.name).join(&self.vers).join("download")
1760 }
1761 }
1762}
1763
1764pub fn cksum(s: &[u8]) -> String {
1766 Sha256::new().update(s).finish_hex()
1767}
1768
1769impl Dependency {
1770 pub fn new(name: &str, vers: &str) -> Dependency {
1771 Dependency {
1772 name: name.to_string(),
1773 vers: vers.to_string(),
1774 kind: "normal".to_string(),
1775 artifact: None,
1776 bindep_target: None,
1777 lib: false,
1778 target: None,
1779 features: Vec::new(),
1780 package: None,
1781 optional: false,
1782 registry: None,
1783 default_features: true,
1784 public: false,
1785 }
1786 }
1787
1788 pub fn build(&mut self) -> &mut Self {
1790 self.kind = "build".to_string();
1791 self
1792 }
1793
1794 pub fn dev(&mut self) -> &mut Self {
1796 self.kind = "dev".to_string();
1797 self
1798 }
1799
1800 pub fn target(&mut self, target: &str) -> &mut Self {
1802 self.target = Some(target.to_string());
1803 self
1804 }
1805
1806 pub fn artifact(&mut self, kind: &str, target: Option<String>) -> &mut Self {
1809 self.artifact = Some(kind.to_string());
1810 self.bindep_target = target;
1811 self
1812 }
1813
1814 pub fn registry(&mut self, registry: &str) -> &mut Self {
1816 self.registry = Some(registry.to_string());
1817 self
1818 }
1819
1820 pub fn enable_features(&mut self, features: &[&str]) -> &mut Self {
1822 self.features.extend(features.iter().map(|s| s.to_string()));
1823 self
1824 }
1825
1826 pub fn package(&mut self, pkg: &str) -> &mut Self {
1828 self.package = Some(pkg.to_string());
1829 self
1830 }
1831
1832 pub fn optional(&mut self, optional: bool) -> &mut Self {
1834 self.optional = optional;
1835 self
1836 }
1837
1838 pub fn public(&mut self, public: bool) -> &mut Self {
1840 self.public = public;
1841 self
1842 }
1843
1844 pub fn default_features(&mut self, default_features: bool) -> &mut Self {
1846 self.default_features = default_features;
1847 self
1848 }
1849}