THRIFT-3833 haxe http server implementation (by embeding into php web server)
Client: Haxe
Patch: Oleksii Prudkyi <Oleksii.Prudkyi@gmail.com> + some modifications by Jens Geyer
This closes #1013
This closes #1020
diff --git a/lib/haxe/README.md b/lib/haxe/README.md
index 2692be9..c5ad680 100644
--- a/lib/haxe/README.md
+++ b/lib/haxe/README.md
@@ -94,8 +94,8 @@
Current status
========================
- tested with Haxe C++ target
-- tested with Haxe PHP target (console, binary protocols)
-- transports: Socket, HTTP (client only), Stream
+- tested with Haxe PHP target (console/web server, binary protocols)
+- transports: Socket, HTTP (servers run inside PHP server/PHP target only), Stream
- protocols: Binary, JSON, Multiplex, Compact
- tutorial client and server available
- cross-test client and server available
@@ -117,3 +117,48 @@
Javascript:
- tutorial fails to build because of unsupported Sys.args
+PHP HTTP Server notes
+========================
+
+- you have to import PHP files generated by haxe into PHP
+```php
+require_once dirname(__FILE__) . '/bin/php-web-server/Main-debug.php';
+```
+
+- trace() by default outputs into stdout (http response), so you have to redirect it to stderr or you own logs, something like
+```haxe
+//remap trace to error log
+haxe.Log.trace = function(v:Dynamic, ?infos:haxe.PosInfos)
+{
+ //simulate normal trace https://github.com/HaxeFoundation/haxe/blob/development/std/haxe/Log.hx
+ var newValue : Dynamic;
+ if (infos != null && infos.customParams!=null) {
+ var extra:String = "";
+ for( v in infos.customParams )
+ extra += "," + v;
+ newValue = v + extra;
+ }
+ else {
+ newValue = v;
+ }
+ var msg = infos != null ? infos.fileName + ':' + infos.lineNumber + ': ' : '';
+ Sys.stderr().writeString('${msg}${newValue}\n');
+}
+```
+
+- to allow thrift server to read/write HTTP request/response, it should be pointed out to php streams
+```haxe
+transport = new TWrappingServerTransport(
+ new TStreamTransport(
+ new TFileStream("php://input", Read),
+ new TFileStream("php://output", Append)
+ )
+ );
+```
+
+- TSimpleServer doesn't stop after first call, so processor.process() should be called instead, or use runOnce property
+```haxe
+var server = new TSimpleServer( processor, transport, transfactory, protfactory);
+server.runOnce = true;
+```
+
diff --git a/lib/haxe/src/org/apache/thrift/server/TSimpleServer.hx b/lib/haxe/src/org/apache/thrift/server/TSimpleServer.hx
index 3b64b62..c9856b4 100644
--- a/lib/haxe/src/org/apache/thrift/server/TSimpleServer.hx
+++ b/lib/haxe/src/org/apache/thrift/server/TSimpleServer.hx
@@ -29,6 +29,10 @@
private var stop : Bool = false;
+ //stops just after input transport returns EOF
+ //useful for limited scenarios, like embeding into php server
+ public var runOnce : Bool = false;
+
public function new( processor : TProcessor,
serverTransport : TServerTransport,
transportFactory : TTransportFactory = null,
@@ -102,6 +106,11 @@
catch( ttx : TTransportException)
{
// Usually a client disconnect, expected
+ if(runOnce && ttx.errorID == TTransportException.END_OF_FILE) {
+ //input returns eof, exit
+ //follows lib/cpp/src/thrift/server/TServerFramework.cpp
+ Stop();
+ }
}
catch( pex : TProtocolException)
{
diff --git a/lib/haxe/src/org/apache/thrift/transport/TTransport.hx b/lib/haxe/src/org/apache/thrift/transport/TTransport.hx
index 4a3bd90..e6b3179 100644
--- a/lib/haxe/src/org/apache/thrift/transport/TTransport.hx
+++ b/lib/haxe/src/org/apache/thrift/transport/TTransport.hx
@@ -19,6 +19,7 @@
package org.apache.thrift.transport;
+import haxe.io.Eof;
import haxe.io.Bytes;
import haxe.io.BytesBuffer;
import org.apache.thrift.AbstractMethodError;
@@ -85,16 +86,21 @@
var got : Int = 0;
var ret : Int = 0;
while (got < len) {
+ try {
ret = read(buf, off+got, len-got);
if (ret <= 0) {
throw new TTransportException(TTransportException.UNKNOWN,
- "Cannot read. Remote side has closed. Tried to read "
- + len + " bytes, but only got " + got + " bytes.");
+ "Cannot read. Remote side has closed. Tried to read "
+ + len + " bytes, but only got " + got + " bytes.");
}
- got += ret;
+ }
+ catch (eof : Eof) {
+ throw new TTransportException(TTransportException.END_OF_FILE, 'Can\'t read $len bytes!');
+ }
+ got += ret;
}
return got;
- }
+ }
/**
* Writes the buffer to the output
@@ -130,4 +136,4 @@
throw new AbstractMethodError();
}
-}
\ No newline at end of file
+}
diff --git a/lib/haxe/src/org/apache/thrift/transport/TWrappingServerTransport.hx b/lib/haxe/src/org/apache/thrift/transport/TWrappingServerTransport.hx
new file mode 100644
index 0000000..b2272f3
--- /dev/null
+++ b/lib/haxe/src/org/apache/thrift/transport/TWrappingServerTransport.hx
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.transport;
+
+
+/*
+ wraps real transport, provided by constructor
+*/
+class TWrappingServerTransport extends TServerTransport {
+
+ private var transport(default,null) : TTransport;
+
+ public function new(transport : TTransport) {
+ this.transport = transport;
+ }
+
+ public override function Listen() : Void
+ {
+ }
+
+ private override function AcceptImpl() : TTransport
+ {
+ return transport;
+ }
+
+ public override function Close() : Void
+ {
+
+ }
+}
diff --git a/test/haxe/Makefile.am b/test/haxe/Makefile.am
index 6094452..1a32185 100644
--- a/test/haxe/Makefile.am
+++ b/test/haxe/Makefile.am
@@ -23,11 +23,12 @@
BIN_CPP = bin/Main-debug
BIN_PHP = bin/php/Main-debug.php
+BIN_PHP_WEB = bin/php-web-server/Main-debug.php
gen-haxe/thrift/test/ThriftTest.hx: $(THRIFTTEST)
$(THRIFTCMD) $(THRIFTTEST)
-all-local: $(BIN_CPP) $(BIN_PHP)
+all-local: $(BIN_CPP) $(BIN_PHP) $(BIN_PHP_WEB)
$(BIN_CPP): \
src/*.hx \
@@ -41,6 +42,11 @@
gen-haxe/thrift/test/ThriftTest.hx
$(HAXE) --cwd . php.hxml
+$(BIN_PHP_WEB): \
+ src/*.hx \
+ ../../lib/haxe/src/org/apache/thrift/**/*.hx \
+ gen-haxe/thrift/test/ThriftTest.hx
+ $(HAXE) --cwd . php-web-server.hxml
@@ -56,20 +62,30 @@
clean-local:
$(RM) -r gen-haxe bin
-check: check_cpp check_php
+.NOTPARALLEL:
+
+check: check_cpp \
+ check_php \
+ check_php_web
check_cpp: $(BIN_CPP)
- timeout 10 $(BIN_CPP) server &
+ timeout 20 $(BIN_CPP) server &
sleep 1
$(BIN_CPP) client
sleep 10
check_php: $(BIN_PHP)
- timeout 10 php -f $(BIN_PHP) server &
+ timeout 20 php -f $(BIN_PHP) server &
sleep 1
php -f $(BIN_PHP) client
sleep 10
+check_php_web: $(BIN_PHP_WEB) $(BIN_CPP)
+ timeout 20 php -S 127.0.0.1:9090 router.php &
+ sleep 1
+ $(BIN_CPP) client --transport http
+ sleep 10
+
EXTRA_DIST = \
src \
diff --git a/test/haxe/php-web-server.hxml b/test/haxe/php-web-server.hxml
new file mode 100644
index 0000000..395a852
--- /dev/null
+++ b/test/haxe/php-web-server.hxml
@@ -0,0 +1,43 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+#integrate files to classpath
+-cp src
+-cp gen-haxe
+-cp ../../lib/haxe/src
+
+#this class wil be used as entry point for your app.
+-main Main
+
+#PHP target
+-php bin/php-web-server/
+--php-front Main-debug.php
+
+#defines
+-D phpwebserver
+
+
+#Add debug information
+-debug
+
+#dead code elimination : remove unused code
+#"-dce no" : do not remove unused code
+#"-dce std" : remove unused code in the std lib (default)
+#"-dce full" : remove all unused code
+-dce full
diff --git a/test/haxe/router.php b/test/haxe/router.php
new file mode 100644
index 0000000..e34135c
--- /dev/null
+++ b/test/haxe/router.php
@@ -0,0 +1,31 @@
+<?php
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * @package thrift
+ */
+
+
+
+//router file to run testing web server
+
+//set_time_limit(1);
+
+require_once dirname(__FILE__) . '/bin/php-web-server/Main-debug.php';
+
+
diff --git a/test/haxe/src/Arguments.hx b/test/haxe/src/Arguments.hx
index cae91df..cc10749 100644
--- a/test/haxe/src/Arguments.hx
+++ b/test/haxe/src/Arguments.hx
@@ -72,12 +72,18 @@
public function new() {
#if sys
- try {
+ #if !phpwebserver
+ try {
ParseArgs();
- } catch (e : String) {
+ } catch (e : String) {
trace(GetHelp());
throw e;
- }
+ }
+ #else
+ //forcing server
+ server = true;
+ transport = http;
+ #end
#else
trace("WN: Platform does not support program arguments, using defaults.");
#end
diff --git a/test/haxe/src/Main.hx b/test/haxe/src/Main.hx
index 30c04a6..9eb828f 100644
--- a/test/haxe/src/Main.hx
+++ b/test/haxe/src/Main.hx
@@ -31,6 +31,15 @@
class Main
{
static function main() {
+ #if phpwebserver
+ initPhpWebServer();
+ //check method
+ if(php.Web.getMethod() != 'POST') {
+ Sys.println('http endpoint for thrift test server');
+ return;
+ }
+ #end
+
try {
var args = new Arguments();
@@ -48,4 +57,27 @@
}
}
+ #if phpwebserver
+ private static function initPhpWebServer()
+ {
+ //remap trace to error log
+ haxe.Log.trace = function(v:Dynamic, ?infos:haxe.PosInfos)
+ {
+ // handle trace
+ var newValue : Dynamic;
+ if (infos != null && infos.customParams!=null) {
+ var extra:String = "";
+ for( v in infos.customParams )
+ extra += "," + v;
+ newValue = v + extra;
+ }
+ else {
+ newValue = v;
+ }
+ var msg = infos != null ? infos.fileName + ':' + infos.lineNumber + ': ' : '';
+ Sys.stderr().writeString('${msg}${newValue}\n');
+ }
+ }
+ #end
+
}
diff --git a/test/haxe/src/TestClient.hx b/test/haxe/src/TestClient.hx
index 9436865..aa496dc 100644
--- a/test/haxe/src/TestClient.hx
+++ b/test/haxe/src/TestClient.hx
@@ -212,7 +212,9 @@
case socket:
transport = new TSocket(args.host, args.port);
case http:
- transport = new THttpClient(args.host);
+ var uri = 'http://${args.host}:${args.port}';
+ trace('- http client : ${uri}');
+ transport = new THttpClient(uri);
default:
throw "Unhandled transport";
}
diff --git a/test/haxe/src/TestServer.hx b/test/haxe/src/TestServer.hx
index 8f4604a..450c8f2 100644
--- a/test/haxe/src/TestServer.hx
+++ b/test/haxe/src/TestServer.hx
@@ -42,8 +42,18 @@
transport = new TServerSocket( args.port);
case http:
trace("- http");
- throw "HTTP server not implemented yet";
+ #if !phpwebserver
+ throw "HTTP server not implemented yet";
//transport = new THttpServer( targetHost);
+ #else
+ transport = new TWrappingServerTransport(
+ new TStreamTransport(
+ new TFileStream("php://input", Read),
+ new TFileStream("php://output", Append)
+ )
+ );
+
+ #end
default:
throw "Unhandled transport";
}
@@ -84,7 +94,12 @@
switch( args.servertype)
{
case simple:
- server = new TSimpleServer( processor, transport, transfactory, protfactory);
+ var simpleServer = new TSimpleServer( processor, transport, transfactory, protfactory);
+ #if phpwebserver
+ simpleServer.runOnce = true;
+ #end
+ server = simpleServer;
+
default:
throw "Unhandled server type";
}
diff --git a/tutorial/haxe/Makefile.am b/tutorial/haxe/Makefile.am
index 139bccb..e9c8820 100644
--- a/tutorial/haxe/Makefile.am
+++ b/tutorial/haxe/Makefile.am
@@ -18,17 +18,20 @@
#
THRIFT = $(top_builddir)/compiler/cpp/thrift
+
+BIN_CPP = bin/Main-debug
BIN_PHP = bin/php/Main-debug.php
+BIN_PHP_WEB = bin/php-web-server/Main-debug.php
gen-haxe/tutorial/calculator.hx gen-haxe/shared/shared_service.hx: $(top_srcdir)/tutorial/tutorial.thrift
$(THRIFT) --gen haxe -r $<
-all-local: bin/Main-debug $(BIN_PHP)
+all-local: $(BIN_CPP) $(BIN_PHP) $(BIN_PHP_WEB)
check: gen-haxe/tutorial/calculator.hx
-bin/Main-debug: \
+$(BIN_CPP): \
src/*.hx \
../../lib/haxe/src/org/apache/thrift/**/*.hx \
gen-haxe/tutorial/calculator.hx
@@ -40,30 +43,42 @@
gen-haxe/tutorial/calculator.hx
$(HAXE) --cwd . php.hxml
+$(BIN_PHP_WEB): \
+ src/*.hx \
+ ../../lib/haxe/src/org/apache/thrift/**/*.hx \
+ gen-haxe/tutorial/calculator.hx
+ $(HAXE) --cwd . php-web-server.hxml
+
tutorialserver: all
- bin/Main-debug server
+ $(BIN_CPP) server
tutorialserver_php: all
php -f $(BIN_PHP) server
tutorialclient: all
- bin/Main-debug
+ $(BIN_CPP)
tutorialclient_php: all
php -f $(BIN_PHP)
tutorialsecureserver: all
- bin/Main-debug server secure
+ $(BIN_CPP) server secure
tutorialsecureserver_php: all
php -f $(BIN_PHP) server secure
tutorialsecureclient: all
- bin/Main-debug secure
+ $(BIN_CPP) secure
tutorialsecureclient_php: all
php -f $(BIN_PHP) secure
+tutorialserver_php_http: all
+ php -S 127.0.0.1:9090 router.php
+
+tutorialclient_http: all
+ $(BIN_CPP) client http
+
clean-local:
$(RM) -r gen-haxe bin
diff --git a/tutorial/haxe/php-web-server.hxml b/tutorial/haxe/php-web-server.hxml
new file mode 100644
index 0000000..395a852
--- /dev/null
+++ b/tutorial/haxe/php-web-server.hxml
@@ -0,0 +1,43 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+#integrate files to classpath
+-cp src
+-cp gen-haxe
+-cp ../../lib/haxe/src
+
+#this class wil be used as entry point for your app.
+-main Main
+
+#PHP target
+-php bin/php-web-server/
+--php-front Main-debug.php
+
+#defines
+-D phpwebserver
+
+
+#Add debug information
+-debug
+
+#dead code elimination : remove unused code
+#"-dce no" : do not remove unused code
+#"-dce std" : remove unused code in the std lib (default)
+#"-dce full" : remove all unused code
+-dce full
diff --git a/tutorial/haxe/router.php b/tutorial/haxe/router.php
new file mode 100644
index 0000000..e34135c
--- /dev/null
+++ b/tutorial/haxe/router.php
@@ -0,0 +1,31 @@
+<?php
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * @package thrift
+ */
+
+
+
+//router file to run testing web server
+
+//set_time_limit(1);
+
+require_once dirname(__FILE__) . '/bin/php-web-server/Main-debug.php';
+
+
diff --git a/tutorial/haxe/src/Main.hx b/tutorial/haxe/src/Main.hx
index 8f168c1..6bebe71 100644
--- a/tutorial/haxe/src/Main.hx
+++ b/tutorial/haxe/src/Main.hx
@@ -51,7 +51,8 @@
private static var targetPort : Int = 9090;
static function main() {
- #if ! (flash || js)
+
+ #if ! (flash || js || phpwebserver)
try {
ParseArgs();
} catch (e : String) {
@@ -59,6 +60,17 @@
trace(GetHelp());
return;
}
+
+ #elseif phpwebserver
+ //forcing server
+ server = true;
+ trns = http;
+ initPhpWebServer();
+ //check method
+ if(php.Web.getMethod() != 'POST') {
+ Sys.println('http endpoint for thrift test server');
+ return;
+ }
#end
try {
@@ -73,6 +85,29 @@
trace("Completed.");
}
+ #if phpwebserver
+ private static function initPhpWebServer()
+ {
+ //remap trace to error log
+ haxe.Log.trace = function(v:Dynamic, ?infos:haxe.PosInfos)
+ {
+ // handle trace
+ var newValue : Dynamic;
+ if (infos != null && infos.customParams!=null) {
+ var extra:String = "";
+ for( v in infos.customParams )
+ extra += "," + v;
+ newValue = v + extra;
+ }
+ else {
+ newValue = v;
+ }
+ var msg = infos != null ? infos.fileName + ':' + infos.lineNumber + ': ' : '';
+ Sys.stderr().writeString('${msg}${newValue}\n');
+ }
+ }
+ #end
+
#if ! (flash || js)
@@ -154,8 +189,9 @@
trace('- socket transport $targetHost:$targetPort');
transport = new TSocket( targetHost, targetPort);
case http:
- trace('- HTTP transport $targetHost');
- transport = new THttpClient( targetHost);
+ var uri = 'http://${targetHost}:${targetPort}';
+ trace('- HTTP transport $uri');
+ transport = new THttpClient(uri);
default:
throw "Unhandled transport";
}
@@ -267,9 +303,20 @@
transport = new TServerSocket( targetPort);
#end
case http:
- throw "HTTP server not implemented yet";
- //trace("- http transport");
- //transport = new THttpClient( targetHost);
+ #if !phpwebserver
+ throw "HTTP server not implemented yet";
+ //trace("- http transport");
+ //transport = new THttpClient( targetHost);
+ #else
+ trace("- http transport");
+ transport = new TWrappingServerTransport(
+ new TStreamTransport(
+ new TFileStream("php://input", Read),
+ new TFileStream("php://output", Append)
+ )
+ );
+
+ #end
default:
throw "Unhandled transport";
}
@@ -301,6 +348,10 @@
var handler = new CalculatorHandler();
var processor = new CalculatorProcessor(handler);
var server = new TSimpleServer( processor, transport, transfactory, protfactory);
+ #if phpwebserver
+ server.runOnce = true;
+ #end
+
return server;
}
@@ -321,4 +372,4 @@
}
}
-
\ No newline at end of file
+