mirror of
https://github.com/Netflix/dial-reference.git
synced 2026-06-08 02:49:58 +00:00
16
README.md
16
README.md
@@ -123,3 +123,19 @@ Options:
|
|||||||
```
|
```
|
||||||
Log file of test run is written in js_tests_log.txt in the
|
Log file of test run is written in js_tests_log.txt in the
|
||||||
server/tests/js_tests/tests folder.
|
server/tests/js_tests/tests folder.
|
||||||
|
|
||||||
|
This reference does not provide code for sleeping an app, so
|
||||||
|
tests that involve sleeping an app will fail.
|
||||||
|
|
||||||
|
There is also a test suite for lightly testing how the implementation handles edge cases.
|
||||||
|
this can be run with:
|
||||||
|
```
|
||||||
|
server/tests/js_tests/tests$ node testEdgeCases.js
|
||||||
|
|
||||||
|
Usage: node testEdgeCases.js[options]
|
||||||
|
|
||||||
|
Options:
|
||||||
|
--host IP address of host on which DIAL server under test is running
|
||||||
|
[string] [required]
|
||||||
|
--help, -h Show help [boolean]
|
||||||
|
```
|
||||||
|
|||||||
@@ -1,80 +0,0 @@
|
|||||||
#include "LinuxInterfaces.h"
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
/* Fetch the MACAddress of a network interface */
|
|
||||||
int getMACAddress(char* ifname, char* macBuf, unsigned int macBufSize)
|
|
||||||
{
|
|
||||||
struct ifreq ifr;
|
|
||||||
int sock;
|
|
||||||
unsigned char chMAC[6];
|
|
||||||
|
|
||||||
sock=socket(AF_INET,SOCK_DGRAM,0);
|
|
||||||
strcpy( ifr.ifr_name, ifname );
|
|
||||||
ifr.ifr_addr.sa_family = AF_INET;
|
|
||||||
if (ioctl( sock, SIOCGIFHWADDR, &ifr ) < 0) {
|
|
||||||
close(sock);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
memcpy(chMAC, ifr.ifr_hwaddr.sa_data, 6);
|
|
||||||
if (macBufSize<32){
|
|
||||||
close(sock);
|
|
||||||
return -1;
|
|
||||||
}else{
|
|
||||||
sprintf(macBuf,"%02X:%02X:%02X:%02X:%02X:%02X",chMAC[0],chMAC[1],chMAC[2],chMAC[3],chMAC[4],chMAC[5]);
|
|
||||||
}
|
|
||||||
close(sock);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool hasRoute(const char *nf, int flag)
|
|
||||||
{
|
|
||||||
bool result = false;
|
|
||||||
const char* fileName = "/proc/net/route";
|
|
||||||
int r;
|
|
||||||
FILE* fp = fopen(fileName, "r");
|
|
||||||
|
|
||||||
if (fp) {
|
|
||||||
char line[1024];
|
|
||||||
int nfBegin = 0, nfEnd = 0, nfFlag = 0;
|
|
||||||
const int nfLen = strlen(nf);
|
|
||||||
while (!feof(fp)) {
|
|
||||||
if (!fgets(line, sizeof(line), fp))
|
|
||||||
break;
|
|
||||||
r = sscanf(line, "%n%*s%n %*s %*s %d %*s %*s %*s %*s %*s %*s %*s", &nfBegin, &nfEnd, &nfFlag);
|
|
||||||
if (r == 1 && nfBegin == 0 && nfEnd == nfLen && !strncmp(nf, line, nfLen) && (flag == -1 || nfFlag == flag)) {
|
|
||||||
result = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fclose(fp);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isDefault(char *nf)
|
|
||||||
{
|
|
||||||
return hasRoute(nf, 0x03);
|
|
||||||
}
|
|
||||||
|
|
||||||
NetInterface getDefaultNetworkInterfaces()
|
|
||||||
{
|
|
||||||
DIR *d;
|
|
||||||
struct dirent *dir;
|
|
||||||
d = opendir("/sys/class/net/");
|
|
||||||
NetInterface defaultNet;
|
|
||||||
if (d){
|
|
||||||
while ((dir = readdir(d)) != NULL){
|
|
||||||
if(strcmp(dir->d_name,".") && strcmp(dir->d_name,"..")){
|
|
||||||
if (hasRoute(dir->d_name, -1) && isDefault(dir->d_name)){
|
|
||||||
getMACAddress(dir->d_name, defaultNet.macAddress, MACBUFSIZE);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
closedir(d);
|
|
||||||
}
|
|
||||||
return defaultNet;
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
#ifndef LINUXINTERFACES_H_
|
|
||||||
#define LINUXINTERFACES_H_
|
|
||||||
|
|
||||||
#ifndef __APPLE__
|
|
||||||
#include <linux/limits.h>
|
|
||||||
#include <linux/wireless.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <arpa/inet.h>
|
|
||||||
#include <dirent.h>
|
|
||||||
#include <netinet/in.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <sys/ioctl.h>
|
|
||||||
#include <sys/socket.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
/* Fetch the MACAddress of a network interface */
|
|
||||||
#define MACBUFSIZE 32
|
|
||||||
#define WAKEUPTIMEOUT 15
|
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
char macAddress[MACBUFSIZE];
|
|
||||||
}NetInterface;
|
|
||||||
|
|
||||||
NetInterface getDefaultNetworkInterfaces();
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2014 Netflix, Inc.
|
* Copyright (c) 2014-2019 Netflix, Inc.
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
@@ -40,50 +40,90 @@ void set_dial_data_dir(const char *data_dir) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the path where data is stored for the given app.
|
* Returns the path where data is stored for the given app.
|
||||||
|
*
|
||||||
|
* The DIAL data directory must have been already set.
|
||||||
|
*
|
||||||
|
* @param app_name application name.
|
||||||
|
* @return the location of the application path within the DIAL data
|
||||||
|
* directory or NULL if memory could not be allocated.
|
||||||
|
* @see set_dial_data_dir(const char*)
|
||||||
*/
|
*/
|
||||||
static char* getAppPath(char *app_name) {
|
static char* getAppPath(char *app_name) {
|
||||||
size_t name_size = strlen(app_name) + sizeof(dial_data_dir) + 1;
|
size_t name_size = strlen(app_name) + sizeof(dial_data_dir) + 1;
|
||||||
char* filename = (char*) malloc(name_size);
|
char* filename = (char*) malloc(name_size);
|
||||||
|
if (filename == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
filename[0] = 0;
|
filename[0] = 0;
|
||||||
strncat(filename, dial_data_dir, name_size);
|
strncat(filename, dial_data_dir, name_size);
|
||||||
strncat(filename, app_name, name_size - sizeof(dial_data_dir));
|
strncat(filename, app_name, name_size - sizeof(dial_data_dir));
|
||||||
return filename;
|
return filename;
|
||||||
}
|
}
|
||||||
|
|
||||||
void store_dial_data(char app_name[], DIALData *data) {
|
void store_dial_data(char *app_name, DIALData *data) {
|
||||||
char* filename = getAppPath(app_name);
|
char* filename = getAppPath(app_name);
|
||||||
|
if (filename == NULL) {
|
||||||
|
printf("Cannot open DIAL data output file, out-of-memory.");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
FILE *f = fopen(filename, "w");
|
FILE *f = fopen(filename, "w");
|
||||||
|
free(filename); filename = NULL;
|
||||||
if (f == NULL) {
|
if (f == NULL) {
|
||||||
printf("Cannot open DIAL data output file: %s\n", filename);
|
printf("Cannot open DIAL data output file: %s\n", filename);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
free(filename);
|
|
||||||
for (DIALData *first = data; first != NULL; first = first->next) {
|
for (DIALData *first = data; first != NULL; first = first->next) {
|
||||||
fprintf(f, "%s %s\n", first->key, first->value);
|
// truncate because we have limits on length when retrieving.
|
||||||
|
fprintf(f, "%.*s %.*s\n", DIAL_KEY_OR_VALUE_MAX_LEN, first->key, DIAL_KEY_OR_VALUE_MAX_LEN, first->value);
|
||||||
}
|
}
|
||||||
fclose(f);
|
fclose(f);
|
||||||
}
|
}
|
||||||
|
|
||||||
DIALData *retrieve_dial_data(char *app_name) {
|
DIALData *retrieve_dial_data(char *app_name) {
|
||||||
char* filename = getAppPath(app_name);
|
char* filename = getAppPath(app_name);
|
||||||
|
if (filename == NULL) {
|
||||||
|
return NULL; // no dial data found, that's fine
|
||||||
|
}
|
||||||
FILE *f = fopen(filename, "r");
|
FILE *f = fopen(filename, "r");
|
||||||
free(filename);
|
free(filename); filename = NULL;
|
||||||
if (f == NULL) {
|
if (f == NULL) {
|
||||||
return NULL; // no dial data found, that's fine
|
return NULL; // no dial data found, that's fine
|
||||||
}
|
}
|
||||||
DIALData *result = NULL;
|
DIALData *result = NULL;
|
||||||
char key[256];
|
char key[DIAL_KEY_OR_VALUE_MAX_LEN + 1] = {0,};
|
||||||
char value[256];
|
char value[DIAL_KEY_OR_VALUE_MAX_LEN + 1] = {0,};
|
||||||
while (fscanf(f, "%255s %255s\n", key, value) != EOF) {
|
int err = 0;
|
||||||
|
while (fscanf(f, "%" DIAL_KEY_OR_VALUE_MAX_LEN_STR "s %" DIAL_KEY_OR_VALUE_MAX_LEN_STR "s\n", key, value) != EOF) {
|
||||||
DIALData *newNode = (DIALData *) malloc(sizeof(DIALData));
|
DIALData *newNode = (DIALData *) malloc(sizeof(DIALData));
|
||||||
newNode->key = (char *) calloc(1, strlen(key));
|
if (newNode == NULL) {
|
||||||
|
err = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
newNode->key = (char *) calloc(strlen(key) + 1, sizeof(char));
|
||||||
|
if (newNode->key == NULL) {
|
||||||
|
err = 1;
|
||||||
|
free(newNode); newNode = NULL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
strncpy(newNode->key, key, strlen(key));
|
strncpy(newNode->key, key, strlen(key));
|
||||||
newNode->value = (char *) calloc(1, strlen(value));
|
|
||||||
|
newNode->value = (char *) calloc(strlen(value) + 1, sizeof(char));
|
||||||
|
if (newNode->value == NULL) {
|
||||||
|
err = 1;
|
||||||
|
free(newNode->key); newNode->key = NULL;
|
||||||
|
free(newNode); newNode = NULL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
strncpy(newNode->value, value, strlen(value));
|
strncpy(newNode->value, value, strlen(value));
|
||||||
newNode->next = result;
|
newNode->next = result;
|
||||||
result = newNode;
|
result = newNode;
|
||||||
}
|
}
|
||||||
fclose(f);
|
fclose(f);
|
||||||
|
if (err) {
|
||||||
|
free_dial_data(&result);
|
||||||
|
result = NULL;
|
||||||
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,10 +132,10 @@ void free_dial_data(DIALData **dialData)
|
|||||||
DIALData *curNode=NULL;
|
DIALData *curNode=NULL;
|
||||||
while (*dialData != NULL) {
|
while (*dialData != NULL) {
|
||||||
curNode = *dialData;
|
curNode = *dialData;
|
||||||
*dialData =curNode->next;
|
*dialData = curNode->next;
|
||||||
|
|
||||||
free(curNode->key);
|
free(curNode->key); curNode->key = NULL;
|
||||||
free(curNode->value);
|
free(curNode->value); curNode->value = NULL;
|
||||||
free(curNode);
|
free(curNode); curNode = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2014 Netflix, Inc.
|
* Copyright (c) 2014-2019 Netflix, Inc.
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
@@ -46,6 +46,13 @@
|
|||||||
*/
|
*/
|
||||||
#define DIAL_DATA_URI "/dial_data"
|
#define DIAL_DATA_URI "/dial_data"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The DIAL data key and value values cannot contain any spaces. They are
|
||||||
|
* expected to be URL-escaped strings, so any spaces would be represented as
|
||||||
|
* the '+' character. They have a max length of 255 characters.
|
||||||
|
*
|
||||||
|
* THE STRINGS key AND value POINT TO MUST BE DYNAMICALLY ALLOCATED
|
||||||
|
*/
|
||||||
struct DIALData_ {
|
struct DIALData_ {
|
||||||
struct DIALData_ *next;
|
struct DIALData_ *next;
|
||||||
char *key;
|
char *key;
|
||||||
@@ -54,12 +61,45 @@ struct DIALData_ {
|
|||||||
|
|
||||||
typedef struct DIALData_ DIALData;
|
typedef struct DIALData_ DIALData;
|
||||||
|
|
||||||
|
#define DIAL_KEY_OR_VALUE_MAX_LEN (255)
|
||||||
|
#define DIAL_KEY_OR_VALUE_MAX_LEN_STR "255"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Store the DIAL data key/value pairs in the application data store.
|
||||||
|
*
|
||||||
|
* Will exit immediately if the data output file cannot be accessed due to
|
||||||
|
* out-of-memory or I/O errors.
|
||||||
|
*
|
||||||
|
* Keys and values are truncated to DIAL_KEY_OR_VALUE_MAX_LEN.
|
||||||
|
*
|
||||||
|
* @param app_name application name.
|
||||||
|
* @param data pointer to head of DIAL data linked list.
|
||||||
|
*/
|
||||||
void store_dial_data(char *app_name, DIALData *data);
|
void store_dial_data(char *app_name, DIALData *data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the DIAL data key/value pairs from the application data store.
|
||||||
|
*
|
||||||
|
* @param app_name application name.
|
||||||
|
* @return data pointer to head of DIAL data linked list or NULL if
|
||||||
|
* there is no valid data or if the data output file cannot be accessed
|
||||||
|
* due to out-of-memory or I/O errors.
|
||||||
|
*/
|
||||||
DIALData *retrieve_dial_data(char *app_name);
|
DIALData *retrieve_dial_data(char *app_name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the DIAL data directory.
|
||||||
|
*
|
||||||
|
* @param data_dir the DIAL data directory path, which must include the
|
||||||
|
* trailing directory separator (e.g. '/' character).
|
||||||
|
*/
|
||||||
void set_dial_data_dir(const char *data_dir);
|
void set_dial_data_dir(const char *data_dir);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Frees the DIAL data linked list memory.
|
||||||
|
*
|
||||||
|
* @param dialData pointer to the DIAL data linked list.
|
||||||
|
*/
|
||||||
void free_dial_data(DIALData **dialData);
|
void free_dial_data(DIALData **dialData);
|
||||||
|
|
||||||
#endif /* SRC_SERVER_DIAL_DATA_H_ */
|
#endif /* SRC_SERVER_DIAL_DATA_H_ */
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2014 Netflix, Inc.
|
* Copyright (c) 2014-2019 Netflix, Inc.
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
@@ -37,7 +37,6 @@
|
|||||||
|
|
||||||
#include "mongoose.h"
|
#include "mongoose.h"
|
||||||
#include "url_lib.h"
|
#include "url_lib.h"
|
||||||
#include "LinuxInterfaces.h"
|
|
||||||
|
|
||||||
// TODO: Partners should define this port
|
// TODO: Partners should define this port
|
||||||
#define DIAL_PORT (56789)
|
#define DIAL_PORT (56789)
|
||||||
@@ -67,16 +66,42 @@ struct DIALServer_ {
|
|||||||
pthread_mutex_t mux;
|
pthread_mutex_t mux;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void ds_lock(DIALServer *ds) {
|
/**
|
||||||
pthread_mutex_lock(&ds->mux);
|
* Acquire the DIAL server mutex.
|
||||||
|
*
|
||||||
|
* @return 1 if acquisition succeeded, 0 if it failed.
|
||||||
|
*/
|
||||||
|
static int ds_lock(DIALServer *ds) {
|
||||||
|
int err = 0;
|
||||||
|
if ((err = pthread_mutex_lock(&ds->mux)) != 0) {
|
||||||
|
printf("Unable to acquire DS mutex: [%d]", err);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ds_unlock(DIALServer *ds) {
|
/**
|
||||||
pthread_mutex_unlock(&ds->mux);
|
* Release the DIAL server mutex.
|
||||||
|
*
|
||||||
|
* @return 1 if release succeeded, 0 if it failed.
|
||||||
|
*/
|
||||||
|
static int ds_unlock(DIALServer *ds) {
|
||||||
|
int err = 0;
|
||||||
|
if ((err = pthread_mutex_unlock(&ds->mux)) != 0) {
|
||||||
|
printf("Unable to release DS mutex: [%d]", err);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// finds an app and returns a pointer to the previous element's next pointer
|
/**
|
||||||
// if not found, return a pointer to the last element's next pointer
|
* Finds an application in the DIAL server application linked list.
|
||||||
|
*
|
||||||
|
* @param ds the DIAL server.
|
||||||
|
* @param app_name application name.
|
||||||
|
* @return a pointer to the DIAL application; the pointer value may be NULL if
|
||||||
|
* the application was not found.
|
||||||
|
*/
|
||||||
static DIALApp **find_app(DIALServer *ds, const char *app_name) {
|
static DIALApp **find_app(DIALServer *ds, const char *app_name) {
|
||||||
DIALApp *app;
|
DIALApp *app;
|
||||||
DIALApp **ret = &ds->apps;
|
DIALApp **ret = &ds->apps;
|
||||||
@@ -89,16 +114,34 @@ static DIALApp **find_app(DIALServer *ds, const char *app_name) {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void url_decode_xml_encode(char *dst, char *src, size_t src_size) {
|
/**
|
||||||
|
* URL-unescape the string, then XML-escape it.
|
||||||
|
*
|
||||||
|
* @param dst the destination XML-escaped string. Must be large enough for
|
||||||
|
* the resulting string (technically as much as 6x the raw string
|
||||||
|
* length based on the implementation in url_lib.c).
|
||||||
|
* @param src the URL-escaped string.
|
||||||
|
* @param src_size size of the URL-escaped string, including trailing NULL.
|
||||||
|
* @return true if successful, false if out-of-memory.
|
||||||
|
*/
|
||||||
|
static int url_decode_xml_encode(char *dst, char *src, size_t src_size) {
|
||||||
char *url_decoded_key = (char *) malloc(src_size + 1);
|
char *url_decoded_key = (char *) malloc(src_size + 1);
|
||||||
|
if (url_decoded_key == NULL) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
urldecode(url_decoded_key, src, src_size);
|
urldecode(url_decoded_key, src, src_size);
|
||||||
xmlencode(dst, url_decoded_key, 2 * src_size);
|
xmlencode(dst, url_decoded_key, 2 * src_size);
|
||||||
free(url_decoded_key);
|
free(url_decoded_key);
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* A bad payload is defined to be an unprintable character or a
|
* Checks if a payload string contains invalid characters.
|
||||||
* non-ascii character.
|
*
|
||||||
|
* @param pPayload payload string.
|
||||||
|
* @param numBytes length of payload string in bytes (excluding trailing NULL).
|
||||||
|
*
|
||||||
|
* @return 1 if the payload contains an unprintable or non-ASCII character.
|
||||||
*/
|
*/
|
||||||
static int isBadPayload(const char* pPayload, int numBytes) {
|
static int isBadPayload(const char* pPayload, int numBytes) {
|
||||||
int i = 0;
|
int i = 0;
|
||||||
@@ -118,19 +161,21 @@ static void handle_app_start(struct mg_connection *conn,
|
|||||||
const struct mg_request_info *request_info,
|
const struct mg_request_info *request_info,
|
||||||
const char *app_name,
|
const char *app_name,
|
||||||
const char *origin_header) {
|
const char *origin_header) {
|
||||||
char additional_data_param[256] = {0, };
|
char additional_data_param[DIAL_MAX_ADDITIONALURL] = {0, };
|
||||||
char body[DIAL_MAX_PAYLOAD + sizeof(additional_data_param) + 2] = {0, };
|
char body[DIAL_MAX_PAYLOAD + sizeof(additional_data_param) + 2] = {0, };
|
||||||
DIALApp *app;
|
DIALApp *app;
|
||||||
DIALServer *ds = request_info->user_data;
|
DIALServer *ds = request_info->user_data;
|
||||||
int body_size;
|
int body_size;
|
||||||
|
|
||||||
ds_lock(ds);
|
if (!ds_lock(ds)) {
|
||||||
|
mg_send_http_error(conn, 500, "500 Internal Server Error", "500 Internal Server Error");
|
||||||
|
return;
|
||||||
|
}
|
||||||
app = *find_app(ds, app_name);
|
app = *find_app(ds, app_name);
|
||||||
if (!app) {
|
if (!app) {
|
||||||
mg_send_http_error(conn, 404, "Not Found", "Not Found");
|
mg_send_http_error(conn, 404, "Not Found", "Not Found");
|
||||||
} else {
|
} else {
|
||||||
body_size = mg_read(conn, body, sizeof(body));
|
body_size = mg_read(conn, body, sizeof(body) - 1);
|
||||||
// NUL-terminate it just in case
|
|
||||||
if (body_size > DIAL_MAX_PAYLOAD) {
|
if (body_size > DIAL_MAX_PAYLOAD) {
|
||||||
mg_send_http_error(conn, 413, "413 Request Entity Too Large",
|
mg_send_http_error(conn, 413, "413 Request Entity Too Large",
|
||||||
"413 Request Entity Too Large");
|
"413 Request Entity Too Large");
|
||||||
@@ -145,7 +190,7 @@ static void handle_app_start(struct mg_connection *conn,
|
|||||||
|
|
||||||
if (app->useAdditionalData) {
|
if (app->useAdditionalData) {
|
||||||
// Construct additionalDataUrl=http://host:port/apps/app_name/dial_data
|
// Construct additionalDataUrl=http://host:port/apps/app_name/dial_data
|
||||||
sprintf(additional_data_param,
|
snprintf(additional_data_param, DIAL_MAX_ADDITIONALURL,
|
||||||
"additionalDataUrl=http%%3A%%2F%%2Flocalhost%%3A%d%%2Fapps%%2F%s%%2Fdial_data%%3F",
|
"additionalDataUrl=http%%3A%%2F%%2Flocalhost%%3A%d%%2Fapps%%2F%s%%2Fdial_data%%3F",
|
||||||
dial_port, app_name);
|
dial_port, app_name);
|
||||||
}
|
}
|
||||||
@@ -171,6 +216,8 @@ static void handle_app_start(struct mg_connection *conn,
|
|||||||
mg_send_http_error(conn, 403, "Forbidden", "Forbidden");
|
mg_send_http_error(conn, 403, "Forbidden", "Forbidden");
|
||||||
} else if (app->state == kDIALStatusErrorUnauth) {
|
} else if (app->state == kDIALStatusErrorUnauth) {
|
||||||
mg_send_http_error(conn, 401, "Unauthorized", "Unauthorized");
|
mg_send_http_error(conn, 401, "Unauthorized", "Unauthorized");
|
||||||
|
} else if (app->state == kDIALStatusErrorNotImplemented) {
|
||||||
|
mg_send_http_error(conn, 501, "Not Implemented", "Not Implemented");
|
||||||
} else {
|
} else {
|
||||||
mg_send_http_error(conn, 503, "Service Unavailable",
|
mg_send_http_error(conn, 503, "Service Unavailable",
|
||||||
"Service Unavailable");
|
"Service Unavailable");
|
||||||
@@ -196,7 +243,10 @@ static void handle_app_status(struct mg_connection *conn,
|
|||||||
free(clientVersionStr);
|
free(clientVersionStr);
|
||||||
}
|
}
|
||||||
|
|
||||||
ds_lock(ds);
|
if (!ds_lock(ds)) {
|
||||||
|
mg_send_http_error(conn, 500, "500 Internal Server Error", "500 Internal Server Error");
|
||||||
|
return;
|
||||||
|
}
|
||||||
app = *find_app(ds, app_name);
|
app = *find_app(ds, app_name);
|
||||||
if (!app) {
|
if (!app) {
|
||||||
mg_send_http_error(conn, 404, "Not Found", "Not Found");
|
mg_send_http_error(conn, 404, "Not Found", "Not Found");
|
||||||
@@ -209,23 +259,45 @@ static void handle_app_status(struct mg_connection *conn,
|
|||||||
char *p = dial_data;
|
char *p = dial_data;
|
||||||
|
|
||||||
for (DIALData* first = app->dial_data; first != NULL; first = first->next) {
|
for (DIALData* first = app->dial_data; first != NULL; first = first->next) {
|
||||||
p = smartstrcat(p, " <", end - p);
|
p = smartstrncpy(p, " <", end - p);
|
||||||
size_t key_length = strlen(first->key);
|
size_t key_length = strlen(first->key);
|
||||||
char *encoded_key = (char *) malloc(2 * key_length + 1);
|
char *encoded_key = (char *) malloc(2 * key_length + 1);
|
||||||
url_decode_xml_encode(encoded_key, first->key, key_length);
|
if (encoded_key == NULL) {
|
||||||
|
mg_send_http_error(conn, 500, "500 Internal Server Error", "500 Internal Server Error");
|
||||||
|
ds_unlock(ds);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!url_decode_xml_encode(encoded_key, first->key, key_length)) {
|
||||||
|
mg_send_http_error(conn, 500, "500 Internal Server Error", "500 Internal Server Error");
|
||||||
|
free(encoded_key); encoded_key = NULL;
|
||||||
|
ds_unlock(ds);
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
size_t value_length = strlen(first->value);
|
size_t value_length = strlen(first->value);
|
||||||
char *encoded_value = (char *) malloc(2 * value_length + 1);
|
char *encoded_value = (char *) malloc(2 * value_length + 1);
|
||||||
url_decode_xml_encode(encoded_value, first->value, value_length);
|
if (encoded_value == NULL) {
|
||||||
|
mg_send_http_error(conn, 500, "500 Internal Server Error", "500 Internal Server Error");
|
||||||
|
free(encoded_key); encoded_key = NULL;
|
||||||
|
ds_unlock(ds);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!url_decode_xml_encode(encoded_value, first->value, value_length)) {
|
||||||
|
mg_send_http_error(conn, 500, "500 Internal Server Error", "500 Internal Server Error");
|
||||||
|
free(encoded_value); encoded_value = NULL;
|
||||||
|
free(encoded_key); encoded_key = NULL;
|
||||||
|
ds_unlock(ds);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
p = smartstrcat(p, encoded_key, end - p);
|
p = smartstrncpy(p, encoded_key, end - p);
|
||||||
p = smartstrcat(p, ">", end - p);
|
p = smartstrncpy(p, ">", end - p);
|
||||||
p = smartstrcat(p, encoded_value, end - p);
|
p = smartstrncpy(p, encoded_value, end - p);
|
||||||
p = smartstrcat(p, "</", end - p);
|
p = smartstrncpy(p, "</", end - p);
|
||||||
p = smartstrcat(p, encoded_key, end - p);
|
p = smartstrncpy(p, encoded_key, end - p);
|
||||||
p = smartstrcat(p, ">", end - p);
|
p = smartstrncpy(p, ">", end - p);
|
||||||
free(encoded_key);
|
free(encoded_key); encoded_key = NULL;
|
||||||
free(encoded_value);
|
free(encoded_value); encoded_value = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
app->state = app->callbacks.status_cb(ds, app_name, app->run_id, &canStop,
|
app->state = app->callbacks.status_cb(ds, app_name, app->run_id, &canStop,
|
||||||
@@ -285,7 +357,10 @@ static void handle_app_stop(struct mg_connection *conn,
|
|||||||
DIALServer *ds = request_info->user_data;
|
DIALServer *ds = request_info->user_data;
|
||||||
int canStop = 0;
|
int canStop = 0;
|
||||||
|
|
||||||
ds_lock(ds);
|
if (!ds_lock(ds)) {
|
||||||
|
mg_send_http_error(conn, 500, "500 Internal Server Error", "500 Internal Server Error");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Special handling for system app
|
// Special handling for system app
|
||||||
if (strcmp(app_name, "system") == 0) {
|
if (strcmp(app_name, "system") == 0) {
|
||||||
@@ -322,7 +397,10 @@ static void handle_app_hide(struct mg_connection *conn,
|
|||||||
DIALServer *ds = request_info->user_data;
|
DIALServer *ds = request_info->user_data;
|
||||||
int canStop = 0;
|
int canStop = 0;
|
||||||
|
|
||||||
ds_lock(ds);
|
if (!ds_lock(ds)) {
|
||||||
|
mg_send_http_error(conn, 500, "500 Internal Server Error", "500 Internal Server Error");
|
||||||
|
return;
|
||||||
|
}
|
||||||
app = *find_app(ds, app_name);
|
app = *find_app(ds, app_name);
|
||||||
|
|
||||||
// update the application state
|
// update the application state
|
||||||
@@ -336,17 +414,17 @@ static void handle_app_hide(struct mg_connection *conn,
|
|||||||
} else {
|
} else {
|
||||||
// not implemented in reference
|
// not implemented in reference
|
||||||
DIALStatus status = app->callbacks.hide_cb(ds, app_name, app->run_id, app->callback_data);
|
DIALStatus status = app->callbacks.hide_cb(ds, app_name, app->run_id, app->callback_data);
|
||||||
if (status!=kDIALStatusHide){
|
if (status != kDIALStatusHide){
|
||||||
fprintf(stderr, "Hide not implemented for reference.\n");
|
fprintf(stderr, "Hide not implemented for reference.\n");
|
||||||
mg_send_http_error(conn, 501, "Not Implemented",
|
mg_send_http_error(conn, 501, "Not Implemented",
|
||||||
"Not Implemented");
|
"Not Implemented");
|
||||||
}else{
|
} else {
|
||||||
app->state = kDIALStatusHide;
|
app->state = kDIALStatusHide;
|
||||||
mg_printf(conn, "HTTP/1.1 200 OK\r\n"
|
mg_printf(conn, "HTTP/1.1 200 OK\r\n"
|
||||||
"Content-Type: text/plain\r\n"
|
"Content-Type: text/plain\r\n"
|
||||||
"Access-Control-Allow-Origin: %s\r\n"
|
"Access-Control-Allow-Origin: %s\r\n"
|
||||||
"\r\n",
|
"\r\n",
|
||||||
origin_header);
|
origin_header);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ds_unlock(ds);
|
ds_unlock(ds);
|
||||||
@@ -362,7 +440,10 @@ static void handle_dial_data(struct mg_connection *conn,
|
|||||||
DIALApp *app;
|
DIALApp *app;
|
||||||
DIALServer *ds = request_info->user_data;
|
DIALServer *ds = request_info->user_data;
|
||||||
|
|
||||||
ds_lock(ds);
|
if (!ds_lock(ds)) {
|
||||||
|
mg_send_http_error(conn, 500, "500 Internal Server Error", "500 Internal Server Error");
|
||||||
|
return;
|
||||||
|
}
|
||||||
app = *find_app(ds, app_name);
|
app = *find_app(ds, app_name);
|
||||||
if (!app) {
|
if (!app) {
|
||||||
mg_send_http_error(conn, 404, "Not Found", "Not Found");
|
mg_send_http_error(conn, 404, "Not Found", "Not Found");
|
||||||
@@ -372,21 +453,22 @@ static void handle_dial_data(struct mg_connection *conn,
|
|||||||
int nread;
|
int nread;
|
||||||
if (!use_payload) {
|
if (!use_payload) {
|
||||||
if (request_info->query_string) {
|
if (request_info->query_string) {
|
||||||
|
int qs_len = strlen(request_info->query_string);
|
||||||
|
if (qs_len > DIAL_DATA_MAX_PAYLOAD) {
|
||||||
|
mg_send_http_error(conn, 413, "413 Request Entity Too Large",
|
||||||
|
"413 Request Entity Too Large");
|
||||||
|
ds_unlock(ds);
|
||||||
|
return;
|
||||||
|
}
|
||||||
strncpy(body, request_info->query_string, DIAL_DATA_MAX_PAYLOAD);
|
strncpy(body, request_info->query_string, DIAL_DATA_MAX_PAYLOAD);
|
||||||
nread = strlen(body);
|
nread = qs_len;
|
||||||
} else {
|
} else {
|
||||||
nread = 0;
|
nread = 0;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
nread = mg_read(conn, body, DIAL_DATA_MAX_PAYLOAD);
|
nread = mg_read(conn, body, DIAL_DATA_MAX_PAYLOAD);
|
||||||
body[nread] = '\0';
|
|
||||||
}
|
|
||||||
if (nread > DIAL_DATA_MAX_PAYLOAD) {
|
|
||||||
mg_send_http_error(conn, 413, "413 Request Entity Too Large",
|
|
||||||
"413 Request Entity Too Large");
|
|
||||||
ds_unlock(ds);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
body[nread] = '\0';
|
||||||
|
|
||||||
if (isBadPayload(body, nread)) {
|
if (isBadPayload(body, nread)) {
|
||||||
mg_send_http_error(conn, 400, "400 Bad Request", "400 Bad Request");
|
mg_send_http_error(conn, 400, "400 Bad Request", "400 Bad Request");
|
||||||
@@ -399,6 +481,7 @@ static void handle_dial_data(struct mg_connection *conn,
|
|||||||
|
|
||||||
app->dial_data = parse_params(body);
|
app->dial_data = parse_params(body);
|
||||||
store_dial_data(app->name, app->dial_data);
|
store_dial_data(app->name, app->dial_data);
|
||||||
|
free_dial_data(&app->dial_data);
|
||||||
|
|
||||||
mg_printf(conn, "HTTP/1.1 200 OK\r\n"
|
mg_printf(conn, "HTTP/1.1 200 OK\r\n"
|
||||||
"Access-Control-Allow-Origin: %s\r\n"
|
"Access-Control-Allow-Origin: %s\r\n"
|
||||||
@@ -419,7 +502,7 @@ static int ends_with(const char *str, const char *suffix) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// str contains a white space separated list of strings (only supports SPACE characters for now)
|
// str contains a white space separated list of strings (only supports SPACE characters for now)
|
||||||
static int ends_with_in_list (const char *str, const char *list) {
|
static int ends_with_in_list (const char *str, const char *list) {
|
||||||
if (!str || !list)
|
if (!str || !list)
|
||||||
return 0;
|
return 0;
|
||||||
@@ -448,12 +531,12 @@ static int ends_with_in_list (const char *str, const char *list) {
|
|||||||
substring[copyLength] = '\0';
|
substring[copyLength] = '\0';
|
||||||
//printf("found %s \n", substring);
|
//printf("found %s \n", substring);
|
||||||
if (ends_with(str, substring)) {
|
if (ends_with(str, substring)) {
|
||||||
free(substring);
|
free(substring); substring = NULL;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
scanPointer = scanPointer + copyLength + 1; // assumption: only 1 character
|
scanPointer = scanPointer + copyLength + 1; // assumption: only 1 character
|
||||||
}
|
}
|
||||||
free(substring);
|
free(substring); substring = NULL;
|
||||||
return ends_with(str, scanPointer);
|
return ends_with(str, scanPointer);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -472,7 +555,10 @@ static int is_allowed_origin(DIALServer* ds, char * origin, const char * app_nam
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
ds_lock(ds);
|
if (!ds_lock(ds)) {
|
||||||
|
// If we can't check, fail in favor of safety.
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
DIALApp *app;
|
DIALApp *app;
|
||||||
int result = 0;
|
int result = 0;
|
||||||
for (app = ds->apps; app != NULL; app = app->next) {
|
for (app = ds->apps; app != NULL; app = app->next) {
|
||||||
@@ -531,11 +617,16 @@ static void *request_handler(enum mg_event event, struct mg_connection *conn,
|
|||||||
fprintf(stderr, "Origin %s, Host: %s\n", origin_header, host_header);
|
fprintf(stderr, "Origin %s, Host: %s\n", origin_header, host_header);
|
||||||
if (event == MG_NEW_REQUEST) {
|
if (event == MG_NEW_REQUEST) {
|
||||||
// URL ends with run
|
// URL ends with run
|
||||||
if (!strncmp(request_info->uri + strlen(request_info->uri) - 4, RUN_URI,
|
if (strlen(request_info->uri) > strlen(RUN_URI) + strlen(APPS_URI)
|
||||||
strlen(RUN_URI))) {
|
&& !strncmp(request_info->uri + strlen(request_info->uri) - strlen(RUN_URI), RUN_URI, strlen(RUN_URI)))
|
||||||
char app_name[256] = {0, }; // assuming the application name is not over 256 chars.
|
{
|
||||||
strncpy(app_name, request_info->uri + strlen(APPS_URI),
|
// Maximum app name length of 255 characters.
|
||||||
((strlen(request_info->uri) - 4) - (sizeof(APPS_URI) - 1)));
|
char app_name[256] = {0, };
|
||||||
|
int appname_len = strlen(request_info->uri) - strlen(RUN_URI) - strlen(APPS_URI);
|
||||||
|
if (appname_len > 255) {
|
||||||
|
appname_len = 255;
|
||||||
|
}
|
||||||
|
strncpy(app_name, request_info->uri + strlen(APPS_URI), appname_len);
|
||||||
|
|
||||||
if (!strcmp(request_info->request_method, "OPTIONS")) {
|
if (!strcmp(request_info->request_method, "OPTIONS")) {
|
||||||
return options_response(ds, conn, host_header, origin_header, app_name, "DELETE, OPTIONS");
|
return options_response(ds, conn, host_header, origin_header, app_name, "DELETE, OPTIONS");
|
||||||
@@ -543,7 +634,8 @@ static void *request_handler(enum mg_event event, struct mg_connection *conn,
|
|||||||
|
|
||||||
// DELETE non-empty app name
|
// DELETE non-empty app name
|
||||||
if (app_name[0] != '\0'
|
if (app_name[0] != '\0'
|
||||||
&& !strcmp(request_info->request_method, "DELETE")) {
|
&& !strcmp(request_info->request_method, "DELETE"))
|
||||||
|
{
|
||||||
if (host_header && is_allowed_origin(ds, origin_header, app_name)) {
|
if (host_header && is_allowed_origin(ds, origin_header, app_name)) {
|
||||||
handle_app_stop(conn, request_info, app_name, origin_header);
|
handle_app_stop(conn, request_info, app_name, origin_header);
|
||||||
} else {
|
} else {
|
||||||
@@ -556,10 +648,12 @@ static void *request_handler(enum mg_event event, struct mg_connection *conn,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// URI starts with "/apps/" and is followed by an app name
|
// URI starts with "/apps/" and is followed by an app name
|
||||||
else if (!strncmp(request_info->uri, APPS_URI, sizeof(APPS_URI) - 1)
|
else if (strlen(request_info->uri) > strlen(APPS_URI)
|
||||||
&& !strchr(request_info->uri + strlen(APPS_URI), '/')) {
|
&& !strncmp(request_info->uri, APPS_URI, strlen(APPS_URI))
|
||||||
|
&& !strchr(request_info->uri + strlen(APPS_URI), '/'))
|
||||||
|
{
|
||||||
const char *app_name;
|
const char *app_name;
|
||||||
app_name = request_info->uri + sizeof(APPS_URI) - 1;
|
app_name = request_info->uri + strlen(APPS_URI);
|
||||||
|
|
||||||
if (!strcmp(request_info->request_method, "OPTIONS")) {
|
if (!strcmp(request_info->request_method, "OPTIONS")) {
|
||||||
return options_response(ds, conn, host_header, origin_header, app_name, "GET, POST, OPTIONS");
|
return options_response(ds, conn, host_header, origin_header, app_name, "GET, POST, OPTIONS");
|
||||||
@@ -577,27 +671,29 @@ static void *request_handler(enum mg_event event, struct mg_connection *conn,
|
|||||||
} else if (!strcmp(request_info->request_method, "GET")) {
|
} else if (!strcmp(request_info->request_method, "GET")) {
|
||||||
handle_app_status(conn, request_info, app_name, origin_header);
|
handle_app_status(conn, request_info, app_name, origin_header);
|
||||||
} else {
|
} else {
|
||||||
mg_send_http_error(conn, 501, "Not Implemented",
|
mg_send_http_error(conn, 501, "Not Implemented", "Not Implemented");
|
||||||
"Not Implemented");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// URI that ends with HIDE_URI
|
// URI that ends with HIDE_URI
|
||||||
else if (!strncmp(request_info->uri + strlen(request_info->uri) - strlen(HIDE_URI), HIDE_URI,
|
else if (strlen(request_info->uri) > strlen(HIDE_URI) + strlen(RUN_URI) + strlen(APPS_URI)
|
||||||
strlen(HIDE_URI))) {
|
&& !strncmp(request_info->uri + strlen(request_info->uri) - strlen(HIDE_URI), HIDE_URI, strlen(HIDE_URI)))
|
||||||
char app_name[256] = {0, }; // assuming the application name is not over 256 chars.
|
{
|
||||||
strncpy(app_name, request_info->uri + strlen(APPS_URI),
|
// Maximum app name length of 255 characters.
|
||||||
((strlen(request_info->uri) - strlen(RUN_URI) - strlen(HIDE_URI)) - (sizeof(APPS_URI) - 1)));
|
char app_name[256] = {0, };
|
||||||
|
int appname_len = strlen(request_info->uri) - strlen(RUN_URI) - strlen(HIDE_URI) - strlen(APPS_URI);
|
||||||
|
if (appname_len > 255) {
|
||||||
|
appname_len = 255;
|
||||||
|
}
|
||||||
|
strncpy(app_name, request_info->uri + strlen(APPS_URI), appname_len);
|
||||||
|
|
||||||
if (!strcmp(request_info->request_method, "OPTIONS")) {
|
if (!strcmp(request_info->request_method, "OPTIONS")) {
|
||||||
return options_response(ds, conn, host_header, origin_header, app_name, "POST, OPTIONS");
|
return options_response(ds, conn, host_header, origin_header, app_name, "POST, OPTIONS");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (app_name[0] != '\0'
|
if (app_name[0] != '\0' && !strcmp(request_info->request_method, "POST")) {
|
||||||
&& !strcmp(request_info->request_method, "POST")) {
|
|
||||||
handle_app_hide(conn, request_info, app_name, origin_header);
|
handle_app_hide(conn, request_info, app_name, origin_header);
|
||||||
}else{
|
} else {
|
||||||
mg_send_http_error(conn, 501, "Not Implemented",
|
mg_send_http_error(conn, 501, "Not Implemented", "Not Implemented");
|
||||||
"Not Implemented");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// URI is of the form */app_name/dial_data
|
// URI is of the form */app_name/dial_data
|
||||||
@@ -608,18 +704,20 @@ static void *request_handler(enum mg_event event, struct mg_connection *conn,
|
|||||||
inet_ntop(addr->sin_family, &addr->sin_addr, laddr, sizeof(laddr));
|
inet_ntop(addr->sin_family, &addr->sin_addr, laddr, sizeof(laddr));
|
||||||
if ( !strncmp(laddr, gLocalhost, strlen(gLocalhost)) ) {
|
if ( !strncmp(laddr, gLocalhost, strlen(gLocalhost)) ) {
|
||||||
char *app_name = parse_app_name(request_info->uri);
|
char *app_name = parse_app_name(request_info->uri);
|
||||||
|
if (app_name == NULL) {
|
||||||
|
mg_send_http_error(conn, 500, "Internal Error", "Internal Error");
|
||||||
|
} else {
|
||||||
|
if (!strcmp(request_info->request_method, "OPTIONS")) {
|
||||||
|
void *ret = options_response(ds, conn, host_header, origin_header, app_name, "POST, OPTIONS");
|
||||||
|
free(app_name);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
int use_payload = strcmp(request_info->request_method, "POST") ? 0 : 1;
|
||||||
|
handle_dial_data(conn, request_info, app_name, origin_header,
|
||||||
|
use_payload);
|
||||||
|
|
||||||
if (!strcmp(request_info->request_method, "OPTIONS")) {
|
|
||||||
void *ret = options_response(ds, conn, host_header, origin_header, app_name, "POST, OPTIONS");
|
|
||||||
free(app_name);
|
free(app_name);
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
int use_payload =
|
|
||||||
strcmp(request_info->request_method, "POST") ? 0 : 1;
|
|
||||||
handle_dial_data(conn, request_info, app_name, origin_header,
|
|
||||||
use_payload);
|
|
||||||
|
|
||||||
free(app_name);
|
|
||||||
} else {
|
} else {
|
||||||
// If the request is not from local host, return an error
|
// If the request is not from local host, return an error
|
||||||
mg_send_http_error(conn, 403, "Forbidden", "Forbidden");
|
mg_send_http_error(conn, 403, "Forbidden", "Forbidden");
|
||||||
@@ -637,12 +735,19 @@ static void *request_handler(enum mg_event event, struct mg_connection *conn,
|
|||||||
|
|
||||||
DIALServer *DIAL_create() {
|
DIALServer *DIAL_create() {
|
||||||
DIALServer *ds = calloc(1, sizeof(DIALServer));
|
DIALServer *ds = calloc(1, sizeof(DIALServer));
|
||||||
pthread_mutex_init(&ds->mux, NULL);
|
if (ds == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (pthread_mutex_init(&ds->mux, NULL) != 0) {
|
||||||
|
free(ds); ds = NULL;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
return ds;
|
return ds;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DIAL_start(DIALServer *ds) {
|
int DIAL_start(DIALServer *ds) {
|
||||||
ds->ctx = mg_start(&request_handler, ds, DIAL_PORT);
|
ds->ctx = mg_start(&request_handler, ds, DIAL_PORT);
|
||||||
|
return (ds->ctx != NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DIAL_stop(DIALServer *ds) {
|
void DIAL_stop(DIALServer *ds) {
|
||||||
@@ -664,51 +769,60 @@ int DIAL_register_app(DIALServer *ds, const char *app_name,
|
|||||||
int useAdditionalData,
|
int useAdditionalData,
|
||||||
const char* corsAllowedOrigin) {
|
const char* corsAllowedOrigin) {
|
||||||
DIALApp **ptr, *app;
|
DIALApp **ptr, *app;
|
||||||
int ret;
|
|
||||||
|
|
||||||
ds_lock(ds);
|
if (!ds_lock(ds)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
ptr = find_app(ds, app_name);
|
ptr = find_app(ds, app_name);
|
||||||
if (*ptr != NULL) { // app already registered
|
if (*ptr != NULL) { // app already registered
|
||||||
ret = 0;
|
ds_unlock(ds);
|
||||||
|
return 0;
|
||||||
} else {
|
} else {
|
||||||
app = malloc(sizeof(DIALApp));
|
app = malloc(sizeof(DIALApp));
|
||||||
|
if (app == NULL) {
|
||||||
|
ds_unlock(ds);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
app->callbacks = *callbacks;
|
app->callbacks = *callbacks;
|
||||||
app->name = strdup(app_name);
|
app->name = strdup(app_name);
|
||||||
|
if (app->name == NULL) {
|
||||||
|
free(app); app = NULL;
|
||||||
|
ds_unlock(ds);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
app->next = *ptr;
|
app->next = *ptr;
|
||||||
app->state = kDIALStatusStopped;
|
app->state = kDIALStatusStopped;
|
||||||
app->callback_data = user_data;
|
app->callback_data = user_data;
|
||||||
app->dial_data = retrieve_dial_data(app->name);
|
app->dial_data = retrieve_dial_data(app->name);
|
||||||
app->useAdditionalData = useAdditionalData;
|
app->useAdditionalData = useAdditionalData;
|
||||||
app->corsAllowedOrigin[0] = '\0';
|
app->corsAllowedOrigin[0] = '\0';
|
||||||
if (corsAllowedOrigin &&
|
if (corsAllowedOrigin && strlen(corsAllowedOrigin) < sizeof(app->corsAllowedOrigin)) {
|
||||||
strlen(corsAllowedOrigin) < sizeof(app->corsAllowedOrigin)) {
|
strcpy(app->corsAllowedOrigin, corsAllowedOrigin);
|
||||||
strcpy(app->corsAllowedOrigin, corsAllowedOrigin);
|
|
||||||
}
|
}
|
||||||
*ptr = app;
|
*ptr = app;
|
||||||
ret = 1;
|
ds_unlock(ds);
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
ds_unlock(ds);
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int DIAL_unregister_app(DIALServer *ds, const char *app_name) {
|
int DIAL_unregister_app(DIALServer *ds, const char *app_name) {
|
||||||
DIALApp **ptr, *app;
|
DIALApp **ptr, *app;
|
||||||
int ret;
|
|
||||||
|
|
||||||
ds_lock(ds);
|
if (!ds_lock(ds)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
ptr = find_app(ds, app_name);
|
ptr = find_app(ds, app_name);
|
||||||
if (*ptr == NULL) { // no such app
|
if (*ptr == NULL) { // no such app
|
||||||
ret = 0;
|
ds_unlock(ds);
|
||||||
|
return 0;
|
||||||
} else {
|
} else {
|
||||||
app = *ptr;
|
app = *ptr;
|
||||||
*ptr = app->next;
|
*ptr = app->next;
|
||||||
free(app->name);
|
free(app->name); app->name = NULL;
|
||||||
free(app);
|
free(app); app = NULL;
|
||||||
ret = 1;
|
ds_unlock(ds);
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
ds_unlock(ds);
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const char * DIAL_get_payload(DIALServer *ds, const char *app_name) {
|
const char * DIAL_get_payload(DIALServer *ds, const char *app_name) {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2014 Netflix, Inc.
|
* Copyright (c) 2014-2019 Netflix, Inc.
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
@@ -59,7 +59,9 @@ typedef enum {
|
|||||||
#define DIAL_MAX_PAYLOAD (4096)
|
#define DIAL_MAX_PAYLOAD (4096)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The maximum additionalDataUrl length
|
* The maximum additionalDataUrl length.
|
||||||
|
*
|
||||||
|
* There is no value defined in the DIAL specification.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define DIAL_MAX_ADDITIONALURL (1024)
|
#define DIAL_MAX_ADDITIONALURL (1024)
|
||||||
@@ -120,8 +122,10 @@ DIALServer *DIAL_create();
|
|||||||
* Starts the DIAL server.
|
* Starts the DIAL server.
|
||||||
*
|
*
|
||||||
* @param[in] ds DIAL server handle
|
* @param[in] ds DIAL server handle
|
||||||
|
* @return true if the DIAL server was started and the DIAL server handle
|
||||||
|
* now contains a valid Mongoose context.
|
||||||
*/
|
*/
|
||||||
void DIAL_start(DIALServer *ds);
|
int DIAL_start(DIALServer *ds);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Stop the DIAL server.
|
* Stop the DIAL server.
|
||||||
@@ -141,7 +145,7 @@ void DIAL_stop(DIALServer *ds);
|
|||||||
* @param[in] if non-0, the app supports DIALadditionalDataURL.
|
* @param[in] if non-0, the app supports DIALadditionalDataURL.
|
||||||
* @param[in] if non-NULL, specifies the CORS allowed origin for this app.
|
* @param[in] if non-NULL, specifies the CORS allowed origin for this app.
|
||||||
*
|
*
|
||||||
* @return 1 if successful, 0 otherwise
|
* @return 1 if successful, 0 if already registered, -1 on error.
|
||||||
*/
|
*/
|
||||||
int DIAL_register_app(DIALServer *ds, const char *app_name,
|
int DIAL_register_app(DIALServer *ds, const char *app_name,
|
||||||
struct DIALAppCallbacks *callbacks,
|
struct DIALAppCallbacks *callbacks,
|
||||||
@@ -154,7 +158,7 @@ int DIAL_register_app(DIALServer *ds, const char *app_name,
|
|||||||
* @param[in] ds DIAL server handle
|
* @param[in] ds DIAL server handle
|
||||||
* @param[in] app_name Name of the DIAL application
|
* @param[in] app_name Name of the DIAL application
|
||||||
*
|
*
|
||||||
* @return 1 if successful, 0 otherwise
|
* @return 1 if successful, 0 if not found, -1 on error.
|
||||||
*/
|
*/
|
||||||
int DIAL_unregister_app(DIALServer *ds, const char *app_name);
|
int DIAL_unregister_app(DIALServer *ds, const char *app_name);
|
||||||
|
|
||||||
|
|||||||
@@ -112,19 +112,19 @@ int isAppRunning( char *pzName, char *pzCommandPattern ) {
|
|||||||
struct dirent* procEntry;
|
struct dirent* procEntry;
|
||||||
while((procEntry=readdir(proc_fd)) != NULL) {
|
while((procEntry=readdir(proc_fd)) != NULL) {
|
||||||
if( doesMatch( "^[0-9][0-9]*$", procEntry->d_name ) ) {
|
if( doesMatch( "^[0-9][0-9]*$", procEntry->d_name ) ) {
|
||||||
char exePath[64] = {0,};
|
char exePath[384] = {0,};
|
||||||
char link[256] = {0,};
|
char link[256] = {0,};
|
||||||
char cmdlinePath[64] = {0,};
|
char cmdlinePath[384] = {0,};
|
||||||
char buffer[1024] = {0,};
|
char buffer[1024] = {0,};
|
||||||
int len;
|
int len;
|
||||||
sprintf( exePath, "/proc/%s/exe", procEntry->d_name);
|
snprintf( exePath, sizeof(exePath), "/proc/%s/exe", procEntry->d_name);
|
||||||
sprintf( cmdlinePath, "/proc/%s/cmdline", procEntry->d_name);
|
snprintf( cmdlinePath, sizeof(cmdlinePath), "/proc/%s/cmdline", procEntry->d_name);
|
||||||
|
|
||||||
if( (len = readlink( exePath, link, sizeof(link)-1)) != -1 ) {
|
if( (len = readlink( exePath, link, sizeof(link)-1)) != -1 ) {
|
||||||
char executable[256] = {0,};
|
char executable[256] = {0,};
|
||||||
strcat( executable, pzName );
|
strncpy( executable, pzName, sizeof(executable) - 2 );
|
||||||
strcat( executable, "$" );
|
strcat( executable, "$" );
|
||||||
// TODO: Make this search for EOL to prevent false positivies
|
// TODO: Make this search for EOL to prevent false positives
|
||||||
if( !doesMatch( executable, link ) ) {
|
if( !doesMatch( executable, link ) ) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -147,9 +147,9 @@ int isAppRunning( char *pzName, char *pzCommandPattern ) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
int d_name = atoi(procEntry->d_name);
|
||||||
closedir(proc_fd);
|
closedir(proc_fd);
|
||||||
return atoi(procEntry->d_name);
|
return d_name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -202,14 +202,14 @@ static DIALStatus youtube_start(DIALServer *ds, const char *appname,
|
|||||||
printf("\n\n ** LAUNCH YouTube ** with payload %s\n\n", payload);
|
printf("\n\n ** LAUNCH YouTube ** with payload %s\n\n", payload);
|
||||||
|
|
||||||
char url[512] = {0,}, data[512] = {0,};
|
char url[512] = {0,}, data[512] = {0,};
|
||||||
if (strlen(payload) && strlen(additionalDataUrl)){
|
if (strlen(payload) && strlen(additionalDataUrl)) {
|
||||||
sprintf( url, "https://www.youtube.com/tv?%s&%s", payload, additionalDataUrl);
|
snprintf( url, sizeof(url), "https://www.youtube.com/tv?%s&%s", payload, additionalDataUrl);
|
||||||
}else if (strlen(payload)){
|
} else if (strlen(payload)) {
|
||||||
sprintf( url, "https://www.youtube.com/tv?%s", payload);
|
snprintf( url, sizeof(url), "https://www.youtube.com/tv?%s", payload);
|
||||||
}else{
|
} else {
|
||||||
sprintf( url, "https://www.youtube.com/tv");
|
snprintf( url, sizeof(url), "https://www.youtube.com/tv");
|
||||||
}
|
}
|
||||||
sprintf( data, "--user-data-dir=%s/.config/google-chrome-dial", getenv("HOME") );
|
snprintf( data, sizeof(data), "--user-data-dir=%s/.config/google-chrome-dial", getenv("HOME") );
|
||||||
|
|
||||||
const char * const youtube_args[] = { spAppYouTubeExecutable,
|
const char * const youtube_args[] = { spAppYouTubeExecutable,
|
||||||
spYouTubePS3UserAgent,
|
spYouTubePS3UserAgent,
|
||||||
@@ -268,13 +268,18 @@ static void setValue( char * pSource, char dest[] )
|
|||||||
static void setDataDir(char *pData)
|
static void setDataDir(char *pData)
|
||||||
{
|
{
|
||||||
setValue( spNfDataDir, spDataDir );
|
setValue( spNfDataDir, spDataDir );
|
||||||
strcat(spDataDir, pData);
|
strncat(spDataDir, pData, sizeof(spDataDir) - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void runDial(void)
|
void runDial(void)
|
||||||
{
|
{
|
||||||
DIALServer *ds;
|
DIALServer *ds;
|
||||||
ds = DIAL_create();
|
ds = DIAL_create();
|
||||||
|
if (ds == NULL) {
|
||||||
|
printf("Unable to create DIAL server.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
struct DIALAppCallbacks cb_nf;
|
struct DIALAppCallbacks cb_nf;
|
||||||
cb_nf.start_cb = netflix_start;
|
cb_nf.start_cb = netflix_start;
|
||||||
cb_nf.hide_cb = netflix_hide;
|
cb_nf.hide_cb = netflix_hide;
|
||||||
@@ -283,16 +288,20 @@ void runDial(void)
|
|||||||
struct DIALAppCallbacks cb_yt = {youtube_start, youtube_hide, youtube_stop, youtube_status};
|
struct DIALAppCallbacks cb_yt = {youtube_start, youtube_hide, youtube_stop, youtube_status};
|
||||||
struct DIALAppCallbacks cb_system = {system_start, system_hide, NULL, system_status};
|
struct DIALAppCallbacks cb_system = {system_start, system_hide, NULL, system_status};
|
||||||
|
|
||||||
DIAL_register_app(ds, "Netflix", &cb_nf, NULL, 1, ".netflix.com");
|
if (DIAL_register_app(ds, "Netflix", &cb_nf, NULL, 1, ".netflix.com") == -1 ||
|
||||||
DIAL_register_app(ds, "YouTube", &cb_yt, NULL, 1, ".youtube.com");
|
DIAL_register_app(ds, "YouTube", &cb_yt, NULL, 1, ".youtube.com") == -1 ||
|
||||||
DIAL_register_app(ds, "system", &cb_system, NULL, 1, "");
|
DIAL_register_app(ds, "system", &cb_system, NULL, 1, "") == -1)
|
||||||
DIAL_start(ds);
|
{
|
||||||
|
printf("Unable to register DIAL applications.\n");
|
||||||
gDialPort = DIAL_get_port(ds);
|
} else if (!DIAL_start(ds)) {
|
||||||
printf("launcher listening on gDialPort %d\n", gDialPort);
|
printf("Unable to start DIAL master listening thread.\n");
|
||||||
run_ssdp(gDialPort, spFriendlyName, spModelName, spUuid);
|
} else {
|
||||||
|
gDialPort = DIAL_get_port(ds);
|
||||||
DIAL_stop(ds);
|
printf("launcher listening on gDialPort %d\n", gDialPort);
|
||||||
|
run_ssdp(gDialPort, spFriendlyName, spModelName, spUuid);
|
||||||
|
|
||||||
|
DIAL_stop(ds);
|
||||||
|
}
|
||||||
free(ds);
|
free(ds);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -317,11 +326,11 @@ static void processOption( int index, char * pOption )
|
|||||||
setValue( pOption, spUuid );
|
setValue( pOption, spUuid );
|
||||||
break;
|
break;
|
||||||
case 5:
|
case 5:
|
||||||
if (strcmp(pOption, "on")==0){
|
if (strcmp(pOption, "on")==0) {
|
||||||
wakeOnWifiLan=true;
|
wakeOnWifiLan=true;
|
||||||
}else if (strcmp(pOption, "off")==0){
|
} else if (strcmp(pOption, "off") == 0) {
|
||||||
wakeOnWifiLan=false;
|
wakeOnWifiLan=false;
|
||||||
}else{
|
} else {
|
||||||
fprintf(stderr, "Option %s is not valid for %s",
|
fprintf(stderr, "Option %s is not valid for %s",
|
||||||
pOption, WAKE_OPTION_LONG);
|
pOption, WAKE_OPTION_LONG);
|
||||||
exit(1);
|
exit(1);
|
||||||
|
|||||||
@@ -22,10 +22,14 @@ nf_callbacks_lib: nf_callbacks.o
|
|||||||
dialserver: nf_callbacks_lib $(OBJS)
|
dialserver: nf_callbacks_lib $(OBJS)
|
||||||
$(CC) -Wall -Werror -Wl,-rpath,. -g $(OBJS) -ldl -lpthread -lrt -L. -lnfCallbacks -o dialserver
|
$(CC) -Wall -Werror -Wl,-rpath,. -g $(OBJS) -ldl -lpthread -lrt -L. -lnfCallbacks -o dialserver
|
||||||
|
|
||||||
|
|
||||||
|
dialserver_with_ASAN: nf_callbacks_lib $(OBJS)
|
||||||
|
$(CC) -Wall -Werror -fsanitize=address -Wl,-rpath,. -g $(OBJS) -ldl -lpthread -lrt -L. -lnfCallbacks -o dialserver_with_ASAN
|
||||||
|
|
||||||
test:
|
test:
|
||||||
make -C tests
|
make -C tests
|
||||||
./tests/run_tests
|
./tests/run_tests
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f *.o dialserver *.so
|
rm -f *.o dialserver dialserver_with_ASAN *.so
|
||||||
make -C tests clean
|
make -C tests clean
|
||||||
|
|||||||
@@ -37,6 +37,10 @@
|
|||||||
#ifndef BUFSIZ
|
#ifndef BUFSIZ
|
||||||
#define BUFSIZ 4096
|
#define BUFSIZ 4096
|
||||||
#endif
|
#endif
|
||||||
|
#ifndef REASON_SIZ
|
||||||
|
/* REASON_SIZ must be non-trivially smaller than BUFSIZ. */
|
||||||
|
#define REASON_SIZ 2048
|
||||||
|
#endif
|
||||||
|
|
||||||
#define MAX_REQUEST_SIZE 4096
|
#define MAX_REQUEST_SIZE 4096
|
||||||
#define NUM_THREADS 4
|
#define NUM_THREADS 4
|
||||||
@@ -214,33 +218,12 @@ static int mg_snprintf(struct mg_connection *conn, char *buf, size_t buflen,
|
|||||||
// Skip the characters until one of the delimiters characters found.
|
// Skip the characters until one of the delimiters characters found.
|
||||||
// 0-terminate resulting word. Skip the delimiter and following whitespaces if any.
|
// 0-terminate resulting word. Skip the delimiter and following whitespaces if any.
|
||||||
// Advance pointer to buffer to the next word. Return found 0-terminated word.
|
// Advance pointer to buffer to the next word. Return found 0-terminated word.
|
||||||
// Delimiters can be quoted with quotechar.
|
static char *skip_quoted(char **buf, const char *delimiters, const char *whitespace) {
|
||||||
static char *skip_quoted(char **buf, const char *delimiters, const char *whitespace, char quotechar) {
|
|
||||||
char *p, *begin_word, *end_word, *end_whitespace;
|
char *p, *begin_word, *end_word, *end_whitespace;
|
||||||
|
|
||||||
begin_word = *buf;
|
begin_word = *buf;
|
||||||
end_word = begin_word + strcspn(begin_word, delimiters);
|
end_word = begin_word + strcspn(begin_word, delimiters);
|
||||||
|
|
||||||
/* Check for quotechar */
|
|
||||||
if (end_word > begin_word) {
|
|
||||||
p = end_word - 1;
|
|
||||||
while (*p == quotechar) {
|
|
||||||
/* If there is anything beyond end_word, copy it */
|
|
||||||
if (*end_word == '\0') {
|
|
||||||
*p = '\0';
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
size_t end_off = strcspn(end_word + 1, delimiters);
|
|
||||||
memmove (p, end_word, end_off + 1);
|
|
||||||
p += end_off; /* p must correspond to end_word - 1 */
|
|
||||||
end_word += end_off + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (p++; p < end_word; p++) {
|
|
||||||
*p = '\0';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (*end_word == '\0') {
|
if (*end_word == '\0') {
|
||||||
*buf = end_word;
|
*buf = end_word;
|
||||||
} else {
|
} else {
|
||||||
@@ -259,7 +242,7 @@ static char *skip_quoted(char **buf, const char *delimiters, const char *whitesp
|
|||||||
// Simplified version of skip_quoted without quote char
|
// Simplified version of skip_quoted without quote char
|
||||||
// and whitespace == delimiters
|
// and whitespace == delimiters
|
||||||
static char *skip(char **buf, const char *delimiters) {
|
static char *skip(char **buf, const char *delimiters) {
|
||||||
return skip_quoted(buf, delimiters, delimiters, 0);
|
return skip_quoted(buf, delimiters, delimiters);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -296,7 +279,14 @@ void mg_send_http_error(struct mg_connection *conn, int status,
|
|||||||
|
|
||||||
/* Errors 1xx, 204 and 304 MUST NOT send a body */
|
/* Errors 1xx, 204 and 304 MUST NOT send a body */
|
||||||
if (status > 199 && status != 204 && status != 304) {
|
if (status > 199 && status != 204 && status != 304) {
|
||||||
len = mg_snprintf(conn, buf, sizeof(buf), "Error %d: %s", status, reason);
|
// If reason is really long, buf will have been completely filled up.
|
||||||
|
// Then buf[len] = '\n' will replace the trailing NULL with '\n' and kill buf
|
||||||
|
// as a string. Plus the next call to mg_vsnprintf() will be writing to an
|
||||||
|
// invalid location and negative (gigantic because size_t is unsigned) size.
|
||||||
|
//
|
||||||
|
// Given how this function is called, the safest thing to do is probably to
|
||||||
|
// require reason remains relatively short and will get truncated.
|
||||||
|
len = mg_snprintf(conn, buf, REASON_SIZ, "Error %d: %s", status, reason);
|
||||||
cry(conn, "%s", buf);
|
cry(conn, "%s", buf);
|
||||||
buf[len++] = '\n';
|
buf[len++] = '\n';
|
||||||
|
|
||||||
@@ -314,6 +304,14 @@ void mg_send_http_error(struct mg_connection *conn, int status,
|
|||||||
conn->num_bytes_sent += mg_printf(conn, "%s", buf);
|
conn->num_bytes_sent += mg_printf(conn, "%s", buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new thread.
|
||||||
|
*
|
||||||
|
* @param ctx Mongoose context.
|
||||||
|
* @param func thread function.
|
||||||
|
* @param param thread function arguments.
|
||||||
|
* @return the return value of pthread_create.
|
||||||
|
*/
|
||||||
static int start_thread(struct mg_context *ctx, mg_thread_func_t func,
|
static int start_thread(struct mg_context *ctx, mg_thread_func_t func,
|
||||||
void *param) {
|
void *param) {
|
||||||
pthread_t thread_id;
|
pthread_t thread_id;
|
||||||
@@ -332,13 +330,11 @@ static int start_thread(struct mg_context *ctx, mg_thread_func_t func,
|
|||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int set_non_blocking_mode(SOCKET sock) {
|
static void set_non_blocking_mode(SOCKET sock) {
|
||||||
int flags;
|
int flags;
|
||||||
|
|
||||||
flags = fcntl(sock, F_GETFL, 0);
|
flags = fcntl(sock, F_GETFL, 0);
|
||||||
(void) fcntl(sock, F_SETFL, flags | O_NONBLOCK);
|
(void) fcntl(sock, F_SETFL, flags | O_NONBLOCK);
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write data to the IO channel - opened file descriptor, socket or SSL
|
// Write data to the IO channel - opened file descriptor, socket or SSL
|
||||||
@@ -350,8 +346,8 @@ static int64_t push(FILE *fp, SOCKET sock, const char *buf, int64_t len) {
|
|||||||
sent = 0;
|
sent = 0;
|
||||||
while (sent < len) {
|
while (sent < len) {
|
||||||
|
|
||||||
/* How many bytes we send in this iteration */
|
/* How many bytes should we send in this iteration */
|
||||||
k = len - sent > INT_MAX ? INT_MAX : (int) (len - sent);
|
k = ((len - sent) > INT_MAX) ? INT_MAX : (int) (len - sent);
|
||||||
|
|
||||||
if (fp != NULL) {
|
if (fp != NULL) {
|
||||||
n = fwrite(buf + sent, 1, (size_t)k, fp);
|
n = fwrite(buf + sent, 1, (size_t)k, fp);
|
||||||
@@ -458,9 +454,10 @@ static size_t url_decode(const char *src, size_t src_len, char *dst,
|
|||||||
#define HEXTOI(x) (isdigit(x) ? x - '0' : x - 'W')
|
#define HEXTOI(x) (isdigit(x) ? x - '0' : x - 'W')
|
||||||
|
|
||||||
for (i = j = 0; i < src_len && j < dst_len - 1; i++, j++) {
|
for (i = j = 0; i < src_len && j < dst_len - 1; i++, j++) {
|
||||||
if (src[i] == '%' &&
|
if (src[i] == '%' && (i + 2 < src_len) &&
|
||||||
isxdigit(* (const unsigned char *) (src + i + 1)) &&
|
isxdigit(* (const unsigned char *) (src + i + 1)) &&
|
||||||
isxdigit(* (const unsigned char *) (src + i + 2))) {
|
isxdigit(* (const unsigned char *) (src + i + 2)))
|
||||||
|
{
|
||||||
a = tolower(* (const unsigned char *) (src + i + 1));
|
a = tolower(* (const unsigned char *) (src + i + 1));
|
||||||
b = tolower(* (const unsigned char *) (src + i + 2));
|
b = tolower(* (const unsigned char *) (src + i + 2));
|
||||||
dst[j] = (char) ((HEXTOI(a) << 4) | HEXTOI(b));
|
dst[j] = (char) ((HEXTOI(a) << 4) | HEXTOI(b));
|
||||||
@@ -490,6 +487,7 @@ static int get_request_len(const char *buf, int buflen) {
|
|||||||
// Control characters are not allowed but >=128 is.
|
// Control characters are not allowed but >=128 is.
|
||||||
if (!isprint(* (const unsigned char *) s) && *s != '\r' &&
|
if (!isprint(* (const unsigned char *) s) && *s != '\r' &&
|
||||||
*s != '\n' && * (const unsigned char *) s < 128) {
|
*s != '\n' && * (const unsigned char *) s < 128) {
|
||||||
|
// FIXME: Why doesn't this immediately return as malformed?
|
||||||
len = -1;
|
len = -1;
|
||||||
} else if (s[0] == '\n' && s[1] == '\n') {
|
} else if (s[0] == '\n' && s[1] == '\n') {
|
||||||
len = (int) (s - buf) + 2;
|
len = (int) (s - buf) + 2;
|
||||||
@@ -529,7 +527,7 @@ static void parse_http_headers(char **buf, struct mg_request_info *ri) {
|
|||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < (int) ARRAY_SIZE(ri->http_headers); i++) {
|
for (i = 0; i < (int) ARRAY_SIZE(ri->http_headers); i++) {
|
||||||
ri->http_headers[i].name = skip_quoted(buf, ":", " ", 0);
|
ri->http_headers[i].name = skip_quoted(buf, ":", " ");
|
||||||
ri->http_headers[i].value = skip(buf, "\r\n");
|
ri->http_headers[i].value = skip(buf, "\r\n");
|
||||||
if (ri->http_headers[i].name[0] == '\0')
|
if (ri->http_headers[i].name[0] == '\0')
|
||||||
break;
|
break;
|
||||||
@@ -637,6 +635,8 @@ static int set_ports_option(struct mg_context *ctx, int port) {
|
|||||||
tv.tv_sec = 0;
|
tv.tv_sec = 0;
|
||||||
tv.tv_usec = 500 * 1000;
|
tv.tv_usec = 500 * 1000;
|
||||||
|
|
||||||
|
// TODO This code calls close(INVALID_SOCKET), even though close expects positive file descriptors.
|
||||||
|
// Not sure what the behavior is as a result, might be fine.
|
||||||
if ((ctx->local_socket = socket(PF_INET, SOCK_STREAM, 6)) == INVALID_SOCKET ||
|
if ((ctx->local_socket = socket(PF_INET, SOCK_STREAM, 6)) == INVALID_SOCKET ||
|
||||||
setsockopt(ctx->local_socket, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(reuseaddr)) != 0 ||
|
setsockopt(ctx->local_socket, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(reuseaddr)) != 0 ||
|
||||||
setsockopt(ctx->local_socket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) != 0 ||
|
setsockopt(ctx->local_socket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) != 0 ||
|
||||||
@@ -702,22 +702,28 @@ static void close_connection(struct mg_connection *conn) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void discard_current_request_from_buffer(struct mg_connection *conn) {
|
static void discard_current_request_from_buffer(struct mg_connection *conn) {
|
||||||
int buffered_len, body_len;
|
int buffered_len;
|
||||||
|
int64_t remaining = 0, pull_len = 0, count = 0;
|
||||||
|
char discard_buffer[16384];
|
||||||
|
|
||||||
buffered_len = conn->data_len - conn->request_len;
|
buffered_len = conn->data_len - conn->request_len;
|
||||||
assert(buffered_len >= 0);
|
assert(buffered_len >= 0);
|
||||||
|
|
||||||
if (conn->content_len == -1) {
|
// If there was no specified Content-Length header, then there's no way for
|
||||||
body_len = 0;
|
// us to know how much more data needs to be discarded.
|
||||||
} else if (conn->content_len < (int64_t) buffered_len) {
|
//
|
||||||
body_len = (int) conn->content_len;
|
// Otherwise keep pull()-ing content until we consume the full content length.
|
||||||
} else {
|
if (conn->content_len != -1 && conn->consumed_content < conn->content_len) {
|
||||||
body_len = buffered_len;
|
remaining = conn->content_len - conn->consumed_content;
|
||||||
|
while (remaining > 0) {
|
||||||
|
pull_len = (remaining < sizeof(discard_buffer)) ? remaining : sizeof(discard_buffer);
|
||||||
|
count = mg_read(conn, discard_buffer, pull_len);
|
||||||
|
if (count <= 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
remaining -= count;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
conn->data_len -= conn->request_len + body_len;
|
|
||||||
memmove(conn->buf, conn->buf + conn->request_len + body_len,
|
|
||||||
(size_t) conn->data_len);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void process_new_connection(struct mg_connection *conn) {
|
static void process_new_connection(struct mg_connection *conn) {
|
||||||
@@ -731,7 +737,7 @@ static void process_new_connection(struct mg_connection *conn) {
|
|||||||
conn->request_len = read_request(conn->client.sock,
|
conn->request_len = read_request(conn->client.sock,
|
||||||
conn->buf, conn->buf_size, &conn->data_len);
|
conn->buf, conn->buf_size, &conn->data_len);
|
||||||
}
|
}
|
||||||
assert(conn->data_len >= conn->request_len && conn->request_len > 1);
|
assert(conn->data_len >= conn->request_len);
|
||||||
if (conn->request_len == 0 && conn->data_len == conn->buf_size) {
|
if (conn->request_len == 0 && conn->data_len == conn->buf_size) {
|
||||||
mg_send_http_error(conn, 413, "Request Too Large", "");
|
mg_send_http_error(conn, 413, "Request Too Large", "");
|
||||||
return;
|
return;
|
||||||
@@ -739,7 +745,7 @@ static void process_new_connection(struct mg_connection *conn) {
|
|||||||
return; // Remote end closed the connection
|
return; // Remote end closed the connection
|
||||||
}
|
}
|
||||||
|
|
||||||
// Nul-terminate the request cause parse_http_request() uses sscanf
|
// Null-terminate the request because parse_http_request() uses sscanf
|
||||||
conn->buf[conn->request_len - 1] = '\0';
|
conn->buf[conn->request_len - 1] = '\0';
|
||||||
if (!parse_http_request(conn->buf, ri)) {
|
if (!parse_http_request(conn->buf, ri)) {
|
||||||
// Do not put garbage in the access log, just send it back to the client
|
// Do not put garbage in the access log, just send it back to the client
|
||||||
@@ -752,6 +758,11 @@ static void process_new_connection(struct mg_connection *conn) {
|
|||||||
// Request is valid, handle it
|
// Request is valid, handle it
|
||||||
cl = get_header(ri, "Content-Length");
|
cl = get_header(ri, "Content-Length");
|
||||||
conn->content_len = cl == NULL ? -1 : strtoll(cl, NULL, 10);
|
conn->content_len = cl == NULL ? -1 : strtoll(cl, NULL, 10);
|
||||||
|
if (cl != NULL && conn->content_len < 0) {
|
||||||
|
mg_send_http_error(conn, 400, "Bad Request",
|
||||||
|
"Invalid Content-Length header value: [%s]", cl);
|
||||||
|
return;
|
||||||
|
}
|
||||||
conn->birth_time = time(NULL);
|
conn->birth_time = time(NULL);
|
||||||
handle_request(conn);
|
handle_request(conn);
|
||||||
discard_current_request_from_buffer(conn);
|
discard_current_request_from_buffer(conn);
|
||||||
@@ -800,9 +811,6 @@ static void worker_thread(struct mg_context *ctx) {
|
|||||||
// required in the DIAL specification.
|
// required in the DIAL specification.
|
||||||
int buf_size = MAX_REQUEST_SIZE;
|
int buf_size = MAX_REQUEST_SIZE;
|
||||||
|
|
||||||
#ifndef __APPLE__
|
|
||||||
pthread_setname_np( pthread_self(), __func__);
|
|
||||||
#endif
|
|
||||||
conn = (struct mg_connection *) calloc(1, sizeof(*conn) + buf_size);
|
conn = (struct mg_connection *) calloc(1, sizeof(*conn) + buf_size);
|
||||||
conn->buf_size = buf_size;
|
conn->buf_size = buf_size;
|
||||||
conn->buf = (char *) (conn + 1);
|
conn->buf = (char *) (conn + 1);
|
||||||
@@ -820,16 +828,22 @@ static void worker_thread(struct mg_context *ctx) {
|
|||||||
|
|
||||||
// Fill in local IP info
|
// Fill in local IP info
|
||||||
socklen_t addr_len = sizeof(conn->request_info.local_addr);
|
socklen_t addr_len = sizeof(conn->request_info.local_addr);
|
||||||
getsockname(conn->client.sock,
|
if (getsockname(conn->client.sock, (struct sockaddr *) &conn->request_info.local_addr, &addr_len) != 0) {
|
||||||
(struct sockaddr *) &conn->request_info.local_addr, &addr_len);
|
// Something went wrong.
|
||||||
|
mg_send_http_error(conn, 500, "Internal Server Error", "");
|
||||||
process_new_connection(conn);
|
} else {
|
||||||
|
process_new_connection(conn);
|
||||||
|
}
|
||||||
|
|
||||||
close_connection(conn);
|
close_connection(conn);
|
||||||
}
|
}
|
||||||
free(conn);
|
free(conn);
|
||||||
|
|
||||||
// Signal master that we're done with connection and exiting
|
// Signal master that we're done with connection and exiting.
|
||||||
|
//
|
||||||
|
// It is possible that we fail to acquire the mutex and then num_threads will
|
||||||
|
// end up decrementing incorrectly which may cause the server to hang
|
||||||
|
// indefinitely while trying to shutdown. But we'll try our best.
|
||||||
(void) pthread_mutex_lock(&ctx->mutex);
|
(void) pthread_mutex_lock(&ctx->mutex);
|
||||||
ctx->num_threads--;
|
ctx->num_threads--;
|
||||||
(void) pthread_cond_signal(&ctx->cond);
|
(void) pthread_cond_signal(&ctx->cond);
|
||||||
@@ -839,13 +853,25 @@ static void worker_thread(struct mg_context *ctx) {
|
|||||||
DEBUG_TRACE(("exiting"));
|
DEBUG_TRACE(("exiting"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Master thread adds accepted socket to a queue
|
/**
|
||||||
static void produce_socket(struct mg_context *ctx, const struct socket *sp) {
|
* Copy an accepted socket onto the queue. Blocks if the queue is full. This
|
||||||
(void) pthread_mutex_lock(&ctx->mutex);
|
* function is called from the master thread.
|
||||||
|
*
|
||||||
|
* @param ctx Mongoose context.
|
||||||
|
* @param sp the socket.
|
||||||
|
* @return true if successful, false if there was a mutex error.
|
||||||
|
*/
|
||||||
|
static int produce_socket(struct mg_context *ctx, const struct socket *sp) {
|
||||||
|
if (pthread_mutex_lock(&ctx->mutex) != 0) {
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
// If the queue is full, wait
|
// If the queue is full, wait
|
||||||
while (ctx->sq_head - ctx->sq_tail >= (int) ARRAY_SIZE(ctx->queue)) {
|
while (ctx->sq_head - ctx->sq_tail >= (int) ARRAY_SIZE(ctx->queue)) {
|
||||||
(void) pthread_cond_wait(&ctx->sq_empty, &ctx->mutex);
|
if (pthread_cond_wait(&ctx->sq_empty, &ctx->mutex) != 0) {
|
||||||
|
(void) pthread_mutex_unlock(&ctx->mutex);
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
assert(ctx->sq_head - ctx->sq_tail < (int) ARRAY_SIZE(ctx->queue));
|
assert(ctx->sq_head - ctx->sq_tail < (int) ARRAY_SIZE(ctx->queue));
|
||||||
|
|
||||||
@@ -854,17 +880,16 @@ static void produce_socket(struct mg_context *ctx, const struct socket *sp) {
|
|||||||
ctx->sq_head++;
|
ctx->sq_head++;
|
||||||
DEBUG_TRACE(("queued socket %d", sp->sock));
|
DEBUG_TRACE(("queued socket %d", sp->sock));
|
||||||
|
|
||||||
|
// Nothing to do if there is an error on signal. But if we fail to unlock
|
||||||
|
// then we're in a bad state.
|
||||||
(void) pthread_cond_signal(&ctx->sq_full);
|
(void) pthread_cond_signal(&ctx->sq_full);
|
||||||
(void) pthread_mutex_unlock(&ctx->mutex);
|
return (pthread_mutex_unlock(&ctx->mutex) == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void master_thread(struct mg_context *ctx) {
|
static void master_thread(struct mg_context *ctx) {
|
||||||
struct socket accepted;
|
struct socket accepted;
|
||||||
|
|
||||||
#ifndef __APPLE__
|
|
||||||
pthread_setname_np( pthread_self(), __func__);
|
|
||||||
#endif
|
|
||||||
socklen_t sock_len = sizeof(accepted.local_addr);
|
socklen_t sock_len = sizeof(accepted.local_addr);
|
||||||
memcpy(&accepted.local_addr, &ctx->local_address, sock_len);
|
memcpy(&accepted.local_addr, &ctx->local_address, sock_len);
|
||||||
|
|
||||||
@@ -877,7 +902,10 @@ static void master_thread(struct mg_context *ctx) {
|
|||||||
if (accepted.sock != INVALID_SOCKET) {
|
if (accepted.sock != INVALID_SOCKET) {
|
||||||
// Put accepted socket structure into the queue.
|
// Put accepted socket structure into the queue.
|
||||||
DEBUG_TRACE(("accepted socket %d", accepted.sock));
|
DEBUG_TRACE(("accepted socket %d", accepted.sock));
|
||||||
produce_socket(ctx, &accepted);
|
// If the socket fails, trigger stop and try to exit gracefully.
|
||||||
|
if (!produce_socket(ctx, &accepted)) {
|
||||||
|
ctx->stop_flag = 1;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DEBUG_TRACE(("stopping workers"));
|
DEBUG_TRACE(("stopping workers"));
|
||||||
@@ -886,14 +914,18 @@ static void master_thread(struct mg_context *ctx) {
|
|||||||
close_all_listening_sockets(ctx);
|
close_all_listening_sockets(ctx);
|
||||||
|
|
||||||
// Wakeup workers that are waiting for connections to handle.
|
// Wakeup workers that are waiting for connections to handle.
|
||||||
pthread_cond_broadcast(&ctx->sq_full);
|
// Nothing we can do if there is an error.
|
||||||
|
(void) pthread_cond_broadcast(&ctx->sq_full);
|
||||||
|
|
||||||
// Wait until all threads finish
|
// Wait until all threads finish.
|
||||||
(void) pthread_mutex_lock(&ctx->mutex);
|
// If we cannot acquire the lock, we're in a bad state so skip this and
|
||||||
while (ctx->num_threads > 0) {
|
// just try to clean up and shut down.
|
||||||
(void) pthread_cond_wait(&ctx->cond, &ctx->mutex);
|
if (pthread_mutex_lock(&ctx->mutex) == 0) {
|
||||||
|
while (ctx->num_threads > 0) {
|
||||||
|
(void) pthread_cond_wait(&ctx->cond, &ctx->mutex);
|
||||||
|
}
|
||||||
|
(void) pthread_mutex_unlock(&ctx->mutex);
|
||||||
}
|
}
|
||||||
(void) pthread_mutex_unlock(&ctx->mutex);
|
|
||||||
|
|
||||||
// All threads exited, no sync is needed. Destroy mutex and condvars
|
// All threads exited, no sync is needed. Destroy mutex and condvars
|
||||||
(void) pthread_mutex_destroy(&ctx->mutex);
|
(void) pthread_mutex_destroy(&ctx->mutex);
|
||||||
@@ -925,10 +957,13 @@ void mg_stop(struct mg_context *ctx) {
|
|||||||
|
|
||||||
struct mg_context *mg_start(mg_callback_t user_callback, void *user_data, int port) {
|
struct mg_context *mg_start(mg_callback_t user_callback, void *user_data, int port) {
|
||||||
struct mg_context *ctx;
|
struct mg_context *ctx;
|
||||||
|
int retval;
|
||||||
|
|
||||||
// Allocate context and initialize reasonable general case defaults.
|
// Allocate context and initialize reasonable general case defaults.
|
||||||
// TODO(lsm): do proper error handling here.
|
|
||||||
ctx = (struct mg_context *) calloc(1, sizeof(*ctx));
|
ctx = (struct mg_context *) calloc(1, sizeof(*ctx));
|
||||||
|
if (ctx == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
ctx->user_callback = user_callback;
|
ctx->user_callback = user_callback;
|
||||||
ctx->user_data = user_data;
|
ctx->user_data = user_data;
|
||||||
|
|
||||||
@@ -937,15 +972,26 @@ struct mg_context *mg_start(mg_callback_t user_callback, void *user_data, int po
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
// Ignore SIGPIPE signal, so if browser cancels the request, it
|
// Ignore SIGPIPE signal, so if browser cancels the request, it
|
||||||
// won't kill the whole process.
|
// won't kill the whole
|
||||||
(void) signal(SIGPIPE, SIG_IGN);
|
if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) {
|
||||||
(void) pthread_mutex_init(&ctx->mutex, NULL);
|
free_context(ctx);
|
||||||
(void) pthread_cond_init(&ctx->cond, NULL);
|
return NULL;
|
||||||
(void) pthread_cond_init(&ctx->sq_empty, NULL);
|
};
|
||||||
(void) pthread_cond_init(&ctx->sq_full, NULL);
|
if (pthread_mutex_init(&ctx->mutex, NULL) != 0 ||
|
||||||
|
pthread_cond_init(&ctx->cond, NULL) != 0 ||
|
||||||
|
pthread_cond_init(&ctx->sq_empty, NULL) != 0 ||
|
||||||
|
pthread_cond_init(&ctx->sq_full, NULL) != 0)
|
||||||
|
{
|
||||||
|
free_context(ctx);
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
// Start master (listening) thread
|
// Start master (listening) thread
|
||||||
start_thread(ctx, (mg_thread_func_t) master_thread, ctx);
|
retval = start_thread(ctx, (mg_thread_func_t) master_thread, ctx);
|
||||||
|
if (retval != 0) {
|
||||||
|
free_context(ctx);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
// Start worker threads
|
// Start worker threads
|
||||||
for (int i = 0; i < NUM_THREADS; i++) {
|
for (int i = 0; i < NUM_THREADS; i++) {
|
||||||
|
|||||||
@@ -1,3 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2014-2019 Netflix, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* Redistributions of source code must retain the above copyright notice, this
|
||||||
|
* list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY NETFLIX, INC. AND CONTRIBUTORS "AS IS" AND ANY
|
||||||
|
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL NETFLIX OR CONTRIBUTORS BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||||
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@@ -14,8 +38,14 @@ extern char *spAppNetflix;
|
|||||||
extern char spNetflix[];
|
extern char spNetflix[];
|
||||||
static char *defaultLaunchParam = "source_type=12";
|
static char *defaultLaunchParam = "source_type=12";
|
||||||
|
|
||||||
// Adding 20 bytes for prepended source_type for Netflix
|
// Adding 40 bytes for defaultLaunchParam, plus additional characters that
|
||||||
static char sQueryParam[DIAL_MAX_PAYLOAD+DIAL_MAX_ADDITIONALURL+40];
|
// may get added.
|
||||||
|
//
|
||||||
|
// dial_server.c ensures the payload is <= DIAL_MAX_PAYLOAD but since we
|
||||||
|
// URL-encode it, the total string length may triple.
|
||||||
|
//
|
||||||
|
// dial_server.c ensures the additional data URL is <= DIAL_MAX_ADDITIONALURL.
|
||||||
|
static char sQueryParam[3 * DIAL_MAX_PAYLOAD + DIAL_MAX_ADDITIONALURL + 40];
|
||||||
|
|
||||||
int isAppRunning( char *pzName, char *pzCommandPattern );
|
int isAppRunning( char *pzName, char *pzCommandPattern );
|
||||||
int shouldRelaunch(DIALServer *pServer, const char *pAppName, const char *args );
|
int shouldRelaunch(DIALServer *pServer, const char *pAppName, const char *args );
|
||||||
@@ -34,21 +64,31 @@ DIALStatus netflix_start(DIALServer *ds, const char *appname,
|
|||||||
|
|
||||||
// construct the payload to determine if it has changed from the previous launch
|
// construct the payload to determine if it has changed from the previous launch
|
||||||
memset( sQueryParam, 0, sizeof(sQueryParam) );
|
memset( sQueryParam, 0, sizeof(sQueryParam) );
|
||||||
strcat( sQueryParam, defaultLaunchParam );
|
strncpy( sQueryParam, defaultLaunchParam, sizeof(sQueryParam) - 1);
|
||||||
if(strlen(payload))
|
if(strlen(payload))
|
||||||
{
|
{
|
||||||
char * pUrlEncodedParams;
|
char * pUrlEncodedParams;
|
||||||
pUrlEncodedParams = url_encode( payload );
|
pUrlEncodedParams = url_encode( payload );
|
||||||
if( pUrlEncodedParams ){
|
if( pUrlEncodedParams ){
|
||||||
strcat( sQueryParam, "&dial=");
|
if (strlen(sQueryParam) + sizeof("&dial=") + strlen(pUrlEncodedParams) < sizeof(sQueryParam)) {
|
||||||
strcat( sQueryParam, pUrlEncodedParams );
|
strcat( sQueryParam, "&dial=" );
|
||||||
free( pUrlEncodedParams );
|
strcat( sQueryParam, pUrlEncodedParams );
|
||||||
|
free( pUrlEncodedParams );
|
||||||
|
} else {
|
||||||
|
free( pUrlEncodedParams );
|
||||||
|
return kDIALStatusError;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(strlen(additionalDataUrl)){
|
if(strlen(additionalDataUrl)){
|
||||||
strcat(sQueryParam, "&");
|
if (strlen(sQueryParam) + sizeof("&") + strlen(additionalDataUrl) < sizeof(sQueryParam)) {
|
||||||
strcat(sQueryParam, additionalDataUrl);
|
strcat(sQueryParam, "&");
|
||||||
|
strcat(sQueryParam, additionalDataUrl);
|
||||||
|
} else {
|
||||||
|
return kDIALStatusError;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("appPid = %s, shouldRelaunch = %s queryParams = %s\n",
|
printf("appPid = %s, shouldRelaunch = %s queryParams = %s\n",
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2014 Netflix, Inc.
|
* Copyright (c) 2014-2019 Netflix, Inc.
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
|||||||
@@ -35,13 +35,6 @@
|
|||||||
#include "mongoose.h"
|
#include "mongoose.h"
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
#ifdef __APPLE__
|
|
||||||
#include <ifaddrs.h>
|
|
||||||
#include <net/if.h>
|
|
||||||
#include <net/if_dl.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
// TODO: Partners should define this port
|
// TODO: Partners should define this port
|
||||||
#define SSDP_PORT (56790)
|
#define SSDP_PORT (56790)
|
||||||
static char gBuf[4096];
|
static char gBuf[4096];
|
||||||
@@ -85,8 +78,7 @@ static const char ssdp_reply[] = "HTTP/1.1 200 OK\r\n"
|
|||||||
static const char wakeup_header[] = "WAKEUP: MAC=%s;Timeout=%d\r\n";
|
static const char wakeup_header[] = "WAKEUP: MAC=%s;Timeout=%d\r\n";
|
||||||
#define STR_TIMEOUTLEN 6 /* Longest is 32767 */
|
#define STR_TIMEOUTLEN 6 /* Longest is 32767 */
|
||||||
#define HW_ADDRSTRLEN 18
|
#define HW_ADDRSTRLEN 18
|
||||||
static char ip_addr[INET_ADDRSTRLEN] = "127.0.0.1";
|
static char ip_addr[INET6_ADDRSTRLEN] = "127.0.0.1";
|
||||||
static char hw_addr[HW_ADDRSTRLEN] = "00:00:00:00:00:00";
|
|
||||||
static int dial_port = 0;
|
static int dial_port = 0;
|
||||||
static int my_port = 0;
|
static int my_port = 0;
|
||||||
static char friendly_name[256];
|
static char friendly_name[256];
|
||||||
@@ -115,35 +107,24 @@ static void *request_handler(enum mg_event event,
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef __APPLE__
|
/**
|
||||||
static void get_local_address() {
|
* Returns the local hardware address (e.g. MAC address). On macOS the "en0"
|
||||||
struct ifaddrs* iflist;
|
* interface is used. On other platforms the first non-loopback interface is
|
||||||
char *if_name= "en0";
|
* used.
|
||||||
char buf[INET6_ADDRSTRLEN];
|
*
|
||||||
|
* As a side-effect, the local global ip_addr is also populated.
|
||||||
if (getifaddrs(&iflist) == 0) {
|
*
|
||||||
for (struct ifaddrs* cur = iflist; cur; cur = cur->ifa_next) {
|
* (Are these choices of interface really the right ones? Seems risky for
|
||||||
if (strcmp(cur->ifa_name, if_name) == 0) {
|
* multi-homed systems.)
|
||||||
if ((cur->ifa_addr->sa_family == AF_LINK) && cur->ifa_addr) {
|
*
|
||||||
unsigned char mac[6];
|
* @return the local hardware address or NULL if it does not exist, cannot
|
||||||
struct sockaddr_dl* sdl = (struct sockaddr_dl*)cur->ifa_addr;
|
* be retrieved, or out-of-memory. The caller must free the returned
|
||||||
memcpy(mac, LLADDR(sdl), sdl->sdl_alen);
|
* memory.
|
||||||
sprintf(hw_addr, "%02x:%02x:%02x:%02x:%02x:%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
*/
|
||||||
}
|
static char * get_local_address() {
|
||||||
|
|
||||||
if (cur->ifa_addr->sa_family == AF_INET) {
|
|
||||||
void *tmp = &((struct sockaddr_in *)cur->ifa_addr)->sin_addr;
|
|
||||||
strcpy(ip_addr, inet_ntop(cur->ifa_addr->sa_family, tmp, buf, sizeof(buf)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
freeifaddrs(iflist);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
static void get_local_address() {
|
|
||||||
struct ifconf ifc;
|
struct ifconf ifc;
|
||||||
char buf[4096];
|
char buf[4096];
|
||||||
|
char * hw_addr = NULL;
|
||||||
int s, i;
|
int s, i;
|
||||||
if (-1 == (s = socket(AF_INET, SOCK_DGRAM, 0))) {
|
if (-1 == (s = socket(AF_INET, SOCK_DGRAM, 0))) {
|
||||||
perror("socket");
|
perror("socket");
|
||||||
@@ -160,8 +141,8 @@ static void get_local_address() {
|
|||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
for (i = 0; i < ifc.ifc_len/sizeof(ifc.ifc_req[0]); i++) {
|
for (i = 0; i < ifc.ifc_len/sizeof(ifc.ifc_req[0]); i++) {
|
||||||
strcpy(ip_addr,
|
strncpy(ip_addr,
|
||||||
inet_ntoa(((struct sockaddr_in *)(&ifc.ifc_req[i].ifr_addr))->sin_addr));
|
inet_ntoa(((struct sockaddr_in *)(&ifc.ifc_req[i].ifr_addr))->sin_addr), sizeof(ip_addr) - 1);
|
||||||
if (0 > ioctl(s, SIOCGIFFLAGS, &ifc.ifc_req[i])) {
|
if (0 > ioctl(s, SIOCGIFFLAGS, &ifc.ifc_req[i])) {
|
||||||
perror("SIOCGIFFLAGS");
|
perror("SIOCGIFFLAGS");
|
||||||
exit(1);
|
exit(1);
|
||||||
@@ -174,6 +155,14 @@ static void get_local_address() {
|
|||||||
perror("SIOCGIFHWADDR");
|
perror("SIOCGIFHWADDR");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
// FIXME: How do I figure out what type of interface this is in order to
|
||||||
|
// cast the struct sockaddr ifr_hraddr to the proper type and extract the
|
||||||
|
// hardware address?
|
||||||
|
//
|
||||||
|
// Make sure this is correct for the target device and platform.
|
||||||
|
hw_addr = (char*)malloc(HW_ADDRSTRLEN + 1);
|
||||||
|
if (hw_addr == NULL)
|
||||||
|
break;
|
||||||
sprintf(hw_addr, "%02x:%02x:%02x:%02x:%02x:%02x",
|
sprintf(hw_addr, "%02x:%02x:%02x:%02x:%02x:%02x",
|
||||||
(unsigned char)ifc.ifc_req[i].ifr_hwaddr.sa_data[0],
|
(unsigned char)ifc.ifc_req[i].ifr_hwaddr.sa_data[0],
|
||||||
(unsigned char)ifc.ifc_req[i].ifr_hwaddr.sa_data[1],
|
(unsigned char)ifc.ifc_req[i].ifr_hwaddr.sa_data[1],
|
||||||
@@ -184,14 +173,14 @@ static void get_local_address() {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
close(s);
|
close(s);
|
||||||
|
return hw_addr;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
static void handle_mcast() {
|
static void handle_mcast(char *hw_addr) {
|
||||||
int s, one = 1, bytes;
|
int s, one = 1, bytes;
|
||||||
socklen_t addrlen;
|
socklen_t addrlen;
|
||||||
struct sockaddr_in saddr;
|
struct sockaddr_in saddr = {0};
|
||||||
struct ip_mreq mreq;
|
struct ip_mreq mreq = {0};
|
||||||
char wakeup_buf[sizeof(wakeup_header) + HW_ADDRSTRLEN + STR_TIMEOUTLEN] = {0, };
|
char wakeup_buf[sizeof(wakeup_header) + HW_ADDRSTRLEN + STR_TIMEOUTLEN] = {0, };
|
||||||
char send_buf[sizeof(ssdp_reply) + INET_ADDRSTRLEN + 256 + 256 + sizeof(wakeup_buf)] = {0,};
|
char send_buf[sizeof(ssdp_reply) + INET_ADDRSTRLEN + 256 + 256 + sizeof(wakeup_buf)] = {0,};
|
||||||
int send_size;
|
int send_size;
|
||||||
@@ -208,11 +197,7 @@ static void handle_mcast() {
|
|||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
saddr.sin_family = AF_INET;
|
saddr.sin_family = AF_INET;
|
||||||
#ifdef __APPLE__
|
|
||||||
saddr.sin_addr.s_addr = INADDR_ANY;
|
|
||||||
#else
|
|
||||||
saddr.sin_addr.s_addr = inet_addr("239.255.255.250");
|
saddr.sin_addr.s_addr = inet_addr("239.255.255.250");
|
||||||
#endif
|
|
||||||
saddr.sin_port = htons(1900);
|
saddr.sin_port = htons(1900);
|
||||||
|
|
||||||
if (-1 == bind(s, (struct sockaddr *)&saddr, sizeof(saddr))) {
|
if (-1 == bind(s, (struct sockaddr *)&saddr, sizeof(saddr))) {
|
||||||
@@ -261,32 +246,42 @@ static void handle_mcast() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void run_ssdp(int port, const char *pFriendlyName, const char * pModelName, const char *pUuid) {
|
void run_ssdp(int port, const char *pFriendlyName, const char * pModelName, const char *pUuid) {
|
||||||
|
char *hw_addr = NULL;
|
||||||
struct sockaddr sa;
|
struct sockaddr sa;
|
||||||
socklen_t len = sizeof(sa);
|
socklen_t len = sizeof(sa);
|
||||||
if(pFriendlyName) {
|
if(pFriendlyName) {
|
||||||
strncpy(friendly_name, pFriendlyName, sizeof(friendly_name));
|
strncpy(friendly_name, pFriendlyName, sizeof(friendly_name));
|
||||||
friendly_name[255] = '\0';
|
friendly_name[sizeof(friendly_name) - 1] = '\0';
|
||||||
} else {
|
} else {
|
||||||
strcpy(friendly_name, "DIAL server sample");
|
strcpy(friendly_name, "DIAL server sample");
|
||||||
}
|
}
|
||||||
if(pModelName) {
|
if(pModelName) {
|
||||||
strncpy(model_name, pModelName, sizeof(model_name));
|
strncpy(model_name, pModelName, sizeof(model_name));
|
||||||
uuid[255] = '\0';
|
model_name[sizeof(model_name) - 1] = '\0';
|
||||||
} else {
|
} else {
|
||||||
strcpy(model_name, "deadbeef-dead-beef-dead-beefdeadbeef");
|
strcpy(model_name, "deadbeef-dead-beef-dead-beefdeadbeef");
|
||||||
}
|
}
|
||||||
if(pUuid) {
|
if(pUuid) {
|
||||||
strncpy(uuid, pUuid, sizeof(uuid));
|
strncpy(uuid, pUuid, sizeof(uuid));
|
||||||
uuid[255] = '\0';
|
uuid[sizeof(uuid) - 1] = '\0';
|
||||||
} else {
|
} else {
|
||||||
strcpy(uuid, "deadbeef-dead-beef-dead-beefdeadbeef");
|
strcpy(uuid, "deadbeef-dead-beef-dead-beefdeadbeef");
|
||||||
}
|
}
|
||||||
dial_port = port;
|
dial_port = port;
|
||||||
get_local_address();
|
hw_addr = get_local_address();
|
||||||
ctx = mg_start(&request_handler, NULL, SSDP_PORT);
|
if (hw_addr == NULL) {
|
||||||
if (mg_get_listen_addr(ctx, &sa, &len)) {
|
printf("Unable to retrieve hardware address.");
|
||||||
my_port = ntohs(((struct sockaddr_in *)&sa)->sin_port);
|
return;
|
||||||
}
|
}
|
||||||
printf("SSDP listening on %s:%d\n", ip_addr, my_port);
|
ctx = mg_start(&request_handler, NULL, SSDP_PORT);
|
||||||
handle_mcast();
|
if (ctx == NULL) {
|
||||||
|
printf("Unable to start SSDP master listening thread.");
|
||||||
|
} else {
|
||||||
|
if (mg_get_listen_addr(ctx, &sa, &len)) {
|
||||||
|
my_port = ntohs(((struct sockaddr_in *)&sa)->sin_port);
|
||||||
|
}
|
||||||
|
printf("SSDP listening on %s:%d\n", ip_addr, my_port);
|
||||||
|
handle_mcast(hw_addr);
|
||||||
|
}
|
||||||
|
free(hw_addr); hw_addr = NULL;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2019 Netflix, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* Redistributions of source code must retain the above copyright notice, this
|
||||||
|
* list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY NETFLIX, INC. AND CONTRIBUTORS "AS IS" AND ANY
|
||||||
|
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL NETFLIX OR CONTRIBUTORS BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||||
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include "system_callbacks.h"
|
#include "system_callbacks.h"
|
||||||
@@ -20,8 +44,8 @@ DIALStatus system_start(DIALServer *ds, const char *appname, const char *payload
|
|||||||
if (strlen(spSleepPassword) != 0) {
|
if (strlen(spSleepPassword) != 0) {
|
||||||
|
|
||||||
/* Look for key */
|
/* Look for key */
|
||||||
char *key_value;
|
char *key_value = strchr(query_string, '&');
|
||||||
if ( (key_value = strchr(query_string, '&')) == '\0' ) {
|
if ( key_value == NULL || *key_value == '\0' ) {
|
||||||
return kDIALStatusErrorForbidden; // No key specified.
|
return kDIALStatusErrorForbidden; // No key specified.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2018 Netflix, Inc.
|
* Copyright (c) 2018-2019 Netflix, Inc.
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
@@ -28,6 +28,11 @@
|
|||||||
|
|
||||||
#include "dial_server.h"
|
#include "dial_server.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Sleep is the only supported action for 'system', and not implemented in this reference.
|
||||||
|
*/
|
||||||
DIALStatus system_start(DIALServer *ds, const char *appname,
|
DIALStatus system_start(DIALServer *ds, const char *appname,
|
||||||
const char *payload, const char* query_string,
|
const char *payload, const char* query_string,
|
||||||
const char *additionalDataUrl,
|
const char *additionalDataUrl,
|
||||||
|
|||||||
@@ -142,6 +142,40 @@ function launchApplication(host, app, payload) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* send HTTP request to the host.
|
||||||
|
*
|
||||||
|
* @param {String} host IP address of host on which DIAL server
|
||||||
|
* @param {String} method HTTP method.
|
||||||
|
* @param {Object} headers HTTP headers, passed as {"Header": Value}
|
||||||
|
* @param {String} urlSuffix appended to the URL
|
||||||
|
* @param {String} body HTTP body
|
||||||
|
*/
|
||||||
|
function sendRequest(host, method, headers, urlSuffix, body) {
|
||||||
|
urlSuffix = urlSuffix || "";
|
||||||
|
|
||||||
|
return new Q()
|
||||||
|
.then(getAppsUrl.bind(null, host))
|
||||||
|
.then(function (appUrl) {
|
||||||
|
var request = {
|
||||||
|
url: encodeURI(appUrl + urlSuffix),
|
||||||
|
method: method,
|
||||||
|
timeout: 6000,
|
||||||
|
headers: headers
|
||||||
|
};
|
||||||
|
request.body = body;
|
||||||
|
return new Q.Promise(function (resolve, reject) {
|
||||||
|
return httpRequest(request, function handleResponse(error, response) {
|
||||||
|
if(error) {
|
||||||
|
reject(error);
|
||||||
|
} else {
|
||||||
|
resolve(response);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function sleepSystem(host, key) {
|
function sleepSystem(host, key) {
|
||||||
var keyComponent = key ? `&key=${key}` : '';
|
var keyComponent = key ? `&key=${key}` : '';
|
||||||
return getAppsUrl(host)
|
return getAppsUrl(host)
|
||||||
@@ -424,3 +458,4 @@ module.exports.constructAppResourceUrl = constructAppResourceUrl;
|
|||||||
module.exports.getAppsUrl = getAppsUrl;
|
module.exports.getAppsUrl = getAppsUrl;
|
||||||
module.exports.getLocation = getLocation;
|
module.exports.getLocation = getLocation;
|
||||||
module.exports.sleepSystem = sleepSystem;
|
module.exports.sleepSystem = sleepSystem;
|
||||||
|
module.exports.sendRequest = sendRequest;
|
||||||
|
|||||||
248
server/tests/js_tests/package-lock.json
generated
248
server/tests/js_tests/package-lock.json
generated
@@ -9,10 +9,10 @@
|
|||||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz",
|
"resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz",
|
||||||
"integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=",
|
"integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=",
|
||||||
"requires": {
|
"requires": {
|
||||||
"co": "4.6.0",
|
"co": "^4.6.0",
|
||||||
"fast-deep-equal": "1.1.0",
|
"fast-deep-equal": "^1.0.0",
|
||||||
"fast-json-stable-stringify": "2.0.0",
|
"fast-json-stable-stringify": "^2.0.0",
|
||||||
"json-schema-traverse": "0.3.1"
|
"json-schema-traverse": "^0.3.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ansi-regex": {
|
"ansi-regex": {
|
||||||
@@ -25,7 +25,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz",
|
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz",
|
||||||
"integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==",
|
"integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"safer-buffer": "2.1.2"
|
"safer-buffer": "~2.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"assert-plus": {
|
"assert-plus": {
|
||||||
@@ -38,7 +38,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/async/-/async-2.6.1.tgz",
|
"resolved": "https://registry.npmjs.org/async/-/async-2.6.1.tgz",
|
||||||
"integrity": "sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ==",
|
"integrity": "sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"lodash": "4.17.11"
|
"lodash": "^4.17.10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"asynckit": {
|
"asynckit": {
|
||||||
@@ -61,7 +61,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
|
||||||
"integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=",
|
"integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=",
|
||||||
"requires": {
|
"requires": {
|
||||||
"tweetnacl": "0.14.5"
|
"tweetnacl": "^0.14.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"bluebird": {
|
"bluebird": {
|
||||||
@@ -89,9 +89,9 @@
|
|||||||
"resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz",
|
||||||
"integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=",
|
"integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=",
|
||||||
"requires": {
|
"requires": {
|
||||||
"string-width": "1.0.2",
|
"string-width": "^1.0.1",
|
||||||
"strip-ansi": "3.0.1",
|
"strip-ansi": "^3.0.1",
|
||||||
"wrap-ansi": "2.1.0"
|
"wrap-ansi": "^2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"co": {
|
"co": {
|
||||||
@@ -114,7 +114,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz",
|
||||||
"integrity": "sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w==",
|
"integrity": "sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"delayed-stream": "1.0.0"
|
"delayed-stream": "~1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"core-util-is": {
|
"core-util-is": {
|
||||||
@@ -132,7 +132,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
|
"resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
|
||||||
"integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
|
"integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
|
||||||
"requires": {
|
"requires": {
|
||||||
"assert-plus": "1.0.0"
|
"assert-plus": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"debug": {
|
"debug": {
|
||||||
@@ -140,7 +140,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
|
"resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
|
||||||
"integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
|
"integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"ms": "2.1.1"
|
"ms": "^2.1.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"decamelize": {
|
"decamelize": {
|
||||||
@@ -158,8 +158,8 @@
|
|||||||
"resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
|
||||||
"integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=",
|
"integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=",
|
||||||
"requires": {
|
"requires": {
|
||||||
"jsbn": "0.1.1",
|
"jsbn": "~0.1.0",
|
||||||
"safer-buffer": "2.1.2"
|
"safer-buffer": "^2.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"error-ex": {
|
"error-ex": {
|
||||||
@@ -167,7 +167,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
|
||||||
"integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
|
"integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"is-arrayish": "0.2.1"
|
"is-arrayish": "^0.2.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"extend": {
|
"extend": {
|
||||||
@@ -200,8 +200,8 @@
|
|||||||
"resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz",
|
||||||
"integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=",
|
"integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=",
|
||||||
"requires": {
|
"requires": {
|
||||||
"path-exists": "2.1.0",
|
"path-exists": "^2.0.0",
|
||||||
"pinkie-promise": "2.0.1"
|
"pinkie-promise": "^2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"forever-agent": {
|
"forever-agent": {
|
||||||
@@ -214,9 +214,9 @@
|
|||||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
|
||||||
"integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
|
"integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"asynckit": "0.4.0",
|
"asynckit": "^0.4.0",
|
||||||
"combined-stream": "1.0.7",
|
"combined-stream": "^1.0.6",
|
||||||
"mime-types": "2.1.21"
|
"mime-types": "^2.1.12"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"get-caller-file": {
|
"get-caller-file": {
|
||||||
@@ -229,7 +229,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
|
"resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
|
||||||
"integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
|
"integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
|
||||||
"requires": {
|
"requires": {
|
||||||
"assert-plus": "1.0.0"
|
"assert-plus": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"graceful-fs": {
|
"graceful-fs": {
|
||||||
@@ -247,8 +247,8 @@
|
|||||||
"resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.0.tgz",
|
||||||
"integrity": "sha512-+qnmNjI4OfH2ipQ9VQOw23bBd/ibtfbVdK2fYbY4acTDqKTW/YDp9McimZdDbG8iV9fZizUqQMD5xvriB146TA==",
|
"integrity": "sha512-+qnmNjI4OfH2ipQ9VQOw23bBd/ibtfbVdK2fYbY4acTDqKTW/YDp9McimZdDbG8iV9fZizUqQMD5xvriB146TA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"ajv": "5.5.2",
|
"ajv": "^5.3.0",
|
||||||
"har-schema": "2.0.0"
|
"har-schema": "^2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"hosted-git-info": {
|
"hosted-git-info": {
|
||||||
@@ -261,9 +261,9 @@
|
|||||||
"resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
|
||||||
"integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
|
"integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
|
||||||
"requires": {
|
"requires": {
|
||||||
"assert-plus": "1.0.0",
|
"assert-plus": "^1.0.0",
|
||||||
"jsprim": "1.4.1",
|
"jsprim": "^1.2.2",
|
||||||
"sshpk": "1.15.1"
|
"sshpk": "^1.7.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"invert-kv": {
|
"invert-kv": {
|
||||||
@@ -286,7 +286,7 @@
|
|||||||
"resolved": "http://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz",
|
"resolved": "http://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz",
|
||||||
"integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=",
|
"integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=",
|
||||||
"requires": {
|
"requires": {
|
||||||
"builtin-modules": "1.1.1"
|
"builtin-modules": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"is-fullwidth-code-point": {
|
"is-fullwidth-code-point": {
|
||||||
@@ -294,7 +294,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
|
||||||
"integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
|
"integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
|
||||||
"requires": {
|
"requires": {
|
||||||
"number-is-nan": "1.0.1"
|
"number-is-nan": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"is-typedarray": {
|
"is-typedarray": {
|
||||||
@@ -353,7 +353,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz",
|
||||||
"integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=",
|
"integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=",
|
||||||
"requires": {
|
"requires": {
|
||||||
"invert-kv": "1.0.0"
|
"invert-kv": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"load-json-file": {
|
"load-json-file": {
|
||||||
@@ -361,11 +361,11 @@
|
|||||||
"resolved": "http://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz",
|
"resolved": "http://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz",
|
||||||
"integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=",
|
"integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=",
|
||||||
"requires": {
|
"requires": {
|
||||||
"graceful-fs": "4.1.11",
|
"graceful-fs": "^4.1.2",
|
||||||
"parse-json": "2.2.0",
|
"parse-json": "^2.2.0",
|
||||||
"pify": "2.3.0",
|
"pify": "^2.0.0",
|
||||||
"pinkie-promise": "2.0.1",
|
"pinkie-promise": "^2.0.0",
|
||||||
"strip-bom": "2.0.0"
|
"strip-bom": "^2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"lodash": {
|
"lodash": {
|
||||||
@@ -383,7 +383,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.21.tgz",
|
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.21.tgz",
|
||||||
"integrity": "sha512-3iL6DbwpyLzjR3xHSFNFeb9Nz/M8WDkX33t1GFQnFOllWk8pOrh/LSrB5OXlnlW5P9LH73X6loW/eogc+F5lJg==",
|
"integrity": "sha512-3iL6DbwpyLzjR3xHSFNFeb9Nz/M8WDkX33t1GFQnFOllWk8pOrh/LSrB5OXlnlW5P9LH73X6loW/eogc+F5lJg==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"mime-db": "1.37.0"
|
"mime-db": "~1.37.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"moment": {
|
"moment": {
|
||||||
@@ -401,11 +401,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/node-ssdp/-/node-ssdp-3.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/node-ssdp/-/node-ssdp-3.3.0.tgz",
|
||||||
"integrity": "sha512-hFBkfUJytKC2x64jljojAbktG8aOL0C1YuNjCK54ZGBBg2382J3oTuK17T+aFgmy47noKHE5arLnYppo0JjcLw==",
|
"integrity": "sha512-hFBkfUJytKC2x64jljojAbktG8aOL0C1YuNjCK54ZGBBg2382J3oTuK17T+aFgmy47noKHE5arLnYppo0JjcLw==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"async": "2.6.1",
|
"async": "^2.6.0",
|
||||||
"bluebird": "3.5.2",
|
"bluebird": "^3.5.1",
|
||||||
"debug": "3.2.6",
|
"debug": "^3.1.0",
|
||||||
"extend": "3.0.2",
|
"extend": "^3.0.1",
|
||||||
"ip": "1.1.5"
|
"ip": "^1.1.5"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"normalize-package-data": {
|
"normalize-package-data": {
|
||||||
@@ -413,10 +413,10 @@
|
|||||||
"resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz",
|
||||||
"integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==",
|
"integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"hosted-git-info": "2.7.1",
|
"hosted-git-info": "^2.1.4",
|
||||||
"is-builtin-module": "1.0.0",
|
"is-builtin-module": "^1.0.0",
|
||||||
"semver": "5.6.0",
|
"semver": "2 || 3 || 4 || 5",
|
||||||
"validate-npm-package-license": "3.0.4"
|
"validate-npm-package-license": "^3.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"number-is-nan": {
|
"number-is-nan": {
|
||||||
@@ -434,7 +434,7 @@
|
|||||||
"resolved": "http://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz",
|
"resolved": "http://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz",
|
||||||
"integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=",
|
"integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=",
|
||||||
"requires": {
|
"requires": {
|
||||||
"lcid": "1.0.0"
|
"lcid": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"parse-json": {
|
"parse-json": {
|
||||||
@@ -442,7 +442,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz",
|
||||||
"integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=",
|
"integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=",
|
||||||
"requires": {
|
"requires": {
|
||||||
"error-ex": "1.3.2"
|
"error-ex": "^1.2.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"path-exists": {
|
"path-exists": {
|
||||||
@@ -450,7 +450,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz",
|
||||||
"integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=",
|
"integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=",
|
||||||
"requires": {
|
"requires": {
|
||||||
"pinkie-promise": "2.0.1"
|
"pinkie-promise": "^2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"path-type": {
|
"path-type": {
|
||||||
@@ -458,9 +458,9 @@
|
|||||||
"resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz",
|
||||||
"integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=",
|
"integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=",
|
||||||
"requires": {
|
"requires": {
|
||||||
"graceful-fs": "4.1.11",
|
"graceful-fs": "^4.1.2",
|
||||||
"pify": "2.3.0",
|
"pify": "^2.0.0",
|
||||||
"pinkie-promise": "2.0.1"
|
"pinkie-promise": "^2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"performance-now": {
|
"performance-now": {
|
||||||
@@ -483,7 +483,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz",
|
||||||
"integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=",
|
"integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=",
|
||||||
"requires": {
|
"requires": {
|
||||||
"pinkie": "2.0.4"
|
"pinkie": "^2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"psl": {
|
"psl": {
|
||||||
@@ -511,9 +511,9 @@
|
|||||||
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz",
|
||||||
"integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=",
|
"integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=",
|
||||||
"requires": {
|
"requires": {
|
||||||
"load-json-file": "1.1.0",
|
"load-json-file": "^1.0.0",
|
||||||
"normalize-package-data": "2.4.0",
|
"normalize-package-data": "^2.3.2",
|
||||||
"path-type": "1.1.0"
|
"path-type": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"read-pkg-up": {
|
"read-pkg-up": {
|
||||||
@@ -521,8 +521,8 @@
|
|||||||
"resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz",
|
||||||
"integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=",
|
"integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=",
|
||||||
"requires": {
|
"requires": {
|
||||||
"find-up": "1.1.2",
|
"find-up": "^1.0.0",
|
||||||
"read-pkg": "1.1.0"
|
"read-pkg": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"request": {
|
"request": {
|
||||||
@@ -530,26 +530,26 @@
|
|||||||
"resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz",
|
"resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz",
|
||||||
"integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==",
|
"integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"aws-sign2": "0.7.0",
|
"aws-sign2": "~0.7.0",
|
||||||
"aws4": "1.8.0",
|
"aws4": "^1.8.0",
|
||||||
"caseless": "0.12.0",
|
"caseless": "~0.12.0",
|
||||||
"combined-stream": "1.0.7",
|
"combined-stream": "~1.0.6",
|
||||||
"extend": "3.0.2",
|
"extend": "~3.0.2",
|
||||||
"forever-agent": "0.6.1",
|
"forever-agent": "~0.6.1",
|
||||||
"form-data": "2.3.3",
|
"form-data": "~2.3.2",
|
||||||
"har-validator": "5.1.0",
|
"har-validator": "~5.1.0",
|
||||||
"http-signature": "1.2.0",
|
"http-signature": "~1.2.0",
|
||||||
"is-typedarray": "1.0.0",
|
"is-typedarray": "~1.0.0",
|
||||||
"isstream": "0.1.2",
|
"isstream": "~0.1.2",
|
||||||
"json-stringify-safe": "5.0.1",
|
"json-stringify-safe": "~5.0.1",
|
||||||
"mime-types": "2.1.21",
|
"mime-types": "~2.1.19",
|
||||||
"oauth-sign": "0.9.0",
|
"oauth-sign": "~0.9.0",
|
||||||
"performance-now": "2.1.0",
|
"performance-now": "^2.1.0",
|
||||||
"qs": "6.5.2",
|
"qs": "~6.5.2",
|
||||||
"safe-buffer": "5.1.2",
|
"safe-buffer": "^5.1.2",
|
||||||
"tough-cookie": "2.4.3",
|
"tough-cookie": "~2.4.3",
|
||||||
"tunnel-agent": "0.6.0",
|
"tunnel-agent": "^0.6.0",
|
||||||
"uuid": "3.3.2"
|
"uuid": "^3.3.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"require-directory": {
|
"require-directory": {
|
||||||
@@ -587,8 +587,8 @@
|
|||||||
"resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.0.2.tgz",
|
||||||
"integrity": "sha512-q9hedtzyXHr5S0A1vEPoK/7l8NpfkFYTq6iCY+Pno2ZbdZR6WexZFtqeVGkGxW3TEJMN914Z55EnAGMmenlIQQ==",
|
"integrity": "sha512-q9hedtzyXHr5S0A1vEPoK/7l8NpfkFYTq6iCY+Pno2ZbdZR6WexZFtqeVGkGxW3TEJMN914Z55EnAGMmenlIQQ==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"spdx-expression-parse": "3.0.0",
|
"spdx-expression-parse": "^3.0.0",
|
||||||
"spdx-license-ids": "3.0.1"
|
"spdx-license-ids": "^3.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"spdx-exceptions": {
|
"spdx-exceptions": {
|
||||||
@@ -601,8 +601,8 @@
|
|||||||
"resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz",
|
||||||
"integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==",
|
"integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"spdx-exceptions": "2.2.0",
|
"spdx-exceptions": "^2.1.0",
|
||||||
"spdx-license-ids": "3.0.1"
|
"spdx-license-ids": "^3.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"spdx-license-ids": {
|
"spdx-license-ids": {
|
||||||
@@ -620,15 +620,15 @@
|
|||||||
"resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.15.1.tgz",
|
"resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.15.1.tgz",
|
||||||
"integrity": "sha512-mSdgNUaidk+dRU5MhYtN9zebdzF2iG0cNPWy8HG+W8y+fT1JnSkh0fzzpjOa0L7P8i1Rscz38t0h4gPcKz43xA==",
|
"integrity": "sha512-mSdgNUaidk+dRU5MhYtN9zebdzF2iG0cNPWy8HG+W8y+fT1JnSkh0fzzpjOa0L7P8i1Rscz38t0h4gPcKz43xA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"asn1": "0.2.4",
|
"asn1": "~0.2.3",
|
||||||
"assert-plus": "1.0.0",
|
"assert-plus": "^1.0.0",
|
||||||
"bcrypt-pbkdf": "1.0.2",
|
"bcrypt-pbkdf": "^1.0.0",
|
||||||
"dashdash": "1.14.1",
|
"dashdash": "^1.12.0",
|
||||||
"ecc-jsbn": "0.1.2",
|
"ecc-jsbn": "~0.1.1",
|
||||||
"getpass": "0.1.7",
|
"getpass": "^0.1.1",
|
||||||
"jsbn": "0.1.1",
|
"jsbn": "~0.1.0",
|
||||||
"safer-buffer": "2.1.2",
|
"safer-buffer": "^2.0.2",
|
||||||
"tweetnacl": "0.14.5"
|
"tweetnacl": "~0.14.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"stack-trace": {
|
"stack-trace": {
|
||||||
@@ -641,9 +641,9 @@
|
|||||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
|
||||||
"integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
|
"integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
|
||||||
"requires": {
|
"requires": {
|
||||||
"code-point-at": "1.1.0",
|
"code-point-at": "^1.0.0",
|
||||||
"is-fullwidth-code-point": "1.0.0",
|
"is-fullwidth-code-point": "^1.0.0",
|
||||||
"strip-ansi": "3.0.1"
|
"strip-ansi": "^3.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"strip-ansi": {
|
"strip-ansi": {
|
||||||
@@ -651,7 +651,7 @@
|
|||||||
"resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
|
"resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
|
||||||
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
|
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
|
||||||
"requires": {
|
"requires": {
|
||||||
"ansi-regex": "2.1.1"
|
"ansi-regex": "^2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"strip-bom": {
|
"strip-bom": {
|
||||||
@@ -659,7 +659,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz",
|
||||||
"integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=",
|
"integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=",
|
||||||
"requires": {
|
"requires": {
|
||||||
"is-utf8": "0.2.1"
|
"is-utf8": "^0.2.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"tough-cookie": {
|
"tough-cookie": {
|
||||||
@@ -667,8 +667,8 @@
|
|||||||
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz",
|
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz",
|
||||||
"integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==",
|
"integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"psl": "1.1.29",
|
"psl": "^1.1.24",
|
||||||
"punycode": "1.4.1"
|
"punycode": "^1.4.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"tunnel-agent": {
|
"tunnel-agent": {
|
||||||
@@ -676,7 +676,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
|
||||||
"integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
|
"integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
|
||||||
"requires": {
|
"requires": {
|
||||||
"safe-buffer": "5.1.2"
|
"safe-buffer": "^5.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"tweetnacl": {
|
"tweetnacl": {
|
||||||
@@ -694,8 +694,8 @@
|
|||||||
"resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
|
||||||
"integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
|
"integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"spdx-correct": "3.0.2",
|
"spdx-correct": "^3.0.0",
|
||||||
"spdx-expression-parse": "3.0.0"
|
"spdx-expression-parse": "^3.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"verror": {
|
"verror": {
|
||||||
@@ -703,9 +703,9 @@
|
|||||||
"resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
|
"resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
|
||||||
"integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=",
|
"integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=",
|
||||||
"requires": {
|
"requires": {
|
||||||
"assert-plus": "1.0.0",
|
"assert-plus": "^1.0.0",
|
||||||
"core-util-is": "1.0.2",
|
"core-util-is": "1.0.2",
|
||||||
"extsprintf": "1.3.0"
|
"extsprintf": "^1.2.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"which-module": {
|
"which-module": {
|
||||||
@@ -718,12 +718,12 @@
|
|||||||
"resolved": "https://registry.npmjs.org/winston/-/winston-2.4.4.tgz",
|
"resolved": "https://registry.npmjs.org/winston/-/winston-2.4.4.tgz",
|
||||||
"integrity": "sha512-NBo2Pepn4hK4V01UfcWcDlmiVTs7VTB1h7bgnB0rgP146bYhMxX0ypCz3lBOfNxCO4Zuek7yeT+y/zM1OfMw4Q==",
|
"integrity": "sha512-NBo2Pepn4hK4V01UfcWcDlmiVTs7VTB1h7bgnB0rgP146bYhMxX0ypCz3lBOfNxCO4Zuek7yeT+y/zM1OfMw4Q==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"async": "1.0.0",
|
"async": "~1.0.0",
|
||||||
"colors": "1.0.3",
|
"colors": "1.0.x",
|
||||||
"cycle": "1.0.3",
|
"cycle": "1.0.x",
|
||||||
"eyes": "0.1.8",
|
"eyes": "0.1.x",
|
||||||
"isstream": "0.1.2",
|
"isstream": "0.1.x",
|
||||||
"stack-trace": "0.0.10"
|
"stack-trace": "0.0.x"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"async": {
|
"async": {
|
||||||
@@ -743,8 +743,8 @@
|
|||||||
"resolved": "http://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz",
|
"resolved": "http://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz",
|
||||||
"integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=",
|
"integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=",
|
||||||
"requires": {
|
"requires": {
|
||||||
"string-width": "1.0.2",
|
"string-width": "^1.0.1",
|
||||||
"strip-ansi": "3.0.1"
|
"strip-ansi": "^3.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"y18n": {
|
"y18n": {
|
||||||
@@ -757,19 +757,19 @@
|
|||||||
"resolved": "http://registry.npmjs.org/yargs/-/yargs-6.6.0.tgz",
|
"resolved": "http://registry.npmjs.org/yargs/-/yargs-6.6.0.tgz",
|
||||||
"integrity": "sha1-eC7CHvQDNF+DCoCMo9UTr1YGUgg=",
|
"integrity": "sha1-eC7CHvQDNF+DCoCMo9UTr1YGUgg=",
|
||||||
"requires": {
|
"requires": {
|
||||||
"camelcase": "3.0.0",
|
"camelcase": "^3.0.0",
|
||||||
"cliui": "3.2.0",
|
"cliui": "^3.2.0",
|
||||||
"decamelize": "1.2.0",
|
"decamelize": "^1.1.1",
|
||||||
"get-caller-file": "1.0.3",
|
"get-caller-file": "^1.0.1",
|
||||||
"os-locale": "1.4.0",
|
"os-locale": "^1.4.0",
|
||||||
"read-pkg-up": "1.0.1",
|
"read-pkg-up": "^1.0.1",
|
||||||
"require-directory": "2.1.1",
|
"require-directory": "^2.1.1",
|
||||||
"require-main-filename": "1.0.1",
|
"require-main-filename": "^1.0.1",
|
||||||
"set-blocking": "2.0.0",
|
"set-blocking": "^2.0.0",
|
||||||
"string-width": "1.0.2",
|
"string-width": "^1.0.2",
|
||||||
"which-module": "1.0.0",
|
"which-module": "^1.0.0",
|
||||||
"y18n": "3.2.1",
|
"y18n": "^3.2.1",
|
||||||
"yargs-parser": "4.2.1"
|
"yargs-parser": "^4.2.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"yargs-parser": {
|
"yargs-parser": {
|
||||||
@@ -777,7 +777,7 @@
|
|||||||
"resolved": "http://registry.npmjs.org/yargs-parser/-/yargs-parser-4.2.1.tgz",
|
"resolved": "http://registry.npmjs.org/yargs-parser/-/yargs-parser-4.2.1.tgz",
|
||||||
"integrity": "sha1-KczqwNxPA8bIe0qfIX3RjJ90hxw=",
|
"integrity": "sha1-KczqwNxPA8bIe0qfIX3RjJ90hxw=",
|
||||||
"requires": {
|
"requires": {
|
||||||
"camelcase": "3.0.0"
|
"camelcase": "^3.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
93
server/tests/js_tests/tests/testEdgeCases.js
Normal file
93
server/tests/js_tests/tests/testEdgeCases.js
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
var dial = require("../libs/dialClient.js"),
|
||||||
|
utils = require("../libs/utils.js"),
|
||||||
|
Q = require("q"),
|
||||||
|
winston = require("winston");
|
||||||
|
|
||||||
|
|
||||||
|
const argv = require("yargs")
|
||||||
|
.usage("\nUsage: node " + __filename.slice(__dirname.length + 1) + "[options]")
|
||||||
|
.option("host", {
|
||||||
|
describe: "IP address of host on which DIAL server under test is running",
|
||||||
|
type: "string",
|
||||||
|
demand: true
|
||||||
|
})
|
||||||
|
.help("help").alias("help", "h").argv;
|
||||||
|
|
||||||
|
var testCount = 0;
|
||||||
|
var failureCount = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* These edge cases are
|
||||||
|
*/
|
||||||
|
function edgeCases() {
|
||||||
|
var host = argv.host;
|
||||||
|
|
||||||
|
var methods = [
|
||||||
|
"GET",
|
||||||
|
"POST",
|
||||||
|
];
|
||||||
|
|
||||||
|
// weird data
|
||||||
|
var headers = [
|
||||||
|
{"Content-Length": "0xff1af1581"},
|
||||||
|
{"Content-Length": "sfsadfdsat"},
|
||||||
|
{"Content-Length": 13377777777777},
|
||||||
|
{"Content-Length": -13377777777777},
|
||||||
|
{"Content-Type": "text/plain;charset=\"utf-8\""},
|
||||||
|
{"Content-Type": "text/plain;charset=\"ascii\""},
|
||||||
|
{"Content-Type": "asdoffmoaserq"},
|
||||||
|
{"Foo": "Bar"},
|
||||||
|
];
|
||||||
|
|
||||||
|
var utf8 = ";(QmN̸鱏=叽]𰸹h˳╭ܮ煣ِt𐍁J픖pЗB-ٴۼ퍃%۞灱Ҡɝ~2望Lԇƀݛ&±⫯WܾNJX礴ēʧ耉ݭpi}Ǖʝ'䬿^ٿ]샑좘쒾"
|
||||||
|
+ "ԝbւ蹈ۥ웦(歉貤䃶6ƢӋØ𪺭ĩk┋譒ց,Ŋ칱آ3㛣Oܕ=裁fĴjٕpۅ=@ԉ'1⃮Ȩ핣ƺ匽ݚ뎔叇χ&죙𱘱ʴذȞ鞾ﰟO썟X9⛥ȧ走쾙"
|
||||||
|
+ "ӱإ#!۵Σƀ4䦼쪭ϖ0ڧp梛՚嬆߷域᳁١ޜۡÜ]ʘ厤ȆwYݖ(ן饲a<61>`V<$Dz&ܗʧbҘ@E1ч&{㜃팸儈Ā㖿rb^Ӟʶ"
|
||||||
|
+ "Ө㡷sΠ●ŽٗDҤƱ(t-瞜BЏ;Ғeࡻs`=ݔu樭迕^;䐈Ǥ3䝡XM붩ݱϖȓܕ췃`Ỷ3LV9폧赹ⴢ+⇩ӛ뵖띟䎵钥¶繥Ҕ J礢м2_8̑hg";
|
||||||
|
utf8 = utf8 + utf8 + utf8;
|
||||||
|
var strings = [
|
||||||
|
utf8,
|
||||||
|
"",
|
||||||
|
'<a href="\x19javascript:javascript:alert(1)" id="fuzzelement1">test</a>\n',
|
||||||
|
'$HOME',
|
||||||
|
'../../../../../../../../../../../etc/hosts\n',
|
||||||
|
];
|
||||||
|
|
||||||
|
// generate test cases
|
||||||
|
var testCases = [];
|
||||||
|
for (var m = 0; m < methods.length; m++) {
|
||||||
|
for (var h = 0; h < headers.length; h++) {
|
||||||
|
for (var q = 0; q < strings.length; q++) {
|
||||||
|
for (var b = 0; b < strings.length; b++) {
|
||||||
|
testCases.push(function (m, h, q, b) {
|
||||||
|
return new Q()
|
||||||
|
.then(dial.sendRequest.bind(null, host, methods[m], headers[h], strings[q], strings[b]))
|
||||||
|
.then(function (response) {
|
||||||
|
testCount = testCount + 1;
|
||||||
|
if (![400, 404, 500].includes(response.statusCode)) {
|
||||||
|
return Q.reject(new Error("Sent the DIAL server an edge case. Expected a bad status code but got " + response.statusCode));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.fail(function handleError(err) {
|
||||||
|
utils.printTestInfo(__filename.slice(__dirname.length + 1),
|
||||||
|
"Edge Case, method: " + methods[m] + " header: "
|
||||||
|
+ headers[h] + " query string: "
|
||||||
|
+ strings[q] + " body: " + strings[b]);
|
||||||
|
utils.printTestFailure(err);
|
||||||
|
failureCount = failureCount + 1;
|
||||||
|
return err;
|
||||||
|
})
|
||||||
|
}.bind(null, m, h, q, b));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return testCases;
|
||||||
|
}
|
||||||
|
|
||||||
|
winston.info("Testing edge cases, only failing tests will appear.");
|
||||||
|
return edgeCases().reduce(Q.when, Promise.resolve()).done(function() {
|
||||||
|
winston.info("Tests complete. Passing: " + (testCount - failureCount) + ", Failures: " + failureCount);
|
||||||
|
});
|
||||||
@@ -78,6 +78,4 @@ Promise.resolve()
|
|||||||
// Application hide tests
|
// Application hide tests
|
||||||
.then(hideInvalidApplicationInstance.test)
|
.then(hideInvalidApplicationInstance.test)
|
||||||
.then(hideApplicationInHiddenState.test)
|
.then(hideApplicationInHiddenState.test)
|
||||||
.then(hideApplicationInRunningState.test)
|
.then(hideApplicationInRunningState.test);
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ CC=$(TARGET)gcc
|
|||||||
.PHONY: clean
|
.PHONY: clean
|
||||||
.DEFAULT_GOAL=test
|
.DEFAULT_GOAL=test
|
||||||
|
|
||||||
OBJS := test_dial_data.o test_url_lib.o ../url_lib.o ../dial_data.o run_tests.o
|
OBJS := test_dial_data.o test_url_lib.o test_callbacks.o ../url_lib.o ../dial_data.o ../system_callbacks.o run_tests.o
|
||||||
HEADERS := $(wildcard ../*.h)
|
HEADERS := $(wildcard ../*.h)
|
||||||
|
|
||||||
%.c: $(HEADERS)
|
%.c: $(HEADERS)
|
||||||
@@ -12,7 +12,7 @@ HEADERS := $(wildcard ../*.h)
|
|||||||
$(CC) -Wall -Werror -g -std=gnu99 $(CFLAGS) -c $*.c -o $*.o
|
$(CC) -Wall -Werror -g -std=gnu99 $(CFLAGS) -c $*.c -o $*.o
|
||||||
|
|
||||||
test: $(OBJS)
|
test: $(OBJS)
|
||||||
$(CC) -Wall -Werror -g $(OBJS) -ldl -lpthread -o run_tests
|
$(CC) -Wall -Werror -fsanitize=address -g $(OBJS) -ldl -lpthread -o run_tests
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f *.o run_tests
|
rm -f *.o run_tests
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
#include "test_callbacks.h"
|
||||||
#include "test_dial_data.h"
|
#include "test_dial_data.h"
|
||||||
#include "test_url_lib.h"
|
#include "test_url_lib.h"
|
||||||
|
|
||||||
@@ -29,14 +30,8 @@
|
|||||||
|
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
int main(int argc, char** argv) {
|
||||||
printf("====\n");
|
test_dial_data_suite();
|
||||||
test_smartstrcat();
|
test_url_lib_suite();
|
||||||
test_urldecode();
|
test_callbacks_suite();
|
||||||
test_parse_app_name();
|
return 0;
|
||||||
test_parse_params();
|
|
||||||
printf("====\n");
|
|
||||||
test_read_dial_data();
|
|
||||||
test_write_dial_data();
|
|
||||||
printf("====\n");
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,9 +29,20 @@
|
|||||||
|
|
||||||
#define EXPECT(a, m) \
|
#define EXPECT(a, m) \
|
||||||
do { \
|
do { \
|
||||||
if (!a) { \
|
if (!(a)) { \
|
||||||
printf("[%s] failed: %s\n", #a, m); \
|
printf("\033[31m [%s] failed: %s \033[0m\n", #a, m); \
|
||||||
printf("%s -> FAILED\n", __func__); \
|
printf("\033[31m %s -> FAILED \033[0m\n", __func__); \
|
||||||
|
return; \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define EXPECT_EQ(a, b) \
|
||||||
|
do { \
|
||||||
|
if (a != b) { \
|
||||||
|
printf("\033[31m expected [%s == %s] \033[0m\n", #a, #b); \
|
||||||
|
printf("\033[31m a = \"%ld\" \033[0m\n", (unsigned long) a); \
|
||||||
|
printf("\033[31m b = \"%ld\" \033[0m\n", (unsigned long) b); \
|
||||||
|
printf("\033[31m %s -> FAILED \033[0m\n", __func__); \
|
||||||
return; \
|
return; \
|
||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
@@ -39,15 +50,18 @@
|
|||||||
#define EXPECT_STREQ(a, b) \
|
#define EXPECT_STREQ(a, b) \
|
||||||
do { \
|
do { \
|
||||||
if (strcmp(a, b)) { \
|
if (strcmp(a, b)) { \
|
||||||
printf("expected [%s == %s]\n", #a, #b); \
|
printf("\033[31m expected [%s == %s] \033[0m\n", #a, #b); \
|
||||||
printf(" a = \"%s\"\n", a); \
|
printf("\033[31m a = \"%s\" \033[0m\n", a); \
|
||||||
printf(" b = \"%s\"\n", b); \
|
printf("\033[31m b = \"%s\" \033[0m\n", b); \
|
||||||
printf("%s -> FAILED\n", __func__); \
|
printf("\033[31m %s -> FAILED \033[0m\n", __func__); \
|
||||||
return; \
|
return; \
|
||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
|
#define START_SUITE() \
|
||||||
|
printf("== %s ==\n", __FILE__)
|
||||||
|
|
||||||
#define DONE() \
|
#define DONE() \
|
||||||
printf("%s -> OK\n", __func__)
|
printf("\033[32m %s -> OK \033[0m\n", __func__)
|
||||||
|
|
||||||
#endif /* SRC_SERVER_TESTS_TEST_H_ */
|
#endif /* SRC_SERVER_TESTS_TEST_H_ */
|
||||||
|
|||||||
52
server/tests/test_callbacks.c
Normal file
52
server/tests/test_callbacks.c
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2019 Netflix, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* Redistributions of source code must retain the above copyright notice, this
|
||||||
|
* list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY NETFLIX, INC. AND CONTRIBUTORS "AS IS" AND ANY
|
||||||
|
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL NETFLIX OR CONTRIBUTORS BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||||
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
#include "../system_callbacks.h"
|
||||||
|
#include "../dial_data.h"
|
||||||
|
|
||||||
|
#include "test_callbacks.h"
|
||||||
|
#include "test.h"
|
||||||
|
|
||||||
|
char spSleepPassword[256];
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The reference does not provide implementations for interfacing with the System Application.
|
||||||
|
*/
|
||||||
|
void test_system_callbacks() {
|
||||||
|
EXPECT_EQ(system_start(NULL, NULL, NULL, "action=sleep", NULL, NULL, NULL), kDIALStatusErrorNotImplemented);
|
||||||
|
EXPECT_EQ(system_hide(NULL, NULL, NULL, NULL), kDIALStatusHide);
|
||||||
|
EXPECT_EQ(system_status(NULL, NULL, NULL, NULL, NULL), kDIALStatusHide);
|
||||||
|
|
||||||
|
DONE();
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_callbacks_suite() {
|
||||||
|
START_SUITE();
|
||||||
|
test_system_callbacks();
|
||||||
|
}
|
||||||
30
server/tests/test_callbacks.h
Normal file
30
server/tests/test_callbacks.h
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2019 Netflix, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* Redistributions of source code must retain the above copyright notice, this
|
||||||
|
* list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY NETFLIX, INC. AND CONTRIBUTORS "AS IS" AND ANY
|
||||||
|
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL NETFLIX OR CONTRIBUTORS BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||||
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
#ifndef SRC_SERVER_TESTS_TEST_CALLBACKS_H_
|
||||||
|
#define SRC_SERVER_TESTS_TEST_CALLBACKS_H_
|
||||||
|
|
||||||
|
void test_callbacks_suite();
|
||||||
|
|
||||||
|
#endif /* SRC_SERVER_TESTS_TEST_CALLBACKS_H_ */
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2014 Netflix, Inc.
|
* Copyright (c) 2014-2019 Netflix, Inc.
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
@@ -30,17 +30,19 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
#include "test.h"
|
#include "test.h"
|
||||||
|
#include "test_dial_data.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* All tests should malloc
|
||||||
|
*/
|
||||||
|
|
||||||
int key_value_pairs = 3;
|
int key_value_pairs = 3;
|
||||||
char *keys[] = {"key1", "key2", "key3"};
|
char *keys[] = {"key3", "key2", "key3"};
|
||||||
char *values[] = {"value1", "value2", "value3"};
|
char *values[] = {"value1", "value2", "value3"};
|
||||||
|
|
||||||
void test_read_dial_data() {
|
void test_read_dial_missing_data() {
|
||||||
DIALData *data = retrieve_dial_data("dial_data");
|
DIALData *data = retrieve_dial_data("NON_EXISTENT_DATA");
|
||||||
for (int i = 0; data != NULL; data = data->next, i++) {
|
EXPECT(NULL == data, "retrieve_dial_data() returns NULL\n");
|
||||||
EXPECT_STREQ(data->key, keys[2 - i]);
|
|
||||||
EXPECT_STREQ(data->value, values[2 - i]);
|
|
||||||
}
|
|
||||||
DONE();
|
DONE();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,19 +50,76 @@ void test_write_dial_data() {
|
|||||||
DIALData *result = NULL;
|
DIALData *result = NULL;
|
||||||
for (int i = 0; i < key_value_pairs; ++i) {
|
for (int i = 0; i < key_value_pairs; ++i) {
|
||||||
DIALData *node = (DIALData *) malloc(sizeof(DIALData));
|
DIALData *node = (DIALData *) malloc(sizeof(DIALData));
|
||||||
node->key = keys[i];
|
node->key = malloc(strlen(keys[i]) + 1);
|
||||||
node->value = values[i];
|
node->value = malloc(strlen(values[i]) + 1);
|
||||||
|
strcpy(node->key, keys[i]);
|
||||||
|
strcpy(node->value, values[i]);
|
||||||
|
|
||||||
node->next = result;
|
node->next = result;
|
||||||
result = node;
|
result = node;
|
||||||
}
|
}
|
||||||
store_dial_data("YouTube", result);
|
store_dial_data("YouTube", result);
|
||||||
|
|
||||||
DIALData *readBack = retrieve_dial_data("YouTube");
|
DIALData *readBack = retrieve_dial_data("YouTube");
|
||||||
|
DIALData *datum = readBack;
|
||||||
for (int i = 0; readBack != NULL; readBack = readBack->next, i++) {
|
for (int i = 0; datum != NULL; datum = datum->next, i++) {
|
||||||
EXPECT_STREQ(readBack->key, keys[i]);
|
EXPECT_STREQ(datum->key, keys[i]);
|
||||||
EXPECT_STREQ(readBack->value, values[i]);
|
EXPECT_STREQ(datum->value, values[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
free_dial_data(&result);
|
||||||
|
free_dial_data(&readBack);
|
||||||
|
|
||||||
DONE();
|
DONE();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void test_write_kv_larger_than_max_len() {
|
||||||
|
// result contains k & v both larger than DIAL_KEY_OR_VALUE_MAX_LEN
|
||||||
|
DIALData *result = (DIALData *) calloc(1, sizeof(DIALData));
|
||||||
|
result->key = calloc(DIAL_KEY_OR_VALUE_MAX_LEN * 2, sizeof(char));
|
||||||
|
result->value = calloc(DIAL_KEY_OR_VALUE_MAX_LEN * 2, sizeof(char));
|
||||||
|
memset(result->key, 'k', DIAL_KEY_OR_VALUE_MAX_LEN * 2 - 1);
|
||||||
|
memset(result->value, 'v', DIAL_KEY_OR_VALUE_MAX_LEN * 2 - 1);
|
||||||
|
|
||||||
|
store_dial_data("YouTube", result);
|
||||||
|
|
||||||
|
DIALData *readBack = retrieve_dial_data("YouTube");
|
||||||
|
EXPECT(NULL != readBack, "retrieve_dial_data should not be NULL\n");
|
||||||
|
|
||||||
|
EXPECT_EQ(readBack->key[0], 'k');
|
||||||
|
EXPECT_EQ(readBack->value[0], 'v');
|
||||||
|
EXPECT_EQ(strlen(readBack->key), DIAL_KEY_OR_VALUE_MAX_LEN);
|
||||||
|
EXPECT_EQ(strlen(readBack->value), DIAL_KEY_OR_VALUE_MAX_LEN);
|
||||||
|
|
||||||
|
free_dial_data(&result);
|
||||||
|
free_dial_data(&readBack);
|
||||||
|
|
||||||
|
DONE();
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_write_empty_kv() {
|
||||||
|
DIALData *result = (DIALData *) calloc(1, sizeof(DIALData));
|
||||||
|
result->key = calloc(1, sizeof(char));
|
||||||
|
result->value = calloc(1, sizeof(char));
|
||||||
|
|
||||||
|
store_dial_data("YouTube", result);
|
||||||
|
|
||||||
|
DIALData *readBack = retrieve_dial_data("YouTube");
|
||||||
|
EXPECT(NULL != readBack, "retrieve_dial_data should not be NULL\n");
|
||||||
|
|
||||||
|
EXPECT_EQ(readBack->key[0], '\0');
|
||||||
|
EXPECT_EQ(readBack->value[0], '\0');
|
||||||
|
|
||||||
|
free_dial_data(&result);
|
||||||
|
free_dial_data(&readBack);
|
||||||
|
|
||||||
|
DONE();
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_dial_data_suite() {
|
||||||
|
START_SUITE();
|
||||||
|
|
||||||
|
test_read_dial_missing_data();
|
||||||
|
test_write_dial_data();
|
||||||
|
test_write_kv_larger_than_max_len();
|
||||||
|
}
|
||||||
@@ -25,7 +25,6 @@
|
|||||||
#ifndef SRC_SERVER_TESTS_TEST_DIAL_DATA_H_
|
#ifndef SRC_SERVER_TESTS_TEST_DIAL_DATA_H_
|
||||||
#define SRC_SERVER_TESTS_TEST_DIAL_DATA_H_
|
#define SRC_SERVER_TESTS_TEST_DIAL_DATA_H_
|
||||||
|
|
||||||
void test_read_dial_data();
|
void test_dial_data_suite();
|
||||||
void test_write_dial_data();
|
|
||||||
|
|
||||||
#endif /* SRC_SERVER_TESTS_TEST_DIAL_DATA_H_ */
|
#endif /* SRC_SERVER_TESTS_TEST_DIAL_DATA_H_ */
|
||||||
|
|||||||
@@ -26,25 +26,26 @@
|
|||||||
#include "../dial_data.h"
|
#include "../dial_data.h"
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include "test.h"
|
#include "test.h"
|
||||||
|
|
||||||
void test_smartstrcat() {
|
void test_smartstrncpy() {
|
||||||
char* src1 = "Hello ";
|
char* src1 = "Hello ";
|
||||||
char* src2 = "world!";
|
char* src2 = "world!";
|
||||||
char* src3 = " Trunc ated";
|
char* src3 = " Trunc ated";
|
||||||
char dest[128] = {0, };
|
char dest[128] = {0, };
|
||||||
|
|
||||||
char* p = (char *) dest;
|
char* p = (char *) dest;
|
||||||
p = smartstrcat(p, src1, 128);
|
p = smartstrncpy(p, src1, 128);
|
||||||
EXPECT_STREQ(dest, "Hello ");
|
EXPECT_STREQ(dest, "Hello ");
|
||||||
p = smartstrcat(p, src2, dest + 128 - p);
|
p = smartstrncpy(p, src2, dest + 128 - p);
|
||||||
|
|
||||||
EXPECT_STREQ(dest, "Hello world!");
|
EXPECT_STREQ(dest, "Hello world!");
|
||||||
|
|
||||||
p = smartstrcat(p, src3, 6);
|
p = smartstrncpy(p, src3, 6);
|
||||||
EXPECT_STREQ(dest, "Hello world! Trunc");
|
EXPECT_STREQ(dest, "Hello world! Trunc");
|
||||||
|
|
||||||
DONE();
|
DONE();
|
||||||
@@ -63,19 +64,24 @@ void test_parse_app_name() {
|
|||||||
char *app_name;
|
char *app_name;
|
||||||
EXPECT((app_name = parse_app_name(NULL)), "Failed to extract app_name");
|
EXPECT((app_name = parse_app_name(NULL)), "Failed to extract app_name");
|
||||||
EXPECT_STREQ(app_name, "unknown");
|
EXPECT_STREQ(app_name, "unknown");
|
||||||
|
free(app_name);
|
||||||
EXPECT((app_name = parse_app_name("")), "Failed to extract app_name");
|
EXPECT((app_name = parse_app_name("")), "Failed to extract app_name");
|
||||||
EXPECT_STREQ(app_name, "unknown");
|
EXPECT_STREQ(app_name, "unknown");
|
||||||
|
free(app_name);
|
||||||
EXPECT((app_name = parse_app_name("/")), "Failed to extract app_name");
|
EXPECT((app_name = parse_app_name("/")), "Failed to extract app_name");
|
||||||
EXPECT_STREQ(app_name, "unknown");
|
EXPECT_STREQ(app_name, "unknown");
|
||||||
|
free(app_name);
|
||||||
EXPECT((app_name = parse_app_name("/apps/YouTube/DialData")),
|
EXPECT((app_name = parse_app_name("/apps/YouTube/DialData")),
|
||||||
"Failed to extract app_name");
|
"Failed to extract app_name");
|
||||||
EXPECT_STREQ(app_name, "YouTube");
|
EXPECT_STREQ(app_name, "YouTube");
|
||||||
|
free(app_name);
|
||||||
EXPECT((app_name = parse_app_name("//")), "Failed to extract app_name");
|
EXPECT((app_name = parse_app_name("//")), "Failed to extract app_name");
|
||||||
EXPECT_STREQ(app_name, "");
|
EXPECT_STREQ(app_name, "");
|
||||||
|
free(app_name);
|
||||||
EXPECT((app_name = parse_app_name("/invalid")),
|
EXPECT((app_name = parse_app_name("/invalid")),
|
||||||
"Failed to extract app_name");
|
"Failed to extract app_name");
|
||||||
EXPECT_STREQ(app_name, "unknown");
|
EXPECT_STREQ(app_name, "unknown");
|
||||||
|
free(app_name);
|
||||||
DONE();
|
DONE();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,28 +92,52 @@ void test_parse_params() {
|
|||||||
DIALData *result = parse_params("a=b");
|
DIALData *result = parse_params("a=b");
|
||||||
EXPECT_STREQ(result->key, "a");
|
EXPECT_STREQ(result->key, "a");
|
||||||
EXPECT_STREQ(result->value, "b");
|
EXPECT_STREQ(result->value, "b");
|
||||||
|
free_dial_data(&result);
|
||||||
|
|
||||||
result = parse_params("?a=b");
|
result = parse_params("?a=b");
|
||||||
EXPECT_STREQ(result->key, "a");
|
EXPECT_STREQ(result->key, "a");
|
||||||
EXPECT_STREQ(result->value, "b");
|
EXPECT_STREQ(result->value, "b");
|
||||||
|
free_dial_data(&result);
|
||||||
|
|
||||||
result = parse_params("?a=b&c=d");
|
result = parse_params("?a=b&c=d");
|
||||||
EXPECT_STREQ(result->key, "c");
|
EXPECT_STREQ(result->key, "c");
|
||||||
EXPECT_STREQ(result->value, "d");
|
EXPECT_STREQ(result->value, "d");
|
||||||
EXPECT_STREQ(result->next->key, "a");
|
EXPECT_STREQ(result->next->key, "a");
|
||||||
EXPECT_STREQ(result->next->value, "b");
|
EXPECT_STREQ(result->next->value, "b");
|
||||||
|
free_dial_data(&result);
|
||||||
|
|
||||||
|
result = parse_params("ሳ=€");
|
||||||
|
EXPECT_STREQ(result->key, "ሳ");
|
||||||
|
EXPECT_STREQ(result->value, "€");
|
||||||
|
free_dial_data(&result);
|
||||||
|
|
||||||
char query_string[1024] = {0, };
|
char query_string[1024] = {0, };
|
||||||
char *current = query_string;
|
char *current = query_string;
|
||||||
for (int i = 0; i < 25; ++i) {
|
for (int i = 0; i < 25; ++i) {
|
||||||
current = smartstrcat(current, "a=b&", 256);
|
current = smartstrncpy(current, "a=b&", 256);
|
||||||
}
|
}
|
||||||
result = parse_params(query_string);
|
result = parse_params(query_string);
|
||||||
int length = 0;
|
int length = 0;
|
||||||
for (; result != NULL; result = result->next) {
|
for (DIALData *current = result; current != NULL; current = current->next) {
|
||||||
length++;
|
length++;
|
||||||
}
|
}
|
||||||
EXPECT((length == 25), "25 params should have been parsed");
|
EXPECT((length == 25), "25 params should have been parsed");
|
||||||
|
free_dial_data(&result);
|
||||||
|
|
||||||
DONE();
|
DONE();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void test_parse_params_malformatted() {
|
||||||
|
EXPECT(NULL == parse_params("abcdefghijkl"), "no params expected");
|
||||||
|
EXPECT(NULL == parse_params("\u2639"), "no params expected");
|
||||||
|
DONE();
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_url_lib_suite() {
|
||||||
|
START_SUITE();
|
||||||
|
test_smartstrncpy();
|
||||||
|
test_urldecode();
|
||||||
|
test_parse_app_name();
|
||||||
|
test_parse_params();
|
||||||
|
test_parse_params_malformatted();
|
||||||
|
}
|
||||||
@@ -25,10 +25,6 @@
|
|||||||
#ifndef SRC_SERVER_TESTS_TEST_URL_LIB_H_
|
#ifndef SRC_SERVER_TESTS_TEST_URL_LIB_H_
|
||||||
#define SRC_SERVER_TESTS_TEST_URL_LIB_H_
|
#define SRC_SERVER_TESTS_TEST_URL_LIB_H_
|
||||||
|
|
||||||
void test_smartstrcat();
|
void test_url_lib_suite();
|
||||||
void test_urldecode();
|
|
||||||
void test_parse_app_name();
|
|
||||||
void test_parse_param();
|
|
||||||
void test_parse_params();
|
|
||||||
|
|
||||||
#endif /* SRC_SERVER_TESTS_TEST_URL_LIB_H_ */
|
#endif /* SRC_SERVER_TESTS_TEST_URL_LIB_H_ */
|
||||||
|
|||||||
107
server/url_lib.c
107
server/url_lib.c
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2014 Netflix, Inc.
|
* Copyright (c) 2014-2019 Netflix, Inc.
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
@@ -30,15 +30,29 @@
|
|||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
char* smartstrcat(char* dest, char* src, size_t max_chars) {
|
static const char * unknown_str = "unknown";
|
||||||
size_t copied = 0;
|
|
||||||
while ((*(dest++) = *(src++)) && copied++ < max_chars) {
|
char* smartstrncpy(char* dest, char* src, size_t max_chars) {
|
||||||
|
size_t copied;
|
||||||
|
for (copied = 0; copied < max_chars; copied++, dest++, src++) {
|
||||||
|
*dest = *src;
|
||||||
|
if (*dest == 0)
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
// In case we over-stepped the size, end the string nicely.
|
*dest = 0;
|
||||||
*(--dest) = '\0';
|
|
||||||
return dest;
|
return dest;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a two-character hex representation into its ASCII equivalent
|
||||||
|
* and copy it into dest.
|
||||||
|
*
|
||||||
|
* @param dest the destination character buffer.
|
||||||
|
* @param a the first hex character.
|
||||||
|
* @param b the second hex character.
|
||||||
|
* @return 1 if the character was converted and copied, 0 if the hex
|
||||||
|
* characters are invalid.
|
||||||
|
*/
|
||||||
static int append_char_from_hex(char* dest, char a, char b) {
|
static int append_char_from_hex(char* dest, char a, char b) {
|
||||||
if ('a' <= a && a <= 'f')
|
if ('a' <= a && a <= 'f')
|
||||||
a = 10 + a - 'a';
|
a = 10 + a - 'a';
|
||||||
@@ -89,23 +103,33 @@ void xmlencode(char *dst, const char *src, size_t max_size) {
|
|||||||
while (*src && current_size < max_size) {
|
while (*src && current_size < max_size) {
|
||||||
switch (*src) {
|
switch (*src) {
|
||||||
case '&':
|
case '&':
|
||||||
dst = smartstrcat(dst, "&", max_size - current_size);
|
if (current_size + 5 >= max_size)
|
||||||
|
break;
|
||||||
|
dst = smartstrncpy(dst, "&", max_size - current_size);
|
||||||
current_size += 5;
|
current_size += 5;
|
||||||
break;
|
break;
|
||||||
case '\"':
|
case '\"':
|
||||||
dst = smartstrcat(dst, """, max_size - current_size);
|
if (current_size + 6 >= max_size)
|
||||||
|
break;
|
||||||
|
dst = smartstrncpy(dst, """, max_size - current_size);
|
||||||
current_size += 6;
|
current_size += 6;
|
||||||
break;
|
break;
|
||||||
case '\'':
|
case '\'':
|
||||||
dst = smartstrcat(dst, "'", max_size - current_size);
|
if (current_size + 6 >= max_size)
|
||||||
|
break;
|
||||||
|
dst = smartstrncpy(dst, "'", max_size - current_size);
|
||||||
current_size += 6;
|
current_size += 6;
|
||||||
break;
|
break;
|
||||||
case '<':
|
case '<':
|
||||||
dst = smartstrcat(dst, "<", max_size - current_size);
|
if (current_size + 4 >= max_size)
|
||||||
|
break;
|
||||||
|
dst = smartstrncpy(dst, "<", max_size - current_size);
|
||||||
current_size += 4;
|
current_size += 4;
|
||||||
break;
|
break;
|
||||||
case '>':
|
case '>':
|
||||||
dst = smartstrcat(dst, ">", max_size - current_size);
|
if (current_size + 4 >= max_size)
|
||||||
|
break;
|
||||||
|
dst = smartstrncpy(dst, ">", max_size - current_size);
|
||||||
current_size += 4;
|
current_size += 4;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@@ -119,18 +143,34 @@ void xmlencode(char *dst, const char *src, size_t max_size) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
char *parse_app_name(const char *uri) {
|
char *parse_app_name(const char *uri) {
|
||||||
|
char *unknown = NULL;
|
||||||
if (uri == NULL) {
|
if (uri == NULL) {
|
||||||
return "unknown";
|
unknown = (char*)calloc(strlen(unknown_str) + 1, sizeof(char));
|
||||||
|
if (unknown == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
strncpy(unknown, unknown_str, strlen(unknown_str) + 1);
|
||||||
|
return unknown;
|
||||||
}
|
}
|
||||||
char *slash = strrchr(uri, '/');
|
char *slash = strrchr(uri, '/');
|
||||||
if (slash == NULL || slash == uri) {
|
if (slash == NULL || slash == uri) {
|
||||||
return "unknown";
|
unknown = (char*)calloc(strlen(unknown_str) + 1, sizeof(char));
|
||||||
|
if (unknown == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
strncpy(unknown, unknown_str, strlen(unknown_str) + 1);
|
||||||
|
return unknown;
|
||||||
}
|
}
|
||||||
char *begin = slash;
|
char *begin = slash;
|
||||||
while ((begin != uri) && (*--begin != '/'))
|
while ((begin != uri) && (*--begin != '/'))
|
||||||
;
|
;
|
||||||
begin++; // skip the slash
|
if (*begin == '/') {
|
||||||
char *result = (char *) calloc(1, slash - begin+1);
|
begin++; // skip the slash
|
||||||
|
}
|
||||||
|
char *result = (char *) calloc(slash - begin+1, sizeof(char));
|
||||||
|
if (result == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
strncpy(result, begin, slash - begin);
|
strncpy(result, begin, slash - begin);
|
||||||
result[slash-begin]='\0';
|
result[slash-begin]='\0';
|
||||||
return result;
|
return result;
|
||||||
@@ -151,6 +191,9 @@ char *parse_param(char *query_string, char *param_name) {
|
|||||||
end++;
|
end++;
|
||||||
int result_size = end - start;
|
int result_size = end - start;
|
||||||
char *result = malloc(result_size + 1);
|
char *result = malloc(result_size + 1);
|
||||||
|
if (result == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
result[0] = '\0';
|
result[0] = '\0';
|
||||||
strncpy(result, start, result_size);
|
strncpy(result, start, result_size);
|
||||||
result[result_size] = '\0';
|
result[result_size] = '\0';
|
||||||
@@ -165,20 +208,46 @@ DIALData *parse_params(char * query_string) {
|
|||||||
query_string++; // skip leading question mark
|
query_string++; // skip leading question mark
|
||||||
}
|
}
|
||||||
DIALData *result = NULL;
|
DIALData *result = NULL;
|
||||||
|
int err = 0;
|
||||||
char *query_string_dup = strdup(query_string);
|
char *query_string_dup = strdup(query_string);
|
||||||
char * name_value = strtok(query_string_dup, "&");
|
char * name_value = strtok(query_string_dup, "&");
|
||||||
while (name_value != NULL) {
|
while (name_value != NULL) {
|
||||||
DIALData *tmp = (DIALData *) malloc(sizeof(DIALData));
|
DIALData *tmp = (DIALData *) malloc(sizeof(DIALData));
|
||||||
|
if (tmp == NULL) {
|
||||||
|
err = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
size_t name_value_length = strlen(name_value);
|
size_t name_value_length = strlen(name_value);
|
||||||
tmp->key = (char *) malloc(name_value_length);
|
tmp->key = (char *) calloc(name_value_length + 1, sizeof(char));
|
||||||
tmp->value = (char *) malloc(name_value_length);
|
if (tmp->key == NULL) {
|
||||||
sscanf(name_value, "%[^=]=%s", tmp->key, tmp->value);
|
free(tmp); tmp = NULL;
|
||||||
|
err = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
tmp->value = (char *) calloc(name_value_length + 1, sizeof(char));
|
||||||
|
if (tmp->value == NULL) {
|
||||||
|
free(tmp->key); tmp->key = NULL;
|
||||||
|
free(tmp); tmp = NULL;
|
||||||
|
err = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
int match = sscanf(name_value, "%[^=]=%s", tmp->key, tmp->value);
|
||||||
|
if (match != 2) {
|
||||||
|
free(tmp->value); tmp->value = NULL;
|
||||||
|
free(tmp->key); tmp->key = NULL;
|
||||||
|
free(tmp); tmp = NULL;
|
||||||
|
err = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
tmp->next = result;
|
tmp->next = result;
|
||||||
result = tmp;
|
result = tmp;
|
||||||
|
|
||||||
name_value = strtok(NULL, "&"); // read next token
|
name_value = strtok(NULL, "&"); // read next token
|
||||||
}
|
}
|
||||||
free(query_string_dup);
|
free(query_string_dup);
|
||||||
|
if (err) {
|
||||||
|
free_dial_data(&result); result = NULL;
|
||||||
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -194,7 +263,7 @@ char from_hex(char ch) {
|
|||||||
/* Converts an integer value to its hex character*/
|
/* Converts an integer value to its hex character*/
|
||||||
char to_hex(char code) {
|
char to_hex(char code) {
|
||||||
static char hex[] = "0123456789abcdef";
|
static char hex[] = "0123456789abcdef";
|
||||||
return hex[code & 15];
|
return hex[code & 15];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2014 Netflix, Inc.
|
* Copyright (c) 2014-2019 Netflix, Inc.
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
@@ -31,35 +31,94 @@
|
|||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Concatenate a maxim of max_chars characters from src into dest,
|
* Copy a maximum of max_chars characters from src into dest,
|
||||||
* and return a pointer to the last character in dest.
|
* and return a pointer to the terminating NULL in dest.
|
||||||
|
*
|
||||||
|
* @param dest destination buffer.
|
||||||
|
* @param src string written into the destination buffer
|
||||||
|
* @param max_chars maximum number of characters to put into the destination
|
||||||
|
* buffer, excluding the trailing NULL. Must be less than or equal to
|
||||||
|
* the number of bytes available at dest - 1.
|
||||||
|
* @return a pointer to the end of the written string (the location of the
|
||||||
|
* terminating NULL).
|
||||||
*/
|
*/
|
||||||
char* smartstrcat(char* dest, char* src, size_t max_chars);
|
char* smartstrncpy(char* dest, char* src, size_t max_chars);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* URL-unescape the source string into the destination string, up to the
|
||||||
|
* maximum number of destination characters.
|
||||||
|
*
|
||||||
|
* @param dst raw string buffer.
|
||||||
|
* @param src URL-escaped source string.
|
||||||
|
* @param max_size maximum number of characters to put into the destination
|
||||||
|
* buffer, excluding the trailing NULL. Must be less than or equal to
|
||||||
|
* sizeof(dst) - 1.
|
||||||
|
* @return the length of the raw string excluding the trailing NULL. Will be
|
||||||
|
* 0 if there were no characters to unescape or if the source string
|
||||||
|
* was malformed.
|
||||||
|
*/
|
||||||
int urldecode(char *dst, const char *src, size_t max_size);
|
int urldecode(char *dst, const char *src, size_t max_size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* XML-escape the source string into the destination string, up to the maximum
|
||||||
|
* number of destination characters.
|
||||||
|
*
|
||||||
|
* @param dst XML-escaped string buffer.
|
||||||
|
* @param src raw source string.
|
||||||
|
* @param max_size maximum number of characters to put into the destination
|
||||||
|
* buffer, excluding the trailing NULL. Must be less than or equal to
|
||||||
|
* sizeof(dst) - 1.
|
||||||
|
* @return the length of the XML-escaped string excluding the trailing NULL.
|
||||||
|
*/
|
||||||
void xmlencode(char *dst, const char *src, size_t max_size);
|
void xmlencode(char *dst, const char *src, size_t max_size);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse the value of the parameter with the given name from a query string.
|
* Return the value in the query string for the requested parameter name.
|
||||||
|
*
|
||||||
|
* @param query_string the URL query string.
|
||||||
|
* @param param_name the parameter name.
|
||||||
|
* @return the parameter value or NULL if out-of-memory. The caller must free
|
||||||
|
* the returned memory.
|
||||||
*/
|
*/
|
||||||
char *parse_param(char *query_string, char *param_name);
|
char *parse_param(char *query_string, char *param_name);
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* Parse the application name out of a URI, such as /app/YouTube/dial_data.
|
* Parse the application name out of the full URI, for example
|
||||||
* Note: this parser assumes that the dial_data url is of the form
|
* /app/YouTube/dial_data. The application name identified as the string
|
||||||
* /apps/<app_name>/dial_data. If your DIAL server uses a different url-path,
|
* appearing before the last trailing slash, possibly prefixed with a slash,
|
||||||
* you will need to adapt the method below.
|
* so /apps/<app_name>/dial_data and YouTube would be the application name in
|
||||||
|
* the previous example.
|
||||||
|
*
|
||||||
|
* If your DIAL server uses a different path format you will need to change
|
||||||
|
* this method to match.
|
||||||
|
*
|
||||||
|
* @param uri the URI, there must be a trailing slash.
|
||||||
|
* @return the application name, or "unknown" if two slashes cannot be found
|
||||||
|
* or the application name is zero-length, or NULL if out-of-memory.
|
||||||
|
* The caller must free the returned memory.
|
||||||
*/
|
*/
|
||||||
char *parse_app_name(const char *uri);
|
char *parse_app_name(const char *uri);
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* Parse a list of DIALData params out of a query string.
|
* Return a linked list of DIAL data constructed from the name/value parameter
|
||||||
|
* pairs of the provided query string.
|
||||||
|
*
|
||||||
|
* This function must be called while holding a mutex and is not itself
|
||||||
|
* thread-safe.
|
||||||
|
*
|
||||||
|
* @param query_string the URL query string.
|
||||||
|
* @return the DIAL data or NULL if there is none (e.g. parse error) or out-of-
|
||||||
|
* memory. The caller must free the returned memory.
|
||||||
*/
|
*/
|
||||||
DIALData *parse_params(char * query_string);
|
DIALData *parse_params(char * query_string);
|
||||||
|
|
||||||
/* Returns a url-encoded version of str */
|
/**
|
||||||
/* IMPORTANT: be sure to free() the returned string after use */
|
* Return the URL-escaped version of the provided string, which may be as
|
||||||
|
* large as 3x the size of the provided string, plus a trailing NULL byte.
|
||||||
|
*
|
||||||
|
* @return the URL-escaped version of the provided string. The caller must
|
||||||
|
* free the returned string.
|
||||||
|
*/
|
||||||
char *url_encode(const char *str);
|
char *url_encode(const char *str);
|
||||||
|
|
||||||
#endif // URLLIB_H_
|
#endif // URLLIB_H_
|
||||||
|
|||||||
Reference in New Issue
Block a user