Coverage Report

Created: 2026-11-12 06:54

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/open62541/src/server/ua_transport_http.c
Line
Count
Source
1
/* This Source Code Form is subject to the terms of the Mozilla Public
2
 * License, v. 2.0. If a copy of the MPL was not distributed with this
3
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
4
 *
5
 *    Copyright 2026 (c) o6 Automation GmbH (Author: Julius Pfrommer)
6
 */
7
8
#include "ua_server_internal.h"
9
#include "ua_services.h"
10
11
typedef struct UA_HttpProtocolManager UA_HttpProtocolManager;
12
typedef struct UA_HttpListener UA_HttpListener;
13
typedef struct UA_HttpServerSequence UA_HttpServerSequence;
14
15
77.1k
#define UA_HTTP_LISTENERS_SIZE 2
16
17
struct UA_HttpProtocolManager {
18
    UA_Driver drv;
19
    UA_ConnectionManager *connectionManager;
20
21
    /* One listener for opc.http and one for opc.https. Binary uses the
22
     * configured path and JSON its derived /json child route. */
23
    struct UA_HttpListener {
24
        UA_HttpProtocolManager *manager;
25
        uintptr_t connectionId;
26
        UA_String endpointUrl;
27
        UA_Boolean discoveryUrlAdded;
28
    } listeners[UA_HTTP_LISTENERS_SIZE];
29
30
    LIST_HEAD(, UA_HttpServerSequence) sequences;
31
};
32
33
/* Part 6 describes one URL-level SecureChannel. Internally we keep routing
34
 * channels below that transport endpoint so a Session, its nonces and its
35
 * AuthenticationToken never become shared across unrelated clients. */
36
37
/* HTTP/1.1 requests are strictly serial on an accepted connection. The
38
 * ConnectionManager id therefore identifies both the sequence and its one
39
 * active service response. */
