elm / url

Build and parse URLs. Useful for HTTP and "routing" in single-page apps (SPAs)
https://package.elm-lang.org/packages/elm/url/latest/
BSD 3-Clause "New" or "Revised" License
75 stars 43 forks source link

Support protocols different from http / https #10

Open Gozala opened 6 years ago

Gozala commented 6 years ago

I have originally reported elm/browser#20 but as far as I can tell reason is not in Browser.application but rather in the fact that following code explicitly deals with just http / https protocols

https://github.com/elm/url/blob/384b1dcf84065a500a71402ec367f3982b35093d/src/Url.elm#L111-L120

Gozala commented 6 years ago

Reading the source I see that supporting just Https | Http was deliberate choice. Reported error

What is the root? The root of your file system?

makes me think that is related to the file:// protocol where Browser.Navigation would have questionable semantics.

I'd like to still suggest to make this module compatible with arbitrary protocols and instead reject URLs with no authority like file:// from the Browser instead. That would allow Elm apps to be usable in other protocols that have notion of root in form of authority.

Give that currently protocols are tags it does not seems like this could be accomplished without non-breaking change. Maybe Protocol could be extended to:

type Protocol = Http | Https | Custom String

To make this change less disruptive ? Alternative could be just adding known protocols to the list but I don't think that would be a good idea.

It is also worth pointing out that electron apps often introduce custom app specific protocols, we at Mozilla also working with network protocol authors to allow adding them to Firefox via WebExtensions it would be unfortunate if Elm would not be an option for those use cases due to this limitation.

evancz commented 6 years ago

Can you give examples of the other protocols you care about? I just need more information. I cannot design without any examples.

Gozala commented 6 years ago

Can you give examples of the other protocols you care about? I just need more information. I cannot design without any examples.

In the context of libdweb we're collaborating with following projects that are bringing corresponding protocols and are the ones I care the most.

There are also few other projects that we may collaborate with but I don't feel comfortable discussing them publicly (yet).

I think these are the things to consider in terms of design. Most of the custom protocols are semantically similar, which is being reinforced by the API that we (and others projects) provide for protocol implementers, which are:

Let me know if there is anything else that I can address.

Gozala commented 6 years ago

What I meant to imply that while I do have two or three protocols that I personally care about, I don't think that would necessarily provide required context. Which is why my last comment focused more on the semantics imposed on custom protocols as I think that would better inform design constraints. But if that assumption is incorrect please let me know and I'm happy to provide specific details per protocol basis.

Mouvedia commented 6 years ago

Will this fix ws and wss schemes?

Can you give examples of the other protocols you care about?

The app scheme comes to mind.

w0rm commented 6 years ago

I've been creating html files of my presentations, e.g. file:///Users/w0rm/Work/elm-slice-show/example/index.html and used hash based navigation between slides. Now I have to start a web server just to see my slides. It makes it harder to access the content.

sarasfox commented 5 years ago

Any Idea When the file:// could be working? I new to elm but I am willing to help.

m4lvin commented 5 years ago

It would also be nice to support data:,Hello%2C%20World!

aforemny commented 5 years ago

I have been using file: URLs and hash based navigation in electron apps, too.

Gozala commented 5 years ago

It might be worth considering if browser native URL could be worth leveraging for URL parsing / resolution as it would automatically support every protocol that underlying runtime does. But as I’m mostly guessing why current implementation is the way it very well could be intentionally not doing it.

lestersm commented 5 years ago

I'm working with electron apps and just got here with the same issue. Is there any way we can enable file: ?

ream88 commented 5 years ago

This has bitten me as well while trying to update our electron based app to 0.19.

xla commented 5 years ago

@evancz Another use-case which currently doesn't work is hash-based routing in web extensions. The protocols would: chrome-extension:// and moz-extension://.

alythobani commented 5 years ago

Built a .html file just like @w0rm above and got the same error because of the file:// prefix.

Uncaught Error: Browser.application programs cannot handle URLs like this

