diff mbox series

[v2,2/5] Update to reqwest 0.9 and temporarily disable pagination

Message ID 20181211034601.29061-2-ruscur@russell.cc
State Accepted
Headers show
Series [v2,1/5] setup_rustfmt_hook: create hooks dir if it doesn't exist | expand

Commit Message

Russell Currey Dec. 11, 2018, 3:45 a.m. UTC
reqwest 0.8 uses an older version of hyper which uses an older version
of the openssl crate, which means that snowpatch can't build on any
particularly modern distribution.  Updating reqwest to 0.9 however is
quite the ordeal, because it now uses hyper 0.12 which no longer has
stringly typed header values, which isn't too much hassle...

except for the following:

 - authorization, which we now have to do manually, which is
   particularly annoying for the Basic Auth case

 - pagination using the Link header, which is now a significant
   undertaking to parse

I am opting on the side of disabling pagination until some kind of
helper is released, or we put in the effort to write one ourselves.

Signed-off-by: Russell Currey <ruscur@russell.cc>
---
 Cargo.toml       |  3 ++-
 src/jenkins.rs   | 47 +++++++++++++++++++++++------------------
 src/main.rs      |  1 +
 src/patchwork.rs | 54 ++++++++++++++++++++++++------------------------
 4 files changed, 57 insertions(+), 48 deletions(-)
diff mbox series

Patch

diff --git a/Cargo.toml b/Cargo.toml
index 494c663..d17b0cf 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -28,7 +28,7 @@  license = "GPL-2.0+"
 keywords = ["CI", "patch", "patchwork", "jenkins", "git"]
 
 [dependencies]
-reqwest = "0.8"
+reqwest = "0.9"
 git2 = "0.7"
 url = "1.7"
 serde = "1.0"
@@ -39,3 +39,4 @@  tempdir = "0.3"
 docopt = "1.0"
 log = "0.4"
 env_logger = "0.5"
+base64 = "0.9"
diff --git a/src/jenkins.rs b/src/jenkins.rs
index 0aa5212..4906139 100644
--- a/src/jenkins.rs
+++ b/src/jenkins.rs
@@ -20,6 +20,7 @@ 
 // * get artifacts + console log from completed build (do we make this configurable?)
 // * integrate into snowpatch worker thread
 
+extern crate base64;
 extern crate reqwest;
 extern crate url;
 
@@ -29,7 +30,7 @@  use std::sync::Arc;
 use std::thread::sleep;
 use std::time::Duration;
 
-use reqwest::header::{Authorization, Basic, Headers, Location};
+use reqwest::header::{HeaderMap, AUTHORIZATION, LOCATION};
 use reqwest::{Client, IntoUrl, Response};
 use serde_json::{self, Value};
 
