Revision 1a126ed1

View differences:

Makefile
1
EVDIR=$(HOME)/local/libev
2

  
3
OIINC = $(HOME)/projects/oi/
4
OILIB = $(HOME)/projects/oi/liboi.a
5

  
6
V8INC = $(HOME)/src/v8/include
7
#V8LIB = $(HOME)/src/v8/libv8_g.a
8
V8LIB = $(HOME)/src/v8/libv8.a
9

  
10
CFLAGS = -g -I$(V8INC) -I$(OIINC) -DHAVE_GNUTLS=0 -Ideps/ebb 
11
LDFLAGS = -lev -pthread # -lefence
12

  
13
ifdef EVDIR
14
	CFLAGS += -I$(EVDIR)/include
15
	LDFLAGS += -L$(EVDIR)/lib
16
endif
17

  
18
node: node.o node_tcp.o node_http.o node_timer.o ebb_request_parser.o
19
	g++ -o node $^ $(LDFLAGS) $(V8LIB) $(OILIB)
20

  
21
node.o: node.cc 
22
	g++ $(CFLAGS) -c $<
23

  
24
node_tcp.o: node_tcp.cc 
25
	g++ $(CFLAGS) -c $<
26

  
27
node_http.o: node_http.cc 
28
	g++ $(CFLAGS) -c $<
29
	
30
node_timer.o: node_timer.cc 
31
	g++ $(CFLAGS) -c $<
32
	
33
ebb_request_parser.o: ebb_request_parser.c deps/ebb/ebb_request_parser.h 
34
	g++ $(CFLAGS) -c $<
35

  
36
ebb_request_parser.c: deps/ebb/ebb_request_parser.rl
37
	ragel -s -G2 $< -o $@
38

  
39
PASS="\033[1;32mPASS\033[0m\n" 
40
FAIL="\033[1;31mFAIL\033[0m\n" 
41

  
42
test: node test/test_*
43
	@for i in test/test_*; do \
44
		if [ -x $$i ]; then \
45
			echo "\n\033[1m$$i\033[0m";	\
46
			./$$i && echo $(PASS) || echo $(FAIL); \
47
		fi \
48
	done 
49

  
50
clean:
51
	rm -f ebb_request_parser.c
52
	rm -f *.o 
53
	rm -f node
54

  
55
.PHONY: clean test
deps/libeio/wscript
1
import Options
2

  
3
def set_options(opt):
4
  pass
5
  #opt.tool_options('compiler_cc')
6

  
7
def configure(conf):
8
  print "--- libeio ---"
9
  #conf.check_tool('compiler_cc')
10

  
11
  conf.check_cc(lib="pthread", header_name="pthread.h", function_name="pthread_create", mandatory=True)
12

  
13
  platform_string = "__" + Options.platform
14
  if Options.platform == "linux2":
15
    platform_string = "__linux"
16
  conf.define(platform_string, 1)
17

  
18
  conf.check_cc(msg="Checking for futimes(2)", define_name="HAVE_FUTIMES", fragment="""
19
    #include <sys/types.h>
20
    #include <sys/time.h>
21
    #include <utime.h>
22
    struct timeval tv[2];
23
    int res;
24
    int fd;
25
    int main(void)
26
    {
27
       res = futimes (fd, tv);
28
       return 0;
29
    }
30
  """)
31

  
32
  conf.check_cc(msg="Checking for readahead(2)", define_name="HAVE_READAHEAD", fragment="""
33
    #include <fcntl.h>
34
    int main(void)
35
    {
36
       int fd = 0;
37
       size_t count = 2;
38
       ssize_t res;
39
       res = readahead (fd, 0, count);
40
       return 0;
41
    }
42
  """)
43

  
44
  conf.check_cc(msg="Checking for fdatasync(2)", define_name="HAVE_FDATASYNC", fragment="""
45
    #include <unistd.h>
46
    int main(void)
47
    {
48
       int fd = 0;
49
       fdatasync (fd);
50
       return 0;
51
    }
52
  """)
53

  
54
  conf.check_cc(msg="Checking for pread(2) and pwrite(2)", define_name="HAVE_PREADWRITE", fragment="""
55
    #include <unistd.h>
56
    int main(void)
57
    {
58
       int fd = 0;
59
       size_t count = 1;
60
       char buf;
61
       off_t offset = 1;
62
       ssize_t res;
63
       res = pread (fd, &buf, count, offset);
64
       res = pwrite (fd, &buf, count, offset);
65
       return 0;
66
    }
67
  """)
68

  
69
  conf.check_cc(msg="Checking for sendfile(2)" , defines=[platform_string + "=1"] , define_name="HAVE_SENDFILE" , fragment=""" 
70
    # include <sys/types.h>
71
    #if __linux
72
    # include <sys/sendfile.h>
73
    #elif __freebsd
74
    # include <sys/socket.h>
75
    # include <sys/uio.h>
76
    #elif __hpux
77
    # include <sys/socket.h>
78
    #else
79
    # error unsupported architecture
80
    #endif
81
    int main(void)
82
    {
83
       int fd = 0;
84
       off_t offset = 1;
85
       size_t count = 2;
86
       ssize_t res;
87
    #if __linux
88
       res = sendfile (fd, fd, offset, count);
89
    #elif __freebsd
90
       res = sendfile (fd, fd, offset, count, 0, &offset, 0);
91
    #elif __hpux
92
       res = sendfile (fd, fd, offset, count, 0, 0);
93
    #endif
94
       return 0;
95
    }
96
  """)
97

  
98
  conf.check_cc(msg="Checking for sync_file_range(2) ", fragment="""
99
    #include <fcntl.h>
100
    int main(void)
101
    {
102
       int fd = 0;
103
       off64_t offset = 1;
104
       off64_t nbytes = 1;
105
       unsigned int flags = SYNC_FILE_RANGE_WAIT_BEFORE|SYNC_FILE_RANGE_WRITE|SYNC_FILE_RANGE_WAIT_AFTER;
106
       ssize_t res;
107
       res = sync_file_range (fd, offset, nbytes, flags);
108
       return 0;
109
    }
110
  """, define_name="HAVE_SYNC_FILE_RANGE")
