Skip to content

Commit

Permalink
Support file:// protocol (close DevExpress#908)
Browse files Browse the repository at this point in the history
  • Loading branch information
LavrovArtem committed Jan 24, 2017
1 parent 7c829d6 commit c8d5610
Show file tree
Hide file tree
Showing 8 changed files with 110 additions and 13 deletions.
2 changes: 1 addition & 1 deletion src/client/utils/url-resolver.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ export default {
url = url || destLocation.get();

var parsedUrl = parseUrl(url);
var isRelativeUrl = !parsedUrl.host;
var isRelativeUrl = parsedUrl.protocol !== 'file:' && !parsedUrl.host;
var isProtocolRelativeUrl = /^\/\//.test(url) && !!parsedUrl.host;

if (isRelativeUrl || isProtocolRelativeUrl) {
Expand Down
9 changes: 5 additions & 4 deletions src/request-pipeline/context.js
Original file line number Diff line number Diff line change
Expand Up @@ -106,10 +106,11 @@ export default class RequestPipelineContext {
_initRequestNatureInfo () {
var acceptHeader = this.req.headers['accept'];

this.isXhr = !!this.req.headers[XHR_HEADERS.requestMarker];
this.isPage = !this.isXhr && acceptHeader && contentTypeUtils.isPage(acceptHeader);
this.isIframe = this.dest.isIframe;
this.isSpecialPage = urlUtils.isSpecialPage(this.dest.url);
this.isXhr = !!this.req.headers[XHR_HEADERS.requestMarker];
this.isPage = !this.isXhr && acceptHeader && contentTypeUtils.isPage(acceptHeader);
this.isIframe = this.dest.isIframe;
this.isSpecialPage = urlUtils.isSpecialPage(this.dest.url);
this.isFileProtocol = this.dest.protocol === 'file:';
}

// API
Expand Down
50 changes: 50 additions & 0 deletions src/request-pipeline/file-request.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import fs from 'fs';
import { EventEmitter } from 'events';
import { parse } from 'url';

const DISK_RE = /^\/[A-Za-z]:/;

export default class FileRequest extends EventEmitter {
constructor (opts) {
super();

var path = parse(opts.url).pathname;

if (DISK_RE.test(path))
path = path.substr(1);

this.stream = fs.createReadStream(path);
this.headers = {};
this.trailers = {};

this.stream.on('open', () => this._onOpen());
this.stream.on('error', err => this._onOpen(err));
}

_onOpen (err) {
this.statusCode = err ? 404 : 200;
this.emit('response', this);
}

on (type, handler) {
if (type === 'data' || type === 'end') {
if (this.statusCode !== 404) {
this.stream.on(type, handler);

if (type === 'end')
this.stream.on(type, () => this.stream.close());
}
else if (type === 'end')
handler.call(this);
}
else
EventEmitter.prototype.on.call(this, type, handler);
}

pipe (res) {
if (this.statusCode === 404)
res.end('');
else
this.stream.pipe(res);
}
}
3 changes: 2 additions & 1 deletion src/request-pipeline/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import DestinationRequest from './destination-request';
import FileRequest from './file-request';
import RequestPipelineContext from './context';
import * as headerTransforms from './header-transforms';
import { process as processResource } from '../processing/resources';
Expand All @@ -24,7 +25,7 @@ var stages = {
}

else {
var req = new DestinationRequest(opts);
var req = ctx.isFileProtocol ? new FileRequest(opts) : new DestinationRequest(opts);

req.on('response', res => {
ctx.destRes = res;
Expand Down
2 changes: 1 addition & 1 deletion src/utils/content-type.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export function isPage (header) {

export function isCSSResource (contentTypeHeader, acceptHeader) {
return contentTypeHeader.toLowerCase().indexOf(CSS_MIME) > -1 ||
acceptHeader.toLowerCase() === CSS_MIME;
acceptHeader.toLowerCase().indexOf(CSS_MIME) > -1;
}

export function isScriptResource (contentTypeHeader, acceptHeader) {
Expand Down
7 changes: 2 additions & 5 deletions src/utils/url.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const PORT_RE = /:([0-9]*)$/;
const QUERY_AND_HASH_RE = /(\?.+|#[^#]*)$/;
const PATH_AFTER_HOST_RE = /^\/([^\/]+?)\/([\S\s]+)$/;

export const SUPPORTED_PROTOCOL_RE = /^https?:/i;
export const SUPPORTED_PROTOCOL_RE = /^(https?|file):/i;
export const HASH_RE = /^#/;
export const REQUEST_DESCRIPTOR_VALUES_SEPARATOR = '!';
export const SPECIAL_PAGES = ['about:blank', 'about:error'];
Expand Down Expand Up @@ -320,8 +320,5 @@ function isValidPort (port) {
export function isValidUrl (url) {
var parsedUrl = parseUrl(url);

if (!parsedUrl.hostname || parsedUrl.port && !isValidPort(parsedUrl.port))
return false;

return true;
return parsedUrl.protocol === 'file:' || parsedUrl.hostname && (!parsedUrl.port || isValidPort(parsedUrl.port));
}
2 changes: 1 addition & 1 deletion test/playground/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ exports.start = function () {
res.render('403');
}
else {
if (url.indexOf('http://') !== 0 && url.indexOf('https://') !== 0)
if (url.indexOf('file://') !== 0 && url.indexOf('http://') !== 0 && url.indexOf('https://') !== 0)
url = 'http://' + url;

res.statusCode = 301;
Expand Down
48 changes: 48 additions & 0 deletions test/server/proxy-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ var fs = require('fs');
var http = require('http');
var urlLib = require('url');
var request = require('request');
var path = require('path');
var expect = require('chai').expect;
var express = require('express');
var read = require('read-file-relative').readSync;
Expand Down Expand Up @@ -936,6 +937,53 @@ describe('Proxy', function () {
});
});

describe('file protocol', function () {
var getFileProtocolUrl = function (filePath) {
return path.resolve(__dirname, filePath).replace(/\\/g, '/');
};

it('Should process page and ignore search string', function (done) {
session.id = 'sessionId';
session.injectable.scripts.push('/script1.js');
session.injectable.scripts.push('/script2.js');
session.injectable.styles.push('/styles1.css');
session.injectable.styles.push('/styles2.css');

var options = {
url: proxy.openSession('file:///' + getFileProtocolUrl('./data/page/src.html') + '?a=1&b=3', session),

headers: {
accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,*!/!*;q=0.8'
}
};

request(options, function (err, res, body) {
var expected = fs.readFileSync('test/server/data/page/expected.html').toString();

compareCode(body, expected);
done();
});
});

it('Should process stylesheets', function (done) {
session.id = 'sessionId';

var options = {
url: proxy.openSession('file:///' + getFileProtocolUrl('./data/stylesheet/src.css'), session),
headers: {
accept: 'text/css,*/*;q=0.1'
}
};

request(options, function (err, res, body) {
var expected = fs.readFileSync('test/server/data/stylesheet/expected.css').toString();

compareCode(body, expected);
done();
});
});
});

describe('Regression', function () {
it('Should force "Origin" header for the same-domain requests (B234325)', function (done) {
var options = {
Expand Down

0 comments on commit c8d5610

Please sign in to comment.