diff --git a/.github/workflows/taskcluster.yml b/.github/workflows/taskcluster.yml index ecc9d5c19..1ffb748dd 100644 --- a/.github/workflows/taskcluster.yml +++ b/.github/workflows/taskcluster.yml @@ -9,7 +9,6 @@ jobs: PATH: /usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Library/Frameworks/Python.framework/Versions/2.7/bin steps: - uses: actions/checkout@v3 - - run: sudo -H python2.7 -m pip install pip==20.3.4 wheel==0.37.0 --upgrade - uses: actions/setup-python@v4 with: python-version: '3.9.14' diff --git a/CI/decision.py b/CI/decision.py index 1c4919c72..555acf53e 100644 --- a/CI/decision.py +++ b/CI/decision.py @@ -116,9 +116,13 @@ def __init__(self, **kwargs): if env.os == 'macos': output_sync = '' kwargs['command'] = command + [ + 'hg init repo/hg.pure.hg', + 'hg -R repo/hg.pure.hg unbundle bundle.hg', 'make -C repo -f CI/tests.mk -j$({}){}' .format(nproc(env), output_sync), ] + kwargs.setdefault('mounts', []).append( + {'file:bundle.hg': HgClone.by_name(MERCURIAL_VERSION)}) if variant == 'coverage': kwargs['command'].extend([ @@ -199,6 +203,35 @@ def __init__(self, version): ) +class HgClone(Task, metaclass=Tool): + PREFIX = "hgclone" + + def __init__(self, version): + if REPO == DEFAULT_REPO: + index = 'hgclone.{}'.format(version) + else: + index = 'hgclone.{}.{}'.format( + hashlib.sha1(REPO).hexdigest(), version) + hg_task = Hg.by_name(f'linux.{version}') + Task.__init__( + self, + task_env=TaskEnvironment.by_name('linux.test'), + description=f'hg clone w/ {version}', + index=index, + expireIn='26 weeks', + command=hg_task.install() + [ + 'hg clone -U --stream $REPO repo', + 'hg -R repo bundle -t none-v1 -a $ARTIFACTS/bundle.hg', + ], + mounts=[hg_task.mount()], + artifact='bundle.hg', + env={ + 'REPO': REPO, + }, + priority='high', + ) + + @action('decision') def decision(): for env in ('linux', 'mingw64', 'osx'): diff --git a/CI/msys.py b/CI/msys.py index 508eb7dba..11e270811 100644 --- a/CI/msys.py +++ b/CI/msys.py @@ -107,11 +107,12 @@ def __init__(self, name): create_commands = [ 'pacman-key --init', 'pacman-key --populate msys2', + 'pacman-key --refresh', 'pacman --noconfirm -Sy procps tar {}'.format( ' '.join(self.packages(name))), 'pkill gpg-agent', + 'pkill dirmngr', 'rm -rf /var/cache/pacman/pkg', - 'python2.7 -m pip install pip==20.3.4 wheel==0.37.1 --upgrade', 'python3 -m pip install pip==22.2.2 wheel==0.37.1 --upgrade', 'mv {}/{}/bin/{{mingw32-,}}make.exe'.format(msys(cpu), mingw(cpu)), 'tar -c --hard-dereference {} | zstd -c > msys2.tar.zst'.format( @@ -144,8 +145,6 @@ def mingw_packages(pkgs): packages = mingw_packages([ 'curl', 'make', - 'python2', - 'python2-pip', 'python3', 'python3-pip', ]) diff --git a/CI/start-worker.sh b/CI/start-worker.sh index b014d81f3..27d5eec57 100755 --- a/CI/start-worker.sh +++ b/CI/start-worker.sh @@ -40,7 +40,7 @@ EOF env -curl -OL https://github.com/taskcluster/taskcluster/releases/download/v54.1.2/generic-worker-simple-darwin-amd64 +curl -OL https://github.com/taskcluster/taskcluster/releases/download/v54.4.0/generic-worker-simple-darwin-amd64 chmod +x generic-worker-simple-darwin-amd64 mkdir tasks ./generic-worker-simple-darwin-amd64 new-ed25519-keypair --file worker.key diff --git a/CI/tests.mk b/CI/tests.mk index 73ce11aae..aa8517e0e 100644 --- a/CI/tests.mk +++ b/CI/tests.mk @@ -157,7 +157,7 @@ hg.clonebundles-full.git: hg.clonebundles-full.hg hg.git hg.clonebundles-bz2.git: hg.clonebundles-bz2.hg hg.git hg.clonebundles-full-bz2.git: hg.clonebundles-full-bz2.hg hg.git hg.clonebundles.git hg.clonebundles-full.git hg.clonebundles-bz2.git hg.clonebundles-full-bz2.git: - $(HG) -R $< --config serve.other=http --config serve.otherport=88$(NUM) --config web.port=80$(NUM) --config extensions.clonebundles= --config extensions.x=$(TOPDIR)/CI/hg-serve-exec.py serve-and-exec -- $(GIT) clone --progress -n hg://localhost:80$(NUM).http/ $@ + $(HG) -R $< --config serve.other=http --config serve.otherport=88$(NUM) --config web.port=80$(NUM) --config experimental.httppostargs=true --config extensions.clonebundles= --config extensions.x=$(TOPDIR)/CI/hg-serve-exec.py serve-and-exec -- $(GIT) clone --progress -n hg://localhost:80$(NUM).http/ $@ $(call COMPARE_REFS, $(word 2,$^), $@) hg.pure.git: hg.git @@ -179,7 +179,7 @@ hg.http.hg.gitcredentials hg.http.hg.nobundle2.gitcredentials: hg.http.hg hg.http.hg.nobundle2: %: %.gitcredentials hg.git $(call HG_INIT, $@) - $(HG) -R $@ --config extensions.x=$(TOPDIR)/CI/hg-serve-exec.py --config web.port=80$(NUM) serve-and-exec -- $(GIT) -c credential.helper='store --file=$(CURDIR)/$@.gitcredentials' -c cinnabar.data=never -C $(word 2,$^) push hg://localhost:80$(NUM).http/ refs/remotes/origin/*:refs/heads/* + $(HG) -R $@ --config experimental.httppostargs=true --config extensions.x=$(TOPDIR)/CI/hg-serve-exec.py --config web.port=80$(NUM) serve-and-exec -- $(GIT) -c credential.helper='store --file=$(CURDIR)/$@.gitcredentials' -c cinnabar.data=never -C $(word 2,$^) push hg://localhost:80$(NUM).http/ refs/remotes/origin/*:refs/heads/* hg.incr.base.git: hg.incr.hg $(HG) clone -U $< $@.hg @@ -213,7 +213,7 @@ hg.cinnabarclone-bundle.git hg.cinnabarclone-bundle-full.git hg.cinnabarclone-gr hg.cinnabarclone.git hg.cinnabarclone-full.git hg.cinnabarclone-bundle.git hg.cinnabarclone-bundle-full.git hg.cinnabarclone-graft.git hg.cinnabarclone-graft-replace.git: hg.pure.hg $(HG) clone -U $< $@.hg echo http://localhost:88$(NUM)/$(word 2,$^) | tee $@.hg/.hg/cinnabar.manifest - $(HG) -R $@.hg --config web.port=80$(NUM) --config serve.other=$(OTHER_SERVER) --config serve.otherport=88$(NUM) --config extensions.x=$(TOPDIR)/CI/hg-serve-exec.py --config extensions.cinnabarclone=$(HG_CINNABARCLONE_EXT) serve-and-exec -- $(GIT) clone --progress hg://localhost:80$(NUM).http/ $@ + $(HG) -R $@.hg --config web.port=80$(NUM) --config serve.other=$(OTHER_SERVER) --config serve.otherport=88$(NUM) --config experimental.httppostargs=true --config extensions.x=$(TOPDIR)/CI/hg-serve-exec.py --config extensions.cinnabarclone=$(HG_CINNABARCLONE_EXT) serve-and-exec -- $(GIT) clone --progress hg://localhost:80$(NUM).http/ $@ $(call COMPARE_REFS, $(or $(word 3,$^),$(word 2,$^)), $@) $(GIT) -C $@ cinnabar fsck $(GIT) -C $@ cinnabar fsck --full @@ -224,7 +224,7 @@ hg.cinnabarclone-graft-bundle.git: hg.pure.hg $(GIT) -C $@ cinnabar rollback 0000000000000000000000000000000000000000 $(GIT) -C $@ remote rename origin grafted (echo http://localhost:88$(NUM)/$(word 2,$^); echo http://localhost:88$(NUM)/$(word 4,$^) graft=$$($(GIT) ls-remote $(CURDIR)/$(word 4,$^) refs/cinnabar/replace/* | awk -F/ '{print $$NF}')) | tee $@.hg/.hg/cinnabar.manifest - $(HG) -R $@.hg --config serve.other=http --config serve.otherport=88$(NUM) --config web.port=80$(NUM) --config extensions.x=$(TOPDIR)/CI/hg-serve-exec.py --config extensions.cinnabarclone=$(HG_CINNABARCLONE_EXT) serve-and-exec -- $(GIT) -c cinnabar.graft=true -C $@ fetch --progress hg://localhost:80$(NUM).http/ refs/heads/*:refs/remotes/origin/* + $(HG) -R $@.hg --config serve.other=http --config serve.otherport=88$(NUM) --config web.port=80$(NUM) --config experimental.httppostargs=true --config extensions.x=$(TOPDIR)/CI/hg-serve-exec.py --config extensions.cinnabarclone=$(HG_CINNABARCLONE_EXT) serve-and-exec -- $(GIT) -c cinnabar.graft=true -C $@ fetch --progress hg://localhost:80$(NUM).http/ refs/heads/*:refs/remotes/origin/* $(call COMPARE_REFS, $(or $(word 3,$^),$(word 2,$^)), $@) $(GIT) -C $@ cinnabar fsck $(GIT) -C $@ cinnabar fsck --full @@ -262,8 +262,9 @@ hg.graft.replace.git: hg.graft.base.git hg.upgraded.git $(call COMPARE_COMMANDS,$(call GET_ROOTS,$(word 2,$^),--remotes),$(call GET_ROOTS,$@,--glob=refs/cinnabar/replace)) $(GIT) -C $@ cinnabar fsck --full -hg.graft.cinnabar.git: hg.upgraded.git +hg.graft.cinnabar.git: hg.upgraded.git hg.pure.hg cp -r $< $@ + $(GIT) -C $@ remote set-url origin hg::$(PATH_URL)/$(word 2,$^) $(GIT) -C $@ -c cinnabar.graft=true cinnabar reclone $(call COMPARE_REFS, $<, $@, XARGS_GIT2HG) test $$($(GIT) -C $@ for-each-ref refs/cinnabar/replace | wc -l) -eq 0 diff --git a/Cargo.lock b/Cargo.lock index 7182ae8fd..619681743 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -362,7 +362,7 @@ checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" [[package]] name = "git-cinnabar" -version = "0.6.2" +version = "0.6.3" dependencies = [ "all_asserts", "array-init", diff --git a/Cargo.toml b/Cargo.toml index 8da55229a..0c2b3425a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ [package] name = "git-cinnabar" -version = "0.6.2" +version = "0.6.3" description = "git remote helper to interact with mercurial repositories" authors = ["Mike Hommey "] edition = "2021" diff --git a/download.py b/download.py index 1bfd44409..0b9da4bbc 100755 --- a/download.py +++ b/download.py @@ -366,10 +366,14 @@ def main(args): help='only print the download url') pgroup = parser.add_mutually_exclusive_group() pgroup.add_argument('--branch', metavar='BRANCH', + default=os.environ.get("GIT_CINNABAR_DOWNLOAD_BRANCH"), help='download a build for the given branch') pgroup.add_argument('--exact', metavar='EXACT', + default=os.environ.get("GIT_CINNABAR_DOWNLOAD_EXACT"), help='download a build for the given commit') parser.add_argument('--variant', metavar='VARIANT', + default=os.environ.get( + "GIT_CINNABAR_DOWNLOAD_VARIANT"), help='download the given variant') parser.add_argument('--system', default=platform.system(), help=argparse.SUPPRESS) diff --git a/src/cinnabar-fast-import.c b/src/cinnabar-fast-import.c index 85722c04c..4a80e7406 100644 --- a/src/cinnabar-fast-import.c +++ b/src/cinnabar-fast-import.c @@ -1014,7 +1014,7 @@ const struct object_id *ensure_empty_tree(void) { if (!oe) { struct object_id hash; struct strbuf buf = STRBUF_INIT; - store_object(OBJ_BLOB, &buf, NULL, &hash, 0); + store_object(OBJ_TREE, &buf, NULL, &hash, 0); assert(oidcmp(&hash, &empty_tree) == 0); } return &empty_tree; diff --git a/src/hg_connect_http.rs b/src/hg_connect_http.rs index c0339d20f..35bf59b40 100644 --- a/src/hg_connect_http.rs +++ b/src/hg_connect_http.rs @@ -4,6 +4,7 @@ use std::borrow::ToOwned; use std::cmp; +use std::convert::AsRef; use std::ffi::{c_void, CStr, CString, OsStr}; use std::fs::File; use std::io::{self, copy, stderr, Cursor, Read, Seek, SeekFrom, Write}; @@ -31,7 +32,7 @@ use curl_sys::{ use either::Either; use flate2::read::ZlibDecoder; use itertools::Itertools; -use once_cell::sync::Lazy; +use once_cell::sync::{Lazy, OnceCell}; use url::{form_urlencoded, Url}; use zstd::stream::read::Decoder as ZstdDecoder; @@ -45,10 +46,12 @@ use crate::libgit::{ credential_fill, curl_errorstr, get_active_slot, http_auth, http_follow_config, run_one_slot, slot_results, ssl_cainfo, HTTP_OK, HTTP_REAUTH, }; -use crate::util::{ImmutBString, OsStrExt, PrefixWriter, ReadExt, SeekExt, SliceExt, ToBoxed}; +use crate::util::{ImmutBString, OsStrExt, PrefixWriter, ReadExt, SliceExt, ToBoxed}; use self::git_http_state::{GitHttpStateToken, GIT_HTTP_STATE}; +pub static CURL_GLOBAL_INIT: OnceCell<()> = OnceCell::new(); + mod git_http_state { use std::{ffi::CString, ptr, sync::Mutex}; @@ -81,6 +84,13 @@ mod git_http_state { match &self.url { Some(url) if url == &normalized_url => {} _ => { + super::CURL_GLOBAL_INIT.get_or_init(|| { + if unsafe { curl_sys::curl_global_init(curl_sys::CURL_GLOBAL_ALL) } + != curl_sys::CURLE_OK + { + die!("curl_global_init failed"); + } + }); let c_url = CString::new(normalized_url.to_string()).unwrap(); unsafe { if self.url.is_some() { @@ -130,14 +140,141 @@ pub struct HgHttpConnection { * The command results are simply the corresponding HTTP responses. */ -trait ReadAndSeek: Read + Seek {} +/// A `Read` that knows its exact length and can seek to its beginning. +trait ExactSizeReadRewind: Read { + fn len(&self) -> io::Result; + + fn rewind(&mut self) -> io::Result<()>; +} + +impl ExactSizeReadRewind for File { + fn len(&self) -> io::Result { + self.metadata().map(|m| m.len()) + } + + fn rewind(&mut self) -> io::Result<()> { + self.seek(SeekFrom::Start(0)).map(|_| ()) + } +} + +impl> ExactSizeReadRewind for Cursor { + fn len(&self) -> io::Result { + Ok(self.get_ref().as_ref().len().try_into().unwrap()) + } + + fn rewind(&mut self) -> io::Result<()> { + self.set_position(0); + Ok(()) + } +} + +enum Body { + None, + Simple(Box), + Chained( + std::io::Chain, Box>, + ), +} + +impl Body { + fn new() -> Body { + Body::None + } + + fn is_some(&self) -> bool { + !matches!(self, Body::None) + } + + fn add(&mut self, r: impl ExactSizeReadRewind + Send + 'static) { + let current = mem::replace(self, Body::None); + *self = match current { + Body::None => Body::Simple(Box::new(r)), + Body::Simple(first) => Body::Chained(first.chain(Box::new(r))), + Body::Chained(_) => Body::Chained( + (Box::new(current) as Box).chain(Box::new(r)), + ), + } + } +} + +impl Read for Body { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + match self { + Body::None => Ok(0), + Body::Simple(first) => first.read(buf), + Body::Chained(chained) => chained.read(buf), + } + } +} + +impl ExactSizeReadRewind for Body { + fn len(&self) -> io::Result { + match self { + Body::None => Ok(0), + Body::Simple(first) => first.len(), + Body::Chained(chained) => { + let (first, second) = chained.get_ref(); + Ok(first.len()? + second.len()?) + } + } + } -impl ReadAndSeek for T {} + fn rewind(&mut self) -> io::Result<()> { + match self { + Body::None => Ok(()), + Body::Simple(first) => first.rewind(), + Body::Chained(_) => { + let current = mem::replace(self, Body::None); + if let Body::Chained(chained) = current { + let (mut first, mut second) = chained.into_inner(); + first.rewind()?; + second.rewind()?; + *self = Body::Chained(first.chain(second)); + } + Ok(()) + } + } + } +} + +#[test] +fn test_exactsize_read_rewind_body() { + let a = "abcd"; + let b = "efg"; + let c = "hijklm"; + + let mut body1 = Body::new(); + body1.add(Cursor::new(&a[..])); + + assert_eq!(body1.len().unwrap(), 4); + assert_eq!(&body1.read_all_to_string().unwrap()[..], "abcd"); + body1.rewind().unwrap(); + assert_eq!(&body1.read_all_to_string().unwrap()[..], "abcd"); + + let mut body2 = Body::new(); + body2.add(Cursor::new(&a[..])); + body2.add(Cursor::new(&b[..])); + + assert_eq!(body2.len().unwrap(), 7); + assert_eq!(&body2.read_all_to_string().unwrap()[..], "abcdefg"); + body2.rewind().unwrap(); + assert_eq!(&body2.read_all_to_string().unwrap()[..], "abcdefg"); + + let mut body3 = Body::new(); + body3.add(Cursor::new(&a[..])); + body3.add(Cursor::new(&b[..])); + body3.add(Cursor::new(&c[..])); + + assert_eq!(body3.len().unwrap(), 13); + assert_eq!(&body3.read_all_to_string().unwrap()[..], "abcdefghijklm"); + body3.rewind().unwrap(); + assert_eq!(&body3.read_all_to_string().unwrap()[..], "abcdefghijklm"); +} pub struct HttpRequest { url: Url, headers: Vec<(String, String)>, - body: Option>, + body: Body, follow_redirects: bool, token: Arc, } @@ -175,7 +312,7 @@ impl HttpRequest { HttpRequest { url, headers: Vec::new(), - body: None, + body: Body::new(), follow_redirects: false, token: Arc::new(token), } @@ -189,8 +326,8 @@ impl HttpRequest { self.headers.push((name.to_string(), value.to_string())); } - fn post_data(&mut self, data: Box) { - self.body = Some(data); + fn post_data(&mut self, data: impl ExactSizeReadRewind + Send + 'static) { + self.body.add(data); } #[allow(clippy::result_large_err)] @@ -225,21 +362,21 @@ impl HttpRequest { http_request_execute as *const c_void, ); let mut headers = ptr::null_mut(); - if let Some(ref mut body) = self.body { + if self.body.is_some() { curl_easy_setopt(slot.curl, CURLOPT_POST, 1); curl_easy_setopt( slot.curl, CURLOPT_POSTFIELDSIZE_LARGE, - body.stream_len_().unwrap(), + self.body.len().unwrap(), ); /* Ensure we have no state from a previous attempt that failed because * of authentication (401). */ - body.seek(SeekFrom::Start(0)).unwrap(); - curl_easy_setopt(slot.curl, CURLOPT_READDATA, &mut *body); + self.body.rewind().unwrap(); + curl_easy_setopt(slot.curl, CURLOPT_READDATA, &mut self.body); curl_easy_setopt( slot.curl, CURLOPT_READFUNCTION, - read_from_read::<&mut (dyn ReadAndSeek + Send)> as *const c_void, + read_from_read:: as *const c_void, ); curl_easy_setopt(slot.curl, CURLOPT_FOLLOWLOCATION, 0); headers = curl_slist_append(headers, cstr!("Expect:").as_ptr()); @@ -478,28 +615,36 @@ impl HgHttpConnection { .and_then(|s| usize::from_str(s).ok()) .unwrap_or(0); + let httppostargs = self.get_capability(b"httppostargs").is_some(); + let mut command_url = self.url.clone(); let mut query_pairs = command_url.query_pairs_mut(); query_pairs.append_pair("cmd", command); let mut headers = Vec::new(); + let mut body = None; - if httpheader > 0 && !args.is_empty() { + if !args.is_empty() && (httppostargs || httpheader > 0) { let mut encoder = form_urlencoded::Serializer::new(String::new()); for (name, value) in args.iter() { encoder.append_pair(name, value); } let args = encoder.finish(); - let mut args = &args[..]; - let mut num = 1; - while !args.is_empty() { - let header_name = format!("X-HgArg-{}", num); - num += 1; - let (chunk, remainder) = args.split_at(cmp::min( - args.len(), - httpheader - header_name.len() - ": ".len(), - )); - headers.push((header_name, chunk.to_string())); - args = remainder; + if httppostargs { + headers.push(("X-HgArgs-Post".to_string(), args.len().to_string())); + body = Some(args); + } else { + let mut args = &args[..]; + let mut num = 1; + while !args.is_empty() { + let header_name = format!("X-HgArg-{}", num); + num += 1; + let (chunk, remainder) = args.split_at(cmp::min( + args.len(), + httpheader - header_name.len() - ": ".len(), + )); + headers.push((header_name, chunk.to_string())); + args = remainder; + } } } else { for (name, value) in args.iter() { @@ -517,6 +662,9 @@ impl HgHttpConnection { for (name, value) in headers { request.header(&name, &value); } + if let Some(body) = body { + request.post_data(Cursor::new(body)); + } request } @@ -535,7 +683,7 @@ impl HgWireConnection for HgHttpConnection { let mut http_req = self.start_command_request(command, args); if command == "pushkey" { http_req.header("Content-Type", "application/mercurial-0.1"); - http_req.post_data(Box::new(Cursor::new(Vec::::new()))); + http_req.post_data(Cursor::new(Vec::::new())); } let mut http_resp = http_req.execute().unwrap(); self.handle_redirect(&http_resp); @@ -588,7 +736,7 @@ impl HgWireConnection for HgHttpConnection { fn push_command(&mut self, input: File, command: &str, args: HgArgs) -> UnbundleResponse { let mut http_req = self.start_command_request(command, args); - http_req.post_data(Box::new(input)); + http_req.post_data(input); http_req.header("Content-Type", "application/mercurial-0.1"); let mut http_resp = http_req.execute().unwrap(); self.handle_redirect(&http_resp); diff --git a/src/hg_connect_stdio.rs b/src/hg_connect_stdio.rs index dc8f6cc05..71b6e8b29 100644 --- a/src/hg_connect_stdio.rs +++ b/src/hg_connect_stdio.rs @@ -25,7 +25,7 @@ use crate::hg_connect::{ use crate::libc::FdFile; use crate::libcinnabar::{hg_connect_stdio, stdio_finish}; use crate::libgit::child_process; -use crate::util::{ImmutBString, OsStrExt, PrefixWriter, ReadExt, SeekExt}; +use crate::util::{ImmutBString, OsStrExt, PrefixWriter, ReadExt}; pub struct HgStdioConnection { capabilities: HgCapabilities, @@ -115,7 +115,7 @@ impl HgWireConnection for HgStdioConnection { self.proc_in.write_all(&header).unwrap(); drop(header); - let len = input.stream_len_().unwrap(); + let len = input.metadata().unwrap().len(); //TODO: chunk in smaller pieces. writeln!(self.proc_in, "{}", len).unwrap(); diff --git a/src/http.c.patch b/src/http.c.patch index 89cf0f8ca..6f6f937ba 100644 --- a/src/http.c.patch +++ b/src/http.c.patch @@ -1,8 +1,8 @@ diff --git a/http.c b/http.c -index 2a722f6357..c7e0395abd 100644 +index 239421a983..8d1086ca4d 100644 --- a/http.c +++ b/http.c -@@ -63,7 +63,7 @@ static const char *curl_no_proxy; +@@ -68,7 +68,7 @@ static const char *curl_no_proxy; #ifdef GIT_CURL_HAVE_CURLOPT_PINNEDPUBLICKEY static const char *ssl_pinnedkey; #endif @@ -11,7 +11,7 @@ index 2a722f6357..c7e0395abd 100644 static long curl_low_speed_limit = -1; static long curl_low_speed_time = -1; static int curl_ftp_no_epsv; -@@ -288,11 +288,13 @@ static int http_options(const char *var, const char *value, void *cb) +@@ -406,11 +406,13 @@ static int http_options(const char *var, const char *value, curl_ssl_try = git_config_bool(var, value); return 0; } @@ -25,3 +25,21 @@ index 2a722f6357..c7e0395abd 100644 if (!strcmp("http.schannelcheckrevoke", var)) { if (value && !strcmp(value, "best-effort")) { +@@ -1300,9 +1302,6 @@ void http_init(struct remote *remote, const char *url, int proactive_auth) + } + #endif + +- if (curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) +- die("curl_global_init failed"); +- + http_proactive_auth = proactive_auth; + + if (remote && remote->http_proxy) +@@ -1391,7 +1390,6 @@ void http_cleanup(void) + curl_easy_cleanup(curl_default); + + curl_multi_cleanup(curlm); +- curl_global_cleanup(); + + string_list_clear(&extra_http_headers, 0); + diff --git a/src/main.rs b/src/main.rs index b8dde71d7..167575e8d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4242,6 +4242,12 @@ unsafe extern "C" fn cinnabar_main(_argc: c_int, argv: *const *const c_char) -> Some(_) | None => Ok(1), }; done_cinnabar(); + + if hg_connect_http::CURL_GLOBAL_INIT.get().is_some() { + unsafe { + curl_sys::curl_global_cleanup(); + } + } match ret { Ok(code) => code, Err(msg) => { diff --git a/src/util.rs b/src/util.rs index 690952fcd..4c1f4bdb2 100644 --- a/src/util.rs +++ b/src/util.rs @@ -4,7 +4,7 @@ use std::ffi::{CStr, CString, OsStr}; use std::fmt; -use std::io::{self, LineWriter, Read, Seek, SeekFrom, Write}; +use std::io::{self, LineWriter, Read, Write}; use std::mem; #[cfg(unix)] use std::os::unix::ffi; @@ -95,17 +95,6 @@ pub trait ReadExt: Read { impl ReadExt for T {} -pub trait SeekExt: Seek { - fn stream_len_(&mut self) -> io::Result { - let old_pos = self.stream_position()?; - let len = self.seek(SeekFrom::End(0))?; - self.seek(SeekFrom::Start(old_pos))?; - Ok(len) - } -} - -impl SeekExt for T {} - pub trait SliceExt { fn splitn_exact(&self, c: C) -> Option<[&Self; N]>; fn rsplitn_exact(&self, c: C) -> Option<[&Self; N]>;