typedef struct
{
gint state;
+ GstRTSPResult status;
guint8 buffer[4096];
guint offset;
}
static GstRTSPResult
+parse_protocol_version (gchar * protocol, GstRTSPMsgType * type,
+ GstRTSPVersion * version)
+{
+ GstRTSPResult res = GST_RTSP_OK;
+ gchar *ver;
+
+ if (G_LIKELY ((ver = strchr (protocol, '/')) != NULL)) {
+ guint major;
+ guint minor;
+ gchar dummychar;
+
+ *ver++ = '\0';
+
+ /* the version number must be formatted as X.Y with nothing following */
+ if (sscanf (ver, "%u.%u%c", &major, &minor, &dummychar) != 2)
+ res = GST_RTSP_EPARSE;
+
+ if (g_ascii_strcasecmp (protocol, "RTSP") == 0) {
+ if (major != 1 || minor != 0) {
+ *version = GST_RTSP_VERSION_INVALID;
+ res = GST_RTSP_ERROR;
+ }
+ } else if (g_ascii_strcasecmp (protocol, "HTTP") == 0) {
+ if (*type == GST_RTSP_MESSAGE_REQUEST)
+ *type = GST_RTSP_MESSAGE_HTTP_REQUEST;
+ else if (*type == GST_RTSP_MESSAGE_RESPONSE)
+ *type = GST_RTSP_MESSAGE_HTTP_RESPONSE;
+
+ if (major == 1 && minor == 1) {
+ *version = GST_RTSP_VERSION_1_1;
+ } else if (major != 1 || minor != 0) {
+ *version = GST_RTSP_VERSION_INVALID;
+ res = GST_RTSP_ERROR;
+ }
+ } else
+ res = GST_RTSP_EPARSE;
+ } else
+ res = GST_RTSP_EPARSE;
+
+ return res;
+}
+
+static GstRTSPResult
parse_response_status (guint8 * buffer, GstRTSPMessage * msg)
{
- GstRTSPResult res;
+ GstRTSPResult res = GST_RTSP_OK;
+ GstRTSPResult res2;
gchar versionstr[20];
gchar codestr[4];
gint code;
bptr = (gchar *) buffer;
parse_string (versionstr, sizeof (versionstr), &bptr);
+
parse_string (codestr, sizeof (codestr), &bptr);
code = atoi (codestr);
+ if (G_UNLIKELY (*codestr == '\0' || code < 0 || code >= 600))
+ res = GST_RTSP_EPARSE;
while (g_ascii_isspace (*bptr))
bptr++;
- if (strcmp (versionstr, "RTSP/1.0") == 0)
- GST_RTSP_CHECK (gst_rtsp_message_init_response (msg, code, bptr, NULL),
- parse_error);
- else if (strncmp (versionstr, "RTSP/", 5) == 0) {
- GST_RTSP_CHECK (gst_rtsp_message_init_response (msg, code, bptr, NULL),
- parse_error);
- msg->type_data.response.version = GST_RTSP_VERSION_INVALID;
- } else
- goto parse_error;
+ if (G_UNLIKELY (gst_rtsp_message_init_response (msg, code, bptr,
+ NULL) != GST_RTSP_OK))
+ res = GST_RTSP_EPARSE;
- return GST_RTSP_OK;
+ res2 = parse_protocol_version (versionstr, &msg->type,
+ &msg->type_data.response.version);
+ if (G_LIKELY (res == GST_RTSP_OK))
+ res = res2;
-parse_error:
- {
- return GST_RTSP_EPARSE;
- }
+ return res;
}
static GstRTSPResult
-parse_request_line (GstRTSPConnection * conn, guint8 * buffer,
- GstRTSPMessage * msg)
+parse_request_line (guint8 * buffer, GstRTSPMessage * msg)
{
GstRTSPResult res = GST_RTSP_OK;
+ GstRTSPResult res2;
gchar versionstr[20];
gchar methodstr[20];
gchar urlstr[4096];
gchar *bptr;
GstRTSPMethod method;
- GstRTSPTunnelState tstate = TUNNEL_STATE_NONE;
bptr = (gchar *) buffer;
parse_string (methodstr, sizeof (methodstr), &bptr);
method = gst_rtsp_find_method (methodstr);
- if (method == GST_RTSP_INVALID) {
- /* a tunnel request is allowed when we don't have one yet */
- if (conn->tstate != TUNNEL_STATE_NONE)
- goto invalid_method;
- /* we need GET or POST for a valid tunnel request */
- if (!strcmp (methodstr, "GET"))
- tstate = TUNNEL_STATE_GET;
- else if (!strcmp (methodstr, "POST"))
- tstate = TUNNEL_STATE_POST;
- else
- goto invalid_method;
- }
parse_string (urlstr, sizeof (urlstr), &bptr);
if (G_UNLIKELY (*urlstr == '\0'))
- goto invalid_url;
+ res = GST_RTSP_EPARSE;
parse_string (versionstr, sizeof (versionstr), &bptr);
if (G_UNLIKELY (*bptr != '\0'))
- goto invalid_version;
-
- if (strcmp (versionstr, "RTSP/1.0") == 0) {
- res = gst_rtsp_message_init_request (msg, method, urlstr);
- } else if (strncmp (versionstr, "RTSP/", 5) == 0) {
- res = gst_rtsp_message_init_request (msg, method, urlstr);
- msg->type_data.request.version = GST_RTSP_VERSION_INVALID;
- } else if (strcmp (versionstr, "HTTP/1.0") == 0) {
- /* tunnel request, we need a tunnel method */
- if (tstate == TUNNEL_STATE_NONE) {
- res = GST_RTSP_EPARSE;
- } else {
- conn->tstate = tstate;
- }
- } else {
res = GST_RTSP_EPARSE;
- }
- return res;
+ if (G_UNLIKELY (gst_rtsp_message_init_request (msg, method,
+ urlstr) != GST_RTSP_OK))
+ res = GST_RTSP_EPARSE;
- /* ERRORS */
-invalid_method:
- {
- GST_ERROR ("invalid method %s", methodstr);
- return GST_RTSP_EPARSE;
- }
-invalid_url:
- {
- GST_ERROR ("invalid url %s", urlstr);
- return GST_RTSP_EPARSE;
- }
-invalid_version:
- {
- GST_ERROR ("invalid version");
- return GST_RTSP_EPARSE;
+ res2 = parse_protocol_version (versionstr, &msg->type,
+ &msg->type_data.request.version);
+ if (G_LIKELY (res == GST_RTSP_OK))
+ res = res2;
+
+ if (G_LIKELY (msg->type == GST_RTSP_MESSAGE_REQUEST)) {
+ /* GET and POST are not allowed as RTSP methods */
+ if (msg->type_data.request.method == GST_RTSP_GET ||
+ msg->type_data.request.method == GST_RTSP_POST) {
+ msg->type_data.request.method = GST_RTSP_INVALID;
+ if (res == GST_RTSP_OK)
+ res = GST_RTSP_ERROR;
+ }
+ } else if (msg->type == GST_RTSP_MESSAGE_HTTP_REQUEST) {
+ /* only GET and POST are allowed as HTTP methods */
+ if (msg->type_data.request.method != GST_RTSP_GET &&
+ msg->type_data.request.method != GST_RTSP_POST) {
+ msg->type_data.request.method = GST_RTSP_INVALID;
+ if (res == GST_RTSP_OK)
+ res = GST_RTSP_ERROR;
+ }
}
+
+ return res;
}
static GstRTSPResult
/* parsing lines means reading a Key: Value pair */
static GstRTSPResult
-parse_line (GstRTSPConnection * conn, guint8 * buffer, GstRTSPMessage * msg)
+parse_line (guint8 * buffer, GstRTSPMessage * msg)
{
GstRTSPResult res;
gchar key[32];
if (G_UNLIKELY (res != GST_RTSP_OK))
goto parse_error;
- if (conn->tstate == TUNNEL_STATE_GET || conn->tstate == TUNNEL_STATE_POST) {
- /* save the tunnel session in the connection */
- if (!strcmp (key, "x-sessioncookie")) {
- strncpy (conn->tunnelid, value, TUNNELID_LEN);
- conn->tunnelid[TUNNELID_LEN - 1] = '\0';
- conn->tunneled = TRUE;
- }
- } else {
- field = gst_rtsp_find_header_field (key);
- if (field != GST_RTSP_HDR_INVALID)
- gst_rtsp_message_add_header (msg, field, value);
- }
+ field = gst_rtsp_find_header_field (key);
+ if (field != GST_RTSP_HDR_INVALID)
+ gst_rtsp_message_add_header (msg, field, value);
return GST_RTSP_OK;
/* we have a line */
if (builder->line == 0) {
/* first line, check for response status */
- if (memcmp (builder->buffer, "RTSP", 4) == 0) {
- res = parse_response_status (builder->buffer, message);
+ if (memcmp (builder->buffer, "RTSP", 4) == 0 ||
+ memcmp (builder->buffer, "HTTP", 4) == 0) {
+ builder->status = parse_response_status (builder->buffer, message);
} else {
- res = parse_request_line (conn, builder->buffer, message);
+ builder->status = parse_request_line (builder->buffer, message);
}
- /* the first line must parse without errors */
- if (res != GST_RTSP_OK)
- goto done;
} else {
- /* else just parse the line, ignore errors */
- parse_line (conn, builder->buffer, message);
+ /* else just parse the line */
+ res = parse_line (builder->buffer, message);
+ if (res != GST_RTSP_OK)
+ builder->status = res;
}
builder->line++;
builder->offset = 0;
}
case STATE_END:
{
+ gchar *session_cookie;
gchar *session_id;
- if (conn->tstate == TUNNEL_STATE_GET) {
- res = GST_RTSP_ETGET;
- goto done;
- } else if (conn->tstate == TUNNEL_STATE_POST) {
- res = GST_RTSP_ETPOST;
- goto done;
- }
-
if (message->type == GST_RTSP_MESSAGE_DATA) {
/* data messages don't have headers */
res = GST_RTSP_OK;
goto done;
}
+ /* save the tunnel session in the connection */
+ if (message->type == GST_RTSP_MESSAGE_HTTP_REQUEST &&
+ !conn->manual_http &&
+ conn->tstate == TUNNEL_STATE_NONE &&
+ gst_rtsp_message_get_header (message, GST_RTSP_HDR_X_SESSIONCOOKIE,
+ &session_cookie, 0) == GST_RTSP_OK) {
+ strncpy (conn->tunnelid, session_cookie, TUNNELID_LEN);
+ conn->tunnelid[TUNNELID_LEN - 1] = '\0';
+ conn->tunneled = TRUE;
+ }
+
/* save session id in the connection for further use */
if (message->type == GST_RTSP_MESSAGE_RESPONSE &&
gst_rtsp_message_get_header (message, GST_RTSP_HDR_SESSION,
strncpy (conn->session_id, session_id, maxlen);
conn->session_id[maxlen] = '\0';
}
- res = GST_RTSP_OK;
+ res = builder->status;
goto done;
}
default:
res = build_next (&builder, message, conn);
if (G_UNLIKELY (res == GST_RTSP_EEOF))
goto eof;
- if (G_LIKELY (res == GST_RTSP_OK))
- break;
- if (res == GST_RTSP_ETGET) {
- GString *str;
-
- /* tunnel GET request, we can reply now */
- str = gen_tunnel_reply (conn, GST_RTSP_STS_OK);
- res =
- gst_rtsp_connection_write (conn, (guint8 *) str->str, str->len,
- timeout);
- g_string_free (str, TRUE);
- } else if (res == GST_RTSP_ETPOST) {
- /* tunnel POST request, return the value, the caller now has to link the
- * two connections. */
+ else if (G_LIKELY (res == GST_RTSP_OK)) {
+ if (message->type == GST_RTSP_MESSAGE_HTTP_REQUEST) {
+ if (conn->tstate == TUNNEL_STATE_NONE &&
+ message->type_data.request.method == GST_RTSP_GET) {
+ GString *str;
+
+ conn->tstate = TUNNEL_STATE_GET;
+
+ /* tunnel GET request, we can reply now */
+ str = gen_tunnel_reply (conn, GST_RTSP_STS_OK);
+ res =
+ gst_rtsp_connection_write (conn, (guint8 *) str->str, str->len,
+ timeout);
+ g_string_free (str, TRUE);
+ res = GST_RTSP_ETGET;
+ goto cleanup;
+ } else if (conn->tstate == TUNNEL_STATE_NONE &&
+ message->type_data.request.method == GST_RTSP_POST) {
+ conn->tstate = TUNNEL_STATE_POST;
+
+ /* tunnel POST request, the caller now has to link the two
+ * connections. */
+ res = GST_RTSP_ETPOST;
+ goto cleanup;
+ } else {
+ res = GST_RTSP_EPARSE;
+ goto cleanup;
+ }
+ }
+
break;
} else if (G_UNLIKELY (res != GST_RTSP_EINTR))
goto read_error;
res = build_next (&watch->builder, &watch->message, watch->conn);
if (res == GST_RTSP_EINTR)
break;
- if (G_UNLIKELY (res == GST_RTSP_EEOF))
+ else if (G_UNLIKELY (res == GST_RTSP_EEOF))
goto eof;
- if (res == GST_RTSP_ETGET) {
- GString *str;
- GstRTSPStatusCode code;
- guint size;
-
- if (watch->funcs.tunnel_start)
- code = watch->funcs.tunnel_start (watch, watch->user_data);
- else
- code = GST_RTSP_STS_OK;
-
- /* queue the response string */
- str = gen_tunnel_reply (watch->conn, code);
- size = str->len;
- gst_rtsp_watch_queue_data (watch, (guint8 *) g_string_free (str, FALSE),
- size);
- } else if (res == GST_RTSP_ETPOST) {
- /* in the callback the connection should be tunneled with the
- * GET connection */
- if (watch->funcs.tunnel_complete)
- watch->funcs.tunnel_complete (watch, watch->user_data);
- } else if (G_UNLIKELY (res != GST_RTSP_OK))
- goto error;
+ else if (G_LIKELY (res == GST_RTSP_OK)) {
+ if (watch->message.type == GST_RTSP_MESSAGE_HTTP_REQUEST) {
+ if (watch->conn->tstate == TUNNEL_STATE_NONE &&
+ watch->message.type_data.request.method == GST_RTSP_GET) {
+ GString *str;
+ GstRTSPStatusCode code;
+ guint size;
+
+ watch->conn->tstate = TUNNEL_STATE_GET;
+
+ if (watch->funcs.tunnel_start)
+ code = watch->funcs.tunnel_start (watch, watch->user_data);
+ else
+ code = GST_RTSP_STS_OK;
+
+ /* queue the response string */
+ str = gen_tunnel_reply (watch->conn, code);
+ size = str->len;
+ gst_rtsp_watch_queue_data (watch, (guint8 *) g_string_free (str,
+ FALSE), size);
+ goto read_done;
+ } else if (watch->conn->tstate == TUNNEL_STATE_NONE &&
+ watch->message.type_data.request.method == GST_RTSP_POST) {
+ watch->conn->tstate = TUNNEL_STATE_POST;
+
+ /* in the callback the connection should be tunneled with the
+ * GET connection */
+ if (watch->funcs.tunnel_complete)
+ watch->funcs.tunnel_complete (watch, watch->user_data);
+ goto read_done;
+ } else {
+ res = GST_RTSP_ERROR;
+ }
+ }
+ }
if (G_LIKELY (res == GST_RTSP_OK)) {
if (watch->funcs.message_received)
watch->funcs.message_received (watch, &watch->message,
watch->user_data);
+ } else
+ goto error;
- gst_rtsp_message_unset (&watch->message);
- }
+ read_done:
+ gst_rtsp_message_unset (&watch->message);
build_reset (&watch->builder);
} while (FALSE);
}