@@ -68,14 +69,15 @@  impl CIBackend for JenkinsBackend {
             .extend_pairs(params)
             .finish();
 
-        let resp =
-            self.post_url(&format!(
+        let resp = self
+            .post_url(&format!(
                 "{}/job/{}/buildWithParameters?{}",
                 self.base_url, job_name, params
             )).expect("HTTP request error"); // TODO don't panic here
 
-        match resp.headers().get::<Location>() {
-            Some(loc) => Ok(loc.to_string()),
+        match resp.headers().get(LOCATION) {
+            // TODO do we actually have to return a string, coud we change the API?
+            Some(loc) => Ok(loc.to_str().unwrap().to_string()),
             None => Err("No Location header returned"),
         }
     }
@@ -88,14 +90,20 @@  pub enum JenkinsBuildStatus {
 }
 
 impl JenkinsBackend {
-    fn headers(&self) -> Headers {
-        let mut headers = Headers::new();
+    fn headers(&self) -> HeaderMap {
+        let mut headers = HeaderMap::new();
         if let Some(ref username) = self.username {
-            headers.set(Authorization(Basic {
-                username: username.clone(),
-                password: self.token.clone(),
-            }));
-        }
+            if let Some(ref token) = self.token {
+                headers.insert(
+                    AUTHORIZATION,
+                    format!(
+                        "Basic {}",
+                        base64::encode(&format!("{}:{}", username, token))
+                    ).parse()
+                    .unwrap(),
+                );
+            }
+        };
         headers
     }
 
@@ -139,14 +147,13 @@  impl JenkinsBackend {
             match entry.get("executable") {
                 Some(exec) => {
                     return Some(
-                        exec
-                                          .as_object() // Option<BTreeMap>
-                                          .unwrap() // BTreeMap
-                                          .get("url") // Option<&str>
-                                          .unwrap() // &str ?
-                                          .as_str()
-                                          .unwrap()
-                                          .to_string(),
+                        exec.as_object() // Option<BTreeMap>
+                            .unwrap() // BTreeMap
+                            .get("url") // Option<&str>
+                            .unwrap() // &str ?
+                            .as_str()
+                            .unwrap()
+                            .to_string(),
                     );
                 }
                 None => sleep(Duration::from_millis(JENKINS_POLLING_INTERVAL)),
diff --git a/src/main.rs b/src/main.rs
index 7a0deba..6fe0bdb 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -30,6 +30,7 @@  extern crate env_logger;
 extern crate serde;
 #[macro_use]
 extern crate serde_derive;
+extern crate base64;
 extern crate serde_json;
 extern crate toml;
 
diff --git a/src/patchwork.rs b/src/patchwork.rs
index a0722c7..f7c908f 100644
--- a/src/patchwork.rs
+++ b/src/patchwork.rs
@@ -25,9 +25,7 @@  use std::result::Result;
 use tempdir::TempDir;
 
 use reqwest;
-use reqwest::header::{
-    qitem, Accept, Authorization, Basic, Connection, ContentType, Headers, Link, RelationType,
-};
+use reqwest::header::{HeaderMap, ACCEPT, AUTHORIZATION, CONTENT_TYPE};
 use reqwest::Client;
 use reqwest::Response;
 use reqwest::StatusCode;
@@ -35,6 +33,8 @@  use reqwest::StatusCode;
 use serde::{self, Serializer};
 use serde_json;
 
+use base64;
+
 use utils;
 
 // TODO: more constants.  constants for format strings of URLs and such.
@@ -205,16 +205,19 @@  impl TestResult {
 
 pub struct PatchworkServer {
     pub url: String,
-    headers: Headers,
+    headers: HeaderMap,
     pub client: std::sync::Arc<Client>,
 }
 
 impl PatchworkServer {
     #[cfg_attr(feature = "cargo-clippy", allow(ptr_arg))]
     pub fn new(url: &String, client: &std::sync::Arc<Client>) -> PatchworkServer {
-        let mut headers = Headers::new();
-        headers.set(Accept(vec![qitem(reqwest::mime::APPLICATION_JSON)]));
-        headers.set(ContentType(reqwest::mime::APPLICATION_JSON));
+        let mut headers = HeaderMap::new();
+        // These .parse().unwrap() blocks are making sure it's a valid ASCII
+        // header value.  They don't need to be refactored and hopefully in
+        // future won't be necessary with newer reqwest versions.
+        headers.insert(ACCEPT, "application/json".parse().unwrap());
+        headers.insert(CONTENT_TYPE, "application/json".parse().unwrap());
         PatchworkServer {
             url: url.clone(),
             client: client.clone(),
@@ -231,34 +234,29 @@  impl PatchworkServer {
     ) {
         match (username, password, token) {
             (&None, &None, &Some(ref token)) => {
-                self.headers.set(Authorization(format!("Token {}", token)));
+                self.headers
+                    .insert(AUTHORIZATION, format!("Token {}", token).parse().unwrap());
             }
             (&Some(ref username), &Some(ref password), &None) => {
-                self.headers.set(Authorization(Basic {
-                    username: username.clone(),
-                    password: Some(password.clone()),
-                }));
+                self.headers.insert(
+                    AUTHORIZATION,
+                    format!(
+                        "Basic {}",
+                        base64::encode(&format!("{}:{}", username, password))
+                    ).parse()
+                    .unwrap(),
+                );
             }
             _ => panic!("Invalid patchwork authentication details"),
         }
     }
 
     pub fn get_url(&self, url: &str) -> std::result::Result<Response, reqwest::Error> {
-        self.client
-            .get(&*url)
-            .headers(self.headers.clone())
-            .header(Connection::close())
-            .send()
+        self.client.get(&*url).headers(self.headers.clone()).send()
     }
 
     pub fn get_url_string(&self, url: &str) -> std::result::Result<String, reqwest::Error> {
-        let mut resp = try!(
-            self.client
-                .get(&*url)
-                .headers(self.headers.clone())
-                .header(Connection::close())
-                .send()
-        );
+        let mut resp = try!(self.client.get(&*url).headers(self.headers.clone()).send());
         let mut body: Vec<u8> = vec![];
         io::copy(&mut resp, &mut body).unwrap();
         Ok(String::from_utf8(body).unwrap())
@@ -282,7 +280,7 @@  impl PatchworkServer {
         let mut body: Vec<u8> = vec![];
         io::copy(&mut resp, &mut body).unwrap();
         trace!("{}", String::from_utf8(body).unwrap());
-        assert_eq!(resp.status(), StatusCode::Created);
+        assert_eq!(resp.status(), StatusCode::CREATED);
         Ok(resp.status())
     }
 
@@ -311,8 +309,9 @@  impl PatchworkServer {
         )
     }
 
-    fn get_next_link(&self, resp: &Response) -> Option<String> {
-        let next = resp.headers().get::<Link>()?;
+    fn get_next_link(&self, _resp: &Response) -> Option<String> {
+        /*
+        let next = resp.headers().get(LINK)?;
         for val in next.values() {
             if let Some(rel) = val.rel() {
                 if rel.iter().any(|reltype| reltype == &RelationType::Next) {
@@ -320,6 +319,7 @@  impl PatchworkServer {
                 }
             }
         }
+*/
         None
     }