Skip to content

Commit

Permalink
Merge pull request #2 from Azure/tcp-streaming-service-proxy
Browse files Browse the repository at this point in the history
Tcp streaming service proxy sample
  • Loading branch information
pierreca authored Jul 30, 2018
2 parents 550ec94 + ed06a12 commit d8a6079
Show file tree
Hide file tree
Showing 5 changed files with 93 additions and 6 deletions.
6 changes: 5 additions & 1 deletion common/transport/http/devdoc/rest_api_client_requirements.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,11 @@ The `executeApiCall` method builds the HTTP request using the passed arguments a

**SRS_NODE_IOTHUB_REST_API_CLIENT_13_003: [** If `requestOptions` is not falsy then it shall be passed to the `buildRequest` function. **]**

**SRS_NODE_IOTHUB_REST_API_CLIENT_16_009: [** If the HTTP request is successful the `executeApiCall` method shall parse the JSON response received and call the `done` callback with a `null` first argument, the parsed result as a second argument and the HTTP response object itself as a third argument. **]**
**SRS_NODE_IOTHUB_REST_API_CLIENT_16_009: [** If the HTTP request is successful the `executeApiCall` method shall call the `done` callback with a `null` first argument, the parsed result according to **SRS_NODE_IOTHUB_REST_API_CLIENT_16_037** and **SRS_NODE_IOTHUB_REST_API_CLIENT_16_038** as a second argument and the HTTP response object itself as a third argument. **]**

**SRS_NODE_IOTHUB_REST_API_CLIENT_16_037: [** If the HTTP request is successful and the `content-type` header of the response starts with `application/json` the `executeApiCall` method shall parse the body of the response and provide the `result` as an object. **]**

**SRS_NODE_IOTHUB_REST_API_CLIENT_16_038: [** If the HTTP request is successful and the `content-type` is not set or is set to something else than `application/json`, the `executeApiCall` method shall use the body of the response as is for the `result` object. **]**

**SRS_NODE_IOTHUB_REST_API_CLIENT_16_010: [** If the HTTP request fails with an error code >= 300 the `executeApiCall` method shall translate the HTTP error into a transport-agnostic error using the `translateError` method and call the `done` callback with the resulting error as the only argument. **]**

Expand Down
9 changes: 7 additions & 2 deletions common/transport/http/src/rest_api_client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,8 +151,13 @@ export class RestApiClient {
}
} else {
/*Codes_SRS_NODE_IOTHUB_REST_API_CLIENT_16_009: [If the HTTP request is successful the `executeApiCall` method shall parse the JSON response received and call the `done` callback with a `null` first argument, the parsed result as a second argument and the HTTP response object itself as a third argument.]*/
const result = responseBody ? JSON.parse(responseBody) : '';
done(null, result, response);
/*Codes_SRS_NODE_IOTHUB_REST_API_CLIENT_16_038: [If the HTTP request is successful and the `content-type` is not set or is set to something else than `application/json`, the `executeApiCall` method shall use the body of the response as is for the `result` object.]*/
let result = responseBody;
if (responseBody && response.headers['content-type'].indexOf('application/json') >= 0) {
/*Codes_SRS_NODE_IOTHUB_REST_API_CLIENT_16_037: [If the HTTP request is successful and the `content-type` header of the response starts with `application/json` the `executeApiCall` method shall parse the body of the response and provide the `result` as an object.]*/
result = JSON.parse(responseBody);
}
done(null, result || '', response);
}
};

Expand Down
32 changes: 30 additions & 2 deletions common/transport/http/test/_rest_api_client_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -300,8 +300,9 @@ describe('RestApiClient', function() {
});