111

  
112
  conf.write_config_header('config.h')
113

  
114
def build(bld):
115
  libeio = bld.new_task_gen("cc", "staticlib")
116
  libeio.source = "eio.c"
117
  libeio.target = 'eio'
118
  libeio.name = 'eio'
119
  libeio.includes = '. ../..'
120

  
deps/libev/wscript
1
import Options
2

  
3
def set_options(opt):
4
  pass
5
  #opt.tool_options('compiler_cc')
6

  
7
def configure(conf):
8
  print "--- libev ---"
9
  #conf.check_tool('compiler_cc')
10

  
11
  platform_string = "__" + Options.platform
12
  if Options.platform == "linux2":
13
    platform_string = "__linux"
14
  conf.define(platform_string, 1)
15

  
16
  conf.check_cc(header_name="sys/inotify.h")
17
  conf.check_cc(header_name="sys/epoll.h")
18
  conf.check_cc(header_name="sys/event.h")
19
  conf.check_cc(header_name="sys/queue.h")
20
  conf.check_cc(header_name="port.h")
21
  conf.check_cc(header_name="poll.h")
22
  conf.check_cc(header_name="sys/select.h")
23
  conf.check_cc(header_name="sys/eventfd.h")
24
  conf.check_cc(header_name="sys/inotify.h", function_name="inotify_init")
25
  conf.check_cc(header_name="sys/epoll.h", function_name="epoll_ctl")
26
  conf.check_cc(header_name="sys/queue.h", function_name="kqueue")
27
  conf.check_cc(header_name="port.h", function_name="port_create")
28
  conf.check_cc(header_name="poll.h", function_name="poll")
29
  conf.check_cc(header_name="sys/select.h", function_name="select")
30
  conf.check_cc(header_name="sys/eventfd.h", function_name="eventfd")
31
  code = """
32
      #include <syscall.h>
33
      #include <time.h>
34

  
35
      int main() {
36
          struct timespec ts; 
37
          int status = syscall (SYS_clock_gettime, CLOCK_REALTIME, &ts);
38
          return 0;
39
      }
40
  """
41
  conf.check_cc(fragment=code, define_name="HAVE_CLOCK_SYSCALL")
42
  conf.check_cc(lib="rt", header_name="time.h", function_name="clock_gettime")
43
  conf.check_cc(lib="rt", header_name="time.h", function_name="nanosleep")
44
  conf.check_cc(lib="m", header_name="math.h", function_name="ceil")
45

  
46
  conf.define("HAVE_CONFIG_H", 1)
47
  conf.write_config_header('config.h')
48

  
49
def build(bld):
50
  libev = bld.new_task_gen("cc", "staticlib")
51
  libev.source = 'ev.c'
52
  libev.target = 'ev'
53
  libev.name = 'ev'
54
  libev.includes = '. ../..'
