Home > Back-end >  Extract left part of HTTP URI containing scheme, host and port (everything before the path)
Extract left part of HTTP URI containing scheme, host and port (everything before the path)

Time:09-06

PS C:\Users\user> Split-Path "http://localhost:9200/index" -Parent
http:\\localhost:9200

In above example, I am trying to extract everything before the /index. I thought Split-Path would do the job but it changes the forward slashes in http:// to back slashes.

"http://localhost:9200/index" -replace ([Uri]"http://localhost:9200/index").AbsolutePath

I've worked out the above alternative which works but Split-Path just seems cleaner. Just wondering why it does that with the forward slashes?

CodePudding user response:

Use the [uri] type accelerator. Like so,

$u = [uri]"http://localhost:9200/index"
$u.LocalPath # Get the local path property
/index

Since accelerator creates an Uri obect, it works with more complex urls too. E.g.

$u = [uri]"http://localhost:9200/index/data.htm?foo=bar&qaz=zof"
$u # Print members, some omitted for brevity

AbsolutePath   : /index/data.htm
AbsoluteUri    : http://localhost:9200/index/data.htm?foo=bar&qaz=zof
LocalPath      : /index/data.htm
Authority      : localhost:9200
...
PathAndQuery   : /index/data.htm?foo=bar&qaz=zof
Segments       : {/, index/, data.htm}
IsUnc          : False
Host           : localhost
Port           : 9200
Query          : ?foo=bar&qaz=zof
...
OriginalString : http://localhost:9200/index/data.htm?foo=bar&qaz=zof
...

To get just the protocol and authority part, either search and replace, or join properties. Like so,

# -replace
$u.OriginalString -replace [regex]::escape($u.PathAndQuery), ''
http://localhost:9200

# build string
 "{0}://{1}:{2}" -f $u.scheme, $u.host, $u.port                 
http://localhost:9200

CodePudding user response:

It seems like that behavior is platform dependent, I ran your example with Split-path on both Windows and macOS and on windows you will get a backslash, while on macOS you will get a regular slash....

CodePudding user response:

Split-Path returns a specified part of a (file) path and in Windows the directories are separated using a backslash. If you run the same command on Linux, you will probably get the desired output.

The best way would probably be the solution from Mathias (parse the URI and format the string). Another workaround with the Split-Path cmdlet would be to replace all backslashes with forward slashes:

(Split-Path "http://localhost:9200/index" -Parent) -replace '\\', '/'

CodePudding user response:

Split-Path works with provider paths only. Instead, use the .NET class Uri that is specifically made for parsing URIs.

Using the method Uri.GetLeftPart(), a partial URI can be extracted from the URI. This is much easier than building it "manually" by concatenating the URI components.

$uri = [uri] 'http://localhost:9200/index'
$uri.GetLeftPart('Authority')

# or as a one-liner:
([uri] 'http://localhost:9200/index').GetLeftPart('Authority')

Output:

http://localhost:9200

By passing other values from the UriPartial enum, different parts of the URI can be returned:

$uri = [uri] 'http://localhost:9200/index?param=value#anchor'

foreach( $part in 'Scheme','Authority','Path','Query' ) {
    [pscustomobject]@{
        Part = $part
        URI  = $uri.GetLeftPart( $part )
    }
}

Output:

Part      URI
----      ---
Scheme    http://
Authority http://localhost:9200
Path      http://localhost:9200/index
Query     http://localhost:9200/index?param=value

As noted by this related C# answer, there is one catch with Uri.GetLeftPart():

If the port is the default port for the scheme, it will strip it out. E. g., the URI http://localhost:80/index gets turned into http://localhost/index, because port 80 is the default for the HTTP scheme.

If this is a problem, then you may use the UriBuilder class to manually build the new URI from the parts of the original URI, while avoiding direct string manipulation:

Function Get-UriBase( [Parameter(Mandatory, ValueFromPipeline)] [Uri] $Uri ) {
    process {
        $uriBase = [UriBuilder]::new( $uri.Scheme, $uri.Host, $uri.Port ).ToString()
        # Output $uriBase if original URI starts with this part
        if( $uri.OriginalString.StartsWith( $uriBase ) ) { 
            return $uriBase 
        }
        # Otherwise original URI didn't contain a port, so use GetLeftPart() which omits it
        $uri.GetLeftPart('Authority')
    }
}

foreach( $uri in 'http://localhost:80/index', 'http://localhost:1234/index', 'http://localhost/index' ) {
    # For output in table format
    [PSCustomObject]@{ URI = $uri; URI_Base = Get-UriBase $uri }
}

Output:

URI                         URI_Base
---                         --------
http://localhost:80/index   http://localhost:80/  
http://localhost:1234/index http://localhost:1234/
http://localhost/index      http://localhost  
  • Related