Skip to content

Commit

Permalink
[FIXED JENKINS-20128]
Browse files Browse the repository at this point in the history
CLI hits "/cli", so the server needs to respond to this endpoint.
  • Loading branch information
kohsuke committed Nov 9, 2013
1 parent 1b6ba71 commit 9f1796e
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 34 deletions.
3 changes: 3 additions & 0 deletions changelog.html
Expand Up @@ -55,6 +55,9 @@
<!-- Record your changes in the trunk here. -->
<div id="trunk" style="display:none"><!--=TRUNK-BEGIN=-->
<ul class=image>
<li class=bug>
CLI over HTTP was not working since 1.535.
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-20128">issue 20128</a>)
<li class=bug>
hudson appears in a the webpage title.
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-14380">issue 14380</a>)
Expand Down
82 changes: 48 additions & 34 deletions core/src/main/java/hudson/cli/CLIAction.java
Expand Up @@ -31,10 +31,14 @@
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletResponse;

import hudson.util.IOException2;
import jenkins.model.Jenkins;

import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;
import org.kohsuke.stapler.HttpResponses.HttpResponseException;
import org.kohsuke.stapler.Stapler;
import org.kohsuke.stapler.StaplerProxy;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;

Expand All @@ -44,11 +48,13 @@
import hudson.remoting.Channel;

/**
* Shows usage of CLI and commands.
*
* @author ogondza
*/
@Extension
@Restricted(NoExternalUse.class)
public class CLIAction implements RootAction {
public class CLIAction implements RootAction, StaplerProxy {

private transient final Map<UUID,FullDuplexHttpChannel> duplexChannels = new HashMap<UUID, FullDuplexHttpChannel>();

Expand All @@ -62,7 +68,6 @@ public String getDisplayName() {
}

public String getUrlName() {

return "/cli";
}

Expand All @@ -82,42 +87,51 @@ public void doCommand(StaplerRequest req, StaplerResponse rsp) throws ServletExc
req.getView(this, "command.jelly").forward(req, rsp);
}

/**
* Handles HTTP requests for duplex channels for CLI.
*/
public void doIndex(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException, InterruptedException {
final Jenkins jenkins = Jenkins.getInstance();
if (!"POST".equals(req.getMethod())) {
// for GET request, serve _cli.jelly, assuming this is a browser
jenkins.checkPermission(Jenkins.READ);
req.setAttribute("command", CLICommand.clone("help"));
req.getView(this,"index.jelly").forward(req,rsp);
return;
@Override
public Object getTarget() {
StaplerRequest req = Stapler.getCurrentRequest();
if (req.getRestOfPath().length()==0 && "POST".equals(req.getMethod())) {
// CLI connection request
throw new CliEndpointResponse();
} else {
return this;
}
}

// do not require any permission to establish a CLI connection
// the actual authentication for the connecting Channel is done by CLICommand

UUID uuid = UUID.fromString(req.getHeader("Session"));
rsp.setHeader("Hudson-Duplex",""); // set the header so that the client would know

FullDuplexHttpChannel server;
if(req.getHeader("Side").equals("download")) {
duplexChannels.put(uuid,server=new FullDuplexHttpChannel(uuid, !jenkins.hasPermission(Jenkins.ADMINISTER)) {
@Override
protected void main(Channel channel) throws IOException, InterruptedException {
// capture the identity given by the transport, since this can be useful for SecurityRealm.createCliAuthenticator()
channel.setProperty(CLICommand.TRANSPORT_AUTHENTICATION, Jenkins.getAuthentication());
channel.setProperty(CliEntryPoint.class.getName(),new CliManagerImpl(channel));
}
});
/**
* Serves CLI-over-HTTP response.
*/
private class CliEndpointResponse extends HttpResponseException {
@Override
public void generateResponse(StaplerRequest req, StaplerResponse rsp, Object node) throws IOException, ServletException {
try {
server.download(req,rsp);
} finally {
duplexChannels.remove(uuid);
// do not require any permission to establish a CLI connection
// the actual authentication for the connecting Channel is done by CLICommand

UUID uuid = UUID.fromString(req.getHeader("Session"));
rsp.setHeader("Hudson-Duplex",""); // set the header so that the client would know

FullDuplexHttpChannel server;
if(req.getHeader("Side").equals("download")) {
duplexChannels.put(uuid,server=new FullDuplexHttpChannel(uuid, !Jenkins.getInstance().hasPermission(Jenkins.ADMINISTER)) {
@Override
protected void main(Channel channel) throws IOException, InterruptedException {
// capture the identity given by the transport, since this can be useful for SecurityRealm.createCliAuthenticator()
channel.setProperty(CLICommand.TRANSPORT_AUTHENTICATION, Jenkins.getAuthentication());
channel.setProperty(CliEntryPoint.class.getName(),new CliManagerImpl(channel));
}
});
try {
server.download(req,rsp);
} finally {
duplexChannels.remove(uuid);
}
} else {
duplexChannels.get(uuid).upload(req,rsp);
}
} catch (InterruptedException e) {
throw new IOException2(e);
}
} else {
duplexChannels.get(uuid).upload(req,rsp);
}
}
}

1 comment on commit 9f1796e

@jglick
Copy link
Member

@jglick jglick commented on 9f1796e Feb 3, 2014

Choose a reason for hiding this comment

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

This comment claims the bug continues in 1.549.

Please sign in to comment.