40
struct UA_HttpServerSequence {
41
    LIST_ENTRY(UA_HttpServerSequence) next;
42
    UA_SecureChannel *channel;
43
    uintptr_t connectionId;
44
    UA_UInt32 requestHandle;
45
};
46
47
/* URL and endpoint configuration */
48
49
static UA_Boolean
50
0
isSecureHttpListener(const UA_HttpListener *listener) {
51
0
    return listener == &listener->manager->listeners[1];
52
0
}
53
54
static UA_StatusCode
55
parseHttpServerUrl(const UA_String *url, UA_Boolean *secure,
56
0
                   UA_String *hostname, UA_UInt16 *port, UA_String *path) {
57
0
    if(!getHttpUrlSecurity(url, secure))
58
0
        return UA_STATUSCODE_BADNOTSUPPORTED;
59
0
    *port = *secure ? 443 : 80;
60
0
    UA_StatusCode res = UA_parseEndpointUrl(url, hostname, port, path);
61
0
    if(res != UA_STATUSCODE_GOOD)
62
0
        return res;
63
0
    if(hostname->length == 0)
64
0
        return UA_STATUSCODE_BADINVALIDARGUMENT;
65
66
    /* Normalize the configured Binary route. */
67
0
    size_t begin = 0;
68
0
    while(begin < path->length && path->data[begin] == '/')
69
0
        begin++;
70
0
    size_t end = path->length;
71
0
    while(end > begin && path->data[end - 1] == '/')
72
0
        end--;
73
0
    path->data = path->data ? &path->data[begin] : NULL;
74
0
    path->length = end - begin;
75
76
    /* Keep the derived JSON child route unambiguous. */
77
0
    static const UA_String json = UA_STRING_STATIC("json");
78
0
    if(path->length >= json.length) {
79
0
        UA_String suffix = {json.length,
80
0
                            &path->data[path->length - json.length]};
81
0
        if(UA_String_equal(&suffix, &json) &&
82
0
           (path->length == json.length ||
83
0
            path->data[path->length - json.length - 1] == '/'))
84
0
            return UA_STATUSCODE_BADINVALIDARGUMENT;
85
0
    }
86
0
    return UA_STATUSCODE_GOOD;
87
0
}
88
89
UA_StatusCode
90
270
UA_HttpProtocolManager_validateConfig(UA_Driver *drv) {
91
270
    UA_ServerConfig *config = &drv->server->config;
92
270
    if(!config->httpEnabled)
93
270
        return UA_STATUSCODE_GOOD;
94
95
0
    UA_Boolean haveSecureUrl = false;
96
0
    UA_Boolean haveInsecureUrl = false;
97
0
    for(size_t i = 0; i < config->serverUrlsSize; i++) {
98
0
        const UA_String *url = &config->serverUrls[i];
99
0
        UA_Boolean secure = false;
100
0
        UA_String hostname = UA_STRING_NULL;
101
0
        UA_String path = UA_STRING_NULL;
102
0
        UA_UInt16 port = 0;
103
0
        UA_StatusCode res = parseHttpServerUrl(
104
0
            url, &secure, &hostname, &port, &path);
105
0
        if(res == UA_STATUSCODE_BADNOTSUPPORTED)
106
0
            continue;
107
108
0
        if((secure && haveSecureUrl) || (!secure && haveInsecureUrl)) {
109
0
            UA_LOG_ERROR(config->logging, UA_LOGCATEGORY_SERVER,
110
0
                         "Only one opc.http and one opc.https ServerUrl can "
111
0
                         "be configured");
112
0
            return UA_STATUSCODE_BADCONFIGURATIONERROR;
113
0
        }
114
0
        haveSecureUrl |= secure;
115
0
        haveInsecureUrl |= !secure;
116
117
0
        if(res != UA_STATUSCODE_GOOD) {
118
0
            UA_LOG_ERROR(config->logging, UA_LOGCATEGORY_SERVER,
119
0
                         "An opc.http(s) ServerUrl requires an advertised "
120
0
                         "hostname; use httpListenAddress for wildcard binding");
121
0
            return UA_STATUSCODE_BADCONFIGURATIONERROR;
122
0
        }
123
0
    }
124
125
0
    if(!haveSecureUrl && !haveInsecureUrl) {
126
0
        UA_LOG_ERROR(config->logging, UA_LOGCATEGORY_SERVER,
127
0
                     "HTTP transport is enabled but no opc.http:// or "
128
0
                     "opc.https:// ServerUrl is configured");
129
0
        return UA_STATUSCODE_BADCONFIGURATIONERROR;
130
0
    }
131
0
    const UA_String protocol = UA_STRING_STATIC("http");
132
0
    if(!findConnectionManager(config->eventLoop, &protocol)) {
133
0
        UA_LOG_ERROR(config->logging, UA_LOGCATEGORY_SERVER,
134
0
                     "HTTP transport is enabled but no HTTP "
135
0
                     "ConnectionManager is configured");
136
0
        return UA_STATUSCODE_BADCONFIGURATIONERROR;
137
0
    }
138
0
    if(haveInsecureUrl) {
139
0
        if(!config->httpAllowUnencrypted) {
140
0
            UA_LOG_ERROR(config->logging, UA_LOGCATEGORY_SERVER,
141
0
                         "opc.http:// is configured, but httpAllowUnencrypted "
142
0
                         "is false. Unencrypted HTTP transport is non-standard "
143
0
                         "and must be explicitly allowed");
144
0
            return UA_STATUSCODE_BADCONFIGURATIONERROR;
145
0
        }
146
0
        UA_LOG_WARNING(config->logging, UA_LOGCATEGORY_SERVER,
147
0
                       "opc.http:// is enabled. Unencrypted HTTP transport is "
148
0
                       "non-standard and should only be used for "
149
0
                       "testing/debugging");
150
0
        if(config->allowNonePolicyPassword)
151
0
            UA_LOG_WARNING(config->logging, UA_LOGCATEGORY_SERVER,
152
0
                           "Plaintext username/password authentication is "
153
0
                           "explicitly enabled via allowNonePolicyPassword");
154
0
    }
155
0
    if(haveSecureUrl && (config->httpCertificate.length == 0 ||
156
0
                         config->httpPrivateKey.length == 0)) {
157
0
        UA_LOG_ERROR(config->logging, UA_LOGCATEGORY_SERVER,
158
0
                     "HTTP transport is enabled for opc.https:// but its TLS "
159
0
                     "certificate or private key is empty");
160
0
        return UA_STATUSCODE_BADCONFIGURATIONERROR;
161
0
    }
162
0
    return UA_STATUSCODE_GOOD;
163
0
}
164
165
static UA_StatusCode
166
formatEndpointUrl(const UA_HttpListener *listener, const UA_String *hostname,
167
                  UA_UInt16 port, const UA_String *path,
168
0
                  UA_String *endpointUrl) {
169
0
    UA_Boolean ipv6 = memchr(hostname->data, ':', hostname->length) != NULL;
170
0
    const char *scheme = isSecureHttpListener(listener) ?
171
0
        "opc.https" : "opc.http";
172
0
    if(ipv6)
173
0
        return UA_String_format(endpointUrl, "%s://[%S]:%u/%S", scheme,
174
0
                                *hostname, (unsigned)port, *path);
175
0
    return UA_String_format(endpointUrl, "%s://%S:%u/%S", scheme,
176
0
                            *hostname, (unsigned)port, *path);
177
0
}
178
179
static void
180
25.1k
unregisterHttpDiscoveryUrl(UA_HttpListener *listener) {
181
25.1k
    if(!listener->discoveryUrlAdded)
182
25.1k
        return;
183
0
    removeServerDiscoveryUrl(listener->manager->drv.server,
184
0
                             &listener->endpointUrl);
185
0
    listener->discoveryUrlAdded = false;
186
0
}
187
188
static UA_StatusCode
189
0
setEndpointUrl(UA_HttpListener *listener, UA_UInt16 port) {
190
0
    UA_String hostname = UA_STRING_NULL;
191
0
    UA_String path = UA_STRING_NULL;
192
0
    UA_UInt16 oldPort = 0;
193
0
    UA_StatusCode res = UA_parseEndpointUrl(&listener->endpointUrl, &hostname,
194
0
                                            &oldPort, &path);
195
0
    if(res != UA_STATUSCODE_GOOD)
196
0
        return res;
197
0
    UA_String endpointUrl = UA_STRING_NULL;
198
0
    res = formatEndpointUrl(listener, &hostname, port, &path, &endpointUrl);
199
0
    if(res != UA_STATUSCODE_GOOD)
200
0
        return res;
201
202
0
    unregisterHttpDiscoveryUrl(listener);
203
0
    UA_String_clear(&listener->endpointUrl);
204
0
    listener->endpointUrl = endpointUrl;
205
0
    listener->discoveryUrlAdded = addServerDiscoveryUrl(
206
0
        listener->manager->drv.server, &listener->endpointUrl);
207
0
    return UA_STATUSCODE_GOOD;
208
0
}
209
210
static const UA_String *getStringParam(const UA_KeyValueMap *params,
211
0
                                       const char *name) {
212
0
    return (const UA_String *)UA_KeyValueMap_getScalar(
213
0
        params, UA_QUALIFIEDNAME(0, (char *)(uintptr_t)name),
214
0
        &UA_TYPES[UA_TYPES_STRING]);
215
0
}
216
217
/* SecureChannel and request-sequence lifecycle */
218
219
static UA_Boolean
220
httpChannelMatchesListener(const UA_HttpListener *listener,
221
60
                           const UA_SecureChannel *channel) {
222
60
    return channel->transport == UA_SECURECHANNEL_TRANSPORT_HTTP &&
223
0
           channel->connectionManager == listener->manager->connectionManager &&
224
0
           channel->connectionId != 0 &&
225
0
           channel->connectionId == listener->connectionId;
226
60
}
227
228
static UA_SecureChannel *
229
newHttpChannel(UA_HttpListener *listener, UA_SecurityPolicy *policy,
230
               const UA_ByteString *clientCertificate,
231
               UA_SecureChannelEncoding encoding,
232
0
               const UA_String *remoteAddress) {
233
0
    UA_HttpProtocolManager *hpm = listener->manager;
234
0
    UA_SecureChannel *channel =
235
0
        (UA_SecureChannel *)UA_calloc(1, sizeof(*channel));
236
0
    if(!channel)
237
0
        return NULL;
238
0
    UA_SecureChannel_init(channel);
239
0
    channel->transport = UA_SECURECHANNEL_TRANSPORT_HTTP;
240
0
    channel->encoding = encoding;
241
0
    channel->connectionManager = hpm->connectionManager;
242
0
    channel->connectionId = listener->connectionId;
243
0
    UA_StatusCode res = UA_STATUSCODE_GOOD;
244
0
    if(remoteAddress)
245
0
        res = UA_String_copy(remoteAddress, &channel->remoteAddress);
246
0
    UA_MessageSecurityMode mode = UA_SecureChannel_httpSecurityMode(
247
0
        isSecureHttpListener(listener));
248
0
    if(res == UA_STATUSCODE_GOOD) {
249
0
        res = UA_SecureChannel_setSecurityPolicyWithoutOPN(
250
0
            channel, policy, clientCertificate, mode);
251
0
    }
252
0
    if(res == UA_STATUSCODE_GOOD) {
253
0
        channel->state = UA_SECURECHANNELSTATE_OPEN;
254
0
        res = registerSecureChannel(hpm->drv.server, channel);
255
0
    }
256
0
    if(res != UA_STATUSCODE_GOOD) {
257
0
        UA_SecureChannel_clear(channel);
258
0
        UA_free(channel);
259
0
        return NULL;
260
0
    }
261
0
    return channel;
262
0
}
263
264
static void
265
0
deleteHttpChannel(UA_Server *server, UA_SecureChannel *channel) {
266
0
    UA_assert(!channel->sessions);
267
0
    unregisterSecureChannel(server, channel);
268
0
    UA_SecureChannel_clear(channel);
269
0
    UA_free(channel);
270
0
}
271
272
static void
273
removeHttpChannelIfUnused(UA_HttpProtocolManager *hpm,
274
0
                          UA_SecureChannel *channel) {
275
0
    if(channel->sessions || channel->state == UA_SECURECHANNELSTATE_OPEN)
276
0
        return;
277
278
    /* A closing logical channel stays alive until its accepted responses
279
     * have either completed or been abandoned. */
280
0
    UA_HttpServerSequence *sequence;
281
0
    LIST_FOREACH(sequence, &hpm->sequences, next) {
282
0
        if(sequence->channel == channel)
283
0
            return;
284
0
    }
285
0
    deleteHttpChannel(hpm->drv.server, channel);
286
0
}
287
288
static void
289
releaseHttpSequenceRequest(UA_HttpProtocolManager *hpm,
290
0
                           UA_HttpServerSequence *sequence) {
291
0
    UA_SecureChannel *channel = sequence->channel;
292
0
    sequence->channel = NULL;
293
0
    sequence->requestHandle = 0;
294
0
    if(channel)
295
0
        removeHttpChannelIfUnused(hpm, channel);
296
0
}
297
298
static UA_StatusCode
299
finishHttpSequenceResponse(UA_HttpProtocolManager *hpm,
300
                           UA_HttpServerSequence *sequence,
301
0
                           UA_StatusCode result) {
302
0
    uintptr_t connectionId = sequence->connectionId;
303
0
    releaseHttpSequenceRequest(hpm, sequence);
304
0
    if(result == UA_STATUSCODE_GOOD)
305
0
        return UA_STATUSCODE_GOOD;
306
0
    UA_LOG_DEBUG(hpm->drv.server->config.logging, UA_LOGCATEGORY_NETWORK,
307
0
                 "OPC HTTP response failed: %s", UA_StatusCode_name(result));
308
0
    hpm->connectionManager->closeConnection(hpm->connectionManager,
309
0
                                            connectionId);
310
0
    return UA_STATUSCODE_BADCONNECTIONCLOSED;
311
0
}
312
313
UA_StatusCode
314
sendHttpServiceResponse(UA_Server *server, UA_SecureChannel *channel,
315
                        UA_UInt64 responseToken,
316
0
                        void *payload, const UA_DataType *payloadType) {
317
0
    UA_HttpProtocolManager *hpm =
318
0
        (UA_HttpProtocolManager *)server->httpDriver;
319
0
    if(responseToken > UINTPTR_MAX)
320
0
        return UA_STATUSCODE_BADNOTFOUND;
321
0
    uintptr_t connectionId = (uintptr_t)responseToken;
322
323
    /* Resolve the accepted connection carrying this response. */
324
0
    UA_HttpServerSequence *sequence;
325
0
    LIST_FOREACH(sequence, &hpm->sequences, next) {
326
0
        if(sequence->connectionId == connectionId)
327
0
            break;
328
0
    }
329
0
    if(!sequence || sequence->channel != channel)
330
0
        return UA_STATUSCODE_BADNOTFOUND;
331
0
    UA_StatusCode res = UA_SecureChannel_sendHttpResponse(
332
0
        channel, connectionId, payload, payloadType);
333
0
    if(res == UA_STATUSCODE_BADRESPONSETOOLARGE &&
334
0
       payloadType != &UA_TYPES[UA_TYPES_SERVICEFAULT]) {
335
0
        UA_ServiceFault fault;
336
0
        UA_ServiceFault_init(&fault);
337
0
        fault.responseHeader.timestamp =
338
0
            server->config.eventLoop->dateTime_now(server->config.eventLoop);
339
0
        fault.responseHeader.requestHandle = sequence->requestHandle;
340
0
        fault.responseHeader.serviceResult = UA_STATUSCODE_BADRESPONSETOOLARGE;
341
0
        res = UA_SecureChannel_sendHttpResponse(
342
0
            channel, connectionId, &fault,
343
0
            &UA_TYPES[UA_TYPES_SERVICEFAULT]);
344
0
    }
345
0
    if(res == UA_STATUSCODE_BADNOTSUPPORTED)
346
0
        res = UA_Http_sendResponse(hpm->connectionManager, connectionId, 406,
347
0
                                   NULL, NULL, NULL);
348
0
    return finishHttpSequenceResponse(hpm, sequence, res);
349
0
}
350
351
void
352
shutdownHttpSecureChannel(UA_Server *server, UA_SecureChannel *channel,
353
0
                          UA_ShutdownReason reason) {
354
0
    if(!UA_SecureChannel_isConnected(channel))
355
0
        return;
356
0
    UA_HttpProtocolManager *hpm =
357
0
        (UA_HttpProtocolManager *)server->httpDriver;
358
    /* The listener is shared and must survive this logical channel. */
359
0
    channel->connectionId = 0;
360
0
    UA_SecureChannel_shutdown(channel, reason);
361
362
    /* Detach activated Sessions and reject incomplete ones. */
363
0
    while(channel->sessions) {
364
0
        UA_Session *session = channel->sessions;
365
0
        if(session->state == UA_SESSIONSTATE_CLOSED)
366
0
            UA_Session_detachFromSecureChannel(server, session);
367
0
        else if(session->state == UA_SESSIONSTATE_ACTIVATED)
368
0
            UA_Session_detachFromSecureChannel(server, session);
369
0
        else
370
0
            UA_Session_remove(server, session, reason);
371
0
    }
372
0
    removeHttpChannelIfUnused(hpm, channel);
373
0
}
374
375
static void
376
shutdownHttpChannels(UA_HttpProtocolManager *hpm,
377
                     const UA_HttpListener *listener,
378
810
                     UA_ShutdownReason reason) {
379
810
    UA_Server *server = hpm->drv.server;
380
810
    UA_SecureChannel *channel, *next;
381
810
    TAILQ_FOREACH_SAFE(channel, &server->channels, serverEntry, next) {
382
90
        UA_Boolean matches = listener ?
383
60
            httpChannelMatchesListener(listener, channel) :
384
90
            channel->transport == UA_SECURECHANNEL_TRANSPORT_HTTP;
385
90
        if(!matches)
386
90
            continue;
387
0
        if(UA_SecureChannel_isConnected(channel))
388
0
            shutdownHttpSecureChannel(server, channel, reason);
389
0
        else if(reason == UA_SHUTDOWNREASON_PURGE) {
390
0
            UA_assert(!channel->sessions && LIST_EMPTY(&hpm->sequences));
391
0
            deleteHttpChannel(server, channel);
392
0
        }
393
0
    }
394
810
}
395
396
/* Session and channel routing */
397
398
static UA_SecureChannel *
399
selectHttpChannel(UA_HttpListener *listener, UA_SecurityPolicy *policy,
400
                  UA_ServiceDescription *sd, const UA_Request *request,
401
                  UA_SecureChannelEncoding encoding,
402
0
                  const UA_String *remoteAddress) {
403
0
    UA_HttpProtocolManager *hpm = listener->manager;
404
0
    UA_Server *server = hpm->drv.server;
405
0
    const UA_NodeId *token = &request->requestHeader.authenticationToken;
406
0
    if(!UA_NodeId_isNull(token)) {
407
        /* Resolve only the routing association here. The service layer applies
408
         * the authoritative timeout and channel-binding checks afterwards. */
409
0
        UA_Session *session = findSessionByToken(server, token);
410
0
        UA_SecureChannel *channel = session ? session->channel : NULL;
411
0
        if(channel && httpChannelMatchesListener(listener, channel) &&
412
0
           channel->state == UA_SECURECHANNELSTATE_OPEN &&
413
0
           channel->securityPolicy == policy && channel->encoding == encoding)
414
0
            return channel;
415
0
    }
416
417
0
    if(sd->requestType == &UA_TYPES[UA_TYPES_CREATESESSIONREQUEST]) {
418
0
        const UA_CreateSessionRequest *csr =
419
0
            (const UA_CreateSessionRequest *)request;
420
0
        if(policy->policyType != UA_SECURITYPOLICYTYPE_NONE &&
421
0
           csr->clientCertificate.length == 0)
422
0
            return NULL;
423
        /* An application certificate identifies an application, not a client
424
         * instance. Every CreateSession therefore starts a distinct logical
425
         * channel. The authentication token routes later HTTP requests back
426
         * to this channel. */
427
0
        return newHttpChannel(listener, policy, &csr->clientCertificate,
428
0
                              encoding, remoteAddress);
429
0
    }
430
431
0
    UA_SecureChannel *channel;
432
0
    TAILQ_FOREACH(channel, &server->channels, serverEntry) {
433
0
        if(httpChannelMatchesListener(listener, channel) &&
434
0
           channel->state == UA_SECURECHANNELSTATE_OPEN && !channel->sessions &&
435
0
           channel->securityPolicy == policy &&
436
0
           channel->encoding == encoding)
437
0
            return channel;
438
0
    }
439
0
    return newHttpChannel(listener, policy, NULL, encoding, remoteAddress);
440
0
}
441
442
/* Physical HTTPS connections have an unpredictable lifetime. The opaque
443
 * AuthenticationToken binds later requests to the logical client channel. */
