livefootballtv-tizen 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/app/app-es5.js +1976 -0
- package/app/config.xml +15 -0
- package/app/css/controls.css +54 -0
- package/app/icon.png +0 -0
- package/app/index.html +318 -0
- package/app/js/crypto-js.min.js +1 -0
- package/app/js/hls.min.js +2 -0
- package/app/js/shaka-player.compiled.min.js +1 -0
- package/app/js/shaka-player.ui.debug.js +3068 -0
- package/app/native_service/README.md +94 -0
- package/app/native_service/inc/proxy_service.h +22 -0
- package/app/native_service/src/proxy_service.cpp +453 -0
- package/app/native_service/tizen-manifest.xml +23 -0
- package/app/navigation.js +663 -0
- package/app/styles.css +539 -0
- package/package.json +11 -0
- package/service.js +121 -0
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# LiveFootballTV C++ Proxy Service - Tizen Studio Setup
|
|
2
|
+
|
|
3
|
+
This directory contains the source code for the native C++ proxy service.
|
|
4
|
+
You need to create a Tizen Studio project and import these files.
|
|
5
|
+
|
|
6
|
+
## Quick Setup in Tizen Studio
|
|
7
|
+
|
|
8
|
+
### Step 1: Create New Native Service Project
|
|
9
|
+
|
|
10
|
+
1. Open Tizen Studio
|
|
11
|
+
2. File → New → Tizen Project
|
|
12
|
+
3. Select **Template** → **TV** → **Native Service**
|
|
13
|
+
4. Name it: `proxyservice`
|
|
14
|
+
5. Package ID: `LIVEFTV001` (must match the web app!)
|
|
15
|
+
|
|
16
|
+
### Step 2: Configure C++11
|
|
17
|
+
|
|
18
|
+
1. Right-click project → **Properties**
|
|
19
|
+
2. Go to: C/C++ Build → Settings → Tool Settings → C++ Compiler → Dialect
|
|
20
|
+
3. Set Language standard to: **ISO C++11 (-std=c++0x)**
|
|
21
|
+
4. Click **Apply and Close**
|
|
22
|
+
|
|
23
|
+
### Step 3: Copy Source Files
|
|
24
|
+
|
|
25
|
+
Copy these files into your Tizen Studio project:
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
From: d:\IDE AI\webapp\tizen\native_service\
|
|
29
|
+
├── tizen-manifest.xml → [project root]/tizen-manifest.xml
|
|
30
|
+
├── src/proxy_service.cpp → [project root]/src/proxy_service.cpp
|
|
31
|
+
└── inc/proxy_service.h → [project root]/inc/proxy_service.h
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Step 4: Add Required Libraries
|
|
35
|
+
|
|
36
|
+
In project Properties → C/C++ Build → Settings → Tool Settings → C++ Linker → Libraries:
|
|
37
|
+
|
|
38
|
+
Add these libraries (`-l` flags):
|
|
39
|
+
- `curl`
|
|
40
|
+
- `pthread`
|
|
41
|
+
|
|
42
|
+
### Step 5: Build the Native Service
|
|
43
|
+
|
|
44
|
+
1. Right-click project → **Build Project**
|
|
45
|
+
2. Ensure no compilation errors
|
|
46
|
+
|
|
47
|
+
### Step 6: Create Hybrid Package
|
|
48
|
+
|
|
49
|
+
To package both web app and native service together:
|
|
50
|
+
|
|
51
|
+
1. Right-click the **Web App** project (livefootballtv)
|
|
52
|
+
2. Select: **Export to TPK**
|
|
53
|
+
3. In the dialog, check "Include Native Service"
|
|
54
|
+
4. Select the `proxyservice` native project
|
|
55
|
+
5. Export the combined `.wgt` or `.tpk` file
|
|
56
|
+
|
|
57
|
+
### Step 7: Deploy and Test
|
|
58
|
+
|
|
59
|
+
1. Connect TV via SDB
|
|
60
|
+
2. Install the hybrid package
|
|
61
|
+
3. Launch the app
|
|
62
|
+
4. Check if the Proxy indicator turns green
|
|
63
|
+
|
|
64
|
+
## Endpoints
|
|
65
|
+
|
|
66
|
+
The service runs on `http://localhost:8888` with these endpoints:
|
|
67
|
+
|
|
68
|
+
| Endpoint | Description |
|
|
69
|
+
|----------|-------------|
|
|
70
|
+
| `/health` | Health check - returns `{"status":"ok"}` |
|
|
71
|
+
| `/proxy?url=...` | Proxy request with custom headers |
|
|
72
|
+
|
|
73
|
+
## Custom Headers
|
|
74
|
+
|
|
75
|
+
Pass these headers to add custom headers to proxied requests:
|
|
76
|
+
|
|
77
|
+
- `X-Proxy-Referer: https://example.com` → Sends `Referer: https://example.com`
|
|
78
|
+
- `X-Proxy-Origin: https://example.com` → Sends `Origin: https://example.com`
|
|
79
|
+
- `X-Proxy-User-Agent: Custom UA` → Sends `User-Agent: Custom UA`
|
|
80
|
+
|
|
81
|
+
## Troubleshooting
|
|
82
|
+
|
|
83
|
+
### Service Not Starting
|
|
84
|
+
- Check Tizen Studio console for errors
|
|
85
|
+
- Verify package ID matches between web app and native service
|
|
86
|
+
- Ensure all privileges are granted
|
|
87
|
+
|
|
88
|
+
### Port Already in Use
|
|
89
|
+
- Another instance may be running
|
|
90
|
+
- Restart the TV and try again
|
|
91
|
+
|
|
92
|
+
### CURL Errors
|
|
93
|
+
- Check internet connectivity
|
|
94
|
+
- Verify target URL is accessible
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LiveFootballTV Proxy Service - Header
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
#ifndef __PROXY_SERVICE_H__
|
|
6
|
+
#define __PROXY_SERVICE_H__
|
|
7
|
+
|
|
8
|
+
#include <service_app.h>
|
|
9
|
+
#include <dlog.h>
|
|
10
|
+
|
|
11
|
+
#ifdef __cplusplus
|
|
12
|
+
extern "C" {
|
|
13
|
+
#endif
|
|
14
|
+
|
|
15
|
+
#define LOG_TAG "PROXY_SERVICE"
|
|
16
|
+
#define PROXY_PORT 8888
|
|
17
|
+
|
|
18
|
+
#ifdef __cplusplus
|
|
19
|
+
}
|
|
20
|
+
#endif
|
|
21
|
+
|
|
22
|
+
#endif /* __PROXY_SERVICE_H__ */
|
|
@@ -0,0 +1,453 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LiveFootballTV C++ Proxy Service
|
|
3
|
+
*
|
|
4
|
+
* This native service runs on localhost:8888 and proxies HTTP requests
|
|
5
|
+
* with custom headers for streaming video content.
|
|
6
|
+
*
|
|
7
|
+
* Build with Tizen Studio -> TV Native Service Project
|
|
8
|
+
* Enable C++11 in Project Settings
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
#include <service_app.h>
|
|
12
|
+
#include <dlog.h>
|
|
13
|
+
#include <curl/curl.h>
|
|
14
|
+
#include <sys/socket.h>
|
|
15
|
+
#include <netinet/in.h>
|
|
16
|
+
#include <arpa/inet.h>
|
|
17
|
+
#include <unistd.h>
|
|
18
|
+
#include <pthread.h>
|
|
19
|
+
#include <cstring>
|
|
20
|
+
#include <string>
|
|
21
|
+
#include <sstream>
|
|
22
|
+
#include <map>
|
|
23
|
+
#include <vector>
|
|
24
|
+
|
|
25
|
+
#define LOG_TAG "PROXY_SERVICE"
|
|
26
|
+
#define PROXY_PORT 8888
|
|
27
|
+
#define BUFFER_SIZE 65536
|
|
28
|
+
#define MAX_CLIENTS 10
|
|
29
|
+
|
|
30
|
+
static int server_fd = -1;
|
|
31
|
+
static volatile bool running = false;
|
|
32
|
+
static pthread_t server_thread;
|
|
33
|
+
|
|
34
|
+
// ============================================================================
|
|
35
|
+
// CURL Helpers
|
|
36
|
+
// ============================================================================
|
|
37
|
+
|
|
38
|
+
struct CurlResponse {
|
|
39
|
+
std::vector<char> data;
|
|
40
|
+
std::string content_type;
|
|
41
|
+
long http_code;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
static size_t write_callback(void* contents, size_t size, size_t nmemb, void* userp) {
|
|
45
|
+
CurlResponse* response = static_cast<CurlResponse*>(userp);
|
|
46
|
+
size_t total_size = size * nmemb;
|
|
47
|
+
const char* data = static_cast<char*>(contents);
|
|
48
|
+
response->data.insert(response->data.end(), data, data + total_size);
|
|
49
|
+
return total_size;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
static size_t header_callback(char* buffer, size_t size, size_t nitems, void* userp) {
|
|
53
|
+
CurlResponse* response = static_cast<CurlResponse*>(userp);
|
|
54
|
+
size_t total_size = size * nitems;
|
|
55
|
+
std::string header(buffer, total_size);
|
|
56
|
+
|
|
57
|
+
// Parse Content-Type header
|
|
58
|
+
if (header.find("Content-Type:") == 0 || header.find("content-type:") == 0) {
|
|
59
|
+
size_t start = header.find(':') + 1;
|
|
60
|
+
size_t end = header.find('\r');
|
|
61
|
+
if (end == std::string::npos) end = header.find('\n');
|
|
62
|
+
if (end != std::string::npos) {
|
|
63
|
+
response->content_type = header.substr(start, end - start);
|
|
64
|
+
// Trim whitespace
|
|
65
|
+
size_t first = response->content_type.find_first_not_of(" \t");
|
|
66
|
+
if (first != std::string::npos) {
|
|
67
|
+
response->content_type = response->content_type.substr(first);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return total_size;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// ============================================================================
|
|
75
|
+
// HTTP Parsing Helpers
|
|
76
|
+
// ============================================================================
|
|
77
|
+
|
|
78
|
+
std::string url_decode(const std::string& encoded) {
|
|
79
|
+
CURL* curl = curl_easy_init();
|
|
80
|
+
if (!curl) return encoded;
|
|
81
|
+
|
|
82
|
+
int out_len;
|
|
83
|
+
char* decoded = curl_easy_unescape(curl, encoded.c_str(), encoded.length(), &out_len);
|
|
84
|
+
std::string result = decoded ? std::string(decoded, out_len) : encoded;
|
|
85
|
+
if (decoded) curl_free(decoded);
|
|
86
|
+
curl_easy_cleanup(curl);
|
|
87
|
+
return result;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
std::string parse_url_param(const std::string& request) {
|
|
91
|
+
// Find ?url= or &url= in the request
|
|
92
|
+
size_t url_start = request.find("url=");
|
|
93
|
+
if (url_start == std::string::npos) return "";
|
|
94
|
+
|
|
95
|
+
url_start += 4; // Skip "url="
|
|
96
|
+
|
|
97
|
+
// Find the end of the URL parameter (space or &)
|
|
98
|
+
size_t url_end = request.find_first_of(" &\r\n", url_start);
|
|
99
|
+
if (url_end == std::string::npos) url_end = request.length();
|
|
100
|
+
|
|
101
|
+
std::string url = request.substr(url_start, url_end - url_start);
|
|
102
|
+
return url_decode(url);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
std::map<std::string, std::string> parse_custom_headers(const std::string& request) {
|
|
106
|
+
std::map<std::string, std::string> headers;
|
|
107
|
+
|
|
108
|
+
// Parse headers that start with X-Proxy- prefix
|
|
109
|
+
// These will be forwarded without the prefix
|
|
110
|
+
const char* prefixes[] = {"X-Proxy-Referer:", "X-Proxy-Origin:", "X-Proxy-User-Agent:"};
|
|
111
|
+
const char* header_names[] = {"Referer", "Origin", "User-Agent"};
|
|
112
|
+
|
|
113
|
+
for (int i = 0; i < 3; i++) {
|
|
114
|
+
size_t pos = request.find(prefixes[i]);
|
|
115
|
+
if (pos != std::string::npos) {
|
|
116
|
+
size_t value_start = pos + strlen(prefixes[i]);
|
|
117
|
+
// Skip leading whitespace
|
|
118
|
+
while (value_start < request.length() &&
|
|
119
|
+
(request[value_start] == ' ' || request[value_start] == '\t')) {
|
|
120
|
+
value_start++;
|
|
121
|
+
}
|
|
122
|
+
size_t value_end = request.find("\r\n", value_start);
|
|
123
|
+
if (value_end == std::string::npos) value_end = request.find("\n", value_start);
|
|
124
|
+
if (value_end != std::string::npos) {
|
|
125
|
+
headers[header_names[i]] = request.substr(value_start, value_end - value_start);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return headers;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// ============================================================================
|
|
134
|
+
// HTTP Response Helpers
|
|
135
|
+
// ============================================================================
|
|
136
|
+
|
|
137
|
+
void send_response(int client_fd, int status_code, const char* status_text,
|
|
138
|
+
const char* content_type, const char* body, size_t body_len) {
|
|
139
|
+
std::ostringstream response;
|
|
140
|
+
response << "HTTP/1.1 " << status_code << " " << status_text << "\r\n";
|
|
141
|
+
response << "Access-Control-Allow-Origin: *\r\n";
|
|
142
|
+
response << "Access-Control-Allow-Methods: GET, POST, OPTIONS\r\n";
|
|
143
|
+
response << "Access-Control-Allow-Headers: *\r\n";
|
|
144
|
+
response << "Access-Control-Expose-Headers: *\r\n";
|
|
145
|
+
response << "Connection: close\r\n";
|
|
146
|
+
if (content_type) {
|
|
147
|
+
response << "Content-Type: " << content_type << "\r\n";
|
|
148
|
+
}
|
|
149
|
+
response << "Content-Length: " << body_len << "\r\n";
|
|
150
|
+
response << "\r\n";
|
|
151
|
+
|
|
152
|
+
std::string headers = response.str();
|
|
153
|
+
send(client_fd, headers.c_str(), headers.length(), 0);
|
|
154
|
+
if (body && body_len > 0) {
|
|
155
|
+
send(client_fd, body, body_len, 0);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
void send_json_response(int client_fd, int status_code, const char* status_text, const char* json) {
|
|
160
|
+
send_response(client_fd, status_code, status_text, "application/json", json, strlen(json));
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// ============================================================================
|
|
164
|
+
// Request Handlers
|
|
165
|
+
// ============================================================================
|
|
166
|
+
|
|
167
|
+
void handle_health_check(int client_fd) {
|
|
168
|
+
const char* json = "{\"status\":\"ok\",\"service\":\"LiveFootballTV Proxy\",\"port\":8888}";
|
|
169
|
+
send_json_response(client_fd, 200, "OK", json);
|
|
170
|
+
dlog_print(DLOG_DEBUG, LOG_TAG, "Health check: OK");
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
void handle_cors_preflight(int client_fd) {
|
|
174
|
+
send_response(client_fd, 200, "OK", NULL, "", 0);
|
|
175
|
+
dlog_print(DLOG_DEBUG, LOG_TAG, "CORS preflight handled");
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
void handle_proxy_request(int client_fd, const std::string& request) {
|
|
179
|
+
// Extract target URL
|
|
180
|
+
std::string target_url = parse_url_param(request);
|
|
181
|
+
if (target_url.empty()) {
|
|
182
|
+
const char* error = "{\"error\":\"Missing 'url' parameter\"}";
|
|
183
|
+
send_json_response(client_fd, 400, "Bad Request", error);
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
dlog_print(DLOG_INFO, LOG_TAG, "Proxying: %s", target_url.c_str());
|
|
188
|
+
|
|
189
|
+
// Parse custom headers from request
|
|
190
|
+
auto custom_headers = parse_custom_headers(request);
|
|
191
|
+
|
|
192
|
+
// Initialize CURL
|
|
193
|
+
CURL* curl = curl_easy_init();
|
|
194
|
+
if (!curl) {
|
|
195
|
+
const char* error = "{\"error\":\"Failed to initialize HTTP client\"}";
|
|
196
|
+
send_json_response(client_fd, 500, "Internal Server Error", error);
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
CurlResponse response;
|
|
201
|
+
response.http_code = 0;
|
|
202
|
+
|
|
203
|
+
// Build headers list
|
|
204
|
+
struct curl_slist* curl_headers = NULL;
|
|
205
|
+
|
|
206
|
+
// Add custom headers from request
|
|
207
|
+
for (const auto& h : custom_headers) {
|
|
208
|
+
std::string header = h.first + ": " + h.second;
|
|
209
|
+
curl_headers = curl_slist_append(curl_headers, header.c_str());
|
|
210
|
+
dlog_print(DLOG_DEBUG, LOG_TAG, "Adding header: %s", header.c_str());
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Default User-Agent if not provided
|
|
214
|
+
if (custom_headers.find("User-Agent") == custom_headers.end()) {
|
|
215
|
+
curl_headers = curl_slist_append(curl_headers,
|
|
216
|
+
"User-Agent: Mozilla/5.0 (SMART-TV; Tizen 4.0) AppleWebKit/538.1 (KHTML, like Gecko)");
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Configure CURL
|
|
220
|
+
curl_easy_setopt(curl, CURLOPT_URL, target_url.c_str());
|
|
221
|
+
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, curl_headers);
|
|
222
|
+
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
|
|
223
|
+
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
|
|
224
|
+
curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, header_callback);
|
|
225
|
+
curl_easy_setopt(curl, CURLOPT_HEADERDATA, &response);
|
|
226
|
+
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
|
|
227
|
+
curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 10L);
|
|
228
|
+
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30L);
|
|
229
|
+
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 10L);
|
|
230
|
+
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
|
|
231
|
+
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
|
|
232
|
+
|
|
233
|
+
// Perform request
|
|
234
|
+
CURLcode res = curl_easy_perform(curl);
|
|
235
|
+
|
|
236
|
+
if (res == CURLE_OK) {
|
|
237
|
+
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response.http_code);
|
|
238
|
+
|
|
239
|
+
dlog_print(DLOG_INFO, LOG_TAG, "Proxy response: %ld, size: %zu",
|
|
240
|
+
response.http_code, response.data.size());
|
|
241
|
+
|
|
242
|
+
// Determine content type
|
|
243
|
+
const char* ct = response.content_type.empty() ?
|
|
244
|
+
"application/octet-stream" : response.content_type.c_str();
|
|
245
|
+
|
|
246
|
+
// Send response
|
|
247
|
+
send_response(client_fd, (int)response.http_code, "OK", ct,
|
|
248
|
+
response.data.data(), response.data.size());
|
|
249
|
+
} else {
|
|
250
|
+
dlog_print(DLOG_ERROR, LOG_TAG, "CURL error: %s", curl_easy_strerror(res));
|
|
251
|
+
|
|
252
|
+
std::ostringstream error_json;
|
|
253
|
+
error_json << "{\"error\":\"" << curl_easy_strerror(res) << "\"}";
|
|
254
|
+
std::string error_str = error_json.str();
|
|
255
|
+
send_json_response(client_fd, 502, "Bad Gateway", error_str.c_str());
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// Cleanup
|
|
259
|
+
if (curl_headers) curl_slist_free_all(curl_headers);
|
|
260
|
+
curl_easy_cleanup(curl);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// ============================================================================
|
|
264
|
+
// Client Handler
|
|
265
|
+
// ============================================================================
|
|
266
|
+
|
|
267
|
+
void handle_client(int client_fd) {
|
|
268
|
+
char buffer[BUFFER_SIZE];
|
|
269
|
+
ssize_t bytes_read = recv(client_fd, buffer, sizeof(buffer) - 1, 0);
|
|
270
|
+
|
|
271
|
+
if (bytes_read <= 0) {
|
|
272
|
+
close(client_fd);
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
buffer[bytes_read] = '\0';
|
|
277
|
+
std::string request(buffer);
|
|
278
|
+
|
|
279
|
+
dlog_print(DLOG_DEBUG, LOG_TAG, "Request received (%zd bytes)", bytes_read);
|
|
280
|
+
|
|
281
|
+
// Route request
|
|
282
|
+
if (request.find("GET /health") != std::string::npos) {
|
|
283
|
+
handle_health_check(client_fd);
|
|
284
|
+
}
|
|
285
|
+
else if (request.find("OPTIONS ") == 0) {
|
|
286
|
+
handle_cors_preflight(client_fd);
|
|
287
|
+
}
|
|
288
|
+
else if (request.find("/proxy?") != std::string::npos ||
|
|
289
|
+
request.find("/proxy?") != std::string::npos) {
|
|
290
|
+
handle_proxy_request(client_fd, request);
|
|
291
|
+
}
|
|
292
|
+
else {
|
|
293
|
+
const char* error = "{\"error\":\"Unknown endpoint. Use /health or /proxy?url=...\"}";
|
|
294
|
+
send_json_response(client_fd, 404, "Not Found", error);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
close(client_fd);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// ============================================================================
|
|
301
|
+
// Server Thread
|
|
302
|
+
// ============================================================================
|
|
303
|
+
|
|
304
|
+
void* server_loop(void* arg) {
|
|
305
|
+
dlog_print(DLOG_INFO, LOG_TAG, "Server thread started");
|
|
306
|
+
|
|
307
|
+
while (running) {
|
|
308
|
+
struct sockaddr_in client_addr;
|
|
309
|
+
socklen_t client_len = sizeof(client_addr);
|
|
310
|
+
|
|
311
|
+
// Use select with timeout to allow checking 'running' flag
|
|
312
|
+
fd_set read_fds;
|
|
313
|
+
FD_ZERO(&read_fds);
|
|
314
|
+
FD_SET(server_fd, &read_fds);
|
|
315
|
+
|
|
316
|
+
struct timeval timeout;
|
|
317
|
+
timeout.tv_sec = 1;
|
|
318
|
+
timeout.tv_usec = 0;
|
|
319
|
+
|
|
320
|
+
int ready = select(server_fd + 1, &read_fds, NULL, NULL, &timeout);
|
|
321
|
+
|
|
322
|
+
if (ready > 0 && FD_ISSET(server_fd, &read_fds)) {
|
|
323
|
+
int client_fd = accept(server_fd, (struct sockaddr*)&client_addr, &client_len);
|
|
324
|
+
if (client_fd >= 0) {
|
|
325
|
+
handle_client(client_fd);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
dlog_print(DLOG_INFO, LOG_TAG, "Server thread exiting");
|
|
331
|
+
return NULL;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// ============================================================================
|
|
335
|
+
// Server Start/Stop
|
|
336
|
+
// ============================================================================
|
|
337
|
+
|
|
338
|
+
bool start_server() {
|
|
339
|
+
server_fd = socket(AF_INET, SOCK_STREAM, 0);
|
|
340
|
+
if (server_fd < 0) {
|
|
341
|
+
dlog_print(DLOG_ERROR, LOG_TAG, "Socket creation failed: %s", strerror(errno));
|
|
342
|
+
return false;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// Allow address reuse
|
|
346
|
+
int opt = 1;
|
|
347
|
+
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) {
|
|
348
|
+
dlog_print(DLOG_WARN, LOG_TAG, "setsockopt SO_REUSEADDR failed");
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// Bind to all interfaces on PROXY_PORT
|
|
352
|
+
struct sockaddr_in server_addr;
|
|
353
|
+
memset(&server_addr, 0, sizeof(server_addr));
|
|
354
|
+
server_addr.sin_family = AF_INET;
|
|
355
|
+
server_addr.sin_addr.s_addr = INADDR_ANY;
|
|
356
|
+
server_addr.sin_port = htons(PROXY_PORT);
|
|
357
|
+
|
|
358
|
+
if (bind(server_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
|
|
359
|
+
dlog_print(DLOG_ERROR, LOG_TAG, "Bind failed on port %d: %s", PROXY_PORT, strerror(errno));
|
|
360
|
+
close(server_fd);
|
|
361
|
+
server_fd = -1;
|
|
362
|
+
return false;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
if (listen(server_fd, MAX_CLIENTS) < 0) {
|
|
366
|
+
dlog_print(DLOG_ERROR, LOG_TAG, "Listen failed: %s", strerror(errno));
|
|
367
|
+
close(server_fd);
|
|
368
|
+
server_fd = -1;
|
|
369
|
+
return false;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
running = true;
|
|
373
|
+
|
|
374
|
+
if (pthread_create(&server_thread, NULL, server_loop, NULL) != 0) {
|
|
375
|
+
dlog_print(DLOG_ERROR, LOG_TAG, "Failed to create server thread");
|
|
376
|
+
close(server_fd);
|
|
377
|
+
server_fd = -1;
|
|
378
|
+
running = false;
|
|
379
|
+
return false;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
dlog_print(DLOG_INFO, LOG_TAG, "=== Proxy Server Started on port %d ===", PROXY_PORT);
|
|
383
|
+
return true;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
void stop_server() {
|
|
387
|
+
dlog_print(DLOG_INFO, LOG_TAG, "Stopping proxy server...");
|
|
388
|
+
|
|
389
|
+
running = false;
|
|
390
|
+
|
|
391
|
+
if (server_fd >= 0) {
|
|
392
|
+
shutdown(server_fd, SHUT_RDWR);
|
|
393
|
+
close(server_fd);
|
|
394
|
+
server_fd = -1;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
pthread_join(server_thread, NULL);
|
|
398
|
+
|
|
399
|
+
dlog_print(DLOG_INFO, LOG_TAG, "=== Proxy Server Stopped ===");
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
// ============================================================================
|
|
403
|
+
// Service Lifecycle Callbacks
|
|
404
|
+
// ============================================================================
|
|
405
|
+
|
|
406
|
+
bool service_app_create(void* data) {
|
|
407
|
+
dlog_print(DLOG_INFO, LOG_TAG, "=== Service Creating ===");
|
|
408
|
+
|
|
409
|
+
// Initialize CURL globally
|
|
410
|
+
CURLcode res = curl_global_init(CURL_GLOBAL_DEFAULT);
|
|
411
|
+
if (res != CURLE_OK) {
|
|
412
|
+
dlog_print(DLOG_ERROR, LOG_TAG, "CURL global init failed: %s", curl_easy_strerror(res));
|
|
413
|
+
return false;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
// Start the proxy server
|
|
417
|
+
if (!start_server()) {
|
|
418
|
+
curl_global_cleanup();
|
|
419
|
+
return false;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
dlog_print(DLOG_INFO, LOG_TAG, "=== Service Created Successfully ===");
|
|
423
|
+
return true;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
void service_app_terminate(void* data) {
|
|
427
|
+
dlog_print(DLOG_INFO, LOG_TAG, "=== Service Terminating ===");
|
|
428
|
+
|
|
429
|
+
stop_server();
|
|
430
|
+
curl_global_cleanup();
|
|
431
|
+
|
|
432
|
+
dlog_print(DLOG_INFO, LOG_TAG, "=== Service Terminated ===");
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
void service_app_control(app_control_h app_control, void* data) {
|
|
436
|
+
dlog_print(DLOG_INFO, LOG_TAG, "Service app_control received");
|
|
437
|
+
// Handle any incoming app control requests here
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
// ============================================================================
|
|
441
|
+
// Main Entry Point
|
|
442
|
+
// ============================================================================
|
|
443
|
+
|
|
444
|
+
int main(int argc, char* argv[]) {
|
|
445
|
+
service_app_lifecycle_callback_s event_callback;
|
|
446
|
+
memset(&event_callback, 0, sizeof(event_callback));
|
|
447
|
+
|
|
448
|
+
event_callback.create = service_app_create;
|
|
449
|
+
event_callback.terminate = service_app_terminate;
|
|
450
|
+
event_callback.app_control = service_app_control;
|
|
451
|
+
|
|
452
|
+
return service_app_main(argc, argv, &event_callback, NULL);
|
|
453
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<manifest xmlns="http://tizen.org/ns/packages"
|
|
3
|
+
api-version="4.0"
|
|
4
|
+
package="LIVEFTV001"
|
|
5
|
+
version="1.0.0">
|
|
6
|
+
<profile name="tv"/>
|
|
7
|
+
|
|
8
|
+
<!-- Native Service Application -->
|
|
9
|
+
<service-application appid="LIVEFTV001.proxyservice"
|
|
10
|
+
exec="proxyservice"
|
|
11
|
+
type="capp"
|
|
12
|
+
nodisplay="true"
|
|
13
|
+
multiple="false"
|
|
14
|
+
auto-restart="true"
|
|
15
|
+
on-boot="false">
|
|
16
|
+
<label>Proxy Service</label>
|
|
17
|
+
</service-application>
|
|
18
|
+
|
|
19
|
+
<privileges>
|
|
20
|
+
<privilege>http://tizen.org/privilege/internet</privilege>
|
|
21
|
+
<privilege>http://tizen.org/privilege/network.connection</privilege>
|
|
22
|
+
</privileges>
|
|
23
|
+
</manifest>
|