55

  
node.cc
1
#include "node.h"
2

  
3
#include "node_tcp.h"
4
#include "node_http.h"
5
#include "node_timer.h"
6

  
7
#include <stdio.h>
8
#include <assert.h>
9

  
10
#include <string>
11
#include <list>
12
#include <map>
13

  
14
using namespace v8;
15
using namespace std;
16

  
17
static int exit_code = 0;
18

  
19
// Reads a file into a v8 string.
20
static Handle<String>
21
ReadFile (const string& name) 
22
{
23

  
24
  FILE* file = fopen(name.c_str(), "rb");
25
  if (file == NULL) return Handle<String>();
26
 
27
  fseek(file, 0, SEEK_END);
28
  int size = ftell(file);
29
  rewind(file);
30

  
31
  char chars[size+1];
32
  chars[size] = '\0';
33
  for (int i = 0; i < size;) {
34
    int read = fread(&chars[i], 1, size - i, file);
35
    if(read <= 0) {
36
      perror("read()");
37
    }
38
    i += read;
39
  }
40

  
41
  uint16_t expanded_base[size+1];
42
  expanded_base[size] = '\0';
43
  for(int i = 0; i < size; i++) 
44
    expanded_base[i] = chars[i];
45

  
46
  fclose(file);
47

  
48
  HandleScope scope;
49
  Local<String> result = String::New(expanded_base, size);
50

  
51
  return scope.Close(result);
52
}
53

  
54
static Handle<Value>
55
Log (const Arguments& args) 
56
{
57
  if (args.Length() < 1) return v8::Undefined();
58
  HandleScope scope;
59
  Handle<Value> arg = args[0];
60
  String::Utf8Value value(arg);
61

  
62
  printf("%s\n", *value);
63
  fflush(stdout);
64

  
65
  return Undefined();
66
}
67

  
68

  
69
static Handle<Value>
70
BlockingFileRead (const Arguments& args)
71
{
72
  if (args.Length() < 1) return v8::Undefined();
73
  HandleScope scope;
74

  
75
  String::Utf8Value filename(args[0]);
76
  Handle<String> output = ReadFile (*filename);
77
  return scope.Close(output);
78
}
79

  
80
static void
81
OnFatalError (const char* location, const char* message)
82
{
83
  fprintf(stderr, "Fatal error. %s %s\n", location, message);
84
  ev_unloop(node_loop(), EVUNLOOP_ALL);
85
}
86

  
87

  
88
// Extracts a C string from a V8 Utf8Value.
89
const char* ToCString(const v8::String::Utf8Value& value) {
90
  return *value ? *value : "<string conversion failed>";
91
}
92

  
93
void ReportException(v8::TryCatch* try_catch) {
94
  v8::HandleScope handle_scope;
95
  v8::String::Utf8Value exception(try_catch->Exception());
96
  const char* exception_string = ToCString(exception);
97
  v8::Handle<v8::Message> message = try_catch->Message();
98
  if (message.IsEmpty()) {
99
    // V8 didn't provide any extra information about this error; just
100
    // print the exception.
101
    printf("%s\n", exception_string);
102
  } else {
103
    message->PrintCurrentStackTrace(stdout);
104

  
105
    // Print (filename):(line number): (message).
106
    v8::String::Utf8Value filename(message->GetScriptResourceName());
107
    const char* filename_string = ToCString(filename);
108
    int linenum = message->GetLineNumber();
109
    printf("%s:%i: %s\n", filename_string, linenum, exception_string);
110
    // Print line of source code.
111
    v8::String::Utf8Value sourceline(message->GetSourceLine());
112
    const char* sourceline_string = ToCString(sourceline);
113
    printf("%s\n", sourceline_string);
114
    // Print wavy underline (GetUnderline is deprecated).
115
    int start = message->GetStartColumn();
116
    for (int i = 0; i < start; i++) {
117
      printf(" ");
118
    }
119
    int end = message->GetEndColumn();
120
    for (int i = start; i < end; i++) {
121
      printf("^");
122
    }
123
    printf("\n");
124
  }
125
}
126

  
127
void
128
node_fatal_exception (TryCatch &try_catch)
129
{
130
  ReportException(&try_catch);
131
  ev_unloop(node_loop(), EVUNLOOP_ALL);
132
  exit_code = 1;
133
}
134

  
135

  
136
// Executes a string within the current v8 context.
137
bool ExecuteString(v8::Handle<v8::String> source,
138
                   v8::Handle<v8::Value> name,
139
                   bool print_result,
140
                   bool report_exceptions) {
141
  v8::HandleScope handle_scope;
142
  v8::TryCatch try_catch;
143
  v8::Handle<v8::Script> script = v8::Script::Compile(source, name);
144
  if (script.IsEmpty()) {
145
    // Print errors that happened during compilation.
146
    if (report_exceptions)
147
      ReportException(&try_catch);
148
    return false;
149
  } else {
150
    v8::Handle<v8::Value> result = script->Run();
151
    if (result.IsEmpty()) {
152
      // Print errors that happened during execution.
153
      if (report_exceptions)
154
        ReportException(&try_catch);
155
      return false;
156
    } else {
157
      if (print_result && !result->IsUndefined()) {
158
        // If all went well and the result wasn't undefined then print
159
        // the returned value.
160
        v8::String::Utf8Value str(result);
161
        const char* cstr = ToCString(str);
162
        printf("%s\n", cstr);
163
      }
164
      return true;
165
    }
166
  }
167
}
168

  
169
// The callback that is invoked by v8 whenever the JavaScript 'load'
170
// function is called.  Loads, compiles and executes its argument
171
// JavaScript file.
172
v8::Handle<v8::Value> Load(const v8::Arguments& args) {
173
  for (int i = 0; i < args.Length(); i++) {
174
    v8::HandleScope handle_scope;
175
    v8::String::Utf8Value file(args[i]);
176
    if (*file == NULL) {
177
      return v8::ThrowException(v8::String::New("Error loading file"));
178
    }
179
    v8::Handle<v8::String> source = ReadFile(*file);
180
    if (source.IsEmpty()) {
181
      return v8::ThrowException(v8::String::New("Error loading file"));
182
    }
183
    if (!ExecuteString(source, v8::String::New(*file), false, true)) {
184
      return v8::ThrowException(v8::String::New("Error executing file"));
185
    }
186
  }
187
  return v8::Undefined();
188
}
189

  
190
int
191
main (int argc, char *argv[]) 
192
{
193
  V8::SetFlagsFromCommandLine(&argc, argv, true);
194

  
195
  if(argc != 2)  {
196
    fprintf(stderr, "No script was specified.\n");
197
    return 1;
198
  }
199
  string filename(argv[1]);
200

  
201
  HandleScope handle_scope;
202

  
203
  Persistent<Context> context = Context::New(NULL, ObjectTemplate::New());
204
  Context::Scope context_scope(context);
205

  
206
  Local<Object> g = Context::GetCurrent()->Global();
207

  
208
  g->Set ( String::New("log")
209
         , FunctionTemplate::New(Log)->GetFunction()
210
         );
211

  
212
  g->Set ( String::New("load")
213
         , FunctionTemplate::New(Load)->GetFunction()
214
         );
215

  
216
  g->Set ( String::New("blockingFileRead")
217
         , FunctionTemplate::New(BlockingFileRead)->GetFunction()
218
         );
219

  
220
  Init_timer(g);
221
  Init_tcp(g);
222
  Init_http(g);
223

  
224
  V8::SetFatalErrorHandler(OnFatalError);
225

  
226
  v8::Handle<v8::String> source = ReadFile(filename);
227
  ExecuteString(source, String::New(filename.c_str()), false, true);
228

  
229
  ev_loop(node_loop(), 0);
230

  
231
  context.Dispose();
232

  
233
  return exit_code;
234
}
235

  
node.h
1
#ifndef node_h
2
#define node_h
3

  
4
#include <ev.h>
5
#include <v8.h>
6

  
7
void node_fatal_exception (v8::TryCatch &try_catch); 
8
#define node_loop() ev_default_loop(0)
9

  
10
#endif // node_h
11

  
node_http.cc
1
#include "node_http.h"
2
#include "node.h"
3

  
4
#include <oi_socket.h>
5
#include <ebb_request_parser.h>
6

  
7
#include <string>
8
#include <list>
9

  
10
using namespace v8;
11
using namespace std;
12

  
13
static Persistent<ObjectTemplate> request_template;
14

  
15
// globals
16
static Persistent<String> path_str; 
17
static Persistent<String> uri_str; 
18
static Persistent<String> query_string_str; 
19
static Persistent<String> fragment_str; 
20
static Persistent<String> method_str; 
21
static Persistent<String> http_version_str; 
22
static Persistent<String> headers_str; 
23

  
24
static Persistent<String> on_request_str; 
25
static Persistent<String> on_body_str; 
26
static Persistent<String> respond_str; 
27

  
28
static Persistent<String> copy_str;
29
static Persistent<String> delete_str;
30
static Persistent<String> get_str;
31
static Persistent<String> head_str;
32
static Persistent<String> lock_str;
33
static Persistent<String> mkcol_str;
34
static Persistent<String> move_str;
35
static Persistent<String> options_str;
36
static Persistent<String> post_str;
37
static Persistent<String> propfind_str;
38
static Persistent<String> proppatch_str;
39
static Persistent<String> put_str;
40
static Persistent<String> trace_str;
41
static Persistent<String> unlock_str;
42

  
43
#define INVALID_STATE_ERR 1
44

  
45
class Server {
46
public:
47
  Server (Handle<Object> _js_server);
48
  ~Server ();
49

  
50
  int Start(struct addrinfo *servinfo);
51
  void Stop();
52

  
53
  Handle<Value> Callback()
54
  {
55
    HandleScope scope;
56
    Handle<Value> value = js_server->Get(on_request_str);
57
    return scope.Close(value);
58
  }
59

  
60
private:
61
  oi_server server;
62
  Persistent<Object> js_server;
63
};
64

  
65
class HttpRequest;
66

  
67
class Connection {
68
public:
69
  Connection();
70
  ~Connection();
71

  
72
  void Parse(const void *buf, size_t count);
73
  void Write();
74
  void Close();
75
  void AddRequest (HttpRequest *request);
76

  
77
  oi_socket socket;
78
  Persistent<Function> js_onrequest;
79

  
80
private:
81
  ebb_request_parser parser;
82
  list<HttpRequest*> requests;
83
  list<HttpRequest*> finished_requests;
84
  friend class Server;
85
};
86

  
87
class HttpRequest {
88
 public:
89
  HttpRequest (Connection &c);
90
  /* Deleted from C++ as soon as possible.
91
   * Javascript object might linger. This is okay
92
   */
93
  ~HttpRequest();
94

  
95
  void MakeBodyCallback (const char *base, size_t length);
96
  Local<Object> CreateJSObject ();
97
  void Respond (Handle<Value> data);
98

  
99
  string path;
100
  string query_string;
101
  string fragment;
102
  string uri;
103

  
104
  list<string> header_fields;
105
  list<string> header_values;
106

  
107
  Connection &connection;
108
  ebb_request parser_info;
109

  
110
  list<oi_buf*> output;
111
  bool done;
112
  Persistent<Object> js_object;
113
};
114

  
115
static Handle<Value>
116
GetMethodString (int method)
117
{
118
  switch(method) {
119
    case EBB_COPY:      return copy_str;
120
    case EBB_DELETE:    return delete_str;
121
    case EBB_GET:       return get_str;
122
    case EBB_HEAD:      return head_str;
123
    case EBB_LOCK:      return lock_str;
124
    case EBB_MKCOL:     return mkcol_str;
125
    case EBB_MOVE:      return move_str;
126
    case EBB_OPTIONS:   return options_str;
127
    case EBB_POST:      return post_str;
128
    case EBB_PROPFIND:  return propfind_str;
129
    case EBB_PROPPATCH: return proppatch_str;
130
    case EBB_PUT:       return put_str;
131
    case EBB_TRACE:     return trace_str;
132
    case EBB_UNLOCK:    return unlock_str;
133
  }
134
  return Null();
135
}
136

  
137
static Handle<Value>
138
RespondCallback (const Arguments& args) 
139
{
140
  HandleScope scope;
141

  
142
  Handle<Value> v = args.Holder()->GetInternalField(0);
143
  if(v->IsUndefined()) {
144
    // check that args.Holder()->GetInternalField(0)
145
    // is not NULL if so raise INVALID_STATE_ERR
146
    printf("null request external\n");
147
    ThrowException(Integer::New(INVALID_STATE_ERR)); 
148
    return Undefined();
149
  }
150
  Handle<External> field = Handle<External>::Cast(v);
151
  HttpRequest* request = static_cast<HttpRequest*>(field->Value());
152
  request->Respond(args[0]);
153
  return Undefined();
154
}
155

  
156
void
157
HttpRequest::Respond (Handle<Value> data)
158
{
159
  if(data == Null()) {
160
    done = true;
161
  } else {
162
    Handle<String> s = data->ToString();
163
    oi_buf *buf = oi_buf_new2(s->Length());
164

  
165
    uint16_t expanded[s->Length()];
166
    s->Write(expanded, 0, s->Length());
167

  
168
    for(int i = 0; i < s->Length(); i++) {
169
      buf->base[i] = expanded[i];
170
    }
171

  
172
    output.push_back(buf);
173
  }
174
  connection.Write();
175
}
176

  
177

  
178
static void
179
on_path (ebb_request *req, const char *buf, size_t len)
180
{
181
  HttpRequest *request = static_cast<HttpRequest*> (req->data);
182
  request->path.append(buf, len);
183
}
184

  
185
static void
186
on_uri (ebb_request *req, const char *buf, size_t len)
187
{
188
  HttpRequest *request = static_cast<HttpRequest*> (req->data);
189
  request->uri.append(buf, len);
190
}
191

  
192
static void
193
on_query_string (ebb_request *req, const char *buf, size_t len)
194
{
195
  HttpRequest *request = static_cast<HttpRequest*> (req->data);
196
  request->query_string.append(buf, len);
197
}
198

  
199
static void
200
on_fragment (ebb_request *req, const char *buf, size_t len)
201
{
202
  HttpRequest *request = static_cast<HttpRequest*> (req->data);
203
  request->fragment.append(buf, len);
204
}
205

  
206
static const char upcase[] =
207
  "\0______________________________"