444
static UA_StatusCode
445
setHttpAuthenticationToken(UA_Session *session,
446
                            UA_CreateSessionResponse *response,
447
0
                            const UA_ByteString *requestRandom) {
448
0
    if(!requestRandom || requestRandom->length < 32)
449
0
        return UA_STATUSCODE_BADINTERNALERROR;
450
451
0
    UA_NodeId token = UA_NODEID_NULL;
452
0
    token.namespaceIndex = 1;
453
0
    token.identifierType = UA_NODEIDTYPE_BYTESTRING;
454
0
    UA_ByteString random = {32, requestRandom->data};
455
0
    UA_StatusCode res =
456
0
        UA_ByteString_copy(&random, &token.identifier.byteString);
457
0
    if(res != UA_STATUSCODE_GOOD)
458
0
        return res;
459
460
0
    UA_NodeId responseAuthenticationToken = UA_NODEID_NULL;
461
0
    res = UA_NodeId_copy(&token, &responseAuthenticationToken);
462
0
    if(res != UA_STATUSCODE_GOOD) {
463
0
        UA_NodeId_clear(&token);
464
0
        return res;
465
0
    }
466
0
    UA_NodeId_clear(&session->authenticationToken);
467
0
    session->authenticationToken = token;
468
0
    UA_NodeId_clear(&response->authenticationToken);
469
0
    response->authenticationToken = responseAuthenticationToken;
470
0
    return UA_STATUSCODE_GOOD;
471
0
}
472
473
/* Service-message decoding */
474
475
static UA_StatusCode
476
processDecodedHttpRequest(UA_HttpListener *listener,
477
                          UA_HttpServerSequence *sequence,
478
                          UA_SecureChannelEncoding encoding,
479
                          UA_SecurityPolicy *policy,
480
                          UA_ServiceDescription *sd, UA_Request *request,
481
                          const UA_String *remoteAddress,
482
0
                          const UA_ByteString *requestRandom) {
483
0
    UA_HttpProtocolManager *hpm = listener->manager;
484
0
    UA_Server *server = hpm->drv.server;
485
0
    if(sequence->channel)
486
0
        return UA_STATUSCODE_BADINVALIDSTATE;
487
0
    UA_SecureChannel *channel =
488
0
        selectHttpChannel(listener, policy, sd, request, encoding,
489
0
                          remoteAddress);
490
0
    if(!channel)
491
0
        return UA_STATUSCODE_BADSECURITYCHECKSFAILED;
492
0
    sequence->channel = channel;
493
0
    sequence->requestHandle = request->requestHeader.requestHandle;
494
0
    UA_UInt64 responseToken = (UA_UInt64)sequence->connectionId;
495
496
0
    UA_Response response;
497
0
    UA_Boolean done = processDecodedServiceRequest(
498
0
        server, channel, responseToken, sd, request, &response);
499
0
    UA_Boolean createSession =
500
0
        sd->requestType == &UA_TYPES[UA_TYPES_CREATESESSIONREQUEST];
501
0
    if(createSession) {
502
0
        if(done && response.responseHeader.serviceResult == UA_STATUSCODE_GOOD) {
503
0
            UA_CreateSessionResponse *csr =
504
0
                (UA_CreateSessionResponse *)&response;
505
0
            UA_Session *session =
506
0
                getSessionByToken(server, &csr->authenticationToken);
507
0
            if(!session) {
508
0
                response.responseHeader.serviceResult =
509
0
                    UA_STATUSCODE_BADINTERNALERROR;
510
0
            } else {
511
0
                UA_StatusCode tokenResult =
512
0
                    setHttpAuthenticationToken(session, csr, requestRandom);
513
0
                if(tokenResult != UA_STATUSCODE_GOOD)
514
0
                    UA_Session_remove(server, session,
515
0
                                      UA_SHUTDOWNREASON_REJECT);
516
0
                response.responseHeader.serviceResult = tokenResult;
517
0
            }
518
0
        }
519
0
        if(!done || response.responseHeader.serviceResult != UA_STATUSCODE_GOOD)
520
0
            channel->state = UA_SECURECHANNELSTATE_CLOSING;
521
0
    }
522
0
    UA_StatusCode res = UA_STATUSCODE_GOOD;
523
0
    if(done)
524
0
        res = sendResponse(server, channel, responseToken, &response,
525
0
                           sd->responseType);
526
0
    UA_clear(&response, sd->responseType);
527
0
    return res;
528
0
}
529
530
static UA_StatusCode
531
processHttpBody(UA_HttpListener *listener, UA_HttpServerSequence *sequence,
532
                UA_SecurityPolicy *policy, UA_SecureChannelEncoding encoding,
533
                const UA_ByteString *body, const UA_String *remoteAddress,
534
0
                const UA_ByteString *requestRandom) {
535
0
    UA_Server *server = listener->manager->drv.server;
536
0
    if(encoding == UA_SECURECHANNEL_ENCODING_BINARY) {
537
        /* Binary service decoding is shared with the TCP transport. */
538
0
        UA_Request request;
539
0
        UA_ServiceDescription *sd = NULL;
540
0
        UA_StatusCode res = decodeBinaryServiceRequest(
541
0
            server, body, &sd, &request, NULL, NULL);
542
0
        if(res != UA_STATUSCODE_GOOD)
543
0
            return res;
544
0
        res = processDecodedHttpRequest(
545
0
            listener, sequence, encoding, policy, sd, &request,
546
0
            remoteAddress, requestRandom);
547
0
        UA_clear(&request, sd->requestType);
548
0
        return res;
549
0
    }
550
551
#ifndef UA_ENABLE_JSON_ENCODING
552
    return UA_STATUSCODE_BADNOTSUPPORTED;
553
#else
554
0
    if(encoding != UA_SECURECHANNEL_ENCODING_JSON)
555
0
        return UA_STATUSCODE_BADNOTSUPPORTED;
556
557
    /* Decode the JSON service envelope and resolve its request type. */
558
0
    UA_DecodeJsonOptions options;
559
0
    memset(&options, 0, sizeof(options));
560
0
    options.customTypes = serverCustomTypes(server);
561
0
    UA_STACKARRAY(UA_UInt16, identity, server->namespacesSize);
562
0
    for(size_t i = 0; i < server->namespacesSize; i++)
563
0
        identity[i] = (UA_UInt16)i;
564
0
    UA_NamespaceMapping mapping = {
565
0
        server->namespaces, server->namespacesSize,
566
0
        identity, server->namespacesSize,
567
0
        identity, server->namespacesSize};
568
0
    options.namespaceMapping = &mapping;
569
0
    UA_ExtensionObject envelope;
570
0
    UA_ExtensionObject_init(&envelope);
571
0
    UA_StatusCode res =
572
0
        UA_decodeJson(body, &envelope, &UA_TYPES[UA_TYPES_EXTENSIONOBJECT],
573
0
                      &options);
574
0
    if(res == UA_STATUSCODE_GOOD) {
575
0
        if(envelope.encoding != UA_EXTENSIONOBJECT_DECODED ||
576
0
           !envelope.content.decoded.type ||
577
0
           !envelope.content.decoded.data) {
578
0
            res = UA_STATUSCODE_BADSERVICEUNSUPPORTED;
579
0
        } else {
580
0
            const UA_DataType *requestType = envelope.content.decoded.type;
581
0
            UA_ServiceDescription *sd = NULL;
582
0
            if(requestType->binaryEncodingId.namespaceIndex == 0 &&
583
0
               requestType->binaryEncodingId.identifierType ==
584
0
                   UA_NODEIDTYPE_NUMERIC)
585
0
                sd = getServiceDescription(
586
0
                    requestType->binaryEncodingId.identifier.numeric);
587
0
            if(!sd || sd->requestType != requestType)
588
0
                res = UA_STATUSCODE_BADSERVICEUNSUPPORTED;
589
0
            else
590
0
                res = processDecodedHttpRequest(
591
0
                    listener, sequence, encoding, policy, sd,
592
0
                    (UA_Request *)envelope.content.decoded.data,
593
0
                    remoteAddress, requestRandom);
594
0
        }
595
0
    }
596
0
    UA_ExtensionObject_clear(&envelope);
597
0
    return res;
598
0
#endif
599
0
}
600
601
/* HTTP request routing */
602
603
static UA_Boolean
604
0
stripPathSuffix(UA_String *path, const UA_String *suffix) {
605
0
    if(path->length < suffix->length ||
606
0
       memcmp(&path->data[path->length - suffix->length], suffix->data,
607
0
              suffix->length) != 0)
608
0
        return false;
609
0
    path->length -= suffix->length;
610
0
    return true;
611
0
}
612
613
/* OPC UA HTTP clients conventionally append "/discovery" while selecting an
614
 * endpoint. The discovery request still belongs to the configured endpoint;
615
 * the returned EndpointUrl remains the canonical variant URL. */
