Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(80)

Side by Side Diff: third_party/WebKit/Source/core/page/EventSource.cpp

Issue 1642563002: Introduce EventSourceParser (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@event-source-retry-fix
Patch Set: Created 4 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 /* 1 /*
2 * Copyright (C) 2009, 2012 Ericsson AB. All rights reserved. 2 * Copyright (C) 2009, 2012 Ericsson AB. All rights reserved.
3 * Copyright (C) 2010 Apple Inc. All rights reserved. 3 * Copyright (C) 2010 Apple Inc. All rights reserved.
4 * Copyright (C) 2011, Code Aurora Forum. All rights reserved. 4 * Copyright (C) 2011, Code Aurora Forum. All rights reserved.
5 * 5 *
6 * Redistribution and use in source and binary forms, with or without 6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions 7 * modification, are permitted provided that the following conditions
8 * are met: 8 * are met:
9 * 9 *
10 * 1. Redistributions of source code must retain the above copyright 10 * 1. Redistributions of source code must retain the above copyright
(...skipping 26 matching lines...) Expand all
37 #include "bindings/core/v8/SerializedScriptValue.h" 37 #include "bindings/core/v8/SerializedScriptValue.h"
38 #include "bindings/core/v8/SerializedScriptValueFactory.h" 38 #include "bindings/core/v8/SerializedScriptValueFactory.h"
39 #include "core/dom/Document.h" 39 #include "core/dom/Document.h"
40 #include "core/dom/ExceptionCode.h" 40 #include "core/dom/ExceptionCode.h"
41 #include "core/dom/ExecutionContext.h" 41 #include "core/dom/ExecutionContext.h"
42 #include "core/events/Event.h" 42 #include "core/events/Event.h"
43 #include "core/events/MessageEvent.h" 43 #include "core/events/MessageEvent.h"
44 #include "core/frame/LocalDOMWindow.h" 44 #include "core/frame/LocalDOMWindow.h"
45 #include "core/frame/LocalFrame.h" 45 #include "core/frame/LocalFrame.h"
46 #include "core/frame/csp/ContentSecurityPolicy.h" 46 #include "core/frame/csp/ContentSecurityPolicy.h"
47 #include "core/html/parser/TextResourceDecoder.h"
48 #include "core/inspector/ConsoleMessage.h" 47 #include "core/inspector/ConsoleMessage.h"
49 #include "core/inspector/InspectorInstrumentation.h" 48 #include "core/inspector/InspectorInstrumentation.h"
50 #include "core/loader/ThreadableLoader.h" 49 #include "core/loader/ThreadableLoader.h"
51 #include "core/page/EventSourceInit.h" 50 #include "core/page/EventSourceInit.h"
52 #include "platform/HTTPNames.h" 51 #include "platform/HTTPNames.h"
53 #include "platform/network/ResourceError.h" 52 #include "platform/network/ResourceError.h"
54 #include "platform/network/ResourceRequest.h" 53 #include "platform/network/ResourceRequest.h"
55 #include "platform/network/ResourceResponse.h" 54 #include "platform/network/ResourceResponse.h"
56 #include "platform/weborigin/SecurityOrigin.h" 55 #include "platform/weborigin/SecurityOrigin.h"
57 #include "public/platform/WebURLRequest.h" 56 #include "public/platform/WebURLRequest.h"
58 #include "wtf/ASCIICType.h"
59 #include "wtf/text/StringBuilder.h" 57 #include "wtf/text/StringBuilder.h"
60 58
61 namespace blink { 59 namespace blink {
62 60
63 const unsigned long long EventSource::defaultReconnectDelay = 3000; 61 const unsigned long long EventSource::defaultReconnectDelay = 3000;
64 62
65 inline EventSource::EventSource(ExecutionContext* context, const KURL& url, cons t EventSourceInit& eventSourceInit) 63 inline EventSource::EventSource(ExecutionContext* context, const KURL& url, cons t EventSourceInit& eventSourceInit)
66 : ActiveDOMObject(context) 64 : ActiveDOMObject(context)
67 , m_url(url) 65 , m_url(url)
68 , m_withCredentials(eventSourceInit.withCredentials()) 66 , m_withCredentials(eventSourceInit.withCredentials())
69 , m_state(CONNECTING) 67 , m_state(CONNECTING)
70 , m_decoder(TextResourceDecoder::create("text/plain", "UTF-8"))
71 , m_connectTimer(this, &EventSource::connectTimerFired) 68 , m_connectTimer(this, &EventSource::connectTimerFired)
72 , m_discardTrailingNewline(false)
73 , m_reconnectDelay(defaultReconnectDelay) 69 , m_reconnectDelay(defaultReconnectDelay)
74 { 70 {
75 } 71 }
76 72
77 EventSource* EventSource::create(ExecutionContext* context, const String& url, c onst EventSourceInit& eventSourceInit, ExceptionState& exceptionState) 73 EventSource* EventSource::create(ExecutionContext* context, const String& url, c onst EventSourceInit& eventSourceInit, ExceptionState& exceptionState)
78 { 74 {
79 if (url.isEmpty()) { 75 if (url.isEmpty()) {
80 exceptionState.throwDOMException(SyntaxError, "Cannot open an EventSourc e to an empty URL."); 76 exceptionState.throwDOMException(SyntaxError, "Cannot open an EventSourc e to an empty URL.");
81 return nullptr; 77 return nullptr;
82 } 78 }
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
120 ASSERT(m_state == CONNECTING); 116 ASSERT(m_state == CONNECTING);
121 ASSERT(!m_loader); 117 ASSERT(!m_loader);
122 ASSERT(executionContext()); 118 ASSERT(executionContext());
123 119
124 ExecutionContext& executionContext = *this->executionContext(); 120 ExecutionContext& executionContext = *this->executionContext();
125 ResourceRequest request(m_url); 121 ResourceRequest request(m_url);
126 request.setHTTPMethod(HTTPNames::GET); 122 request.setHTTPMethod(HTTPNames::GET);
127 request.setHTTPHeaderField(HTTPNames::Accept, "text/event-stream"); 123 request.setHTTPHeaderField(HTTPNames::Accept, "text/event-stream");
128 request.setHTTPHeaderField(HTTPNames::Cache_Control, "no-cache"); 124 request.setHTTPHeaderField(HTTPNames::Cache_Control, "no-cache");
129 request.setRequestContext(WebURLRequest::RequestContextEventSource); 125 request.setRequestContext(WebURLRequest::RequestContextEventSource);
130 if (!m_lastEventId.isEmpty()) { 126 if (m_parser && !m_parser->lastEventId().isEmpty()) {
131 // HTTP headers are Latin-1 byte strings, but the Last-Event-ID header i s encoded as UTF-8. 127 // HTTP headers are Latin-1 byte strings, but the Last-Event-ID header i s encoded as UTF-8.
132 // TODO(davidben): This should be captured in the type of setHTTPHeaderF ield's arguments. 128 // TODO(davidben): This should be captured in the type of setHTTPHeaderF ield's arguments.
133 CString lastEventIdUtf8 = m_lastEventId.utf8(); 129 CString lastEventIdUtf8 = m_parser->lastEventId().utf8();
134 request.setHTTPHeaderField(HTTPNames::Last_Event_ID, AtomicString(reinte rpret_cast<const LChar*>(lastEventIdUtf8.data()), lastEventIdUtf8.length())); 130 request.setHTTPHeaderField(HTTPNames::Last_Event_ID, AtomicString(reinte rpret_cast<const LChar*>(lastEventIdUtf8.data()), lastEventIdUtf8.length()));
135 } 131 }
136 132
137 SecurityOrigin* origin = executionContext.securityOrigin(); 133 SecurityOrigin* origin = executionContext.securityOrigin();
138 134
139 ThreadableLoaderOptions options; 135 ThreadableLoaderOptions options;
140 options.preflightPolicy = PreventPreflight; 136 options.preflightPolicy = PreventPreflight;
141 options.crossOriginRequestPolicy = UseAccessControl; 137 options.crossOriginRequestPolicy = UseAccessControl;
142 options.contentSecurityPolicyEnforcement = ContentSecurityPolicy::shouldBypa ssMainWorld(&executionContext) ? DoNotEnforceContentSecurityPolicy : EnforceConn ectSrcDirective; 138 options.contentSecurityPolicyEnforcement = ContentSecurityPolicy::shouldBypa ssMainWorld(&executionContext) ? DoNotEnforceContentSecurityPolicy : EnforceConn ectSrcDirective;
143 139
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after
189 { 185 {
190 return m_state; 186 return m_state;
191 } 187 }
192 188
193 void EventSource::close() 189 void EventSource::close()
194 { 190 {
195 if (m_state == CLOSED) { 191 if (m_state == CLOSED) {
196 ASSERT(!m_loader); 192 ASSERT(!m_loader);
197 return; 193 return;
198 } 194 }
195 if (m_parser)
196 m_parser->stop();
199 197
200 // Stop trying to reconnect if EventSource was explicitly closed or if Activ eDOMObject::stop() was called. 198 // Stop trying to reconnect if EventSource was explicitly closed or if Activ eDOMObject::stop() was called.
201 if (m_connectTimer.isActive()) { 199 if (m_connectTimer.isActive()) {
202 m_connectTimer.stop(); 200 m_connectTimer.stop();
203 } 201 }
204 202
205 if (m_loader) { 203 if (m_loader) {
206 m_loader->cancel(); 204 m_loader->cancel();
207 m_loader = nullptr; 205 m_loader = nullptr;
208 } 206 }
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
249 message.appendLiteral("EventSource's response has a MIME type (\""); 247 message.appendLiteral("EventSource's response has a MIME type (\"");
250 message.append(response.mimeType()); 248 message.append(response.mimeType());
251 message.appendLiteral("\") that is not \"text/event-stream\". Aborti ng the connection."); 249 message.appendLiteral("\") that is not \"text/event-stream\". Aborti ng the connection.");
252 // FIXME: We are missing the source line. 250 // FIXME: We are missing the source line.
253 executionContext()->addConsoleMessage(ConsoleMessage::create(JSMessa geSource, ErrorMessageLevel, message.toString())); 251 executionContext()->addConsoleMessage(ConsoleMessage::create(JSMessa geSource, ErrorMessageLevel, message.toString()));
254 } 252 }
255 } 253 }
256 254
257 if (responseIsValid) { 255 if (responseIsValid) {
258 m_state = OPEN; 256 m_state = OPEN;
257 AtomicString lastEventId;
258 if (m_parser) {
259 // The new parser takes over the event ID.
260 lastEventId = m_parser->lastEventId();
261 }
262 m_parser = new EventSourceParser(lastEventId, this);
259 dispatchEvent(Event::create(EventTypeNames::open)); 263 dispatchEvent(Event::create(EventTypeNames::open));
260 } else { 264 } else {
261 m_loader->cancel(); 265 m_loader->cancel();
262 dispatchEvent(Event::create(EventTypeNames::error)); 266 dispatchEvent(Event::create(EventTypeNames::error));
263 } 267 }
264 } 268 }
265 269
266 void EventSource::didReceiveData(const char* data, unsigned length) 270 void EventSource::didReceiveData(const char* data, unsigned length)
267 { 271 {
268 ASSERT(m_state == OPEN); 272 ASSERT(m_state == OPEN);
269 ASSERT(m_loader); 273 ASSERT(m_loader);
274 ASSERT(m_parser);
270 275
271 append(m_receiveBuf, m_decoder->decode(data, length)); 276 m_parser->addBytes(data, length);
272 parseEventStream();
273 } 277 }
274 278
275 void EventSource::didFinishLoading(unsigned long, double) 279 void EventSource::didFinishLoading(unsigned long, double)
276 { 280 {
277 ASSERT(m_state == OPEN); 281 ASSERT(m_state == OPEN);
278 ASSERT(m_loader); 282 ASSERT(m_loader);
279 283
280 if (m_receiveBuf.size() > 0 || m_data.size() > 0) {
281 parseEventStream();
282
283 // Discard everything that has not been dispatched by now.
284 m_receiveBuf.clear();
285 m_data.clear();
286 m_eventName = emptyAtom;
287 m_currentlyParsedEventId = nullAtom;
288 }
289 networkRequestEnded(); 284 networkRequestEnded();
290 } 285 }
291 286
292 void EventSource::didFail(const ResourceError& error) 287 void EventSource::didFail(const ResourceError& error)
293 { 288 {
294 ASSERT(m_state != CLOSED); 289 ASSERT(m_state != CLOSED);
295 ASSERT(m_loader); 290 ASSERT(m_loader);
296 291
297 if (error.isCancellation()) 292 if (error.isCancellation())
298 m_state = CLOSED; 293 m_state = CLOSED;
(...skipping 10 matching lines...) Expand all
309 abortConnectionAttempt(); 304 abortConnectionAttempt();
310 } 305 }
311 306
312 void EventSource::didFailRedirectCheck() 307 void EventSource::didFailRedirectCheck()
313 { 308 {
314 ASSERT(m_loader); 309 ASSERT(m_loader);
315 310
316 abortConnectionAttempt(); 311 abortConnectionAttempt();
317 } 312 }
318 313
314 void EventSource::onMessageEvent(const AtomicString& eventType, const String& da ta, const AtomicString& lastEventId)
315 {
316 RefPtrWillBeRawPtr<MessageEvent> e = MessageEvent::create();
317 e->initMessageEvent(eventType, false, false, SerializedScriptValueFactory::i nstance().create(data), m_eventStreamOrigin, lastEventId, 0, nullptr);
318
319 InspectorInstrumentation::willDispatchEventSourceEvent(executionContext(), t his, eventType, lastEventId, data);
320 dispatchEvent(e);
321 }
322
323 void EventSource::onReconnectionTimeSet(unsigned long long reconnectionTime)
324 {
325 m_reconnectDelay = reconnectionTime;
326 }
327
319 void EventSource::abortConnectionAttempt() 328 void EventSource::abortConnectionAttempt()
320 { 329 {
321 ASSERT(m_state == CONNECTING); 330 ASSERT(m_state == CONNECTING);
322 331
323 m_loader = nullptr; 332 m_loader = nullptr;
324 m_state = CLOSED; 333 m_state = CLOSED;
325 networkRequestEnded(); 334 networkRequestEnded();
326 335
327 dispatchEvent(Event::create(EventTypeNames::error)); 336 dispatchEvent(Event::create(EventTypeNames::error));
328 } 337 }
329 338
330 void EventSource::parseEventStream()
331 {
332 unsigned bufPos = 0;
333 unsigned bufSize = m_receiveBuf.size();
334 while (bufPos < bufSize) {
335 if (m_discardTrailingNewline) {
336 if (m_receiveBuf[bufPos] == '\n')
337 bufPos++;
338 m_discardTrailingNewline = false;
339 }
340
341 int lineLength = -1;
342 int fieldLength = -1;
343 for (unsigned i = bufPos; lineLength < 0 && i < bufSize; i++) {
344 switch (m_receiveBuf[i]) {
345 case ':':
346 if (fieldLength < 0)
347 fieldLength = i - bufPos;
348 break;
349 case '\r':
350 m_discardTrailingNewline = true;
351 case '\n':
352 lineLength = i - bufPos;
353 break;
354 }
355 }
356
357 if (lineLength < 0)
358 break;
359
360 parseEventStreamLine(bufPos, fieldLength, lineLength);
361 bufPos += lineLength + 1;
362
363 // EventSource.close() might've been called by one of the message event handlers.
364 // Per spec, no further messages should be fired after that.
365 if (m_state == CLOSED)
366 break;
367 }
368
369 if (bufPos == bufSize)
370 m_receiveBuf.clear();
371 else if (bufPos)
372 m_receiveBuf.remove(0, bufPos);
373 }
374
375 void EventSource::parseEventStreamLine(unsigned bufPos, int fieldLength, int lin eLength)
376 {
377 if (!lineLength) {
378 if (!m_data.isEmpty()) {
379 m_data.removeLast();
380 if (!m_currentlyParsedEventId.isNull()) {
381 m_lastEventId = m_currentlyParsedEventId;
382 m_currentlyParsedEventId = nullAtom;
383 }
384 InspectorInstrumentation::willDispachEventSourceEvent(executionConte xt(), this, m_eventName.isEmpty() ? EventTypeNames::message : m_eventName, m_las tEventId, m_data);
385 dispatchEvent(createMessageEvent());
386 }
387 if (!m_eventName.isEmpty())
388 m_eventName = emptyAtom;
389 } else if (fieldLength) {
390 bool noValue = fieldLength < 0;
391
392 String field(&m_receiveBuf[bufPos], noValue ? lineLength : fieldLength);
393 int step;
394 if (noValue)
395 step = lineLength;
396 else if (m_receiveBuf[bufPos + fieldLength + 1] != ' ')
397 step = fieldLength + 1;
398 else
399 step = fieldLength + 2;
400 bufPos += step;
401 int valueLength = lineLength - step;
402
403 if (field == "data") {
404 if (valueLength)
405 m_data.append(&m_receiveBuf[bufPos], valueLength);
406 m_data.append('\n');
407 } else if (field == "event") {
408 m_eventName = valueLength ? AtomicString(&m_receiveBuf[bufPos], valu eLength) : "";
409 } else if (field == "id") {
410 m_currentlyParsedEventId = valueLength ? AtomicString(&m_receiveBuf[ bufPos], valueLength) : "";
411 } else if (field == "retry") {
412 bool hasOnlyDigits = true;
413 for (int i = 0; i < valueLength && hasOnlyDigits; ++i) {
414 hasOnlyDigits = isASCIIDigit(m_receiveBuf[bufPos + i]);
415 }
416 if (!valueLength) {
417 m_reconnectDelay = defaultReconnectDelay;
418 } else if (hasOnlyDigits) {
419 String value(&m_receiveBuf[bufPos], valueLength);
420 bool ok;
421 unsigned long long retry = value.toUInt64(&ok);
422 if (ok)
423 m_reconnectDelay = retry;
424 }
425 }
426 }
427 }
428
429 void EventSource::stop() 339 void EventSource::stop()
430 { 340 {
431 close(); 341 close();
432 } 342 }
433 343
434 bool EventSource::hasPendingActivity() const 344 bool EventSource::hasPendingActivity() const
435 { 345 {
436 return m_state != CLOSED; 346 return m_state != CLOSED;
437 } 347 }
438 348
439 PassRefPtrWillBeRawPtr<MessageEvent> EventSource::createMessageEvent()
440 {
441 RefPtrWillBeRawPtr<MessageEvent> event = MessageEvent::create();
442 event->initMessageEvent(m_eventName.isEmpty() ? EventTypeNames::message : m_ eventName, false, false, SerializedScriptValueFactory::instance().create(String( m_data)), m_eventStreamOrigin, m_lastEventId, 0, nullptr);
443 m_data.clear();
444 return event.release();
445 }
446
447 DEFINE_TRACE(EventSource) 349 DEFINE_TRACE(EventSource)
448 { 350 {
351 visitor->trace(m_parser);
449 RefCountedGarbageCollectedEventTargetWithInlineData::trace(visitor); 352 RefCountedGarbageCollectedEventTargetWithInlineData::trace(visitor);
450 ActiveDOMObject::trace(visitor); 353 ActiveDOMObject::trace(visitor);
354 EventSourceParser::Client::trace(visitor);
451 } 355 }
452 356
453 } // namespace blink 357 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698