208
  "_________________0123456789_____"
209
  "__ABCDEFGHIJKLMNOPQRSTUVWXYZ____"
210
  "__ABCDEFGHIJKLMNOPQRSTUVWXYZ____"
211
  "________________________________"
212
  "________________________________"
213
  "________________________________"
214
  "________________________________";
215

  
216
static void
217
on_header_field (ebb_request *req, const char *buf, size_t len, int header_index)
218
{
219
  HttpRequest *request = static_cast<HttpRequest*> (req->data);
220

  
221
  char upbuf[len];
222

  
223
  for(int i = 0; i < len; i++) 
224
    upbuf[i] = upcase[buf[i]];
225

  
226
  if( request->header_fields.size() == header_index - 1) {
227
    request->header_fields.back().append(upbuf, len);
228
  } else { 
229
    request->header_fields.push_back( string(upbuf, len) );
230
  }
231
}
232

  
233
static void
234
on_header_value (ebb_request *req, const char *buf, size_t len, int header_index)
235
{
236
  HttpRequest *request = static_cast<HttpRequest*> (req->data);
237

  
238
  if( request->header_values.size() == header_index - 1) {
239
    request->header_values.back().append(buf, len);
240
  } else { 
241
    request->header_values.push_back( string(buf, len) );
242
  }
243
}
244

  
245
static void
246
on_headers_complete (ebb_request *req)
247
{
248
  HttpRequest *request = static_cast<HttpRequest*> (req->data);
249

  
250
  HandleScope scope;
251

  
252
  Handle<Object> js_request = request->CreateJSObject();
253

  
254
  // Set up an exception handler before calling the Process function
255
  TryCatch try_catch;
256

  
257
  // Invoke the process function, giving the global object as 'this'
258
  // and one argument, the request.
259
  const int argc = 1;
260
  Handle<Value> argv[argc] = { js_request };
261
  Handle<Value> r = request->connection.js_onrequest->Call(Context::GetCurrent()->Global(), argc, argv);
262

  
263
  if(try_catch.HasCaught())
264
    node_fatal_exception(try_catch);
265
}
266

  
267
static void
268
on_request_complete (ebb_request *req)
269
{
270
  HttpRequest *request = static_cast<HttpRequest*> (req->data);
271
  request->MakeBodyCallback(NULL, 0); // EOF
272
}
273

  
274
static void
275
on_body (ebb_request *req, const char *base, size_t length)
276
{
277
  HttpRequest *request = static_cast<HttpRequest*> (req->data);
278

  
279
  if(length)
280
    request->MakeBodyCallback(base, length);
281
}
282

  
283
static ebb_request * on_request
284
  ( void *data
285
  ) 
