e065a2a160806d587a5ef1833b21d55f36c0ff51
[nodejs-repoproxy.git] / lib / cache.js
1 var fs = require("fs");
2 var http = require("http");
3 var url = require("url");
4
5 function maintainCache() {
6         // TODO i should check that im already running here and exit if i am
7         console.log("Cache maintainence routine starting...");
8         console.log("Cache maintainence routine ended...");
9 }
10
11 exports.startTimer = function() {
12         // our once-a-day cache maintainer
13         var cacheTimer = global.repoproxy.scancache*3600*1000;
14         //var cacheTimer = global.repoproxy.scancache*100;
15         setInterval(maintainCache, cacheTimer);
16 }
17
18 function upstreamRequest(unify, callback) {
19         // first do a head request
20         console.log("upsteram as ", unify.requestFor);
21         
22         var endData = false;
23         var xpath = "";
24         var filefd = null;
25         if(unify.topPath !=null) if(unify.topPath != "") if(typeof global.repoproxy.repo[unify.topPath] != "undefined") {
26                 var uplink = global.repoproxy.repo[unify.topPath].url;
27                 xpath = uplink + unify.subPath;
28         }
29         
30         //unify.b.write("would send to '" + xpath + "'");
31         //unify.b.end();
32         
33         console.log("sending off to '%s'", xpath);
34         
35         var headReq = url.parse(xpath);
36         headReq["method"] = "HEAD";
37         
38         getup = http.request(xpath, function(res) {
39                 res.setEncoding("utf8");
40                 
41                 if(!endData) {
42                         console.log("status code is ", typeof res.statusCode);
43                         switch(res.statusCode) {
44                         // TODO: this 301 directory redirect thing needs to work better
45                         case 301:
46                         case 302:
47                                 
48                                 var loc = res.headers.location.substr(res.headers.location.length-4);
49                                 var against_t = xpath + "/";
50                                 var against = against_t.substr(against_t.length-4);
51                                 
52                                 if(loc == against) {
53                                         console.log("got a redirect, upstream for loc => loc/ assuming its a directory");
54                                         makeCacheDir(unify);
55                                         unify.b.writeHead(302, { "Location": unify.originalReq + "/" });
56                                 } else {
57                                         console.log("checked '%s' against '%s', was false, sending 404", loc, against);
58                                         unify.b.writeHead(404, {"Content-Type": "text/plain"});
59                                         unify.b.write("404 Not Found\n");
60                                 }
61                                 unify.b.end();
62                                 endData = true;
63                                 break;
64                                 
65                         case 404:
66                                 unify.b.writeHead(404, {"Content-Type": "text/plain"});
67                                 unify.b.write("404 Not Found\n");
68                                 unify.b.end();
69                                 endData = true;
70                                 break;
71                         case 200:
72                                 makeCacheDir(unify);
73                                 if(unify.isDirectoryRequest) {
74                                         serviceDirectory(unify);                                        
75                                         endData = true;
76                                 } else {
77                                         // this is where it gets ugly
78                                         console.log("do ugly write: ", unify);
79                                         //unify.b.write(data);
80                                         getAndService(unify, xpath);
81                                         
82                                 }
83                                 break;
84                         default:
85                                 console.log(".... data");
86                                 //unify.b.write(data);
87                         }
88                 }               
89                 //console.log("res is now ", res);
90         });
91         
92         getup.end();
93         
94         //console.log("getup: ", getup);
95 }
96
97 exports.upstreamRequest = upstreamRequest;
98
99 function getAndService(unify, xpath) {
100         
101         if(global.repoproxy.downloads[unify.fullFilePath] == 1) {
102                 
103                 unify.b.write("trying to service inline");
104                 unify.b.end();
105         } else {
106                 global.repoproxy.downloads[unify.fullFilePath] = 1;
107         
108                 http.get(xpath, function(res) {
109         
110                     var file = fs.createWriteStream(unify.fullFilePath);
111                 
112                     console.log("res: ", res);
113                 
114                     //res.setEncoding("utf8");
115                 
116                     res.on("data", function(data) {
117                             //console.log("chunk");
118                             file.write(data);
119                             unify.b.write(data);
120                     });
121                 
122                     res.on("end", function() {
123                             console.log("end...");
124                             unify.b.end();
125                             file.end();
126                             global.repoproxy.downloads[unify.fullFilePath] = 0;
127                     });
128                 });
129         }
130 }
131
132 // the service file routine .... PLEASE KILL ME!
133 function serviceFile(unify) {
134         
135         // for now, ignore range.
136
137         // file should already exist, so we just poop it out
138         var inp = fs.createReadStream(unify.fullFilePath);
139         inp.setEncoding("utf8");
140         inp.on("data", function(data) {
141                 unify.b.write(data);
142         });
143         
144         inp.on("end", function(closed) {
145                 unify.b.end();
146         });
147 }
148
149 exports.serviceFile = serviceFile;
150
151 function makeCacheDir(path) {
152         console.log("attempting to create... '%s' as '%s'", path.fullPathDirName, path.subPathDirName);
153         
154         var startAt = path.topFullPath;
155         var nextbits = path.subPathDirName.split("/");
156         for(var i=0; i < nextbits.length; i++) {
157                 startAt += "/" + nextbits[i];
158                 console.log("attempt mkdir on '%s'", startAt);
159                 try {
160                         fs.mkdirSync(startAt);
161                 } catch(e) {
162                         //console.log("e in mkdir, ", e);
163                 }
164         }
165         //process.exit(0);
166 }
167
168 function serviceDirectory(unify) {
169         var nfiles = 0;
170         var res = unify.b;
171         
172         res.write("<html><h1>Directory listing for " + unify.originalReq + "</h1><hr><pre>");
173         if(unify.fullFilePath != "/") res.write("<a href=\"..\">Parent</a>\n\n");
174         fs.readdir(unify.fullFilePath, function(err, files) {
175                 console.log("doing directory listing on: ", unify.fullFilePath);
176                 if(err == null) {
177                         
178                         // TODO: make this work asynchronously...
179                         for(var i=0; i<files.length; i++) {
180                                 // avoiding statSync is too hard for now, will fix later TODO: fix this sync bit
181                                 var stats = fs.statSync(unify.fullFilePath+"/"+files[i]);
182                                 
183                                 if(files[i].match(/^\..*/) == null) {
184                                         if(stats.isDirectory()) {
185                                                 
186                                                 res.write("Directory: <a href=\""+files[i]+"/\">"+files[i]+"/</a>\n");
187                                                 nfiles++;
188                                         } else if(stats.isFile()) {
189                                                 var padlength = 80 - (files[i].length) - stats.size.toString().length;
190                                                 var padding = "";
191                                                 if(padlength > 0) {
192                                                         padding = new Array(padlength).join(" ");
193                                                 }
194                                                 res.write("File:      <a href=\""+files[i]+"\">"+files[i]+"</a>"+padding+stats.size+" bytes\n");
195                                                 nfiles++;
196                                         }
197                                 } else {
198                                         console.log("ignoring file, ", files[i]);
199                                 }
200                         }
201                         
202                         if(nfiles == 0) res.write("Empty directory....\n");
203                         
204                         res.write("<hr></pre>");
205                         res.end();
206                 } else {
207                         res.write("we have entered bizaro world...\n");
208                         res.write("</pre>");
209                         res.end();
210                 }
211         });
212 }
213
214 exports.serviceDirectory = serviceDirectory;