616
static UA_Boolean
617
findEndpointEncoding(UA_HttpListener *listener, const UA_String *path,
618
0
                     UA_SecureChannelEncoding *encoding) {
619
0
    static const UA_String discovery = UA_STRING_STATIC("/discovery");
620
0
    UA_String hostname = UA_STRING_NULL;
621
0
    UA_String endpointPath = UA_STRING_NULL;
622
0
    UA_UInt16 port = 0;
623
0
    if(UA_parseEndpointUrl(&listener->endpointUrl, &hostname, &port,
624
0
                           &endpointPath) != UA_STATUSCODE_GOOD)
625
0
        return false;
626
627
0
    UA_String route = *path;
628
0
    stripPathSuffix(&route, &discovery);
629
0
#ifdef UA_ENABLE_JSON_ENCODING
630
0
    static const UA_String json = UA_STRING_STATIC("/json");
631
0
    *encoding = stripPathSuffix(&route, &json) ?
632
0
        UA_SECURECHANNEL_ENCODING_JSON : UA_SECURECHANNEL_ENCODING_BINARY;
633
#else
634
    *encoding = UA_SECURECHANNEL_ENCODING_BINARY;
635
#endif
636
0
    static const UA_String root = UA_STRING_STATIC("/");
637
0
    if(route.length == 0 && endpointPath.length == 0)
638
0
        route = root;
639
640
    /* Match the remaining route to the configured Binary endpoint. */
641
0
    if(route.length != endpointPath.length + 1 || route.data[0] != '/')
642
0
        return false;
643
0
    return endpointPath.length == 0 ||
644
0
           memcmp(&route.data[1], endpointPath.data,
645
0
                  endpointPath.length) == 0;
646
0
}
647
648
static UA_UInt16
649
0
httpStatusForProcessingError(UA_StatusCode status) {
650
0
    switch(status) {
651
0
    case UA_STATUSCODE_BADDECODINGERROR:
652
0
    case UA_STATUSCODE_BADSECURITYCHECKSFAILED:
653
0
    case UA_STATUSCODE_BADSECURITYPOLICYREJECTED:
654
0
    case UA_STATUSCODE_BADREQUESTTYPEINVALID:
655
0
        return 400;
656
0
    case UA_STATUSCODE_BADREQUESTTOOLARGE:
657
0
        return 413;
658
0
    case UA_STATUSCODE_BADNOTSUPPORTED:
659
0
        return 406;
660
0
    case UA_STATUSCODE_BADINVALIDSTATE:
661
0
    case UA_STATUSCODE_BADNOTFOUND:
662
0
    case UA_STATUSCODE_BADCONNECTIONCLOSED:
663
0
        return 0; /* The request is gone or already responding. */
664
0
    case UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED:
665
        /* At this layer this can also originate while encoding the response.
666
         * Do not misreport an internal response failure as a client error. */
667
0
        return 500;
668
0
    default:
669
0
        return 500;
670
0
    }
671
0
}
672
673
static void
674
sendHttpStatusResponse(UA_HttpProtocolManager *hpm,
675
                       UA_HttpServerSequence *sequence,
676
0
                       UA_UInt16 status) {
677
0
    UA_StatusCode res = UA_Http_sendResponse(
678
0
        hpm->connectionManager, sequence->connectionId, status,
679
0
        NULL, NULL, NULL);
680
0
    (void)finishHttpSequenceResponse(hpm, sequence, res);
681
0
}
682
683
static void
684
270
finishHttpStopIfDrained(UA_HttpProtocolManager *hpm) {
685
270
    if(hpm->drv.state != UA_LIFECYCLESTATE_STOPPING ||
686
270
       !LIST_EMPTY(&hpm->sequences))
687
0
        return;
688
810
    for(size_t i = 0; i < UA_HTTP_LISTENERS_SIZE; i++) {
689
540
        UA_HttpListener *listener = &hpm->listeners[i];
690
540
        if(listener->connectionId != 0)
691
0
            return;
692
540
    }
693
270
    shutdownHttpChannels(hpm, NULL, UA_SHUTDOWNREASON_PURGE);
694
270
    hpm->drv.state = UA_LIFECYCLESTATE_STOPPED;
695
270
}
696
697
static void
698
processHttpRequest(UA_HttpListener *listener,
699
                   UA_HttpServerSequence *sequence,
700
0
                   const UA_KeyValueMap *params, UA_ByteString msg) {
701
0
    UA_HttpProtocolManager *hpm = listener->manager;
702
0
    uintptr_t connectionId = sequence->connectionId;
703
0
    const UA_String *method = getStringParam(params, "method");
704
0
    const UA_String *path = getStringParam(params, "path");
705
706
0
    const UA_String post = UA_STRING_STATIC("POST");
707
0
    UA_Boolean duplicateContentType = false, invalidContentType = false;
708
0
    const UA_String *contentType =
709
0
        UA_Http_getHeader(params, "content-type", &duplicateContentType,
710
0
                          &invalidContentType);
711
0
    UA_Boolean duplicateContentEncoding = false;
712
0
    UA_Boolean invalidContentEncoding = false;
713
0
    const UA_String *contentEncoding =
714
0
        UA_Http_getHeader(params, "content-encoding",
715
0
                          &duplicateContentEncoding,
716
0
                          &invalidContentEncoding);
717
0
    const UA_ByteString *requestRandom =
718
0
        (const UA_ByteString *)UA_KeyValueMap_getScalar(
719
0
            params, UA_QUALIFIEDNAME(0, "request-random"),
720
0
            &UA_TYPES[UA_TYPES_BYTESTRING]);
721
0
    const UA_String *remoteAddress = getStringParam(params, "remote-address");
722
0
    UA_UInt16 status = 0;
723
0
    UA_SecureChannelEncoding encoding = UA_SECURECHANNEL_ENCODING_BINARY;
724
0
    if(duplicateContentType || invalidContentType ||
725
0
       duplicateContentEncoding || invalidContentEncoding)
726
0
        status = 400;
727
0
    else if(!method || !UA_String_equal(method, &post))
728
0
        status = 405;
729
0
    else if(!path)
730
0
        status = 404;
731
0
    else if(!findEndpointEncoding(listener, path, &encoding))
732
0
        status = 404;
733
0
    else {
734
        /* Validate the selected endpoint representation. */
735
0
        if(!UA_Http_contentTypeMatchesEncoding(contentType, encoding)) {
736
0
            status = 415;
737
0
        } else if(contentEncoding &&
738
0
                  !UA_Http_headerValueEquals(contentEncoding, "identity") &&
739
0
                  (encoding == UA_SECURECHANNEL_ENCODING_BINARY ||
740
0
                   (!UA_Http_headerValueEquals(contentEncoding, "gzip") &&
741
0
                    !UA_Http_headerValueEquals(contentEncoding, "x-gzip")))) {
742
0
            status = 415;
743
0
        }
744
0
    }
745
746
    /* The absent header selects SecurityPolicy None. Application certificate
747
     * validation for secure policies happens in CreateSession, above TLS. */
748
0
    UA_Boolean duplicatePolicy = false, invalidPolicy = false;
749
0
    const UA_String *policyUri =
750
0
        UA_Http_getHeader(params, "opcua-securitypolicy", &duplicatePolicy,
751
0
                          &invalidPolicy);
752
0
    if(duplicatePolicy || invalidPolicy)
753
0
        status = 400;
754
0
    if(!policyUri)
755
0
        policyUri = &UA_SECURITY_POLICY_NONE_URI;
756
0
    UA_SecurityPolicy *policy =
757
0
        getSecurityPolicyByUri(hpm->drv.server, policyUri);
758
0
    if(!policy)
759
0
        status = 400;
760
0
    else if(!isSecureHttpListener(listener) &&
761
0
            policy->policyType != UA_SECURITYPOLICYTYPE_NONE)
762
0
        status = 400;
763
764
0
    if(status != 0) {
765
0
        sendHttpStatusResponse(hpm, sequence, status);
766
0
        return;
767
0
    }
768
769
0
    UA_StatusCode res = processHttpBody(
770
0
        listener, sequence, policy, encoding, &msg,
771
0
        remoteAddress, requestRandom);
772
0
    if(res == UA_STATUSCODE_GOOD)
773
0
        return;
774
775
0
    UA_LOG_DEBUG(hpm->drv.server->config.logging, UA_LOGCATEGORY_NETWORK,
776
0
                 "OPC HTTP request decoding or dispatch failed: %s",
777
0
                 UA_StatusCode_name(res));
778
0
    UA_UInt16 errorStatus = httpStatusForProcessingError(res);
779
0
    if(errorStatus)
780
0
        sendHttpStatusResponse(hpm, sequence, errorStatus);
781
0
    else
782
0
        hpm->connectionManager->closeConnection(hpm->connectionManager,
783
0
                                                connectionId);
784
0
}
785
786
/* Connection lifecycle */
787
788
static void httpNetworkCallback(UA_ConnectionManager *cm,
789
                                uintptr_t connectionId, void *application,
790
                                void **connectionContext,
791
                                UA_ConnectionState state,
792
                                const UA_KeyValueMap *params,
793
0
                                UA_ByteString msg) {
794
0
    UA_HttpListener *listener = (UA_HttpListener *)application;
795
0
    UA_HttpProtocolManager *hpm = listener->manager;
796
0
    UA_assert(cm == hpm->connectionManager);
797
0
    lockServer(hpm->drv.server);
798
799
0
    if(state == UA_CONNECTIONSTATE_CLOSING) {
800
0
        if(listener->connectionId == connectionId) {
801
0
            shutdownHttpChannels(hpm, listener, UA_SHUTDOWNREASON_CLOSE);
802
0
            unregisterHttpDiscoveryUrl(listener);
803
0
            listener->connectionId = 0;
804
0
        } else {
805
0
            UA_HttpServerSequence *sequence =
806
0
                (UA_HttpServerSequence *)*connectionContext;
807
0
            if(sequence && sequence->connectionId == connectionId) {
808
                /* Tear down the accepted sequence and abandon its response. */
809
0
                *connectionContext = NULL;
810
0
                if(sequence->channel)
811
0
                    abandonServiceRequest(
812
0
                        hpm->drv.server, sequence->channel,
813
0
                        (UA_UInt64)sequence->connectionId);
814
0
                releaseHttpSequenceRequest(hpm, sequence);
815
0
                LIST_REMOVE(sequence, next);
816
0
                UA_free(sequence);
817
0
            }
818
0
        }
819
0
        finishHttpStopIfDrained(hpm);
820
0
        unlockServer(hpm->drv.server);
821
0
        return;
822
0
    }
823
824
0
    const UA_UInt16 *listenPort =
825
0
        (const UA_UInt16 *)UA_KeyValueMap_getScalar(
826
0
            params, UA_QUALIFIEDNAME(0, "listen-port"),
827
0
            &UA_TYPES[UA_TYPES_UINT16]);
828
0
    UA_Boolean listenerEvent =
829
0
        (state == UA_CONNECTIONSTATE_OPENING || listenPort != NULL);
830
0
    if(listenerEvent &&
831
0
       (listener->connectionId == 0 ||
832
0
        connectionId == listener->connectionId) &&
833
0
       (state == UA_CONNECTIONSTATE_OPENING ||
834
0
        state == UA_CONNECTIONSTATE_ESTABLISHED)) {
835
0
        listener->connectionId = connectionId;
836
0
        UA_StatusCode res = listenPort ? setEndpointUrl(listener, *listenPort)
837
0
                                       : UA_STATUSCODE_GOOD;
838
0
        if(res != UA_STATUSCODE_GOOD) {
839
0
            UA_LOG_ERROR(hpm->drv.server->config.logging,
840
0
                         UA_LOGCATEGORY_SERVER,
841
0
                         "Constructing the OPC HTTP endpoint URL failed: %s",
842
0
                         UA_StatusCode_name(res));
843
0
            cm->closeConnection(cm, connectionId);
844
0
        }
845
0
        unlockServer(hpm->drv.server);
846
0
        return;
847
0
    }
848
849
0
    if(state != UA_CONNECTIONSTATE_ESTABLISHED) {
850
0
        unlockServer(hpm->drv.server);
851
0
        return;
852
0
    }
853
854
0
    const UA_String *method = getStringParam(params, "method");
855
0
    if(!method) {
856
        /* Attach reusable server-side storage to the newly accepted socket
857
         * before its first request arrives. */
858
0
        UA_assert(*connectionContext == NULL);
859
0
        UA_HttpServerSequence *sequence =
860
0
            (UA_HttpServerSequence*)UA_calloc(1, sizeof(*sequence));
861
0
        if(!sequence) {
862
0
            cm->closeConnection(cm, connectionId);
863
0
            unlockServer(hpm->drv.server);
864
0
            return;
865
0
        }
866
0
        sequence->connectionId = connectionId;
867
0
        LIST_INSERT_HEAD(&hpm->sequences, sequence, next);
868
0
        *connectionContext = sequence;
869
0
        unlockServer(hpm->drv.server);
870
0
        return;
871
0
    }
872
873
0
    UA_HttpServerSequence *sequence =
874
0
        (UA_HttpServerSequence *)*connectionContext;
875
0
    if(!sequence || sequence->connectionId != connectionId) {
876
0
        cm->closeConnection(cm, connectionId);
877
0
        unlockServer(hpm->drv.server);
878
0
        return;
879
0
    }
880
0
    if(sequence->channel) {
881
0
        UA_LOG_DEBUG(hpm->drv.server->config.logging,
882
0
                     UA_LOGCATEGORY_NETWORK,
883
0
                     "Rejecting overlapping request on HTTP connection %" PRIuPTR,
884
0
                     connectionId);
885
0
        cm->closeConnection(cm, connectionId);
886
0
        unlockServer(hpm->drv.server);
887
0
        return;
888
0
    }
889
0
    processHttpRequest(listener, sequence, params, msg);
890
0
    unlockServer(hpm->drv.server);
891
0
}
892
893
/* Listener and driver lifecycle */
894
895
static UA_StatusCode
896
0
openHttpListener(UA_HttpProtocolManager *hpm, const UA_String *serverUrl) {
897
0
    if(!hpm->connectionManager)
898
0
        return UA_STATUSCODE_BADNOTFOUND;
899
0
    UA_String hostname = UA_STRING_NULL;
900
0
    UA_String path = UA_STRING_NULL;
901
0
    UA_Boolean secure = false;
902
0
    UA_UInt16 port = 0;
903
0
    UA_StatusCode res = parseHttpServerUrl(
904
0
        serverUrl, &secure, &hostname, &port, &path);
905
0
    if(res != UA_STATUSCODE_GOOD)
906
0
        return res;
907
908
0
    UA_HttpListener *listener = &hpm->listeners[secure ? 1 : 0];
909
0
    UA_assert(listener->endpointUrl.length == 0);
910
0
    res = formatEndpointUrl(listener, &hostname, port, &path,
911
0
                            &listener->endpointUrl);
912
0
    if(res != UA_STATUSCODE_GOOD)
913
0
        return res;
914
915
0
    UA_ServerConfig *config = &hpm->drv.server->config;
916
0
    const UA_String *listenAddress = &hostname;
917
0
    if(config->httpListenAddress.data)
918
0
        listenAddress = &config->httpListenAddress;
919
0
    UA_KeyValuePair p[11] = {0};
920
0
    size_t n = 0;
921
0
    UA_Boolean yes = true;
922
0
#define ADD_HTTP_PARAM(NAME, VALUE, TYPE)                                      \
923
0
    do {                                                                       \
924
0
        p[n].key = UA_QUALIFIEDNAME(0, NAME);                                  \
925
0
        UA_Variant_setScalar(&p[n++].value, VALUE, &UA_TYPES[TYPE]);           \
926
0
    } while(0)
927
0
    ADD_HTTP_PARAM("port", &port, UA_TYPES_UINT16);
928
0
    ADD_HTTP_PARAM("listen", &yes, UA_TYPES_BOOLEAN);
929
0
    ADD_HTTP_PARAM("useSSL", &secure, UA_TYPES_BOOLEAN);
930
0
    ADD_HTTP_PARAM("timeout", &config->httpTimeout, UA_TYPES_UINT16);
931
0
    ADD_HTTP_PARAM("recv-max-message-size", &config->httpMaxMsgSize,
932
0
                   UA_TYPES_UINT32);
933
0
    ADD_HTTP_PARAM("recv-max-decompressed-message-size",
934
0
                   &config->httpMaxDecompressedMsgSize, UA_TYPES_UINT32);
935
0
    ADD_HTTP_PARAM("send-max-message-size", &config->httpMaxMsgSize,
936
0
                   UA_TYPES_UINT32);
937
0
    if(secure) {
938
0
        ADD_HTTP_PARAM("certificate", &config->httpCertificate,
939
0
                       UA_TYPES_BYTESTRING);
940
0
        ADD_HTTP_PARAM("private-key", &config->httpPrivateKey,
941
0
                       UA_TYPES_BYTESTRING);
942
0
    }
943
0
    if(listenAddress->length > 0)
944
0
        ADD_HTTP_PARAM("address", (void *)(uintptr_t)listenAddress,
945
0
                       UA_TYPES_STRING);
946
0
    if(secure && config->httpPrivateKeyPassword.length > 0)
947
0
        ADD_HTTP_PARAM("private-key-password", &config->httpPrivateKeyPassword,
948
0
                       UA_TYPES_STRING);
949
0
#undef ADD_HTTP_PARAM
950
0
    UA_KeyValueMap map = {n, p};
951
0
    res = hpm->connectionManager->openConnection(
952
0
        hpm->connectionManager, &map, listener, NULL, httpNetworkCallback);
953
0
    if(res != UA_STATUSCODE_GOOD) {
954
0
        UA_LOG_ERROR(config->logging, UA_LOGCATEGORY_SERVER,
955
0
                     "Opening the OPC HTTP listener failed: %s",
956
0
                     UA_StatusCode_name(res));
957
0
        UA_String_clear(&listener->endpointUrl);
958
0
    }
959
0
    return res;
960
0
}
961
962
12.5k
static void clearHttpListeners(UA_HttpProtocolManager *hpm) {
963
    /* Release the endpoint metadata owned by each listener. */
964
37.7k
    for(size_t i = 0; i < UA_HTTP_LISTENERS_SIZE; i++) {
965
25.1k
        UA_HttpListener *listener = &hpm->listeners[i];
966
25.1k
        UA_assert(listener->connectionId == 0);
967
25.1k
        unregisterHttpDiscoveryUrl(listener);
968
25.1k
        UA_String_clear(&listener->endpointUrl);
969
25.1k
    }
970
12.5k
    hpm->connectionManager = NULL;
971
12.5k
}
972
973
static void
974
270
closeHttpListeners(UA_HttpProtocolManager *hpm) {
975
810
    for(size_t i = 0; i < UA_HTTP_LISTENERS_SIZE; i++) {
976
540
        UA_HttpListener *listener = &hpm->listeners[i];
977
540
        if(listener->connectionId)
978
0
            hpm->connectionManager->closeConnection(hpm->connectionManager,
979
0
                                                    listener->connectionId);
980
540
    }
981
270
}
982
983
270
static UA_StatusCode startHttp(UA_Driver *drv) {
984
270
    UA_HttpProtocolManager *hpm = (UA_HttpProtocolManager *)drv;
985
270
    UA_ServerConfig *config = &drv->server->config;
986
    /* Listener callbacks are finished while the driver is stopped. Discard
987
     * their records before recreating the current configuration. */
988
270
    clearHttpListeners(hpm);
989
270
    if(!config->httpEnabled) {
990
270
        drv->state = UA_LIFECYCLESTATE_STARTED;
991
270
        return UA_STATUSCODE_GOOD;
992
270
    }
993
994
    /* Resolve the HTTP ConnectionManager. */
995
0
    const UA_String protocol = UA_STRING_STATIC("http");
996
0
    hpm->connectionManager =
997
0
        findConnectionManager(config->eventLoop, &protocol);
998
0
    if(!hpm->connectionManager) {
999
0
        UA_LOG_ERROR(config->logging, UA_LOGCATEGORY_SERVER,
1000
0
                     "No HTTP ConnectionManager is configured");
1001
0
        return UA_STATUSCODE_BADNOTFOUND;
1002
0
    }
1003
0
    UA_StatusCode res = UA_STATUSCODE_BADNOTFOUND;
1004
0
    for(size_t i = 0; i < config->serverUrlsSize; i++) {
1005
0
        if(!getHttpUrlSecurity(&config->serverUrls[i], NULL))
1006
0
            continue;
1007
0
        res = openHttpListener(hpm, &config->serverUrls[i]);
1008
0
        if(res != UA_STATUSCODE_GOOD) {
1009
            /* The configured listeners are one feature. Never leave a
1010
             * partially available OPC HTTP endpoint set behind. */
1011
0
            drv->state = UA_LIFECYCLESTATE_STOPPING;
1012
0
            closeHttpListeners(hpm);
1013
0
            finishHttpStopIfDrained(hpm);
1014
0
            return res;
1015
0
        }
1016
0
    }
1017
0
    if(res != UA_STATUSCODE_GOOD) {
1018
0
        UA_LOG_ERROR(config->logging, UA_LOGCATEGORY_SERVER,
1019
0
                     "Could not open an OPC HTTP listener");
1020
0
        shutdownHttpChannels(hpm, NULL, UA_SHUTDOWNREASON_PURGE);
1021
0
        return UA_STATUSCODE_BADINTERNALERROR;
1022
0
    }
1023
0
    drv->state = UA_LIFECYCLESTATE_STARTED;
1024
0
    return UA_STATUSCODE_GOOD;
1025
0
}
1026
1027
270
static void stopHttp(UA_Driver *drv) {
1028
270
    UA_HttpProtocolManager *hpm = (UA_HttpProtocolManager *)drv;
1029
270
    drv->state = UA_LIFECYCLESTATE_STOPPING;
1030
810
    for(size_t i = 0; i < UA_HTTP_LISTENERS_SIZE; i++)
1031
540
        shutdownHttpChannels(hpm, &hpm->listeners[i],
1032
540
                             UA_SHUTDOWNREASON_CLOSE);
1033
270
    closeHttpListeners(hpm);
1034
270
    finishHttpStopIfDrained(hpm);
1035
270
}
1036
1037
/* startHttp opens all listeners or none. A disabled transport is started
1038
 * without a ConnectionManager. */
