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