Skip to content

Commit

Permalink
Allow dashes in devtainer names
Browse files Browse the repository at this point in the history
  • Loading branch information
struanb committed Aug 21, 2023
1 parent 6c7c9de commit 978a597
Show file tree
Hide file tree
Showing 5 changed files with 40 additions and 9 deletions.
4 changes: 2 additions & 2 deletions app/client/src/components/Container.vue
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
</h3>
<h3 v-else>
<span><input type="text" v-bind:class="validName ? [] : ['red']" class="form-control" required v-model="form.name" placeholder="Devtainer name" :disabled="!hasProfiles"></span>
<span class="error-info" v-if="!validName">Name must be lower case, consist only of letters and digits, and begin with a letter</span>
<span class="error-info" v-if="!validName">Name must be lower case, consist only of letters, digits and hyphens (but not successive hyphens) and begin with a letter</span>
</h3>
</b-card-header>

Expand Down Expand Up @@ -285,7 +285,7 @@
return `${window.location.protocol}//${window.location.host}/container/${this.container.name}`;
},
validName() {
return this.form.name.match('^([a-z][a-z0-9]*|)$');
return this.form.name.match('^(?:[a-z](?:-[a-z0-9]+|[a-z0-9]+)+|)$');
}
},
methods: {
Expand Down
11 changes: 10 additions & 1 deletion app/server/lib/App.pm
Original file line number Diff line number Diff line change
Expand Up @@ -241,9 +241,18 @@ sub _handler {

# Generate the 'parent fully qualified domain name', i.e.
# a hostname from which child container hostnames can be generated,
# and on which cookies can be assigned,
# (and from which a cookie domain can ultimately be derived)
# by stripping off leading characters up to the first '-' or '.'
#
# Host header may be of the form:
# - www.mydockside.co.uk -> .mydockside.co.uk
# - www-mydevtainer.mydockside.co.uk -> --mydevtainer.mydockside.co.uk
# - www-mydevtainer--mydocksidedevtainer.mydockside.co.uk -> --mydevtainer--mydocksidedevtainer.mydockside.co.uk
#
# When Dockside is accessed on a non-standard port, the Host header may also have :<port> suffixed.

my $parentFQDN = $r->header_in('Host'); $parentFQDN =~ s!^[^\-\.]+!!;
$parentFQDN = '-' . $parentFQDN unless $parentFQDN =~ /^\./;

# Determine level of authorisation of requestor.
my $User = Request->authenticate( { 'cookie' => $r->header_in("Cookie"), 'protocol' => $protocol } );
Expand Down
28 changes: 25 additions & 3 deletions app/server/lib/Proxy.pm
Original file line number Diff line number Diff line change
Expand Up @@ -25,19 +25,41 @@ sub domain_to_host {

# Identify the container to which to proxy.
# In order to support nested dockside containers,
# we parse the hostname (splitting on '-'-delimited container names).
# we parse the hostname, splitting on '--'-delimited container names,
# and splitting the leftmost element again on its first '-'.
#
# The required container name will be N from the right, where N
# is the number of '-'-delimited strings in the X-Nest-Level header.

# e.g. Example inputs and outputs, for each nest level:
#
# www.mydockside.co.uk ->
# - 0: 'www', '', 'mydockside.co.uk', 0 (as seen by the outermost Dockside container)
#
# www-inner.mydockside.co.uk ->
# - 0: 'inner', 'www', 'mydockside.co.uk', 0 (as seen by the outermost Dockside container)
# - 1: 'www', '', 'mydockside.co.uk', 1 (as seen by an inner Dockside devtainer)

# www-my-devtainer--inner.mydockside.co.uk ->
# - 0: 'inner', 'www-my-devtainer', 'mydockside.co.uk', 0 (as seen by the outermost Dockside container)
# - 1: 'my-devtainer', 'www', 'mydockside.co.uk', 1 (as seen by an inner Dockside devtainer; will proxy on to 'my-devtainer')

if( $host =~ /^([^\.]+)\.(.*?)(:\d+)?$/ ) {
my @elements = reverse split(/-/, $1);
my @elements = reverse split(/--/, $1);
my $domain = $2;

# Split again the leftmost element on its first '-'.
# Add the devtainer name (if found) to @elements.
# Always add the service name to @elements.
my ($service, $topHost) = pop(@elements) =~ /^([^-]+)(?:-(.*))?$/;
push(@elements, $topHost ? $topHost : (), $service);

my $nestCount = split(/-/, $r->header_in('X-Nest-Level'));

return undef unless $nestCount < @elements;

my $element = $elements[$nestCount];
my $prefix = join('-', reverse @elements[($nestCount+1)..(@elements-1)]);
my $prefix = join('--', reverse @elements[($nestCount+1)..(@elements-1)]);

wlog("domain_to_host: Host header='$host'; nestCount=$nestCount; container host='$element'; prefix='$prefix'; domain='$domain'");

Expand Down
4 changes: 2 additions & 2 deletions app/server/lib/Reservation.pm
Original file line number Diff line number Diff line change
Expand Up @@ -231,8 +231,8 @@ sub validate {
my $self = shift;

if($self->{'name'} ne '') {
# Name must be lower case, consist only of letters and digits, and begin with a letter
unless( $self->{'name'} =~ /^[a-z][a-z0-9]+$/ ) {
# Name must be lower case, consist only of letters, digits and hyphens (but not successive hyphens) and begin with a letter
unless( $self->{'name'} =~ /^[a-z](?:-[a-z0-9]+|[a-z0-9]+)+$/ ) {
die Exception->new( 'msg' => "Failed to create Reservation with invalid name '$self->{'name'}'" );
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ index 042b37f..879dfb2 100644
+// (technically, package.json's theia.frontend.config.devtainerName)
+// It will be read by our patched window-title-service.js
+let config = ${this.prettyStringify(this.pck.props.frontend.config)};
+try { config.devtainerName=document.location.href.split('/')[2].split('.')[0].split('-')[1]; } catch {};
+try { config.devtainerName=document.location.href.split('/')[2].split('.')[0].split('--')[0].split('-').slice(1).join('-'); } catch {};
+FrontendApplicationConfigProvider.set(config);

${this.ifMonaco(() => `
Expand Down

0 comments on commit 978a597

Please sign in to comment.