Skip to content

Commit 1a49a52

Browse files
committed
feat: support running in background and CORS headers
1 parent 6186568 commit 1a49a52

File tree

6 files changed

+162
-11
lines changed

6 files changed

+162
-11
lines changed

README.md

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,15 @@ Git hooks such as `hooks/update` and `hooks/post-receive` are automatically supp
1818

1919
It also supports HTTP Basic Auth password protection of repos so you can test how your code handles 401 errors.
2020

21+
Using `isomorphic-git` and testing things from browsers? Fear not, `git-http-mock-server` includes appropriate CORS headers.
22+
2123
## How to use
2224

2325
```sh
2426
npm install --save-dev git-http-mock-server
2527
```
2628

27-
Now `cd` to a directory in which you have some bare git repos
28-
and run this server:
29+
Now `cd` to a directory in which you have some bare git repos and run this server:
2930

3031
```sh
3132
> cd __fixtures__
@@ -34,16 +35,33 @@ test-repo1.git test-repo2.git imaginatively-named-repo.git
3435
> git-http-mock-server
3536
```
3637

37-
Now clone and push away...
38+
Now in another shell, clone and push away...
3839
```sh
40+
> git clone http://localhost:8174/test-repo1.git
41+
> git clone http://localhost:8174/test-repo2.git
3942
> git clone http://localhost:8174/imaginatively-named-repo.git
4043
```
4144

45+
## Run in the background
46+
47+
If you want to reuse the same shell (as part of a shell script, for example)
48+
you can run the server as a daemon in the background:
49+
50+
```sh
51+
> git-http-mock-server start
52+
> # do stuff
53+
> git-http-mock-server stop
54+
```
55+
56+
Just be sure to run `start` and `stop` from the same working directory.
57+
(The `start` command writes the PID of the server to `./git-http-mock-server.pid` so that the `stop` command knows what process to kill.)
58+
4259
### Environment Variables
4360