1039
UA_Boolean
1040
50
UA_HttpProtocolManager_isListening(const UA_Driver *drv) {
1041
50
    const UA_HttpProtocolManager *hpm = (const UA_HttpProtocolManager *)drv;
1042
50
    return drv->state == UA_LIFECYCLESTATE_STARTED &&
1043
50
        hpm->connectionManager != NULL;
1044
50
}
1045
1046
12.3k
static UA_StatusCode freeHttp(UA_Driver *drv) {
1047
12.3k
    if(drv->state != UA_LIFECYCLESTATE_STOPPED)
1048
0
        return UA_STATUSCODE_BADINTERNALERROR;
1049
12.3k
    UA_HttpProtocolManager *hpm = (UA_HttpProtocolManager *)drv;
1050
12.3k
    clearHttpListeners(hpm);
1051
12.3k
    UA_free(hpm);
1052
12.3k
    return UA_STATUSCODE_GOOD;
1053
12.3k
}
1054
1055
12.3k
UA_Driver *UA_HttpProtocolManager_new(void) {
1056
12.3k
    UA_HttpProtocolManager *hpm =
1057
12.3k
        (UA_HttpProtocolManager *)UA_calloc(1, sizeof(*hpm));
1058
12.3k
    if(!hpm)
1059
0
        return NULL;
1060
36.9k
    for(size_t i = 0; i < UA_HTTP_LISTENERS_SIZE; i++)
1061
24.6k
        hpm->listeners[i].manager = hpm;
1062
12.3k
    hpm->drv.name = UA_STRING("opc-http");
1063
12.3k
    hpm->drv.start = startHttp;
1064
12.3k
    hpm->drv.stop = stopHttp;
1065
12.3k
    hpm->drv.free = freeHttp;
1066
12.3k
    return &hpm->drv;
1067
12.3k
}