Worked in Elm 0.18 with Navigation.programWithFlags but not in Elm 0.19 with Browser.application.

TroyJoachim commented 5 years ago

I would also like to see support for the Windows style file:/// because it's used in Electron.

Mouvedia commented 5 years ago

Here are the schemes out of the registered ones currently requested in this issue:

glinesbdev commented 5 years ago

I'd like to add mobile frameworks like Capacitor: capacitor://localhost. This is used for custom URL schemes for iOS projects.

Erudition commented 5 years ago

There's also the Safe Network, safe:// and tor onion:// all of which could host elm apps

supermario commented 5 years ago

I have run into this issue in trying to upgrade to 0.19 in our Cordova wrapped Elm app.

In my case related to https://github.com/ionic-team/cordova-plugin-ionic-webview which uses ionic:// when the app is running on-device/emulator on iOS.

This change from 0.18 blocks our upgrade and I cannot seem to see any way around it.

shamansir commented 5 years ago

We really don't like the hacks at all, but in our case we have to do it, since we use Elm 0.19 and we need to provide user with the self-hosted serverless version of our app.

So here's our hack for the WebPack bundle (webpack.config.json), in its glory, it uses replace-in-file-webpack-plugin:

    plugins: [
      new ReplaceInFileWebpackPlugin([{
          files: ['<your-bundle-name>.bundle.js'],
          rules: [
            {
              search: /var .=.\.fragment/,
              replace: function(match) {
                const varLetter = match[4];
                const fragmentLetter = match[6];
                return 'var ' + varLetter + '=' + fragmentLetter + '?' + fragmentLetter + '.fragment:{}';
              }
            },
            {
              search: 'case 1:throw new Error("Browser.application programs cannot handle URLs like this:\\n\\n    "+document.location.href+"\\n\\nWhat is the root? The root of your file system? Try looking at this program with `elm reactor` or some other server.");case 2:',
              replace: 'case 2:'
            },
            {
              search: /return (\w+)\((\w+)\.location\.href\)\.(\w+)\s*\|\|\s*\w+\(1\)/,
              replace: function(match, x, y, z) {
                const href = y + '.location.href';
                const toLocalhost = '\'http://localhost:8080/\'+' + href + '.substring(' + href + '.indexOf(\'index.html\'))';
                return 'return ' + x + '(' + href + ').' + z + '||' + x + '(' + toLocalhost + ').' + z;
              }
            }
          ]
      }])
makeros commented 4 years ago

Hi. Regarding Elm + Navigation + Electron and to avoid the

What is the root? The root of your file system?

there could be another solution that i found recently.

To get this example working in electron:

import Browser
import Browser.Navigation as Nav
import Html exposing (..)
import Html.Attributes exposing (..)
import Url

-- MAIN

main : Program () Model Msg
main =
  Browser.application
    { init = init
    , view = view
    , update = update
    , subscriptions = subscriptions
    , onUrlChange = UrlChanged
    , onUrlRequest = LinkClicked
    }

-- MODEL

type alias Model =
  { key : Nav.Key
  , url : Url.Url
  }

init : () -> Url.Url -> Nav.Key -> ( Model, Cmd Msg )
init flags url key =
  ( Model key url, Cmd.none )

-- UPDATE

type Msg
  = LinkClicked Browser.UrlRequest
  | UrlChanged Url.Url

update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
  case msg of
    LinkClicked urlRequest ->
      case urlRequest of
        Browser.Internal url ->
          ( model, Nav.pushUrl model.key (Url.toString url) )

        Browser.External href ->
          ( model, Nav.load href )

    UrlChanged url ->
      ( { model | url = url }
      , Cmd.none
      )

-- SUBSCRIPTIONS

subscriptions : Model -> Sub Msg
subscriptions _ =
  Sub.none

-- VIEW

view : Model -> Browser.Document Msg
view model =
  { title = "URL Interceptor"
  , body =
      [ text "The current URL is: "
      , b [] [ text (Url.toString model.url) ]
      , ul []
          [ viewLink "/home123"
          , viewLink "/profile"
          , viewLink "/reviews/the-century-of-the-self"
          , viewLink "/reviews/public-opinion"
          , viewLink "/reviews/shah-of-shahs"
          ]
      ]
  }

viewLink : String -> Html msg
viewLink path =
  li [] [ a [ href path ] [ text path ] ]

i used the electron.protocol.interceptStringProtocol like this:

const {
  app, BrowserWindow, protocol
} = require('electron');

const fs = require('fs')
const path = require('path')
const chokidar = require('chokidar')

let mainWindow

function createApp () {
  protocol.interceptStringProtocol('http', function (req, callback) {
    const filePath = path.join('.', req.url.split('http://-/')[1])
    const data = fs.readFileSync(filePath, 'utf8')

    callback({
      mimeType: req.headers['Accept'].split(',')[0],
      data
    });
  }, (error) => {
    if (error) {
      throw Error('failed to register protocol handler for HTTP')
    }

    createWindow();
  })
}

function createWindow() {
  mainWindow = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      nodeIntegration: true
    }
  })
  mainWindow.loadURL('http://-/index.html')

  mainWindow.on('closed', function() {
    mainWindow = null
  })

  chokidar.watch(['app/main.js', 'index.html'])
    .on('change', () => {
      if (mainWindow) {
        mainWindow.reload()
      }
    })
}