286
{
287
  Connection *connection = static_cast<Connection*> (data);
288

  
289
  HttpRequest *request = new HttpRequest(*connection);
290
  connection->AddRequest(request);
291
  
292
  return &request->parser_info;
293
}
294

  
295
static void on_read 
296
  ( oi_socket *socket
297
  , const void *buf
298
  , size_t count
299
  )
300
{
301
  Connection *connection = static_cast<Connection*> (socket->data);
302
  if(count == 0) {
303
    connection->Close();
304
  } else {
305
    //write(1, buf, count);
306
    connection->Parse(buf, count);  
307
  }
308
}
309

  
310
static void on_close 
311
  ( oi_socket *socket
312
  )
313
{
314
  Connection *connection = static_cast<Connection*> (socket->data);
315
  delete connection;
316
}
317

  
318
HttpRequest::~HttpRequest ()
319
{
320
  HandleScope scope; // needed?
321
  // delete a reference c++ HttpRequest
322
  js_object->SetInternalField(0, Undefined());
323
  js_object->Delete(respond_str);
324
  // dispose of Persistent handle so that 
325
  // it can be GC'd normally.
326
  js_object.Dispose();
327
}
328

  
329
HttpRequest::HttpRequest (Connection &c) : connection(c)
330
{
331
  ebb_request_init(&parser_info); 
332
  parser_info.on_path             = on_path;
333
  parser_info.on_query_string     = on_query_string;
334
  parser_info.on_uri              = on_uri;
335
  parser_info.on_fragment         = on_fragment;
336
  parser_info.on_header_field     = on_header_field;
337
  parser_info.on_header_value     = on_header_value;
338
  parser_info.on_headers_complete = on_headers_complete;
339
  parser_info.on_body             = on_body;
340
  parser_info.on_complete         = on_request_complete;
341
  parser_info.data                = this;
342

  
343
  done = false;
344
}
345

  
346
void
347
HttpRequest::MakeBodyCallback (const char *base, size_t length)
348
{
349
  HandleScope handle_scope;
350

  
351
  Handle<Value> onbody_val = js_object->Get(on_body_str);  
352
  if (!onbody_val->IsFunction()) return;
353
  Handle<Function> onbody = Handle<Function>::Cast(onbody_val);
354

  
355
  TryCatch try_catch;
356
  const int argc = 1;
357
  Handle<Value> argv[argc];
358
  
359
  if(length) {
360
    // TODO ByteArray?
361
    //
362
    
363
    uint16_t expanded_base[length];
364
    for(int i = 0; i < length; i++) {
365
      expanded_base[i] = base[i];
366
    }
367

  
368
    Handle<String> chunk = String::New(expanded_base, length);
369
    argv[0] = chunk;
370
  } else {
371
    argv[0] = Null();
372
  }
373

  
374
  Handle<Value> result = onbody->Call(js_object, argc, argv);
375

  
376
  if(try_catch.HasCaught())
377
    node_fatal_exception(try_catch);
378
}
379

  
380
Local<Object>
381
HttpRequest::CreateJSObject ()
382
{
383
  HandleScope scope;
384

  
385
  if (request_template.IsEmpty()) {
386
    Handle<ObjectTemplate> raw_template = ObjectTemplate::New();
387
    raw_template->SetInternalFieldCount(1);
388
    raw_template->Set(respond_str, FunctionTemplate::New(RespondCallback));
389

  
390
    request_template = Persistent<ObjectTemplate>::New(raw_template);
391
  }
392

  
393
  // Create an empty http request wrapper.
394
  Handle<Object> result = request_template->NewInstance();
395

  
396
  // Wrap the raw C++ pointer in an External so it can be referenced
397
  // from within JavaScript.
398
  Handle<External> request_ptr = External::New(this);
399

  
400
  // Store the request pointer in the JavaScript wrapper.
401
  result->SetInternalField(0, request_ptr);
402

  
403
  result->Set ( path_str
404
              , String::New(path.c_str(), path.length())
405
              );
406

  
407
  result->Set ( uri_str
408
              , String::New(uri.c_str(), uri.length())
409
              );
410

  
411
  result->Set ( query_string_str
412
              , String::New(query_string.c_str(), query_string.length())
413
              );
414

  
415
  result->Set ( fragment_str
416
              , String::New(fragment.c_str(), fragment.length())
417
              );
418

  
419
  result->Set ( method_str
420
              , GetMethodString(parser_info.method)
421
              );
422

  
423
  char version[10];
424
  snprintf ( version
425
           , 10 // big enough? :)
426
           , "%d.%d"
427
           , parser_info.version_major
428
           , parser_info.version_minor
429
           ); 
430
  result->Set ( http_version_str
431
              , String::New(version)
432
              );
433

  
434
  
435
  Handle<Object> headers = Object::New();
436
  list<string>::iterator field_iterator = header_fields.begin();
437
  list<string>::iterator value_iterator = header_values.begin();
438
  while( value_iterator != header_values.end() ) {
439
    string &f = *field_iterator;
440
    string &v = *value_iterator;
441
    
442
    headers->Set( String::NewSymbol(f.c_str(), f.length())
443
                , String::New(v.c_str(), v.length() ) 
444
                );
445

  
446
    field_iterator++;
447
    value_iterator++;
448
  }
449
  result->Set(headers_str, headers);
450

  
451
  js_object = Persistent<Object>::New(result);
452
  // XXX does the request's js_object need a MakeWeak callback?
453
  // i dont think so because at some point the connection closes
454
  // and we're going to delete the request. 
455
  
456
  return scope.Close(result);
457
}
458

  
459

  
460
static oi_socket*
461
on_connection (oi_server *_server, struct sockaddr *addr, socklen_t len)
462
{
463
  HandleScope scope;
464

  
465
  Server *server = static_cast<Server*> (_server->data);
466

  
467
  Handle<Value> callback_v = server->Callback();
468

  
469
  if(callback_v == Undefined())
470
    return NULL;
471

  
472
  Connection *connection = new Connection();
473

  
474
  Handle<Function> f = Handle<Function>::Cast(callback_v);
475
  connection->js_onrequest = Persistent<Function>::New(f);
476

  
477
  return &connection->socket;
478
}
479

  
480
Connection::Connection ()
481
{
482
  oi_socket_init (&socket, 30.0); // TODO make timeout adjustable
483
  socket.on_read    = on_read;
484
  socket.on_error   = NULL;
485
  socket.on_close   = on_close;
486
  socket.on_timeout = NULL;
487
  socket.on_drain   = NULL;
488
  socket.data       = this;
489

  
490
  ebb_request_parser_init (&parser);
491
  parser.new_request = on_request;
492
  parser.data        = this;
493
}
494

  
495
Connection::~Connection ()
496
{
497
  list<HttpRequest*>::iterator it = requests.begin();
498

  
499
  // delete all the requests
500

  
501
  for(it = requests.begin(); it != requests.end(); it++)
502
    delete *it;
503

  
504
  for(it = finished_requests.begin(); it != finished_requests.end(); it++)
505
    delete *it;
506
}
507

  
508
void
509
Connection::Parse(const void *buf, size_t count)
510
{
511
  // FIXME change ebb_request_parser to have void* arg
512
  ebb_request_parser_execute ( &parser
513
                             , static_cast<const char*> (buf) 
514
                             , count
515
                             );
516

  
517
  if(ebb_request_parser_has_error(&parser)) {
518
    fprintf(stderr, "parse error closing connection\n");
519
    oi_socket_close(&socket);
520
  }
521
}
522

  
523
void 
524
Connection::AddRequest(HttpRequest *request)
525
{
526
  requests.push_back(request);
527
}
528

  
529
void
530
Connection::Write ( ) 
531
{
532
  if(requests.size() == 0)
533
    return;
534

  
535
  HttpRequest *request = requests.front(); 
536

  
537
  while(request->output.size() > 0) {
538
    oi_buf *buf = request->output.front();
539
    oi_socket_write(&socket, buf);
540
    request->output.pop_front();
541
  }
542

  
543
  if(request->done) {
544
    if(!ebb_request_should_keep_alive(&request->parser_info)) {
545
      socket.on_drain = oi_socket_close;
546
    } 
547

  
548
    requests.pop_front();
549
    finished_requests.push_back(request);
550

  
551
    Write();
552
  }
553
}
554

  
555
void
556
Connection::Close ( ) 
557
{
558
  oi_socket_close(&socket);
559
}
560

  
561
static void
562
server_destroy (Persistent<Value> _, void *data)
563
{
564
  Server *server = static_cast<Server *> (data);
565
  delete server;
566
}
567

  
568
Server::Server (Handle<Object> _js_server)
569
{
570
  oi_server_init(&server, 1024);
571
  server.on_connection = on_connection;
572
  server.data = this;
573
  HandleScope scope;
574
  js_server = Persistent<Object>::New (_js_server);
575
  // are we ever going to need this external?
576
  js_server->SetInternalField (0, External::New(this));
577
  js_server.MakeWeak (this, server_destroy);
578
}
579

  
580
Server::~Server ()
581
{
582
  Stop();
583
  js_server.Dispose();
584
  js_server.Clear(); // necessary? 
585
}
586

  
587
int
588
Server::Start(struct addrinfo *servinfo) 
589
{
590
  int r = oi_server_listen(&server, servinfo);
591
  if(r == 0)
592
    oi_server_attach(&server, node_loop());
593
  return r;
594
}
595

  
596
void
597
Server::Stop() 
598
{
599
  oi_server_close (&server);
600
  oi_server_detach (&server);
601
}
602

  
603
/* This constructor takes 2 arguments: host, port. */
604
static Handle<Value>
605
newHTTPServer (const Arguments& args) 
606
{
607
  if (args.Length() < 3)
608
    return Undefined();
609

  
610
  HandleScope scope;
611

  
612
  char *host = NULL; 
613
  String::AsciiValue host_v(args[0]->ToString());
614
  if(args[0]->IsString()) {
615
    host = *host_v;
616
  }
617
  String::AsciiValue port(args[1]->ToString());
618

  
619
  Handle<Function> onrequest = Handle<Function>::Cast(args[2]);
620
  args.This()->Set(on_request_str, onrequest);
621

  
622
  // get addrinfo for localhost, PORT
623
  struct addrinfo *servinfo;
624
  struct addrinfo hints;
625
  memset(&hints, 0, sizeof hints);
626
  hints.ai_family = AF_UNSPEC;
627
  hints.ai_socktype = SOCK_STREAM;
628
  hints.ai_flags = AI_PASSIVE;
629
  // FIXME BLOCKING
630
  int r = getaddrinfo(host, *port, &hints, &servinfo);
631
  if (r != 0)
632
    return Undefined(); // XXX raise error?
633

  
634
  //
635
  //
636
  //
637
  // TODO host is ignored for now assumed localhost
638
  //
639
  //
640
  //
641
  //
642

  
643
  Server *server = new Server(args.This());
644
  if(server == NULL)
645
    return Undefined(); // XXX raise error?
646

  
647
  r = server->Start(servinfo);
648
  if (r != 0)
649
    return Undefined(); // XXX raise error?
650

  
651
  return args.This();
652
}
653

  
654
void
655
Init_http (Handle<Object> target)
656
{
657
  HandleScope scope;
658

  
659
  Local<FunctionTemplate> server_t = FunctionTemplate::New(newHTTPServer);
660
  server_t->InstanceTemplate()->SetInternalFieldCount(1);
661
  
662
  server_t->Set("INVALID_STATE_ERR", Integer::New(INVALID_STATE_ERR));
663

  
664
  target->Set(String::New("HTTPServer"), server_t->GetFunction());
665

  
666
  path_str         = Persistent<String>::New( String::NewSymbol("path") );
667
  uri_str          = Persistent<String>::New( String::NewSymbol("uri") );
668
  query_string_str = Persistent<String>::New( String::NewSymbol("query_string") );
669
  fragment_str     = Persistent<String>::New( String::NewSymbol("fragment") );
670
  method_str       = Persistent<String>::New( String::NewSymbol("method") );
671
  http_version_str = Persistent<String>::New( String::NewSymbol("http_version") );
672
  headers_str      = Persistent<String>::New( String::NewSymbol("headers") );
673

  
674
  on_request_str = Persistent<String>::New( String::NewSymbol("onrequest") );
675
  on_body_str    = Persistent<String>::New( String::NewSymbol("onbody") );
676
  respond_str    = Persistent<String>::New( String::NewSymbol("respond") );
677

  
678
  copy_str      = Persistent<String>::New( String::New("COPY") );
679
  delete_str    = Persistent<String>::New( String::New("DELETE") );
680
  get_str       = Persistent<String>::New( String::New("GET") );
681
  head_str      = Persistent<String>::New( String::New("HEAD") );
682
  lock_str      = Persistent<String>::New( String::New("LOCK") );
683
  mkcol_str     = Persistent<String>::New( String::New("MKCOL") );
684
  move_str      = Persistent<String>::New( String::New("MOVE") );
685
  options_str   = Persistent<String>::New( String::New("OPTIONS") );
686
  post_str      = Persistent<String>::New( String::New("POST") );
687
  propfind_str  = Persistent<String>::New( String::New("PROPFIND") );
688
  proppatch_str = Persistent<String>::New( String::New("PROPPATCH") );
689
  put_str       = Persistent<String>::New( String::New("PUT") );
690
  trace_str     = Persistent<String>::New( String::New("TRACE") );
691
  unlock_str    = Persistent<String>::New( String::New("UNLOCK") );
692
}
node_http.h
1
#ifndef node_http_h
2
#define node_http_h
3

  
4
#include <v8.h>
5

  
6
void Init_http (v8::Handle<v8::Object> target);
7

  
8
#endif
node_tcp.cc
1
#include "node_tcp.h"
2
#include "node.h"
3

  
4
#include <oi_socket.h>
5
#include <oi_buf.h>
6

  
7
#include <assert.h>
8
#include <sys/types.h>
9
#include <sys/socket.h>
10
#include <netdb.h>
11

  
12
using namespace v8;
13

  
14
static Persistent<String> readyState_str; 
15

  
16
static Persistent<Integer> readyState_CONNECTING; 
17
static Persistent<Integer> readyState_OPEN; 
18
static Persistent<Integer> readyState_CLOSED; 
19

  
20
enum readyState { READY_STATE_CONNECTING = 0
21
                , READY_STATE_OPEN       = 1
22
                , READY_STATE_CLOSED     = 2
23
                } ;
