88 */
99
1010var async = require ( 'async' ) ;
11- var nodeDomain = require ( 'domain' ) ;
11+ var shellFork = require ( 'child_process' ) . fork ;
1212var request = require ( 'supertest' ) ;
1313var crypto = require ( 'crypto' ) ;
1414var restify = require ( 'restify' ) ;
@@ -44,38 +44,35 @@ module.exports = function(hydraterFunction, logger, errLogger) {
4444 /**
4545 * Download the file from task.file_path, store it in a temporary file if there is file_path
4646 */
47- function initHydration ( cb ) {
47+ function downloadFile ( cb ) {
4848 if ( task . file_path ) {
49- // Download the file
50- var stream = fs . createWriteStream ( path ) ;
51-
52- // Store error if statusCode !== 200
53- var err ;
54- stream . on ( "finish" , function ( ) {
55- cb ( err ) ;
56- } ) ;
57-
5849 var apiUrl = url . parse ( task . file_path , false , true ) ;
59- var req = request ( apiUrl . protocol + "//" + apiUrl . host )
60- . get ( apiUrl . path ) ;
61-
62- req . end ( ) . req . once ( 'response' , function ( res ) {
63- if ( res . statusCode !== 200 ) {
64- err = new restify . BadGatewayError ( 'Error when downloading file ' + task . file_path + ': ' + res . statusCode ) ;
65- stream . end ( ) ;
66- this . abort ( ) ;
67- }
68- } ) ;
69-
70- req . pipe ( stream ) ;
50+ request ( apiUrl . protocol + "//" + apiUrl . host )
51+ . get ( apiUrl . path )
52+ . expect ( 200 )
53+ . end ( function ( err , res ) {
54+ if ( err ) {
55+ err = new restify . BadGatewayError ( 'Error when downloading file ' + task . file_path + ': ' + err ) ;
56+ }
57+ cb ( err , res && res . text ) ;
58+ } ) ;
7159 }
7260 else {
7361 cb ( null ) ;
7462 }
7563 } ,
64+ function saveFile ( res , cb ) {
65+ if ( res ) {
66+ fs . writeFile ( path , res , cb ) ;
67+ }
68+ else {
69+ cb ( ) ;
70+ }
71+ } ,
7672 function performHydration ( cb ) {
77- var domain = nodeDomain . create ( ) ;
78-
73+ var child = shellFork ( __dirname + '/child-process.js' , { silent : true } ) ;
74+ var stderr = "" ;
75+ var stdout = "" ;
7976 var timeout ;
8077 /**
8178 * Function to call, either on domain error, on hydration error or successful hydration.
@@ -84,53 +81,73 @@ module.exports = function(hydraterFunction, logger, errLogger) {
8481 var cleaner = function ( err , changes ) {
8582 if ( ! cleaner . called ) {
8683 cleaner . called = true ;
87- domain . exit ( ) ;
88- domain . dispose ( ) ;
8984 cb ( err , changes ) ;
9085 }
86+ clearTimeout ( timeout ) ;
9187 } ;
9288 cleaner . called = false ;
9389
94- domain . on ( 'error' , cleaner ) ;
95-
96- // Run in a domain to prevent memory leak on crash
97- domain . run ( function ( ) {
98- async . waterfall ( [
99- function callHydrationFunction ( cb ) {
100- // Give user access to the final URL callback and the API url (which can be staging, prod or anything)
101- // In case he wants to bypass us and send the changes himself
102- cb . urlCallback = task . callback ;
103- cb . apiUrl = '' ;
104- if ( task . callback ) {
105- var parsed = url . parse ( task . callback ) ;
106- cb . apiUrl = parsed . protocol + "//" + parsed . host ;
107- }
90+ child . on ( 'error' , function ( exitCode ) {
91+ cleaner ( new HydrationError ( "Wild error appeared while spawning child. Exit code:" + exitCode ) ) ;
92+ } ) ;
10893
109- // Call the real function for hydration.
110- hydraterFunction ( ( task . file_path ) ? path : null , task . document , lib . defaultChanges ( ) , cb ) ;
111- }
112- ] , function cleanHydration ( err , changes ) {
113- // If the function replied with an "HydrationError", we'll wrap this in a nicely formatted document
114- // and stop the error from bubbling up.
115- if ( err instanceof HydrationError ) {
116- changes = { } ;
117- changes . hydration_errored = true ;
118- changes . hydration_error = err . message ;
119- err = null ;
120- }
94+ child . stderr . on ( 'readable' , function ( ) {
95+ var chunk ;
96+ while ( null !== ( chunk = child . stderr . read ( ) ) ) {
97+ stderr += chunk ;
98+ }
99+ } ) ;
100+
101+ child . stdout . on ( 'readable' , function ( ) {
102+ var chunk ;
103+ while ( null !== ( chunk = child . stdout . read ( ) ) ) {
104+ stdout += chunk ;
105+ }
106+ } ) ;
107+
108+ child . on ( 'exit' , function ( errCode ) {
109+ if ( errCode !== 0 ) {
110+ cleaner ( new HydrationError ( "Child exiting with err code: " + errCode + stdout + stderr ) ) ;
111+ }
112+ } ) ;
113+
114+ // Build objects to send to child
115+ var options = { } ;
116+ options . urlCallback = task . callback ;
117+ options . apiUrl = '' ;
118+ if ( task . callback ) {
119+ var parsed = url . parse ( task . callback ) ;
120+ options . apiUrl = parsed . protocol + "//" + parsed . host ;
121+ }
122+
123+ child . send ( {
124+ functionPath : hydraterFunction ,
125+ path : ( task . file_path ) ? path : null ,
126+ document : task . document ,
127+ changes : lib . defaultChanges ( ) ,
128+ options : options ,
129+ } ) ;
130+
131+ child . on ( 'message' , function ( res ) {
132+ var err = res . err
133+ // If the function replied with an "HydrationError", we'll wrap this in a nicely formatted document
134+ // and stop the error from bubbling up.
135+ if ( err && err . _hydrationError ) {
136+ res . changes = { } ;
137+ res . changes . hydration_errored = true ;
138+ res . changes . hydration_error = res . err . message ;
139+ err = null ;
140+ }
141+ cleaner ( err , res . changes ) ;
121142
122- // Wait for nexttick, to end this function and be able to properly GC it on domain.dispose().
123- process . nextTick ( function ( ) {
124- cleaner ( err , changes ) ;
125- } ) ;
126- } ) ;
127143 } ) ;
128144
129145 timeout = setTimeout ( function ( ) {
130146 if ( ! cleaner . called ) {
131147 var changes = { } ;
132148 changes . hydration_errored = true ;
133149 changes . hydration_error = "Task took too long." ;
150+ child . kill ( 'SIGKILL' ) ;
134151 cleaner ( null , changes ) ;
135152 }
136153 } , process . env . TIMEOUT || 60 * 1000 ) ;
@@ -183,6 +200,7 @@ module.exports = function(hydraterFunction, logger, errLogger) {
183200 }
184201
185202 var apiUrl = url . parse ( task . callback , false , true ) ;
203+
186204 request ( apiUrl . protocol + "//" + apiUrl . host )
187205 . patch ( apiUrl . path )
188206 . send ( changes )
0 commit comments