4461
- `GIT_HTTP_MOCK_SERVER_PORT` default is 8174 (to be compatible with [git-http-server](https://github.com/bahamas10/node-git-http-server))
4562
- `GIT_HTTP_MOCK_SERVER_ROUTE` default is `/`
4663
- `GIT_HTTP_MOCK_SERVER_ROOT` default is `process.cwd()`
64+
- `GIT_HTTP_MOCK_SERVER_ALLOW_ORIGIN` default is `*` (used for CORS)
4765

4866
### .htpasswd support
4967

bin.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,13 @@
33
var http = require('http')
44
var path = require('path')
55
var factory = require('./middleware')
6+
var cors = require('./cors')
67

78
var config = {
89
root: path.resolve(process.cwd(), process.env.GIT_HTTP_MOCK_SERVER_ROOT || '.'),
910
glob: '*',
1011
route: process.env.GIT_HTTP_MOCK_SERVER_ROUTE || '/'
1112
}
1213

13-
var server = http.createServer(factory(config))
14+
var server = http.createServer(cors(factory(config)))
1415
server.listen(process.env.GIT_HTTP_MOCK_SERVER_PORT || 8174)

cors.js

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
const origin = process.env.GIT_HTTP_MOCK_SERVER_ALLOW_ORIGIN
2+
const allowHeaders = [
3+
'accept-encoding',
4+
'accept-language',
5+
'accept',
6+
'access-control-allow-origin',
7+
'authorization',
8+
'cache-control',
9+
'connection',
10+
'content-length',
11+
'content-type',
12+
'dnt',
13+
'pragma',
14+
'range',
15+
'referer',
16+
'user-agent',
17+
'x-http-method-override',
18+
'x-requested-with',
19+
]
20+
const exposeHeaders = [
21+
'accept-ranges',
22+
'age',
23+
'cache-control',
24+
'content-length',
25+
'content-language',
26+
'content-type',
27+
'date',
28+
'etag',
29+
'expires',
30+
'last-modified',
31+
'pragma',
32+
'server',
33+
'transfer-encoding',
34+
'vary',
35+
'x-github-request-id',
36+
]
37+
const allowMethods = [
38+
'POST',
39+
'GET',
40+
'OPTIONS'
41+
]
42+
const cors = require('micro-cors')({
43+
allowHeaders,
44+
exposeHeaders,
45+
allowMethods,
46+
allowCredentials: false,
47+
origin
48+
})
49+
50+
module.exports = cors

daemon.js

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
#!/usr/bin/env node
2+
const fs = require('fs')
3+
const path = require('path')
4+
const {spawn} = require('child_process')
5+
const kill = require('tree-kill')
6+
const minimisted = require('minimisted')
7+
8+
const cmdName = 'git-http-mock-server'
9+
const target = require.resolve('./bin.js')
10+
const args = [
11+
target
12+
]
13+
14+
async function main({_: [cmd]}) {
15+
switch (cmd) {
16+
case 'start': {
17+
require('daemonize-process')()
18+
let server = spawn(
19+
'node', args,
20+
{
21+
stdio: 'inherit',
22+
windowsHide: true,
23+
}
24+
)
25+
fs.writeFileSync(
26+
path.join(process.cwd(), `${cmdName}.pid`),
27+
String(process.pid),
28+
'utf8'
29+
)
30+
process.on('exit', server.kill)
31+
return
32+
}
33+
case 'stop': {
34+
let pid
35+
try {
36+
pid = fs.readFileSync(
37+
path.join(process.cwd(), `${cmdName}.pid`),
38+
'utf8'
39+
);
40+
} catch (err) {
41+
console.log(`No ${cmdName}.pid file`)
42+
return
43+
}
44+
pid = parseInt(pid)
45+
console.log('killing', pid)
46+
kill(pid, (err) => {
47+
if (err) {
48+
console.log(err)
49+
} else {
50+
fs.unlinkSync(path.join(process.cwd(), `${cmdName}.pid`))
51+
}
52+
})
53+
return
54+
}
55+
default: {
56+
require(target)
57+
}
58+
}
59+
}
60+
61+
minimisted(main)

middleware.js

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ var fixturez = require('fixturez')
99
var backend = require('git-http-backend')
1010
var htpasswd = require('htpasswd-js')
1111

12+
function pad (str) {
13+
return (str + ' ').slice(0, 7)
14+
}
15+
1216
function factory (config) {
1317
if (!config.root) throw new Error('Missing required "gitHttpServer.root" config option')
1418
if (!config.route) throw new Error('Missing required "gitHttpServer.route" config option')
@@ -39,13 +43,20 @@ function factory (config) {
3943
}
4044

4145
return async function middleware (req, res, next) {
46+
// handle pre-flight OPTIONS
47+
if (req.method === 'OPTIONS') {
48+
res.statusCode = 204
49+
res.end('')
50+
console.log(chalk.green('[git-http-server] 204 ' + pad(req.method) + ' ' + req.url))
51+
return
52+
}
4253
if (!next) next = () => void(0)
4354
try {
4455
var gitdir = getGitDir(req)
4556
} catch (err) {
4657
res.statusCode = 404
4758
res.end(err.message + '\n')
48-
console.log(chalk.red('[git-http-server] 404 ' + req.url))
59+
console.log(chalk.red('[git-http-server] 404 ' + pad(req.method) + ' ' + req.url))
4960
return
5061
}
5162
if (gitdir == null) return next()
@@ -63,9 +74,12 @@ function factory (config) {
6374
let cred = auth.parse(req.headers['authorization'])
6475
if (cred === undefined) {
6576
res.statusCode = 401
77+
// The default reason phrase used in Node is "Unauthorized", but
78+
// we will use "Authorization Required" to match what Github uses.
79+
res.statusMessage = 'Authorization Required'
6680
res.setHeader('WWW-Authenticate', 'Basic')
6781
res.end('Unauthorized' + '\n')
68-
console.log(chalk.red('[git-http-server] 401 ' + req.url))
82+
console.log(chalk.green('[git-http-server] 401 ' + pad(req.method) + ' ' + req.url))
6983
return
7084
}
7185
let valid = await htpasswd.authenticate({
@@ -75,9 +89,12 @@ function factory (config) {
7589
})
7690
if (!valid) {
7791
res.statusCode = 401
92+
// The default reason phrase used in Node is "Unauthorized", but
93+
// we will use "Authorization Required" to match what Github uses.
94+
res.statusMessage = 'Authorization Required'
7895
res.setHeader('WWW-Authenticate', 'Basic')
7996
res.end('Bad credentials' + '\n')
80-
console.log(chalk.red('[git-http-server] 401 ' + req.url))
97+
console.log(chalk.green('[git-http-server] 401 ' + pad(req.method) + ' ' + req.url))
8198
return
8299
}
83100
}
@@ -86,12 +103,12 @@ function factory (config) {
86103
if (err) {
87104
res.statusCode = 500
88105
res.end(err + '\n')
89-
console.log(chalk.red('[git-http-server] 500 ' + req.url))
106+
console.log(chalk.red('[git-http-server] 500 ' + pad(req.method) + ' ' + req.url))
90107
return
91108
}
92109

93110
res.setHeader('content-type', service.type)
94-
console.log(chalk.green('[git-http-server] 200 ' + req.url))
111+
console.log(chalk.green('[git-http-server] 200 ' + pad(req.method) + ' ' + req.url))
95112
// console.log('[git-http-server] ' + service.cmd + ' ' + service.args.concat(gitdir).join(' '))
96113
var ps = spawn(service.cmd, service.args.concat(gitdir))
97114
ps.stdout.pipe(service.createStream()).pipe(ps.stdin)

package.json

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"description": "Clone and push to git repository test fixtures over HTTP.",
55
"main": "index.js",
66
"bin": {
7-
"git-http-mock-server": "bin.js"
7+
"git-http-mock-server": "daemon.js"
88
},
99
"scripts": {
1010
"test": "echo \"No tests\"",
@@ -30,9 +30,13 @@
3030
"dependencies": {
3131
"basic-auth": "^2.0.0",
3232
"chalk": "^2.4.1",
33+
"daemonize-process": "^1.0.9",
3334
"fixturez": "^1.1.0",
3435
"git-http-backend": "^1.0.2",
35-
"htpasswd-js": "^1.0.2"
36+
"htpasswd-js": "^1.0.2",
37+
"micro-cors": "^0.1.1",
38+
"minimisted": "^2.0.0",
39+
"tree-kill": "^1.2.0"
3640
},
3741
"devDependencies": {
3842
"semantic-release": "15.1.7",

0 commit comments

Comments
 (0)