app.on('ready', createApp)

app.on('window-all-closed', function() {
  if (process.platform !== 'darwin') {
    app.quit()
  }
})

app.on('activate', function() {
  if (mainWindow === null) {
    createWindow()
  }
})
wolfadex commented 4 years ago

To add another datapoint, BeakerBrowser just hit v1.0.0-beta and uses the protocol hyper://. This protocol is now part of the IANA URL registry too https://twitter.com/pfrazee/status/1261347702073446400?s=20

BendingBender commented 4 years ago

I have run into this issue in trying to upgrade to 0.19 in our Cordova wrapped Elm app.

In my case related to https://github.com/ionic-team/cordova-plugin-ionic-webview which uses ionic:// when the app is running on-device/emulator on iOS.

This change from 0.18 blocks our upgrade and I cannot seem to see any way around it.

To support this even more, the implementation for cordova-ios has changed recently (v6.0.0) and it doesn't support serving the app from http/https scheme any more. The default scheme they've picked is app:// but this is configurable. However, configuring http/https doesn't work, it is simply ignored.

And as already described, you can't simply write an Elm app for cordova-android either, the default implemenation uses the file:// scheme which is also not supported by this package.

ronanyeah commented 4 years ago

I'm currently looking into using Capacitor to publish an Elm app and looks like I'll also be blocked by this.

Strepto commented 2 years ago

I'm also looking into serving a local Elm app on iOS using capacitor. I have developed it fine for Android, but porting it to iOS is slowed by this issue.

It's not possible to serve local assets (on iOS) using http(s) scheme, https scheme is reserved by the WKWebView for remote urls.

mradke commented 2 years ago

@Strepto @ronanyeah I had the same problem with capacitor and came up with a workaround for a specific usecase: https://discourse.elm-lang.org/t/handling-custom-protocol-in-url-with-elm-was-forking-elm-url-to-enable-custom-url-protocol/7856/6 (beware: the "solution" is ugly...)

That being said: at least iOS, MacOS and Android also have the ability to follow "app links" which may reference content in the application (e. g. a signup view). While not strictly necessary it would probably be convenient to simply hand the URL to the webview and letting Elm do the parsing.

philer commented 2 years ago

I'm trying to use Browser.application with electron and am also blocked by this since electron uses file:// urls.

phenax commented 2 years ago

+1 bump. Need this for Browser.application with electron using custom app:// scheme.

tlentz commented 1 year ago

+1 bump. Need this for Browser.application with tauri using custom tauri:// scheme.