/*Tests_SRS_NODE_IOTHUB_REST_API_CLIENT_16_009: [If the HTTP request is successful the `executeApiCall` method shall parse the JSON response received and call the `done` callback with a `null` first argument, the parsed result as a second argument and the HTTP response object itself as a third argument.]*/
it('calls the done callback with null, a result and a response if the request succeeds', function (testCallback) {
var fakeResponse = { statusCode: 200 };
it('calls the done callback with null, a parsed result and a response if the request succeeds and the content-type response headers starts with application/json', function (testCallback) {
/*Tests_SRS_NODE_IOTHUB_REST_API_CLIENT_16_037: [If the HTTP request is successful and the `content-type` header of the response starts with `application/json` the `executeApiCall` method shall parse the body of the response and provide the `result` as an object.]*/
var fakeResponse = { statusCode: 200, headers: { 'content-type' : 'application/json; charset=utf-8' } };
var fakeResponseBody = {
foo: 'bar'
};
Expand All @@ -327,6 +328,33 @@ describe('RestApiClient', function() {
});
});

/*Tests_SRS_NODE_IOTHUB_REST_API_CLIENT_16_038: [If the HTTP request is successful and the `content-type` is not set or is set to something else than `application/json`, the `executeApiCall` method shall use the body of the response as is for the `result` object.]*/
it('calls the done callback with null, an unparsed result and a response if the request succeeds and the headers is not application/json', function (testCallback) {
/*Tests_SRS_NODE_IOTHUB_REST_API_CLIENT_16_037: [If the HTTP request is successful and the `content-type` header of the response starts with `application/json` the `executeApiCall` method shall parse the body of the response and provide the `result` as an object.]*/
var fakeResponse = { statusCode: 200, headers: { 'content-type' : 'text/plain; charset=utf-8' } };
var fakeResponseBody = "Hello world";

var fakeHttpHelper = {
buildRequest: function(method, path, headers, host, requestCallback) {
return {
setTimeout: function() {},
write: function() {},
end: function() {
requestCallback(null, JSON.stringify(fakeResponseBody), fakeResponse);
}
};
}
};

var client = new RestApiClient(fakeConfig, fakeAgent, fakeHttpHelper);
client.executeApiCall('GET', '/test/path', null, null, function(err, result, response) {
assert.isNull(err);
assert.strictEqual(result, fakeResponseBody);
assert.equal(response, fakeResponse);
testCallback();
});
});

/*Tests_SRS_NODE_IOTHUB_REST_API_CLIENT_16_010: [If the HTTP request fails with an error code >= 300 the `executeApiCall` method shall translate the HTTP error into a transport-agnostic error using the `translateError` method and call the `done` callback with the resulting error as the only argument.]*/
it('calls the done callback with a translated error if the request fails with an HTTP error', function (testCallback) {
var fakeResponse = { statusCode: 401 };
Expand Down
2 changes: 1 addition & 1 deletion service/samples/c2d_tcp_streaming.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ var streamInit = {
var client = ServiceClient.fromConnectionString(process.env.IOTHUB_CONNECTION_STRING);

console.log('initiating stream');
client.initiateStream('<DEVICE_ID>', streamInit, function(err, result) {
client.initiateStream(process.env.STREAMING_TARGET_DEVICE, streamInit, function(err, result) {
if (err) {
console.error(err.toString());
process.exit(-1);
Expand Down
50 changes: 50 additions & 0 deletions service/samples/tcp_streaming_proxy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

'use strict';

var websocket = require('websocket-stream')
var ServiceClient = require('azure-iothub').Client;
var net = require('net');

var streamInit = {
streamName: 'TestStream',
contentType: 'text/plain',
contentEncoding: 'utf-8',
connectTimeoutInSeconds: 30,
responseTimeoutInSeconds: 30,
payload: undefined
}

var client = ServiceClient.fromConnectionString(process.env.IOTHUB_CONNECTION_STRING);

console.log('initiating stream');
client.initiateStream(process.env.STREAMING_TARGET_DEVICE, streamInit, function(err, result) {
if (err) {
console.error(err.toString());
process.exit(-1);
} else {
console.log(JSON.stringify(result, null, 2));

var ws = websocket(result.uri, { headers: { 'Authorization': 'Bearer ' + result.authorizationToken} });
console.log('Got websocket - creating local server on port ' + process.env.PROXY_PORT);
var proxyServer = net.createServer(function (socket) {
socket.on('end', function () {
console.log('client disconnected');
process.exit(0);
})

socket.pipe(ws);
ws.pipe(socket);
});

proxyServer.on('error', function (err) {
console.error('error on the proxy server socket: ' + err.toString());
process.exit(-1);
})

proxyServer.listen(process.env.PROXY_PORT, function () {
console.log('listening on port: ' + process.env.PROXY_PORT + '...');
})
}
});

0 comments on commit d8a6079

Please sign in to comment.