24

  
25
class TCPClient {
26
public:
27
  TCPClient(Handle<Object> obj);
28
  ~TCPClient();
29

  
30
  int Connect(char *host, char *port);
31
  void Write (Handle<Value> arg);
32
  void Disconnect();
33

  
34
  void OnOpen();
35
  void OnRead(const void *buf, size_t count);
36
  void OnClose();
37

  
38
private:
39
  int ReadyState();
40
  oi_socket socket;
41
  struct addrinfo *address;
42
  Persistent<Object> js_client;
43
};
44

  
45

  
46
static void
47
on_connect (oi_socket *socket)
48
{
49
  TCPClient *client = static_cast<TCPClient*> (socket->data);
50
  client->OnOpen();
51
}
52

  
53
static void
54
on_close (oi_socket *socket)
55
{
56
  TCPClient *client = static_cast<TCPClient*> (socket->data);
57
  client->OnClose();
58
}
59

  
60
static void
61
on_read (oi_socket *socket, const void *buf, size_t count)
62
{
63
  TCPClient *client = static_cast<TCPClient*> (socket->data);
64
  client->OnRead(buf, count);
65
}
66

  
67
static struct addrinfo tcp_hints = 
68
/* ai_flags      */ { AI_PASSIVE
69
/* ai_family     */ , AF_UNSPEC
70
/* ai_socktype   */ , SOCK_STREAM
71
/* ai_protocol   */ , 0
72
/* ai_addrlen    */ , 0
73
/* ai_addr       */ , 0
74
/* ai_canonname  */ , 0
75
/* ai_next       */ , 0
76
                    };
77

  
78
static Handle<Value> newTCPClient
79
  ( const Arguments& args
80
  ) 
81
{
82
  if (args.Length() < 1)
83
    return Undefined();
84

  
85
  HandleScope scope;
86

  
87
  char *host = NULL; 
88
  String::AsciiValue host_v(args[0]->ToString());
89
  if(args[0]->IsString()) {
90
    host = *host_v;
91
  }
92
  String::AsciiValue port(args[1]);
93

  
94
  TCPClient *client = new TCPClient(args.This());
95
  if(client == NULL)
96
    return Undefined(); // XXX raise error?
97

  
98
  int r = client->Connect(host, *port);
99
  if (r != 0)
100
    return Undefined(); // XXX raise error?
101

  
102

  
103
  return args.This();
104
}
105

  
106
static TCPClient*
107
UnwrapClient (Handle<Object> obj) 
108
{
109
  HandleScope scope;
110
  Handle<External> field = Handle<External>::Cast(obj->GetInternalField(0));
111
  TCPClient* client = static_cast<TCPClient*>(field->Value());
112
  return client;
113
}
114

  
115
static Handle<Value>
116
WriteCallback (const Arguments& args) 
117
{
118
  HandleScope scope;
119
  TCPClient *client = UnwrapClient(args.Holder());
120
  client->Write(args[0]);
121
  return Undefined();
122
}
123

  
124
static Handle<Value>
125
DisconnectCallback (const Arguments& args) 
126
{
127
  HandleScope scope;
128
  TCPClient *client = UnwrapClient(args.Holder());
129
  client->Disconnect();
130
  return Undefined();
131
}
132

  
133
static void
134
client_destroy (Persistent<Value> _, void *data)
135
{
136
  TCPClient *client = static_cast<TCPClient *> (data);
137
  delete client;
138
}
139

  
140
TCPClient::TCPClient(Handle<Object> _js_client)
141
{
142
  oi_socket_init(&socket, 30.0); // TODO adjustable timeout
143
  socket.on_connect = on_connect;
144
  socket.on_read    = on_read;
145
  socket.on_drain   = NULL;
146
  socket.on_error   = NULL;
147
  socket.on_close   = on_close;
148
  socket.on_timeout = on_close;
149
  socket.data = this;
150

  
151
  HandleScope scope;
152
  js_client = Persistent<Object>::New(_js_client);
153
  js_client->SetInternalField (0, External::New(this));
154
  js_client.MakeWeak (this, client_destroy);
155
}
156

  
157
TCPClient::~TCPClient ()
158
{
159
  Disconnect();
160
  oi_socket_detach (&socket);
161
  js_client.Dispose();
162
  js_client.Clear(); // necessary? 
163
}
164

  
165
int
166
TCPClient::Connect(char *host, char *port)
167
{
168
  int r;
169

  
170
  HandleScope scope;
171

  
172
  js_client->Set(readyState_str, readyState_CONNECTING);
173

  
174
  /* FIXME Blocking DNS resolution. Use oi_async. */
175
  printf("resolving host: %s, port: %s\n", host, port);
176
  r = getaddrinfo (host, port, &tcp_hints, &address);
177
  if(r != 0)  {
178
    perror("getaddrinfo");
179
    return r;
180
  }
181

  
182
  r = oi_socket_connect (&socket, address);
183
  if(r != 0)  {
184
    perror("oi_socket_connect");
185
    return r;
186
  }
187
  oi_socket_attach (&socket, node_loop());
188

  
189
  freeaddrinfo(address);
190
  address = NULL;
191
}
192

  
193
void TCPClient::Write (Handle<Value> arg)
194
{
195
  HandleScope scope;
196

  
197
  //
198
  // TODO if ReadyState() is not READY_STATE_OPEN then raise INVALID_STATE_ERR
199
  //
200
 
201
  if(arg == Null()) {
202

  
203
    oi_socket_write_eof(&socket);
204

  
205
  } else {
206
    Local<String> s = arg->ToString();
207

  
208
    oi_buf *buf = oi_buf_new2(s->Length());
209
    s->WriteAscii(buf->base, 0, s->Length());
210

  
211
    oi_socket_write(&socket, buf);
212
  }
213
}
214

  
215
int
216
TCPClient::ReadyState()
217
{
218
  return js_client->Get(readyState_str)->IntegerValue();
219
}
220
 
