Skip to content

Conversation

@jglick
Copy link
Member

@jglick jglick commented Jun 5, 2018

jglick added a commit to jenkinsci/artifact-manager-s3-plugin that referenced this pull request Jun 5, 2018
* THE SOFTWARE.
*/

package io.jenkins.plugins.httpclient;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Package name does differ from artifactId. I would propose apache.httpcomponents.client.api or so.
OTOH the code does not use the library on its own. So more reviews may be required

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That package name is pretty awkward, and also gives the false impression that this is Apache code.

Using the artifactId ~ shortName, in some modified form, as a final package component is indeed typical. This particular artifactId is quite unwieldy though. Also I am not sure there is a solid convention for “miscellaneous source packages added to a library wrapper”, which is not really the same as “main source package of a regular plugin”.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume Oleg meant io.jenkins.plugins.apache.httpcomponents.client.api

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Normally - is converted to _ or camel casing when constructing a package component from an artifactId, so something like io.jenkins.plugins.apache_httpcomponents_client_4_api. Pretty ugly.

public void connect(String whatConcise, String whatVerbose, ConnectionCreator connectionCreator, ConnectionUser connectionUser, TaskListener listener) throws IOException, InterruptedException {
AtomicInteger responseCode = new AtomicInteger();
int attempt = 1;
while (true) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a little complex to follow, there are too many alternatives in the same method

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FWIW the original version, which depended on the guava-retrying library that is supposed to wrap this logic in a pretty higher-level API, was just as long and (I think) harder to follow.

try {
executors.submit(() -> {
responseCode.set(0);
try (CloseableHttpClient client = HttpClients.createSystem()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not join the two try?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They have different scopes.

try (CloseableHttpClient client = HttpClients.createSystem()) {
try (CloseableHttpResponse response = connectionCreator.connect(client)) {
StatusLine statusLine = response.getStatusLine();
responseCode.set(statusLine != null ? statusLine.getStatusCode() : 0);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe make the ? in the declaration is more clear

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, what?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nothing, I read response.getStatusLine() instead statusLine.getStatusCode()

try (CloseableHttpResponse response = connectionCreator.connect(client)) {
StatusLine statusLine = response.getStatusLine();
responseCode.set(statusLine != null ? statusLine.getStatusCode() : 0);
if (responseCode.get() < 200 || responseCode.get() >= 300) {
Copy link
Contributor

@kuisathaverat kuisathaverat Jun 6, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what happens if there is a redirection 301/302?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Treated as a failure currently. The existing use cases do not require following redirects. It would be possible to handle I think if there were some use case.

Header contentEncoding = entity.getContentEncoding();
diag = IOUtils.toString(err, contentEncoding != null ? contentEncoding.getValue() : null);
}
} else {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe you can initialized diag to null and remove the else

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could, I just have a mild preference for effectively final variables.

} else {
diag = null;
}
throw new AbortException(String.format("Failed to %s, response: %d %s, body: %s", whatVerbose, responseCode.get(), statusLine != null ? statusLine.getReasonPhrase() : "?", diag));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if you make the ? in the declaration of statusLine you have not repeat the code here

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still not sure I am following. You mean, make statusLine nonnull by creating a fake object as a fallback?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

forget I mixed method names

}
return null; // success
}).get(timeout, TimeUnit.SECONDS);
} catch (TimeoutException x) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can move it a level up and move the Exception wrapper to a method, this makes the code simpler

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thought about doing that but did not see a way to do so without making things much more complicated.

* @return the same, but with any query string concealed
*/
public static String sanitize(URL url) {
return url.toString().replaceFirst("[?].+$", "?…");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure, but Could be interesting to remove the stuff between @ and //? I mean, if we have the URL http://U:P@HOST?QUERY we replace it with http://U:P@HOST?..., it is possible to make the same with the user and password and transform the URL to http://..@HOST?...

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

passwords should be removed, user would be ok

also, should it be three chars ... or one unicode char ? I think the former

Copy link
Member Author

@jglick jglick Jun 6, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

URL.userInfo is generally opaque, not necessarily user:pass. It could be masked if needed. Currently there is no use case for doing so, as (semi-)secrets are passed in the query string.

Copy link

@carlossg carlossg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMHO if this is exposed it lacks docs on how to use it and an example
We should move some of the mock tests from downstream to here

* @return the same, but with any query string concealed
*/
public static String sanitize(URL url) {
return url.toString().replaceFirst("[?].+$", "?…");
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

passwords should be removed, user would be ok

also, should it be three chars ... or one unicode char ? I think the former

@jglick
Copy link
Member Author

jglick commented Jun 6, 2018

it lacks docs on how to use it

Well, there is Javadoc…

and an example

The upload and download methods are examples of using the base method.

We should move some of the mock tests from downstream to here

Not possible I think.

@oleg-nenashev oleg-nenashev self-requested a review June 6, 2018 19:12
@jglick
Copy link
Member Author

jglick commented Jun 7, 2018

@carlossg do you consider this OK to merge? (I cannot request you as a reviewer.) I think would be up to @oleg-nenashev or @dwnusbaum to actually do so.

public static String sanitize(URL url) {
try {
URI orig = url.toURI();
return new URI(orig.getScheme(), orig.getUserInfo() != null ? "…" : null, orig.getHost(), orig.getPort(), orig.getPath(), orig.getQuery() != null ? "…" : null, orig.getFragment()).toString();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I asked before, is intended or should be ... ? will unicode behave correctly?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Either should be fine. This is just for use in build log messages, where we already often print ‘’ or » etc. (Cf. jenkinsci/workflow-job-plugin#89.)

}

/**
* Maximum number of seconds between upload/download attempts.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: I think it would be good to use the same units or make the user pass a ChronoUnit/TimeUnit for waitMultiplier and waitMaximum (maybe also timeout) to avoid potential confusion.

Copy link
Member

@dwnusbaum dwnusbaum left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me. I would like to see some tests for the general logic, but it seems like that would need a mocking framework which might get pretty nasty. I have no strong opinion about the package name.

@jglick
Copy link
Member Author

jglick commented Jun 8, 2018

tests for the general logic, but it seems like that would need a mocking frameworks

Not necessarily a mocking framework, just a local server…but NetworkTest in artifact-manager-s3 already exercises all of this stuff. The original code was anyway written in TDD style against that test. (Originally using the guava-retrying library, then throwing that out and reimplemented directly with a loop because exponential backoff is not really that complicated.)

Copy link
Member

@oleg-nenashev oleg-nenashev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still do not like the current package name, but I am fine with it if @dwnusbaum is fine.

@dwnusbaum dwnusbaum merged commit 085500f into jenkinsci:master Jun 8, 2018
@jglick jglick deleted the RobustHTTPClient branch June 8, 2018 14:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants