Server-class is functional
[gopher-lib.git] / client.js
1 'use strict';
2
3 const net = require('net');
4 const fs = require('fs');
5
6 const Common = require('./common.js');
7 const GopherResource = Common.Resource;
8 const GopherTypes = Common.Type;
9
10 const supportedTypes = '0145679hIgM';
11 class GopherClient {
12         constructor( options ) {
13                 this.timeout = (options && options.timeout)?options.timeout:5000;
14                 this.parseDir = (options && options.parseDir !== undefined)?options.parseDir:true;
15         }
16
17         get(res, callback, fileName) {
18                 if( !(res instanceof GopherResource)) {
19                         try {
20                                 res = new GopherResource(res);
21                         } catch(err) {
22                                 return callback(err);
23                         }
24                 }
25                 if( supportedTypes.indexOf(res.type) === -1 ) {
26                         return callback(new Error('Unsupported type. Can get resources of type: '+supportedTypes));
27                 }
28
29                 var requestInfo = {
30                         resource: res,
31                         start: null,
32                         stop: null,
33                         elapsed: null,
34                         remoteAddress: null,
35                         bytesReceived: null,
36                         bytesSent: null,
37                         fileName: null
38                 };
39
40                 var fileWriteStream=null;
41                 if( fileName ) {
42                         fileWriteStream = fs.createWriteStream(fileName);
43                         requestInfo.fileName=fileName;
44                 }
45                 requestInfo.start = new Date();
46                 var socket = net.createConnection( {port: res.port, host: res.host}, ()=>{
47                         //Add the selector
48                         var request=res.selector;
49                         //Add a query?
50                         if(res.query!==false) {
51                                 request +='\t'+res.query;
52                         }
53                         //End line
54                         request+='\r\n';
55
56                         socket.write(request);
57                         requestInfo.remoteAddress=socket.remoteAddress;
58                 });
59                 var data=[];
60                 if(fileWriteStream) {
61                         socket.pipe(fileWriteStream);
62                 }
63
64                 socket.on('error', (err)=>{
65                         if(fileName && fileWriteStream) {
66                                 fileWriteStream.end();
67                                 fs.unlinkSync(fileName);
68                         }
69                         return callback(err, {request: requestInfo} );
70                 });
71                 if(!fileWriteStream) {
72                         socket.on('data', (d)=>{
73                                 data.push(d);
74                         });
75                 }
76                 socket.on('end', ()=>{
77                         requestInfo.stop=new Date();
78                         requestInfo.elapsed = requestInfo.stop-requestInfo.start;
79
80                         requestInfo.bytesReceived=socket.bytesRead;
81                         requestInfo.bytesSent=socket.bytesWritten;
82
83                         socket.end();
84                         socket.destroy();
85
86                         if(fileWriteStream) {
87                                 fileWriteStream.end( ()=>{
88                                         return callback(null, { request: requestInfo });
89                                 });
90                         }
91
92                         data = Buffer.concat(data);
93
94                         var dir=null;
95                         var buffer=null;
96                         var txt=null;
97
98                         if(res.type===GopherTypes.directory || res.type===GopherTypes.search) {
99                                 data=data.toString();
100                                 if(!this.parseDir) {
101                                         dir=data;
102                                 } else {
103                                         dir=[];
104                                         var itemNum=0;
105                                         var arr = data.replace(/\r\n/g,'\n').split('\n');
106                                         for( var idx=0; idx < arr.length; ++idx) {
107                                                 var l=arr[idx];
108                                                 if(l.length===0) {
109                                                         break;
110                                                 }
111                                                 switch(l[0]) {
112                                                         case 'i':
113                                                         case '3':
114                                                         dir.push( new GopherResource( '-', '1', '', l[0], l.substring(1).replace(/\t.+$/,'') ) );  
115                                                         break;
116                                                         default:
117                                                         if(l==='.') {
118                                                                 break;
119                                                         }       
120                                                         var split=l.substring(1).split('\t');
121                                                         var name=split[0];
122                                                         var selector=split[1];
123                                                         var host=split[2];
124                                                         var port=split[3];
125                                                         itemNum++;
126                                                         try {
127                                                                 dir.push( new GopherResource( host, port, selector, l[0], name, false, itemNum ) );
128                                                         } catch(e) {
129                                                                 return callback(new Error('Error parsing directory item.'), { request: requestInfo, offendingLine: l, split:split, exception: e });
130                                                         }
131                                                         break;
132                                                 }
133                                         }
134                                 }
135                         } else if(!fileName && (res.type==='0' || res.type==='h' || res.type==='4' || res.type === '6' || res.type === 'M')) {
136                                 txt=data.toString();
137                         } else if(!fileName && (res.type==='5' || res.type==='9' || res.type==='g' ||res.type==='I')) {
138                                 buffer=data;
139                         }
140                         return callback( null, { request: requestInfo, directory: dir, buffer: buffer, text: txt } );
141                 });
142                 
143                 if(this.timeout) {
144                         socket.setTimeout( this.timeout, ()=>{
145                                 socket.destroy();
146                                 callback( new Error('Connection to '+res.toString()+' timed out after '+this.timeout+' ms.') );
147                         });
148                 }
149         }
150 }
151
152 module.exports = GopherClient;