221
void
222
TCPClient::Disconnect()
223
{
224
  oi_socket_close(&socket);
225
}
226

  
227
void
228
TCPClient::OnOpen()
229
{
230
  HandleScope scope;
231

  
232
  assert(READY_STATE_CONNECTING == ReadyState());
233
  js_client->Set(readyState_str, readyState_OPEN);
234

  
235
  Handle<Value> onopen_value = js_client->Get( String::NewSymbol("onopen") );
236
  if (!onopen_value->IsFunction())
237
    return; 
238
  Handle<Function> onopen = Handle<Function>::Cast(onopen_value);
239

  
240
  TryCatch try_catch;
241

  
242
  Handle<Value> r = onopen->Call(js_client, 0, NULL);
243

  
244
  if(try_catch.HasCaught())
245
    node_fatal_exception(try_catch);
246
}
247

  
248
void
249
TCPClient::OnRead(const void *buf, size_t count)
250
{
251
  HandleScope scope;
252

  
253
  assert(READY_STATE_OPEN == ReadyState());
254

  
255
  Handle<Value> onread_value = js_client->Get( String::NewSymbol("onread") );
256
  if (!onread_value->IsFunction()) return; 
257
  Handle<Function> onread = Handle<Function>::Cast(onread_value);
258

  
259
  const int argc = 1;
260
  Handle<Value> argv[argc];
261

  
262
  if(count) {
263
    Handle<String> chunk = String::New((const char*)buf, count); // TODO binary data?
264
    argv[0] = chunk;
265
  } else {
266
    // TODO eof? delete write method?
267
    argv[0] = Null();
268
  }
269

  
270
  TryCatch try_catch;
271

  
... This diff was truncated because it exceeds the maximum size that can be displayed.

Also available in: Unified diff